From d1af6778de758e4925cae4ea0a3b98932991393b Mon Sep 17 00:00:00 2001 From: RahulGautamSingh <rahultesnik@gmail.com> Date: Thu, 29 Aug 2024 11:37:39 +0530 Subject: [PATCH] refactor: data validation using schema (#30797) Co-authored-by: Michael Kriese <michael.kriese@visualon.de> --- lib/data/monorepo.json | 1 + test/validate-schemas.spec.ts | 64 ++++++++++++++++++++++++++++++ tools/schemas/monorepo-schema.json | 53 +++++++++++++++++++++++++ tools/schemas/schema.ts | 14 +++++++ 4 files changed, 132 insertions(+) create mode 100644 test/validate-schemas.spec.ts create mode 100644 tools/schemas/monorepo-schema.json create mode 100644 tools/schemas/schema.ts diff --git a/lib/data/monorepo.json b/lib/data/monorepo.json index 6705901889..a23fdaa4f8 100644 --- a/lib/data/monorepo.json +++ b/lib/data/monorepo.json @@ -1,4 +1,5 @@ { + "$schema": "../../tools/schemas/monorepo-schema.json", "repoGroups": { "accounts": "https://github.com/accounts-js/accounts", "acot": "https://github.com/acot-a11y/acot", diff --git a/test/validate-schemas.spec.ts b/test/validate-schemas.spec.ts new file mode 100644 index 0000000000..e7f424570b --- /dev/null +++ b/test/validate-schemas.spec.ts @@ -0,0 +1,64 @@ +import fs from 'fs-extra'; +import upath from 'upath'; +import { Json } from '../lib/util/schema-utils'; +import { capitalize } from '../tools/docs/utils'; +import * as Schemas from '../tools/schemas/schema'; + +describe('validate-schemas', () => { + it('validate json files in lib/data against their schemas', async () => { + const dataFileDir = 'lib/data'; + const schemaDir = 'tools/schemas'; + const schemasAndJsonFiles: { + schemaName: keyof typeof Schemas; + dataFileName: string; + }[] = []; + + const schemaFiles = (await fs.readdir(schemaDir)).filter( + (file) => upath.extname(file) === '.json', + ); + + for (const schemaFile of schemaFiles) { + const correspondingDatFileName = schemaFile.replace('-schema', ''); + const schemaName = `${schemaFile + .replace('.json', '') + .split('-') + .map(capitalize) + .join('')}` as keyof typeof Schemas; + schemasAndJsonFiles.push({ + schemaName, + dataFileName: correspondingDatFileName, + }); + } + + const settledPromises = await Promise.allSettled( + schemasAndJsonFiles.map(async ({ schemaName, dataFileName }) => { + const data = Json.parse( + await fs.readFile(upath.join(dataFileDir, dataFileName), 'utf8'), + ); + + // validate json data against schema: using parse here instead of safeParse so we throw + // this leads to a better error message when the assertion fails + // eslint-disable-next-line import/namespace + Schemas[schemaName].parse(data); + }), + ); + + for (let i = 0; i < settledPromises.length; i++) { + const { schemaName, dataFileName } = schemasAndJsonFiles[i]; + const res = { + schemaName, + dataFileName, + settledPromise: { reason: undefined, ...settledPromises[i] }, + }; + + expect(res).toMatchObject({ + schemaName, + dataFileName, + settledPromise: { + status: 'fulfilled', + reason: undefined, + }, + }); + } + }); +}); diff --git a/tools/schemas/monorepo-schema.json b/tools/schemas/monorepo-schema.json new file mode 100644 index 0000000000..6bc1d6e7a9 --- /dev/null +++ b/tools/schemas/monorepo-schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "repoGroups": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9. -]+$": { + "oneOf": [ + { "type": "string", "format": "uri" }, + { + "type": "array", + "items": { "type": "string", "format": "uri" } + } + ] + } + }, + "additionalProperties": false + }, + "orgGroups": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9. -]+$": { + "oneOf": [ + { "type": "string", "format": "uri" }, + { + "type": "array", + "items": { "type": "string", "format": "uri" } + } + ] + } + }, + "additionalProperties": false + }, + "patternGroups": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9. -]+$": { + "oneOf": [ + { "type": "string", "pattern": "^/.+/$" }, + { + "type": "array", + "items": { "type": "string", "pattern": "^/.+/$" } + } + ] + } + }, + "additionalProperties": false + } + }, + "required": ["repoGroups", "orgGroups", "patternGroups"], + "additionalProperties": false +} diff --git a/tools/schemas/schema.ts b/tools/schemas/schema.ts new file mode 100644 index 0000000000..b5361a2472 --- /dev/null +++ b/tools/schemas/schema.ts @@ -0,0 +1,14 @@ +import { z } from 'zod'; + +const UrlSchema = z.record( + z.string(), + z.union([z.string(), z.array(z.string())]), +); + +const MonorepoSchema = z.object({ + repoGroups: UrlSchema, + orgGroups: UrlSchema, + patternGroups: UrlSchema, +}); + +export { MonorepoSchema }; -- GitLab