fix templates

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com>
Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2024-03-03 11:10:36 -05:00
parent ddf9e55c3a
commit be1db622eb
11 changed files with 753 additions and 254 deletions
@@ -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
@@ -1,3 +1,3 @@
apis/DefaultApi.client.ts
apis/DefaultApi.server.ts
apis/index.ts
index.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<string>,
limit?: number,
filter?: Array<string>,
offset?: number,
after?: string,
order?: Array<string>,
},
response: Array<Entity>,
},
'#GET|/entities/by-query': {
path: {
},
query: {
fields?: Array<string>,
limit?: number,
orderField?: Array<string>,
cursor?: string,
filter?: Array<string>,
fullTextFilterTerm?: string,
fullTextFilterFields?: Array<string>,
},
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<string>,
filter?: Array<string>,
},
response: EntityFacetsResponse,
},
'#GET|/locations/{id}': {
path: {
id: string,
},
query: {
},
response: Location,
},
'#GET|/locations': {
path: {
},
query: {
},
response: Array<GetLocations200ResponseInner>,
},
'#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> = T[keyof T];
'#DELETE|/entities/by-uid/{uid}': {
path: {
uid: string;
};
query: {};
response: void;
};
type Filter<T, K> = T extends K ? K : never;
'#DELETE|/locations/{id}': {
path: {
id: string;
};
query: {};
response: void;
};
type ExtractDocPath<Path extends string> = Path extends `#${string}|${infer OpenApiPath}` ? OpenApiPath : never;
'#GET|/entities': {
path: {};
query: {
fields?: Array<string>;
limit?: number;
filter?: Array<string>;
offset?: number;
after?: string;
order?: Array<string>;
};
response: Array<Entity>;
};
type ExtractDocMethod<Path extends string> = Path extends `#${infer Method}|${string}` ? Method : never
'#GET|/entities/by-query': {
path: {};
query: {
fields?: Array<string>;
limit?: number;
orderField?: Array<string>;
cursor?: string;
filter?: Array<string>;
fullTextFilterTerm?: string;
fullTextFilterFields?: Array<string>;
};
response: EntitiesQueryResponse;
};
type DocPath<Doc extends InputOutput> = ExtractDocPath<Filter<keyof Doc, string>>;
'#POST|/entities/by-refs': {
path: {};
query: {};
body: GetEntitiesByRefsRequest;
response: EntitiesBatchResponse;
};
type DocPathMethod<Doc extends InputOutput, Path extends string> = ExtractDocMethod<Path>;
'#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<string>;
filter?: Array<string>;
};
response: EntityFacetsResponse;
};
'#GET|/locations/{id}': {
path: {
id: string;
};
query: {};
response: Location;
};
'/locations': {
get: {
path: {};
query: {};
response: Array<GetLocations200ResponseInner>;
};
};
'#POST|/refresh': {
path: {};
query: {};
body: RefreshEntityRequest;
response: void;
};
'#POST|/validate-entity': {
path: {};
query: {};
body: ValidateEntityRequest;
response: void;
};
};
type PathTemplate<Path extends string> =
Path extends `${infer Prefix}{${infer PathName}}${infer Suffix}`
? `${Prefix}:${PathName}${PathTemplate<Suffix>}`
: Path;
type HttpMethods = Uppercase<'all' | 'put' | 'get' | 'post' | 'delete'>;
type ValueOf<T> = T[keyof T];
/**
* @public
*/
export type Filter<T, U> = T extends U ? T : never;
type DocPath<Doc extends InputOutput> = ValueOf<{
[Template in keyof Doc]: Template extends `#${string}|${infer Path}`
? Path
: never;
}>;
type DocPathMethod<
Doc extends InputOutput,
Path extends DocPath<Doc>,
> = ValueOf<{
[Template in keyof Doc]: Template extends `#${infer Method}|${Path}`
? Method extends string
? Method
: never
: never;
}>;
type PathSchema<
Doc extends InputOutput,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = `#${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<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = `#${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<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = `#${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<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = `#${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<Doc, Path>,
> = core.RequestHandler<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
RequestBodyToJsonSchema<Doc, Path, Method>,
ResponseBody<Doc, Path, Method>,
RequestBody<Doc, Path, Method>,
QuerySchema<Doc, Path, Method>,
Record<string, string>
>;
@@ -242,60 +262,85 @@ export type DocRequestHandler<
* @public
*/
export type DocRequestHandlerParams<
Doc extends RequiredDoc,
Doc extends InputOutput,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = core.RequestHandlerParams<
PathSchema<Doc, Path, Method>,
ResponseBodyToJsonSchema<Doc, Path, Method>,
RequestBodyToJsonSchema<Doc, Path, Method>,
ResponseBody<Doc, Path, Method>,
RequestBody<Doc, Path, Method>,
QuerySchema<Doc, Path, Method>,
Record<string, string>
>;
/**
* @public
*/
export type MethodAwareDocPath<
Doc extends InputOutput,
Path extends DocPath<Doc>,
Method extends DocPathMethod<Doc, Path>,
> = 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<Extract<keyof Doc['paths'], string>>,
Method
>,
TPath extends DocPath<Doc>,
TMethod extends Filter<DocPathMethod<Doc, TPath>, Method>,
>(
path: Path,
...handlers: Array<
DocRequestHandler<Doc, TemplateToDocPath<Doc, Path>, Method>
>
path: PathTemplate<MethodAwareDocPath<Doc, TPath, TMethod>>,
...handlers: Array<DocRequestHandler<Doc, TPath, TMethod>>
): T;
<
Path extends MethodAwareDocPath<
Doc,
PathTemplate<Extract<keyof Doc['paths'], string>>,
Method
>,
TPath extends DocPath<Doc>,
TMethod extends Filter<DocPathMethod<Doc, TPath>, Method>,
>(
path: Path,
...handlers: Array<
DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>
>
path: PathTemplate<MethodAwareDocPath<Doc, TPath, TMethod>>,
...handlers: Array<DocRequestHandlerParams<Doc, TPath, TMethod>>
): T;
}
export interface TypedRouter extends Router {
get:
get: DocRequestMatcher<InputOutput, this, 'GET'>;
post: DocRequestMatcher<InputOutput, this, 'POST'>;
put: DocRequestMatcher<InputOutput, this, 'PUT'>;
delete: DocRequestMatcher<InputOutput, this, 'DELETE'>;
}
type method = DocRequestMatcher<InputOutput, Router, 'POST'>;
type test2 = keyof method;
type paths = PathTemplate<DocPath<InputOutput>>;
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<InputOutput>;
type why = DocPathMethod<
InputOutput,
'/entities/by-name/{kind}/{namespace}/{name}'
>;
type test5 = MethodAwareDocPath<InputOutput, '/entities', 'GET'>;
const router: TypedRouter = Router() as TypedRouter;
router.post('/entities', (req, res) => {
res.json([{}]);
});
@@ -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<GetLocations200ResponseInner>;
};
};
'/entities/by-uid/{uid}': {
DELETE: {
path: {
uid: string;
};
query: {};
response: void;
};
GET: {
path: {
uid: string;
};
query: {};
response: Entity;
};
};
'/entities': {
GET: {
path: {};
query: {
fields?: Array<string>;
limit?: number;
filter?: Array<string>;
offset?: number;
after?: string;
order?: Array<string>;
};
response: Array<Entity>;
};
};
'/entities/by-query': {
GET: {
path: {};
query: {
fields?: Array<string>;
limit?: number;
orderField?: Array<string>;
cursor?: string;
filter?: Array<string>;
fullTextFilterTerm?: string;
fullTextFilterFields?: Array<string>;
};
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<string>;
filter?: Array<string>;
};
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;
};
};
};
@@ -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> = T[keyof T];
/**
* All paths for a given doc,
* @example `/pet/{petId}` | `/pet`
* @public
*/
export type DocPath<Doc extends PathDoc> = Extract<keyof Doc['paths'], string>;
/**
* 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 string> =
Path extends `${infer Prefix}{${infer PathName}}${infer Suffix}`
? `${Prefix}:${PathName}${PathTemplate<Suffix>}`
: 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<typeof spec, "/posts/:postId/comments/:commentId"> = "/posts/{postId}/comments/{commentId}";
* const specPathWithoutParams: DocPath<typeof spec, "/posts/comments"> = "/posts/comments";
* ```
*
* @public
*/
export type TemplateToDocPath<
Doc extends PathDoc,
Path extends DocPathTemplate<Doc>,
> = ValueOf<{
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Template
: never;
}>;
/**
* @public
*/
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<DocPath<Doc>>;
/**
* @public
*/
export type DocPathMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPath<Doc>,
> = keyof Doc['paths'][Path];
/**
* @public
*/
export type DocPathTemplateMethod<
Doc extends Pick<RequiredDoc, 'paths'>,
Path extends DocPathTemplate<Doc>,
> = keyof Doc['paths'][TemplateToDocPath<Doc, Path>];
/**
* @public
*/
export type MethodAwareDocPath<
Doc extends PathDoc,
Path extends DocPathTemplate<Doc>,
Method extends DocPathTemplateMethod<Doc, Path>,
> = ValueOf<{
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
? Method extends DocPathTemplateMethod<Doc, Path>
? PathTemplate<Template>
: never
: never;
}>;
/**
* From {@link https://stackoverflow.com/questions/71393738/typescript-intersection-not-union-type-from-json-schema}
*
* StackOverflow says not to do this, but union types aren't possible any other way.
*
* @public
*/
export type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends (k: infer I) => void
? I
: never;
/**
* @public
*/
export type LastOf<T> = UnionToIntersection<
T extends any ? () => T : never
> extends () => infer R
? R
: never;
/**
* @public
*/
export type Push<T extends any[], V> = [...T, V];
/**
* @public
*/
export type TuplifyUnion<
T,
L = LastOf<T>,
N = [T] extends [never] ? true : false,
> = true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>;
/**
* @public
*/
export type UnknownIfNever<P> = [P] extends [never] ? unknown : P;
/**
* @public
*/
export type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = Extract<
T,
Record<K, V>
>;
/**
* @public
*/
export type MapDiscriminatedUnion<
T extends Record<K, string>,
K extends keyof T,
> = {
[V in T[K]]: DiscriminateUnion<T, K, V>;
};
/**
* @public
*/
export type PickOptionalKeys<T extends { [key: string]: any }> = {
[K in keyof T]: true extends T[K]['required'] ? never : K;
}[keyof T];
/**
* @public
*/
export type PickRequiredKeys<T extends { [key: string]: any }> = {
[K in keyof T]: true extends T[K]['required'] ? K : never;
}[keyof T];
/**
* @public
*/
export type OptionalMap<T extends { [key: string]: any }> = {
[P in Exclude<PickOptionalKeys<T>, undefined>]?: NonNullable<T[P]>;
};
/**
* @public
*/
export type RequiredMap<T extends { [key: string]: any }> = {
[P in Exclude<PickRequiredKeys<T>, undefined>]: NonNullable<T[P]>;
};
/**
* @public
*/
export type FullMap<T extends { [key: string]: any }> = RequiredMap<T> &
OptionalMap<T>;
/**
* @public
*/
export type Filter<T, U> = T extends U ? T : never;