Skip to content

Commit

Permalink
feat: Allow reusing common DSL parts in different LambdaDslJsonBody o…
Browse files Browse the repository at this point in the history
…bjects #1796
  • Loading branch information
rholshausen committed May 22, 2024
1 parent 4752365 commit 9d46d36
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,16 @@ public static LambdaDslJsonBody newJsonBody(Consumer<LambdaDslJsonBody> array) {
array.accept(dslBody);
return dslBody;
}

/**
* DSL function to simplify creating a {@link DslPart} generated from a {@link LambdaDslJsonBody}. This takes a
* base template to copy the attributes from.
*/
public static LambdaDslJsonBody newJsonBody(LambdaDslJsonBody baseTemplate, Consumer<LambdaDslJsonBody> array) {
final PactDslJsonBody pactDslJsonBody = new PactDslJsonBody();
pactDslJsonBody.extendFrom((PactDslJsonBody) baseTemplate.build());
final LambdaDslJsonBody dslBody = new LambdaDslJsonBody(pactDslJsonBody);
array.accept(dslBody);
return dslBody;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ fun newJsonObject(kClass: KClass<*>): DslPart {
return LambdaDsl.newJsonBody(DslJsonBodyBuilder().basedOnRequiredConstructorFields(kClass)).build()
}

/**
* DSL function to simplify creating a [DslPart] generated from a [LambdaDslJsonBody]. The new object is
* extended from a base template object.
*/
fun newJsonObject(baseTemplate: DslPart, function: LambdaDslJsonBody.() -> Unit): DslPart {
require(baseTemplate is PactDslJsonBody) { "baseTemplate must be a PactDslJsonBody" }
val dslBody = LambdaDslJsonBody(baseTemplate.asBody())
return LambdaDsl.newJsonBody(dslBody) { it.function() }.build()
}

/**
* DSL function to simplify creating a [DslPart] generated from a [LambdaDslJsonArray].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2191,4 +2191,25 @@ open class PactDslJsonBody : DslPart {
override fun arrayContaining(name: String): DslPart {
return PactDslJsonArrayContaining(rootPath, name, this)
}

/**
* Extends this JSON object from a base template.
*/
fun extendFrom(baseTemplate: PactDslJsonBody) {
this.body = copyBody(baseTemplate.body)
matchers = baseTemplate.matchers.copyWithUpdatedMatcherRootPrefix("")
generators = baseTemplate.generators.copyWithUpdatedMatcherRootPrefix("")
}

// TODO: Replace this with JsonValue.copy in the next major version
private fun copyBody(body: JsonValue): JsonValue {
return when (body) {
is JsonValue.Array -> JsonValue.Array(body.values.map { it.copy() }.toMutableList())
is JsonValue.Decimal -> JsonValue.Decimal(body.value.chars)
is JsonValue.Integer -> JsonValue.Integer(body.value.chars)
is JsonValue.Object -> JsonValue.Object(body.entries.mapValues { it.value.copy() }.toMutableMap())
is JsonValue.StringValue -> JsonValue.StringValue(body.value.chars)
else -> body
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package au.com.dius.pact.consumer.dsl;

import au.com.dius.pact.core.model.PactSpecVersion;
import au.com.dius.pact.core.model.matchingrules.MatchingRuleCategory;
import au.com.dius.pact.core.model.matchingrules.MatchingRuleGroup;
import au.com.dius.pact.core.model.matchingrules.NumberTypeMatcher;
import au.com.dius.pact.core.model.matchingrules.NullMatcher;
import au.com.dius.pact.core.model.matchingrules.TypeMatcher;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
Expand All @@ -16,7 +21,6 @@
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;

Expand Down Expand Up @@ -1070,4 +1074,46 @@ public void testArrayContains() {
Map<String, Object> variant2Matcher = (Map<String, Object>) ((List) map2.get("matchers")).get(0);
assertThat(variant2Matcher, hasEntry("match", "number"));
}

// Issue #1796
@Test
public void allowDslToBExtendedFromACommonBase() {
LambdaDslJsonBody base = newJsonBody(o -> {
o.stringType("a", "foo");
o.id("b", 0L);
o.integerType("c", 0);
o.booleanType("d", false);
});
LambdaDslJsonBody y = newJsonBody(base, o -> {
o.stringType("e", "bar");
});
LambdaDslJsonBody z = newJsonBody(base, o -> {
o.nullValue("e");
});

String expectedY = "{\"a\":\"foo\",\"b\":0,\"c\":0,\"d\":false,\"e\":\"bar\"}";
Map<String, MatchingRuleGroup> yRules = Map.of(
"$.a", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE)),
"$.b", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE)),
"$.c", new MatchingRuleGroup(List.of(new NumberTypeMatcher(NumberTypeMatcher.NumberType.INTEGER))),
"$.d", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE)),
"$.e", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE))
);
MatchingRuleCategory expectedYMatchers = new MatchingRuleCategory("body", yRules);
String expectedZ = "{\"a\":\"foo\",\"b\":0,\"c\":0,\"d\":false,\"e\":null}";
Map<String, MatchingRuleGroup> zRules = Map.of(
"$.a", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE)),
"$.b", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE)),
"$.c", new MatchingRuleGroup(List.of(new NumberTypeMatcher(NumberTypeMatcher.NumberType.INTEGER))),
"$.d", new MatchingRuleGroup(List.of(TypeMatcher.INSTANCE))
);
MatchingRuleCategory expectedZMatchers = new MatchingRuleCategory("body", zRules);

