From be1db622eb97a4dbed9c96947cd6adc2b4bfca2b Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sun, 3 Mar 2024 11:10:36 -0500 Subject: [PATCH] fix templates Signed-off-by: aramissennyeydd Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: aramissennyeydd --- .../package/schema/openapi/generate/server.ts | 100 ++-- .../repo-tools/src/lib/openapi/constants.ts | 1 + .../typescript-backstage.client.yaml | 20 +- .../typescript-backstage.server.yaml | 2 +- .../{client => }/index.mustache | 0 .../typescript-backstage/server/api.mustache | 4 +- .../src/generated/.openapi-generator-ignore | 2 + .../src/generated/.openapi-generator/FILES | 2 +- .../src/generated/apis/DefaultApi.client.ts | 463 ++++++++++-------- .../src/generated/apis/DefaultApi.server.ts | 206 ++++++++ .../src/generated/apis/router.ts | 207 ++++++++ 11 files changed, 753 insertions(+), 254 deletions(-) rename packages/repo-tools/templates/typescript-backstage/{client => }/index.mustache (100%) create mode 100644 plugins/catalog-backend/src/generated/apis/DefaultApi.server.ts create mode 100644 plugins/catalog-backend/src/generated/apis/router.ts diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts index 1ef6fd10d2..eb0a043b9b 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts @@ -14,51 +14,85 @@ * limitations under the License. */ -import fs from 'fs-extra'; -import YAML from 'js-yaml'; import chalk from 'chalk'; +import { resolve } from 'path'; +import { + OPENAPI_IGNORE_FILES, + OUTPUT_PATH, +} from '../../../../../lib/openapi/constants'; import { paths as cliPaths } from '../../../../../lib/paths'; -import { TS_SCHEMA_PATH } from '../../../../../lib/openapi/constants'; -import { promisify } from 'util'; -import { exec as execCb } from 'child_process'; -import { getPathToCurrentOpenApiSpec } from '../../../../../lib/openapi/helpers'; - -const exec = promisify(execCb); +import { mkdirpSync } from 'fs-extra'; +import fs from 'fs-extra'; +import { exec } from '../../../../../lib/exec'; +import { resolvePackagePath } from '@backstage/backend-common'; +import { + getPathToCurrentOpenApiSpec, + getRelativePathToFile, +} from '../../../../../lib/openapi/helpers'; async function generate(abortSignal?: AbortController) { - const openapiPath = await getPathToCurrentOpenApiSpec(); - const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); + const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); + const resolvedOutputDirectory = await getRelativePathToFile(OUTPUT_PATH); + mkdirpSync(resolvedOutputDirectory); - const tsPath = cliPaths.resolveTarget(TS_SCHEMA_PATH); + await fs.mkdirp(resolvedOutputDirectory); - // The first set of comment slashes allow for the eslint notice plugin to run - // with onNonMatchingHeader: 'replace', as is the case in the open source - // Backstage repo. Otherwise the auto-generated comment will be removed by the - // lint call below. await fs.writeFile( - tsPath, - `// - -// ****************************************************************** -// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** -import {createValidatedOpenApiRouter} from '@backstage/backend-openapi-utils'; -export const spec = ${JSON.stringify(yaml, null, 2)} as const; -export const createOpenApiRouter = async ( - options?: Parameters['1'], -) => createValidatedOpenApiRouter(spec, options); -`, + resolve(resolvedOutputDirectory, '.openapi-generator-ignore'), + OPENAPI_IGNORE_FILES.join('\n'), ); - await exec(`yarn backstage-cli package lint --fix ${tsPath}`, { - signal: abortSignal?.signal, - }); - if (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { - await exec(`yarn prettier --write ${tsPath}`, { - cwd: cliPaths.targetRoot, + await exec( + 'node', + [ + resolvePackagePath('@openapitools/openapi-generator-cli', 'main.js'), + 'generate', + '-i', + resolvedOpenapiPath, + '-o', + resolvedOutputDirectory, + '-g', + 'typescript', + '-c', + resolvePackagePath( + '@backstage/repo-tools', + 'templates/typescript-backstage.server.yaml', + ), + `--additional-properties=clientPackageName=@backstage/catalog-client`, + '--generator-key', + 'v3.0', + ], + { + maxBuffer: Number.MAX_VALUE, + cwd: resolvePackagePath('@backstage/repo-tools'), + env: { + ...process.env, + }, + signal: abortSignal?.signal, + }, + ); + + await exec( + `yarn backstage-cli package lint --fix ${resolvedOutputDirectory}`, + [], + { + signal: abortSignal?.signal, + }, + ); + + const prettier = cliPaths.resolveTargetRoot('node_modules/.bin/prettier'); + if (prettier) { + await exec(`${prettier} --write ${resolvedOutputDirectory}`, [], { signal: abortSignal?.signal, }); } + + fs.removeSync(resolve(resolvedOutputDirectory, '.openapi-generator-ignore')); + + fs.rmSync(resolve(resolvedOutputDirectory, '.openapi-generator'), { + recursive: true, + force: true, + }); } export async function command({ diff --git a/packages/repo-tools/src/lib/openapi/constants.ts b/packages/repo-tools/src/lib/openapi/constants.ts index 4936a5aee7..9a54a9a0ff 100644 --- a/packages/repo-tools/src/lib/openapi/constants.ts +++ b/packages/repo-tools/src/lib/openapi/constants.ts @@ -45,6 +45,7 @@ export const OPENAPI_IGNORE_FILES = [ // Override the created version. 'apis/*.ts', '!apis/*.client.ts', + '!apis/*.server.ts', 'models/*.ts', '!models/*.model.ts', diff --git a/packages/repo-tools/templates/typescript-backstage.client.yaml b/packages/repo-tools/templates/typescript-backstage.client.yaml index 12f001654d..e9f25c18ec 100644 --- a/packages/repo-tools/templates/typescript-backstage.client.yaml +++ b/packages/repo-tools/templates/typescript-backstage.client.yaml @@ -8,25 +8,27 @@ files: client/model.mustache: templateType: Model destinationFilename: .model.ts - client/models/modelGeneric.mustache: + modelGeneric.mustache: templateType: SupportingFiles - client/models/modelOneOf.mustache: + modelOneOf.mustache: templateType: SupportingFiles - client/models/modelGenericAdditionalProperties.mustache: + modelGenericAdditionalProperties.mustache: templateType: SupportingFiles - client/models/ modelGenericEnums.mustache: + modelGenericEnums.mustache: templateType: SupportingFiles - client/models/modelAlias.mustache: + modelAlias.mustache: templateType: SupportingFiles - client/models/modelEnum.mustache: + modelEnum.mustache: templateType: SupportingFiles - client/models/modelTaggedUnion.mustache: + modelTaggedUnion.mustache: templateType: SupportingFiles client/models/models_all.mustache: templateType: SupportingFiles destinationFilename: models/index.ts - client/types/fetch.ts: {} - client/types/discovery.ts: {} + client/types/fetch.ts: + destinationFilename: types/fetch.ts + client/types/discovery.ts: + destinationFilename: types/discovery.ts client/apis/index.mustache: templateType: SupportingFiles destinationFilename: apis/index.ts diff --git a/packages/repo-tools/templates/typescript-backstage.server.yaml b/packages/repo-tools/templates/typescript-backstage.server.yaml index 546c3c181e..06bb3ebdc0 100644 --- a/packages/repo-tools/templates/typescript-backstage.server.yaml +++ b/packages/repo-tools/templates/typescript-backstage.server.yaml @@ -4,7 +4,7 @@ files: server/api.mustache: templateType: API # For some reason, they check for destinationFilename differences. We have to change the ending to override the file. - destinationFilename: .client.ts + destinationFilename: .server.ts client/apis/index.mustache: templateType: SupportingFiles destinationFilename: apis/index.ts diff --git a/packages/repo-tools/templates/typescript-backstage/client/index.mustache b/packages/repo-tools/templates/typescript-backstage/index.mustache similarity index 100% rename from packages/repo-tools/templates/typescript-backstage/client/index.mustache rename to packages/repo-tools/templates/typescript-backstage/index.mustache diff --git a/packages/repo-tools/templates/typescript-backstage/server/api.mustache b/packages/repo-tools/templates/typescript-backstage/server/api.mustache index 7e1c82621c..7b64c49b5c 100644 --- a/packages/repo-tools/templates/typescript-backstage/server/api.mustache +++ b/packages/repo-tools/templates/typescript-backstage/server/api.mustache @@ -22,7 +22,8 @@ type ExtendsString = Path; type InputOutput = { {{#operation}} - '#{{httpMethod}}|{{path}}': { + '{{path}}': { + '{{httpMethod}}': { path: { {{#pathParams}} {{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, @@ -37,6 +38,7 @@ type ExtendsString = Path; body: {{{dataType}}}, {{/bodyParam}} response: {{{returnType}}}{{^returnType}}void{{/returnType}}, + } }, {{/operation}} diff --git a/plugins/catalog-backend/src/generated/.openapi-generator-ignore b/plugins/catalog-backend/src/generated/.openapi-generator-ignore index 1b59086ab7..0561af9e08 100644 --- a/plugins/catalog-backend/src/generated/.openapi-generator-ignore +++ b/plugins/catalog-backend/src/generated/.openapi-generator-ignore @@ -1,4 +1,5 @@ *.md +*.mustache apis/baseapi.ts apis/exception.ts auth/* @@ -11,6 +12,7 @@ rxjsStub.ts .gitignore apis/*.ts !apis/*.client.ts +!apis/*.server.ts models/*.ts !models/*.model.ts !index.ts diff --git a/plugins/catalog-backend/src/generated/.openapi-generator/FILES b/plugins/catalog-backend/src/generated/.openapi-generator/FILES index d8308a8aa6..fa74778d78 100644 --- a/plugins/catalog-backend/src/generated/.openapi-generator/FILES +++ b/plugins/catalog-backend/src/generated/.openapi-generator/FILES @@ -1,3 +1,3 @@ -apis/DefaultApi.client.ts +apis/DefaultApi.server.ts apis/index.ts index.ts diff --git a/plugins/catalog-backend/src/generated/apis/DefaultApi.client.ts b/plugins/catalog-backend/src/generated/apis/DefaultApi.client.ts index 43fff31806..7d7fa2ecd3 100644 --- a/plugins/catalog-backend/src/generated/apis/DefaultApi.client.ts +++ b/plugins/catalog-backend/src/generated/apis/DefaultApi.client.ts @@ -16,7 +16,6 @@ import type core from 'express-serve-static-core'; - import type { AnalyzeLocationRequest, AnalyzeLocationResponse, @@ -35,191 +34,212 @@ import type { } from '@backstage/catalog-client'; import { Router } from 'express'; - /** * no description */ - type InputOutput = { - - '#POST|/analyze-location': { - path: { - }, - query: { - }, - body: AnalyzeLocationRequest, - response: AnalyzeLocationResponse, - }, - - - '#POST|/locations': { - path: { - }, - query: { - dryRun?: string, - }, - body: CreateLocationRequest, - response: CreateLocation201Response, - }, - - - '#DELETE|/entities/by-uid/{uid}': { - path: { - uid: string, - }, - query: { - }, - response: void, - }, - - - '#DELETE|/locations/{id}': { - path: { - id: string, - }, - query: { - }, - response: void, - }, - - - '#GET|/entities': { - path: { - }, - query: { - fields?: Array, - limit?: number, - filter?: Array, - offset?: number, - after?: string, - order?: Array, - }, - response: Array, - }, - - - '#GET|/entities/by-query': { - path: { - }, - query: { - fields?: Array, - limit?: number, - orderField?: Array, - cursor?: string, - filter?: Array, - fullTextFilterTerm?: string, - fullTextFilterFields?: Array, - }, - response: EntitiesQueryResponse, - }, - - - '#POST|/entities/by-refs': { - path: { - }, - query: { - }, - body: GetEntitiesByRefsRequest, - response: EntitiesBatchResponse, - }, - - - '#GET|/entities/by-name/{kind}/{namespace}/{name}/ancestry': { - path: { - kind: string, - namespace: string, - name: string, - }, - query: { - }, - response: EntityAncestryResponse, - }, - - - '#GET|/entities/by-name/{kind}/{namespace}/{name}': { - path: { - kind: string, - namespace: string, - name: string, - }, - query: { - }, - response: Entity, - }, - - - '#GET|/entities/by-uid/{uid}': { - path: { - uid: string, - }, - query: { - }, - response: Entity, - }, - - - '#GET|/entity-facets': { - path: { - }, - query: { - facet: Array, - filter?: Array, - }, - response: EntityFacetsResponse, - }, - - - '#GET|/locations/{id}': { - path: { - id: string, - }, - query: { - }, - response: Location, - }, - - - '#GET|/locations': { - path: { - }, - query: { - }, - response: Array, - }, - - - '#POST|/refresh': { - path: { - }, - query: { - }, - body: RefreshEntityRequest, - response: void, - }, - - - '#POST|/validate-entity': { - path: { - }, - query: { - }, - body: ValidateEntityRequest, - response: void, - }, - +type InputOutput = { + '#POST|/analyze-location': { + path: {}; + query: {}; + body: AnalyzeLocationRequest; + response: AnalyzeLocationResponse; + }; + + '#POST|/locations': { + path: {}; + query: { + dryRun?: string; }; + body: CreateLocationRequest; + response: CreateLocation201Response; + }; -type ValueOf = T[keyof T]; + '#DELETE|/entities/by-uid/{uid}': { + path: { + uid: string; + }; + query: {}; + response: void; + }; -type Filter = T extends K ? K : never; + '#DELETE|/locations/{id}': { + path: { + id: string; + }; + query: {}; + response: void; + }; -type ExtractDocPath = Path extends `#${string}|${infer OpenApiPath}` ? OpenApiPath : never; + '#GET|/entities': { + path: {}; + query: { + fields?: Array; + limit?: number; + filter?: Array; + offset?: number; + after?: string; + order?: Array; + }; + response: Array; + }; -type ExtractDocMethod = Path extends `#${infer Method}|${string}` ? Method : never + '#GET|/entities/by-query': { + path: {}; + query: { + fields?: Array; + limit?: number; + orderField?: Array; + cursor?: string; + filter?: Array; + fullTextFilterTerm?: string; + fullTextFilterFields?: Array; + }; + response: EntitiesQueryResponse; + }; -type DocPath = ExtractDocPath>; + '#POST|/entities/by-refs': { + path: {}; + query: {}; + body: GetEntitiesByRefsRequest; + response: EntitiesBatchResponse; + }; -type DocPathMethod = ExtractDocMethod; + '#GET|/entities/by-name/{kind}/{namespace}/{name}/ancestry': { + path: { + kind: string; + namespace: string; + name: string; + }; + query: {}; + response: EntityAncestryResponse; + }; + + '#GET|/entities/by-name/{kind}/{namespace}/{name}': { + path: { + kind: string; + namespace: string; + name: string; + }; + query: {}; + response: Entity; + }; + + '#GET|/entities/by-uid/{uid}': { + path: { + uid: string; + }; + query: {}; + response: Entity; + }; + + '#GET|/entity-facets': { + path: {}; + query: { + facet: Array; + filter?: Array; + }; + response: EntityFacetsResponse; + }; + + '#GET|/locations/{id}': { + path: { + id: string; + }; + query: {}; + response: Location; + }; + + '/locations': { + get: { + path: {}; + query: {}; + response: Array; + }; + }; + + '#POST|/refresh': { + path: {}; + query: {}; + body: RefreshEntityRequest; + response: void; + }; + + '#POST|/validate-entity': { + path: {}; + query: {}; + body: ValidateEntityRequest; + response: void; + }; +}; + +type PathTemplate = + Path extends `${infer Prefix}{${infer PathName}}${infer Suffix}` + ? `${Prefix}:${PathName}${PathTemplate}` + : Path; + +type HttpMethods = Uppercase<'all' | 'put' | 'get' | 'post' | 'delete'>; + +type ValueOf = T[keyof T]; + +/** + * @public + */ +export type Filter = T extends U ? T : never; + +type DocPath = ValueOf<{ + [Template in keyof Doc]: Template extends `#${string}|${infer Path}` + ? Path + : never; +}>; + +type DocPathMethod< + Doc extends InputOutput, + Path extends DocPath, +> = ValueOf<{ + [Template in keyof Doc]: Template extends `#${infer Method}|${Path}` + ? Method extends string + ? Method + : never + : never; +}>; + +type PathSchema< + Doc extends InputOutput, + Path extends DocPath, + Method extends DocPathMethod, +> = `#${Method}|${Path}` extends keyof Doc + ? 'path' extends keyof Doc[`#${Method}|${Path}`] + ? Doc[`#${Method}|${Path}`]['path'] + : never + : never; +type RequestBody< + Doc extends InputOutput, + Path extends DocPath, + Method extends DocPathMethod, +> = `#${Method}|${Path}` extends keyof Doc + ? 'body' extends keyof Doc[`#${Method}|${Path}`] + ? Doc[`#${Method}|${Path}`]['body'] + : any + : any; +type ResponseBody< + Doc extends InputOutput, + Path extends DocPath, + Method extends DocPathMethod, +> = `#${Method}|${Path}` extends keyof Doc + ? 'response' extends keyof Doc[`#${Method}|${Path}`] + ? Doc[`#${Method}|${Path}`]['response'] + : any + : any; +type QuerySchema< + Doc extends InputOutput, + Path extends DocPath, + Method extends DocPathMethod, +> = `#${Method}|${Path}` extends keyof Doc + ? 'query' extends keyof Doc[`#${Method}|${Path}`] + ? Doc[`#${Method}|${Path}`]['query'] + : never + : never; /** * Typed express request handler. @@ -231,8 +251,8 @@ export type DocRequestHandler< Method extends DocPathMethod, > = core.RequestHandler< PathSchema, - ResponseBodyToJsonSchema, - RequestBodyToJsonSchema, + ResponseBody, + RequestBody, QuerySchema, Record >; @@ -242,60 +262,85 @@ export type DocRequestHandler< * @public */ export type DocRequestHandlerParams< - Doc extends RequiredDoc, + Doc extends InputOutput, Path extends DocPath, Method extends DocPathMethod, > = core.RequestHandlerParams< PathSchema, - ResponseBodyToJsonSchema, - RequestBodyToJsonSchema, + ResponseBody, + RequestBody, QuerySchema, Record >; +/** + * @public + */ +export type MethodAwareDocPath< + Doc extends InputOutput, + Path extends DocPath, + Method extends DocPathMethod, +> = ValueOf<{ + [Template in keyof Doc]: Template extends `#${Method}|${Path}` ? Path : never; +}>; + /** * Superset of the express router path matcher that enforces typed request and response bodies. * @public */ export interface DocRequestMatcher< - Doc extends RequiredDoc, + Doc extends InputOutput, T, - Method extends - | 'all' - | 'get' - | 'post' - | 'put' - | 'delete' - | 'patch' - | 'options' - | 'head', + Method extends HttpMethods, > { < - Path extends MethodAwareDocPath< - Doc, - PathTemplate>, - Method - >, + TPath extends DocPath, + TMethod extends Filter, Method>, >( - path: Path, - ...handlers: Array< - DocRequestHandler, Method> - > + path: PathTemplate>, + ...handlers: Array> ): T; < - Path extends MethodAwareDocPath< - Doc, - PathTemplate>, - Method - >, + TPath extends DocPath, + TMethod extends Filter, Method>, >( - path: Path, - ...handlers: Array< - DocRequestHandlerParams, Method> - > + path: PathTemplate>, + ...handlers: Array> ): T; } export interface TypedRouter extends Router { - get: + get: DocRequestMatcher; + post: DocRequestMatcher; + put: DocRequestMatcher; + delete: DocRequestMatcher; } + +type method = DocRequestMatcher; + +type test2 = keyof method; + +type paths = PathTemplate>; +type test3 = PathSchema< + InputOutput, + '/entities/by-name/{kind}/{namespace}/{name}', + 'GET' +>; + +type test4 = ResponseBody< + InputOutput, + '/entities/by-name/{kind}/{namespace}/{name}', + 'GET' +>; +type Method = DocPath; +type why = DocPathMethod< + InputOutput, + '/entities/by-name/{kind}/{namespace}/{name}' +>; + +type test5 = MethodAwareDocPath; + +const router: TypedRouter = Router() as TypedRouter; +router.post('/entities', (req, res) => { + res.json([{}]); +}); diff --git a/plugins/catalog-backend/src/generated/apis/DefaultApi.server.ts b/plugins/catalog-backend/src/generated/apis/DefaultApi.server.ts new file mode 100644 index 0000000000..096a4aac70 --- /dev/null +++ b/plugins/catalog-backend/src/generated/apis/DefaultApi.server.ts @@ -0,0 +1,206 @@ +/* + * Copyright 2024 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. + */ + +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** + +import type { + AnalyzeLocationRequest, + AnalyzeLocationResponse, + CreateLocation201Response, + CreateLocationRequest, + EntitiesBatchResponse, + EntitiesQueryResponse, + Entity, + EntityAncestryResponse, + EntityFacetsResponse, + GetEntitiesByRefsRequest, + GetLocations200ResponseInner, + Location, + RefreshEntityRequest, + ValidateEntityRequest, +} from '@backstage/catalog-client'; + +/** + * no description + */ + +type InputOutput = { + '/analyze-location': { + POST: { + path: {}; + query: {}; + body: AnalyzeLocationRequest; + response: AnalyzeLocationResponse; + }; + }; + + '/locations': { + POST: { + path: {}; + query: { + dryRun?: string; + }; + body: CreateLocationRequest; + response: CreateLocation201Response; + }; + DELETE: { + path: { + id: string; + }; + query: {}; + response: void; + }; + GET: { + path: {}; + query: {}; + response: Array; + }; + }; + + '/entities/by-uid/{uid}': { + DELETE: { + path: { + uid: string; + }; + query: {}; + response: void; + }; + GET: { + path: { + uid: string; + }; + query: {}; + response: Entity; + }; + }; + + '/entities': { + GET: { + path: {}; + query: { + fields?: Array; + limit?: number; + filter?: Array; + offset?: number; + after?: string; + order?: Array; + }; + response: Array; + }; + }; + + '/entities/by-query': { + GET: { + path: {}; + query: { + fields?: Array; + limit?: number; + orderField?: Array; + cursor?: string; + filter?: Array; + fullTextFilterTerm?: string; + fullTextFilterFields?: Array; + }; + response: EntitiesQueryResponse; + }; + }; + + '/entities/by-refs': { + POST: { + path: {}; + query: {}; + body: GetEntitiesByRefsRequest; + response: EntitiesBatchResponse; + }; + }; + + '/entities/by-name/{kind}/{namespace}/{name}/ancestry': { + GET: { + path: { + kind: string; + namespace: string; + name: string; + }; + query: {}; + response: EntityAncestryResponse; + }; + }; + + '/entities/by-name/{kind}/{namespace}/{name}': { + GET: { + path: { + kind: string; + namespace: string; + name: string; + }; + query: {}; + response: Entity; + }; + }; + + '/entity-facets': { + GET: { + path: {}; + query: { + facet: Array; + filter?: Array; + }; + response: EntityFacetsResponse; + }; + }; + + '/locations/{id}': { + GET: { + path: { + id: string; + }; + query: {}; + response: Location; + }; + }; + + '/locations/by-entity/{kind}/{namespace}/{name}': { + GET: { + path: { + kind: string; + namespace: string; + name: string; + }; + query: {}; + response: Location; + }; + }; + + '/refresh': { + POST: { + path: {}; + query: {}; + body: RefreshEntityRequest; + response: void; + }; + }; + + '/validate-entity': { + POST: { + path: {}; + query: {}; + body: ValidateEntityRequest; + response: void; + }; + }; +}; diff --git a/plugins/catalog-backend/src/generated/apis/router.ts b/plugins/catalog-backend/src/generated/apis/router.ts new file mode 100644 index 0000000000..38020570db --- /dev/null +++ b/plugins/catalog-backend/src/generated/apis/router.ts @@ -0,0 +1,207 @@ +/* + * 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. + */ + +/** + * Pulled from https://github.com/varanauskas/oatx. + */ + +/** + * Get value types of `T`. + * @public + */ +export type ValueOf = T[keyof T]; + +/** + * All paths for a given doc, + * @example `/pet/{petId}` | `/pet` + * @public + */ +export type DocPath = Extract; + +/** + * Validate a string against OpenAPI path template, {@link https://spec.openapis.org/oas/v3.1.0#path-templating-matching}. + * + * @example + * ```ts + * const path: PathTemplate<"/posts/{postId}/comments/{commentId}"> = "/posts/:postId/comments/:commentId"; + * const pathWithoutParams: PathTemplate<"/posts/comments"> = "/posts/comments"; + * ``` + * + * @public + */ +export type PathTemplate = + Path extends `${infer Prefix}{${infer PathName}}${infer Suffix}` + ? `${Prefix}:${PathName}${PathTemplate}` + : Path; + +/** + * Extract path as specified in OpenAPI `Doc` based on request path + * @example + * ```ts + * const spec = { + * paths: { + * "/posts/{postId}/comments/{commentId}": {}, + * "/posts/comments": {}, + * } + * }; + * const specPathWithParams: DocPath = "/posts/{postId}/comments/{commentId}"; + * const specPathWithoutParams: DocPath = "/posts/comments"; + * ``` + * + * @public + */ +export type TemplateToDocPath< + Doc extends PathDoc, + Path extends DocPathTemplate, +> = ValueOf<{ + [Template in DocPath]: Path extends PathTemplate