Add generate and verify commands.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
committed by
Fredrik Adelöw
parent
978b09f273
commit
06b016e9b8
@@ -92,6 +92,9 @@ jobs:
|
||||
- name: verify api reference
|
||||
run: node scripts/verify-api-reference.js
|
||||
|
||||
- name: verify openapi yaml <> ts files
|
||||
run: yarn backstage-cli schema:openapi:verify
|
||||
|
||||
- name: verify doc links
|
||||
run: node scripts/verify-links.js
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"jest-css-modules": "^2.1.0",
|
||||
"jest-environment-jsdom": "^29.0.2",
|
||||
"jest-runtime": "^29.0.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-schema": "^0.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mini-css-extract-plugin": "^2.4.2",
|
||||
|
||||
@@ -80,6 +80,13 @@ export function registerRepoCommand(program: Command) {
|
||||
lazy(() => import('./repo/list-deprecations').then(m => m.command)),
|
||||
);
|
||||
|
||||
command
|
||||
.command('schema:openapi:verify')
|
||||
.description(
|
||||
'Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.ts` file.',
|
||||
)
|
||||
.action(lazy(() => import('./openapi/verify').then(m => m.bulkCommand)));
|
||||
|
||||
command
|
||||
.command('test')
|
||||
.allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args
|
||||
@@ -179,6 +186,20 @@ export function registerScriptCommand(program: Command) {
|
||||
.command('postpack')
|
||||
.description('Restores the changes made by the prepack command')
|
||||
.action(lazy(() => import('./pack').then(m => m.post)));
|
||||
|
||||
command
|
||||
.command('schema:openapi:verify')
|
||||
.description(
|
||||
'Verifies that an OpenAPI spec is defined in both an `openapi.yaml` file and a `schema/openapi.ts` file.',
|
||||
)
|
||||
.action(lazy(() => import('./openapi/verify').then(m => m.command)));
|
||||
|
||||
command
|
||||
.command('schema:openapi:generate')
|
||||
.description(
|
||||
'Generates a `schema/openapi.ts` file from an OpenAPI spec defined in `openapi.yaml`.',
|
||||
)
|
||||
.action(lazy(() => import('./openapi/generate').then(m => m.command)));
|
||||
}
|
||||
|
||||
export function registerMigrateCommand(program: Command) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { paths } from '../../lib/paths';
|
||||
import YAML from 'js-yaml';
|
||||
|
||||
export async function command() {
|
||||
const openapiPath = paths.resolveTarget('openapi.yaml');
|
||||
if (!(await fs.pathExists(openapiPath))) {
|
||||
console.warn('Could not find openapi.yaml in root of directory.');
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
const yaml = YAML.load(
|
||||
await fs.readFile(paths.resolveTarget('openapi.yaml'), 'utf8'),
|
||||
);
|
||||
|
||||
// For now, we're not adding a header or linting after pasting.
|
||||
await fs.writeFile(
|
||||
paths.resolveTarget('schema/openapi.ts'),
|
||||
`export default ${JSON.stringify(yaml, null, 2)} as const`,
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { paths } from '../../lib/paths';
|
||||
import YAML from 'js-yaml';
|
||||
import { isEqual } from 'lodash';
|
||||
import { join } from 'path';
|
||||
import chalk from 'chalk';
|
||||
import { relative as relativePath } from 'path';
|
||||
import { PackageGraph } from '../../lib/monorepo';
|
||||
|
||||
async function verify(directoryPath: string) {
|
||||
const openapiPath = join(directoryPath, 'openapi.yaml');
|
||||
if (!(await fs.pathExists(openapiPath))) {
|
||||
return;
|
||||
}
|
||||
const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8'));
|
||||
|
||||
const schemaPath = join(directoryPath, 'schema/openapi.ts');
|
||||
if (!(await fs.pathExists(schemaPath))) {
|
||||
throw new Error('No `schema/openapi.ts` file found.');
|
||||
}
|
||||
const schema = await import(join(directoryPath, 'schema/openapi'));
|
||||
if (schema.default) {
|
||||
if (!isEqual(schema.default, yaml)) {
|
||||
throw new Error(
|
||||
'`openapi.yaml` and `schema/openapi.ts` do not match. Please run `yarn build:openapi` to generate the `schema/openapi.ts` file from the `openapi.yaml` file.',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error('`schemas/openapi.ts` needs to have a default export.');
|
||||
}
|
||||
}
|
||||
|
||||
export async function command() {
|
||||
try {
|
||||
await verify(paths.resolveTarget('.'));
|
||||
} catch (err) {
|
||||
console.error(chalk.red(err.message));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export async function bulkCommand(): Promise<void> {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
|
||||
const resultsList = await Promise.all(
|
||||
packages.map(async pkg => {
|
||||
let resultText = '';
|
||||
try {
|
||||
await verify(pkg.dir);
|
||||
} catch (err) {
|
||||
resultText = err.message;
|
||||
}
|
||||
|
||||
return {
|
||||
relativeDir: relativePath(paths.targetRoot, pkg.dir),
|
||||
resultText,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
let failed = false;
|
||||
for (const { relativeDir, resultText } of resultsList) {
|
||||
if (resultText) {
|
||||
console.log();
|
||||
console.log(chalk.red(`Lint failed in ${relativeDir}:`));
|
||||
console.log(resultText.trimStart());
|
||||
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ import {
|
||||
validateRequestBody,
|
||||
} from './util';
|
||||
import { ApiRouter } from '@backstage/backend-openapi-utils';
|
||||
import spec from './schema/openapi';
|
||||
import spec from '../../schema/openapi';
|
||||
|
||||
/**
|
||||
* Options used by {@link createRouter}.
|
||||
|
||||
Reference in New Issue
Block a user