sourcemeta/alterschema

Analysis of transformation requirements

Closed this issue · 6 comments

Hello! About a week ago, I noticed that the JSON Schema website implementations page doesn't have any version migration tools listed, so I think it's great that you're building this. To that end, I took some time to put together a collection of rules that describe what needs to be changed when moving from one version to another.

For all of these, the format looks like

{
  "fromDraft": [ "draft-4", "draft-6", "draft-7" ],
  "toDraft": [ "draft-2019-09", "draft-2020-12" ],
  "from": { ... },
  "to": { ... }
}

If you're coming from one of the versions in fromDraft and going to one of the versions in toDraft, then the rule applies. from and to provide a minimal example of the required change.

Required changes

This is the list of things that MUST be updated. These are cases where the old keyword has been removed, replaced, or deprecated and will not be recognized by the new version.

I think I found everything.

Click to expand!
[
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": { "id": "http://example.com/schema" },
    "to": { "$id": "http://example.com/schema" }
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7" ],
    "from": { "id": "#tag" },
    "to": { "$id": "#tag" }
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-2019-09", "draft-2020-12" ],
    "from": { "id": "#tag" },
    "to": { "$anchor": "tag" }
  },
  {
    "fromDraft": [ "draft-6", "draft-7" ],
    "toDraft": [ "draft-2019-09", "draft-2020-12" ],
    "from": { "$id": "#tag" },
    "to": { "$anchor": "tag" }
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": { "minimum": 5, "exclusiveMinimum": true },
    "to": { "exclusiveMinimum": 5 }
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": { "maximum": 5, "exclusiveMaximum": true },
    "to": { "exclusiveMaximum": 5 }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7" ],
    "toDraft": [ "draft-2019-09", "draft-2020-12" ],
    "from": { "definitions": { "foo": { "type": "string" } } },
    "to": { "$defs": { "foo": { "type": "string" } } }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7", "draft-2019-09" ],
    "toDraft": [ "draft-2020-12" ],
    "from": { "items": [ { "type": "string" }, { "type": "integer" } ] },
    "to": { "prefixItems": [ { "type": "string" }, { "type": "integer" } ] }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7", "draft-2019-09" ],
    "toDraft": [ "draft-2020-12" ],
    "from": { "additionalItems": false },
    "to": { "items": false }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7", "draft-2019-09" ],
    "toDraft": [ "draft-2020-12" ],
    "from": { "additionalItems": { "type": "string" } },
    "to": { "items": { "type": "string" } }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7" ],
    "toDraft": [ "draft-2019-09", "draft-2020-12" ],
    "from": { "dependencies": { "foo": { "type": "string" } } },
    "to": { "dependentSchemas": { "foo": { "type": "string" } } }
  },
  {
    "fromDraft": [ "draft-4", "draft-6", "draft-7" ],
    "toDraft": [ "draft-2019-09", "draft-2020-12" ],
    "from": { "dependencies": { "foo": [ "bar" ] } },
    "to": { "dependentProperties": { "foo": [ "bar" ] } }
  }
]

Suggested change

These are changes where the syntax is still supported, but better ways are available.

Click to expand!
[
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": {},
    "to": true
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": { "not": {} },
    "to": false
  },
  {
    "fromDraft": [ "draft-4" ],
    "toDraft": [ "draft-6", "draft-7", "draft-2019-09", "draft-2020-12" ],
    "from": { "enum": [ "single-value" ] },
    "to": { "const": "single-value" }
  }
]

This is amazing @gregsdennis. Thanks a lot for taking the time to write this up. I'll start sending PRs implementing these

@gregsdennis I think your list is missing $recursiveAnchor and $recursiveRef to $dynamicAnchor and $dynamicRef from 2019-09 to 2020-12

While it's true that dynamic* replaced recursive*, they don't exactly work the same, so I left that one out intentionally.

That's not to say there's not a migration path, but value transformation will likely be required.

That's not to say there's not a migration path, but value transformation will likely be required.

And if you are using these keywords, you are likely to have more than one schema referring to each other, in which case even a single focused transformation might not be enough, right?

OK, so at this point alterschema is handling everything except from dependencies, including your optional suggestions 🎉 That one requires some further fiddling with JSON-e... I can hopefully figure it out today

@gregsdennis The last rule has been implemented! I'll be doing some cleanups today and will release a new version to NPM that we can use for testing purposes.