Skip to content

Commit

Permalink
Feat: Implement new Gradle DSL for consumer version selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
uglyog committed Aug 2, 2022
1 parent 01e45dc commit 0afc954
Show file tree
Hide file tree
Showing 12 changed files with 593 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.github.michaelbull.result.map
import com.github.michaelbull.result.mapError
import com.github.michaelbull.result.unwrap
import com.google.common.net.UrlEscapers.urlFormParameterEscaper
import com.google.common.net.UrlEscapers.urlPathSegmentEscaper
import mu.KLogging
import java.io.File
import java.io.IOException
Expand Down Expand Up @@ -110,6 +109,7 @@ data class CanIDeployResult(val ok: Boolean, val message: String, val reason: St
/**
* Consumer version selector. See https://docs.pact.io/pact_broker/advanced_topics/selectors
*/
@Deprecated(message = "Has been replaced with ConsumerVersionSelectors sealed classes")
data class ConsumerVersionSelector(
val tag: String? = null,
val latest: Boolean = true,
Expand All @@ -129,6 +129,153 @@ data class ConsumerVersionSelector(
}
return obj
}

/**
* Converts this deprecated version to a ConsumerVersionSelectors
*/
fun toSelector(): ConsumerVersionSelectors {
return ConsumerVersionSelectors.Selector(tag, latest, consumer, fallbackTag)
}
}

/**
* Consumer version selectors. See https://docs.pact.io/pact_broker/advanced_topics/selectors
*/
sealed class ConsumerVersionSelectors {
/**
* The latest version from the main branch of each consumer, as specified by the consumer's mainBranch property.
*/
object MainBranch: ConsumerVersionSelectors()

/**
* The latest version from a particular branch of each consumer, or for a particular consumer if the second
* parameter is provided. If fallback is provided, falling back to the fallback branch if none is found from the
* specified branch.
*/
data class Branch @JvmOverloads constructor(
val name: String,
val consumer: String? = null,
val fallback: String? = null
): ConsumerVersionSelectors()

/**
* All the currently deployed and currently released and supported versions of each consumer.
*/
object DeployedOrReleased: ConsumerVersionSelectors()

/**
* The latest version from any branch of the consumer that has the same name as the current branch of the provider.
* Used for coordinated development between consumer and provider teams using matching feature branch names.
*/
object MatchingBranch: ConsumerVersionSelectors()

/**
* Any versions currently deployed to the specified environment
*/
data class DeployedTo(val environment: String): ConsumerVersionSelectors()

/**
* Any versions currently released and supported in the specified environment
*/
data class ReleasedTo(val environment: String): ConsumerVersionSelectors()

/**
* Any versions currently deployed or released and supported in the specified environment
*/
data class Environment(val environment: String): ConsumerVersionSelectors()

/**
* All versions with the specified tag
*/
@Deprecated(message = "Tags have been deprecated in favor of branches")
data class Tag(val tag: String): ConsumerVersionSelectors()

/**
* The latest version for each consumer with the specified tag. If fallback is provided, will fall back to the
* fallback tag if none is found with the specified tag
*/
@Deprecated(message = "Tags have been deprecated in favor of branches")
data class LatestTag @JvmOverloads constructor(
val tag: String,
val fallback: String? = null
): ConsumerVersionSelectors()

/**
* Corresponds to the old consumer version selectors
*/
@Deprecated(message = "Tags have been deprecated in favor of branches")
data class Selector @JvmOverloads constructor(
val tag: String? = null,
val latest: Boolean? = null,
val consumer: String? = null,
val fallbackTag: String? = null
): ConsumerVersionSelectors()

fun toJson(): JsonValue {
return when (this) {
is Branch -> {
val entries = mutableMapOf<String, JsonValue>("branch" to JsonValue.StringValue(this.name))

if (this.consumer.isNotEmpty()) {
entries["consumer"] = JsonValue.StringValue(this.consumer!!)
}

if (this.fallback.isNotEmpty()) {
entries["fallbackBranch"] = JsonValue.StringValue(this.fallback!!)
}

JsonValue.Object(entries)
}
DeployedOrReleased -> JsonValue.Object("deployedOrReleased" to JsonValue.True)
is DeployedTo -> JsonValue.Object(
"deployed" to JsonValue.True,
"environment" to JsonValue.StringValue(this.environment)
)
is Environment -> JsonValue.Object("environment" to JsonValue.StringValue(this.environment))
is LatestTag -> {
val entries = mutableMapOf(
"tag" to JsonValue.StringValue(this.tag),
"latest" to JsonValue.True
)

if (this.fallback.isNotEmpty()) {
entries["fallbackTag"] = JsonValue.StringValue(this.fallback!!)
}

JsonValue.Object(entries)
}
MainBranch -> JsonValue.Object("mainBranch" to JsonValue.True)
MatchingBranch -> JsonValue.Object("matchingBranch" to JsonValue.True)
is ReleasedTo -> JsonValue.Object(
"released" to JsonValue.True,
"environment" to JsonValue.StringValue(this.environment)
)
is Selector -> {
val entries = mutableMapOf<String, JsonValue>()

if (this.tag.isNotEmpty()) {
entries["tag"] = JsonValue.StringValue(this.tag!!)
}

if (this.consumer.isNotEmpty()) {
entries["consumer"] = JsonValue.StringValue(this.consumer!!)
}

if (this.latest == true) {
entries["latest"] = JsonValue.True
} else if (this.latest == false) {
entries["latest"] = JsonValue.False
}

if (this.fallbackTag.isNotEmpty()) {
entries["fallbackTag"] = JsonValue.StringValue(this.fallbackTag!!)
}

JsonValue.Object(entries)
}
is Tag -> JsonValue.Object("tag" to JsonValue.StringValue(this.tag))
}
}
}

