Skip to content

Commit f449da5

Browse files
samukcerholshausen
authored andcommitted
feat: protect json body generation against loop for cicly reference
1 parent e256c0c commit f449da5

File tree

3 files changed

+99
-8
lines changed

3 files changed

+99
-8
lines changed

consumer/java8/src/main/kotlin/io/pactfoundation/consumer/dsl/DslJsonBodyBuilder.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@ class DslJsonBodyBuilder {
1414
}
1515

1616
/**
17-
* Build a {@link LambdaDslJsonBody} based on the Data Object required constructor fields
17+
* Build a {@link LambdaDslJsonBody} based on the required constructor fields
1818
*/
1919
fun basedOnRequiredConstructorFields(kClass: KClass<*>): (LambdaDslJsonBody) -> Unit =
2020
{ root: LambdaDslJsonBody ->
2121
root.run {
2222
val constructor = kClass.primaryConstructor
23-
fillBasedOnConstructorFields(constructor, root)
23+
fillBasedOnConstructorFields(constructor, root, setOf(kClass))
2424
}
2525
}
2626

2727
private fun fillBasedOnConstructorFields(
2828
constructor: KFunction<Any>?,
29-
root: LambdaDslObject
29+
root: LambdaDslObject,
30+
alreadyProcessedObject: Set<KClass<*>> = setOf()
3031
) {
3132
constructor?.parameters?.filterNot { it.isOptional }?.forEach {
3233
when (val baseField = it.type.jvmErasure) {
@@ -45,10 +46,13 @@ class DslJsonBodyBuilder {
4546
else ->
4647
root.`object`(it.name) { objDsl ->
4748
objDsl.run {
48-
fillBasedOnConstructorFields(
49-
baseField.primaryConstructor,
50-
objDsl
51-
)
49+
if (!alreadyProcessedObject.contains(baseField)){
50+
fillBasedOnConstructorFields(
51+
baseField.primaryConstructor,
52+
objDsl,
53+
alreadyProcessedObject + baseField
54+
)
55+
}
5256
}
5357
}
5458
}

consumer/java8/src/main/kotlin/io/pactfoundation/consumer/dsl/Extensions.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ fun newJsonObject(body: LambdaDslJsonBody.() -> Unit): DslPart {
1212
}
1313

1414
/**
15-
* DSL function to simplify creating a [DslPart] generated from a [LambdaDslJsonBody].
15+
* DSL function to simplify creating a [DslPart] generated from a [LambdaDslJsonBody]
16+
* based on a required constructor fields for a give [KClass].
1617
*/
1718
fun newJsonObject(kClass: KClass<*>): DslPart {
1819
return LambdaDsl.newJsonBody(DslJsonBodyBuilder().basedOnRequiredConstructorFields(kClass)).build()

consumer/java8/src/test/kotlin/io/pactfoundation/consumer/dsl/DslJsonBodyBuilderTest.kt

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,92 @@ internal class DslJsonBodyBuilderTest {
118118
.isEqualTo(expectedBody.pactDslObject.toString())
119119
}
120120

121+
@Test
122+
internal fun `should map inner object multiple occurrences`() {
123+
data class InnerObjectRequiredProperty(val property: String)
124+
data class ObjectRequiredProperty(val inner: InnerObjectRequiredProperty,
125+
val second: InnerObjectRequiredProperty)
126+
127+
val actualJsonBody = LambdaDsl.newJsonBody(basedOnConstructor(ObjectRequiredProperty::class))
128+
129+
val expectedBody =
130+
LambdaDsl.newJsonBody { root ->
131+
root.`object`("inner") {
132+
it.stringType("property")
133+
}
134+
root.`object`("second") {
135+
it.stringType("property")
136+
}
137+
}
138+
139+
assertThat(actualJsonBody.pactDslObject.toString())
140+
.isEqualTo(expectedBody.pactDslObject.toString())
141+
}
142+
143+
data class InnerObjectRequiredProperty(val property: ObjectRequiredProperty)
144+
data class ObjectRequiredProperty(val inner: InnerObjectRequiredProperty)
145+
@Test
146+
internal fun `should map inner object with loop reference for the first level`() {
147+
val actualJsonBody = LambdaDsl.newJsonBody(basedOnConstructor(ObjectRequiredProperty::class))
148+
149+
val expectedBody =
150+
LambdaDsl.newJsonBody { root ->
151+
root.`object`("inner") { it.`object`("property") { } }
152+
}
153+
154+
assertThat(actualJsonBody.pactDslObject.toString())
155+
.isEqualTo(expectedBody.pactDslObject.toString())
156+
}
157+
158+
data class ThirdProperty(val dependOnFirst: FirstProperty, val property: String)
159+
data class SecondProperty(val third: ThirdProperty)
160+
data class FirstProperty(val second: SecondProperty)
161+
@Test
162+
internal fun `should map inner object with loop reference keeping other fields`() {
163+
val actualJsonBody = LambdaDsl.newJsonBody(basedOnConstructor(FirstProperty::class))
164+
165+
val expectedBody =
166+
LambdaDsl.newJsonBody { root ->
167+
root.`object`("second") {
168+
it.`object`("third") { loop ->
169+
loop.`object`("dependOnFirst") { }
170+
loop.stringType("property")
171+
}
172+
}
173+
}
174+
175+
assertThat(actualJsonBody.pactDslObject.toString())
176+
.isEqualTo(expectedBody.pactDslObject.toString())
177+
}
178+
179+
@Test
180+
internal fun `should map inner object reusing the same class internally`() {
181+
data class CommonClass(val name: String)
182+
data class ThirdToUseCommonProperty(val common: CommonClass)
183+
data class SecondToUseCommonProperty(val third: ThirdToUseCommonProperty, val common: CommonClass)
184+
data class FirstToUseCommonProperty(val second: SecondToUseCommonProperty)
185+
186+
val actualJsonBody = LambdaDsl.newJsonBody(basedOnConstructor(FirstToUseCommonProperty::class))
187+
188+
val expectedBody =
189+
LambdaDsl.newJsonBody { root ->
190+
root.`object`("second") {
191+
it.`object`("third") { loop ->
192+
loop.`object`("common") { third -> third.stringType("name") }
193+
}
194+
it.`object`("common") { loop ->
195+
loop.stringType("name")
196+
}
197+
}
198+
}
199+
200+
assertThat(actualJsonBody.pactDslObject.toString())
201+
.isEqualTo(expectedBody.pactDslObject.toString())
202+
}
203+
121204
companion object {
122205
@JvmStatic
206+
@Suppress("UnusedPrivateMember")
123207
private fun numberPropertyNonOptional(): Stream<KClass<*>> {
124208
data class ByteObjectNonRequiredProperty(val property: Byte)
125209
data class ShortObjectNonRequiredProperty(val property: Short)
@@ -141,6 +225,7 @@ internal class DslJsonBodyBuilderTest {
141225
}
142226

143227
@JvmStatic
228+
@Suppress("UnusedPrivateMember")
144229
private fun stringPropertyOptionalProperties(): Stream<KClass<*>> {
145230
data class StringObjectNonRequiredPropertyImmutable(val property: String = "")
146231
data class StringObjectNonRequiredPropertyMutable(var property: String = "")
@@ -152,6 +237,7 @@ internal class DslJsonBodyBuilderTest {
152237
}
153238

154239
@JvmStatic
240+
@Suppress("UnusedPrivateMember")
155241
private fun stringPropertyNonOptionalProperties(): Stream<KClass<*>> {
156242
data class StringObjectRequiredPropertyImmutable(val property: String)
157243
data class StringObjectRequiredPropertyMutable(var property: String)

0 commit comments

Comments
 (0)