diff --git a/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt b/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt index 1624ab46d3..e735934470 100644 --- a/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt +++ b/core/model/src/main/kotlin/au/com/dius/pact/core/model/PactReader.kt @@ -10,6 +10,7 @@ import au.com.dius.pact.core.support.Auth import au.com.dius.pact.core.support.CustomServiceUnavailableRetryStrategy import au.com.dius.pact.core.support.HttpClient import au.com.dius.pact.core.support.Json +import au.com.dius.pact.core.support.Version import au.com.dius.pact.core.support.Utils import au.com.dius.pact.core.support.json.JsonException import au.com.dius.pact.core.support.json.JsonParser @@ -20,8 +21,8 @@ import au.com.dius.pact.core.support.jsonObject import com.github.michaelbull.result.Err import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Result +import com.github.michaelbull.result.expect import com.github.michaelbull.result.runCatching -import com.github.zafarkhaja.semver.Version import mu.KLogging import mu.KotlinLogging import org.apache.http.auth.AuthScope @@ -186,6 +187,11 @@ interface PactReader { * @param options to use when loading the pact */ fun loadPact(source: Any, options: Map): Pact<*> + + /** + * Parses the JSON into a Pact model + */ + fun pactFromJson(json: JsonValue.Object, source: PactSource): Pact<*> } /** @@ -201,12 +207,17 @@ object DefaultPactReader : PactReader, KLogging() { override fun loadPact(source: Any) = loadPact(source, emptyMap()) override fun loadPact(source: Any, options: Map): Pact<*> { - val pactInfo = loadFile(source, options) - val version = determineSpecVersion(pactInfo.first) - val specVersion = Version.valueOf(version) - return when (specVersion.majorVersion) { - 3 -> loadV3Pact(pactInfo.second, pactInfo.first) - else -> loadV2Pact(pactInfo.second, pactInfo.first) + val json = loadFile(source, options) + return pactFromJson(json.first, json.second) + } + + override fun pactFromJson(json: JsonValue.Object, source: PactSource): Pact<*> { + val version = determineSpecVersion(json) + val specVersion = Version.parse(version).expect { "'$version' is not a valid version" } + return when (specVersion.major) { + 3 -> loadV3Pact(source, json) + 4 -> throw IllegalArgumentException("V4 Pacts require Pact-JVM 4.2.x+") + else -> loadV2Pact(source, json) } } @@ -222,9 +233,6 @@ object DefaultPactReader : PactReader, KLogging() { else -> version } } - if (version == "3.0") { - version = "3.0.0" - } return version } diff --git a/core/model/src/test/groovy/au/com/dius/pact/core/model/PactReaderSpec.groovy b/core/model/src/test/groovy/au/com/dius/pact/core/model/PactReaderSpec.groovy index c8e52aa003..d81185f884 100644 --- a/core/model/src/test/groovy/au/com/dius/pact/core/model/PactReaderSpec.groovy +++ b/core/model/src/test/groovy/au/com/dius/pact/core/model/PactReaderSpec.groovy @@ -346,9 +346,8 @@ class PactReaderSpec extends Specification { '{"metadata":{"pactSpecification":"1.2.3"}}' | '2.0.0' '{"metadata":{"pactSpecification":{}}}' | '2.0.0' '{"metadata":{"pactSpecification":{"version":"1.2.3"}}}' | '1.2.3' - '{"metadata":{"pactSpecification":{"version":"3.0"}}}' | '3.0.0' + '{"metadata":{"pactSpecification":{"version":"3.0"}}}' | '3.0' '{"metadata":{"pact-specification":{"version":"1.2.3"}}}' | '1.2.3' - } @Issue('#1031') diff --git a/core/support/src/main/antlr/au/com/dius/pact/core/support/Version.g4 b/core/support/src/main/antlr/au/com/dius/pact/core/support/Version.g4 new file mode 100644 index 0000000000..dfb507df7f --- /dev/null +++ b/core/support/src/main/antlr/au/com/dius/pact/core/support/Version.g4 @@ -0,0 +1,19 @@ +grammar Version; + +@header { + package au.com.dius.pact.core.support; +} + +version returns [ Version v ] : + { Integer major, minor, patch = null; } + INT { major = $INT.int; } '.' INT { minor = $INT.int; } ('.' INT { patch = $INT.int; })? EOF { + if (patch != null) { + $v = new Version(major, minor, patch); + } else { + $v = new Version(major, minor); + } + } + ; + +INT : DIGIT+ ; +fragment DIGIT : [0-9] ; diff --git a/core/support/src/main/java/au/com/dius/pact/core/support/Version.java b/core/support/src/main/java/au/com/dius/pact/core/support/Version.java new file mode 100644 index 0000000000..a5c76b7086 --- /dev/null +++ b/core/support/src/main/java/au/com/dius/pact/core/support/Version.java @@ -0,0 +1,89 @@ +package au.com.dius.pact.core.support; + +import com.github.michaelbull.result.Err; +import com.github.michaelbull.result.Ok; +import com.github.michaelbull.result.Result; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.TokenStream; + +import java.util.Objects; + +public class Version { + private Integer major; + private Integer minor; + private Integer patch; + + public Version(Integer major, Integer minor, Integer patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + public Version(Integer major, Integer minor) { + this.major = major; + this.minor = minor; + } + + public Integer getMajor() { + return major; + } + + public void setMajor(Integer major) { + this.major = major; + } + + public Integer getMinor() { + return minor; + } + + public void setMinor(Integer minor) { + this.minor = minor; + } + + public Integer getPatch() { + return patch; + } + + public void setPatch(Integer patch) { + this.patch = patch; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Version version = (Version) o; + return Objects.equals(major, version.major) && + Objects.equals(minor, version.minor) && + Objects.equals(patch, version.patch); + } + + @Override + public int hashCode() { + return Objects.hash(major, minor, patch); + } + + @Override + public String toString() { + if (patch == null) { + return String.format("%d.%d", major, minor); + } else { + return String.format("%d.%d.%d", major, minor, patch); + } + } + + public static Result parse(String version) { + CharStream charStream = CharStreams.fromString(version); + VersionLexer lexer = new VersionLexer(charStream); + TokenStream tokens = new CommonTokenStream(lexer); + VersionParser parser = new VersionParser(tokens); + VersionParser.VersionContext result = parser.version(); + if (result.exception != null) { + return new Err(result.exception); + } else { + return new Ok(result.v); + } + } +} diff --git a/core/support/src/main/kotlin/au/com/dius/pact/core/support/Json.kt b/core/support/src/main/kotlin/au/com/dius/pact/core/support/Json.kt index 9421181dfd..7432f36568 100644 --- a/core/support/src/main/kotlin/au/com/dius/pact/core/support/Json.kt +++ b/core/support/src/main/kotlin/au/com/dius/pact/core/support/Json.kt @@ -36,7 +36,7 @@ object Json { /** * Converts a JSON object to a raw string if it is a string value, else just calls toString() */ - fun toString(json: JsonValue?): String = json?.asString() ?: "null" + fun toString(json: JsonValue?): String = json?.asString() ?: json?.serialise() ?: "null" /** * Converts a JSON object to the Map of values diff --git a/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy b/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy new file mode 100644 index 0000000000..a0ab05a2a1 --- /dev/null +++ b/core/support/src/test/groovy/au/com/dius/pact/core/support/VersionParserSpec.groovy @@ -0,0 +1,22 @@ +package au.com.dius.pact.core.support + +import spock.lang.Specification +import org.antlr.v4.runtime.InputMismatchException + +class VersionParserSpec extends Specification { + + def 'parse full version'() { + expect: + Version.parse('1.2.3').component1() == new Version(1, 2, 3) + } + + def 'parse major.minor version'() { + expect: + Version.parse('1.2').component1() == new Version(1, 2, null) + } + + def 'parse invalid version'() { + expect: + Version.parse('lkzasdjskjdf').component2() instanceof InputMismatchException + } +}