DslPart yPart = y.build();
assertThat(yPart.getBody().toString(), is(expectedY));
assertThat(yPart.getMatchers(), is(expectedYMatchers));
DslPart zPart = z.build();
assertThat(zPart.getBody().toString(), is(expectedZ));
assertThat(zPart.getMatchers(), is(expectedZMatchers));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package au.com.dius.pact.consumer.dsl

import au.com.dius.pact.core.model.matchingrules.MatchingRuleCategory
import au.com.dius.pact.core.model.matchingrules.MatchingRuleGroup
import au.com.dius.pact.core.model.matchingrules.NumberTypeMatcher
import au.com.dius.pact.core.model.matchingrules.TypeMatcher
import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.Test
import java.util.List
import java.util.Map

class ExtensionsTest {
@Test
Expand Down Expand Up @@ -51,4 +58,44 @@ class ExtensionsTest {

assertThat(actualJson, equalTo(expectedJson))
}

// Issue #1796
@Test
fun `allow dsl to be extended from a common base`() {
val x = newJsonObject {
stringType("a", "foo")
id("b", 0L)
integerType("c", 0)
booleanType("d", false)
}
val y = newJsonObject(x) {
stringType("e", "bar")
}
val z = newJsonObject(x) {
nullValue("e")
}

val expectedY = "{\"a\":\"foo\",\"b\":0,\"c\":0,\"d\":false,\"e\":\"bar\"}"
val yRules = Map.of(
"$.a", MatchingRuleGroup(List.of(TypeMatcher)),
"$.b", MatchingRuleGroup(List.of(TypeMatcher)),
"$.c", MatchingRuleGroup(List.of(NumberTypeMatcher(NumberTypeMatcher.NumberType.INTEGER))),
"$.d", MatchingRuleGroup(List.of(TypeMatcher)),
"$.e", MatchingRuleGroup(List.of(TypeMatcher))
)
val expectedYMatchers = MatchingRuleCategory("body", yRules)
val expectedZ = "{\"a\":\"foo\",\"b\":0,\"c\":0,\"d\":false,\"e\":null}"
val zRules = Map.of(
"$.a", MatchingRuleGroup(List.of(TypeMatcher)),
"$.b", MatchingRuleGroup(List.of(TypeMatcher)),
"$.c", MatchingRuleGroup(List.of(NumberTypeMatcher(NumberTypeMatcher.NumberType.INTEGER))),
"$.d", MatchingRuleGroup(List.of(TypeMatcher))
)
val expectedZMatchers = MatchingRuleCategory("body", zRules)

assertThat(y.body.toString(), CoreMatchers.`is`(expectedY))
assertThat(y.matchers, CoreMatchers.`is`(expectedYMatchers))
assertThat(z.body.toString(), CoreMatchers.`is`(expectedZ))
assertThat(z.matchers, CoreMatchers.`is`(expectedZMatchers))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ sealed class JsonValue {
constructor(value: CharArray) : this(JsonToken.Integer(value))
constructor(value: Int) : this(JsonToken.Integer(value.toString().toCharArray()))
fun toBigInteger() = String(this.value.chars).toBigInteger()

override fun copy() = Integer(value.chars)
}

class Decimal(val value: JsonToken.Decimal) : JsonValue() {
constructor(value: CharArray) : this(JsonToken.Decimal(value))
constructor(value: Number) : this(JsonToken.Decimal(value.toString().toCharArray()))
fun toBigDecimal() = String(this.value.chars).toBigDecimal()

override fun copy() = Decimal(value.chars)
}

class StringValue(val value: JsonToken.StringValue) : JsonValue() {
Expand All @@ -34,11 +38,21 @@ sealed class JsonValue {
result = 31 * result + value.hashCode()
return result
}

override fun copy() = StringValue(value.chars)
}

object True : JsonValue() {
override fun copy() = True
}

object False : JsonValue() {
override fun copy() = False
}

object True : JsonValue()
object False : JsonValue()
object Null : JsonValue()
object Null : JsonValue() {
override fun copy() = Null
}

class Array @JvmOverloads constructor (val values: MutableList<JsonValue> = mutableListOf()) : JsonValue() {
fun find(function: (JsonValue) -> Boolean) = values.find(function)
Expand Down Expand Up @@ -69,14 +83,19 @@ sealed class JsonValue {
}

companion object {
fun of(vararg value: JsonValue) = JsonValue.Array(value.toMutableList())
fun of(vararg value: JsonValue) = Array(value.toMutableList())
}

override fun copy() = Array(values.map { it.copy() }.toMutableList())
}

class Object @JvmOverloads constructor (val entries: MutableMap<String, JsonValue> = mutableMapOf()) : JsonValue() {
constructor(vararg values: Pair<String, JsonValue>) : this(values.associate { it }.toMutableMap())
operator fun get(name: String) = entries[name] ?: Null
override fun has(field: String) = entries.containsKey(field)

override fun copy() = Object(entries.mapValues { it.value.copy() }.toMutableMap())

operator fun set(key: String, value: Any?) {
entries[key] = Json.toJson(value)
}
Expand Down Expand Up @@ -313,6 +332,11 @@ sealed class JsonValue {
throw UnsupportedOperationException("Can not downcast ${this.name} to type ${T::class}")
}
}

/**
* Makes a copy of this JSON value
*/
abstract fun copy(): JsonValue
}

fun <R> JsonValue?.map(transform: (JsonValue) -> R): List<R> = when {
Expand Down

0 comments on commit 9d46d36

Please sign in to comment.