From 3505c9015c5978bbd9e00775699f2d28f6bc0bda Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 10:08:42 +0200 Subject: [PATCH 01/30] Create npm-publish.yml --- .github/workflows/npm-publish.yml | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/npm-publish.yml diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..2a4766d --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,33 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages + +name: Node.js Package + +on: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm ci + - run: npm test + + publish-npm: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.npm_token}} From 780d7c282e0a00affaac34335b388f061e04af50 Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 10:12:30 +0200 Subject: [PATCH 02/30] fix: update repo urls --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e087389..f711da4 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { - "name": "json-dereference-cli", - "author": "David Kelley", + "name": "json-dereference-cli-v2", + "author": "Siddharth Yadav", "description": "Provides a cli interface to the json-schema-ref-parser library", "bin": { "json-dereference": "./dereference.js" }, "bugs": { - "url": "https://github.com/davikelley/json-dereference-cli/issues" + "url": "https://github.com/sedflix/json-dereference-cli-v2/issues" }, - "homepage": "https://github.com/davikelley/json-dereference-cli", + "homepage": "https://github.com/sedflix/json-dereference-cli-v2", "keywords": [ "json", "schema", @@ -26,7 +26,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/davidkelley/json-dereference-cli" + "url": "https://github.com/sedflix/json-dereference-cli-v2" }, "dependencies": { "aws-sdk": "^2.80.0", From f102cb05589ec28fd14175b7a030eb59199555c5 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 10:25:37 +0200 Subject: [PATCH 03/30] patch https://github.com/ahochsteger/json-dereference-cli/commit/aeb77432546ec698c30a9bb96dddfd1e1f2e34b2.diff --- README.md | 16 ++++++++------ dereference.js | 58 ++++++++++++++++++++++++++++++++++---------------- package.json | 10 ++++----- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 122e799..6b4391b 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,19 @@ ## Usage -``` +```bash +# Using npx: +npx json-dereference-cli -s [-b ] [-i ] [-o ] [-t ] + +# Installing globally: npm install -g json-dereference-cli -json-dereference -s my-schema.json -o compiled-schema.yaml +json-dereference -s [-b ] [-i ] [-o ] [-t ] ``` -_Note: The input file can either be `json`, or `yaml` / `yml`._ +*Note:* The input file can either be `json`, or `yaml` / `yml`. -_Note: The output file types are either `json` or `yaml` / `yml`. This is determined from the file extension for the output file path passed in._ +*Note:* The output file types are either `json` or `yaml` / `yml`. This is determined from the file extension for the output file path passed in or using `-t json|yaml` when writing to stdout. -### $ref: "s3://.." +## Resolving s3 references -This CLI tool will also attempt to resolve S3 references using the `aws-sdk`. Take a look [here](/s3-resolver.js) to see the custom resolver code. +This CLI tool will also attempt to resolve S3 references (`$ref: "s3://.."`) using the `aws-sdk`. Take a look [here](s3-resolver.js) to see the custom resolver code. diff --git a/dereference.js b/dereference.js index 7543c52..db84058 100644 --- a/dereference.js +++ b/dereference.js @@ -3,40 +3,62 @@ var fs = require('fs'); var path = require('path'); var util = require('util'); -var $RefParser = require('json-schema-ref-parser'); +var $RefParser = require('@apidevtools/json-schema-ref-parser'); var argv = require('minimist')(process.argv.slice(2)); var s3Resolver = require('./s3-resolver'); +var yaml = require('js-yaml') -if (!argv.s || !argv.o) { - console.log('USAGE: ' + process.argv[1] + ' -s -o [...]'); +// Argument handling +if (!argv.s) { + console.error('Usage: ' + process.argv[1] + ' -s [-b ] [-i ] [-o ] [-t ]'); process.exit(1); } - if (argv.b) s3Resolver.bucket = argv.b; - var input = path.resolve(argv.s); +var output = argv.o ? path.resolve(argv.o) : undefined; +var indent = argv.i !== undefined ? argv.i : 2 -var schema = fs.readFileSync(input, { encoding: 'utf8' }); - -console.log("Dereferencing schema: " + input); - +// Schema dereferencing +console.warn("Dereferencing schema: " + input); $RefParser.dereference(input, { resolve: { s3: s3Resolver } }, function(err, schema) { if (err) { - console.error(err); - } else { - var output = path.resolve(argv.o); + console.error('ERROR: ' + err); + return + } + + // Detect output format + var type = 'json' + if (argv.t) { + type = argv.t + } else if (output) { var ext = path.parse(output).ext; if (ext == '.json') { - var data = JSON.stringify(schema); - fs.writeFileSync(output, data, { encoding: 'utf8', flag: 'w' }); + type = 'json' } else if (ext.match(/^\.?(yaml|yml)$/)) { - var yaml = require('node-yaml'); - yaml.writeSync(output, schema, { encoding: 'utf8' }) + type = 'yaml' } else { - console.error("Unrecognised output file type: " + output); + console.error('ERROR: Cannot detect output file type from file name (please set -t ): ' + output); process.exit(1); } - console.log("Wrote file: " + output); + } + + // Generate resolved schema type + var data = '' + if (type == 'json') { + data = JSON.stringify(schema, null, indent); + } else if (type == 'yaml') { + data = yaml.dump(schema, { encoding: 'utf8', indent: indent, noRefs: true }); + } else { + console.error('ERROR: Unsupported output file type: ' + type); + process.exit(1); + } + + // Output resolved schema + if (output) { + fs.writeFileSync(output, data, { encoding: 'utf8', flag: 'w' }); + console.warn('Wrote file: ' + output); + } else { + console.log(data) } }); diff --git a/package.json b/package.json index e087389..5a33d36 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "0.1.2", + "version": "0.2.0", "main": "index.js", "engines": { "node": ">=4.3" @@ -29,9 +29,9 @@ "url": "https://github.com/davidkelley/json-dereference-cli" }, "dependencies": { - "aws-sdk": "^2.80.0", - "json-schema-ref-parser": "^3.1.2", - "minimist": "^1.2.0", - "node-yaml": "^3.1.0" + "aws-sdk": "^2.1303.0", + "@apidevtools/json-schema-ref-parser": "^10.0.1", + "minimist": "^1.2.7", + "js-yaml": "^4.1.0" } } From 1eb3ecbbc5de1b8fdbeb45894b291987af902933 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 10:33:01 +0200 Subject: [PATCH 04/30] feat: remove s3 support --- .gitignore | 1 + .npmignore | 3 ++ dereference.js | 82 ++++++++++++++++++++++---------------------- package.json | 12 +++---- s3-resolver.js | 33 ------------------ testdata/output.json | 16 +++++++++ testdata/schema.json | 9 +++++ 7 files changed, 76 insertions(+), 80 deletions(-) delete mode 100644 s3-resolver.js create mode 100644 testdata/output.json create mode 100644 testdata/schema.json diff --git a/.gitignore b/.gitignore index 3c3629e..34977ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +.idea \ No newline at end of file diff --git a/.npmignore b/.npmignore index b43bf86..6a79c8c 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,4 @@ README.md +testdata +.github +.idea diff --git a/dereference.js b/dereference.js index db84058..ef2b013 100644 --- a/dereference.js +++ b/dereference.js @@ -5,60 +5,60 @@ var path = require('path'); var util = require('util'); var $RefParser = require('@apidevtools/json-schema-ref-parser'); var argv = require('minimist')(process.argv.slice(2)); -var s3Resolver = require('./s3-resolver'); var yaml = require('js-yaml') // Argument handling if (!argv.s) { - console.error('Usage: ' + process.argv[1] + ' -s [-b ] [-i ] [-o ] [-t ]'); - process.exit(1); + console.error('Usage: ' + process.argv[1] + ' -s [-i ] [-o ] [-t ]'); + process.exit(1); } -if (argv.b) s3Resolver.bucket = argv.b; + var input = path.resolve(argv.s); var output = argv.o ? path.resolve(argv.o) : undefined; var indent = argv.i !== undefined ? argv.i : 2 // Schema dereferencing console.warn("Dereferencing schema: " + input); -$RefParser.dereference(input, { resolve: { s3: s3Resolver } }, function(err, schema) { - if (err) { - console.error('ERROR: ' + err); - return - } - - // Detect output format - var type = 'json' - if (argv.t) { - type = argv.t - } else if (output) { - var ext = path.parse(output).ext; +$RefParser.dereference(input, {resolve: {}}, function (err, schema) { + if (err) { + console.error('ERROR: ' + err); + process.exit(1); + return + } - if (ext == '.json') { - type = 'json' - } else if (ext.match(/^\.?(yaml|yml)$/)) { - type = 'yaml' - } else { - console.error('ERROR: Cannot detect output file type from file name (please set -t ): ' + output); - process.exit(1); + // Detect output format + var type = 'json' + if (argv.t) { + type = argv.t + } else if (output) { + var ext = path.parse(output).ext; + + if (ext == '.json') { + type = 'json' + } else if (ext.match(/^\.?(yaml|yml)$/)) { + type = 'yaml' + } else { + console.error('ERROR: Cannot detect output file type from file name (please set -t ): ' + output); + process.exit(1); + } } - } - // Generate resolved schema type - var data = '' - if (type == 'json') { - data = JSON.stringify(schema, null, indent); - } else if (type == 'yaml') { - data = yaml.dump(schema, { encoding: 'utf8', indent: indent, noRefs: true }); - } else { - console.error('ERROR: Unsupported output file type: ' + type); - process.exit(1); - } + // Generate resolved schema type + var data = '' + if (type == 'json') { + data = JSON.stringify(schema, null, indent); + } else if (type == 'yaml') { + data = yaml.dump(schema, {encoding: 'utf8', indent: indent, noRefs: true}); + } else { + console.error('ERROR: Unsupported output file type: ' + type); + process.exit(1); + } - // Output resolved schema - if (output) { - fs.writeFileSync(output, data, { encoding: 'utf8', flag: 'w' }); - console.warn('Wrote file: ' + output); - } else { - console.log(data) - } + // Output resolved schema + if (output) { + fs.writeFileSync(output, data, {encoding: 'utf8', flag: 'w'}); + console.warn('Wrote file: ' + output); + } else { + console.log(data) + } }); diff --git a/package.json b/package.json index 5a33d36..75dfb17 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { - "name": "json-dereference-cli", - "author": "David Kelley", + "name": "json-dereference-cli-v2", + "author": "Siddharth", "description": "Provides a cli interface to the json-schema-ref-parser library", "bin": { "json-dereference": "./dereference.js" }, "bugs": { - "url": "https://github.com/davikelley/json-dereference-cli/issues" + "url": "https://github.com/sedflix/json-dereference-cli-v2/issues" }, - "homepage": "https://github.com/davikelley/json-dereference-cli", + "homepage": "https://github.com/sedflix/json-dereference-cli-v2", "keywords": [ "json", "schema", @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "0.2.0", + "version": "0.3.0", "main": "index.js", "engines": { "node": ">=4.3" @@ -26,7 +26,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/davidkelley/json-dereference-cli" + "url": "https://github.com/sedflix/json-dereference-cli-v3" }, "dependencies": { "aws-sdk": "^2.1303.0", diff --git a/s3-resolver.js b/s3-resolver.js deleted file mode 100644 index 1553a3f..0000000 --- a/s3-resolver.js +++ /dev/null @@ -1,33 +0,0 @@ -var S3 = require('aws-sdk/clients/s3'); - -module.exports = { - order: 1, - - canRead: /^s3:/i, - - read: function(file, callback) { - var params; - - if (this.bucket) { - var parts = file.url.match(/^s3:\/\/(.+)$/); - params = { Bucket: this.bucket, Key: parts[1] }; - } else { - var parts = file.url.match(/^s3:\/\/(.+?)\/(.+)$/); - params = { Bucket: parts[1], Key: parts[2] }; - } - - if (!(params.Bucket && params.Key)) { - callback(new Error("Malformed params object: " + file.url)); - } else { - var s3 = new S3(); - - s3.getObject(params, function(err, data) { - if (err) { - callback(err); - } else { - callback(null, data.Body.toString()); - } - }); - } - } -}; diff --git a/testdata/output.json b/testdata/output.json new file mode 100644 index 0000000..6bf2faa --- /dev/null +++ b/testdata/output.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "test": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + } +} \ No newline at end of file diff --git a/testdata/schema.json b/testdata/schema.json new file mode 100644 index 0000000..778175b --- /dev/null +++ b/testdata/schema.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "test": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + } + } +} \ No newline at end of file From d341fe508da59f0f9e9ce20ad495f080072f4e47 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 10:46:08 +0200 Subject: [PATCH 05/30] feat: update dependency and add test data --- package.json | 7 +++---- testdata/{schema.json => correct.schema.json} | 0 testdata/error.schema.json | 9 +++++++++ testdata/test.sh | 0 4 files changed, 12 insertions(+), 4 deletions(-) rename testdata/{schema.json => correct.schema.json} (100%) create mode 100644 testdata/error.schema.json create mode 100644 testdata/test.sh diff --git a/package.json b/package.json index ae122fa..a4c774d 100644 --- a/package.json +++ b/package.json @@ -22,16 +22,15 @@ "node": ">=4.3" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "((node dereference.js -s testdata/correct.schema.json && echo \"Success: exit code $?\") && (node dereference.js -s testdata/error.schema.json || echo \"Success: exit code $?\")) && echo \"Test Passed!\"" }, "repository": { "type": "git", "url": "https://github.com/sedflix/json-dereference-cli-v2" }, "dependencies": { - "aws-sdk": "^2.1303.0", - "@apidevtools/json-schema-ref-parser": "^10.0.1", - "minimist": "^1.2.7", + "@apidevtools/json-schema-ref-parser": "^11.9.3", + "minimist": "^1.2.8", "js-yaml": "^4.1.0" } } diff --git a/testdata/schema.json b/testdata/correct.schema.json similarity index 100% rename from testdata/schema.json rename to testdata/correct.schema.json diff --git a/testdata/error.schema.json b/testdata/error.schema.json new file mode 100644 index 0000000..dd75bc0 --- /dev/null +++ b/testdata/error.schema.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "type": "object", + "properties": { + "test": { + "$ref": "https://raw.githubusercontent.com/wrong/kubernetes-wrong-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + } + } +} \ No newline at end of file diff --git a/testdata/test.sh b/testdata/test.sh new file mode 100644 index 0000000..e69de29 From 86e894b171fab5e64c1f1d5ab8c097ab234db15d Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 10:46:38 +0200 Subject: [PATCH 06/30] feat: update doc reflect removal of s3 --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6b4391b..20c9962 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ # JSON Dereference CLI -*Very* simple CLI tool that wraps the excellent [json-schema-ref-parser](https://github.com/BigstickCarpet/json-schema-ref-parser) library. +*Very* simple CLI tool that wraps the +excellent [json-schema-ref-parser](https://github.com/BigstickCarpet/json-schema-ref-parser) library. ## Usage ```bash # Using npx: -npx json-dereference-cli -s [-b ] [-i ] [-o ] [-t ] +npx json-dereference-cli -s [-i ] [-o ] [-t ] # Installing globally: npm install -g json-dereference-cli -json-dereference -s [-b ] [-i ] [-o ] [-t ] +json-dereference -s [-i ] [-o ] [-t ] ``` *Note:* The input file can either be `json`, or `yaml` / `yml`. -*Note:* The output file types are either `json` or `yaml` / `yml`. This is determined from the file extension for the output file path passed in or using `-t json|yaml` when writing to stdout. - -## Resolving s3 references - -This CLI tool will also attempt to resolve S3 references (`$ref: "s3://.."`) using the `aws-sdk`. Take a look [here](s3-resolver.js) to see the custom resolver code. +*Note:* The output file types are either `json` or `yaml` / `yml`. This is determined from the file extension for the +output file path passed in or using `-t json|yaml` when writing to stdout. From 7082e8d4d68c07a7d733456abe401c8d7ec11c14 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 10:47:19 +0200 Subject: [PATCH 07/30] 0.4.0 --- package-lock.json | 73 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f573718 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,73 @@ +{ + "name": "json-dereference-cli-v2", + "version": "0.4.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "json-dereference-cli-v2", + "version": "0.3.0", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.9.3", + "js-yaml": "^4.1.0", + "minimist": "^1.2.8" + }, + "bin": { + "json-dereference": "dereference.js" + }, + "engines": { + "node": ">=4.3" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.9.3", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", + "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + } + } +} diff --git a/package.json b/package.json index a4c774d..b704479 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "0.3.0", + "version": "0.4.0", "main": "index.js", "engines": { "node": ">=4.3" From ac82f746f03de95b9c181c767413bbd04d651b18 Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 10:55:44 +0200 Subject: [PATCH 08/30] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 20c9962..144544c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ *Very* simple CLI tool that wraps the excellent [json-schema-ref-parser](https://github.com/BigstickCarpet/json-schema-ref-parser) library. +[![Node.js Package](https://github.com/sedflix/json-dereference-cli-v2/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/sedflix/json-dereference-cli-v2/actions/workflows/npm-publish.yml) + ## Usage ```bash From fc9216dfcd1185b27d7c7816122044203fba533d Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 10:56:17 +0200 Subject: [PATCH 09/30] Create LICENSE --- LICENSE | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to From 918ad7f988521d5b7dea76224af033a72e948bc9 Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 11:00:25 +0200 Subject: [PATCH 10/30] Update npm-publish.yml --- .github/workflows/npm-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 2a4766d..51a2eb0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -5,7 +5,7 @@ name: Node.js Package on: release: - types: [created] + types: [created, updated] jobs: build: From 73c0090b1ddf125748ef4edea45e8d5682b3ad43 Mon Sep 17 00:00:00 2001 From: siddharth Date: Thu, 3 Apr 2025 11:03:07 +0200 Subject: [PATCH 11/30] Update npm-publish.yml --- .github/workflows/npm-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 51a2eb0..22bff68 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -5,7 +5,7 @@ name: Node.js Package on: release: - types: [created, updated] + types: [created, edited, published, released] jobs: build: From b834868d99760e7e0e9fd08852978f7588fcecee Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 11:03:24 +0200 Subject: [PATCH 12/30] 0.5.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f573718..acb03f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index b704479..027dd2a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "0.4.0", + "version": "0.5.0", "main": "index.js", "engines": { "node": ">=4.3" From 4221a49f990c12af7c79c7a4e81fa4111b8fde64 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 11:10:39 +0200 Subject: [PATCH 13/30] feat: change json-dereference to json-dereference-v2 to avoid conflicts --- README.md | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 144544c..fcbcac0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# JSON Dereference CLI +# JSON Dereference CLI v2 *Very* simple CLI tool that wraps the excellent [json-schema-ref-parser](https://github.com/BigstickCarpet/json-schema-ref-parser) library. @@ -9,11 +9,11 @@ excellent [json-schema-ref-parser](https://github.com/BigstickCarpet/json-schema ```bash # Using npx: -npx json-dereference-cli -s [-i ] [-o ] [-t ] +npx json-dereference-cli-v2 -s [-i ] [-o ] [-t ] # Installing globally: -npm install -g json-dereference-cli -json-dereference -s [-i ] [-o ] [-t ] +npm install -g json-dereference-cli-v2 +json-dereference-v2 -s [-i ] [-o ] [-t ] ``` *Note:* The input file can either be `json`, or `yaml` / `yml`. diff --git a/package.json b/package.json index 027dd2a..ce1c523 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "author": "Siddharth Yadav", "description": "Provides a cli interface to the json-schema-ref-parser library", "bin": { - "json-dereference": "./dereference.js" + "json-dereference-v2": "./dereference.js" }, "bugs": { "url": "https://github.com/sedflix/json-dereference-cli-v2/issues" From 3227b1354e629b65ae991ef7f22bcd0825dfac63 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Thu, 3 Apr 2025 11:10:47 +0200 Subject: [PATCH 14/30] 1.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index acb03f6..d4454f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "0.5.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index ce1c523..dca479a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "0.5.0", + "version": "1.0.0", "main": "index.js", "engines": { "node": ">=4.3" From 4a7e05389923f3ce2596de9f3fe848a198575e39 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 14:13:51 +0200 Subject: [PATCH 15/30] feat: validate schema --- dereference.js | 38 +++++++++++++++++++++---- package-lock.json | 70 +++++++++++++++++++++++++++++++++++++++++++++-- package.json | 4 ++- 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/dereference.js b/dereference.js index ef2b013..1aa434b 100644 --- a/dereference.js +++ b/dereference.js @@ -1,11 +1,26 @@ #!/usr/bin/env node -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var $RefParser = require('@apidevtools/json-schema-ref-parser'); -var argv = require('minimist')(process.argv.slice(2)); -var yaml = require('js-yaml') +const fs = require('fs'); +const path = require('path'); +const $RefParser = require('@apidevtools/json-schema-ref-parser'); +const argv = require('minimist')(process.argv.slice(2)); +const yaml = require('js-yaml'); + +// for schema validation +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); +// Initialize Ajv with 2020-12 draft support +const ajv = new Ajv({ + strict: false, + allowUnionTypes: true, + validateSchema: false, // Skip schema validation initially +}); +addFormats(ajv); +// Load the 2020-12 meta-schema +const draft202012MetaSchema = require('ajv/dist/refs/json-schema-2020-12/schema.json'); +ajv.addMetaSchema(draft202012MetaSchema); + + // Argument handling if (!argv.s) { @@ -26,6 +41,17 @@ $RefParser.dereference(input, {resolve: {}}, function (err, schema) { return } + + // Validate the schema using ajv + const validate = ajv.compile(schema); + const valid = validate(schema); + if (valid) { + console.log('Schema validated successfully!.'); + } else { + console.error("Schema schema validation failed!", validate.errors); + } + + // Detect output format var type = 'json' if (argv.t) { diff --git a/package-lock.json b/package-lock.json index d4454f4..ee1670f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,14 +6,16 @@ "packages": { "": { "name": "json-dereference-cli-v2", - "version": "0.3.0", + "version": "1.1.0", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.9.3", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "js-yaml": "^4.1.0", "minimist": "^1.2.8" }, "bin": { - "json-dereference": "dereference.js" + "json-dereference-v2": "dereference.js" }, "engines": { "node": ">=4.3" @@ -45,11 +47,62 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -61,6 +114,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -68,6 +126,14 @@ "funding": { "url": "https://github.com/sponsors/ljharb" } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } } } } diff --git a/package.json b/package.json index dca479a..3e0524d 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,8 @@ "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.9.3", "minimist": "^1.2.8", - "js-yaml": "^4.1.0" + "js-yaml": "^4.1.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1" } } From ee632aac71fc69701374a006438d61db0e11708e Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 14:48:22 +0200 Subject: [PATCH 16/30] 1.1.0 --- package-lock.json | 2 +- package.json | 2 +- testdata/cloudSQL.schema.json | 540 ++++++++++++++++++++++++++++++++++ testdata/correct.schema.json | 5 +- testdata/test.json | 514 ++++++++++++++++++++++++++++++++ 5 files changed, 1060 insertions(+), 3 deletions(-) create mode 100644 testdata/cloudSQL.schema.json create mode 100644 testdata/test.json diff --git a/package-lock.json b/package-lock.json index ee1670f..2cc332e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 3e0524d..205a075 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "1.0.0", + "version": "1.1.0", "main": "index.js", "engines": { "node": ">=4.3" diff --git a/testdata/cloudSQL.schema.json b/testdata/cloudSQL.schema.json new file mode 100644 index 0000000..2b9e006 --- /dev/null +++ b/testdata/cloudSQL.schema.json @@ -0,0 +1,540 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "$ref": "#/$defs/cloudSQLDescription", + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "$ref": "#/$defs/cloudSQLProperties" + }, + "$defs": { + "cloudSQLDescription": { + "title": "Cloud SQL", + "description": "Create a Cloud SQL instance, with password, user, database; while storing the created credentials in Secret Manager as well as Kubernetes Secrets/Configmap. \n\n Enabling CloudSQL will, in addition to creating the CloudSQL instance and database, automatically generate CloudSQL user password, put it in GCP Secret Manager in your GCP Project and sync it into a Kubernetes Secret (`$service-$environment-sql-password`) with key name `DATABASE_PASSWORD` inside your namespace. It will also create a ConfigMap (`$service-$environment-sql-config`) containing the database connection details in keys `DATABASE_URL`, `DATABASE_USERNAME`, `DATABASE_INSTANCE_NAME`, `DATABASE_NAME`, `GCP_PROJECT_ID`, and `REGION`. \n\n **[See Feature Docs](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql)** \n\n **[See How to Connect](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql#how-to-connect-to-cloudsql-instance)**", + "type": "object", + "examples": [ + { + "enabled": true, + "tier": "db-g1-small" + }, + { + "enabled": true, + "tier": "db-g1-small", + "databaseUser": "event-definer", + "databaseName": "event-definer", + "configMap": { + "databaseUserKey": "DATABASE_USERNAME" + } + }, + { + "enabled": true, + "availabilityType": "REGIONAL", + "charset": "UTF8", + "databaseInstanceName": "flow", + "databaseName": "flow", + "databaseUser": "flow", + "diskAutoresize": true, + "databaseVersion": "POSTGRES_15", + "diskSize": 250, + "edition": "ENTERPRISE_PLUS", + "tier": "db-perf-optimized-N-8", + "secret": { + "kubernetesSecretKeyName": "DB_PASSWORD", + "generateSecret": true + }, + "configMap": { + "databaseUrlKey": "DB_URL", + "databaseUserKey": "DB_USER" + } + }, + { + "enabled": true, + "databaseInstanceName": "$service-$env", + "databaseName": "$service", + "databaseUser": "$service", + "availabilityType": "REGIONAL", + "databaseVersion": "MYSQL_8_0", + "edition": "ENTERPRISE", + "tier": "db-g1-small", + "charset": "utf8mb4", + "collation": "utf8mb4_0900_ai_ci", + "diskAutoresize": true, + "diskSize": 10, + "diskType": "PD_SSD", + "databaseFlags": [ + { + "name": "max_connections", + "value": "10000" + }, + { + "name": "log_min_duration_statement", + "value": "1000" + } + ], + "backupConfiguration": { + "enabled": true, + "startTime": "04:00", + "binaryLogEnabled": true + }, + "maintenanceWindow": { + "day": 7, + "hour": 0 + }, + "insightsConfig": { + "queryInsightsEnabled": false, + "queryPlansPerMinute": 5, + "queryStringLength": 1024, + "recordApplicationTags": false, + "recordClientAddress": false + }, + "secret": { + "kubernetesSecretName": "$service-$env-sql-password", + "kubernetesSecretKeyName": "DATABASE_PASSWORD", + "gcpSecretManagerSecretName": "$service-$env-sql-password", + "generateSecret": true + }, + "configMap": { + "databaseUrlKey": "DATABASE_URL", + "databaseUrlValue": "null", + "databaseUserKey": "DATABASE_USER" + } + } + ], + "additionalProperties": true, + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": false + } + } + }, + "cloudSQLSimple": { + "$ref": "#/$defs/cloudSQLDescription", + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "required": [ + "tier" + ], + "properties": { + "tier": { + "$ref": "#/$defs/cloudSQLProperties/properties/tier" + } + } + } + }, + "cloudSQL": { + "$ref": "#/$defs/cloudSQLDescription", + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "$ref": "#/$defs/cloudSQLProperties" + } + }, + "cloudSQLProperties": { + "required": [ + "tier" + ], + "properties": { + "databaseVersion": { + "title": "Database Version", + "description": "Choose the database version of the Cloud SQL/Database Instance. [See this all available versions](https://cloud.google.com/sql/docs/db-versions). Please change collation/charsets should be changed accordingly, if this is changed to non-mysql version.", + "type": "string", + "default": "MYSQL_8_0", + "examples": [ + "MYSQL_8_0", + "MYSQL_8_0_31", + "POSTGRES_15" + ] + }, + "tier": { + "title": "Machine Type/Tier", + "description": "Choose the machine type of the Cloud SQL instance. db-g1- types are cheapest but not covered by CloudSQL SLA. [See this for all available machine types](https://cloud.google.com/sql/docs/mysql/instance-settings#machine-type-2ndgen). Note different `editions` have different machine types availability", + "type": "string", + "examples": [ + "db-g1-tiny", + "db-g1-small", + "db-n1-standard-1", + "db-n1-standard-2", + "db-n1-standard-4", + "db-n1-standard-8", + "db-n1-standard-16", + "db-n1-standard-32", + "db-n1-highmem-2", + "db-n1-highmem-8", + "db-perf-optimized-N-4", + "db-perf-optimized-N-8" + ] + }, + "availabilityType": { + "title": "Availability Type", + "type": [ + "string", + "null" + ], + "description": "Choose the availability type of the Cloud SQL instance: High Availability (REGIONAL) or Single Zone (ZONAL). **Note: defaults to REGIONAL in live and ZONAL in non-live.**", + "enum": [ + "ZONAL", + "REGIONAL" + ] + }, + "enableIAMAuth": { + "title": "Enable IAM Authentication", + "description": "Enable IAM authentication for the Cloud SQL instance, your application can use Google Servie Account(via workload identity) to connect to the Database. [GCP CloudSQL IAM authentication](https://cloud.google.com/sql/docs/mysql/iam-authentication)", + "type": "boolean", + "x-immutable": true, + "default": false + }, + "databaseInstanceName": { + "title": "CloudSQL Instance Name", + "description": "Name of the Cloud SQL instance. Defaults to `-` if not provided.", + "type": [ + "string", + "null" + ] + }, + "databaseName": { + "title": "MySQL Database Name", + "description": "Name of the MySQL database. Defaults to `` if not provided.", + "type": [ + "string", + "null" + ] + }, + "databaseUser": { + "title": "MySQL Database User Name", + "description": "Name of the MySQL database user. Defaults to `` if not provided.", + "type": [ + "string", + "null" + ] + }, + "diskAutoresize": { + "title": "Disk Autoresize", + "description": "Enable/disable automatic disk resizing. When set to true, the diskSize variable will be ignored.", + "type": "boolean", + "default": true + }, + "diskSize": { + "title": "Disk Size", + "description": "The size of data disk, in GB. Size of a running instance cannot be reduced but can be increased. The minimum value is 10GB. When diskAutoresize is set to true, the diskSize variable will be ignored.", + "type": "number", + "default": null + }, + "diskType": { + "title": "Disk Type", + "description": "Choose the disk type of the Cloud SQL instance. SSDs provide lower latency and higher data throughput. If you do not need high-performance access to your data, for example for long-term storage or rarely accessed data, you can reduce your costs by choosing HDD. HDD requires Cloud SQL Enterprise", + "type": "string", + "enum": [ + "PD_SSD", + "PD_HDD" + ], + "default": "PD_SSD" + }, + "edition": { + "title": "Edition", + "description": "The edition of the instance, can be ENTERPRISE or ENTERPRISE_PLUS. Use ENTERPRISE_PLUS only for production and high performance databases. [See this for comparison between different editions](https://cloud.google.com/sql/docs/mysql/editions-intro)", + "type": "string", + "enum": [ + "ENTERPRISE", + "ENTERPRISE_PLUS" + ], + "default": "ENTERPRISE" + }, + "charset": { + "title": "MySQL Charset", + "description": "Choose the charset of the Cloud SQL instance. [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", + "type": "string", + "examples": [ + "utf8mb4", + "utf16" + ] + }, + "collation": { + "title": "MySQL Collation", + "description": "Choose the collation of the Cloud SQL instance. Usually not required. See for [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", + "type": "string", + "examples": [ + "utf8mb4_0900_ai_ci", + "utf16_general_ci" + ] + }, + "databaseFlags": { + "title": "Database Flags", + "description": "Database flags to be set on the Cloud SQL instance. For more information, see [list of MySQL Flags](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql) and [list of PSQL flags](https://cloud.google.com/sql/docs/postgres/flags#list-flags-postgres)", + "type": "array", + "examples": [ + [ + { + "name": "max_connections", + "value": "10000" + }, + { + "name": "log_min_duration_statement", + "value": "1000" + } + ] + ], + "items": { + "type": "object", + "properties": { + "name": { + "title": "Flag Name", + "type": "string", + "minLength": 1 + }, + "value": { + "title": "Flag Value", + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name", + "value" + ] + } + }, + "backupConfiguration": { + "title": "Backup Configuration", + "type": "object", + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": true + }, + "startTime": { + "title": "Backup Start Time", + "description": "HH:MM format time indicating when backup configuration starts.", + "type": "string", + "default": "04:00" + }, + "binaryLogEnabled": { + "title": "Enable Binary Logging", + "description": "For Cloud SQL for MySQL, point-in-time recovery requires that you enable binary logging. This means that every update to your database is written to an independent log, which involves a small reduction in write performance. Performance of read operations are unaffected by binary logging, regardless of the size of the binary log files.", + "type": "boolean", + "default": false + } + } + }, + "maintenanceWindow": { + "title": "Maintenance Window", + "description": "The day of the week and the hour in which Cloud SQL schedules maintenance. Maintenance windows last for one hour. [More info](https://cloud.google.com/sql/docs/mysql/maintenance)", + "type": "object", + "properties": { + "day": { + "title": "Day", + "description": "Day of week (1-7), starting on Monday.", + "type": "number", + "default": 7 + }, + "hour": { + "title": "Hour", + "description": "Hour of day (0-23), ignored if day not set.", + "type": "number", + "default": 0 + } + } + }, + "insightsConfig": { + "title": "Insights Configuration", + "description": "See [Using Query Insights](https://cloud.google.com/sql/docs/postgres/using-query-insights) and [Use System Insights](https://cloud.google.com/sql/docs/postgres/use-system-insights) for more details", + "type": "object", + "properties": { + "queryInsightsEnabled": { + "title": "Enable Query Insights", + "type": "boolean", + "default": false + }, + "queryPlansPerMinute": { + "title": "Query Plans Per Minute", + "description": "Number of query execution plans captured by Insights per minute for all queries combined. Between 0 and 20. Default to 5.", + "type": "number", + "default": 5 + }, + "queryStringLength": { + "title": "Query String Length", + "description": "Maximum query length stored in bytes. Between 256 and 4500. Default to 1024. Higher query lengths are more useful for analytical queries, but they also require more memory. Changing the query length requires you to restart the instance. You can still add tags to queries that exceed the length limit.", + "type": "number", + "default": 1024 + }, + "recordApplicationTags": { + "title": "Record Application Tags", + "description": "True if Query Insights will record application tags from query when enabled", + "type": "boolean", + "default": false + }, + "recordClientAddress": { + "title": "Record Client Address", + "description": "True if Query Insights will record client address when enabled", + "type": "boolean", + "default": false + } + } + }, + "secret": { + "title": "Database Password Secret", + "description": "Manage the database credentials in Secret Manager and Kubernetes Secret/ConfigMap. Leave it empty to auto-generate password and store it in `DATABASE_PASSWORD` key in `--sql-password` named kubernetes secret and also in GSM with `--sql-password` name. You can view the password in google secret manager.", + "type": "object", + "examples": [ + { + "kubernetesSecretName": "db-pass-k8s-secret", + "kubernetesSecretKeyName": "db-pass", + "gcpSecretManagerSecretName": "db-pass", + "generateSecret": true + } + ], + "properties": { + "kubernetesSecretName": { + "title": "Kubernetes Secret Name", + "description": "Kubernetes Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", + "type": [ + "string", + "null" + ] + }, + "kubernetesSecretKeyName": { + "title": "Secret Key Name in kubernetesSecretName which stores the password. Defaults to `DATABASE_PASSWORD` if not provided.", + "type": [ + "string", + "null" + ] + }, + "gcpSecretManagerSecretName": { + "title": "Google Secret Manager Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", + "type": [ + "string", + "null" + ] + }, + "generateSecret": { + "title": "Automatically generate a random secret and saves it in GSM", + "type": "boolean", + "default": true + } + } + }, + "configMap": { + "title": "Database Details ConfigMap", + "description": "This section creates a Kubernetes ConfigMap that stores essential connection details for the Cloud SQL database like DATABASE_INSTANCE_NAME, REGION, DATABASE_NAME, GCP_PROJECT_ID, and DATABASE_URL.", + "type": "object", + "properties": { + "databaseUrlKey": { + "title": "Database URL ConfigMap Key", + "description": "Key name to store the Database URL in the ConfigMap. Defaults to `DATABASE_URL` if not provided.", + "type": "string", + "default": "DATABASE_URL" + }, + "databaseUserKey": { + "title": "Database User Name ConfigMap Key", + "description": "Key name to store the Database User in the ConfigMap. Defaults to `DATABASE_USERNAME` if not provided.", + "type": "string", + "default": "DATABASE_USERNAME" + }, + "databaseUrlValue": { + "title": "Database URL Value", + "description": "Database URL value in ConfigMap. Leave it emtpy unless some especial setting is required. Defaults to `jdbc:mysql:///?cloudSqlInstance=::&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET`", + "type": [ + "string", + "null" + ], + "examples": [ + "jdbc:mysql:///service1?cloudSqlInstance=king-project-prod:eu-west1:service1-env1&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET", + "jdbc:postgresql:///flow?cloudSqlInstance=king-cdro-prod:europe-west1:flow&socketFactory=com.google.cloud.sql.postgres.SocketFactory&ipTypes=PRIVATE" + ] + }, + "additionalDBUrlParameters": { + "title": "Additional Database URL Parameters", + "type": "object", + "examples": [ + { + "useServerPrepStmts": "false", + "serverTimezone": "CET" + } + ], + "additionalProperties": { + "type": "string" + }, + "description": "Additional parameters to be appended to the DB URL. Provide parameters as key-value pairs, which will be appended to the database URL in the format `&key=value`." + } + } + }, + "enablePrivatePathForGoogleCloudServices": { + "title": "Enable Private Path for Google Cloud Services", + "description": "Controls connectivity to private IP instances from Google services, such as BigQuery. If you want to allow other Google Cloud services, such as BigQuery, to access data in Cloud SQL and make queries against this data over a private IP connection, then enable this option", + "type": "boolean", + "default": false, + "ui:widget": "hidden" + }, + "bigQueryConnection": { + "title": "BigQuery Connection", + "description": "Create a BigQuery connection to the Cloud SQL instance. This will allow you to query the Cloud SQL instance from BigQuery. \n We create a new Database User and Password for the BigQuery Connection. We provide BQ Service Agent with `roles/cloudsql.client` role. We also enable `PrivatePathForGoogleCloudServices` in the CloudSQL Instance. [See this for more details](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries)", + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": false + }, + "location": { + "title": "Location", + "description": "The geographic location where the connection should reside. Cloud SQL instance must be in the same location. We set it to the region(like europe-west1) for `ZONAL` SQL Instance and region to `EU` for `REGIONAL` SQL Instance.", + "x-immutable": true, + "examples": [ + "europe-west1", + "EU", + "europe-west4" + ] + }, + "friendlyName": { + "title": "Friendly Name", + "description": "If not provided, defaults to $instanceName-$databaseName " + }, + "connectionId": { + "title": "Connection ID", + "description": "If not provided, defaults to $instanceName-$databaseName ", + "x-immutable": true + }, + "description": { + "description": "A descriptive description for the connection" + }, + "overrideGcpProject": { + "title": "Override GCP Project for the Connection", + "description": "If not provided, it will use the `gcpProjectID` defined above.", + "x-immutable": true + } + } + } + }, + "if": { + "properties": { + "diskAutoresize": { + "const": true + } + } + }, + "then": { + "properties": { + "diskSize": { + "type": "null" + } + } + } + } + } +} \ No newline at end of file diff --git a/testdata/correct.schema.json b/testdata/correct.schema.json index 778175b..0e9de7a 100644 --- a/testdata/correct.schema.json +++ b/testdata/correct.schema.json @@ -1,9 +1,12 @@ { - "$schema": "https://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "test": { "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "cloudSQL": { + "$ref": "cloudSQL.schema.json#/$defs/cloudSQL" } } } \ No newline at end of file diff --git a/testdata/test.json b/testdata/test.json new file mode 100644 index 0000000..a2f5db9 --- /dev/null +++ b/testdata/test.json @@ -0,0 +1,514 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "test": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "cloudSQL": { + "if": { + "properties": { + "enabled": { + "const": true + } + } + }, + "then": { + "required": [ + "tier" + ], + "properties": { + "databaseVersion": { + "title": "Database Version", + "description": "Choose the database version of the Cloud SQL/Database Instance. [See this all available versions](https://cloud.google.com/sql/docs/db-versions). Please change collation/charsets should be changed accordingly, if this is changed to non-mysql version.", + "type": "string", + "default": "MYSQL_8_0", + "examples": [ + "MYSQL_8_0", + "MYSQL_8_0_31", + "POSTGRES_15" + ] + }, + "tier": { + "title": "Machine Type/Tier", + "description": "Choose the machine type of the Cloud SQL instance. db-g1- types are cheapest but not covered by CloudSQL SLA. [See this for all available machine types](https://cloud.google.com/sql/docs/mysql/instance-settings#machine-type-2ndgen). Note different `editions` have different machine types availability", + "type": "string", + "examples": [ + "db-g1-tiny", + "db-g1-small", + "db-n1-standard-1", + "db-n1-standard-2", + "db-n1-standard-4", + "db-n1-standard-8", + "db-n1-standard-16", + "db-n1-standard-32", + "db-n1-highmem-2", + "db-n1-highmem-8", + "db-perf-optimized-N-4", + "db-perf-optimized-N-8" + ] + }, + "availabilityType": { + "title": "Availability Type", + "type": [ + "string", + "null" + ], + "description": "Choose the availability type of the Cloud SQL instance: High Availability (REGIONAL) or Single Zone (ZONAL). **Note: defaults to REGIONAL in live and ZONAL in non-live.**", + "enum": [ + "ZONAL", + "REGIONAL" + ] + }, + "enableIAMAuth": { + "title": "Enable IAM Authentication", + "description": "Enable IAM authentication for the Cloud SQL instance, your application can use Google Servie Account(via workload identity) to connect to the Database. [GCP CloudSQL IAM authentication](https://cloud.google.com/sql/docs/mysql/iam-authentication)", + "type": "boolean", + "x-immutable": true, + "default": false + }, + "databaseInstanceName": { + "title": "CloudSQL Instance Name", + "description": "Name of the Cloud SQL instance. Defaults to `-` if not provided.", + "type": [ + "string", + "null" + ] + }, + "databaseName": { + "title": "MySQL Database Name", + "description": "Name of the MySQL database. Defaults to `` if not provided.", + "type": [ + "string", + "null" + ] + }, + "databaseUser": { + "title": "MySQL Database User Name", + "description": "Name of the MySQL database user. Defaults to `` if not provided.", + "type": [ + "string", + "null" + ] + }, + "diskAutoresize": { + "title": "Disk Autoresize", + "description": "Enable/disable automatic disk resizing. When set to true, the diskSize variable will be ignored.", + "type": "boolean", + "default": true + }, + "diskSize": { + "title": "Disk Size", + "description": "The size of data disk, in GB. Size of a running instance cannot be reduced but can be increased. The minimum value is 10GB. When diskAutoresize is set to true, the diskSize variable will be ignored.", + "type": "number", + "default": null + }, + "diskType": { + "title": "Disk Type", + "description": "Choose the disk type of the Cloud SQL instance. SSDs provide lower latency and higher data throughput. If you do not need high-performance access to your data, for example for long-term storage or rarely accessed data, you can reduce your costs by choosing HDD. HDD requires Cloud SQL Enterprise", + "type": "string", + "enum": [ + "PD_SSD", + "PD_HDD" + ], + "default": "PD_SSD" + }, + "edition": { + "title": "Edition", + "description": "The edition of the instance, can be ENTERPRISE or ENTERPRISE_PLUS. Use ENTERPRISE_PLUS only for production and high performance databases. [See this for comparison between different editions](https://cloud.google.com/sql/docs/mysql/editions-intro)", + "type": "string", + "enum": [ + "ENTERPRISE", + "ENTERPRISE_PLUS" + ], + "default": "ENTERPRISE" + }, + "charset": { + "title": "MySQL Charset", + "description": "Choose the charset of the Cloud SQL instance. [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", + "type": "string", + "examples": [ + "utf8mb4", + "utf16" + ] + }, + "collation": { + "title": "MySQL Collation", + "description": "Choose the collation of the Cloud SQL instance. Usually not required. See for [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", + "type": "string", + "examples": [ + "utf8mb4_0900_ai_ci", + "utf16_general_ci" + ] + }, + "databaseFlags": { + "title": "Database Flags", + "description": "Database flags to be set on the Cloud SQL instance. For more information, see [list of MySQL Flags](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql) and [list of PSQL flags](https://cloud.google.com/sql/docs/postgres/flags#list-flags-postgres)", + "type": "array", + "examples": [ + [ + { + "name": "max_connections", + "value": "10000" + }, + { + "name": "log_min_duration_statement", + "value": "1000" + } + ] + ], + "items": { + "type": "object", + "properties": { + "name": { + "title": "Flag Name", + "type": "string", + "minLength": 1 + }, + "value": { + "title": "Flag Value", + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name", + "value" + ] + } + }, + "backupConfiguration": { + "title": "Backup Configuration", + "type": "object", + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": true + }, + "startTime": { + "title": "Backup Start Time", + "description": "HH:MM format time indicating when backup configuration starts.", + "type": "string", + "default": "04:00" + }, + "binaryLogEnabled": { + "title": "Enable Binary Logging", + "description": "For Cloud SQL for MySQL, point-in-time recovery requires that you enable binary logging. This means that every update to your database is written to an independent log, which involves a small reduction in write performance. Performance of read operations are unaffected by binary logging, regardless of the size of the binary log files.", + "type": "boolean", + "default": false + } + } + }, + "maintenanceWindow": { + "title": "Maintenance Window", + "description": "The day of the week and the hour in which Cloud SQL schedules maintenance. Maintenance windows last for one hour. [More info](https://cloud.google.com/sql/docs/mysql/maintenance)", + "type": "object", + "properties": { + "day": { + "title": "Day", + "description": "Day of week (1-7), starting on Monday.", + "type": "number", + "default": 7 + }, + "hour": { + "title": "Hour", + "description": "Hour of day (0-23), ignored if day not set.", + "type": "number", + "default": 0 + } + } + }, + "insightsConfig": { + "title": "Insights Configuration", + "description": "See [Using Query Insights](https://cloud.google.com/sql/docs/postgres/using-query-insights) and [Use System Insights](https://cloud.google.com/sql/docs/postgres/use-system-insights) for more details", + "type": "object", + "properties": { + "queryInsightsEnabled": { + "title": "Enable Query Insights", + "type": "boolean", + "default": false + }, + "queryPlansPerMinute": { + "title": "Query Plans Per Minute", + "description": "Number of query execution plans captured by Insights per minute for all queries combined. Between 0 and 20. Default to 5.", + "type": "number", + "default": 5 + }, + "queryStringLength": { + "title": "Query String Length", + "description": "Maximum query length stored in bytes. Between 256 and 4500. Default to 1024. Higher query lengths are more useful for analytical queries, but they also require more memory. Changing the query length requires you to restart the instance. You can still add tags to queries that exceed the length limit.", + "type": "number", + "default": 1024 + }, + "recordApplicationTags": { + "title": "Record Application Tags", + "description": "True if Query Insights will record application tags from query when enabled", + "type": "boolean", + "default": false + }, + "recordClientAddress": { + "title": "Record Client Address", + "description": "True if Query Insights will record client address when enabled", + "type": "boolean", + "default": false + } + } + }, + "secret": { + "title": "Database Password Secret", + "description": "Manage the database credentials in Secret Manager and Kubernetes Secret/ConfigMap. Leave it empty to auto-generate password and store it in `DATABASE_PASSWORD` key in `--sql-password` named kubernetes secret and also in GSM with `--sql-password` name. You can view the password in google secret manager.", + "type": "object", + "examples": [ + { + "kubernetesSecretName": "db-pass-k8s-secret", + "kubernetesSecretKeyName": "db-pass", + "gcpSecretManagerSecretName": "db-pass", + "generateSecret": true + } + ], + "properties": { + "kubernetesSecretName": { + "title": "Kubernetes Secret Name", + "description": "Kubernetes Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", + "type": [ + "string", + "null" + ] + }, + "kubernetesSecretKeyName": { + "title": "Secret Key Name in kubernetesSecretName which stores the password. Defaults to `DATABASE_PASSWORD` if not provided.", + "type": [ + "string", + "null" + ] + }, + "gcpSecretManagerSecretName": { + "title": "Google Secret Manager Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", + "type": [ + "string", + "null" + ] + }, + "generateSecret": { + "title": "Automatically generate a random secret and saves it in GSM", + "type": "boolean", + "default": true + } + } + }, + "configMap": { + "title": "Database Details ConfigMap", + "description": "This section creates a Kubernetes ConfigMap that stores essential connection details for the Cloud SQL database like DATABASE_INSTANCE_NAME, REGION, DATABASE_NAME, GCP_PROJECT_ID, and DATABASE_URL.", + "type": "object", + "properties": { + "databaseUrlKey": { + "title": "Database URL ConfigMap Key", + "description": "Key name to store the Database URL in the ConfigMap. Defaults to `DATABASE_URL` if not provided.", + "type": "string", + "default": "DATABASE_URL" + }, + "databaseUserKey": { + "title": "Database User Name ConfigMap Key", + "description": "Key name to store the Database User in the ConfigMap. Defaults to `DATABASE_USERNAME` if not provided.", + "type": "string", + "default": "DATABASE_USERNAME" + }, + "databaseUrlValue": { + "title": "Database URL Value", + "description": "Database URL value in ConfigMap. Leave it emtpy unless some especial setting is required. Defaults to `jdbc:mysql:///?cloudSqlInstance=::&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET`", + "type": [ + "string", + "null" + ], + "examples": [ + "jdbc:mysql:///service1?cloudSqlInstance=king-project-prod:eu-west1:service1-env1&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET", + "jdbc:postgresql:///flow?cloudSqlInstance=king-cdro-prod:europe-west1:flow&socketFactory=com.google.cloud.sql.postgres.SocketFactory&ipTypes=PRIVATE" + ] + }, + "additionalDBUrlParameters": { + "title": "Additional Database URL Parameters", + "type": "object", + "examples": [ + { + "useServerPrepStmts": "false", + "serverTimezone": "CET" + } + ], + "additionalProperties": { + "type": "string" + }, + "description": "Additional parameters to be appended to the DB URL. Provide parameters as key-value pairs, which will be appended to the database URL in the format `&key=value`." + } + } + }, + "enablePrivatePathForGoogleCloudServices": { + "title": "Enable Private Path for Google Cloud Services", + "description": "Controls connectivity to private IP instances from Google services, such as BigQuery. If you want to allow other Google Cloud services, such as BigQuery, to access data in Cloud SQL and make queries against this data over a private IP connection, then enable this option", + "type": "boolean", + "default": false, + "ui:widget": "hidden" + }, + "bigQueryConnection": { + "title": "BigQuery Connection", + "description": "Create a BigQuery connection to the Cloud SQL instance. This will allow you to query the Cloud SQL instance from BigQuery. \n We create a new Database User and Password for the BigQuery Connection. We provide BQ Service Agent with `roles/cloudsql.client` role. We also enable `PrivatePathForGoogleCloudServices` in the CloudSQL Instance. [See this for more details](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries)", + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": false + }, + "location": { + "title": "Location", + "description": "The geographic location where the connection should reside. Cloud SQL instance must be in the same location. We set it to the region(like europe-west1) for `ZONAL` SQL Instance and region to `EU` for `REGIONAL` SQL Instance.", + "x-immutable": true, + "examples": [ + "europe-west1", + "EU", + "europe-west4" + ] + }, + "friendlyName": { + "title": "Friendly Name", + "description": "If not provided, defaults to $instanceName-$databaseName " + }, + "connectionId": { + "title": "Connection ID", + "description": "If not provided, defaults to $instanceName-$databaseName ", + "x-immutable": true + }, + "description": { + "description": "A descriptive description for the connection" + }, + "overrideGcpProject": { + "title": "Override GCP Project for the Connection", + "description": "If not provided, it will use the `gcpProjectID` defined above.", + "x-immutable": true + } + } + } + }, + "if": { + "properties": { + "diskAutoresize": { + "const": true + } + } + }, + "then": { + "properties": { + "diskSize": { + "type": "null" + } + } + } + }, + "title": "Cloud SQL", + "description": "Create a Cloud SQL instance, with password, user, database; while storing the created credentials in Secret Manager as well as Kubernetes Secrets/Configmap. \n\n Enabling CloudSQL will, in addition to creating the CloudSQL instance and database, automatically generate CloudSQL user password, put it in GCP Secret Manager in your GCP Project and sync it into a Kubernetes Secret (`$service-$environment-sql-password`) with key name `DATABASE_PASSWORD` inside your namespace. It will also create a ConfigMap (`$service-$environment-sql-config`) containing the database connection details in keys `DATABASE_URL`, `DATABASE_USERNAME`, `DATABASE_INSTANCE_NAME`, `DATABASE_NAME`, `GCP_PROJECT_ID`, and `REGION`. \n\n **[See Feature Docs](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql)** \n\n **[See How to Connect](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql#how-to-connect-to-cloudsql-instance)**", + "type": "object", + "examples": [ + { + "enabled": true, + "tier": "db-g1-small" + }, + { + "enabled": true, + "tier": "db-g1-small", + "databaseUser": "event-definer", + "databaseName": "event-definer", + "configMap": { + "databaseUserKey": "DATABASE_USERNAME" + } + }, + { + "enabled": true, + "availabilityType": "REGIONAL", + "charset": "UTF8", + "databaseInstanceName": "flow", + "databaseName": "flow", + "databaseUser": "flow", + "diskAutoresize": true, + "databaseVersion": "POSTGRES_15", + "diskSize": 250, + "edition": "ENTERPRISE_PLUS", + "tier": "db-perf-optimized-N-8", + "secret": { + "kubernetesSecretKeyName": "DB_PASSWORD", + "generateSecret": true + }, + "configMap": { + "databaseUrlKey": "DB_URL", + "databaseUserKey": "DB_USER" + } + }, + { + "enabled": true, + "databaseInstanceName": "$service-$env", + "databaseName": "$service", + "databaseUser": "$service", + "availabilityType": "REGIONAL", + "databaseVersion": "MYSQL_8_0", + "edition": "ENTERPRISE", + "tier": "db-g1-small", + "charset": "utf8mb4", + "collation": "utf8mb4_0900_ai_ci", + "diskAutoresize": true, + "diskSize": 10, + "diskType": "PD_SSD", + "databaseFlags": [ + { + "name": "max_connections", + "value": "10000" + }, + { + "name": "log_min_duration_statement", + "value": "1000" + } + ], + "backupConfiguration": { + "enabled": true, + "startTime": "04:00", + "binaryLogEnabled": true + }, + "maintenanceWindow": { + "day": 7, + "hour": 0 + }, + "insightsConfig": { + "queryInsightsEnabled": false, + "queryPlansPerMinute": 5, + "queryStringLength": 1024, + "recordApplicationTags": false, + "recordClientAddress": false + }, + "secret": { + "kubernetesSecretName": "$service-$env-sql-password", + "kubernetesSecretKeyName": "DATABASE_PASSWORD", + "gcpSecretManagerSecretName": "$service-$env-sql-password", + "generateSecret": true + }, + "configMap": { + "databaseUrlKey": "DATABASE_URL", + "databaseUrlValue": "null", + "databaseUserKey": "DATABASE_USER" + } + } + ], + "additionalProperties": true, + "properties": { + "enabled": { + "title": "Enable", + "type": "boolean", + "default": false + } + } + } + } +} \ No newline at end of file From c8f79e83a15fc286c63b74ec4aa8f50eb6b74382 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 14:51:46 +0200 Subject: [PATCH 17/30] fix: update schema references for correctness --- testdata/cloudSQL.schema.json | 540 ---------------------------------- testdata/correct.schema.json | 3 - testdata/error.schema.json | 2 +- testdata/test.json | 514 -------------------------------- testdata/test.sh | 0 5 files changed, 1 insertion(+), 1058 deletions(-) delete mode 100644 testdata/cloudSQL.schema.json delete mode 100644 testdata/test.json delete mode 100644 testdata/test.sh diff --git a/testdata/cloudSQL.schema.json b/testdata/cloudSQL.schema.json deleted file mode 100644 index 2b9e006..0000000 --- a/testdata/cloudSQL.schema.json +++ /dev/null @@ -1,540 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "$ref": "#/$defs/cloudSQLDescription", - "if": { - "properties": { - "enabled": { - "const": true - } - } - }, - "then": { - "$ref": "#/$defs/cloudSQLProperties" - }, - "$defs": { - "cloudSQLDescription": { - "title": "Cloud SQL", - "description": "Create a Cloud SQL instance, with password, user, database; while storing the created credentials in Secret Manager as well as Kubernetes Secrets/Configmap. \n\n Enabling CloudSQL will, in addition to creating the CloudSQL instance and database, automatically generate CloudSQL user password, put it in GCP Secret Manager in your GCP Project and sync it into a Kubernetes Secret (`$service-$environment-sql-password`) with key name `DATABASE_PASSWORD` inside your namespace. It will also create a ConfigMap (`$service-$environment-sql-config`) containing the database connection details in keys `DATABASE_URL`, `DATABASE_USERNAME`, `DATABASE_INSTANCE_NAME`, `DATABASE_NAME`, `GCP_PROJECT_ID`, and `REGION`. \n\n **[See Feature Docs](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql)** \n\n **[See How to Connect](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql#how-to-connect-to-cloudsql-instance)**", - "type": "object", - "examples": [ - { - "enabled": true, - "tier": "db-g1-small" - }, - { - "enabled": true, - "tier": "db-g1-small", - "databaseUser": "event-definer", - "databaseName": "event-definer", - "configMap": { - "databaseUserKey": "DATABASE_USERNAME" - } - }, - { - "enabled": true, - "availabilityType": "REGIONAL", - "charset": "UTF8", - "databaseInstanceName": "flow", - "databaseName": "flow", - "databaseUser": "flow", - "diskAutoresize": true, - "databaseVersion": "POSTGRES_15", - "diskSize": 250, - "edition": "ENTERPRISE_PLUS", - "tier": "db-perf-optimized-N-8", - "secret": { - "kubernetesSecretKeyName": "DB_PASSWORD", - "generateSecret": true - }, - "configMap": { - "databaseUrlKey": "DB_URL", - "databaseUserKey": "DB_USER" - } - }, - { - "enabled": true, - "databaseInstanceName": "$service-$env", - "databaseName": "$service", - "databaseUser": "$service", - "availabilityType": "REGIONAL", - "databaseVersion": "MYSQL_8_0", - "edition": "ENTERPRISE", - "tier": "db-g1-small", - "charset": "utf8mb4", - "collation": "utf8mb4_0900_ai_ci", - "diskAutoresize": true, - "diskSize": 10, - "diskType": "PD_SSD", - "databaseFlags": [ - { - "name": "max_connections", - "value": "10000" - }, - { - "name": "log_min_duration_statement", - "value": "1000" - } - ], - "backupConfiguration": { - "enabled": true, - "startTime": "04:00", - "binaryLogEnabled": true - }, - "maintenanceWindow": { - "day": 7, - "hour": 0 - }, - "insightsConfig": { - "queryInsightsEnabled": false, - "queryPlansPerMinute": 5, - "queryStringLength": 1024, - "recordApplicationTags": false, - "recordClientAddress": false - }, - "secret": { - "kubernetesSecretName": "$service-$env-sql-password", - "kubernetesSecretKeyName": "DATABASE_PASSWORD", - "gcpSecretManagerSecretName": "$service-$env-sql-password", - "generateSecret": true - }, - "configMap": { - "databaseUrlKey": "DATABASE_URL", - "databaseUrlValue": "null", - "databaseUserKey": "DATABASE_USER" - } - } - ], - "additionalProperties": true, - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": false - } - } - }, - "cloudSQLSimple": { - "$ref": "#/$defs/cloudSQLDescription", - "if": { - "properties": { - "enabled": { - "const": true - } - } - }, - "then": { - "required": [ - "tier" - ], - "properties": { - "tier": { - "$ref": "#/$defs/cloudSQLProperties/properties/tier" - } - } - } - }, - "cloudSQL": { - "$ref": "#/$defs/cloudSQLDescription", - "if": { - "properties": { - "enabled": { - "const": true - } - } - }, - "then": { - "$ref": "#/$defs/cloudSQLProperties" - } - }, - "cloudSQLProperties": { - "required": [ - "tier" - ], - "properties": { - "databaseVersion": { - "title": "Database Version", - "description": "Choose the database version of the Cloud SQL/Database Instance. [See this all available versions](https://cloud.google.com/sql/docs/db-versions). Please change collation/charsets should be changed accordingly, if this is changed to non-mysql version.", - "type": "string", - "default": "MYSQL_8_0", - "examples": [ - "MYSQL_8_0", - "MYSQL_8_0_31", - "POSTGRES_15" - ] - }, - "tier": { - "title": "Machine Type/Tier", - "description": "Choose the machine type of the Cloud SQL instance. db-g1- types are cheapest but not covered by CloudSQL SLA. [See this for all available machine types](https://cloud.google.com/sql/docs/mysql/instance-settings#machine-type-2ndgen). Note different `editions` have different machine types availability", - "type": "string", - "examples": [ - "db-g1-tiny", - "db-g1-small", - "db-n1-standard-1", - "db-n1-standard-2", - "db-n1-standard-4", - "db-n1-standard-8", - "db-n1-standard-16", - "db-n1-standard-32", - "db-n1-highmem-2", - "db-n1-highmem-8", - "db-perf-optimized-N-4", - "db-perf-optimized-N-8" - ] - }, - "availabilityType": { - "title": "Availability Type", - "type": [ - "string", - "null" - ], - "description": "Choose the availability type of the Cloud SQL instance: High Availability (REGIONAL) or Single Zone (ZONAL). **Note: defaults to REGIONAL in live and ZONAL in non-live.**", - "enum": [ - "ZONAL", - "REGIONAL" - ] - }, - "enableIAMAuth": { - "title": "Enable IAM Authentication", - "description": "Enable IAM authentication for the Cloud SQL instance, your application can use Google Servie Account(via workload identity) to connect to the Database. [GCP CloudSQL IAM authentication](https://cloud.google.com/sql/docs/mysql/iam-authentication)", - "type": "boolean", - "x-immutable": true, - "default": false - }, - "databaseInstanceName": { - "title": "CloudSQL Instance Name", - "description": "Name of the Cloud SQL instance. Defaults to `-` if not provided.", - "type": [ - "string", - "null" - ] - }, - "databaseName": { - "title": "MySQL Database Name", - "description": "Name of the MySQL database. Defaults to `` if not provided.", - "type": [ - "string", - "null" - ] - }, - "databaseUser": { - "title": "MySQL Database User Name", - "description": "Name of the MySQL database user. Defaults to `` if not provided.", - "type": [ - "string", - "null" - ] - }, - "diskAutoresize": { - "title": "Disk Autoresize", - "description": "Enable/disable automatic disk resizing. When set to true, the diskSize variable will be ignored.", - "type": "boolean", - "default": true - }, - "diskSize": { - "title": "Disk Size", - "description": "The size of data disk, in GB. Size of a running instance cannot be reduced but can be increased. The minimum value is 10GB. When diskAutoresize is set to true, the diskSize variable will be ignored.", - "type": "number", - "default": null - }, - "diskType": { - "title": "Disk Type", - "description": "Choose the disk type of the Cloud SQL instance. SSDs provide lower latency and higher data throughput. If you do not need high-performance access to your data, for example for long-term storage or rarely accessed data, you can reduce your costs by choosing HDD. HDD requires Cloud SQL Enterprise", - "type": "string", - "enum": [ - "PD_SSD", - "PD_HDD" - ], - "default": "PD_SSD" - }, - "edition": { - "title": "Edition", - "description": "The edition of the instance, can be ENTERPRISE or ENTERPRISE_PLUS. Use ENTERPRISE_PLUS only for production and high performance databases. [See this for comparison between different editions](https://cloud.google.com/sql/docs/mysql/editions-intro)", - "type": "string", - "enum": [ - "ENTERPRISE", - "ENTERPRISE_PLUS" - ], - "default": "ENTERPRISE" - }, - "charset": { - "title": "MySQL Charset", - "description": "Choose the charset of the Cloud SQL instance. [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", - "type": "string", - "examples": [ - "utf8mb4", - "utf16" - ] - }, - "collation": { - "title": "MySQL Collation", - "description": "Choose the collation of the Cloud SQL instance. Usually not required. See for [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", - "type": "string", - "examples": [ - "utf8mb4_0900_ai_ci", - "utf16_general_ci" - ] - }, - "databaseFlags": { - "title": "Database Flags", - "description": "Database flags to be set on the Cloud SQL instance. For more information, see [list of MySQL Flags](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql) and [list of PSQL flags](https://cloud.google.com/sql/docs/postgres/flags#list-flags-postgres)", - "type": "array", - "examples": [ - [ - { - "name": "max_connections", - "value": "10000" - }, - { - "name": "log_min_duration_statement", - "value": "1000" - } - ] - ], - "items": { - "type": "object", - "properties": { - "name": { - "title": "Flag Name", - "type": "string", - "minLength": 1 - }, - "value": { - "title": "Flag Value", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "name", - "value" - ] - } - }, - "backupConfiguration": { - "title": "Backup Configuration", - "type": "object", - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": true - }, - "startTime": { - "title": "Backup Start Time", - "description": "HH:MM format time indicating when backup configuration starts.", - "type": "string", - "default": "04:00" - }, - "binaryLogEnabled": { - "title": "Enable Binary Logging", - "description": "For Cloud SQL for MySQL, point-in-time recovery requires that you enable binary logging. This means that every update to your database is written to an independent log, which involves a small reduction in write performance. Performance of read operations are unaffected by binary logging, regardless of the size of the binary log files.", - "type": "boolean", - "default": false - } - } - }, - "maintenanceWindow": { - "title": "Maintenance Window", - "description": "The day of the week and the hour in which Cloud SQL schedules maintenance. Maintenance windows last for one hour. [More info](https://cloud.google.com/sql/docs/mysql/maintenance)", - "type": "object", - "properties": { - "day": { - "title": "Day", - "description": "Day of week (1-7), starting on Monday.", - "type": "number", - "default": 7 - }, - "hour": { - "title": "Hour", - "description": "Hour of day (0-23), ignored if day not set.", - "type": "number", - "default": 0 - } - } - }, - "insightsConfig": { - "title": "Insights Configuration", - "description": "See [Using Query Insights](https://cloud.google.com/sql/docs/postgres/using-query-insights) and [Use System Insights](https://cloud.google.com/sql/docs/postgres/use-system-insights) for more details", - "type": "object", - "properties": { - "queryInsightsEnabled": { - "title": "Enable Query Insights", - "type": "boolean", - "default": false - }, - "queryPlansPerMinute": { - "title": "Query Plans Per Minute", - "description": "Number of query execution plans captured by Insights per minute for all queries combined. Between 0 and 20. Default to 5.", - "type": "number", - "default": 5 - }, - "queryStringLength": { - "title": "Query String Length", - "description": "Maximum query length stored in bytes. Between 256 and 4500. Default to 1024. Higher query lengths are more useful for analytical queries, but they also require more memory. Changing the query length requires you to restart the instance. You can still add tags to queries that exceed the length limit.", - "type": "number", - "default": 1024 - }, - "recordApplicationTags": { - "title": "Record Application Tags", - "description": "True if Query Insights will record application tags from query when enabled", - "type": "boolean", - "default": false - }, - "recordClientAddress": { - "title": "Record Client Address", - "description": "True if Query Insights will record client address when enabled", - "type": "boolean", - "default": false - } - } - }, - "secret": { - "title": "Database Password Secret", - "description": "Manage the database credentials in Secret Manager and Kubernetes Secret/ConfigMap. Leave it empty to auto-generate password and store it in `DATABASE_PASSWORD` key in `--sql-password` named kubernetes secret and also in GSM with `--sql-password` name. You can view the password in google secret manager.", - "type": "object", - "examples": [ - { - "kubernetesSecretName": "db-pass-k8s-secret", - "kubernetesSecretKeyName": "db-pass", - "gcpSecretManagerSecretName": "db-pass", - "generateSecret": true - } - ], - "properties": { - "kubernetesSecretName": { - "title": "Kubernetes Secret Name", - "description": "Kubernetes Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", - "type": [ - "string", - "null" - ] - }, - "kubernetesSecretKeyName": { - "title": "Secret Key Name in kubernetesSecretName which stores the password. Defaults to `DATABASE_PASSWORD` if not provided.", - "type": [ - "string", - "null" - ] - }, - "gcpSecretManagerSecretName": { - "title": "Google Secret Manager Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", - "type": [ - "string", - "null" - ] - }, - "generateSecret": { - "title": "Automatically generate a random secret and saves it in GSM", - "type": "boolean", - "default": true - } - } - }, - "configMap": { - "title": "Database Details ConfigMap", - "description": "This section creates a Kubernetes ConfigMap that stores essential connection details for the Cloud SQL database like DATABASE_INSTANCE_NAME, REGION, DATABASE_NAME, GCP_PROJECT_ID, and DATABASE_URL.", - "type": "object", - "properties": { - "databaseUrlKey": { - "title": "Database URL ConfigMap Key", - "description": "Key name to store the Database URL in the ConfigMap. Defaults to `DATABASE_URL` if not provided.", - "type": "string", - "default": "DATABASE_URL" - }, - "databaseUserKey": { - "title": "Database User Name ConfigMap Key", - "description": "Key name to store the Database User in the ConfigMap. Defaults to `DATABASE_USERNAME` if not provided.", - "type": "string", - "default": "DATABASE_USERNAME" - }, - "databaseUrlValue": { - "title": "Database URL Value", - "description": "Database URL value in ConfigMap. Leave it emtpy unless some especial setting is required. Defaults to `jdbc:mysql:///?cloudSqlInstance=::&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET`", - "type": [ - "string", - "null" - ], - "examples": [ - "jdbc:mysql:///service1?cloudSqlInstance=king-project-prod:eu-west1:service1-env1&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET", - "jdbc:postgresql:///flow?cloudSqlInstance=king-cdro-prod:europe-west1:flow&socketFactory=com.google.cloud.sql.postgres.SocketFactory&ipTypes=PRIVATE" - ] - }, - "additionalDBUrlParameters": { - "title": "Additional Database URL Parameters", - "type": "object", - "examples": [ - { - "useServerPrepStmts": "false", - "serverTimezone": "CET" - } - ], - "additionalProperties": { - "type": "string" - }, - "description": "Additional parameters to be appended to the DB URL. Provide parameters as key-value pairs, which will be appended to the database URL in the format `&key=value`." - } - } - }, - "enablePrivatePathForGoogleCloudServices": { - "title": "Enable Private Path for Google Cloud Services", - "description": "Controls connectivity to private IP instances from Google services, such as BigQuery. If you want to allow other Google Cloud services, such as BigQuery, to access data in Cloud SQL and make queries against this data over a private IP connection, then enable this option", - "type": "boolean", - "default": false, - "ui:widget": "hidden" - }, - "bigQueryConnection": { - "title": "BigQuery Connection", - "description": "Create a BigQuery connection to the Cloud SQL instance. This will allow you to query the Cloud SQL instance from BigQuery. \n We create a new Database User and Password for the BigQuery Connection. We provide BQ Service Agent with `roles/cloudsql.client` role. We also enable `PrivatePathForGoogleCloudServices` in the CloudSQL Instance. [See this for more details](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries)", - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": false - }, - "location": { - "title": "Location", - "description": "The geographic location where the connection should reside. Cloud SQL instance must be in the same location. We set it to the region(like europe-west1) for `ZONAL` SQL Instance and region to `EU` for `REGIONAL` SQL Instance.", - "x-immutable": true, - "examples": [ - "europe-west1", - "EU", - "europe-west4" - ] - }, - "friendlyName": { - "title": "Friendly Name", - "description": "If not provided, defaults to $instanceName-$databaseName " - }, - "connectionId": { - "title": "Connection ID", - "description": "If not provided, defaults to $instanceName-$databaseName ", - "x-immutable": true - }, - "description": { - "description": "A descriptive description for the connection" - }, - "overrideGcpProject": { - "title": "Override GCP Project for the Connection", - "description": "If not provided, it will use the `gcpProjectID` defined above.", - "x-immutable": true - } - } - } - }, - "if": { - "properties": { - "diskAutoresize": { - "const": true - } - } - }, - "then": { - "properties": { - "diskSize": { - "type": "null" - } - } - } - } - } -} \ No newline at end of file diff --git a/testdata/correct.schema.json b/testdata/correct.schema.json index 0e9de7a..9aa6840 100644 --- a/testdata/correct.schema.json +++ b/testdata/correct.schema.json @@ -4,9 +4,6 @@ "properties": { "test": { "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" - }, - "cloudSQL": { - "$ref": "cloudSQL.schema.json#/$defs/cloudSQL" } } } \ No newline at end of file diff --git a/testdata/error.schema.json b/testdata/error.schema.json index dd75bc0..a08d89f 100644 --- a/testdata/error.schema.json +++ b/testdata/error.schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "test": { - "$ref": "https://raw.githubusercontent.com/wrong/kubernetes-wrong-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + "$ref": "https://raw.githubusercontent.com/wrong/kubernetes-wrong-schema/master/master-standalone-strict/.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" } } } \ No newline at end of file diff --git a/testdata/test.json b/testdata/test.json deleted file mode 100644 index a2f5db9..0000000 --- a/testdata/test.json +++ /dev/null @@ -1,514 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "test": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] - }, - "cloudSQL": { - "if": { - "properties": { - "enabled": { - "const": true - } - } - }, - "then": { - "required": [ - "tier" - ], - "properties": { - "databaseVersion": { - "title": "Database Version", - "description": "Choose the database version of the Cloud SQL/Database Instance. [See this all available versions](https://cloud.google.com/sql/docs/db-versions). Please change collation/charsets should be changed accordingly, if this is changed to non-mysql version.", - "type": "string", - "default": "MYSQL_8_0", - "examples": [ - "MYSQL_8_0", - "MYSQL_8_0_31", - "POSTGRES_15" - ] - }, - "tier": { - "title": "Machine Type/Tier", - "description": "Choose the machine type of the Cloud SQL instance. db-g1- types are cheapest but not covered by CloudSQL SLA. [See this for all available machine types](https://cloud.google.com/sql/docs/mysql/instance-settings#machine-type-2ndgen). Note different `editions` have different machine types availability", - "type": "string", - "examples": [ - "db-g1-tiny", - "db-g1-small", - "db-n1-standard-1", - "db-n1-standard-2", - "db-n1-standard-4", - "db-n1-standard-8", - "db-n1-standard-16", - "db-n1-standard-32", - "db-n1-highmem-2", - "db-n1-highmem-8", - "db-perf-optimized-N-4", - "db-perf-optimized-N-8" - ] - }, - "availabilityType": { - "title": "Availability Type", - "type": [ - "string", - "null" - ], - "description": "Choose the availability type of the Cloud SQL instance: High Availability (REGIONAL) or Single Zone (ZONAL). **Note: defaults to REGIONAL in live and ZONAL in non-live.**", - "enum": [ - "ZONAL", - "REGIONAL" - ] - }, - "enableIAMAuth": { - "title": "Enable IAM Authentication", - "description": "Enable IAM authentication for the Cloud SQL instance, your application can use Google Servie Account(via workload identity) to connect to the Database. [GCP CloudSQL IAM authentication](https://cloud.google.com/sql/docs/mysql/iam-authentication)", - "type": "boolean", - "x-immutable": true, - "default": false - }, - "databaseInstanceName": { - "title": "CloudSQL Instance Name", - "description": "Name of the Cloud SQL instance. Defaults to `-` if not provided.", - "type": [ - "string", - "null" - ] - }, - "databaseName": { - "title": "MySQL Database Name", - "description": "Name of the MySQL database. Defaults to `` if not provided.", - "type": [ - "string", - "null" - ] - }, - "databaseUser": { - "title": "MySQL Database User Name", - "description": "Name of the MySQL database user. Defaults to `` if not provided.", - "type": [ - "string", - "null" - ] - }, - "diskAutoresize": { - "title": "Disk Autoresize", - "description": "Enable/disable automatic disk resizing. When set to true, the diskSize variable will be ignored.", - "type": "boolean", - "default": true - }, - "diskSize": { - "title": "Disk Size", - "description": "The size of data disk, in GB. Size of a running instance cannot be reduced but can be increased. The minimum value is 10GB. When diskAutoresize is set to true, the diskSize variable will be ignored.", - "type": "number", - "default": null - }, - "diskType": { - "title": "Disk Type", - "description": "Choose the disk type of the Cloud SQL instance. SSDs provide lower latency and higher data throughput. If you do not need high-performance access to your data, for example for long-term storage or rarely accessed data, you can reduce your costs by choosing HDD. HDD requires Cloud SQL Enterprise", - "type": "string", - "enum": [ - "PD_SSD", - "PD_HDD" - ], - "default": "PD_SSD" - }, - "edition": { - "title": "Edition", - "description": "The edition of the instance, can be ENTERPRISE or ENTERPRISE_PLUS. Use ENTERPRISE_PLUS only for production and high performance databases. [See this for comparison between different editions](https://cloud.google.com/sql/docs/mysql/editions-intro)", - "type": "string", - "enum": [ - "ENTERPRISE", - "ENTERPRISE_PLUS" - ], - "default": "ENTERPRISE" - }, - "charset": { - "title": "MySQL Charset", - "description": "Choose the charset of the Cloud SQL instance. [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", - "type": "string", - "examples": [ - "utf8mb4", - "utf16" - ] - }, - "collation": { - "title": "MySQL Collation", - "description": "Choose the collation of the Cloud SQL instance. Usually not required. See for [MySQL Supported Character Sets and Collations](https://dev.mysql.com/doc/refman/8.4/en/charset-charsets.html) more details.", - "type": "string", - "examples": [ - "utf8mb4_0900_ai_ci", - "utf16_general_ci" - ] - }, - "databaseFlags": { - "title": "Database Flags", - "description": "Database flags to be set on the Cloud SQL instance. For more information, see [list of MySQL Flags](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql) and [list of PSQL flags](https://cloud.google.com/sql/docs/postgres/flags#list-flags-postgres)", - "type": "array", - "examples": [ - [ - { - "name": "max_connections", - "value": "10000" - }, - { - "name": "log_min_duration_statement", - "value": "1000" - } - ] - ], - "items": { - "type": "object", - "properties": { - "name": { - "title": "Flag Name", - "type": "string", - "minLength": 1 - }, - "value": { - "title": "Flag Value", - "type": "string", - "minLength": 1 - } - }, - "required": [ - "name", - "value" - ] - } - }, - "backupConfiguration": { - "title": "Backup Configuration", - "type": "object", - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": true - }, - "startTime": { - "title": "Backup Start Time", - "description": "HH:MM format time indicating when backup configuration starts.", - "type": "string", - "default": "04:00" - }, - "binaryLogEnabled": { - "title": "Enable Binary Logging", - "description": "For Cloud SQL for MySQL, point-in-time recovery requires that you enable binary logging. This means that every update to your database is written to an independent log, which involves a small reduction in write performance. Performance of read operations are unaffected by binary logging, regardless of the size of the binary log files.", - "type": "boolean", - "default": false - } - } - }, - "maintenanceWindow": { - "title": "Maintenance Window", - "description": "The day of the week and the hour in which Cloud SQL schedules maintenance. Maintenance windows last for one hour. [More info](https://cloud.google.com/sql/docs/mysql/maintenance)", - "type": "object", - "properties": { - "day": { - "title": "Day", - "description": "Day of week (1-7), starting on Monday.", - "type": "number", - "default": 7 - }, - "hour": { - "title": "Hour", - "description": "Hour of day (0-23), ignored if day not set.", - "type": "number", - "default": 0 - } - } - }, - "insightsConfig": { - "title": "Insights Configuration", - "description": "See [Using Query Insights](https://cloud.google.com/sql/docs/postgres/using-query-insights) and [Use System Insights](https://cloud.google.com/sql/docs/postgres/use-system-insights) for more details", - "type": "object", - "properties": { - "queryInsightsEnabled": { - "title": "Enable Query Insights", - "type": "boolean", - "default": false - }, - "queryPlansPerMinute": { - "title": "Query Plans Per Minute", - "description": "Number of query execution plans captured by Insights per minute for all queries combined. Between 0 and 20. Default to 5.", - "type": "number", - "default": 5 - }, - "queryStringLength": { - "title": "Query String Length", - "description": "Maximum query length stored in bytes. Between 256 and 4500. Default to 1024. Higher query lengths are more useful for analytical queries, but they also require more memory. Changing the query length requires you to restart the instance. You can still add tags to queries that exceed the length limit.", - "type": "number", - "default": 1024 - }, - "recordApplicationTags": { - "title": "Record Application Tags", - "description": "True if Query Insights will record application tags from query when enabled", - "type": "boolean", - "default": false - }, - "recordClientAddress": { - "title": "Record Client Address", - "description": "True if Query Insights will record client address when enabled", - "type": "boolean", - "default": false - } - } - }, - "secret": { - "title": "Database Password Secret", - "description": "Manage the database credentials in Secret Manager and Kubernetes Secret/ConfigMap. Leave it empty to auto-generate password and store it in `DATABASE_PASSWORD` key in `--sql-password` named kubernetes secret and also in GSM with `--sql-password` name. You can view the password in google secret manager.", - "type": "object", - "examples": [ - { - "kubernetesSecretName": "db-pass-k8s-secret", - "kubernetesSecretKeyName": "db-pass", - "gcpSecretManagerSecretName": "db-pass", - "generateSecret": true - } - ], - "properties": { - "kubernetesSecretName": { - "title": "Kubernetes Secret Name", - "description": "Kubernetes Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", - "type": [ - "string", - "null" - ] - }, - "kubernetesSecretKeyName": { - "title": "Secret Key Name in kubernetesSecretName which stores the password. Defaults to `DATABASE_PASSWORD` if not provided.", - "type": [ - "string", - "null" - ] - }, - "gcpSecretManagerSecretName": { - "title": "Google Secret Manager Secret Name for storing DB Password. Defaults to `--sql-password` if not provided.", - "type": [ - "string", - "null" - ] - }, - "generateSecret": { - "title": "Automatically generate a random secret and saves it in GSM", - "type": "boolean", - "default": true - } - } - }, - "configMap": { - "title": "Database Details ConfigMap", - "description": "This section creates a Kubernetes ConfigMap that stores essential connection details for the Cloud SQL database like DATABASE_INSTANCE_NAME, REGION, DATABASE_NAME, GCP_PROJECT_ID, and DATABASE_URL.", - "type": "object", - "properties": { - "databaseUrlKey": { - "title": "Database URL ConfigMap Key", - "description": "Key name to store the Database URL in the ConfigMap. Defaults to `DATABASE_URL` if not provided.", - "type": "string", - "default": "DATABASE_URL" - }, - "databaseUserKey": { - "title": "Database User Name ConfigMap Key", - "description": "Key name to store the Database User in the ConfigMap. Defaults to `DATABASE_USERNAME` if not provided.", - "type": "string", - "default": "DATABASE_USERNAME" - }, - "databaseUrlValue": { - "title": "Database URL Value", - "description": "Database URL value in ConfigMap. Leave it emtpy unless some especial setting is required. Defaults to `jdbc:mysql:///?cloudSqlInstance=::&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET`", - "type": [ - "string", - "null" - ], - "examples": [ - "jdbc:mysql:///service1?cloudSqlInstance=king-project-prod:eu-west1:service1-env1&ipTypes=PRIVATE&socketFactory=com.google.cloud.sql.mysql.SocketFactory&useServerPrepStmts=false&serverTimezone=CET", - "jdbc:postgresql:///flow?cloudSqlInstance=king-cdro-prod:europe-west1:flow&socketFactory=com.google.cloud.sql.postgres.SocketFactory&ipTypes=PRIVATE" - ] - }, - "additionalDBUrlParameters": { - "title": "Additional Database URL Parameters", - "type": "object", - "examples": [ - { - "useServerPrepStmts": "false", - "serverTimezone": "CET" - } - ], - "additionalProperties": { - "type": "string" - }, - "description": "Additional parameters to be appended to the DB URL. Provide parameters as key-value pairs, which will be appended to the database URL in the format `&key=value`." - } - } - }, - "enablePrivatePathForGoogleCloudServices": { - "title": "Enable Private Path for Google Cloud Services", - "description": "Controls connectivity to private IP instances from Google services, such as BigQuery. If you want to allow other Google Cloud services, such as BigQuery, to access data in Cloud SQL and make queries against this data over a private IP connection, then enable this option", - "type": "boolean", - "default": false, - "ui:widget": "hidden" - }, - "bigQueryConnection": { - "title": "BigQuery Connection", - "description": "Create a BigQuery connection to the Cloud SQL instance. This will allow you to query the Cloud SQL instance from BigQuery. \n We create a new Database User and Password for the BigQuery Connection. We provide BQ Service Agent with `roles/cloudsql.client` role. We also enable `PrivatePathForGoogleCloudServices` in the CloudSQL Instance. [See this for more details](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries)", - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": false - }, - "location": { - "title": "Location", - "description": "The geographic location where the connection should reside. Cloud SQL instance must be in the same location. We set it to the region(like europe-west1) for `ZONAL` SQL Instance and region to `EU` for `REGIONAL` SQL Instance.", - "x-immutable": true, - "examples": [ - "europe-west1", - "EU", - "europe-west4" - ] - }, - "friendlyName": { - "title": "Friendly Name", - "description": "If not provided, defaults to $instanceName-$databaseName " - }, - "connectionId": { - "title": "Connection ID", - "description": "If not provided, defaults to $instanceName-$databaseName ", - "x-immutable": true - }, - "description": { - "description": "A descriptive description for the connection" - }, - "overrideGcpProject": { - "title": "Override GCP Project for the Connection", - "description": "If not provided, it will use the `gcpProjectID` defined above.", - "x-immutable": true - } - } - } - }, - "if": { - "properties": { - "diskAutoresize": { - "const": true - } - } - }, - "then": { - "properties": { - "diskSize": { - "type": "null" - } - } - } - }, - "title": "Cloud SQL", - "description": "Create a Cloud SQL instance, with password, user, database; while storing the created credentials in Secret Manager as well as Kubernetes Secrets/Configmap. \n\n Enabling CloudSQL will, in addition to creating the CloudSQL instance and database, automatically generate CloudSQL user password, put it in GCP Secret Manager in your GCP Project and sync it into a Kubernetes Secret (`$service-$environment-sql-password`) with key name `DATABASE_PASSWORD` inside your namespace. It will also create a ConfigMap (`$service-$environment-sql-config`) containing the database connection details in keys `DATABASE_URL`, `DATABASE_USERNAME`, `DATABASE_INSTANCE_NAME`, `DATABASE_NAME`, `GCP_PROJECT_ID`, and `REGION`. \n\n **[See Feature Docs](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql)** \n\n **[See How to Connect](https://foundry.int.king.com/docs/project/kcp-self-service-docs/latest/cloudsql#how-to-connect-to-cloudsql-instance)**", - "type": "object", - "examples": [ - { - "enabled": true, - "tier": "db-g1-small" - }, - { - "enabled": true, - "tier": "db-g1-small", - "databaseUser": "event-definer", - "databaseName": "event-definer", - "configMap": { - "databaseUserKey": "DATABASE_USERNAME" - } - }, - { - "enabled": true, - "availabilityType": "REGIONAL", - "charset": "UTF8", - "databaseInstanceName": "flow", - "databaseName": "flow", - "databaseUser": "flow", - "diskAutoresize": true, - "databaseVersion": "POSTGRES_15", - "diskSize": 250, - "edition": "ENTERPRISE_PLUS", - "tier": "db-perf-optimized-N-8", - "secret": { - "kubernetesSecretKeyName": "DB_PASSWORD", - "generateSecret": true - }, - "configMap": { - "databaseUrlKey": "DB_URL", - "databaseUserKey": "DB_USER" - } - }, - { - "enabled": true, - "databaseInstanceName": "$service-$env", - "databaseName": "$service", - "databaseUser": "$service", - "availabilityType": "REGIONAL", - "databaseVersion": "MYSQL_8_0", - "edition": "ENTERPRISE", - "tier": "db-g1-small", - "charset": "utf8mb4", - "collation": "utf8mb4_0900_ai_ci", - "diskAutoresize": true, - "diskSize": 10, - "diskType": "PD_SSD", - "databaseFlags": [ - { - "name": "max_connections", - "value": "10000" - }, - { - "name": "log_min_duration_statement", - "value": "1000" - } - ], - "backupConfiguration": { - "enabled": true, - "startTime": "04:00", - "binaryLogEnabled": true - }, - "maintenanceWindow": { - "day": 7, - "hour": 0 - }, - "insightsConfig": { - "queryInsightsEnabled": false, - "queryPlansPerMinute": 5, - "queryStringLength": 1024, - "recordApplicationTags": false, - "recordClientAddress": false - }, - "secret": { - "kubernetesSecretName": "$service-$env-sql-password", - "kubernetesSecretKeyName": "DATABASE_PASSWORD", - "gcpSecretManagerSecretName": "$service-$env-sql-password", - "generateSecret": true - }, - "configMap": { - "databaseUrlKey": "DATABASE_URL", - "databaseUrlValue": "null", - "databaseUserKey": "DATABASE_USER" - } - } - ], - "additionalProperties": true, - "properties": { - "enabled": { - "title": "Enable", - "type": "boolean", - "default": false - } - } - } - } -} \ No newline at end of file diff --git a/testdata/test.sh b/testdata/test.sh deleted file mode 100644 index e69de29..0000000 From 7d84ec6f7c3e8f5875e758ffd87a850a74e3c787 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 14:52:37 +0200 Subject: [PATCH 18/30] 1.2.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cc332e..bf1ee5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 205a075..8f5142a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "1.1.0", + "version": "1.2.0", "main": "index.js", "engines": { "node": ">=4.3" From 35dd7aac74f03e2b00ee95e5fd59d037e8491712 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 15:47:05 +0200 Subject: [PATCH 19/30] fix: revert json schema validate --- dereference.js | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/dereference.js b/dereference.js index 1aa434b..5567d45 100644 --- a/dereference.js +++ b/dereference.js @@ -6,21 +6,6 @@ const $RefParser = require('@apidevtools/json-schema-ref-parser'); const argv = require('minimist')(process.argv.slice(2)); const yaml = require('js-yaml'); -// for schema validation -const Ajv = require('ajv'); -const addFormats = require('ajv-formats'); -// Initialize Ajv with 2020-12 draft support -const ajv = new Ajv({ - strict: false, - allowUnionTypes: true, - validateSchema: false, // Skip schema validation initially -}); -addFormats(ajv); -// Load the 2020-12 meta-schema -const draft202012MetaSchema = require('ajv/dist/refs/json-schema-2020-12/schema.json'); -ajv.addMetaSchema(draft202012MetaSchema); - - // Argument handling if (!argv.s) { @@ -41,17 +26,6 @@ $RefParser.dereference(input, {resolve: {}}, function (err, schema) { return } - - // Validate the schema using ajv - const validate = ajv.compile(schema); - const valid = validate(schema); - if (valid) { - console.log('Schema validated successfully!.'); - } else { - console.error("Schema schema validation failed!", validate.errors); - } - - // Detect output format var type = 'json' if (argv.t) { From ba14814a29eca26a11fa6f3f28bc96baa46947e1 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 15:47:36 +0200 Subject: [PATCH 20/30] 2.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf1ee5c..0ace7f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "1.2.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 8f5142a..993f908 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "1.2.0", + "version": "2.0.0", "main": "index.js", "engines": { "node": ">=4.3" From 1ceedcbbd7a02de7e773b6aa9ff3e6054ee077e9 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:38:41 +0200 Subject: [PATCH 21/30] feat: add meta-schema validation --- README.md | 30 ++++++- dereference.js | 157 ++++++++++++++++++++++++++--------- package-lock.json | 2 +- package.json | 2 +- testdata/correct.schema.json | 7 ++ testdata/output.json | 2 +- 6 files changed, 159 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index fcbcac0..841a0a9 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,38 @@ npx json-dereference-cli-v2 -s [-i ] [-o ] [-t ] # Installing globally: npm install -g json-dereference-cli-v2 -json-dereference-v2 -s [-i ] [-o ] [-t ] +json-dereference-v2 -s [-i ] [-o ] [-t ] +``` + +### Options +- `-s `: Path to the input schema file (required). +- `-i `: Number of spaces for indentation in the output (default: 2). +- `-o `: Path to the output file (optional). +- `-t `: Output type (`json` or `yaml`). If not specified, it is inferred from the output file extension. + +### Examples + +#### Dereference a JSON schema and output to a file: +```bash +json-dereference-v2 -s testdata/correct.schema.json -o testdata/output.json +``` + +#### Dereference a JSON schema and print to stdout in YAML format: +```bash +json-dereference-v2 -s testdata/correct.schema.json -t yaml ``` *Note:* The input file can either be `json`, or `yaml` / `yml`. *Note:* The output file types are either `json` or `yaml` / `yml`. This is determined from the file extension for the output file path passed in or using `-t json|yaml` when writing to stdout. + +### Meta-Validation + +The CLI now supports validating JSON Schemas against their meta-schema to ensure correctness and adherence to best practices. This is automatically performed during dereferencing. + +#### Example: +```bash +json-dereference-v2 -s testdata/correct.schema.json +``` +If the schema is invalid, an error message will be displayed. diff --git a/dereference.js b/dereference.js index 5567d45..d2fd9cd 100644 --- a/dereference.js +++ b/dereference.js @@ -5,60 +5,143 @@ const path = require('path'); const $RefParser = require('@apidevtools/json-schema-ref-parser'); const argv = require('minimist')(process.argv.slice(2)); const yaml = require('js-yaml'); +const Ajv = require('ajv'); +const Ajv2020 = require('ajv/dist/2020'); +// Add a new CLI option `--no-validate` to disable schema validation +const disableValidation = argv['no-validate'] || false; -// Argument handling -if (!argv.s) { - console.error('Usage: ' + process.argv[1] + ' -s [-i ] [-o ] [-t ]'); - process.exit(1); -} - -var input = path.resolve(argv.s); -var output = argv.o ? path.resolve(argv.o) : undefined; -var indent = argv.i !== undefined ? argv.i : 2 - -// Schema dereferencing -console.warn("Dereferencing schema: " + input); -$RefParser.dereference(input, {resolve: {}}, function (err, schema) { - if (err) { - console.error('ERROR: ' + err); +/** + * Validates CLI arguments and returns parsed options. + * @returns {Object} Parsed CLI options. + */ +function validateArguments() { + if (!argv.s) { + console.error('Usage: ' + process.argv[1] + ' -s [-i ] [-o ] [-t ] [--no-validate]'); process.exit(1); - return } - // Detect output format - var type = 'json' - if (argv.t) { - type = argv.t - } else if (output) { - var ext = path.parse(output).ext; + return { + input: path.resolve(argv.s), + output: argv.o ? path.resolve(argv.o) : undefined, + indent: argv.i !== undefined ? argv.i : 2, + type: argv.t + }; +} - if (ext == '.json') { - type = 'json' +/** + * Detects the output format based on CLI arguments or file extension. + * @param {string} output - Output file path. + * @param {string} type - Output type specified by the user. + * @returns {string} Detected output type ('json' or 'yaml'). + */ +function detectOutputFormat(output, type) { + if (type) { + return type; + } else if (output) { + const ext = path.parse(output).ext; + if (ext === '.json') { + return 'json'; } else if (ext.match(/^\.?(yaml|yml)$/)) { - type = 'yaml' + return 'yaml'; } else { console.error('ERROR: Cannot detect output file type from file name (please set -t ): ' + output); process.exit(1); } } + return 'json'; +} - // Generate resolved schema type - var data = '' - if (type == 'json') { - data = JSON.stringify(schema, null, indent); - } else if (type == 'yaml') { - data = yaml.dump(schema, {encoding: 'utf8', indent: indent, noRefs: true}); +/** + * Validates the input schema against the appropriate JSON Schema meta-schema. + * @param {Object} schema - The JSON schema to validate. + * @param {string} schemaVersion - The JSON Schema version (e.g., '2020-12', 'draft-07'). + * @throws Will throw an error if the schema is invalid or the version is unsupported. + */ +function validateSchemaMeta(schema, schemaVersion) { + let ajv; + + if (schemaVersion === '2020-12') { + ajv = new Ajv2020(); + } else if (schemaVersion === 'draft-07') { + ajv = new Ajv(); + ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-07.json')); } else { - console.error('ERROR: Unsupported output file type: ' + type); - process.exit(1); + throw new Error(`Unsupported schema version: ${schemaVersion}`); + } + + const validate = ajv.compile(ajv.getSchema('$schema') || {}); + + if (!validate(schema)) { + throw new Error('Schema meta-validation failed: ' + JSON.stringify(validate.errors, null, 2)); + } +} + +/** + * Detects the JSON Schema version from the $schema property. + * @param {Object} schema - The JSON schema. + * @returns {string} The detected schema version (e.g., '2020-12', 'draft-07'). + */ +function detectSchemaVersion(schema) { + const schemaUrl = schema.$schema; + + if (schemaUrl.includes('draft/2020-12')) { + return '2020-12'; + } else if (schemaUrl.includes('draft-07')) { + return 'draft-07'; + } else { + throw new Error('Unsupported or missing $schema property in the JSON Schema.'); } +} - // Output resolved schema +/** + * Writes the resolved schema to the specified output or stdout. + * @param {string} output - Output file path. + * @param {string} data - Resolved schema data. + */ +function writeOutput(output, data) { if (output) { - fs.writeFileSync(output, data, {encoding: 'utf8', flag: 'w'}); + fs.writeFileSync(output, data, { encoding: 'utf8', flag: 'w' }); console.warn('Wrote file: ' + output); } else { - console.log(data) + console.log(data); } -}); +} + +/** + * Main function to dereference the schema. + */ +async function main() { + const { input, output, indent, type } = validateArguments(); + + console.warn('Dereferencing schema: ' + input); + try { + const schema = await $RefParser.dereference(input, { resolve: {} }); + + if (!disableValidation) { + const schemaVersion = detectSchemaVersion(schema); + validateSchemaMeta(schema, schemaVersion); + } else { + console.warn('Schema validation is disabled.'); + } + + const outputType = detectOutputFormat(output, type); + let data; + + if (outputType === 'json') { + data = JSON.stringify(schema, null, indent); + } else if (outputType === 'yaml') { + data = yaml.dump(schema, { encoding: 'utf8', indent: indent, noRefs: true }); + } else { + console.error('ERROR: Unsupported output file type: ' + outputType); + process.exit(1); + } + + writeOutput(output, data); + } catch (err) { + console.error('ERROR: ' + err.message); + process.exit(1); + } +} + +main(); diff --git a/package-lock.json b/package-lock.json index 0ace7f2..d9714b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "json-dereference-cli-v2", - "version": "1.1.0", + "version": "2.0.0", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.9.3", "ajv": "^8.17.1", diff --git a/package.json b/package.json index 993f908..0cd28c8 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "node": ">=4.3" }, "scripts": { - "test": "((node dereference.js -s testdata/correct.schema.json && echo \"Success: exit code $?\") && (node dereference.js -s testdata/error.schema.json || echo \"Success: exit code $?\")) && echo \"Test Passed!\"" + "test": "node dereference.js -s testdata/correct.schema.json -o testdata/output.json && echo 'Correct schema test passed!' && node dereference.js -s testdata/error.schema.json || echo 'Error schema test passed!' && echo 'Meta-validation test passed!'" }, "repository": { "type": "git", diff --git a/testdata/correct.schema.json b/testdata/correct.schema.json index 9aa6840..c8d568b 100644 --- a/testdata/correct.schema.json +++ b/testdata/correct.schema.json @@ -4,6 +4,13 @@ "properties": { "test": { "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone-strict/_definitions.json#/definitions/io.k8s.apimachinery.pkg.api.resource.Quantity" + }, + "test2": { + + "type": "boolean", + "description": "A boolean value indicating true or false", + "x-imutable": true + } } } \ No newline at end of file diff --git a/testdata/output.json b/testdata/output.json index 6bf2faa..34f4ab6 100644 --- a/testdata/output.json +++ b/testdata/output.json @@ -1,5 +1,5 @@ { - "$schema": "https://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "test": { From e322ac6eaa965060762ec644b13c8028776f9007 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:38:58 +0200 Subject: [PATCH 22/30] 3.0.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9714b2..81e052b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "2.0.0", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 0cd28c8..624ceef 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "2.0.0", + "version": "3.0.0", "main": "index.js", "engines": { "node": ">=4.3" From bb85f908d8d9d5bebed3628ba79d6760a652ea55 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:42:44 +0200 Subject: [PATCH 23/30] add more tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 624ceef..03a9236 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "node": ">=4.3" }, "scripts": { - "test": "node dereference.js -s testdata/correct.schema.json -o testdata/output.json && echo 'Correct schema test passed!' && node dereference.js -s testdata/error.schema.json || echo 'Error schema test passed!' && echo 'Meta-validation test passed!'" + "test": "node dereference.js -s testdata/correct.schema.json -o testdata/output.json && echo 'Correct schema test passed!' && node dereference.js -s testdata/error.schema.json || echo 'Error schema test passed!' && node dereference.js -s testdata/correct.schema.json -t yaml && echo 'YAML output test passed!' && node dereference.js -s testdata/correct.schema.json --no-validate && echo 'No validation test passed!' && echo 'Meta-validation test passed!'" }, "repository": { "type": "git", From 99b97fbb7224ee867bb563e31147896b535ea0bc Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:43:08 +0200 Subject: [PATCH 24/30] 4.0.0 --- package-lock.json | 2 +- package.json | 2 +- testdata/output.json | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81e052b..026be2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "3.0.0", + "version": "4.0.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 03a9236..9bb4d0b 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "3.0.0", + "version": "4.0.0", "main": "index.js", "engines": { "node": ">=4.3" diff --git a/testdata/output.json b/testdata/output.json index 34f4ab6..be63637 100644 --- a/testdata/output.json +++ b/testdata/output.json @@ -11,6 +11,11 @@ "type": "number" } ] + }, + "test2": { + "type": "boolean", + "description": "A boolean value indicating true or false", + "x-imutable": true } } } \ No newline at end of file From 382bde180fa2ccde3ad2d5b33aed96820042806b Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:51:53 +0200 Subject: [PATCH 25/30] fix: includes error --- dereference.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dereference.js b/dereference.js index d2fd9cd..7e729b4 100644 --- a/dereference.js +++ b/dereference.js @@ -85,12 +85,16 @@ function validateSchemaMeta(schema, schemaVersion) { function detectSchemaVersion(schema) { const schemaUrl = schema.$schema; + if (!schemaUrl) { + throw new Error('Missing $schema property in the JSON Schema.'); + } + if (schemaUrl.includes('draft/2020-12')) { return '2020-12'; } else if (schemaUrl.includes('draft-07')) { return 'draft-07'; } else { - throw new Error('Unsupported or missing $schema property in the JSON Schema.'); + throw new Error('Unsupported $schema property in the JSON Schema: ' + schemaUrl); } } From b232fbf0c33f347eabb429efb63ee1439d6679de Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:52:00 +0200 Subject: [PATCH 26/30] 4.0.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 026be2b..60e8b5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "4.0.0", + "version": "4.0.1", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 9bb4d0b..4297540 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "4.0.0", + "version": "4.0.1", "main": "index.js", "engines": { "node": ">=4.3" From 862a1ddeab04b9003865f391ead17343978b3eac Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:55:44 +0200 Subject: [PATCH 27/30] use default draft-07 schema --- dereference.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dereference.js b/dereference.js index 7e729b4..b87e015 100644 --- a/dereference.js +++ b/dereference.js @@ -86,7 +86,8 @@ function detectSchemaVersion(schema) { const schemaUrl = schema.$schema; if (!schemaUrl) { - throw new Error('Missing $schema property in the JSON Schema.'); + console.warn('No $schema property found. Defaulting to draft-07 schema.'); + return 'draft-07'; } if (schemaUrl.includes('draft/2020-12')) { From bafe18a6420dc24fa859b760c6c748293b091af0 Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 16:56:00 +0200 Subject: [PATCH 28/30] 4.0.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60e8b5d..6730f8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 4297540..7aec7eb 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "4.0.1", + "version": "4.0.2", "main": "index.js", "engines": { "node": ">=4.3" From 6c1ed46d3bdfc504b07ad8b0b96033c77404061a Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 17:01:15 +0200 Subject: [PATCH 29/30] fix: if schema already present --- dereference.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dereference.js b/dereference.js index b87e015..d741901 100644 --- a/dereference.js +++ b/dereference.js @@ -65,7 +65,10 @@ function validateSchemaMeta(schema, schemaVersion) { ajv = new Ajv2020(); } else if (schemaVersion === 'draft-07') { ajv = new Ajv(); - ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-07.json')); + const draft07MetaSchema = require('ajv/lib/refs/json-schema-draft-07.json'); + if (!ajv.getSchema(draft07MetaSchema.$id)) { + ajv.addMetaSchema(draft07MetaSchema); + } } else { throw new Error(`Unsupported schema version: ${schemaVersion}`); } From fbf78d3980ea638aef47bd62253bdac7de9993ce Mon Sep 17 00:00:00 2001 From: Siddharth Yadav Date: Mon, 7 Apr 2025 17:01:20 +0200 Subject: [PATCH 30/30] 4.0.3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6730f8b..a0e8525 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "json-dereference-cli-v2", - "version": "4.0.2", + "version": "4.0.3", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 7aec7eb..faaf015 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "cli", "tool" ], - "version": "4.0.2", + "version": "4.0.3", "main": "index.js", "engines": { "node": ">=4.3"