diff --git a/package-lock.json b/package-lock.json index 69c54d7..f432c1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "yargs": "^18.0.0" }, "devDependencies": { - "@hyperjump/json-schema": "^1.9.9", "@hyperjump/json-schema-coverage": "^1.1.1", "c8": "^10.1.3", "markdownlint-cli": "^0.45.0", diff --git a/schemas/v1.1/schema.yaml b/schemas/v1.1/schema.yaml new file mode 100644 index 0000000..459cbef --- /dev/null +++ b/schemas/v1.1/schema.yaml @@ -0,0 +1,63 @@ +$id: https://spec.openapis.org/overlay/1.1/schema/WORK-IN-PROGRESS +$schema: https://json-schema.org/draft/2020-12/schema +description: The description of Overlay v1.1.x documents +type: object +properties: + overlay: + type: string + pattern: ^1\.1\.\d+$ + info: + $ref: "#/$defs/info-object" + extends: + type: string + format: uri-reference + actions: + type: array + minItems: 1 + uniqueItems: true + items: + $ref: "#/$defs/action-object" +required: + - overlay + - info + - actions +$ref: "#/$defs/specification-extensions" +unevaluatedProperties: false +$defs: + info-object: + type: object + properties: + title: + type: string + version: + type: string + required: + - title + - version + $ref: "#/$defs/specification-extensions" + unevaluatedProperties: false + action-object: + properties: + target: + type: string + pattern: ^\$ + description: + type: string + update: + type: + - string + - boolean + - object + - array + - number + - "null" + remove: + type: boolean + default: false + required: + - target + $ref: "#/$defs/specification-extensions" + unevaluatedProperties: false + specification-extensions: + patternProperties: + ^x-: true diff --git a/scripts/validate.mjs b/scripts/validate.mjs index dc6c7cc..d10608a 100755 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -38,12 +38,13 @@ const args = process.argv.reduce((acc, arg) => { const schemaType = args.schema || "schema"; const outputFormat = args.format || defaultOutputFormat; +const version = args.version || "1.0"; // Config setMetaSchemaOutputFormat(outputFormat); // Compile / meta-validate -const validateOverlay = await validate(`./schemas/v1.0/${schemaType}.yaml`); +const validateOverlay = await validate(`./schemas/v${version}/${schemaType}.yaml`); // Validate instance const instanceYaml = await readFile(`${process.argv[process.argv.length - 1]}`, "utf8"); diff --git a/tests/v1.0/schema.test.mjs b/tests/schema.test.mjs similarity index 72% rename from tests/v1.0/schema.test.mjs rename to tests/schema.test.mjs index 06f4dc3..50a9e19 100644 --- a/tests/v1.0/schema.test.mjs +++ b/tests/schema.test.mjs @@ -8,13 +8,13 @@ const parseYamlFromFile = (filePath) => { }; function runTestSuite(version, suite) { - const schema = `./schemas/${version}/schema.yaml`; + const schema = `./schemas/v${version}/schema.yaml`; describe(suite, () => { - readdirSync(`./tests/${version}/${suite}`, { withFileTypes: true }) + readdirSync(`./tests/v${version}/${suite}`, { withFileTypes: true }) .filter((entry) => entry.isFile() && /\.yaml$/.test(entry.name)) .forEach((entry) => { test(entry.name, async () => { - const instance = parseYamlFromFile(`./tests/${version}/${suite}/${entry.name}`); + const instance = parseYamlFromFile(`./tests/v${version}/${suite}/${entry.name}`); if (suite === "pass") { await expect(instance).to.matchJsonSchema(schema); } else { @@ -25,9 +25,9 @@ function runTestSuite(version, suite) { }); } -const version = "v1.0"; +const versions = ["1.0", "1.1"]; -describe(version, () => { +describe.each(versions)("v%s", (version) => { runTestSuite(version, "pass"); runTestSuite(version, "fail"); }); diff --git a/tests/v1.1/fail/actions-invalid-description.yaml b/tests/v1.1/fail/actions-invalid-description.yaml new file mode 100644 index 0000000..7fb44ee --- /dev/null +++ b/tests/v1.1/fail/actions-invalid-description.yaml @@ -0,0 +1,7 @@ +overlay: 1.1.0 +info: + title: Actions invalid description + version: 1.0.0 +actions: + - target: '$' # Root of document + description: 10 diff --git a/tests/v1.1/fail/actions-invalid-target.yaml b/tests/v1.1/fail/actions-invalid-target.yaml new file mode 100644 index 0000000..64641db --- /dev/null +++ b/tests/v1.1/fail/actions-invalid-target.yaml @@ -0,0 +1,7 @@ +overlay: 1.1.0 +info: + title: Invalid `target`, must begin with `$` + version: 1.0.0 +actions: + - target: info.description + update: An updated description diff --git a/tests/v1.1/fail/actions-minimal.yaml b/tests/v1.1/fail/actions-minimal.yaml new file mode 100644 index 0000000..66ae16b --- /dev/null +++ b/tests/v1.1/fail/actions-minimal.yaml @@ -0,0 +1,5 @@ +overlay: 1.1.0 +info: + title: Minimal actions + version: 1.0.0 +actions: [] diff --git a/tests/v1.1/fail/actions-missing-target.yaml b/tests/v1.1/fail/actions-missing-target.yaml new file mode 100644 index 0000000..066e569 --- /dev/null +++ b/tests/v1.1/fail/actions-missing-target.yaml @@ -0,0 +1,6 @@ +overlay: 1.1.0 +info: + title: Missing actions `target` + version: 1.0.0 +actions: + - update: my description diff --git a/tests/v1.1/fail/actions-missing.yaml b/tests/v1.1/fail/actions-missing.yaml new file mode 100644 index 0000000..383761e --- /dev/null +++ b/tests/v1.1/fail/actions-missing.yaml @@ -0,0 +1,5 @@ +overlay: 1.1.0 +info: + title: Missing `actions` + version: 1.0.0 +extends: '/openapi.yaml' diff --git a/tests/v1.1/fail/actions-not-unique.yaml b/tests/v1.1/fail/actions-not-unique.yaml new file mode 100644 index 0000000..da7a657 --- /dev/null +++ b/tests/v1.1/fail/actions-not-unique.yaml @@ -0,0 +1,9 @@ +overlay: 1.1.0 +info: + title: Actions not unique + version: 1.0.0 +actions: + - target: '$.info.title' + update: 'My New title' + - target: '$.info.title' + update: 'My New title' diff --git a/tests/v1.1/fail/extends-invalid-type.yaml b/tests/v1.1/fail/extends-invalid-type.yaml new file mode 100644 index 0000000..d3b699c --- /dev/null +++ b/tests/v1.1/fail/extends-invalid-type.yaml @@ -0,0 +1,7 @@ +overlay: 1.1.0 +info: + title: Invalid `extends` type + version: 1.0.0 +extends: {} +actions: + - target: '$' # Root of document diff --git a/tests/v1.1/fail/info-missing-title.yaml b/tests/v1.1/fail/info-missing-title.yaml new file mode 100644 index 0000000..3b3088b --- /dev/null +++ b/tests/v1.1/fail/info-missing-title.yaml @@ -0,0 +1,5 @@ +overlay: 1.1.0 +info: + version: 1.0.0 +actions: + - target: '$' # Root of document diff --git a/tests/v1.1/fail/info-missing-version.yaml b/tests/v1.1/fail/info-missing-version.yaml new file mode 100644 index 0000000..7372b67 --- /dev/null +++ b/tests/v1.1/fail/info-missing-version.yaml @@ -0,0 +1,5 @@ +overlay: 1.1.0 +info: + title: Missing Info version +actions: + - target: '$' # Root of document diff --git a/tests/v1.1/fail/invalid-overlay-version.yaml b/tests/v1.1/fail/invalid-overlay-version.yaml new file mode 100644 index 0000000..1936290 --- /dev/null +++ b/tests/v1.1/fail/invalid-overlay-version.yaml @@ -0,0 +1,6 @@ +overlay: 2 +info: + title: Invalid Overlay version + version: 1.0.0 +actions: + - target: '$' # Root of document diff --git a/tests/v1.1/fail/not-an-object.yaml b/tests/v1.1/fail/not-an-object.yaml new file mode 100644 index 0000000..574dc05 --- /dev/null +++ b/tests/v1.1/fail/not-an-object.yaml @@ -0,0 +1,5 @@ +- root +- must +- be +- an +- object diff --git a/tests/v1.1/pass/actions-array-modification-example-remove.yaml b/tests/v1.1/pass/actions-array-modification-example-remove.yaml new file mode 100644 index 0000000..c3990a1 --- /dev/null +++ b/tests/v1.1/pass/actions-array-modification-example-remove.yaml @@ -0,0 +1,11 @@ +overlay: 1.1.0 +info: + title: Add an array element + version: 1.0.0 +actions: + - target: $.paths.*.get.parameters + update: + name: newParam + in: query + schema: + type: string diff --git a/tests/v1.1/pass/actions-array-modification-example-update.yaml b/tests/v1.1/pass/actions-array-modification-example-update.yaml new file mode 100644 index 0000000..29c0059 --- /dev/null +++ b/tests/v1.1/pass/actions-array-modification-example-update.yaml @@ -0,0 +1,7 @@ +overlay: 1.1.0 +info: + title: Remove a array element + version: 1.0.0 +actions: + - target: $.paths.*.get.parameters[?@.name == 'dummy'] + remove: true diff --git a/tests/v1.1/pass/actions-description.yaml b/tests/v1.1/pass/actions-description.yaml new file mode 100644 index 0000000..80e00d9 --- /dev/null +++ b/tests/v1.1/pass/actions-description.yaml @@ -0,0 +1,8 @@ +overlay: 1.1.0 +info: + title: Actions Description + version: 1.0.0 +actions: + - target: '$' # Root of document + description: this is an action description + update: {} diff --git a/tests/v1.1/pass/actions-extensions.yaml b/tests/v1.1/pass/actions-extensions.yaml new file mode 100644 index 0000000..88eed5d --- /dev/null +++ b/tests/v1.1/pass/actions-extensions.yaml @@ -0,0 +1,8 @@ +overlay: 1.1.0 +info: + title: Actions Extensions + version: 1.0.0 +actions: + - target: '$' # Root of document + x-myActionsExtension: {} + update: {} diff --git a/tests/v1.1/pass/actions-structured-overlay-example.yaml b/tests/v1.1/pass/actions-structured-overlay-example.yaml new file mode 100644 index 0000000..1f6d714 --- /dev/null +++ b/tests/v1.1/pass/actions-structured-overlay-example.yaml @@ -0,0 +1,22 @@ +overlay: 1.1.0 +info: + title: Structured Overlay + version: 1.0.0 +extends: '/openapi.yaml' +actions: + - target: '$' # Root of document + update: + info: + x-overlay-applied: structured-overlay + paths: + '/': + summary: 'The root resource' + get: + summary: 'Retrieve the root resource' + x-rate-limit: 100 + '/pets': + get: + summary: 'Retrieve a list of pets' + x-rate-limit: 100 + components: + tags: [] diff --git a/tests/v1.1/pass/actions-targeted-overlay-example.yaml b/tests/v1.1/pass/actions-targeted-overlay-example.yaml new file mode 100644 index 0000000..dd7fb91 --- /dev/null +++ b/tests/v1.1/pass/actions-targeted-overlay-example.yaml @@ -0,0 +1,16 @@ +overlay: 1.1.0 +info: + title: Targeted Overlay + version: 1.0.0 +actions: + - target: $.paths['/foo'].get + update: + description: This is the new description + - target: $.paths['/bar'].get + update: + description: This is the updated description + - target: $.paths['/bar'] + update: + post: + description: This is an updated description of a child object + x-safe: false diff --git a/tests/v1.1/pass/actions-traits-example.yaml b/tests/v1.1/pass/actions-traits-example.yaml new file mode 100644 index 0000000..1863558 --- /dev/null +++ b/tests/v1.1/pass/actions-traits-example.yaml @@ -0,0 +1,14 @@ +overlay: 1.1.0 +info: + title: Apply Traits + version: 1.0.0 +actions: + - target: $.paths.*.get[?@.x-oai-traits.paged] + update: + parameters: + - name: top + in: query + # ... + - name: skip + in: query + # ... diff --git a/tests/v1.1/pass/actions-wildcard-overlay-example.yaml b/tests/v1.1/pass/actions-wildcard-overlay-example.yaml new file mode 100644 index 0000000..7897225 --- /dev/null +++ b/tests/v1.1/pass/actions-wildcard-overlay-example.yaml @@ -0,0 +1,12 @@ +overlay: 1.1.0 +info: + title: Update many objects at once + version: 1.0.0 +actions: + - target: $.paths.*.get + update: + x-safe: true + - target: $.paths.*.get.parameters[?@.name=='filter' && @.in=='query'] + update: + schema: + $ref: '#/components/schemas/filterSchema' diff --git a/tests/v1.1/pass/info-extensions.yaml b/tests/v1.1/pass/info-extensions.yaml new file mode 100644 index 0000000..b8d3471 --- /dev/null +++ b/tests/v1.1/pass/info-extensions.yaml @@ -0,0 +1,8 @@ +overlay: 1.1.0 +info: + title: Info Extensions + version: 1.0.0 + x-myInfoExtension: {} +actions: + - target: '$' # Root of document + update: {} diff --git a/tests/v1.1/pass/minimal.yaml b/tests/v1.1/pass/minimal.yaml new file mode 100644 index 0000000..894f630 --- /dev/null +++ b/tests/v1.1/pass/minimal.yaml @@ -0,0 +1,7 @@ +overlay: 1.1.0 +info: + title: Minimal + version: 1.0.0 +actions: + - target: '$' # Root of document + update: {} diff --git a/tests/v1.1/pass/root-extensions.yaml b/tests/v1.1/pass/root-extensions.yaml new file mode 100644 index 0000000..e76abfc --- /dev/null +++ b/tests/v1.1/pass/root-extensions.yaml @@ -0,0 +1,8 @@ +overlay: 1.1.0 +info: + title: Root Extensions + version: 1.0.0 +actions: + - target: '$' # Root of document + update: {} +x-myExtension: {}