Skip to content
This repository was archived by the owner on Aug 21, 2025. It is now read-only.

Commit f2bef3c

Browse files
alexjamesmacphersonjbwheatley
authored andcommitted
Only remove pb:consumer-versions if it exists; added tests to assert this behaviour.
1 parent eeb707b commit f2bef3c

File tree

4 files changed

+282
-27
lines changed

4 files changed

+282
-27
lines changed

scalapact-circe-0-13/src/main/scala/com/itv/scalapact/circe13/PactImplicits.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.itv.scalapact.circe13
22

33
import com.itv.scalapact.shared.Notice._
44
import com.itv.scalapact.shared._
5-
import io.circe.{Codec, Decoder, DecodingFailure, Encoder, Json, parser}
5+
import io.circe.{ACursor, Codec, Decoder, DecodingFailure, Encoder, HCursor, Json, parser}
66
import io.circe.generic.semiauto.{deriveCodec, deriveDecoder, deriveEncoder}
77
import io.circe.syntax._
88

@@ -81,19 +81,19 @@ object PactImplicits {
8181

8282
implicit val pactMetaDataDecoder: Codec[PactMetaData] = deriveCodec
8383

84+
private def sanitizeLinks(cursor: HCursor): ACursor = {
85+
val links: ACursor = cursor.downField("_links").downField("curies").delete
86+
if (links.keys.exists(_.toList.contains("pb:consumer-versions"))) links.downField("pb:consumer-versions").delete
87+
else links
88+
}
89+
8490
implicit val scalaPactDecoder: Decoder[Pact] = Decoder.instance { cur =>
8591
for {
8692
provider <- cur.get[PactActor]("provider")
8793
consumer <- cur.get[PactActor]("consumer")
8894
interactions <- cur.get[List[Interaction]]("interactions")
89-
_links <- cur
90-
.downField("_links")
91-
.downField("curies")
92-
.delete
93-
.downField("pb:consumer-versions")
94-
.delete
95-
.as[Option[Links]]
96-
metadata <- cur.get[Option[PactMetaData]]("metadata")
95+
_links <- sanitizeLinks(cur).as[Option[Links]]
96+
metadata <- cur.get[Option[PactMetaData]]("metadata")
9797
} yield Pact(provider, consumer, interactions, _links, metadata)
9898
}
9999

@@ -115,7 +115,7 @@ object PactImplicits {
115115
}
116116

117117
implicit val halIndexDecoder: Decoder[HALIndex] = Decoder.instance { cur =>
118-
cur.downField("_links").downField("curies").delete.downField("pb:consumer-versions").delete.as[Links].map(HALIndex)
118+
sanitizeLinks(cur).as[Links].map(HALIndex)
119119
}
120120

121121
implicit val embeddedPactsForVerificationDecoder: Decoder[EmbeddedPactsForVerification] = deriveDecoder

scalapact-circe-0-13/src/test/scala/com/itv/scalapact/circe13/ScalaPactReaderWriterSpec.scala

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,137 @@ class ScalaPactReaderWriterSpec extends AnyFunSpec with Matchers with OptionValu
115115
pactEither.toOption.value shouldEqual PactFileExamples.simpleWithLinksAndMetaData
116116
}
117117

