kgiszczak / shale

Shale is a Ruby object mapper and serializer for JSON, YAML, TOML, CSV and XML. It allows you to parse JSON, YAML, TOML, CSV and XML data and convert it into Ruby data structures, as well as serialize data structures into JSON, YAML, TOML, CSV or XML.
https://shalerb.org/
MIT License
618 stars 19 forks source link

Compiling a JSON schema -> Ruby fails when there are relative path $refs in the schema #25

Closed jwilmoth-nc closed 10 months ago

jwilmoth-nc commented 10 months ago

https://www.shalerb.org/#compiling-json-and-xml-schema uses a simple example for generation that doesn't include $refs. Given a file that uses a simple relative path ref that exists in the same directory, the shaleb -c -i user.json command fails with the following message:

/Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:184:in `resolve_ref': can't resolve reference 'http://example.com/schemas/common.json#/definitions/address' (Shale::SchemaError)
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:310:in `compile'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:336:in `block in compile'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:335:in `each'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:335:in `compile'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:89:in `as_models'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema/json_compiler.rb:124:in `to_models'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/lib/shale/schema.rb:51:in `from_json'
    from /Library/Ruby/Gems/2.6.0/gems/shale-1.0.0/exe/shaleb:88:in `<top (required)>'
    from /usr/local/bin/shaleb:23:in `load'
    from /usr/local/bin/shaleb:23:in `<main>'

cat user.json| jq

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://example.com/schemas/user.json",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "address": {
      "$ref": "./common.json#/definitions/address"
    }
  }
}

cat common.json| jq

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://example.com/schemas/common.json",
  "definitions": {
    "address": {
      "type": "object",
      "properties": {
        "street": {
          "type": "string"
        },
        "city": {
          "type": "string"
        }
      }
    }
  }
}
kgiszczak commented 10 months ago

Hey Jon,

first thing is you are using Draft-07 JSON schema. Shale supports only Draft 2020-12. Second thing is $refs in JSON schema are not paths, they are just identifiers used to resolve references. You have to provide all the schema files that you're using.

Taken from https://json-schema.org/understanding-json-schema/structuring.html#id

Even though schemas are identified by URIs, those identifiers are not necessarily network-addressable. They are just identifiers. Generally, implementations don’t make HTTP requests (https://) or read from the file system (file://) to fetch schemas. Instead, they provide a way to load schemas into an internal schema database. When a schema is referenced by it’s URI identifier, the schema is retrieved from the internal schema database.

So working example would look like this:

shaleb -c -i user.json,common.json

And schema files:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://example.com/schemas/user.json",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "address": {
      "$ref": "http://example.com/schemas/common.json#/$defs/address"
    }
  }
}
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "http://example.com/schemas/common.json",
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": {
          "type": "string"
        },
        "city": {
          "type": "string"
        }
      }
    }
  }
}