Skip to content

Commit d5614b5

Browse files
Fixes #207: tagging provider verifications (#208)
* Fixes #207: tagging provider verifications * Fix compilation. Add some test coverage.
1 parent 916b377 commit d5614b5

File tree

5 files changed

+162
-38
lines changed

5 files changed

+162
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ credentials.sbt
2020
*.html
2121

2222
# IDE
23+
.bsp
2324
.idea
2425
.idea_modules
2526
*.iml

scalapact-core/src/main/scala/com/itv/scalapactcore/common/PactBrokerClient.scala

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -201,42 +201,98 @@ class PactBrokerClient(implicit
201201
def publishVerificationResults(
202202
pactVerifyResults: List[PactVerifyResult],
203203
brokerPublishData: BrokerPublishData,
204+
providerVersionTags: List[String],
204205
pactBrokerAuthorization: Option[PactBrokerAuthorization],
205206
brokerClientTimeout: Option[Duration],
206207
sslContextName: Option[String]
207208
): Unit = {
208209
val httpClient = httpClientBuilder.build(brokerClientTimeout.getOrElse(2.seconds), sslContextName, 2)
209210
pactVerifyResults.foreach { result =>
210-
result.pact._links.flatMap(_.get("pb:publish-verification-results")).map(_.href) match {
211-
case Some(link) =>
212-
val success = !result.results.exists(_.result.isLeft)
213-
val request = SimpleRequest(
214-
link,
215-
"",
216-
HttpMethod.POST,
217-
Map("Content-Type" -> "application/json; charset=UTF-8") ++ pactBrokerAuthorization.map(_.asHeader).toList,
218-
body(brokerPublishData, success),
219-
None
220-
)
221-
httpClient.doRequest(request) match {
222-
case Right(response) =>
223-
if (response.is2xx) {
224-
PactLogger.message(
225-
s"Verification results published for provider '${result.pact.provider.name}' and consumer '${result.pact.consumer.name}'"
226-
)
227-
} else {
228-
PactLogger.error(prettifyBrokerError("Publish verification results failed.", response))
229-
}
230-
case Left(err) => PactLogger.error(s"Unable to publish verification results: $err".red)
231-
}
232-
case None =>
233-
PactLogger.error(
234-
"Unable to publish verification results as there is no pb:publish-verification-results link".red
211+
val publishedVerificationTags = result.pact._links
212+
.flatMap(_.get("pb:provider"))
213+
.map(_.href)
214+
.map { providerUrl =>
215+
publishVerificationResultTags(
216+
httpClient,
217+
providerUrl,
218+
brokerPublishData.providerVersion,
219+
providerVersionTags,
220+
pactBrokerAuthorization
235221
)
222+
}
223+
.getOrElse {
224+
PactLogger.error("Unable to publish verification results as there is no pb:provider link".red)
225+
false
226+
}
227+
228+
if (publishedVerificationTags) {
229+
result.pact._links.flatMap(_.get("pb:publish-verification-results")).map(_.href) match {
230+
case Some(link) =>
231+
val success = !result.results.exists(_.result.isLeft)
232+
val request = SimpleRequest(
233+
link,
234+
"",
235+
HttpMethod.POST,
236+
Map("Content-Type" -> "application/json; charset=UTF-8") ++ pactBrokerAuthorization
237+
.map(_.asHeader)
238+
.toList,
239+
body(brokerPublishData, success),
240+
None
241+
)
242+
httpClient.doRequest(request) match {
243+
case Right(response) =>
244+
if (response.is2xx) {
245+
PactLogger.message(
246+
s"Verification results published for provider '${result.pact.provider.name}' and consumer '${result.pact.consumer.name}'"
247+
)
248+
} else {
249+
PactLogger.error(prettifyBrokerError("Publish verification results failed.", response))
250+
}
251+
case Left(err) => PactLogger.error(s"Unable to publish verification results: $err".red)
252+
}
253+
case None =>
254+
PactLogger.error(
255+
"Unable to publish verification results as there is no pb:publish-verification-results link".red
256+
)
257+
}
236258
}
237259
}
238260
}
239261

262+
private def publishVerificationResultTags(
263+
client: IScalaPactHttpClient,
264+
providerUrl: String,
265+
providerVersion: String,
266+
providerVersionTags: List[String],
267+
pactBrokerAuthorization: Option[PactBrokerAuthorization]
268+
): Boolean = {
269+
val tagResponses = providerVersionTags
270+
.map { tag =>
271+
val response = client.doRequest(
272+
SimpleRequest(
273+
baseUrl = providerUrl + "/versions/" + providerVersion + "/tags/" + URLEncoder.encode(tag, "UTF-8"),
274+
endPoint = "",
275+
method = HttpMethod.POST,
276+
headers = Map("Content-Type" -> "application/json; charset=UTF-8") ++ pactBrokerAuthorization
277+
.map(_.asHeader)
278+
.toList,
279+
body = None,
280+
sslContextName = None
281+
)
282+
)
283+
response match {
284+
case Left(e) =>
285+
PactLogger.error(s"Unable to tag verification result: ${e.getMessage}".red)
286+
case Right(r) if !r.is2xx =>
287+
PactLogger.error(prettifyBrokerError("Tagging of verification results failed.", r))
288+
case Right(_) =>
289+
PactLogger.message(s"Created tag $tag for provider version $providerVersion.")
290+
}
291+
response
292+
}
293+
tagResponses.forall(_.exists(_.is2xx))
294+
}
295+
240296
private def body(brokerPublishData: BrokerPublishData, success: Boolean): Option[String] = {
241297
val buildUrl = brokerPublishData.buildUrl.fold("")(u => s""", "buildUrl": "$u"""")
242298
Some(s"""{ "success": $success, "providerApplicationVersion": "${brokerPublishData.providerVersion}"$buildUrl }""")

scalapact-core/src/main/scala/com/itv/scalapactcore/verifier/PactsForVerificationVerifier.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ private[verifier] class PactsForVerificationVerifier(
4242
pactBrokerClient.publishVerificationResults(
4343
resultsAndProperties.map(_._1),
4444
publishData,
45+
verificationSettings.providerVersionTags,
4546
verificationSettings.pactBrokerAuthorization,
4647
verificationSettings.pactBrokerClientTimeout,
4748
verificationSettings.sslContextName

scalapact-core/src/main/scala/com/itv/scalapactcore/verifier/PrePactsForVerificationVerifier.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ private[verifier] class PrePactsForVerificationVerifier(
5757

5858
scalaPactSettings.publishResultsEnabled.foreach { publishData =>
5959
brokerClient.publishVerificationResults(
60-
pactVerifyResults,
61-
publishData,
62-
pactVerifySettings.pactBrokerAuthorization,
63-
pactVerifySettings.pactBrokerClientTimeout,
64-
pactVerifySettings.sslContextName
60+
pactVerifyResults = pactVerifyResults,
61+
brokerPublishData = publishData,
62+
providerVersionTags = Nil,
63+
pactBrokerAuthorization = pactVerifySettings.pactBrokerAuthorization,
64+
brokerClientTimeout = pactVerifySettings.pactBrokerClientTimeout,
65+
sslContextName = pactVerifySettings.sslContextName
6566
)
6667
}
6768

scalapact-core/src/test/scala/com/itv/scalapactcore/common/PactBrokerClientSpec.scala

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.itv.scalapact.shared.http.{
1111
import com.itv.scalapact.shared.json.{IPactReader, IPactWriter}
1212
import org.scalatest.{BeforeAndAfter, FunSpec, Matchers}
1313

14+
import java.net.URLEncoder
1415
import scala.collection.mutable.ArrayBuffer
1516
import scala.concurrent.duration.Duration
1617

@@ -42,9 +43,19 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
4243
metadata = None
4344
)
4445

46+
private val providerUrl = "http://localhost/pacticipants/provider-service"
47+
def getProviderUrl(providerVersion: String, tag: String): String =
48+
providerUrl + "/versions/" + providerVersion + "/tags/" + URLEncoder.encode(tag, "UTF-8")
49+
4550
private val publishUrl = "http://localhost/pacts/provider/provider-service/consumer/consumer-service/latest/{tag}"
4651

4752
private val _links = Map(
53+
"pb:provider" -> LinkValues(
54+
title = Option("Provider url"),
55+
name = None,
56+
href = providerUrl,
57+
templated = Option(true)
58+
),
4859
"pb:publish-verification-results" -> LinkValues(
4960
title = Option("Publish result url"),
5061
name = None,
@@ -85,12 +96,13 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
8596
val failedResult = PactVerifyResultInContext(Left("failed"), "context")
8697
val failedResults = List(successfulResult, failedResult)
8798
val pactVerifyResults = List(PactVerifyResult(simpleWithLinks, successfulResults))
99+
val providerVersionTags = List("master")
88100

89101
it("should publish successful results") {
90102
val results = successfulResults
91103
val pactVerifyResults = List(PactVerifyResult(simpleWithLinks, results))
92104

93-
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, None, None, None)
105+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, Nil, None, None, None)
94106

95107
val successfulRequest = SimpleRequest(
96108
publishUrl,
@@ -103,11 +115,43 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
103115
requests shouldBe ArrayBuffer(successfulRequest)
104116
}
105117

118+
it("should publish successful results with provider version tags") {
119+
val results = successfulResults
120+
val pactVerifyResults = List(PactVerifyResult(simpleWithLinks, results))
121+
122+
resultPublisher.publishVerificationResults(
123+
pactVerifyResults,
124+
brokerPublishData,
125+
providerVersionTags,
126+
pactBrokerAuthorization = None,
127+
brokerClientTimeout = None,
128+
sslContextName = None
129+
)
130+
131+
val successfulTagRequest = SimpleRequest(
132+
baseUrl = getProviderUrl(brokerPublishData.providerVersion, providerVersionTags.head),
133+
endPoint = "",
134+
method = HttpMethod.POST,
135+
headers = Map("Content-Type" -> "application/json; charset=UTF-8"),
136+
body = None,
137+
sslContextName = None
138+
)
139+
val successfulVerificationRequest = SimpleRequest(
140+
publishUrl,
141+
"",
142+
HttpMethod.POST,
143+
Map("Content-Type" -> "application/json; charset=UTF-8"),
144+
Option("""{ "success": true, "providerApplicationVersion": "1.0.0", "buildUrl": "http://buildUrl.com" }"""),
145+
None
146+
)
147+
requests shouldBe ArrayBuffer(successfulTagRequest, successfulVerificationRequest)
148+
}
149+
106150
it("should publish successful results without buildUrl") {
107151
val results = successfulResults
108152
val pactVerifyResults = List(PactVerifyResult(simpleWithLinks, results))
109153

110-
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishDataNoBuildUrl, None, None, None)
154+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishDataNoBuildUrl, Nil, None, None, None)
111155

112156
val successfulRequest = SimpleRequest(
113157
publishUrl,
@@ -123,7 +167,7 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
123167
it("should publish failure results") {
124168
val pactVerifyResults = List(PactVerifyResult(simpleWithLinks, failedResults))
125169

126-
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, None, None, None)
170+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, Nil, None, None, None)
127171

128172
val failedRequest = SimpleRequest(
129173
publishUrl,
@@ -136,11 +180,30 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
136180
requests shouldBe ArrayBuffer(failedRequest)
137181
}
138182

139-
it("should not publish if no _links available") {
140-
val results = successfulResults
141-
val pactVerifyResults = List(PactVerifyResult(simple, results))
183+
it("should not publish if no pb:publish-verification-results available in _links") {
184+
val results = successfulResults
185+
val pactVerifyResults = List(
186+
PactVerifyResult(
187+
simple.copy(_links = Option(_links.filterNot(_._1 == "pb:publish-verification-results"))),
188+
results
189+
)
190+
)
191+
192+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, Nil, None, None, None)
193+
194+
requests shouldBe ArrayBuffer.empty[SimpleRequest]
195+
}
196+
197+
it("should not publish if no pb:provider available in _links") {
198+
val results = successfulResults
199+
val pactVerifyResults = List(
200+
PactVerifyResult(
201+
simple.copy(_links = Option(_links.filterNot(_._1 == "pb:provider"))),
202+
results
203+
)
204+
)
142205

143-
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, None, None, None)
206+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, Nil, None, None, None)
144207

145208
requests shouldBe ArrayBuffer.empty[SimpleRequest]
146209
}
@@ -153,6 +216,7 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
153216
resultPublisher.publishVerificationResults(
154217
pactVerifyResults,
155218
brokerPublishData,
219+
Nil,
156220
PactBrokerAuthorization(("username", "password"), ""),
157221
None,
158222
None
@@ -176,6 +240,7 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
176240
resultPublisher.publishVerificationResults(
177241
pactVerifyResults,
178242
brokerPublishData,
243+
Nil,
179244
PactBrokerAuthorization(("", ""), token),
180245
None,
181246
None
@@ -193,7 +258,7 @@ class PactBrokerClientSpec extends FunSpec with Matchers with BeforeAndAfter {
193258
}
194259

195260
it("should have no Authorization header when no auth config is provided") {
196-
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, None, None, None)
261+
resultPublisher.publishVerificationResults(pactVerifyResults, brokerPublishData, Nil, None, None, None)
197262

198263
val successfulRequest = SimpleRequest(
199264
publishUrl,

0 commit comments

Comments
 (0)