118+
it("should remove curies and pb:consumer-versions from parsed _links") {
119+
val simpleWithCuriesAndPbConsumerVersionsAsString: String =
120+
"""{
121+
| "provider" : {
122+
| "name" : "provider"
123+
| },
124+
| "consumer" : {
125+
| "name" : "consumer"
126+
| },
127+
| "interactions" : [
128+
| {
129+
| "request" : {
130+
| "method" : "GET",
131+
| "body" : "fish",
132+
| "path" : "/fetch-json",
133+
| "matchingRules" : {
134+
| "$.headers.Accept" : {
135+
| "match" : "regex",
136+
| "regex" : "\\w+"
137+
| },
138+
| "$.headers.Content-Length" : {
139+
| "match" : "type"
140+
| }
141+
| },
142+
| "query" : "fish=chips",
143+
| "headers" : {
144+
| "Content-Type" : "text/plain"
145+
| }
146+
| },
147+
| "description" : "a simple request",
148+
| "response" : {
149+
| "status" : 200,
150+
| "headers" : {
151+
| "Content-Type" : "application/json"
152+
| },
153+
| "body" : {
154+
| "fish" : [
155+
| "cod",
156+
| "haddock",
157+
| "flying"
158+
| ]
159+
| },
160+
| "matchingRules" : {
161+
| "$.headers.Accept" : {
162+
| "match" : "regex",
163+
| "regex" : "\\w+"
164+
| },
165+
| "$.headers.Content-Length" : {
166+
| "match" : "type"
167+
| }
168+
| }
169+
| },
170+
| "providerState" : "a simple state"
171+
| },
172+
| {
173+
| "request" : {
174+
| "method" : "GET",
175+
| "body" : "fish",
176+
| "path" : "/fetch-json2",
177+
| "headers" : {
178+
| "Content-Type" : "text/plain"
179+
| }
180+
| },
181+
| "description" : "a simple request 2",
182+
| "response" : {
183+
| "status" : 200,
184+
| "headers" : {
185+
| "Content-Type" : "application/json"
186+
| },
187+
| "body" : {
188+
| "chips" : true,
189+
| "fish" : [
190+
| "cod",
191+
| "haddock"
192+
| ]
193+
| }
194+
| },
195+
| "providerState" : "a simple state 2"
196+
| }
197+
| ],
198+
| "_links": {
199+
| "self": {
200+
| "title": "Pact",
201+
| "name": "Pact between consumer (v1.0.0) and provider",
202+
| "href": "http://localhost/pacts/provider/provider/consumer/consumer/version/1.0.0"
203+
| },
204+
| "pb:consumer": {
205+
| "title": "Consumer",
206+
| "name": "consumer",
207+
| "href": "http://localhost/pacticipants/consumer"
208+
| },
209+
| "pb:provider": {
210+
| "title": "Provider",
211+
| "name": "provider",
212+
| "href": "http://localhost/pacticipants/provider"
213+
| },
214+
| "pb:latest-tagged-pact-version": {
215+
| "title": "Latest tagged version of this pact",
216+
| "href": "http://localhost/pacts/provider/provider-service/consumer/consumer-service/latest/{tag}",
217+
| "templated": true
218+
| },
219+
| "pb:consumer-versions": [
220+
| {
221+
| "title": "Consumer version",
222+
| "name": "1.2.3",
223+
| "href": "http://localhost/pacticipants/consumer/versions/1.2.3"
224+
| }
225+
| ],
226+
| "curies": [
227+
| {
228+
| "name": "pb",
229+
| "href": "http://localhost/doc/{rel}",
230+
| "templated": true
231+
| }
232+
| ]
233+
| },
234+
| "metadata": {
235+
| "pactSpecification": {
236+
| "version": "2.0.0"
237+
| },
238+
| "scala-pact": {
239+
| "version": "1.0.0"
240+
| }
241+
| }
242+
|}""".stripMargin
243+
244+
val pactEither = pactReader.jsonStringToScalaPact(simpleWithCuriesAndPbConsumerVersionsAsString)
245+
246+
pactEither.toOption.value shouldEqual PactFileExamples.simpleWithLinksAndMetaData
247+
}
248+
118249
it("should be able to write Pact files and add metadata when missing") {
119250

120251
val written = pactWriter.pactToJsonString(PactFileExamples.simple, scalaPactVersion)

scalapact-circe-0-14/src/main/scala/com/itv/scalapact/circe14/PactImplicits.scala

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.itv.scalapact.circe14
22

33
import com.itv.scalapact.shared.Notice._
44
import com.itv.scalapact.shared._
5-
import io.circe.{Codec, Decoder, DecodingFailure, Encoder, Json, parser}
5+
import io.circe.{ACursor, Codec, Decoder, DecodingFailure, Encoder, HCursor, Json, parser}
66
import io.circe.generic.semiauto.{deriveCodec, deriveDecoder, deriveEncoder}
77
import io.circe.syntax._
88

@@ -82,19 +82,19 @@ object PactImplicits {
8282

8383
implicit val pactMetaDataDecoder: Codec[PactMetaData] = deriveCodec
8484

85+
private def sanitizeLinks(cursor: HCursor): ACursor = {
86+
val links: ACursor = cursor.downField("_links").downField("curies").delete
87+
if (links.keys.exists(_.toList.contains("pb:consumer-versions"))) links.downField("pb:consumer-versions").delete
88+
else links
89+
}
90+
8591
implicit val scalaPactDecoder: Decoder[Pact] = Decoder.instance { cur =>
8692
for {
8793
provider <- cur.get[PactActor]("provider")
8894
consumer <- cur.get[PactActor]("consumer")
8995
interactions <- cur.get[List[Interaction]]("interactions")
90-
_links <- cur
91-
.downField("_links")
92-
.downField("curies")
93-
.delete
94-
.downField("pb:consumer-versions")
95-
.delete
96-
.as[Option[Links]]
97-
metadata <- cur.get[Option[PactMetaData]]("metadata")
96+
_links <- sanitizeLinks(cur).as[Option[Links]]
97+
metadata <- cur.get[Option[PactMetaData]]("metadata")
9898
} yield Pact(provider, consumer, interactions, _links, metadata)
9999
}
100100

@@ -116,14 +116,7 @@ object PactImplicits {
116116
}
117117

118118
implicit val halIndexDecoder: Decoder[HALIndex] = Decoder.instance { cur =>
119-
cur
120-
.downField("_links")
121-
.downField("curies")
122-
.delete
123-
.downField("pb:consumer-versions")
124-
.delete
125-
.as[Links]
126-
.map(HALIndex.apply)
119+
sanitizeLinks(cur).as[Links].map(HALIndex.apply)
127120
}
128121

129122
implicit val embeddedPactForVerificationDecoder: Decoder[PactForVerification] = deriveDecoder

scalapact-circe-0-14/src/test/scala/com/itv/scalapact/circe14/ScalaPactReaderWriterSpec.scala

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,137 @@ class ScalaPactReaderWriterSpec extends AnyFunSpec with Matchers with OptionValu
115115
pactEither.toOption.value shouldEqual PactFileExamples.simpleWithLinksAndMetaData
116116
}
117117

118+
it("should remove curies and pb:consumer-versions from parsed _links") {
119+
val simpleWithCuriesAndPbConsumerVersionsAsString: String =
120+
"""{
121+
| "provider" : {
122+
| "name" : "provider"
123+
| },
124+
| "consumer" : {
125+
| "name" : "consumer"
126+
| },
127+
| "interactions" : [
128+
| {
129+
| "request" : {
130+
| "method" : "GET",
131+
| "body" : "fish",
132+
| "path" : "/fetch-json",
133+
| "matchingRules" : {
134+
| "$.headers.Accept" : {
135+
| "match" : "regex",
136+
| "regex" : "\\w+"
137+
| },
138+
| "$.headers.Content-Length" : {
139+
| "match" : "type"
140+
| }
141+
| },
142+
| "query" : "fish=chips",
143+
| "headers" : {
144+
| "Content-Type" : "text/plain"
145+
| }
146+
| },
147+
| "description" : "a simple request",
148+
| "response" : {
149+
| "status" : 200,
150+
| "headers" : {
151+
| "Content-Type" : "application/json"
152+
| },
153+
| "body" : {
154+
| "fish" : [
155+
| "cod",
156+
| "haddock",
157+
| "flying"
158+
| ]
159+
| },
160+
| "matchingRules" : {
161+
| "$.headers.Accept" : {
162+
| "match" : "regex",
163+
| "regex" : "\\w+"
164+
| },
165+
| "$.headers.Content-Length" : {
166+
| "match" : "type"
167+
| }
168+
| }
169+
| },
170+
| "providerState" : "a simple state"
171+
| },
172+
| {
173+
| "request" : {
174+
| "method" : "GET",
175+
| "body" : "fish",
176+
| "path" : "/fetch-json2",
177+
| "headers" : {
178+
| "Content-Type" : "text/plain"
179+
| }
180+
| },
181+
| "description" : "a simple request 2",
182+
| "response" : {
183+
| "status" : 200,
184+
| "headers" : {
185+
| "Content-Type" : "application/json"
186+
| },
187+
| "body" : {
188+
| "chips" : true,
189+
| "fish" : [
190+
| "cod",
191+
| "haddock"
192+
| ]
193+
| }
194+
| },
195+
| "providerState" : "a simple state 2"
196+
| }
197+
| ],
198+
| "_links": {
199+
| "self": {
200+
| "title": "Pact",
201+
| "name": "Pact between consumer (v1.0.0) and provider",
202+
| "href": "http://localhost/pacts/provider/provider/consumer/consumer/version/1.0.0"
203+
| },
204+
| "pb:consumer": {
205+
| "title": "Consumer",
206+
| "name": "consumer",
207+
| "href": "http://localhost/pacticipants/consumer"
208+
| },
209+
| "pb:provider": {
210+
| "title": "Provider",
211+
| "name": "provider",
212+
| "href": "http://localhost/pacticipants/provider"
213+
| },
214+
| "pb:latest-tagged-pact-version": {
215+
| "title": "Latest tagged version of this pact",
216+
| "href": "http://localhost/pacts/provider/provider-service/consumer/consumer-service/latest/{tag}",
217+
| "templated": true
218+
| },
219+
| "pb:consumer-versions": [
220+
| {
221+
| "title": "Consumer version",
222+
| "name": "1.2.3",
223+
| "href": "http://localhost/pacticipants/consumer/versions/1.2.3"
224+
| }
225+
| ],
226+
| "curies": [
227+
| {
228+
| "name": "pb",
229+
| "href": "http://localhost/doc/{rel}",
230+
| "templated": true
231+
| }
232+
| ]
233+
| },
234+
| "metadata": {
235+
| "pactSpecification": {
236+
| "version": "2.0.0"
237+
| },
238+
| "scala-pact": {
239+
| "version": "1.0.0"
240+
| }
241+
| }
242+
|}""".stripMargin
243+
244+
val pactEither = pactReader.jsonStringToScalaPact(simpleWithCuriesAndPbConsumerVersionsAsString)
245+
246+
pactEither.toOption.value shouldEqual PactFileExamples.simpleWithLinksAndMetaData
247+
}
248+
118249
it("should be able to write Pact files and add metadata when missing") {
119250

120251
val written = pactWriter.pactToJsonString(PactFileExamples.simple, scalaPactVersion)

0 commit comments

Comments
 (0)