diff --git a/build.sc b/build.sc index 69cdafb..252abd5 100644 --- a/build.sc +++ b/build.sc @@ -425,13 +425,13 @@ object Deps { val jawn = ivy"io.circe::circe-jawn:0.14.6" } object everit { - val jsonSchema = ivy"com.github.erosb:everit-json-schema:1.14.2" + val jsonSchema = ivy"com.github.erosb:everit-json-schema:1.14.3" } val slf4j = ivy"org.slf4j:slf4j-nop:2.0.9" // needed since swagger-parser relies on slf4j-api object swagger { val parser = Agg( - ivy"io.swagger.parser.v3:swagger-parser:2.1.16", + ivy"io.swagger.parser.v3:swagger-parser:2.1.18", // included to override the version brought in by swagger-parser which has a vulnerability ivy"org.mozilla:rhino:1.7.14" ) @@ -459,7 +459,7 @@ object Deps { val services = ivy"io.grpc:grpc-services:$version" } object scalapb { - val version = "0.11.13" + val version = "0.11.14" val runtimeGrpc = ivy"com.thesamet.scalapb::scalapb-runtime-grpc:$version" val compilerPlugin = ivy"com.thesamet.scalapb::compilerplugin:$version" diff --git a/modules/json-schema/src/internals/Extractors.scala b/modules/json-schema/src/internals/Extractors.scala index 29cf2d9..44a3c06 100644 --- a/modules/json-schema/src/internals/Extractors.scala +++ b/modules/json-schema/src/internals/Extractors.scala @@ -250,7 +250,7 @@ object Extractors { /* * The most complicated thing */ - abstract class JsonSchemaCaseRefBuilder(id: String, ns: Path) + abstract class JsonSchemaCaseRefBuilder(id: Option[String], ns: Path) extends smithytranslate.openapi.internals.CaseRefBuilder(ns) { def unapply(sch: Schema): Option[Either[ModelError, DefId]] = sch match { case ref: ReferenceSchema => @@ -260,11 +260,14 @@ object Extractors { // The number of `/` chars is not always consistent between the id and the refValue. val fileRegex = "^file:\\/*" val refValueNoPrefix = refValue.replaceFirst(fileRegex, "") - val idNoPrefix = id.replaceFirst(fileRegex, "") val sanitisedRefValue = - if (refValueNoPrefix.startsWith(idNoPrefix)) - refValueNoPrefix.drop(idNoPrefix.length()) - else refValue + id.map(_.replaceFirst(fileRegex, "")) + .collectFirst { + case idNoPrefix if (refValueNoPrefix.startsWith(idNoPrefix)) => + refValueNoPrefix.drop(idNoPrefix.length()) + } + .getOrElse(refValue) + Option(sanitisedRefValue).map(this.apply) case _ => None } diff --git a/modules/json-schema/src/internals/JsonSchemaToIModel.scala b/modules/json-schema/src/internals/JsonSchemaToIModel.scala index 5cb0875..0bc3cf3 100644 --- a/modules/json-schema/src/internals/JsonSchemaToIModel.scala +++ b/modules/json-schema/src/internals/JsonSchemaToIModel.scala @@ -69,7 +69,10 @@ private class JsonSchemaToIModel[F[_]: Parallel: TellShape: TellError]( implicit val F: Monad[F] = Parallel[F].monad private val CaseRef = - new Extractors.JsonSchemaCaseRefBuilder(jsonSchema.getId(), namespace) {} + new Extractors.JsonSchemaCaseRefBuilder( + Option(jsonSchema.getId()), + namespace + ) {} private val allSchemas: Vector[Local] = { val schemaNameSegment = @@ -77,9 +80,9 @@ private class JsonSchemaToIModel[F[_]: Parallel: TellShape: TellError]( val schemaName = Name(schemaNameSegment) // Computing schemas under the $defs field, if it exists. - def $defSchemas = { + def $defSchemas(name: String): Vector[Local] = { val defsObject = rawJson.asObject - .flatMap(_.apply("$defs")) + .flatMap(_.apply(name)) .flatMap(_.asObject) val allDefs = defsObject.toVector @@ -89,14 +92,14 @@ private class JsonSchemaToIModel[F[_]: Parallel: TellShape: TellError]( allDefs .flatMap { case (key, value) => val topLevelJson = Json.fromJsonObject { - value.add("$defs", Json.fromJsonObject(defsObject.get)) + value.add(name, Json.fromJsonObject(defsObject.get)) } val defSchema = LoadSchema(new JSONObject(topLevelJson.noSpaces)) val defSchemaName = Name( - Segment.Arbitrary(CIString("$defs")), + Segment.Arbitrary(CIString(name)), Segment.Derived(CIString(key)) ) Vector( @@ -110,7 +113,7 @@ private class JsonSchemaToIModel[F[_]: Parallel: TellShape: TellError]( val topLevelLocal = Local(schemaName, jsonSchema, rawJson).addHints(List(Hint.TopLevel)) - topLevelLocal +: $defSchemas + topLevelLocal +: ($defSchemas("$defs") ++ $defSchemas("definitions")) } /** Refolds the schema, aggregating found definitions in Tell. diff --git a/modules/json-schema/tests/src/RefSpec.scala b/modules/json-schema/tests/src/RefSpec.scala index 01ecd55..ddb3a8b 100644 --- a/modules/json-schema/tests/src/RefSpec.scala +++ b/modules/json-schema/tests/src/RefSpec.scala @@ -254,4 +254,72 @@ final class RefSpec extends munit.FunSuite { TestUtils.runConversionTest(jsonSchString, expectedString) } + + test("schema with no id") { + val jsonSchString = """|{ + | "$schema": "http://json-schema.org/draft-07/schema#", + | "title": "Person", + | "type": "object", + | "properties": { + | "firstName": { + | "$ref": "#/$defs/name" + | }, + | "lastName": { + | "$ref": "#/$defs/name" + | } + | }, + | "$defs":{ + | "name": { + | "type": "string" + | } + | } + |} + |""".stripMargin + + val expectedString = """|namespace foo + | + |structure Person { + | firstName: Name, + | lastName: Name + |} + | + |string Name + |""".stripMargin + + TestUtils.runConversionTest(jsonSchString, expectedString) + } + + test("schema with definitions rather than $defs") { + val jsonSchString = """|{ + | "$schema": "http://json-schema.org/draft-07/schema#", + | "title": "Person", + | "type": "object", + | "properties": { + | "firstName": { + | "$ref": "#/definitions/name" + | }, + | "lastName": { + | "$ref": "#/definitions/name" + | } + | }, + | "definitions":{ + | "name": { + | "type": "string" + | } + | } + |} + |""".stripMargin + + val expectedString = """|namespace foo + | + |structure Person { + | firstName: Name, + | lastName: Name + |} + | + |string Name + |""".stripMargin + + TestUtils.runConversionTest(jsonSchString, expectedString) + } }