From 9076fab7036d4af7c12034e5c7f6f3cda5496b82 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Thu, 16 Feb 2023 17:31:38 -0500 Subject: [PATCH] Small type fix. Signed-off-by: Aramis Sennyey --- plugins/catalog-backend/openapi.yaml | 118 +++++++++++++++--- .../src/service/createRouter.ts | 8 +- .../src/service/schema/openapi.ts | 102 +++++++++++++-- plugins/openapi-router-common/src/router.ts | 20 ++- 4 files changed, 215 insertions(+), 33 deletions(-) diff --git a/plugins/catalog-backend/openapi.yaml b/plugins/catalog-backend/openapi.yaml index ea09f5da10..037b4633b0 100644 --- a/plugins/catalog-backend/openapi.yaml +++ b/plugins/catalog-backend/openapi.yaml @@ -207,19 +207,24 @@ components: that are null signify that no entity existed with that ref. required: - items - EntityFacets: + EntityFacet: type: object properties: value: type: string count: type: number - description: Construct a type with a set of properties K of type T + EntityFacets: + type: array + items: + $ref: '#/components/schemas/EntityFacet' EntityFacetsResponse: type: object - properties: {} - additionalProperties: - $ref: '#/components/schemas/EntityFacets' + properties: + facets: + type: object + additionalProperties: + $ref: '#/components/schemas/EntityFacets' required: - facets Location: @@ -270,6 +275,91 @@ components: read and emitted like this so that the frontend can inform the user that it located them and can make sure to register them as well if they weren't already + RecursivePartialEntityRelation: + type: object + properties: + targetRef: + type: string + description: The entity ref of the target of this relation. + type: + type: string + description: The type of the relation. + description: A relation of a specific type to another entity in the catalog. + RecursivePartialEntityMeta: + allOf: + - $ref: '#/components/schemas/JsonObject' + - type: object + properties: + properties: + links: + type: array + items: + $ref: '#/components/schemas/EntityLink' + description: A list of external hyperlinks related to the entity. + tags: + type: array + items: + type: string + description: |- + A list of single-valued strings, to for example classify catalog entities in + various ways. + annotations: + $ref: '#/components/schemas/MapStringString' + labels: + $ref: '#/components/schemas/MapStringString' + description: + type: string + description: |- + A short (typically relatively few words, on one line) description of the + entity. + title: + type: string + description: |- + A display name of the entity, to be presented in user interfaces instead + of the `name` property above, when available. + This field is sometimes useful when the `name` is cumbersome or ends up + being perceived as overly technical. The title generally does not have + as stringent format requirements on it, so it may contain special + characters and be more explanatory. Do keep it very short though, and + avoid situations where a title can be confused with the name of another + entity, or where two entities share a title. + Note that this is only for display purposes, and may be ignored by some + parts of the code. Entity references still always make use of the `name` + property, not the title. + namespace: + type: string + description: The namespace that the entity belongs to. + name: + type: string + description: |- + The name of the entity. + Must be unique within the catalog at any given point in time, for any + given namespace + kind pair. This value is part of the technical + identifier of the entity, and as such it will appear in URLs, database + tables, entity references, and similar. It is subject to restrictions + regarding what characters are allowed. + If you want to use a different, more human readable string with fewer + restrictions on it in user interfaces, see the `title` field below. + etag: + type: string + description: |- + An opaque string that changes for each update operation to any part of + the entity, including metadata. + This field can not be set by the user at creation time, and the server + will reject an attempt to do so. The field will be populated in read + operations. The field can (optionally) be specified when performing + update or delete operations, and the server will then reject the + operation if it does not match the current stored value. + uid: + type: string + description: |- + A globally unique ID for the entity. + This field can not be set by the user at creation time, and the server + will reject an attempt to do so. The field will be populated in read + operations. The field can (optionally) be specified when performing + update or delete operations, but the server is free to reject requests + that do so in such a way that it breaks semantics. + description: Metadata fields common to all versions/kinds of entity. RecursivePartial_Entity_: type: object properties: @@ -282,13 +372,13 @@ components: type: string description: The high level entity type being described. metadata: - $ref: '#/components/schemas/EntityMeta' + $ref: '#/components/schemas/RecursivePartialEntityMeta' spec: $ref: '#/components/schemas/JsonObject' relations: type: array items: - $ref: '#/components/schemas/EntityRelation' + $ref: '#/components/schemas/RecursivePartialEntityRelation' description: The relations that this entity has with other entities. description: Makes all keys of an entire hierarchy optional. AnalyzeLocationEntityField: @@ -298,7 +388,7 @@ components: type: string description: |- A text to show to the user to inform about the choices made. Like, it could say - "Found a CODEOWNERS file that covers this target, so we suggest leaving this + "Found a CODEOWNERS file that covers this target, so we suggest leaving this field empty; which would currently make it owned by X" where X is taken from the codeowners file. value: @@ -395,7 +485,6 @@ components: type: http scheme: bearer bearerFormat: JWT - paths: /refresh: post: @@ -404,7 +493,6 @@ paths: '200': description: Refreshed security: - # From https://stackoverflow.com/questions/47659324/how-to-specify-an-endpoints-authorization-is-optional-in-openapi-v3 - {} - JWT: [] parameters: [] @@ -414,7 +502,6 @@ paths: application/json: schema: $ref: '#/components/schemas/RefreshOptions' - /entities: get: operationId: GetEntities @@ -456,7 +543,6 @@ paths: required: false schema: type: string - /entities/by-uid/{uid}: get: operationId: GetEntityByUid @@ -490,7 +576,6 @@ paths: required: true schema: type: string - /entities/by-name/{kind}/{namespace}/{name}: get: operationId: GetEntityByName @@ -520,7 +605,6 @@ paths: required: true schema: type: string - /entities/by-name/{kind}/{namespace}/{name}/ancestry: get: operationId: GetEntityAncestryByName @@ -550,7 +634,6 @@ paths: required: true schema: type: string - /entities/by-refs: post: operationId: GetEntitiesByRefs @@ -578,7 +661,6 @@ paths: type: array items: type: string - /entity-facets: get: operationId: GetEntityFacets @@ -603,7 +685,6 @@ paths: required: false schema: type: string - /locations: post: operationId: CreateLocation @@ -675,7 +756,6 @@ paths: - {} - JWT: [] parameters: [] - /locations/{id}: get: operationId: GetLocation @@ -709,7 +789,6 @@ paths: required: true schema: type: string - /analyze-location: post: operationId: AnalyzeLocation @@ -738,7 +817,6 @@ paths: required: - catalogFileName - location - /validate-entity: post: operationId: ValidateEntity diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 19535bfec7..8b912ae6f1 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -189,7 +189,9 @@ export async function createRouter( fields: parseEntityTransformParams(req.query, request.fields), authorizationToken: token, }); - res.status(200).json(response); + // These responses are interacting weirdly with the underlying interface passed to them, + // we just need to re-load the TS server's short term memory. + res.status(200).json({ ...response }); }) .get('/entity-facets', async (req, res) => { const response = await entitiesCatalog.facets({ @@ -197,7 +199,9 @@ export async function createRouter( facets: parseEntityFacetParams(req.query), authorizationToken: getBearerToken(req.header('authorization')), }); - res.status(200).json(response); + // These responses are interacting weirdly with the underlying interface passed to them, + // we just need to re-load the TS server's short term memory. + res.status(200).json({ ...response }); }); } diff --git a/plugins/catalog-backend/src/service/schema/openapi.ts b/plugins/catalog-backend/src/service/schema/openapi.ts index a8f78a36ec..81e585ee01 100644 --- a/plugins/catalog-backend/src/service/schema/openapi.ts +++ b/plugins/catalog-backend/src/service/schema/openapi.ts @@ -250,7 +250,7 @@ export default { }, required: ['items'], }, - EntityFacets: { + EntityFacet: { type: 'object', properties: { value: { @@ -260,13 +260,22 @@ export default { type: 'number', }, }, - description: 'Construct a type with a set of properties K of type T', + }, + EntityFacets: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityFacet', + }, }, EntityFacetsResponse: { type: 'object', - properties: {}, - additionalProperties: { - $ref: '#/components/schemas/EntityFacets', + properties: { + facets: { + type: 'object', + additionalProperties: { + $ref: '#/components/schemas/EntityFacets', + }, + }, }, required: ['facets'], }, @@ -320,6 +329,85 @@ export default { description: "If the folder pointed to already contained catalog info yaml files, they are\nread and emitted like this so that the frontend can inform the user that it\nlocated them and can make sure to register them as well if they weren't\nalready", }, + RecursivePartialEntityRelation: { + type: 'object', + properties: { + targetRef: { + type: 'string', + description: 'The entity ref of the target of this relation.', + }, + type: { + type: 'string', + description: 'The type of the relation.', + }, + }, + description: + 'A relation of a specific type to another entity in the catalog.', + }, + RecursivePartialEntityMeta: { + allOf: [ + { + $ref: '#/components/schemas/JsonObject', + }, + { + type: 'object', + properties: { + links: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityLink', + }, + description: + 'A list of external hyperlinks related to the entity.', + }, + tags: { + type: 'array', + items: { + type: 'string', + }, + description: + 'A list of single-valued strings, to for example classify catalog entities in\nvarious ways.', + }, + annotations: { + $ref: '#/components/schemas/MapStringString', + }, + labels: { + $ref: '#/components/schemas/MapStringString', + }, + description: { + type: 'string', + description: + 'A short (typically relatively few words, on one line) description of the\nentity.', + }, + title: { + type: 'string', + description: + 'A display name of the entity, to be presented in user interfaces instead\nof the `name` property above, when available.\nThis field is sometimes useful when the `name` is cumbersome or ends up\nbeing perceived as overly technical. The title generally does not have\nas stringent format requirements on it, so it may contain special\ncharacters and be more explanatory. Do keep it very short though, and\navoid situations where a title can be confused with the name of another\nentity, or where two entities share a title.\nNote that this is only for display purposes, and may be ignored by some\nparts of the code. Entity references still always make use of the `name`\nproperty, not the title.', + }, + namespace: { + type: 'string', + description: 'The namespace that the entity belongs to.', + }, + name: { + type: 'string', + description: + 'The name of the entity.\nMust be unique within the catalog at any given point in time, for any\ngiven namespace + kind pair. This value is part of the technical\nidentifier of the entity, and as such it will appear in URLs, database\ntables, entity references, and similar. It is subject to restrictions\nregarding what characters are allowed.\nIf you want to use a different, more human readable string with fewer\nrestrictions on it in user interfaces, see the `title` field below.', + }, + etag: { + type: 'string', + description: + 'An opaque string that changes for each update operation to any part of\nthe entity, including metadata.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, and the server will then reject the\noperation if it does not match the current stored value.', + }, + uid: { + type: 'string', + description: + 'A globally unique ID for the entity.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, but the server is free to reject requests\nthat do so in such a way that it breaks semantics.', + }, + }, + }, + ], + description: 'Metadata fields common to all versions/kinds of entity.', + }, RecursivePartial_Entity_: { type: 'object', properties: { @@ -333,7 +421,7 @@ export default { description: 'The high level entity type being described.', }, metadata: { - $ref: '#/components/schemas/EntityMeta', + $ref: '#/components/schemas/RecursivePartialEntityMeta', }, spec: { $ref: '#/components/schemas/JsonObject', @@ -341,7 +429,7 @@ export default { relations: { type: 'array', items: { - $ref: '#/components/schemas/EntityRelation', + $ref: '#/components/schemas/RecursivePartialEntityRelation', }, description: 'The relations that this entity has with other entities.', diff --git a/plugins/openapi-router-common/src/router.ts b/plugins/openapi-router-common/src/router.ts index 8703c691c5..3e0a08ffd6 100644 --- a/plugins/openapi-router-common/src/router.ts +++ b/plugins/openapi-router-common/src/router.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; -import { Router } from 'express'; +import { ErrorRequestHandler, RequestHandler, Router } from 'express'; import core, { ParamsDictionary } from 'express-serve-static-core'; import { FromSchema, JSONSchema7 } from 'json-schema-to-ts'; import { @@ -283,6 +283,17 @@ type DocRequestHandler< Record >; +export type RequestHandlerParams< + P = ParamsDictionary, + ResBody = any, + ReqBody = any, + ReqQuery = ParsedQs, + LocalsObj extends Record = Record, +> = + | RequestHandler + | ErrorRequestHandler + | Array | ErrorRequestHandler

>; + export class ApiRouter { private _router = Router(); @@ -298,7 +309,7 @@ export class ApiRouter { path: Path, ...handlers: DocRequestHandler[] ) { - console.log(path); + console.log(path, this.spec); this._router.get(path, ...handlers); return this; } @@ -361,8 +372,8 @@ export class ApiRouter { return this; } - use(...handlers: core.RequestHandler[]) { - return this._router.use(handlers); + use(...handlers: RequestHandlerParams[]) { + return this._router.use(...handlers); } build() { @@ -373,6 +384,7 @@ export class ApiRouter { interface RouterOptions {} export async function createRouter(options: RouterOptions) { + console.log(options); const router = ApiRouter.fromSpec>( // As const forces the doc to readonly which conflicts with imported types. doc as DeepWriteable,