Fix API definitions and update types.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
committed by
Fredrik Adelöw
parent
8eda7722a3
commit
4669b87364
@@ -6,6 +6,7 @@ info:
|
||||
description: The Backstage backend plugin that provides the Backstage catalog
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
contact: {}
|
||||
|
||||
servers:
|
||||
@@ -18,17 +19,6 @@ components:
|
||||
requestBodies: {}
|
||||
responses: {}
|
||||
schemas:
|
||||
RefreshOptions:
|
||||
type: object
|
||||
properties:
|
||||
authorizationToken:
|
||||
type: string
|
||||
entityRef:
|
||||
type: string
|
||||
description: The reference to a single entity that should be refreshed
|
||||
required:
|
||||
- entityRef
|
||||
description: Options for requesting a refresh of entities in the catalog.
|
||||
JsonObject:
|
||||
type: object
|
||||
properties: {}
|
||||
@@ -57,6 +47,7 @@ components:
|
||||
required:
|
||||
- url
|
||||
description: A link to external information that is related to the entity.
|
||||
additionalProperties: false
|
||||
EntityMeta:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/JsonObject'
|
||||
@@ -133,6 +124,7 @@ components:
|
||||
required:
|
||||
- name
|
||||
description: Metadata fields common to all versions/kinds of entity.
|
||||
additionalProperties: false
|
||||
EntityRelation:
|
||||
type: object
|
||||
properties:
|
||||
@@ -146,6 +138,7 @@ components:
|
||||
- targetRef
|
||||
- type
|
||||
description: A relation of a specific type to another entity in the catalog.
|
||||
additionalProperties: false
|
||||
Entity:
|
||||
type: object
|
||||
properties:
|
||||
@@ -171,6 +164,7 @@ components:
|
||||
- kind
|
||||
- apiVersion
|
||||
description: The parts of the format that's common to all versions/kinds of entity.
|
||||
additionalProperties: false
|
||||
EntityAncestryResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -193,6 +187,7 @@ components:
|
||||
required:
|
||||
- items
|
||||
- rootEntityRef
|
||||
additionalProperties: false
|
||||
EntitiesBatchResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -207,6 +202,7 @@ components:
|
||||
that are null signify that no entity existed with that ref.
|
||||
required:
|
||||
- items
|
||||
additionalProperties: false
|
||||
EntityFacet:
|
||||
type: object
|
||||
properties:
|
||||
@@ -214,6 +210,7 @@ components:
|
||||
type: string
|
||||
count:
|
||||
type: number
|
||||
additionalProperties: false
|
||||
EntityFacets:
|
||||
type: array
|
||||
items:
|
||||
@@ -227,6 +224,7 @@ components:
|
||||
$ref: '#/components/schemas/EntityFacets'
|
||||
required:
|
||||
- facets
|
||||
additionalProperties: false
|
||||
Location:
|
||||
type: object
|
||||
properties:
|
||||
@@ -241,6 +239,7 @@ components:
|
||||
- type
|
||||
- id
|
||||
description: Entity location for a specific entity.
|
||||
additionalProperties: false
|
||||
LocationSpec:
|
||||
type: object
|
||||
properties:
|
||||
@@ -257,6 +256,7 @@ components:
|
||||
- target
|
||||
- type
|
||||
description: Holds the entity location information.
|
||||
additionalProperties: false
|
||||
AnalyzeLocationExistingEntity:
|
||||
type: object
|
||||
properties:
|
||||
@@ -275,6 +275,7 @@ 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
|
||||
additionalProperties: false
|
||||
RecursivePartialEntityRelation:
|
||||
type: object
|
||||
properties:
|
||||
@@ -285,6 +286,7 @@ components:
|
||||
type: string
|
||||
description: The type of the relation.
|
||||
description: A relation of a specific type to another entity in the catalog.
|
||||
additionalProperties: false
|
||||
RecursivePartialEntityMeta:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/JsonObject'
|
||||
@@ -360,6 +362,7 @@ components:
|
||||
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.
|
||||
additionalProperties: false
|
||||
RecursivePartial_Entity_:
|
||||
type: object
|
||||
properties:
|
||||
@@ -381,6 +384,7 @@ components:
|
||||
$ref: '#/components/schemas/RecursivePartialEntityRelation'
|
||||
description: The relations that this entity has with other entities.
|
||||
description: Makes all keys of an entire hierarchy optional.
|
||||
additionalProperties: false
|
||||
AnalyzeLocationEntityField:
|
||||
type: object
|
||||
properties:
|
||||
@@ -411,6 +415,7 @@ components:
|
||||
- value
|
||||
- state
|
||||
- field
|
||||
additionalProperties: false
|
||||
AnalyzeLocationGenerateEntity:
|
||||
type: object
|
||||
properties:
|
||||
@@ -429,6 +434,7 @@ components:
|
||||
the frontend. It'll probably contain a (possibly incomplete) entity, plus
|
||||
enough info for the frontend to know what form data to show to the user
|
||||
for overriding/completing the info.
|
||||
additionalProperties: false
|
||||
AnalyzeLocationResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -443,6 +449,7 @@ components:
|
||||
required:
|
||||
- generateEntities
|
||||
- existingEntityFiles
|
||||
additionalProperties: false
|
||||
LocationInput:
|
||||
type: object
|
||||
properties:
|
||||
@@ -459,6 +466,7 @@ components:
|
||||
- type
|
||||
- target
|
||||
- presence
|
||||
additionalProperties: false
|
||||
SerializedError:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/JsonObject'
|
||||
@@ -480,13 +488,14 @@ components:
|
||||
- message
|
||||
- name
|
||||
description: The serialized form of an Error.
|
||||
additionalProperties: false
|
||||
EntitiesQueryResponse:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
- $ref: '#/components/schemas/Entity'
|
||||
$ref: '#/components/schemas/Entity'
|
||||
description: |-
|
||||
The list of entities paginated by a specific filter.
|
||||
totalItems:
|
||||
@@ -502,6 +511,7 @@ components:
|
||||
type: string
|
||||
description: |-
|
||||
Base64 encoded database query for the previous page.
|
||||
additionalProperties: false
|
||||
securitySchemes:
|
||||
JWT:
|
||||
type: http
|
||||
@@ -523,7 +533,17 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RefreshOptions'
|
||||
type: object
|
||||
properties:
|
||||
authorizationToken:
|
||||
type: string
|
||||
entityRef:
|
||||
type: string
|
||||
description: The reference to a single entity that should be refreshed
|
||||
required:
|
||||
- entityRef
|
||||
description: Options for requesting a refresh of entities in the catalog.
|
||||
additionalProperties: false
|
||||
/entities:
|
||||
get:
|
||||
operationId: GetEntities
|
||||
|
||||
@@ -22,6 +22,7 @@ export default {
|
||||
'The Backstage backend plugin that provides the Backstage catalog',
|
||||
license: {
|
||||
name: 'Apache-2.0',
|
||||
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
||||
},
|
||||
contact: {},
|
||||
},
|
||||
@@ -37,22 +38,6 @@ export default {
|
||||
requestBodies: {},
|
||||
responses: {},
|
||||
schemas: {
|
||||
RefreshOptions: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
authorizationToken: {
|
||||
type: 'string',
|
||||
},
|
||||
entityRef: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The reference to a single entity that should be refreshed',
|
||||
},
|
||||
},
|
||||
required: ['entityRef'],
|
||||
description:
|
||||
'Options for requesting a refresh of entities in the catalog.',
|
||||
},
|
||||
JsonObject: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
@@ -91,6 +76,7 @@ export default {
|
||||
required: ['url'],
|
||||
description:
|
||||
'A link to external information that is related to the entity.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntityMeta: {
|
||||
allOf: [
|
||||
@@ -156,6 +142,7 @@ export default {
|
||||
},
|
||||
],
|
||||
description: 'Metadata fields common to all versions/kinds of entity.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntityRelation: {
|
||||
type: 'object',
|
||||
@@ -172,6 +159,7 @@ export default {
|
||||
required: ['targetRef', 'type'],
|
||||
description:
|
||||
'A relation of a specific type to another entity in the catalog.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
Entity: {
|
||||
type: 'object',
|
||||
@@ -203,6 +191,7 @@ export default {
|
||||
required: ['metadata', 'kind', 'apiVersion'],
|
||||
description:
|
||||
"The parts of the format that's common to all versions/kinds of entity.",
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntityAncestryResponse: {
|
||||
type: 'object',
|
||||
@@ -230,6 +219,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['items', 'rootEntityRef'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntitiesBatchResponse: {
|
||||
type: 'object',
|
||||
@@ -249,6 +239,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['items'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntityFacet: {
|
||||
type: 'object',
|
||||
@@ -260,6 +251,7 @@ export default {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntityFacets: {
|
||||
type: 'array',
|
||||
@@ -278,6 +270,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['facets'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
Location: {
|
||||
type: 'object',
|
||||
@@ -294,6 +287,7 @@ export default {
|
||||
},
|
||||
required: ['target', 'type', 'id'],
|
||||
description: 'Entity location for a specific entity.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
LocationSpec: {
|
||||
type: 'object',
|
||||
@@ -311,6 +305,7 @@ export default {
|
||||
},
|
||||
required: ['target', 'type'],
|
||||
description: 'Holds the entity location information.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
AnalyzeLocationExistingEntity: {
|
||||
type: 'object',
|
||||
@@ -328,6 +323,7 @@ export default {
|
||||
required: ['entity', 'isRegistered', 'location'],
|
||||
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",
|
||||
additionalProperties: false,
|
||||
},
|
||||
RecursivePartialEntityRelation: {
|
||||
type: 'object',
|
||||
@@ -343,6 +339,7 @@ export default {
|
||||
},
|
||||
description:
|
||||
'A relation of a specific type to another entity in the catalog.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
RecursivePartialEntityMeta: {
|
||||
allOf: [
|
||||
@@ -409,6 +406,7 @@ export default {
|
||||
'Metadata fields common to all versions/kinds of entity.',
|
||||
},
|
||||
],
|
||||
additionalProperties: false,
|
||||
},
|
||||
RecursivePartial_Entity_: {
|
||||
type: 'object',
|
||||
@@ -438,6 +436,7 @@ export default {
|
||||
},
|
||||
},
|
||||
description: 'Makes all keys of an entire hierarchy optional.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
AnalyzeLocationEntityField: {
|
||||
type: 'object',
|
||||
@@ -468,6 +467,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['description', 'value', 'state', 'field'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
AnalyzeLocationGenerateEntity: {
|
||||
type: 'object',
|
||||
@@ -485,6 +485,7 @@ export default {
|
||||
required: ['fields', 'entity'],
|
||||
description:
|
||||
"This is some form of representation of what the analyzer could deduce.\nWe should probably have a chat about how this can best be conveyed to\nthe frontend. It'll probably contain a (possibly incomplete) entity, plus\nenough info for the frontend to know what form data to show to the user\nfor overriding/completing the info.",
|
||||
additionalProperties: false,
|
||||
},
|
||||
AnalyzeLocationResponse: {
|
||||
type: 'object',
|
||||
@@ -503,6 +504,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['generateEntities', 'existingEntityFiles'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
LocationInput: {
|
||||
type: 'object',
|
||||
@@ -519,6 +521,7 @@ export default {
|
||||
},
|
||||
},
|
||||
required: ['type', 'target', 'presence'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
SerializedError: {
|
||||
allOf: [
|
||||
@@ -550,17 +553,16 @@ export default {
|
||||
},
|
||||
],
|
||||
description: 'The serialized form of an Error.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
EntitiesQueryResponse: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
items: {
|
||||
type: 'array',
|
||||
items: [
|
||||
{
|
||||
$ref: '#/components/schemas/Entity',
|
||||
},
|
||||
],
|
||||
items: {
|
||||
$ref: '#/components/schemas/Entity',
|
||||
},
|
||||
description: 'The list of entities paginated by a specific filter.',
|
||||
},
|
||||
totalItems: {
|
||||
@@ -581,6 +583,7 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
securitySchemes: {
|
||||
@@ -612,7 +615,21 @@ export default {
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/RefreshOptions',
|
||||
type: 'object',
|
||||
properties: {
|
||||
authorizationToken: {
|
||||
type: 'string',
|
||||
},
|
||||
entityRef: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The reference to a single entity that should be refreshed',
|
||||
},
|
||||
},
|
||||
required: ['entityRef'],
|
||||
description:
|
||||
'Options for requesting a refresh of entities in the catalog.',
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -25,14 +25,22 @@ export async function createRouter(
|
||||
console.log(options);
|
||||
const router = Router() as ApiRouter<DeepWriteable<typeof doc>>;
|
||||
|
||||
router.get('/pets/:uid', (req, res) => {
|
||||
router.get('/pets/:uid', (_, res) => {
|
||||
res.json({
|
||||
id: 1,
|
||||
name: req.params.uid,
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
// router.get('/pet') will complain with a TS error
|
||||
router.get('/pets', (_, res) => {
|
||||
res.json([
|
||||
{
|
||||
id: 1,
|
||||
tag: '123',
|
||||
name: 'test',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
router.post('/pets', (_, res) => {
|
||||
res.send();
|
||||
|
||||
@@ -23,17 +23,17 @@ export type DeepWriteable<T> = {
|
||||
export interface ApiRouter<Doc extends RequiredDoc> extends Router {
|
||||
get: DocRequestMatcher<Doc, this, 'get'>;
|
||||
|
||||
post: DocRequestMatcher<Doc, this, 'get'>;
|
||||
post: DocRequestMatcher<Doc, this, 'post'>;
|
||||
|
||||
all: DocRequestMatcher<Doc, this, 'get'>;
|
||||
all: DocRequestMatcher<Doc, this, 'all'>;
|
||||
|
||||
put: DocRequestMatcher<Doc, this, 'get'>;
|
||||
put: DocRequestMatcher<Doc, this, 'put'>;
|
||||
|
||||
delete: DocRequestMatcher<Doc, this, 'get'>;
|
||||
delete: DocRequestMatcher<Doc, this, 'delete'>;
|
||||
|
||||
patch: DocRequestMatcher<Doc, this, 'get'>;
|
||||
patch: DocRequestMatcher<Doc, this, 'patch'>;
|
||||
|
||||
options: DocRequestMatcher<Doc, this, 'get'>;
|
||||
options: DocRequestMatcher<Doc, this, 'options'>;
|
||||
|
||||
head: DocRequestMatcher<Doc, this, 'get'>;
|
||||
head: DocRequestMatcher<Doc, this, 'head'>;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -156,6 +156,7 @@ export default {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
Pets: {
|
||||
type: 'array',
|
||||
@@ -175,6 +176,7 @@ export default {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -131,7 +131,7 @@ export type ObjectWithContentSchema<
|
||||
Object extends { content?: ContentObject },
|
||||
> = Object['content'] extends ContentObject
|
||||
? SchemaRef<Doc, Object['content']['application/json']['schema']>
|
||||
: unknown;
|
||||
: never;
|
||||
|
||||
/**
|
||||
* From https://stackoverflow.com/questions/71393738/typescript-intersection-not-union-type-from-json-schema.
|
||||
@@ -164,3 +164,7 @@ export type ConvertAll<T, R extends ReadonlyArray<unknown> = []> = T extends [
|
||||
]
|
||||
? ConvertAll<Rest, [...R, FromSchema<First>]>
|
||||
: R;
|
||||
|
||||
type UnknownIfNever<P> = [P] extends [never] ? unknown : P;
|
||||
|
||||
export type ToTypeSafe<T> = UnknownIfNever<ConvertAll<TuplifyUnion<T>>[number]>;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Application } from 'express';
|
||||
import core from 'express-serve-static-core';
|
||||
import { DocPathTemplate, MethodAwareDocPath, RequiredDoc } from './common';
|
||||
import { RequestBodyToJsonSchema } from './requests';
|
||||
@@ -60,12 +59,8 @@ export interface DocRequestMatcher<
|
||||
| 'delete'
|
||||
| 'patch'
|
||||
| 'options'
|
||||
| 'head' = any,
|
||||
| 'head',
|
||||
> {
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: Path,
|
||||
...handlers: DocRequestHandler<Doc, Path, Method>[]
|
||||
): T;
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
|
||||
@@ -74,13 +69,4 @@ export interface DocRequestMatcher<
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
|
||||
): T;
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: PathParams,
|
||||
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
|
||||
): T;
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: PathParams,
|
||||
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
|
||||
): T;
|
||||
(path: PathParams, subApplication: Application): T;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ import type {
|
||||
DocPathMethod,
|
||||
DocPathTemplate,
|
||||
PathTemplate,
|
||||
ConvertAll,
|
||||
TuplifyUnion,
|
||||
ValueOf,
|
||||
ToTypeSafe,
|
||||
} from './common';
|
||||
|
||||
type RequestBody<
|
||||
@@ -54,12 +52,10 @@ export type RequestBodySchema<
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = RequestBody<Doc, DocPath<Doc, Path>, Method> extends RequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
|
||||
: unknown;
|
||||
: never;
|
||||
|
||||
export type RequestBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ConvertAll<
|
||||
TuplifyUnion<ValueOf<RequestBodySchema<Doc, Path, Method>>>
|
||||
>[number];
|
||||
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
|
||||
|
||||
@@ -29,9 +29,8 @@ import type {
|
||||
DocPathMethod,
|
||||
DocPathTemplate,
|
||||
PathTemplate,
|
||||
ConvertAll,
|
||||
ToTypeSafe,
|
||||
ValueOf,
|
||||
TuplifyUnion,
|
||||
} from './common';
|
||||
|
||||
type Response<
|
||||
@@ -85,13 +84,11 @@ export type ResponseSchemas<
|
||||
Doc,
|
||||
Responses<Doc, DocPath<Doc, Path>, Method>[StatusCode]
|
||||
>
|
||||
: unknown;
|
||||
: never;
|
||||
};
|
||||
|
||||
export type ResponseBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ConvertAll<
|
||||
TuplifyUnion<ValueOf<ResponseSchemas<Doc, Path, Method>>>
|
||||
>[number];
|
||||
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
|
||||
|
||||
Reference in New Issue
Block a user