diff --git a/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt b/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt index a212c2e95b..99eb5cccfb 100644 --- a/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt +++ b/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt @@ -2,10 +2,12 @@ package au.com.dius.pact.core.model import au.com.dius.pact.core.model.messaging.MessagePact import au.com.dius.pact.core.pactbroker.PactBrokerClient +import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import au.com.dius.pact.core.pactbroker.PactBrokerResult import au.com.dius.pact.core.pactbroker.util.HttpClientUtils import au.com.dius.pact.core.pactbroker.util.HttpClientUtils.isJsonResponse import au.com.dius.pact.core.support.Auth +import au.com.dius.pact.core.support.Utils import au.com.dius.pact.core.support.CustomServiceUnavailableRetryStrategy import au.com.dius.pact.core.support.HttpClient import au.com.dius.pact.core.support.Json @@ -40,6 +42,7 @@ import java.net.URI import java.net.URL import java.net.URLDecoder import kotlin.collections.set +import kotlin.text.isNotEmpty private val logger = KotlinLogging.logger {} @@ -52,7 +55,9 @@ fun loadPactFromUrl( ): Pair { return when (source) { is BrokerUrlSource -> { - val brokerClient = PactBrokerClient(source.pactBrokerUrl, options.toMutableMap()) + val insecureTLS = Utils.lookupInMap(options, "insecureTLS", Boolean::class.java, false) + val brokerClient = PactBrokerClient(source.pactBrokerUrl, options.toMutableMap(), + PactBrokerClientConfig(insecureTLS = insecureTLS)) val pactResponse = brokerClient.fetchPact(source.url, source.encodePath) pactResponse.pactFile to source.copy(attributes = pactResponse.links, options = options, tag = source.tag) } @@ -352,11 +357,21 @@ object DefaultPactReader : PactReader, KLogging() { } else if (source is InputStream || source is Reader || source is File) { return loadPactFromFile(source) } else if (source is BrokerUrlSource) { - return HttpClient.newHttpClient(options["authentication"], URI(source.pactBrokerUrl)).first.use { + val insecureTLS = Utils.lookupInMap(options, "insecureTLS", Boolean::class.java, false) + return HttpClient.newHttpClient( + options["authentication"], + URI(source.pactBrokerUrl), + insecureTLS = insecureTLS + ).first.use { loadPactFromUrl(source, options, it) } } else if (source is PactBrokerResult) { - return HttpClient.newHttpClient(options["authentication"], URI(source.pactBrokerUrl)).first.use { + val insecureTLS = Utils.lookupInMap(options, "insecureTLS", Boolean::class.java, false) + return HttpClient.newHttpClient( + options["authentication"], + URI(source.pactBrokerUrl), + insecureTLS = insecureTLS + ).first.use { loadPactFromUrl(BrokerUrlSource.fromResult(source, options, source.tag), options, it) } } else if (source is URL || source is UrlPactSource) { diff --git a/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/HalClient.kt b/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/HalClient.kt index 669e4c6b53..2874d7d67e 100644 --- a/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/HalClient.kt +++ b/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/HalClient.kt @@ -139,7 +139,8 @@ interface IHalClient { */ open class HalClient @JvmOverloads constructor( val baseUrl: String, - var options: Map = mapOf() + var options: Map = mapOf(), + val config: PactBrokerClientConfig ) : IHalClient { var httpClient: CloseableHttpClient? = null @@ -200,7 +201,7 @@ open class HalClient @JvmOverloads constructor( } val uri = URI(baseUrl) val result = HttpClient.newHttpClient(options["authentication"], uri, this.maxPublishRetries, - this.publishRetryInterval) + this.publishRetryInterval, config.insecureTLS) httpClient = result.first if (System.getProperty(PREEMPTIVE_AUTHENTICATION) == "true") { diff --git a/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/PactBrokerClient.kt b/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/PactBrokerClient.kt index 83835d964f..68237a12e4 100644 --- a/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/PactBrokerClient.kt +++ b/core/pactbroker/src/main/kotlin/au/com/dius/pact/core/pactbroker/PactBrokerClient.kt @@ -162,9 +162,10 @@ interface IPactBrokerClient { ): Result } -data class PactBrokerClientConfig( +data class PactBrokerClientConfig @JvmOverloads constructor( val retryCountWhileUnknown: Int = 0, - val retryWhileUnknownInterval: Int = 10 + val retryWhileUnknownInterval: Int = 10, + val insecureTLS: Boolean = false ) /** @@ -174,10 +175,11 @@ open class PactBrokerClient( val pactBrokerUrl: String, @Deprecated("Move use of options to PactBrokerClientConfig") override val options: MutableMap, - val config: PactBrokerClientConfig = PactBrokerClientConfig() + val config: PactBrokerClientConfig ) : IPactBrokerClient { - constructor(pactBrokerUrl: String) : this(pactBrokerUrl, mutableMapOf()) + @Deprecated("Use the version that takes PactBrokerClientConfig") + constructor(pactBrokerUrl: String) : this(pactBrokerUrl, mutableMapOf(), PactBrokerClientConfig()) /** * Fetches all consumers for the given provider @@ -353,7 +355,7 @@ open class PactBrokerClient( return PactResponse(halDoc, HalClient.asMap(halDoc["_links"].asObject())) } - open fun newHalClient(): IHalClient = HalClient(pactBrokerUrl, options) + open fun newHalClient(): IHalClient = HalClient(pactBrokerUrl, options, config) override fun publishVerificationResults( docAttributes: Map, diff --git a/core/pactbroker/src/test/groovy/au/com/dius/pact/core/pactbroker/HalClientSpec.groovy b/core/pactbroker/src/test/groovy/au/com/dius/pact/core/pactbroker/HalClientSpec.groovy index 224ce9ee70..c94bd4bfed 100644 --- a/core/pactbroker/src/test/groovy/au/com/dius/pact/core/pactbroker/HalClientSpec.groovy +++ b/core/pactbroker/src/test/groovy/au/com/dius/pact/core/pactbroker/HalClientSpec.groovy @@ -33,7 +33,7 @@ class HalClientSpec extends Specification { def setup() { mockClient = Mock(CloseableHttpClient) - client = Spy(HalClient, constructorArgs: ['http://localhost:1234/']) + client = Spy(HalClient, constructorArgs: ['http://localhost:1234/', [:], new PactBrokerClientConfig()]) client.pathInfo = null } @@ -499,7 +499,8 @@ class HalClientSpec extends Specification { @Issue('1399') def 'navigating with a base URL containing a path'() { given: - HalClient client = Spy(HalClient, constructorArgs: ['http://localhost:1234/subpath/one/two']) + HalClient client = Spy(HalClient, constructorArgs: ['http://localhost:1234/subpath/one/two', [:], + new PactBrokerClientConfig()]) client.pathInfo = null client.httpClient = mockClient def mockResponse = Mock(CloseableHttpResponse) { diff --git a/core/support/src/main/kotlin/au/com/dius/pact/core/support/HttpClient.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/HttpClient.kt index 2ebfccdd43..c482059cae 100644 --- a/core/support/src/main/kotlin/au/com/dius/pact/core/support/HttpClient.kt +++ b/core/support/src/main/kotlin/au/com/dius/pact/core/support/HttpClient.kt @@ -7,13 +7,22 @@ import mu.KLogging import org.apache.http.auth.AuthScope import org.apache.http.auth.UsernamePasswordCredentials import org.apache.http.client.CredentialsProvider +import org.apache.http.config.RegistryBuilder +import org.apache.http.conn.socket.ConnectionSocketFactory +import org.apache.http.conn.socket.PlainConnectionSocketFactory +import org.apache.http.conn.ssl.NoopHostnameVerifier +import org.apache.http.conn.ssl.SSLConnectionSocketFactory +import org.apache.http.conn.ssl.TrustStrategy import org.apache.http.impl.client.BasicCredentialsProvider import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClientBuilder import org.apache.http.impl.client.HttpClients import org.apache.http.impl.client.SystemDefaultCredentialsProvider +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager import org.apache.http.message.BasicHeader +import org.apache.http.ssl.SSLContextBuilder import java.net.URI +import java.security.cert.X509Certificate sealed class Auth { data class BasicAuthentication(val username: String, val password: String) : Auth() @@ -40,7 +49,8 @@ object HttpClient : KLogging() { authentication: Any?, uri: URI, maxPublishRetries: Int = 5, - publishRetryInterval: Int = 3000 + publishRetryInterval: Int = 3000, + insecureTLS: Boolean = false ): Pair { val retryStrategy = CustomServiceUnavailableRetryStrategy(maxPublishRetries, publishRetryInterval) val builder = HttpClients.custom().useSystemProperties().setServiceUnavailableRetryStrategy(retryStrategy) @@ -84,6 +94,11 @@ object HttpClient : KLogging() { } builder.setDefaultHeaders(defaultHeaders.map { BasicHeader(it.key, it.value) }) + + if (insecureTLS) { + setupInsecureTLS(builder) + } + return builder.build() to credsProvider } @@ -99,4 +114,27 @@ object HttpClient : KLogging() { builder.setDefaultCredentialsProvider(credsProvider) return credsProvider } + + private fun setupInsecureTLS(builder: HttpClientBuilder) { + logger.warn { + """ + ***************************************************************** + Setting Insecure TLS + This will disable hostname validation and trust all certificates! + ***************************************************************** + """ + } + + val trustStrategy = TrustStrategy { _: Array, _: String -> true } + val sslContext = SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build() + builder.setSSLContext(sslContext) + val hostnameVerifier = NoopHostnameVerifier() + val sslSocketFactory = SSLConnectionSocketFactory(sslContext, hostnameVerifier) + val socketFactoryRegistry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", sslSocketFactory) + .build() + val connMgr = PoolingHttpClientConnectionManager(socketFactoryRegistry) + builder.setConnectionManager(connMgr) + } } diff --git a/core/support/src/test/groovy/au/com/dius/pact/core/support/HttpClientSpec.groovy b/core/support/src/test/groovy/au/com/dius/pact/core/support/HttpClientSpec.groovy index 4334f32658..c7abbf57c1 100644 --- a/core/support/src/test/groovy/au/com/dius/pact/core/support/HttpClientSpec.groovy +++ b/core/support/src/test/groovy/au/com/dius/pact/core/support/HttpClientSpec.groovy @@ -10,7 +10,7 @@ class HttpClientSpec extends Specification { def authentication = ['bearer', '1234abcd'] when: - def result = HttpClient.INSTANCE.newHttpClient(authentication, uri, 1, 1) + def result = HttpClient.INSTANCE.newHttpClient(authentication, uri, 1, 1, false) def defaultHeaders = result.component1().closeables[0].this$0.defaultHeaders then: diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBaseMojo.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBaseMojo.kt index 12ee4fd394..965073a126 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBaseMojo.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBaseMojo.kt @@ -1,5 +1,6 @@ package au.com.dius.pact.provider.maven +import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import org.apache.maven.plugin.AbstractMojo import org.apache.maven.plugins.annotations.Component import org.apache.maven.plugins.annotations.Parameter @@ -32,6 +33,15 @@ abstract class PactBaseMojo : AbstractMojo() { @Component protected lateinit var decrypter: SettingsDecrypter + @Parameter(property = "retriesWhenUnknown", defaultValue = "0") + private var retriesWhenUnknown: Int? = 0 + + @Parameter(property = "retryInterval", defaultValue = "10") + private var retryInterval: Int? = 10 + + @Parameter(property = "pactBrokerInsecureTLS", defaultValue = "false") + private var pactBrokerInsecureTLS: Boolean? = false + protected fun brokerClientOptions(): MutableMap { val options = mutableMapOf() if (!pactBrokerToken.isNullOrEmpty()) { @@ -49,4 +59,12 @@ abstract class PactBaseMojo : AbstractMojo() { } return options } + + protected fun brokerClientConfig(): PactBrokerClientConfig { + return PactBrokerClientConfig( + retriesWhenUnknown ?: 0, + retryInterval ?: 10, + pactBrokerInsecureTLS ?: false + ) + } } diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBroker.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBroker.kt index fb0552f3cf..7cffeb9e27 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBroker.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactBroker.kt @@ -13,7 +13,8 @@ data class PactBroker @JvmOverloads constructor( val authentication: PactBrokerAuth? = null, val serverId: String? = null, var enablePending: EnablePending? = null, - val fallbackTag: String? = null + val fallbackTag: String? = null, + val insecureTLS: Boolean? = false ) /** diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCanIDeployMojo.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCanIDeployMojo.kt index 5b32356f7e..82755d16eb 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCanIDeployMojo.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCanIDeployMojo.kt @@ -2,7 +2,6 @@ package au.com.dius.pact.provider.maven import au.com.dius.pact.core.pactbroker.Latest import au.com.dius.pact.core.pactbroker.PactBrokerClient -import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import au.com.dius.pact.core.support.isNotEmpty import com.github.ajalt.mordant.TermColors import org.apache.maven.plugin.MojoExecutionException @@ -29,12 +28,6 @@ open class PactCanIDeployMojo : PactBaseMojo() { @Parameter(property = "toTag", defaultValue = "") private var to: String? = "" - @Parameter(property = "retriesWhenUnknown", defaultValue = "0") - private var retriesWhenUnknown: Int? = 0 - - @Parameter(property = "retryInterval", defaultValue = "10") - private var retryInterval: Int? = 10 - override fun execute() { val t = TermColors() @@ -67,10 +60,6 @@ open class PactCanIDeployMojo : PactBaseMojo() { } } - private fun brokerClientConfig(): PactBrokerClientConfig { - return PactBrokerClientConfig(retriesWhenUnknown ?: 0, retryInterval ?: 10) - } - private fun setupLatestParam(): Latest { var latest: Latest = Latest.UseLatest(false) if (this.latest.isNotEmpty()) { diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCreateVersionTagMojo.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCreateVersionTagMojo.kt index 768d25dbd9..154c35483b 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCreateVersionTagMojo.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactCreateVersionTagMojo.kt @@ -58,7 +58,7 @@ open class PactCreateVersionTagMojo : PactBaseMojo() { private fun createBrokerClient() { if (brokerClient == null) - brokerClient = PactBrokerClient(pactBrokerUrl!!, brokerClientOptions()) + brokerClient = PactBrokerClient(pactBrokerUrl!!, brokerClientOptions(), brokerClientConfig()) } private fun createVersionTag() = diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactProviderMojo.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactProviderMojo.kt index 34eb4cc2cf..1e6f390a60 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactProviderMojo.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactProviderMojo.kt @@ -157,6 +157,10 @@ open class PactProviderMojo : PactBaseMojo() { options["authentication"] = listOf("basic", serverDetails.username, result.server.password) } + if (pactBroker?.insecureTLS == true) { + options["insecureTLS"] = true + } + when { pactBroker?.enablePending != null -> { if (pactBroker.enablePending!!.providerTags.isEmpty()) { diff --git a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactPublishMojo.kt b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactPublishMojo.kt index 8f483764a8..a718ff1080 100644 --- a/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactPublishMojo.kt +++ b/provider/maven/src/main/kotlin/au/com/dius/pact/provider/maven/PactPublishMojo.kt @@ -53,7 +53,7 @@ open class PactPublishMojo : PactBaseMojo() { } if (brokerClient == null) { - brokerClient = PactBrokerClient(pactBrokerUrl!!, brokerClientOptions()) + brokerClient = PactBrokerClient(pactBrokerUrl!!, brokerClientOptions(), brokerClientConfig()) } val pactDirectory = File(pactDirectory) diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderInfo.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderInfo.kt index 82d876af3d..544da14f96 100644 --- a/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderInfo.kt +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderInfo.kt @@ -5,6 +5,7 @@ import au.com.dius.pact.core.model.FileSource import au.com.dius.pact.core.model.Interaction import au.com.dius.pact.core.pactbroker.ConsumerVersionSelector import au.com.dius.pact.core.pactbroker.PactBrokerClient +import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import au.com.dius.pact.core.support.Utils import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok @@ -99,8 +100,10 @@ open class ProviderInfo @JvmOverloads constructor ( } } - open fun pactBrokerClient(pactBrokerUrl: String, options: Map) = - PactBrokerClient(pactBrokerUrl, options.toMutableMap()) + open fun pactBrokerClient(pactBrokerUrl: String, options: Map): PactBrokerClient { + val insecureTLS = Utils.lookupInMap(options, "insecureTLS", Boolean::class.java, false) + return PactBrokerClient(pactBrokerUrl, options.toMutableMap(), PactBrokerClientConfig(insecureTLS = insecureTLS)) + } @Suppress("TooGenericExceptionThrown") open fun setupConsumerListFromPactFiles(consumersGroup: ConsumersGroup): MutableList { diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/VerificationReporter.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/VerificationReporter.kt index ba738cf9fb..b04d61c869 100644 --- a/provider/src/main/kotlin/au/com/dius/pact/provider/VerificationReporter.kt +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/VerificationReporter.kt @@ -5,6 +5,7 @@ import au.com.dius.pact.core.model.Interaction import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.pactbroker.IPactBrokerClient import au.com.dius.pact.core.pactbroker.PactBrokerClient +import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import au.com.dius.pact.core.pactbroker.TestResult import au.com.dius.pact.core.support.expressions.SystemPropertyResolver import au.com.dius.pact.core.support.expressions.ValueResolver @@ -81,7 +82,8 @@ object DefaultVerificationReporter : VerificationReporter, KLogging() { ): Result> { return when (val source = pact.source) { is BrokerUrlSource -> { - val brokerClient = client ?: PactBrokerClient(source.pactBrokerUrl, source.options.toMutableMap()) + val brokerClient = client ?: PactBrokerClient(source.pactBrokerUrl, source.options.toMutableMap(), + PactBrokerClientConfig()) publishResult(brokerClient, source, result, version, pact, tags) } else -> { diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt index 8c91dabdbe..19dfdc5d62 100644 --- a/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt @@ -11,6 +11,7 @@ import au.com.dius.pact.core.model.PactSource import au.com.dius.pact.core.pactbroker.ConsumerVersionSelector import au.com.dius.pact.core.pactbroker.IPactBrokerClient import au.com.dius.pact.core.pactbroker.PactBrokerClient +import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig import au.com.dius.pact.core.support.Utils.permutations import au.com.dius.pact.core.support.expressions.DataType import au.com.dius.pact.core.support.expressions.ExpressionParser.parseExpression @@ -292,31 +293,32 @@ open class PactBrokerLoader( } open fun newPactBrokerClient(url: URI, resolver: ValueResolver): IPactBrokerClient { + var options = mapOf() + val config = PactBrokerClientConfig() + if (authentication == null) { logger.debug { "Authentication: None" } - return PactBrokerClient(url.toString(), mutableMapOf()) - } - - val username = parseExpression(authentication!!.username, DataType.RAW, resolver)?.toString() - val token = parseExpression(authentication!!.token, DataType.RAW, resolver)?.toString() - - // Check if username is set. If yes, use basic auth. - if (username.isNotEmpty()) { - logger.debug { "Authentication: Basic" } - val options = mapOf("authentication" to listOf("basic", username, - parseExpression(authentication!!.password, DataType.RAW, resolver))) - return PactBrokerClient(url.toString(), options.toMutableMap()) - } - - // Check if token is set. If yes, use bearer auth. - if (token.isNotEmpty()) { - logger.debug { "Authentication: Bearer" } - val options = mapOf("authentication" to listOf("bearer", token)) - return PactBrokerClient(url.toString(), options.toMutableMap()) + } else { + val username = parseExpression(authentication!!.username, DataType.RAW, resolver)?.toString() + val token = parseExpression(authentication!!.token, DataType.RAW, resolver)?.toString() + + // Check if username is set. If yes, use basic auth. + if (username.isNotEmpty()) { + logger.debug { "Authentication: Basic" } + options = mapOf( + "authentication" to listOf( + "basic", username, + parseExpression(authentication!!.password, DataType.RAW, resolver) + ) + ) + // Check if token is set. If yes, use bearer auth. + } else if (token.isNotEmpty()) { + logger.debug { "Authentication: Bearer" } + options = mapOf("authentication" to listOf("bearer", token)) + } } - logger.debug { "Authentication: None" } - return PactBrokerClient(url.toString(), mutableMapOf()) + return PactBrokerClient(url.toString(), options.toMutableMap(), config) } companion object : KLogging()