/**
Expand All @@ -151,9 +298,12 @@ data class IgnoreSelector @JvmOverloads constructor(var name: String = "", var v
*/
interface IPactBrokerClient {
/**
* Fetches all consumers for the given provider and selectors
* Fetches all consumers for the given provider and selectors. If `pactbroker.consumerversionselectors.rawjson` is set
* as a system property or environment variable, that will override the selectors provided to this method.
*/
@Throws(IOException::class)
@Deprecated("use version that takes a list of ConsumerVersionSelectors",
replaceWith = ReplaceWith("fetchConsumersWithSelectorsV2"))
fun fetchConsumersWithSelectors(
providerName: String,
selectors: List<ConsumerVersionSelector>,
Expand All @@ -163,6 +313,20 @@ interface IPactBrokerClient {
includeWipPactsSince: String?
): Result<List<PactBrokerResult>, Exception>

/**
* Fetches all consumers for the given provider and selectors. If `pactbroker.consumerversionselectors.rawjson` is set
* as a system property or environment variable, that will override the selectors provided to this method.
*/
@Throws(IOException::class)
fun fetchConsumersWithSelectorsV2(
providerName: String,
selectors: List<ConsumerVersionSelectors>,
providerTags: List<String> = emptyList(),
providerBranch: String?,
enablePending: Boolean = false,
includeWipPactsSince: String?
): Result<List<PactBrokerResult>, Exception>

fun getUrlForProvider(providerName: String, tag: String): String?

val options: Map<String, Any>
Expand Down Expand Up @@ -303,6 +467,16 @@ open class PactBrokerClient(
providerBranch: String?,
enablePending: Boolean,
includeWipPactsSince: String?
) = fetchConsumersWithSelectorsV2(providerName, selectors.map { it.toSelector() }, providerTags, providerBranch,
enablePending, includeWipPactsSince)

override fun fetchConsumersWithSelectorsV2(
providerName: String,
selectors: List<ConsumerVersionSelectors>,
providerTags: List<String>,
providerBranch: String?,
enablePending: Boolean,
includeWipPactsSince: String?
): Result<List<PactBrokerResult>, Exception> {
val halClient = when (val navigateResult = handleWith<IHalClient> { newHalClient().navigate() }) {
is Err<Exception> -> return navigateResult
Expand All @@ -315,14 +489,19 @@ open class PactBrokerClient(
}
return if (pactsForVerification != null) {
val selectorsRawJson = lookupEnvironmentValue("pactbroker.consumerversionselectors.rawjson")
if(!selectorsRawJson.isNullOrBlank()){
fetchPactsUsingNewEndpointRaw(selectorsRawJson, enablePending, providerTags, providerBranch, includeWipPactsSince, halClient, pactsForVerification, providerName)
if (selectorsRawJson.isNotEmpty()) {
fetchPactsUsingNewEndpointRaw(selectorsRawJson!!, enablePending, providerTags, providerBranch,
includeWipPactsSince, halClient, pactsForVerification, providerName)
} else {
fetchPactsUsingNewEndpointTyped(selectors, enablePending, providerTags, providerBranch, includeWipPactsSince, halClient, pactsForVerification, providerName)
fetchPactsUsingNewEndpointTyped(selectors, enablePending, providerTags, providerBranch, includeWipPactsSince,
halClient, pactsForVerification, providerName)
}
} else {
handleWith {
val tags = selectors.filter { it.tag.isNotEmpty() }.map { it.tag to it.fallbackTag }
val tags = selectors
.filterIsInstance(ConsumerVersionSelectors.Selector::class.java)
.filter { it.tag.isNotEmpty() }
.map { it.tag to it.fallbackTag }
if (tags.isEmpty()) {
fetchConsumers(providerName)
} else {
Expand All @@ -340,7 +519,7 @@ open class PactBrokerClient(
}

private fun fetchPactsUsingNewEndpointTyped(
selectorsTyped: List<ConsumerVersionSelector>,
selectorsTyped: List<ConsumerVersionSelectors>,
enablePending: Boolean,
providerTags: List<String>,
providerBranch: String?,
Expand All @@ -350,7 +529,8 @@ open class PactBrokerClient(
providerName: String
): Result<List<PactBrokerResult>, Exception> {
val selectorsJson = jsonArray(selectorsTyped.map { it.toJson() })
return fetchPactsUsingNewEndpoint(selectorsJson, enablePending, providerTags, providerBranch, includeWipPactsSince, halClient, pactsForVerification, providerName)
return fetchPactsUsingNewEndpoint(selectorsJson, enablePending, providerTags, providerBranch, includeWipPactsSince,
halClient, pactsForVerification, providerName)
}

private fun fetchPactsUsingNewEndpointRaw(
Expand All @@ -363,7 +543,8 @@ open class PactBrokerClient(
pactsForVerification: String,
providerName: String
): Result<List<PactBrokerResult>, Exception> {
return fetchPactsUsingNewEndpoint(JsonParser.parseString(selectorsRaw), enablePending, providerTags, providerBranch, includeWipPactsSince, halClient, pactsForVerification, providerName)
return fetchPactsUsingNewEndpoint(JsonParser.parseString(selectorsRaw), enablePending, providerTags,
providerBranch, includeWipPactsSince, halClient, pactsForVerification, providerName)
}

private fun fetchPactsUsingNewEndpoint(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package au.com.dius.pact.core.pactbroker

import spock.lang.Specification
import spock.lang.Unroll

@SuppressWarnings('LineLength')
class ConsumerVersionSelectorsSpec extends Specification {
@Unroll
def 'convert to JSON'() {
expect:
selector.toJson().serialise() == json

where:

selector | json
ConsumerVersionSelectors.MainBranch.INSTANCE | '{"mainBranch":true}'
new ConsumerVersionSelectors.Branch('<branch>') | '{"branch":"<branch>"}'
ConsumerVersionSelectors.DeployedOrReleased.INSTANCE | '{"deployedOrReleased":true}'
ConsumerVersionSelectors.MatchingBranch.INSTANCE | '{"matchingBranch":true}'
new ConsumerVersionSelectors.Branch('<branch>', '<consumer>') | '{"branch":"<branch>","consumer":"<consumer>"}'
new ConsumerVersionSelectors.Branch('<branch>', null, '<fbranch>') | '{"branch":"<branch>","fallbackBranch":"<fbranch>"}'
new ConsumerVersionSelectors.DeployedTo('<environment>') | '{"deployed":true,"environment":"<environment>"}'
new ConsumerVersionSelectors.ReleasedTo('<environment>') | '{"environment":"<environment>","released":true}'
new ConsumerVersionSelectors.Environment('<environment>') | '{"environment":"<environment>"}'
new ConsumerVersionSelectors.Tag('<tag>') | '{"tag":"<tag>"}'
new ConsumerVersionSelectors.LatestTag('<tag>') | '{"latest":true,"tag":"<tag>"}'
new ConsumerVersionSelectors.LatestTag('<tag>', '<fallback>') | '{"fallbackTag":"<fallback>","latest":true,"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector('<tag>') | '{"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector('<tag>', true) | '{"latest":true,"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector('<tag>', false) | '{"latest":false,"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector('<tag>', true, null, '<fallback>') | '{"fallbackTag":"<fallback>","latest":true,"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector('<tag>', true, '<consumer>') | '{"consumer":"<consumer>","latest":true,"tag":"<tag>"}'
new ConsumerVersionSelectors.Selector(null, true) | '{"latest":true}'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package au.com.dius.pact.provider.gradle

import au.com.dius.pact.core.model.Consumer
import au.com.dius.pact.core.pactbroker.VerificationNotice
import au.com.dius.pact.core.support.Auth
import au.com.dius.pact.provider.ConsumerInfo
import au.com.dius.pact.provider.IConsumerInfo
import au.com.dius.pact.provider.PactVerification
import javax.inject.Inject

open class GradleConsumerInfo(
override var name: String,
override var stateChange: Any? = null,
override var stateChangeUsesBody: Boolean = false,
override var packagesToScan: List<String> = emptyList(),
override var verificationType: PactVerification? = null,
override var pactSource: Any? = null,
override var pactFileAuthentication: List<Any?> = emptyList(),
override val notices: List<VerificationNotice> = mutableListOf(),
override val pending: Boolean = false,
override val wip: Boolean = false,
override val auth: Auth? = null
) : IConsumerInfo {
@Inject
constructor(name: String): this(name, null, false, emptyList(), null, null, emptyList())

override fun toPactConsumer() = Consumer(name)

override fun resolvePactSource() = ConsumerInfo.resolvePactSource(pactSource)
}
Loading

0 comments on commit 0afc954

Please sign in to comment.