Update api report and shift openapi-utils to a devDependency.
Signed-off-by: Aramis Sennyey <sennyeya@amazon.com>
This commit is contained in:
committed by
Fredrik Adelöw
parent
4efe0a0877
commit
abd4ff4cee
@@ -77,7 +77,7 @@ components:
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
minimum: 0
|
||||
limit:
|
||||
name: limit
|
||||
in: query
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-client": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
@@ -85,6 +84,7 @@
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
|
||||
@@ -50,7 +50,7 @@ import {
|
||||
locationInput,
|
||||
validateRequestBody,
|
||||
} from './util';
|
||||
import { ApiRouter } from '@backstage/backend-openapi-utils';
|
||||
import type { ApiRouter } from '@backstage/backend-openapi-utils';
|
||||
import spec from '../../schema/openapi';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,55 +1,45 @@
|
||||
# @backstage/plugin-openapi-router
|
||||
# @backstage/openapi-utils
|
||||
|
||||
## Purpose
|
||||
## Summary
|
||||
|
||||
This package is meant to provide a typed Express router for an OpenAPI spec. Specs must be converted to JSON and then copied to a Typescript file.
|
||||
This package is meant to provide a typed Express router for an OpenAPI spec. Based on the [oatx](https://github.com/varanauskas/oatx) library and adapted to override Express values.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Configuration
|
||||
|
||||
In your plugin's `schema/openapi.ts`,
|
||||
1. Run `yarn --cwd <package-dir> backstage-cli package schema:openapi:generate` to translate your `openapi.yaml` to a new Typescript file in `schema/openapi.ts`. In the case of projects that require linting + a license header, you will need to do this manually.
|
||||
|
||||
2. In your plugin's `src/service/createRouter.ts`,
|
||||
|
||||
```ts
|
||||
export default {
|
||||
// If your spec is in YAML, convert it to JSON, then paste it here.
|
||||
// If your spec is in JSON, just paste it here.
|
||||
} as const;
|
||||
```
|
||||
|
||||
In your plugin's `service/createRouter.ts`,
|
||||
|
||||
```ts
|
||||
import {ApiRouter} from `@backstage/plugin-openapi-router`;
|
||||
import spec from './schema/openapi'
|
||||
import {ApiRouter} from `@backstage/backend-openapi-utils`;
|
||||
import spec from '../../schema/openapi'
|
||||
...
|
||||
|
||||
export function createRouter(){
|
||||
const router = Router() as ApiRouter<typeof spec>
|
||||
const router = Router() as ApiRouter<typeof spec>;
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
1. OpenAPI definitions must be converted to Typescript files
|
||||
From [#32063](https://github.com/microsoft/TypeScript/issues/32063), we cannot import JSON `as const`. If we could, this would allow us to force all specs to be JSON and then just import from a spec.
|
||||
2. `as const` makes all fields `readonly`
|
||||
To ensure a good DX of using a simple imported JSON spec, we want to remove any type issues between `readonly` arrays and mutable arrays. Typescript does not allow them to be compared, so converting all imports from the `openapi3-ts` library to `readonly` is important.
|
||||
1. `as const` makes all fields `readonly`
|
||||
To ensure a good DX of using a simple imported JSON spec, we want to remove any type issues between `readonly` arrays and mutable arrays. Typescript does not allow them to be compared, so converting all imports from the `openapi3-ts` library to `readonly` is important. This is achieved through the `ImmutableObject` type in `types/immutable.ts`.
|
||||
|
||||
```tsx
|
||||
```ts
|
||||
...
|
||||
// We want an interface like this,
|
||||
Router() as ApiRouter<typeof spec>
|
||||
|
||||
// Not an interface like this,
|
||||
Router() as ApiRouter<DeepWriteable<typeof spec>>
|
||||
...
|
||||
```
|
||||
|
||||
we need to type all internals of this package as `Immutable<T>`.
|
||||
|
||||
## Future Work
|
||||
|
||||
### Runtime validation
|
||||
|
||||
Using a package like [`express-openapi-validator`](https://www.npmjs.com/package/express-openapi-validator), would allow us to remove [validation of request bodies with `AJV`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/service/util.ts#L58).
|
||||
|
||||
### PR-time verification.
|
||||
|
||||
1. Verify that spec file matches the router input/output.
|
||||
Using a package like [`express-openapi-validator`](https://www.npmjs.com/package/express-openapi-validator), would allow us to remove [validation of request bodies with `AJV`](https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/service/util.ts#L58). However, `AJV` currently doesn't have support for OpenAPI 3.1 and `express-openapi-validator` enforces full URL matching for paths, meaning it cannot be mounted at the router level.
|
||||
|
||||
@@ -3,599 +3,28 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import type { ContentObject } from 'openapi3-ts';
|
||||
import core from 'express-serve-static-core';
|
||||
import { FromSchema } from 'json-schema-to-ts';
|
||||
import { JSONSchema7 } from 'json-schema-to-ts';
|
||||
import type { OpenAPIObject } from 'openapi3-ts';
|
||||
import type { ParameterObject } from 'openapi3-ts';
|
||||
import type { ReferenceObject } from 'openapi3-ts';
|
||||
import type { RequestBodyObject } from 'openapi3-ts';
|
||||
import type { ResponseObject } from 'openapi3-ts';
|
||||
import { Router } from 'express';
|
||||
import type { SchemaObject } from 'openapi3-ts';
|
||||
|
||||
// @public
|
||||
export interface ApiRouter<Doc extends RequiredDoc> extends Router {
|
||||
export interface ApiRouter<Doc extends core.Spec> extends Router {
|
||||
// (undocumented)
|
||||
all: DocRequestMatcher<Doc, this, 'all'>;
|
||||
all: core.RequestMatcher<Doc, this, 'all'>;
|
||||
// (undocumented)
|
||||
delete: DocRequestMatcher<Doc, this, 'delete'>;
|
||||
delete: core.RequestMatcher<Doc, this, 'delete'>;
|
||||
// (undocumented)
|
||||
get: DocRequestMatcher<Doc, this, 'get'>;
|
||||
get: core.RequestMatcher<Doc, this, 'get'>;
|
||||
// (undocumented)
|
||||
head: DocRequestMatcher<Doc, this, 'head'>;
|
||||
head: core.RequestMatcher<Doc, this, 'head'>;
|
||||
// (undocumented)
|
||||
options: DocRequestMatcher<Doc, this, 'options'>;
|
||||
options: core.RequestMatcher<Doc, this, 'options'>;
|
||||
// (undocumented)
|
||||
patch: DocRequestMatcher<Doc, this, 'patch'>;
|
||||
patch: core.RequestMatcher<Doc, this, 'patch'>;
|
||||
// (undocumented)
|
||||
post: DocRequestMatcher<Doc, this, 'post'>;
|
||||
post: core.RequestMatcher<Doc, this, 'post'>;
|
||||
// (undocumented)
|
||||
put: DocRequestMatcher<Doc, this, 'put'>;
|
||||
put: core.RequestMatcher<Doc, this, 'put'>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ComponentRef<
|
||||
Doc extends RequiredDoc,
|
||||
Type extends ComponentTypes<Doc>,
|
||||
Ref extends ImmutableReferenceObject,
|
||||
> = Ref extends {
|
||||
$ref: `#/components/${Type}/${infer Name}`;
|
||||
}
|
||||
? Name extends keyof Doc['components'][Type]
|
||||
? Doc['components'][Type][Name] extends ImmutableReferenceObject
|
||||
? ComponentRef<Doc, Type, Doc['components'][Type][Name]>
|
||||
: Doc['components'][Type][Name]
|
||||
: never
|
||||
: never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ComponentTypes<Doc extends RequiredDoc> = Extract<
|
||||
keyof Doc['components'],
|
||||
string
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ConvertAll<T, R extends ReadonlyArray<unknown> = []> = T extends [
|
||||
infer First extends JSONSchema7,
|
||||
...infer Rest,
|
||||
]
|
||||
? ConvertAll<Rest, [...R, FromSchema<First>]>
|
||||
: R;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CookieObject extends ParameterObject {
|
||||
// (undocumented)
|
||||
in: 'cookie';
|
||||
// (undocumented)
|
||||
style?: 'form';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type CookieSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = Extract<
|
||||
T,
|
||||
Record<K, V>
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type DocOperation<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = Doc['paths'][Path][Method];
|
||||
|
||||
// @public (undocumented)
|
||||
export type DocParameter<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Parameter extends keyof Doc['paths'][Path][Method]['parameters'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['parameters'][Parameter] extends ImmutableReferenceObject
|
||||
? 'parameters' extends ComponentTypes<Doc>
|
||||
? ComponentRef<
|
||||
Doc,
|
||||
'parameters',
|
||||
DocOperation<Doc, Path, Method>['parameters'][Parameter]
|
||||
>
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['parameters'][Parameter];
|
||||
|
||||
// @public (undocumented)
|
||||
export type DocParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = DocOperation<Doc, Path, Method>['parameters'] extends ReadonlyArray<any>
|
||||
? {
|
||||
[Index in keyof DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['parameters']]: DocParameter<Doc, Path, Method, Index>;
|
||||
}
|
||||
: never;
|
||||
|
||||
// @public
|
||||
export type DocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
> = ValueOf<{
|
||||
[Template in Extract<
|
||||
keyof Doc['paths'],
|
||||
string
|
||||
>]: Path extends PathTemplate<Template> ? Template : never;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type DocPathMethod<
|
||||
Doc extends Pick<RequiredDoc, 'paths'>,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = keyof Doc['paths'][DocPath<Doc, Path>];
|
||||
|
||||
// @public (undocumented)
|
||||
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
|
||||
Extract<keyof Doc['paths'], string>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export type DocRequestHandler<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = core.RequestHandler<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
RequestBodyToJsonSchema<Doc, Path, Method>,
|
||||
QuerySchema<Doc, Path, Method>,
|
||||
Record<string, string>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export type DocRequestHandlerParams<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = core.RequestHandlerParams<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
RequestBodyToJsonSchema<Doc, Path, Method>,
|
||||
QuerySchema<Doc, Path, Method>,
|
||||
Record<string, string>
|
||||
>;
|
||||
|
||||
// @public
|
||||
export interface DocRequestMatcher<
|
||||
Doc extends RequiredDoc,
|
||||
T,
|
||||
Method extends
|
||||
| 'all'
|
||||
| 'get'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'delete'
|
||||
| 'patch'
|
||||
| 'options'
|
||||
| 'head',
|
||||
> {
|
||||
// (undocumented)
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
|
||||
): T;
|
||||
// (undocumented)
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
|
||||
): T;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type Filter<T, U> = T extends U ? T : never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type FullMap<
|
||||
T extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
> = RequiredMap<T> & OptionalMap<T>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface HeaderObject extends ParameterObject {
|
||||
// (undocumented)
|
||||
in: 'header';
|
||||
// (undocumented)
|
||||
style: 'simple';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type HeaderSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
|
||||
|
||||
// @public
|
||||
export type Immutable<T> = T extends
|
||||
| Function
|
||||
| boolean
|
||||
| number
|
||||
| string
|
||||
| null
|
||||
| undefined
|
||||
? T
|
||||
: T extends Map<infer K, infer V>
|
||||
? ReadonlyMap<Immutable<K>, Immutable<V>>
|
||||
: T extends Set<infer S>
|
||||
? ReadonlySet<Immutable<S>>
|
||||
: {
|
||||
readonly [P in keyof T]: Immutable<T[P]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableContentObject = ImmutableObject<ContentObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableCookieObject = ImmutableObject<CookieObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableHeaderObject = ImmutableObject<HeaderObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableObject<T> = {
|
||||
readonly [K in keyof T]: Immutable<T[K]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableOpenAPIObject = ImmutableObject<OpenAPIObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableParameterObject = ImmutableObject<ParameterObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutablePathObject = ImmutableObject<PathObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableQueryObject = ImmutableObject<QueryObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableReferenceObject = ImmutableObject<ReferenceObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableRequestBodyObject = ImmutableObject<RequestBodyObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableResponseObject = ImmutableObject<ResponseObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ImmutableSchemaObject = ImmutableObject<SchemaObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type LastOf<T> = UnionToIntersection<
|
||||
T extends any ? () => T : never
|
||||
> extends () => infer R
|
||||
? R
|
||||
: never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type MapDiscriminatedUnion<
|
||||
T extends Record<K, string>,
|
||||
K extends keyof T,
|
||||
> = {
|
||||
[V in T[K]]: DiscriminateUnion<T, K, V>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type MapToSchema<
|
||||
Doc extends RequiredDoc,
|
||||
T extends Record<string, ImmutableParameterObject>,
|
||||
> = {
|
||||
[V in keyof T]: NonNullable<T[V]> extends ImmutableParameterObject
|
||||
? ParameterSchema<Doc, NonNullable<T[V]>['schema']>
|
||||
: never;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type MethodAwareDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = ValueOf<{
|
||||
[Template in Extract<
|
||||
keyof Doc['paths'],
|
||||
string
|
||||
>]: Path extends PathTemplate<Template>
|
||||
? Method extends DocPathMethod<Doc, Path>
|
||||
? PathTemplate<Template>
|
||||
: never
|
||||
: never;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ObjectWithContentSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Object extends {
|
||||
content?: ImmutableContentObject;
|
||||
},
|
||||
> = Object['content'] extends ImmutableContentObject
|
||||
? SchemaRef<Doc, Object['content']['application/json']['schema']>
|
||||
: never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type OptionalMap<
|
||||
T extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
> = {
|
||||
[P in Exclude<PickOptionalKeys<T>, undefined>]?: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ParameterSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Schema extends ImmutableParameterObject['schema'],
|
||||
> = ResolveDocParameterSchema<Doc, Schema> extends infer R
|
||||
? R extends ImmutableSchemaObject
|
||||
? R extends JSONSchema7
|
||||
? FromSchema<R>
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ParametersSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
FilterType extends ImmutableParameterObject,
|
||||
> = number extends keyof DocParameters<Doc, Path, Method>
|
||||
? MapToSchema<
|
||||
Doc,
|
||||
FullMap<
|
||||
MapDiscriminatedUnion<
|
||||
Filter<DocParameters<Doc, Path, Method>[number], FilterType>,
|
||||
'name'
|
||||
>
|
||||
>
|
||||
>
|
||||
: never;
|
||||
|
||||
// @public
|
||||
export interface ParsedQs {
|
||||
// (undocumented)
|
||||
[key: string]: undefined | string | string[] | ParsedQs | ParsedQs[];
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type PathDoc = Pick<ImmutableOpenAPIObject, 'paths'>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface PathObject extends ParameterObject {
|
||||
// (undocumented)
|
||||
in: 'path';
|
||||
// (undocumented)
|
||||
style?: 'simple' | 'label' | 'matrix';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type PathSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
|
||||
|
||||
// @public
|
||||
export type PathTemplate<Path extends string> =
|
||||
Path extends `${infer Prefix}{${infer PathName}}${infer Suffix}`
|
||||
? `${Prefix}:${PathName}${PathTemplate<Suffix>}`
|
||||
: Path;
|
||||
|
||||
// @public (undocumented)
|
||||
export type PickOptionalKeys<
|
||||
T extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
> = {
|
||||
[K in keyof T]: true extends T[K]['required'] ? never : K;
|
||||
}[keyof T];
|
||||
|
||||
// @public (undocumented)
|
||||
export type PickRequiredKeys<
|
||||
T extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
> = {
|
||||
[K in keyof T]: true extends T[K]['required'] ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
// @public (undocumented)
|
||||
export type Push<T extends any[], V> = [...T, V];
|
||||
|
||||
// @public (undocumented)
|
||||
export interface QueryObject extends ParameterObject {
|
||||
// (undocumented)
|
||||
in: 'query';
|
||||
// (undocumented)
|
||||
style?: 'form' | 'deepObject' | 'pipeDelimited' | 'spaceDelimited';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type QuerySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableQueryObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type RequestBody<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['requestBody'] extends ImmutableReferenceObject
|
||||
? 'requestBodies' extends ComponentTypes<Doc>
|
||||
? ComponentRef<
|
||||
Doc,
|
||||
'requestBodies',
|
||||
DocOperation<Doc, Path, Method>['requestBody']
|
||||
>
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['requestBody'];
|
||||
|
||||
// @public (undocumented)
|
||||
export type RequestBodySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = RequestBody<
|
||||
Doc,
|
||||
DocPath<Doc, Path>,
|
||||
Method
|
||||
> extends ImmutableRequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
|
||||
: never;
|
||||
|
||||
// @public
|
||||
export type RequestBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
|
||||
|
||||
// @public
|
||||
export type RequiredDoc = Pick<ImmutableOpenAPIObject, 'paths' | 'components'>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type RequiredMap<
|
||||
T extends {
|
||||
[key: string]: any;
|
||||
},
|
||||
> = {
|
||||
[P in Exclude<PickRequiredKeys<T>, undefined>]: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ResolveDocParameterSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Schema extends ImmutableParameterObject['schema'],
|
||||
> = Schema extends ImmutableReferenceObject
|
||||
? 'parameters' extends ComponentTypes<Doc>
|
||||
? ComponentRef<Doc, 'parameters', Schema>
|
||||
: never
|
||||
: Schema;
|
||||
|
||||
// @public (undocumented)
|
||||
type Response_2<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
StatusCode extends keyof Doc['paths'][Path]['responses'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['responses'][StatusCode] extends ImmutableReferenceObject
|
||||
? 'responses' extends ComponentTypes<Doc>
|
||||
? ComponentRef<
|
||||
Doc,
|
||||
'responses',
|
||||
DocOperation<Doc, Path, Method>['responses'][StatusCode]
|
||||
>
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['responses'][StatusCode];
|
||||
export { Response_2 as Response };
|
||||
|
||||
// @public
|
||||
export type ResponseBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type Responses<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = {
|
||||
[StatusCode in keyof DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['responses']]: Response_2<Doc, Path, Method, StatusCode>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ResponseSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Object extends ImmutableResponseObject,
|
||||
> = ObjectWithContentSchema<Doc, Object>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type ResponseSchemas<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = {
|
||||
[StatusCode in keyof Responses<Doc, DocPath<Doc, Path>, Method>]: Responses<
|
||||
Doc,
|
||||
DocPath<Doc, Path>,
|
||||
Method
|
||||
>[StatusCode] extends ImmutableResponseObject
|
||||
? ResponseSchema<
|
||||
Doc,
|
||||
Responses<Doc, DocPath<Doc, Path>, Method>[StatusCode]
|
||||
>
|
||||
: never;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type SchemaRef<Doc extends RequiredDoc, Schema> = Schema extends {
|
||||
$ref: `#/components/schemas/${infer Name}`;
|
||||
}
|
||||
? 'schemas' extends keyof Doc['components']
|
||||
? Name extends keyof Doc['components']['schemas']
|
||||
? SchemaRef<Doc, Doc['components']['schemas'][Name]>
|
||||
: never
|
||||
: never
|
||||
: {
|
||||
[Key in keyof Schema]: SchemaRef<Doc, Schema[Key]>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type ToTypeSafe<T> = UnknownIfNever<ConvertAll<TuplifyUnion<T>>[number]>;
|
||||
|
||||
// @public (undocumented)
|
||||
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 UnionToIntersection<U> = (
|
||||
U extends any ? (k: U) => void : never
|
||||
) extends (k: infer I) => void
|
||||
? I
|
||||
: never;
|
||||
|
||||
// @public (undocumented)
|
||||
export type UnknownIfNever<P> = [P] extends [never] ? unknown : P;
|
||||
|
||||
// @public
|
||||
export type ValueOf<T> = T[keyof T];
|
||||
export namespace core {}
|
||||
```
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"json-schema-to-ts": "^2.6.2",
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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 express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { ApiRouter } from './router';
|
||||
import doc from './schema/petstore';
|
||||
|
||||
interface RouterOptions {}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
console.log(options);
|
||||
const router = Router() as ApiRouter<typeof doc>;
|
||||
|
||||
router.get('/pets/:petId', (_, res) => {
|
||||
res.json({
|
||||
id: 1,
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/pets', (_, res) => {
|
||||
res.json([
|
||||
{
|
||||
id: 1,
|
||||
tag: '123',
|
||||
name: 'test',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
router.post('/pets', (_, res) => {
|
||||
res.send();
|
||||
});
|
||||
return router;
|
||||
}
|
||||
@@ -14,26 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Router } from 'express';
|
||||
import { RequiredDoc, DocRequestMatcher } from './types';
|
||||
import { core } from './types';
|
||||
|
||||
/**
|
||||
* Typed Express router based on an OpenAPI 3.1 spec.
|
||||
* @public
|
||||
*/
|
||||
export interface ApiRouter<Doc extends RequiredDoc> extends Router {
|
||||
get: DocRequestMatcher<Doc, this, 'get'>;
|
||||
export interface ApiRouter<Doc extends core.Spec> extends Router {
|
||||
get: core.RequestMatcher<Doc, this, 'get'>;
|
||||
|
||||
post: DocRequestMatcher<Doc, this, 'post'>;
|
||||
post: core.RequestMatcher<Doc, this, 'post'>;
|
||||
|
||||
all: DocRequestMatcher<Doc, this, 'all'>;
|
||||
all: core.RequestMatcher<Doc, this, 'all'>;
|
||||
|
||||
put: DocRequestMatcher<Doc, this, 'put'>;
|
||||
put: core.RequestMatcher<Doc, this, 'put'>;
|
||||
|
||||
delete: DocRequestMatcher<Doc, this, 'delete'>;
|
||||
delete: core.RequestMatcher<Doc, this, 'delete'>;
|
||||
|
||||
patch: DocRequestMatcher<Doc, this, 'patch'>;
|
||||
patch: core.RequestMatcher<Doc, this, 'patch'>;
|
||||
|
||||
options: DocRequestMatcher<Doc, this, 'options'>;
|
||||
options: core.RequestMatcher<Doc, this, 'options'>;
|
||||
|
||||
head: DocRequestMatcher<Doc, this, 'head'>;
|
||||
head: core.RequestMatcher<Doc, this, 'head'>;
|
||||
}
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export default {
|
||||
openapi: '3.1.0',
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: 'Swagger Petstore',
|
||||
license: {
|
||||
name: 'MIT',
|
||||
url: 'https://opensource.org/licenses/MIT',
|
||||
},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: 'http://petstore.swagger.io/v1',
|
||||
},
|
||||
],
|
||||
paths: {
|
||||
'/pets': {
|
||||
get: {
|
||||
summary: 'List all pets',
|
||||
operationId: 'listPets',
|
||||
tags: ['pets'],
|
||||
parameters: [
|
||||
{
|
||||
name: 'limit',
|
||||
in: 'query',
|
||||
description: 'How many items to return at one time (max 100)',
|
||||
required: false,
|
||||
schema: {
|
||||
type: 'integer',
|
||||
format: 'int32',
|
||||
},
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'A paged array of pets',
|
||||
headers: {
|
||||
'x-next': {
|
||||
description: 'A link to the next page of responses',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Pets',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
post: {
|
||||
summary: 'Create a pet',
|
||||
operationId: 'createPets',
|
||||
tags: ['pets'],
|
||||
responses: {
|
||||
'201': {
|
||||
description: 'Null response',
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/pets/{petId}': {
|
||||
get: {
|
||||
summary: 'Info for a specific pet',
|
||||
operationId: 'showPetById',
|
||||
tags: ['pets'],
|
||||
parameters: [
|
||||
{
|
||||
name: 'petId',
|
||||
in: 'path',
|
||||
required: true,
|
||||
description: 'The id of the pet to retrieve',
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
],
|
||||
responses: {
|
||||
'200': {
|
||||
description: 'Expected response to a valid request',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Pet',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
default: {
|
||||
description: 'unexpected error',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
schemas: {
|
||||
Pet: {
|
||||
type: 'object',
|
||||
required: ['id', 'name'],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
format: 'int64',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
tag: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
Pets: {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: '#/components/schemas/Pet',
|
||||
},
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
required: ['code', 'message'],
|
||||
properties: {
|
||||
code: {
|
||||
type: 'integer',
|
||||
format: 'int32',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
@@ -27,20 +27,13 @@ import {
|
||||
|
||||
/**
|
||||
* Basic OpenAPI spec with paths and components properties enforced.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type RequiredDoc = Pick<ImmutableOpenAPIObject, 'paths' | 'components'>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type PathDoc = Pick<ImmutableOpenAPIObject, 'paths'>;
|
||||
|
||||
/**
|
||||
* Get value types of `T`
|
||||
*
|
||||
* @public
|
||||
* Get value types of `T`.
|
||||
*/
|
||||
export type ValueOf<T> = T[keyof T];
|
||||
|
||||
@@ -49,13 +42,9 @@ export type ValueOf<T> = T[keyof T];
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const path = PathTemplate<"/posts/{postId}/comments/{commentId}"> = "/posts/1/comments/2"const pathWithParams: PathTemplate<"/posts/{postId}/comments/{commentId}"> = "/posts/1/comments/2";
|
||||
* 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}`
|
||||
@@ -72,11 +61,9 @@ export type PathTemplate<Path extends string> =
|
||||
* "/posts/comments": {},
|
||||
* }
|
||||
* };
|
||||
* const specPathWithParams: DocPath<typeof spec, "/posts/1/comments/2"> = "/posts/{postId}/comments/{commentId}";
|
||||
* 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 DocPath<
|
||||
Doc extends PathDoc,
|
||||
@@ -88,24 +75,15 @@ export type DocPath<
|
||||
>]: Path extends PathTemplate<Template> ? Template : never;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
|
||||
Extract<keyof Doc['paths'], string>
|
||||
>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocPathMethod<
|
||||
Doc extends Pick<RequiredDoc, 'paths'>,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = keyof Doc['paths'][DocPath<Doc, Path>];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type MethodAwareDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
@@ -121,26 +99,17 @@ export type MethodAwareDocPath<
|
||||
: never;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocOperation<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = Doc['paths'][Path][Method];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ComponentTypes<Doc extends RequiredDoc> = Extract<
|
||||
keyof Doc['components'],
|
||||
string
|
||||
>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ComponentRef<
|
||||
Doc extends RequiredDoc,
|
||||
Type extends ComponentTypes<Doc>,
|
||||
@@ -153,9 +122,6 @@ export type ComponentRef<
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type SchemaRef<Doc extends RequiredDoc, Schema> = Schema extends {
|
||||
$ref: `#/components/schemas/${infer Name}`;
|
||||
}
|
||||
@@ -166,9 +132,6 @@ export type SchemaRef<Doc extends RequiredDoc, Schema> = Schema extends {
|
||||
: never
|
||||
: { [Key in keyof Schema]: SchemaRef<Doc, Schema[Key]> };
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ObjectWithContentSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Object extends { content?: ImmutableContentObject },
|
||||
@@ -180,7 +143,6 @@ export type ObjectWithContentSchema<
|
||||
* 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
|
||||
@@ -188,32 +150,20 @@ export type UnionToIntersection<U> = (
|
||||
? 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 ConvertAll<T, R extends ReadonlyArray<unknown> = []> = T extends [
|
||||
infer First extends JSONSchema7,
|
||||
...infer Rest,
|
||||
@@ -221,27 +171,15 @@ export type ConvertAll<T, R extends ReadonlyArray<unknown> = []> = T extends [
|
||||
? ConvertAll<Rest, [...R, FromSchema<First>]>
|
||||
: R;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type UnknownIfNever<P> = [P] extends [never] ? unknown : P;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ToTypeSafe<T> = UnknownIfNever<ConvertAll<TuplifyUnion<T>>[number]>;
|
||||
|
||||
/**
|
||||
* @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,
|
||||
@@ -249,41 +187,23 @@ export type MapDiscriminatedUnion<
|
||||
[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;
|
||||
|
||||
@@ -19,19 +19,10 @@ import { PathSchema, QuerySchema } from './params';
|
||||
import { RequestBodyToJsonSchema } from './requests';
|
||||
import { ResponseBodyToJsonSchema } from './responses';
|
||||
|
||||
/**
|
||||
* Pulled from the express library.
|
||||
* @public
|
||||
*/
|
||||
export interface ParsedQs {
|
||||
[key: string]: undefined | string | string[] | ParsedQs | ParsedQs[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Typed express request handler.
|
||||
* @public
|
||||
*/
|
||||
export type DocRequestHandler<
|
||||
type DocRequestHandler<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -45,9 +36,8 @@ export type DocRequestHandler<
|
||||
|
||||
/**
|
||||
* Typed express error handler / request handler union type.
|
||||
* @public
|
||||
*/
|
||||
export type DocRequestHandlerParams<
|
||||
type DocRequestHandlerParams<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -61,7 +51,6 @@ export type DocRequestHandlerParams<
|
||||
|
||||
/**
|
||||
* Superset of the express router path matcher that enforces typed request and response bodies.
|
||||
* @public
|
||||
*/
|
||||
export interface DocRequestMatcher<
|
||||
Doc extends RequiredDoc,
|
||||
|
||||
@@ -31,7 +31,6 @@ import type {
|
||||
/**
|
||||
* From {@link https://github.com/microsoft/TypeScript/issues/13923#issuecomment-653675557}, allows
|
||||
* us to convert from `as const` to the various OpenAPI types documented in `openapi3-ts`.
|
||||
* @public
|
||||
*/
|
||||
export type Immutable<T> = T extends
|
||||
| Function
|
||||
@@ -47,94 +46,46 @@ export type Immutable<T> = T extends
|
||||
? ReadonlySet<Immutable<S>>
|
||||
: { readonly [P in keyof T]: Immutable<T[P]> };
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableReferenceObject = ImmutableObject<ReferenceObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableOpenAPIObject = ImmutableObject<OpenAPIObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableContentObject = ImmutableObject<ContentObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableRequestBodyObject = ImmutableObject<RequestBodyObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableResponseObject = ImmutableObject<ResponseObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableParameterObject = ImmutableObject<ParameterObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface HeaderObject extends ParameterObject {
|
||||
interface HeaderObject extends ParameterObject {
|
||||
in: 'header';
|
||||
style: 'simple';
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableHeaderObject = ImmutableObject<HeaderObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CookieObject extends ParameterObject {
|
||||
interface CookieObject extends ParameterObject {
|
||||
in: 'cookie';
|
||||
style?: 'form';
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableCookieObject = ImmutableObject<CookieObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface QueryObject extends ParameterObject {
|
||||
interface QueryObject extends ParameterObject {
|
||||
in: 'query';
|
||||
style?: 'form' | 'deepObject' | 'pipeDelimited' | 'spaceDelimited';
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableQueryObject = ImmutableObject<QueryObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface PathObject extends ParameterObject {
|
||||
interface PathObject extends ParameterObject {
|
||||
in: 'path';
|
||||
style?: 'simple' | 'label' | 'matrix';
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutablePathObject = ImmutableObject<PathObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ImmutableSchemaObject = ImmutableObject<SchemaObject>;
|
||||
|
||||
@@ -13,9 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './common';
|
||||
export * from './express';
|
||||
export * from './requests';
|
||||
export * from './responses';
|
||||
export * from './immutable';
|
||||
export * from './params';
|
||||
|
||||
import type { RequiredDoc } from './common';
|
||||
import type { DocRequestMatcher } from './express';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace core {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type Spec = RequiredDoc;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type RequestMatcher<
|
||||
Doc extends RequiredDoc,
|
||||
T,
|
||||
Method extends
|
||||
| 'all'
|
||||
| 'get'
|
||||
| 'post'
|
||||
| 'put'
|
||||
| 'delete'
|
||||
| 'patch'
|
||||
| 'options'
|
||||
| 'head',
|
||||
> = DocRequestMatcher<Doc, T, Method>;
|
||||
}
|
||||
|
||||
@@ -37,10 +37,7 @@ import {
|
||||
} from './common';
|
||||
import { FromSchema, JSONSchema7 } from 'json-schema-to-ts';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocParameter<
|
||||
type DocParameter<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -59,10 +56,7 @@ export type DocParameter<
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['parameters'][Parameter];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocParameters<
|
||||
type DocParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -76,10 +70,7 @@ export type DocParameters<
|
||||
}
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ResolveDocParameterSchema<
|
||||
type ResolveDocParameterSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Schema extends ImmutableParameterObject['schema'],
|
||||
> = Schema extends ImmutableReferenceObject
|
||||
@@ -88,10 +79,7 @@ export type ResolveDocParameterSchema<
|
||||
: never
|
||||
: Schema;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ParameterSchema<
|
||||
type ParameterSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Schema extends ImmutableParameterObject['schema'],
|
||||
> = ResolveDocParameterSchema<Doc, Schema> extends infer R
|
||||
@@ -102,10 +90,7 @@ export type ParameterSchema<
|
||||
: never
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type MapToSchema<
|
||||
type MapToSchema<
|
||||
Doc extends RequiredDoc,
|
||||
T extends Record<string, ImmutableParameterObject>,
|
||||
> = {
|
||||
@@ -114,10 +99,7 @@ export type MapToSchema<
|
||||
: never;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ParametersSchema<
|
||||
type ParametersSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -134,36 +116,24 @@ export type ParametersSchema<
|
||||
>
|
||||
: never;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type HeaderSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CookieSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type PathSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type QuerySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
|
||||
@@ -35,10 +35,7 @@ import {
|
||||
ImmutableRequestBodyObject,
|
||||
} from './immutable';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type RequestBody<
|
||||
type RequestBody<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends Extract<keyof Doc['paths'], string>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -56,10 +53,7 @@ export type RequestBody<
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['requestBody'];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type RequestBodySchema<
|
||||
type RequestBodySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
@@ -73,7 +67,6 @@ export type RequestBodySchema<
|
||||
|
||||
/**
|
||||
* Transform the OpenAPI request body schema to a typesafe JSON schema.
|
||||
* @public
|
||||
*/
|
||||
export type RequestBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
|
||||
@@ -33,10 +33,7 @@ import type {
|
||||
} from './common';
|
||||
import { ImmutableReferenceObject, ImmutableResponseObject } from './immutable';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Response<
|
||||
type Response<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -55,10 +52,7 @@ export type Response<
|
||||
: never
|
||||
: DocOperation<Doc, Path, Method>['responses'][StatusCode];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Responses<
|
||||
type Responses<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
@@ -71,18 +65,12 @@ export type Responses<
|
||||
>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ResponseSchema<
|
||||
type ResponseSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Object extends ImmutableResponseObject,
|
||||
> = ObjectWithContentSchema<Doc, Object>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type ResponseSchemas<
|
||||
type ResponseSchemas<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
@@ -101,7 +89,6 @@ export type ResponseSchemas<
|
||||
|
||||
/**
|
||||
* Transform the OpenAPI request body schema to a typesafe JSON schema.
|
||||
* @public
|
||||
*/
|
||||
export type ResponseBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
|
||||
@@ -3833,7 +3833,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@types/express": ^4.17.6
|
||||
"@types/express-serve-static-core": ^4.17.33
|
||||
"@types/express-serve-static-core": ^4.17.5
|
||||
express: ^4.17.1
|
||||
express-promise-router: ^4.1.0
|
||||
json-schema-to-ts: ^2.6.2
|
||||
@@ -15804,17 +15804,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.18, @types/express-serve-static-core@npm:^4.17.31, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5":
|
||||
version: 4.17.33
|
||||
resolution: "@types/express-serve-static-core@npm:4.17.33"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
"@types/qs": "*"
|
||||
"@types/range-parser": "*"
|
||||
checksum: dce580d16b85f207445af9d4053d66942b27d0c72e86153089fa00feee3e96ae336b7bedb31ed4eea9e553c99d6dd356ed6e0928f135375d9f862a1a8015adf2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.30, @types/express-serve-static-core@npm:^4.17.31, @types/express-serve-static-core@npm:^4.17.5":
|
||||
version: 4.17.33
|
||||
resolution: "@types/express-serve-static-core@npm:4.17.33"
|
||||
@@ -15868,18 +15857,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/express@npm:4.17.14":
|
||||
version: 4.17.14
|
||||
resolution: "@types/express@npm:4.17.14"
|
||||
dependencies:
|
||||
"@types/body-parser": "*"
|
||||
"@types/express-serve-static-core": ^4.17.18
|
||||
"@types/qs": "*"
|
||||
"@types/serve-static": "*"
|
||||
checksum: 15c1af46d02de834e4a225eccaa9d85c0370fdbb3ed4e1bc2d323d24872309961542b993ae236335aeb3e278630224a6ea002078d39e651d78a3b0356b1eaa79
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/fs-extra@npm:^9.0.1, @types/fs-extra@npm:^9.0.3, @types/fs-extra@npm:^9.0.5, @types/fs-extra@npm:^9.0.6":
|
||||
version: 9.0.13
|
||||
resolution: "@types/fs-extra@npm:9.0.13"
|
||||
@@ -20732,13 +20709,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"content-type@npm:~1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "content-type@npm:1.0.4"
|
||||
checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"content-type@npm:~1.0.4, content-type@npm:~1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "content-type@npm:1.0.5"
|
||||
@@ -38831,20 +38801,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-log@npm:^2.1.4":
|
||||
version: 2.2.5
|
||||
resolution: "ts-log@npm:2.2.5"
|
||||
checksum: 28f78ab15b8555d56c089dbc243327d8ce4331219956242a29fc4cb3bad6bb0cb8234dd17a292381a1b1dba99a7e4849a2181b2e1a303e8247e9f4ca4e284f2d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-log@npm:^2.1.4, ts-log@npm:^2.2.3":
|
||||
version: 2.2.5
|
||||
resolution: "ts-log@npm:2.2.5"
|
||||
checksum: 28f78ab15b8555d56c089dbc243327d8ce4331219956242a29fc4cb3bad6bb0cb8234dd17a292381a1b1dba99a7e4849a2181b2e1a303e8247e9f4ca4e284f2d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ts-log@npm:^2.2.3":
|
||||
version: 2.2.3
|
||||
resolution: "ts-log@npm:2.2.3"
|
||||
|
||||
Reference in New Issue
Block a user