From 16c2dbedab8597bf0e6541d2d554d8f3151099f7 Mon Sep 17 00:00:00 2001 From: Teo Koon Peng Date: Wed, 7 Aug 2024 10:24:29 +0800 Subject: [PATCH] add app-config json schema (#988) Signed-off-by: Teo Koon Peng --- .husky/pre-commit | 1 - package.json | 5 + packages/dashboard/app-config.d.ts | 56 ------- packages/dashboard/app-config.json | 1 + packages/dashboard/app-config.schema.json | 194 ++++++++++++++++++++++ packages/dashboard/package.json | 2 + packages/dashboard/src/app-config.test.ts | 19 +++ packages/dashboard/tsconfig.app.json | 4 +- packages/dashboard/tsconfig.gen.json | 7 + pnpm-lock.yaml | 125 ++++++++++++++ 10 files changed, 355 insertions(+), 59 deletions(-) delete mode 100644 packages/dashboard/app-config.d.ts create mode 100644 packages/dashboard/app-config.schema.json create mode 100644 packages/dashboard/src/app-config.test.ts create mode 100644 packages/dashboard/tsconfig.gen.json diff --git a/.husky/pre-commit b/.husky/pre-commit index 85b67f155..5ee7abd87 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,2 +1 @@ - pnpm exec lint-staged diff --git a/package.json b/package.json index 647e597e4..6fdfea9d4 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,11 @@ ".venv/bin/pipenv run black" ] }, + "pnpm": { + "overrides": { + "typescript-json-schema>@types/node": "*" + } + }, "overrides": { "react-error-overlay": "6.0.9" } diff --git a/packages/dashboard/app-config.d.ts b/packages/dashboard/app-config.d.ts deleted file mode 100644 index 34ddfbb1b..000000000 --- a/packages/dashboard/app-config.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -export interface RobotResource { - icon?: string; - scale?: number; -} - -export interface FleetResource { - default: RobotResource; -} - -export interface LogoResource { - header: string; -} - -export interface Resources { - fleets: Record; - logos: LogoResource; -} - -export interface TaskResource { - taskDefinitionId: string; - displayName?: string; -} - -export interface StubAuthConfig { - provider: 'stub'; - config: null; -} - -export interface KeycloakAuthConfig { - provider: 'keycloak'; - config: { - url: string; - realm: string; - clientId: string; - }; -} - -export interface AppConfigInput { - rmfServerUrl: string; - trajectoryServerUrl: string; - auth: KeycloakAuthConfig | StubAuthConfig; - helpLink: string; - reportIssue: string; - pickupZones: string[]; - defaultZoom: number; - defaultRobotZoom: number; - attributionPrefix: string; - defaultMapLevel: string; - allowedTasks: TaskResource[]; - resources: Record & Record<'default', Resources>; - customTabs?: boolean; - adminTab?: boolean; - // FIXME(koonpeng): this is used for very specific tasks, should be removed when mission - // system is implemented. - cartIds: string[]; -} diff --git a/packages/dashboard/app-config.json b/packages/dashboard/app-config.json index 7397b8dfe..e77f0a0d6 100644 --- a/packages/dashboard/app-config.json +++ b/packages/dashboard/app-config.json @@ -1,4 +1,5 @@ { + "$schema": "./app-config.schema.json", "rmfServerUrl": "http://localhost:8000", "trajectoryServerUrl": "ws://localhost:8006", "authConfig": {}, diff --git a/packages/dashboard/app-config.schema.json b/packages/dashboard/app-config.schema.json new file mode 100644 index 000000000..c918eeee9 --- /dev/null +++ b/packages/dashboard/app-config.schema.json @@ -0,0 +1,194 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "BuildConfig": { + "properties": { + "adminTab": { + "type": "boolean" + }, + "authProvider": { + "enum": [ + "keycloak", + "stub" + ], + "type": "string" + }, + "baseUrl": { + "type": "string" + }, + "customTabs": { + "type": "boolean" + } + }, + "required": [ + "authProvider", + "baseUrl" + ], + "type": "object" + }, + "KeycloakAuthConfig": { + "properties": { + "clientId": { + "type": "string" + }, + "realm": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "clientId", + "realm", + "url" + ], + "type": "object" + }, + "LogoResource": { + "properties": { + "header": { + "type": "string" + } + }, + "required": [ + "header" + ], + "type": "object" + }, + "Record<\"default\",Resources>": { + "properties": { + "default": { + "$ref": "#/definitions/Resources" + } + }, + "required": [ + "default" + ], + "type": "object" + }, + "Record": { + "type": "object" + }, + "Record": { + "type": "object" + }, + "Resources": { + "properties": { + "fleets": { + "$ref": "#/definitions/Record" + }, + "logos": { + "$ref": "#/definitions/LogoResource" + } + }, + "required": [ + "fleets", + "logos" + ], + "type": "object" + }, + "StubAuthConfig": { + "type": "object" + }, + "TaskResource": { + "properties": { + "displayName": { + "type": "string" + }, + "taskDefinitionId": { + "type": "string" + } + }, + "required": [ + "taskDefinitionId" + ], + "type": "object" + } + }, + "properties": { + "allowedTasks": { + "items": { + "$ref": "#/definitions/TaskResource" + }, + "type": "array" + }, + "attributionPrefix": { + "type": "string" + }, + "authConfig": { + "anyOf": [ + { + "$ref": "#/definitions/StubAuthConfig" + }, + { + "$ref": "#/definitions/KeycloakAuthConfig" + } + ] + }, + "buildConfig": { + "$ref": "#/definitions/BuildConfig" + }, + "cartIds": { + "items": { + "type": "string" + }, + "type": "array" + }, + "defaultMapLevel": { + "type": "string" + }, + "defaultRobotZoom": { + "type": "number" + }, + "defaultZoom": { + "type": "number" + }, + "helpLink": { + "type": "string" + }, + "pickupZones": { + "items": { + "type": "string" + }, + "type": "array" + }, + "reportIssue": { + "type": "string" + }, + "resources": { + "allOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "$ref": "#/definitions/Record<\"default\",Resources>" + } + ] + }, + "rmfServerUrl": { + "type": "string" + }, + "trajectoryServerUrl": { + "type": "string" + } + }, + "required": [ + "allowedTasks", + "attributionPrefix", + "authConfig", + "buildConfig", + "cartIds", + "defaultMapLevel", + "defaultRobotZoom", + "defaultZoom", + "helpLink", + "pickupZones", + "reportIssue", + "resources", + "rmfServerUrl", + "trajectoryServerUrl" + ], + "type": "object" +} + diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json index 10542c49a..d9b83da02 100644 --- a/packages/dashboard/package.json +++ b/packages/dashboard/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "pnpm run --filter {.}^... build && vite build", "build-storybook": "storybook build", + "gen-app-config-schema": "pnpm typescript-json-schema tsconfig.gen.json AppConfig --required -o app-config.schema.json", "lint": "tsc --build && eslint --max-warnings 0 src", "start": "concurrently npm:start:rmf-server npm:start:react", "start:airport": "RMF_DASHBOARD_DEMO_MAP=airport_terminal.launch.xml pnpm run start:sim", @@ -75,6 +76,7 @@ "history": "^5.3.0", "storybook": "^8.0.5", "typescript": "~5.4.3", + "typescript-json-schema": "^0.64.0", "vite": "^5.3.5", "vitest": "^2.0.4" } diff --git a/packages/dashboard/src/app-config.test.ts b/packages/dashboard/src/app-config.test.ts new file mode 100644 index 000000000..52e76c200 --- /dev/null +++ b/packages/dashboard/src/app-config.test.ts @@ -0,0 +1,19 @@ +/** + * @vitest-environment node + */ + +import childProcess from 'child_process'; +import fs from 'fs'; +import { expect, it } from 'vitest'; + +it('app-config json schema is up to date', () => { + const curSchema = fs.readFileSync('app-config.schema.json'); + const newSchema = childProcess.execSync( + 'pnpm typescript-json-schema tsconfig.gen.json AppConfig --required', + ); + if (!newSchema.equals(curSchema)) { + expect.fail( + 'app config schema has changed, please run `pnpm gen-app-config-schema` to update it', + ); + } +}); diff --git a/packages/dashboard/tsconfig.app.json b/packages/dashboard/tsconfig.app.json index f417b81b0..1babef830 100644 --- a/packages/dashboard/tsconfig.app.json +++ b/packages/dashboard/tsconfig.app.json @@ -21,7 +21,7 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, + "noFallthroughCasesInSwitch": true }, - "include": ["src"] + "include": ["src", "app-config.json"] } diff --git a/packages/dashboard/tsconfig.gen.json b/packages/dashboard/tsconfig.gen.json new file mode 100644 index 000000000..94d1308f7 --- /dev/null +++ b/packages/dashboard/tsconfig.gen.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "composite": false, + "tsBuildInfoFile": null + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6179cfb3..b5fcf1564 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + typescript-json-schema>@types/node: '*' + importers: .: @@ -244,6 +247,9 @@ importers: typescript: specifier: ~5.4.3 version: 5.4.5 + typescript-json-schema: + specifier: ^0.64.0 + version: 0.64.0(@swc/core@1.5.7) vite: specifier: ^5.3.5 version: 5.3.5(@types/node@20.14.12)(terser@5.31.3) @@ -1192,6 +1198,10 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@date-io/core@2.17.0': resolution: {integrity: sha512-+EQE8xZhRM/hsY0CDTVyayMDDY5ihc4MqXCrPxooKw19yAzUIC6uUqsZeaOFNL9YKTNxYKrJP5DFgE8o5xRCOw==} @@ -1623,6 +1633,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jsdevtools/coverage-istanbul-loader@3.0.5': resolution: {integrity: sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==} @@ -2502,6 +2515,18 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@tweenjs/tween.js@23.1.2': resolution: {integrity: sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==} @@ -3063,6 +3088,10 @@ packages: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} @@ -6001,6 +6030,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-equal@1.2.5: + resolution: {integrity: sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==} + path-exists@2.1.0: resolution: {integrity: sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==} engines: {node: '>=0.10.0'} @@ -6647,6 +6679,10 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7235,6 +7271,20 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + ts-node@9.1.1: resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==} engines: {node: '>=10.0.0'} @@ -7321,6 +7371,15 @@ packages: typescript: optional: true + typescript-json-schema@0.64.0: + resolution: {integrity: sha512-Sew8llkYSzpxaMoGjpjD6NMFCr6DoWFHLs7Bz1LU48pzzi8ok8W+GZs9cG87IMBpC0UI7qwBMUI2um0LGxxLOg==} + hasBin: true + + typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} @@ -7448,6 +7507,9 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -8678,6 +8740,10 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@date-io/core@2.17.0': {} '@date-io/core@3.0.0': {} @@ -9232,6 +9298,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jsdevtools/coverage-istanbul-loader@3.0.5': dependencies: convert-source-map: 1.9.0 @@ -10349,6 +10420,14 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + '@tweenjs/tween.js@23.1.2': {} '@types/aria-query@4.2.2': {} @@ -11132,6 +11211,10 @@ snapshots: acorn-walk@7.2.0: {} + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.1 + acorn@7.4.1: {} acorn@8.12.1: {} @@ -14719,6 +14802,8 @@ snapshots: parseurl@1.3.3: {} + path-equal@1.2.5: {} + path-exists@2.1.0: dependencies: pinkie-promise: 2.0.1 @@ -15420,6 +15505,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safe-stable-stringify@2.4.3: {} + safer-buffer@2.1.2: {} sax@1.4.1: {} @@ -16131,6 +16218,26 @@ snapshots: ts-dedent@2.2.0: {} + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@20.14.12)(typescript@5.1.6): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.14.12 + acorn: 8.12.1 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.5.7 + ts-node@9.1.1(typescript@5.4.5): dependencies: arg: 4.1.3 @@ -16230,6 +16337,22 @@ snapshots: transitivePeerDependencies: - supports-color + typescript-json-schema@0.64.0(@swc/core@1.5.7): + dependencies: + '@types/json-schema': 7.0.15 + '@types/node': 20.14.12 + glob: 7.2.3 + path-equal: 1.2.5 + safe-stable-stringify: 2.4.3 + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@20.14.12)(typescript@5.1.6) + typescript: 5.1.6 + yargs: 17.7.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + + typescript@5.1.6: {} + typescript@5.4.5: {} ua-parser-js@0.7.38: {} @@ -16357,6 +16480,8 @@ snapshots: uuid@9.0.1: {} + v8-compile-cache-lib@3.0.1: {} + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.25