From bf8ce6809697a22303676e9a1eca0993f9de9cc2 Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Date: Sun, 28 Aug 2022 15:55:46 +0530 Subject: [PATCH] Check and validate JSON schema (#1509) * Validate json * wip: gh workflow test * wip: added workflow for JSON validation * Schema Improved. * Improved Workflow and Schema check for JSON files * Fixed JSON Schema * Fixed typo * Removed auto generated file * updated required fields in chainSchema * updated chain schema * removed `network` from README.md example * improved schemaCheck script * Matching ChainID with file name schema. --- .github/workflows/validate_json.yml | 24 ++++++ tools/package-lock.json | 114 ++++++++++++++++++++++++++ tools/package.json | 5 ++ tools/{index.js => rmNetwork.js} | 12 +-- tools/schema/chainSchema.json | 123 ++++++++++++++++++++++++++++ tools/schemaCheck.js | 25 ++++++ 6 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/validate_json.yml create mode 100644 tools/package-lock.json create mode 100644 tools/package.json rename tools/{index.js => rmNetwork.js} (63%) create mode 100644 tools/schema/chainSchema.json create mode 100644 tools/schemaCheck.js diff --git a/.github/workflows/validate_json.yml b/.github/workflows/validate_json.yml new file mode 100644 index 000000000000..493033306a23 --- /dev/null +++ b/.github/workflows/validate_json.yml @@ -0,0 +1,24 @@ +name: Check JSON Schema + +on: + push: + pull_request: + +jobs: + validate_json: + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - uses: actions/cache@v2 + name: Configure npm caching + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/workflows/validate_json.yml') }} + restore-keys: | + ${{ runner.os }}-npm- + - name: Run JSON Validation + working-directory: ./tools + run: |- + npm install + node schemaCheck.js \ No newline at end of file diff --git a/tools/package-lock.json b/tools/package-lock.json new file mode 100644 index 000000000000..6a00bb35f20a --- /dev/null +++ b/tools/package-lock.json @@ -0,0 +1,114 @@ +{ + "name": "tools", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "ajv": "^8.11.0" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + } + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "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==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + } + } +} diff --git a/tools/package.json b/tools/package.json new file mode 100644 index 000000000000..503dfeba770e --- /dev/null +++ b/tools/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "ajv": "^8.11.0" + } +} diff --git a/tools/index.js b/tools/rmNetwork.js similarity index 63% rename from tools/index.js rename to tools/rmNetwork.js index 8ca515baeef5..603add0a4d85 100644 --- a/tools/index.js +++ b/tools/rmNetwork.js @@ -2,18 +2,20 @@ * This removed `network` param from all the chain files * Since this is the only tool here, it is here in index.js */ + const fs = require('fs'); -const { exec } = require('child_process'); const chainFiles = fs.readdirSync('../_data/chains/'); -for(const chainFile of chainFiles){ +for (const chainFile of chainFiles) { const fileLocation = `../_data/chains/${chainFile}` - const fileData = fs.readFileSync(fileLocation,'utf8') + const fileData = fs.readFileSync(fileLocation, 'utf8') const fileDataJson = JSON.parse(fileData) - if(fileDataJson.network){ + + if (fileDataJson.network) { delete fileDataJson.network fs.writeFileSync(fileLocation, JSON.stringify(fileDataJson, null, 2)) } } -// TODO: Run `npx prettier --write --ignore-unknown _data`from Project Directory \ No newline at end of file +// Note: +// Run `npx prettier --write --ignore-unknown _data`from Project Directory \ No newline at end of file diff --git a/tools/schema/chainSchema.json b/tools/schema/chainSchema.json new file mode 100644 index 000000000000..144876e231f6 --- /dev/null +++ b/tools/schema/chainSchema.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/schema#", + "title": "EIP155 Chain Data", + "type":"object", + "required": ["name","shortName","chain","chainId","networkId","rpc","faucets","infoURL","nativeCurrency"], + "properties": { + "name":{ + "type":"string", + "description": "Name of the Network" + }, + "shortName":{ + "type":"string" + }, + "title":{ + "type":"string", + "description": "Optional title for the Network" + }, + "chain":{ + "type":"string", + "description": "Name of the Network" + }, + "icon":{ + "type":"string", + "description": "Icon type" + }, + "rpc":{ + "type":"array", + "items":{ + "type":"string" + } + }, + "faucets":{ + "type":"array", + "items":{ + "type":"string" + } + }, + "nativeCurrency":{ + "type":"object", + "properties": { + "name":{ + "type":"string", + "description":"Name of the Native Currency" + }, + "symbol":{ + "type":"string", + "description":"Symbol of the Native Currency" + }, + "decimals":{ + "type":"number", + "description":"Decimal points supported" + } + } + }, + "infoURL":{ + "type":"string", + "description": "infoURL" + }, + "chainId":{ + "type":"number", + "description": "Chain ID of the Network" + }, + "networkId":{ + "type":"number", + "description": "Network ID of the Network" + }, + "slip44":{ + "type":"number", + "description": "Slip44 of the Network" + }, + "ens":{ + "type":"object", + "properties": { + "registry":{ + "type":"string" + } + } + }, + "explorers":{ + "type":"array", + "items":{ + "type":"object", + "properties": { + "name":{ + "type":"string" + }, + "url":{ + "type":"string" + }, + "standard":{ + "type":"string" + } + } + } + }, + "parent":{ + "type":"object", + "properties": { + "type":{ + "type":"string" + }, + "chain":{ + "type":"string" + }, + "bridges":{ + "type":"array", + "items": { + "type":"object", + "properties":{ + "url": { + "type":"string" + } + } + } + } + } + }, + "status":{ + "type":"string" + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/tools/schemaCheck.js b/tools/schemaCheck.js new file mode 100644 index 000000000000..e64e454a4c2e --- /dev/null +++ b/tools/schemaCheck.js @@ -0,0 +1,25 @@ +const fs = require('fs'); +const Ajv = require("ajv") +const ajv = new Ajv() +const schema = require('./schema/chainSchema.json') +const chainFiles = fs.readdirSync('../_data/chains/'); + +const filesWithErrors = [] +for(const chainFile of chainFiles){ + const fileLocation = `../_data/chains/${chainFile}` + const fileData = fs.readFileSync(fileLocation,'utf8') + const fileDataJson = JSON.parse(fileData) + const chainIdFromFileName = chainFile.match(/eip155-(\d+)\.json/)[1] + if(chainIdFromFileName != fileDataJson.chainId){ + throw new Error(`File Name does not match with ChainID in ${chainFile}`) + } + const valid = ajv.validate(schema, fileDataJson) + if(!valid) { + console.error(ajv.errors) + filesWithErrors.push(chainFile) + } +} + +if(filesWithErrors.length > 0){ + throw new Error(`Invalid JSON Schema in ${filesWithErrors.length} files at ${filesWithErrors.join(",")}`) +} \ No newline at end of file