From f3d4f8f5f90a990bcb256ede7ecf38d0821df7a9 Mon Sep 17 00:00:00 2001 From: Elmer Bulthuis Date: Sun, 5 May 2024 15:39:44 +0200 Subject: [PATCH] support uuid format validation (#101) - see https://github.com/LuvDaSun/JsonSchema42/issues/99 uuid format validation and sets up future format validation --- .gitignore | 2 +- .prettierignore | 2 +- JsonSchema42.code-workspace | 4 - Makefile | 14 --- cspell.config.yaml | 1 + fixtures/testing/uuid-or-handle.yaml | 28 +++++ packages/npm/jns42-core/package.json | 6 +- .../npm/jns42-core/src/imports/schema-item.ts | 4 +- packages/npm/jns42-core/tsconfig.json | 2 +- packages/npm/jns42-generator/package.json | 6 +- .../src/generators/clean-js.ts | 2 +- .../src/generators/package-json.ts | 6 +- .../jns42-generator/src/generators/package.ts | 2 +- .../src/generators/tsconfig-json.ts | 2 +- .../src/generators/validators-ts.ts | 98 ++++++++++++++- packages/npm/jns42-generator/tsconfig.json | 2 +- .../.vscode/extensions.json | 4 - .../oas/schema-intermediate/src/schema.yaml | 113 ------------------ 18 files changed, 144 insertions(+), 154 deletions(-) create mode 100644 fixtures/testing/uuid-or-handle.yaml delete mode 100644 packages/oas/schema-intermediate/.vscode/extensions.json delete mode 100644 packages/oas/schema-intermediate/src/schema.yaml diff --git a/.gitignore b/.gitignore index a9d546d06..8c9aa8bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ node_modules/ target/ coverage/ transpiled/ -types/ +typed/ bundled/ out/ bin/ diff --git a/.prettierignore b/.prettierignore index ed7a7b521..25bb6e095 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,3 @@ transpiled/ -types/ +typed/ bundled/ diff --git a/JsonSchema42.code-workspace b/JsonSchema42.code-workspace index ef640f467..fbaa8292b 100644 --- a/JsonSchema42.code-workspace +++ b/JsonSchema42.code-workspace @@ -8,10 +8,6 @@ "name": "fixtures", "path": "fixtures", }, - { - "name": "schema-intermediate", - "path": "packages/oas/schema-intermediate", - }, { "name": "npm/jns42-generator", "path": "packages/npm/jns42-generator", diff --git a/Makefile b/Makefile index 629e8ca0b..84f9ba72a 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,7 @@ clean: \ rm -rf generated rm -rf target - generated/npm: \ - generated/npm/schema-intermediate \ generated/npm/schema-draft-04 \ generated/npm/schema-draft-2020-12 \ generated/npm/schema-oas-v3-1 \ @@ -27,18 +25,6 @@ generated/npm: \ # Link the generated code, but don't save those links to the package lock npm install --no-package-lock -generated/cargo: \ - generated/cargo/schema-intermediate \ - - -generated/npm/schema-intermediate: packages/oas/schema-intermediate/src/schema.yaml - mkdir -p $(@D) - - npx jns42-generator package $< \ - --package-directory $@ \ - --package-name @jns42/$(notdir $(basename $@)) \ - --package-version $(shell npx jns42-generator --version) \ - generated/npm/schema-draft-04: mkdir -p $(@D) diff --git a/cspell.config.yaml b/cspell.config.yaml index 6ae43932c..4fecba9bc 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -1,5 +1,6 @@ version: "0.2" words: + - ABNF - automagically - bindgen - Bulthuis diff --git a/fixtures/testing/uuid-or-handle.yaml b/fixtures/testing/uuid-or-handle.yaml new file mode 100644 index 000000000..4ddad712f --- /dev/null +++ b/fixtures/testing/uuid-or-handle.yaml @@ -0,0 +1,28 @@ +$schema: "./schema.json" + +rootTypeName: UuidOrHandleYaml + +schemas: + draft_2020-12: + $schema: "https://json-schema.org/draft/2020-12/schema" + oneOf: + - $ref: "#/$defs/uuid" + - $ref: "#/$defs/handle" + $defs: + uuid: + type: string + format: uuid + handle: + type: string + minLength: 5 + pattern: ^[a-z0-9]+$ + not: + format: uuid + +valid: + uuid: 3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a + handle: handle123 + +invalid: + not-uuid: 3e4666bf-d5e5-4aa7-b8ce-cefe41c7568x + not-handle: handle-123 diff --git a/packages/npm/jns42-core/package.json b/packages/npm/jns42-core/package.json index 59b8c15b9..e6c723346 100644 --- a/packages/npm/jns42-core/package.json +++ b/packages/npm/jns42-core/package.json @@ -6,16 +6,16 @@ "type": "module", "main": "./bundled/main.cjs", "module": "./bundled/main.js", - "types": "./types/main.d.ts", + "types": "./typed/main.d.ts", "exports": { ".": { "require": "./bundled/main.cjs", "import": "./bundled/main.js", - "types": "./types/main.d.ts" + "types": "./typed/main.d.ts" } }, "files": [ - "./types/**", + "./typed/**", "./bundled/**", "./bin/**" ], diff --git a/packages/npm/jns42-core/src/imports/schema-item.ts b/packages/npm/jns42-core/src/imports/schema-item.ts index 5476ba494..ffc5cc404 100644 --- a/packages/npm/jns42-core/src/imports/schema-item.ts +++ b/packages/npm/jns42-core/src/imports/schema-item.ts @@ -50,8 +50,8 @@ export type SchemaItemValue = { multipleOf?: number; minimumLength?: number; maximumLength?: number; - valuePattern?: string[]; - valueFormat?: string[]; + valuePattern?: string; + valueFormat?: string; minimumItems?: number; maximumItems?: number; uniqueItems?: boolean; diff --git a/packages/npm/jns42-core/tsconfig.json b/packages/npm/jns42-core/tsconfig.json index 2f39409fb..15db8ec94 100644 --- a/packages/npm/jns42-core/tsconfig.json +++ b/packages/npm/jns42-core/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./transpiled", - "declarationDir": "./types", + "declarationDir": "./typed", "sourceMap": true, "declaration": true, "composite": true, diff --git a/packages/npm/jns42-generator/package.json b/packages/npm/jns42-generator/package.json index 5d04c5608..e72e886db 100644 --- a/packages/npm/jns42-generator/package.json +++ b/packages/npm/jns42-generator/package.json @@ -6,16 +6,16 @@ "type": "module", "main": "./bundled/main.cjs", "module": "./bundled/main.js", - "types": "./types/main.d.ts", + "types": "./typed/main.d.ts", "exports": { ".": { "require": "./bundled/main.cjs", "import": "./bundled/main.js", - "types": "./types/main.d.ts" + "types": "./typed/main.d.ts" } }, "files": [ - "./types/**", + "./typed/**", "./bundled/**" ], "bin": { diff --git a/packages/npm/jns42-generator/src/generators/clean-js.ts b/packages/npm/jns42-generator/src/generators/clean-js.ts index a13fdbce4..0d4483e07 100644 --- a/packages/npm/jns42-generator/src/generators/clean-js.ts +++ b/packages/npm/jns42-generator/src/generators/clean-js.ts @@ -16,7 +16,7 @@ export function* generateCleanJsCode() { yield itt` fs.rmSync(path.resolve("transpiled"), { recursive: true, force: true }); - fs.rmSync(path.resolve("types"), { recursive: true, force: true }); + fs.rmSync(path.resolve("typed"), { recursive: true, force: true }); fs.rmSync(path.resolve("bundled"), { recursive: true, force: true }); `; } diff --git a/packages/npm/jns42-generator/src/generators/package-json.ts b/packages/npm/jns42-generator/src/generators/package-json.ts index f05b7b346..e3b0043c0 100644 --- a/packages/npm/jns42-generator/src/generators/package-json.ts +++ b/packages/npm/jns42-generator/src/generators/package-json.ts @@ -9,15 +9,15 @@ export function generatePackageJsonData(name: string, version: string) { type: "module", main: "./bundled/main.cjs", module: "./bundled/main.js", - types: "./types/main.d.ts", + types: "./typed/main.d.ts", exports: { ".": { require: "./bundled/main.cjs", import: "./bundled/main.js", - types: "./types/main.d.ts", + types: "./typed/main.d.ts", }, }, - files: ["./types/**", "./bundled/**"], + files: ["./typed/**", "./bundled/**"], scripts: { prepack: "node ./scripts/build.js", pretest: "tsc", diff --git a/packages/npm/jns42-generator/src/generators/package.ts b/packages/npm/jns42-generator/src/generators/package.ts index 77cbb77a2..816ace4d1 100644 --- a/packages/npm/jns42-generator/src/generators/package.ts +++ b/packages/npm/jns42-generator/src/generators/package.ts @@ -104,7 +104,7 @@ export function generatePackage( !.gitignore *.tsbuildinfo transpiled/ - types/ + typed/ bundled/ `; const filePath = path.join(packageDirectoryPath, ".gitignore"); diff --git a/packages/npm/jns42-generator/src/generators/tsconfig-json.ts b/packages/npm/jns42-generator/src/generators/tsconfig-json.ts index c39894790..200e68caa 100644 --- a/packages/npm/jns42-generator/src/generators/tsconfig-json.ts +++ b/packages/npm/jns42-generator/src/generators/tsconfig-json.ts @@ -4,7 +4,7 @@ export function generateTsconfigJsonData() { compilerOptions: { rootDir: "./src", outDir: "./transpiled", - declarationDir: "./types", + declarationDir: "./typed", sourceMap: true, declaration: true, composite: true, diff --git a/packages/npm/jns42-generator/src/generators/validators-ts.ts b/packages/npm/jns42-generator/src/generators/validators-ts.ts index 6e057ddd9..7fc5d4652 100644 --- a/packages/npm/jns42-generator/src/generators/validators-ts.ts +++ b/packages/npm/jns42-generator/src/generators/validators-ts.ts @@ -308,7 +308,8 @@ export function* generateValidatorsTsCode(specification: models.Specification) { if ( itemValue.minimumLength != null || itemValue.maximumLength != null || - itemValue.valuePattern != null + itemValue.valuePattern != null || + itemValue.valueFormat != null ) { yield itt` if( @@ -351,6 +352,21 @@ export function* generateValidatorsTsCode(specification: models.Specification) { } `; } + + if (itemValue.valueFormat != null) { + const isValueFormatExpression = getValueFormatExpression( + itemValue.valueFormat, + valueExpression, + ); + yield itt` + if( + !${isValueFormatExpression} + ) { + recordError("valueFormat"); + return false; + } + `; + } } } @@ -788,3 +804,83 @@ export function* generateValidatorsTsCode(specification: models.Specification) { `; } } + +/** + * @see https://json-schema.org/understanding-json-schema/reference/string#built-in-formats + */ +function getValueFormatExpression(format: string, expression: string) { + switch (format) { + case "date-time": // Date and time together, for example, 2018-11-13T20:20:39+00:00. + return JSON.stringify(true); + + case "time": // New in draft 7 + // Time, for example, 20:20:39+00:00 + return JSON.stringify(true); + + case "date": // New in draft 7 + // Date, for example, 2018-11-13. + return `/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(${expression})`; + + case "duration": // New in draft 2019-09 + // A duration as defined by the ISO 8601 ABNF for "duration". For example, P3D expresses a duration of 3 days. + return JSON.stringify(true); + + case "email": // Internet email address, see RFC 5321, section 4.1.2. + return JSON.stringify(true); + + case "idn-email": // New in draft 7 + // The internationalized form of an Internet email address, see RFC 6531. + return JSON.stringify(true); + + case "hostname": // Internet host name, see RFC 1123, section 2.1. + return JSON.stringify(true); + + case "idn-hostname": // New in draft 7 + // An internationalized Internet host name, see RFC5890, section 2.3.2.3. + return JSON.stringify(true); + + case "ipv4": // IPv4 address, according to dotted-quad ABNF syntax as defined in RFC 2673, section 3.2. + return JSON.stringify(true); + + case "ipv6": // IPv6 address, as defined in RFC 2373, section 2.2. + return JSON.stringify(true); + + case "uuid": // New in draft 2019-09 + // A Universally Unique Identifier as defined by RFC 4122. Example: 3e4666bf-d5e5-4aa7-b8ce-cefe41c7568a + return `/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(${expression})`; + + case "uri": // A universal resource identifier (URI), according to RFC3986. + return JSON.stringify(true); + + case "uri-reference": // New in draft 6 + // A URI Reference (either a URI or a relative-reference), according to RFC3986, section 4.1. + return JSON.stringify(true); + + case "iri": // New in draft 7 + // The internationalized equivalent of a "uri", according to RFC3987. + return JSON.stringify(true); + + case "iri-reference": //New in draft 7 + // The internationalized equivalent of a "uri-reference", according to RFC3987 + return JSON.stringify(true); + + case "uri-template": // New in draft 6 + // A URI Template (of any level) according to RFC6570. If you don't already know what a URI Template is, you probably don't need this value. + return JSON.stringify(true); + + case "json-pointer": // New in draft 6 + // A JSON Pointer, according to RFC6901. There is more discussion on the use of JSON Pointer within JSON Schema in Structuring a complex schema. Note that this should be used only when the entire string contains only JSON Pointer content, e.g. /foo/bar. JSON Pointer URI fragments, e.g. #/foo/bar/ should use "uri-reference". + return JSON.stringify(true); + + case "relative-json-pointer": // New in draft 7 + // A relative JSON pointer. + return JSON.stringify(true); + + case "regex": // New in draft 7 + // A regular expression, which should be valid according to the ECMA 262 dialect. + return JSON.stringify(true); + + default: + return JSON.stringify(true); + } +} diff --git a/packages/npm/jns42-generator/tsconfig.json b/packages/npm/jns42-generator/tsconfig.json index 2f39409fb..15db8ec94 100644 --- a/packages/npm/jns42-generator/tsconfig.json +++ b/packages/npm/jns42-generator/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./transpiled", - "declarationDir": "./types", + "declarationDir": "./typed", "sourceMap": true, "declaration": true, "composite": true, diff --git a/packages/oas/schema-intermediate/.vscode/extensions.json b/packages/oas/schema-intermediate/.vscode/extensions.json deleted file mode 100644 index 2fa7cc2f2..000000000 --- a/packages/oas/schema-intermediate/.vscode/extensions.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "recommendations": ["esbenp.prettier-vscode", "streetsidesoftware.code-spell-checker"], - "unwantedRecommendations": [] -} diff --git a/packages/oas/schema-intermediate/src/schema.yaml b/packages/oas/schema-intermediate/src/schema.yaml deleted file mode 100644 index 64e9b6725..000000000 --- a/packages/oas/schema-intermediate/src/schema.yaml +++ /dev/null @@ -1,113 +0,0 @@ -$schema: "https://json-schema.org/draft/2020-12/schema" -$id: "https://schema.JsonSchema42.org/jns42-intermediate/schema.json" -title: "JsonSchema42 intermediate schema" -type: object -required: - - $schema - - schemas -properties: - $schema: - "type": "string" - "const": "https://schema.JsonSchema42.org/jns42-intermediate/schema.json" - schemas: - type: object - additionalProperties: { $ref: "#/$defs/node" } -$defs: - node: - type: object - properties: - title: { $ref: "#/$defs/non-empty-string-value" } - description: { $ref: "#/$defs/non-empty-string-value" } - examples: - type: array - items: true - deprecated: { $ref: "#/$defs/boolean-value" } - - types: - description: > - What types does this schema describe - type: array - items: - type: string - enum: - - never - - any - - "null" - - boolean - - integer - - number - - string - - array - - map - - reference: { $ref: "#/$defs/node-reference" } - oneOf: - type: array - items: { $ref: "#/$defs/node-reference" } - anyOf: - type: array - items: { $ref: "#/$defs/node-reference" } - allOf: - type: array - items: { $ref: "#/$defs/node-reference" } - if: { $ref: "#/$defs/node-reference" } - then: { $ref: "#/$defs/node-reference" } - else: { $ref: "#/$defs/node-reference" } - not: { $ref: "#/$defs/node-reference" } - dependentSchemas: - type: object - additionalProperties: { $ref: "#/$defs/node-reference" } - objectProperties: - type: object - additionalProperties: { $ref: "#/$defs/node-reference" } - mapProperties: { $ref: "#/$defs/node-reference" } - patternProperties: - type: object - additionalProperties: { $ref: "#/$defs/node-reference" } - propertyNames: { $ref: "#/$defs/node-reference" } - tupleItems: - type: array - items: { $ref: "#/$defs/node-reference" } - arrayItems: { $ref: "#/$defs/node-reference" } - contains: { $ref: "#/$defs/node-reference" } - - options: - type: array - items: true - minimumInclusive: { $ref: "#/$defs/number-value" } - minimumExclusive: { $ref: "#/$defs/number-value" } - maximumInclusive: { $ref: "#/$defs/number-value" } - maximumExclusive: { $ref: "#/$defs/number-value" } - multipleOf: { $ref: "#/$defs/number-value" } - minimumLength: { $ref: "#/$defs/amount" } - maximumLength: { $ref: "#/$defs/amount" } - valuePattern: { $ref: "#/$defs/non-empty-string-value" } - valueFormat: { $ref: "#/$defs/non-empty-string-value" } - minimumItems: { $ref: "#/$defs/amount" } - maximumItems: { $ref: "#/$defs/amount" } - uniqueItems: - type: boolean - required: - type: array - items: { $ref: "#/$defs/string-value" } - uniqueItems: true - minimumProperties: { $ref: "#/$defs/amount" } - maximumProperties: { $ref: "#/$defs/amount" } - - node-reference: - type: string - minimumLength: 1 - integer-value: - type: integer - number-value: - type: number - boolean-value: - type: boolean - string-value: - type: string - non-empty-string-value: - type: string - minimumLength: 1 - amount: - type: integer - minimumInclusive: 0