Skip to content

Commit 647d3bd

Browse files
committed
support migration from draft-04 to draft-2020-12
1 parent 9c12226 commit 647d3bd

9 files changed

+124
-11
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
files: ["*.ts"],
1414
rules: {
1515
...tsConfig.rules,
16-
complexity: ["error", 35],
16+
complexity: ["error", 15],
1717
"@typescript-eslint/no-explicit-any": "off",
1818
"@typescript-eslint/no-unnecessary-condition": "warn",
1919
"@typescript-eslint/no-unsafe-assignment": "off",

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# json-schema-migrate
22

3-
Migrate JSON-Schema from draft-04 to draft-07 or draft-2019-09
3+
Migrate JSON-Schema from draft-04 to draft-07, draft-2019-09 or draft-2020-12
44

55
[![build](https://github.com/ajv-validator/json-schema-migrate/workflows/build/badge.svg)](https://github.com/ajv-validator/json-schema-migrate/actions?query=workflow%3Abuild)
66
[![npm](https://img.shields.io/npm/v/json-schema-migrate)](https://www.npmjs.com/package/json-schema-migrate)
@@ -23,6 +23,7 @@ const schema = {
2323
}
2424
migrate.draft7(schema)
2525
// or migrate.draft2019(schema)
26+
// or migrate.draft2020(schema)
2627

2728
console.log(schema)
2829
// {
@@ -40,7 +41,7 @@ console.log(migrate.getAjv().errorsText(errors))
4041
## Changes in schemas after migration
4142

4243
- `id` is replaced with `$id`
43-
- `$schema` value becomes draft-07 or draft-2019-09 meta-schema
44+
- `$schema` value becomes draft-07, draft-2019-09 or draft-2020-12 meta-schema
4445
- draft-04 boolean form of `exclusiveMaximum/Minimum` is replaced with the current number form
4546
- `enum` with a single allowed value is replaced with `const`
4647
- Non-standard `constant` is replaced with `const`
@@ -51,6 +52,7 @@ console.log(migrate.getAjv().errorsText(errors))
5152
- `dependencies` with `dependentRequired` and `dependentSchemas`
5253
- `"id": "#foo"` with `"$anchor": "foo"`
5354
- `"id": "schema#foo"` with `"$id": "schema", "$anchor": "foo"`
55+
- `draft2020` function additionally replaces array form of `items` with `prefixItems` (and `additionalItems` with `items`)
5456

5557
## License
5658

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"homepage": "https://github.com/epoberezkin/json-schema-migrate#readme",
3030
"dependencies": {
31-
"ajv": "^7.0.0-beta.9"
31+
"ajv": "^8.0.0"
3232
},
3333
"devDependencies": {
3434
"@ajv-validator/config": "^0.3.0",

spec/fixtures/expected-schema-from-draft-04-to-07.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@
2626
"alwaysValid": true,
2727
"alsoAlwaysValid": true,
2828
"alwaysInvalid": false,
29-
"alsoAlwaysInvalid": false
29+
"alsoAlwaysInvalid": false,
30+
"array": {
31+
"items": {"type": "number"}
32+
},
33+
"tuple": {
34+
"items": [{"type": "number"}, {"type": "string"}],
35+
"additionalItems": false
36+
},
37+
"tupleExtras": {
38+
"items": [{"type": "number"}, {"type": "string"}],
39+
"additionalItems": {"type": "boolean"}
40+
}
3041
},
3142
"dependencies": {
3243
"foo": ["bar"],

spec/fixtures/expected-schema-from-draft-04-to-2019.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,18 @@
2727
"alwaysValid": true,
2828
"alsoAlwaysValid": true,
2929
"alwaysInvalid": false,
30-
"alsoAlwaysInvalid": false
30+
"alsoAlwaysInvalid": false,
31+
"array": {
32+
"items": {"type": "number"}
33+
},
34+
"tuple": {
35+
"items": [{"type": "number"}, {"type": "string"}],
36+
"additionalItems": false
37+
},
38+
"tupleExtras": {
39+
"items": [{"type": "number"}, {"type": "string"}],
40+
"additionalItems": {"type": "boolean"}
41+
}
3142
},
3243
"dependentRequired": {
3344
"foo": ["bar"]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"$id": "my-schema",
3+
"$schema": "https://json-schema.org/draft/2019-09/schema",
4+
"definitions": {
5+
"str": {
6+
"$anchor": "str",
7+
"type": "string"
8+
},
9+
"num": {
10+
"$id": "another-schema",
11+
"$anchor": "num",
12+
"type": "string"
13+
}
14+
},
15+
"properties": {
16+
"exclusiveLimits": {
17+
"exclusiveMinimum": 1,
18+
"exclusiveMaximum": 3
19+
},
20+
"nonExclusiveLimits": {
21+
"minimum": 1,
22+
"maximum": 3
23+
},
24+
"singleValue": {"const": "foo"},
25+
"singleValueConstant": {"const": "foo"},
26+
"multipleValues": {"enum": ["foo", "bar"]},
27+
"alwaysValid": true,
28+
"alsoAlwaysValid": true,
29+
"alwaysInvalid": false,
30+
"alsoAlwaysInvalid": false,
31+
"array": {
32+
"items": {"type": "number"}
33+
},
34+
"tuple": {
35+
"prefixItems": [{"type": "number"}, {"type": "string"}],
36+
"items": false
37+
},
38+
"tupleExtras": {
39+
"prefixItems": [{"type": "number"}, {"type": "string"}],
40+
"items": {"type": "boolean"}
41+
}
42+
},
43+
"dependentRequired": {
44+
"foo": ["bar"]
45+
},
46+
"dependentSchemas": {
47+
"bar": {
48+
"required": ["baz"],
49+
"properties": {
50+
"baz": {"type": "string"}
51+
}
52+
}
53+
}
54+
}

spec/fixtures/schema-draft-04.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,18 @@
3030
"alwaysValid": {},
3131
"alsoAlwaysValid": {"not": {"not": {}}},
3232
"alwaysInvalid": {"not": {}},
33-
"alsoAlwaysInvalid": {"not": {"not": {"not": {}}}}
33+
"alsoAlwaysInvalid": {"not": {"not": {"not": {}}}},
34+
"array": {
35+
"items": {"type": "number"}
36+
},
37+
"tuple": {
38+
"items": [{"type": "number"}, {"type": "string"}],
39+
"additionalItems": false
40+
},
41+
"tupleExtras": {
42+
"items": [{"type": "number"}, {"type": "string"}],
43+
"additionalItems": {"type": "boolean"}
44+
}
3445
},
3546
"dependencies": {
3647
"foo": ["bar"],

spec/migrate.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import assert = require("assert")
2-
import {draft7, draft2019, getAjv} from ".."
2+
import {draft7, draft2019, draft2020, getAjv} from ".."
33
import schemaDraft4 = require("./fixtures/schema-draft-04.json")
44
import expectedSchemaDraft7 = require("./fixtures/expected-schema-from-draft-04-to-07.json")
55
import expectedSchemaDraft2019 = require("./fixtures/expected-schema-from-draft-04-to-2019.json")
6+
import expectedSchemaDraft2020 = require("./fixtures/expected-schema-from-draft-04-to-2020.json")
67
import Ajv, {AnySchemaObject} from "ajv/dist/core"
78

89
function clone(schema: AnySchemaObject): AnySchemaObject {
@@ -25,6 +26,12 @@ describe("migrate to draft-07 schema", () => {
2526
assert.deepStrictEqual(schema, expectedSchemaDraft2019)
2627
})
2728

29+
it("should migrate from draft-04 schema to draft-2020-12 schema", () => {
30+
const schema = clone(schemaDraft4)
31+
draft2020(schema)
32+
assert.deepStrictEqual(schema, expectedSchemaDraft2020)
33+
})
34+
2835
describe("invalid schemas", () => {
2936
it("should throw if id is not a string", () => {
3037
assert.throws(() => draft7({id: 1}))

src/index.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import Ajv, {SchemaObject, AnySchemaObject, AnySchema} from "ajv/dist/2019"
22
import type {DataValidationCxt, ValidateFunction} from "ajv/dist/types"
33

4-
type SchemaVersion = "draft7" | "draft2019"
4+
type SchemaVersion = "draft7" | "draft2019" | "draft2020"
55

66
export const draft7 = getMigrate("draft7")
77
export const draft2019 = getMigrate("draft2019")
8+
export const draft2020 = getMigrate("draft2020")
89

910
function getMigrateSchema(version: SchemaVersion): SchemaObject {
1011
return {
@@ -41,7 +42,8 @@ export function getAjv(): Ajv {
4142
keyword: "migrateSchema",
4243
schemaType: "string",
4344
modifying: true,
44-
metaSchema: {enum: ["draft7", "draft2019"]},
45+
metaSchema: {enum: ["draft7", "draft2019", "draft2020"]},
46+
// eslint-disable-next-line complexity
4547
validate(
4648
version: SchemaVersion,
4749
dataSchema: AnySchema,
@@ -66,7 +68,7 @@ export function getAjv(): Ajv {
6668
if (typeof id !== "string") {
6769
throw new Error(`json-schema-migrate: schema id must be string`)
6870
}
69-
if (version === "draft2019" && id.includes("#")) {
71+
if ((version === "draft2019" || version === "draft2020") && id.includes("#")) {
7072
const [$id, $anchor, ...rest] = id.split("#")
7173
if (rest.length > 0) {
7274
throw new Error(`json-schema-migrate: invalid schema id ${id}`)
@@ -127,6 +129,21 @@ export function getAjv(): Ajv {
127129
}
128130
break
129131
}
132+
case "items":
133+
if (version === "draft2020" && Array.isArray(dsCopy.items)) {
134+
dataSchema.prefixItems = dsCopy.items
135+
if (dsCopy.additionalItems !== undefined) {
136+
dataSchema.items = dsCopy.additionalItems
137+
}
138+
} else {
139+
dataSchema.items = dsCopy.items
140+
}
141+
break
142+
case "additionalItems":
143+
if (version !== "draft2020") {
144+
dataSchema.additionalItems = dsCopy.additionalItems
145+
}
146+
break
130147
default:
131148
dataSchema[key] = dsCopy[key]
132149
}

0 commit comments

Comments
 (0)