Merge branch 'backstage:master' into master
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
The `HostDiscovery` export has been deprecated, import it from `@backstage/backend-app-api` instead.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/backend-openapi-utils': patch
|
||||
---
|
||||
|
||||
Adds new public utility types for common OpenAPI values, like request and response shapes and parameters available on an endpoint.
|
||||
|
||||
**deprecated** `internal` namespace
|
||||
The internal namespace will continue to be exported but now uses OpenAPI format for path parameters. You should use the new utility types.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': patch
|
||||
---
|
||||
|
||||
Updated to import `HostDiscovery` from `@backstage/backend-app-api`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Moved `HostDiscovery` from `@backstage/backend-common`.
|
||||
+1
-1
@@ -123,7 +123,7 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/
|
||||
| [Contentful](https://www.contentful.com) | [James Bourne](https://github.com/jamesmbourne) | Centralized documentation of service ownership, APIs, and documentation, and new service creation with a custom scaffolder - [full case study with Roadie](https://roadie.io/case-studies/maintaining-velocity-through-hypergrowth-contentful/). |
|
||||
| [Back Market](https://www.backmarket.com) | [Sami Farhat](https://github.com/skfarhat) | Internal Developer Portal featuring catalog, tech-radar, ownership management, component creation (scaffolder) and centralized infrastructure management -- probably more to come. |
|
||||
| [Avalia Systems](https://avalia.io) | [Olivier Liechti](https://github.com/wasadigi), [Fabio Velloso](https://github.com/fabiovelloso) | Innersource, software analytics, knowledge base for 360 software assessments, collaborative applications, hub for tracking and sharing IP assets. |
|
||||
| [Albert Heijn](https://ah.technology) | [Joost Hofman](https://github.com/joosthofman), [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. |
|
||||
| [Albert Heijn](https://ah.technology) | [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. |
|
||||
| [Wise, formerly TransferWise](https://wise.com) | [Andrew Beveridge](https://github.com/beveradb) | It's early days for us, we're trying to start small with catalog, tech docs and unified search. Future ambitious vision includes scaffolder for one-click component addition, building out integrations with CI/CD tooling, kubernetes clusters, monitoring/alerting tooling etc. and aiming for a frictionless "golden path" for engineers! 🚀 |
|
||||
| [Happy Money](http://happymoney.com/) | [Akshit Lomash](mailto:alomash@happymoney.com) | We are moving from a monolith to microservices-based architecture. We are developing a developer portal based on Backstage to create a service catalog for our new services. All the services created are onboarded Backstage and engineering teams are using a cookie-cutter-based template from backstage to initiate a new service. |
|
||||
| [Lightspeed](http://lightspeedhq.com/) | [Marcus Crane](mailto:marcus.crane@lightspeedhq.com) | We use it within our X-Series division (https://vendhq.com) to catalog ~100+ systems and ~350 components! |
|
||||
|
||||
@@ -10,6 +10,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
import { CacheClient } from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import { CorsOptions } from 'cors';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
import { ErrorRequestHandler } from 'express';
|
||||
import { Express as Express_2 } from 'express';
|
||||
import { Format } from 'logform';
|
||||
@@ -25,7 +26,6 @@ import { LoadConfigOptionsRemote } from '@backstage/config-loader';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { RemoteConfigSourceOptions } from '@backstage/config-loader';
|
||||
import { RequestHandler } from 'express';
|
||||
import { RequestListener } from 'http';
|
||||
@@ -114,7 +114,7 @@ export interface DefaultRootHttpRouterOptions {
|
||||
|
||||
// @public (undocumented)
|
||||
export const discoveryServiceFactory: () => ServiceFactory<
|
||||
PluginEndpointDiscovery,
|
||||
DiscoveryService,
|
||||
'plugin'
|
||||
>;
|
||||
|
||||
@@ -128,6 +128,20 @@ export interface ExtendedHttpServer extends http.Server {
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class HostDiscovery implements DiscoveryService {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options?: {
|
||||
basePath?: string;
|
||||
},
|
||||
): HostDiscovery;
|
||||
// (undocumented)
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
// (undocumented)
|
||||
getExternalBaseUrl(pluginId: string): Promise<string>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface HttpRouterFactoryOptions {
|
||||
getPath?(pluginId: string): string;
|
||||
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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 interface Config {
|
||||
/** Discovery options. */
|
||||
discovery?: {
|
||||
/**
|
||||
* Endpoints
|
||||
*
|
||||
* A list of target baseUrls and the associated plugins.
|
||||
*/
|
||||
endpoints: {
|
||||
/**
|
||||
* The target baseUrl to use for the plugin
|
||||
*
|
||||
* Can be either a string or an object with internal and external keys.
|
||||
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
|
||||
*/
|
||||
target: string | { internal: string; external: string };
|
||||
/** Array of plugins which use the target baseUrl. */
|
||||
plugins: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
@@ -90,8 +90,10 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"config.d.ts",
|
||||
"alpha"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2020 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 { Config } from '@backstage/config';
|
||||
import { readHttpServerOptions } from '@backstage/backend-app-api';
|
||||
import { DiscoveryService } from '@backstage/backend-plugin-api';
|
||||
|
||||
type Target = string | { internal: string; external: string };
|
||||
|
||||
/**
|
||||
* HostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
* that can handle plugins that are hosted in a single or multiple deployments.
|
||||
*
|
||||
* The deployment may be scaled horizontally, as long as the external URL
|
||||
* is the same for all instances. However, internal URLs will always be
|
||||
* resolved to the same host, so there won't be any balancing of internal traffic.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class HostDiscovery implements DiscoveryService {
|
||||
/**
|
||||
* Creates a new HostDiscovery discovery instance by reading
|
||||
* from the `backend` config section, specifically the `.baseUrl` for
|
||||
* discovering the external URL, and the `.listen` and `.https` config
|
||||
* for the internal one.
|
||||
*
|
||||
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
|
||||
* eg.
|
||||
* ```yaml
|
||||
* discovery:
|
||||
* endpoints:
|
||||
* - target: https://internal.example.com/internal-catalog
|
||||
* plugins: [catalog]
|
||||
* - target: https://internal.example.com/secure/api/{{pluginId}}
|
||||
* plugins: [auth, permission]
|
||||
* - target:
|
||||
* internal: https://internal.example.com/search
|
||||
* external: https://example.com/search
|
||||
* plugins: [search]
|
||||
* ```
|
||||
*
|
||||
* The basePath defaults to `/api`, meaning the default full internal
|
||||
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
||||
*/
|
||||
static fromConfig(config: Config, options?: { basePath?: string }) {
|
||||
const basePath = options?.basePath ?? '/api';
|
||||
const externalBaseUrl = config
|
||||
.getString('backend.baseUrl')
|
||||
.replace(/\/+$/, '');
|
||||
|
||||
const {
|
||||
listen: { host: listenHost = '::', port: listenPort },
|
||||
} = readHttpServerOptions(config.getConfig('backend'));
|
||||
const protocol = config.has('backend.https') ? 'https' : 'http';
|
||||
|
||||
// Translate bind-all to localhost, and support IPv6
|
||||
let host = listenHost;
|
||||
if (host === '::' || host === '') {
|
||||
// We use localhost instead of ::1, since IPv6-compatible systems should default
|
||||
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
|
||||
// things will still work.
|
||||
host = 'localhost';
|
||||
} else if (host === '0.0.0.0') {
|
||||
host = '127.0.0.1';
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
|
||||
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
|
||||
|
||||
return new HostDiscovery(
|
||||
internalBaseUrl + basePath,
|
||||
externalBaseUrl + basePath,
|
||||
config.getOptionalConfig('discovery'),
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly internalBaseUrl: string,
|
||||
private readonly externalBaseUrl: string,
|
||||
private readonly discoveryConfig: Config | undefined,
|
||||
) {}
|
||||
|
||||
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
|
||||
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
|
||||
|
||||
const target = endpoints
|
||||
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
|
||||
?.get<Target>('target');
|
||||
|
||||
if (!target) {
|
||||
const baseUrl =
|
||||
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
|
||||
|
||||
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
||||
}
|
||||
|
||||
if (typeof target === 'string') {
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
return target[type].replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'internal');
|
||||
}
|
||||
|
||||
async getExternalBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'external');
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HostDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { HostDiscovery } from './HostDiscovery';
|
||||
|
||||
/** @public */
|
||||
export const discoveryServiceFactory = createServiceFactory({
|
||||
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
export { discoveryServiceFactory } from './discoveryServiceFactory';
|
||||
export { HostDiscovery } from './HostDiscovery';
|
||||
|
||||
@@ -28,6 +28,7 @@ import { GiteaIntegration } from '@backstage/integration';
|
||||
import { GithubCredentialsProvider } from '@backstage/integration';
|
||||
import { GithubIntegration } from '@backstage/integration';
|
||||
import { GitLabIntegration } from '@backstage/integration';
|
||||
import { HostDiscovery as HostDiscovery_2 } from '@backstage/backend-app-api';
|
||||
import { IdentityService } from '@backstage/backend-plugin-api';
|
||||
import { isChildPath } from '@backstage/cli-common';
|
||||
import { Knex } from 'knex';
|
||||
@@ -479,18 +480,7 @@ export class GitlabUrlReader implements UrlReader {
|
||||
}
|
||||
|
||||
// @public
|
||||
export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
options?: {
|
||||
basePath?: string;
|
||||
},
|
||||
): HostDiscovery;
|
||||
// (undocumented)
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
// (undocumented)
|
||||
getExternalBaseUrl(pluginId: string): Promise<string>;
|
||||
}
|
||||
export const HostDiscovery: typeof HostDiscovery_2;
|
||||
|
||||
export { isChildPath };
|
||||
|
||||
@@ -751,7 +741,7 @@ export type ServiceBuilder = {
|
||||
export function setRootLogger(newLogger: winston.Logger): void;
|
||||
|
||||
// @public @deprecated
|
||||
export const SingleHostDiscovery: typeof HostDiscovery;
|
||||
export const SingleHostDiscovery: typeof HostDiscovery_2;
|
||||
|
||||
// @public
|
||||
export type StatusCheck = () => Promise<any>;
|
||||
|
||||
Vendored
-20
@@ -216,24 +216,4 @@ export interface Config {
|
||||
*/
|
||||
csp?: { [policyId: string]: string[] | false };
|
||||
};
|
||||
|
||||
/** Discovery options. */
|
||||
discovery?: {
|
||||
/**
|
||||
* Endpoints
|
||||
*
|
||||
* A list of target baseUrls and the associated plugins.
|
||||
*/
|
||||
endpoints: {
|
||||
/**
|
||||
* The target baseUrl to use for the plugin
|
||||
*
|
||||
* Can be either a string or an object with internal and external keys.
|
||||
* Targets with `{{pluginId}}` or `{{ pluginId }} in the url will be replaced with the pluginId.
|
||||
*/
|
||||
target: string | { internal: string; external: string };
|
||||
/** Array of plugins which use the target baseUrl. */
|
||||
plugins: string[];
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import { PluginEndpointDiscovery } from './types';
|
||||
import { readHttpServerOptions } from '@backstage/backend-app-api';
|
||||
import { HostDiscovery as _HostDiscovery } from '@backstage/backend-app-api';
|
||||
|
||||
type Target = string | { internal: string; external: string };
|
||||
export type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* HostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
@@ -30,106 +28,7 @@ type Target = string | { internal: string; external: string };
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
/**
|
||||
* Creates a new HostDiscovery discovery instance by reading
|
||||
* from the `backend` config section, specifically the `.baseUrl` for
|
||||
* discovering the external URL, and the `.listen` and `.https` config
|
||||
* for the internal one.
|
||||
*
|
||||
* Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`.
|
||||
* eg.
|
||||
* ```yaml
|
||||
* discovery:
|
||||
* endpoints:
|
||||
* - target: https://internal.example.com/internal-catalog
|
||||
* plugins: [catalog]
|
||||
* - target: https://internal.example.com/secure/api/{{pluginId}}
|
||||
* plugins: [auth, permission]
|
||||
* - target:
|
||||
* internal: https://internal.example.com/search
|
||||
* external: https://example.com/search
|
||||
* plugins: [search]
|
||||
* ```
|
||||
*
|
||||
* The basePath defaults to `/api`, meaning the default full internal
|
||||
* path for the `catalog` plugin will be `http://localhost:7007/api/catalog`.
|
||||
*/
|
||||
static fromConfig(config: Config, options?: { basePath?: string }) {
|
||||
const basePath = options?.basePath ?? '/api';
|
||||
const externalBaseUrl = config
|
||||
.getString('backend.baseUrl')
|
||||
.replace(/\/+$/, '');
|
||||
|
||||
const {
|
||||
listen: { host: listenHost = '::', port: listenPort },
|
||||
} = readHttpServerOptions(config.getConfig('backend'));
|
||||
const protocol = config.has('backend.https') ? 'https' : 'http';
|
||||
|
||||
// Translate bind-all to localhost, and support IPv6
|
||||
let host = listenHost;
|
||||
if (host === '::' || host === '') {
|
||||
// We use localhost instead of ::1, since IPv6-compatible systems should default
|
||||
// to using IPv6 when they see localhost, but if the system doesn't support IPv6
|
||||
// things will still work.
|
||||
host = 'localhost';
|
||||
} else if (host === '0.0.0.0') {
|
||||
host = '127.0.0.1';
|
||||
}
|
||||
if (host.includes(':')) {
|
||||
host = `[${host}]`;
|
||||
}
|
||||
|
||||
const internalBaseUrl = `${protocol}://${host}:${listenPort}`;
|
||||
|
||||
return new HostDiscovery(
|
||||
internalBaseUrl + basePath,
|
||||
externalBaseUrl + basePath,
|
||||
config.getOptionalConfig('discovery'),
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(
|
||||
private readonly internalBaseUrl: string,
|
||||
private readonly externalBaseUrl: string,
|
||||
private readonly discoveryConfig: Config | undefined,
|
||||
) {}
|
||||
|
||||
private getTargetFromConfig(pluginId: string, type: 'internal' | 'external') {
|
||||
const endpoints = this.discoveryConfig?.getOptionalConfigArray('endpoints');
|
||||
|
||||
const target = endpoints
|
||||
?.find(endpoint => endpoint.getStringArray('plugins').includes(pluginId))
|
||||
?.get<Target>('target');
|
||||
|
||||
if (!target) {
|
||||
const baseUrl =
|
||||
type === 'external' ? this.externalBaseUrl : this.internalBaseUrl;
|
||||
|
||||
return `${baseUrl}/${encodeURIComponent(pluginId)}`;
|
||||
}
|
||||
|
||||
if (typeof target === 'string') {
|
||||
return target.replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
return target[type].replace(
|
||||
/\{\{\s*pluginId\s*\}\}/g,
|
||||
encodeURIComponent(pluginId),
|
||||
);
|
||||
}
|
||||
|
||||
async getBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'internal');
|
||||
}
|
||||
|
||||
async getExternalBaseUrl(pluginId: string): Promise<string> {
|
||||
return this.getTargetFromConfig(pluginId, 'external');
|
||||
}
|
||||
}
|
||||
export const HostDiscovery = _HostDiscovery;
|
||||
|
||||
/**
|
||||
* SingleHostDiscovery is a basic PluginEndpointDiscovery implementation
|
||||
@@ -142,4 +41,4 @@ export class HostDiscovery implements PluginEndpointDiscovery {
|
||||
* @public
|
||||
* @deprecated Use {@link HostDiscovery} instead
|
||||
*/
|
||||
export const SingleHostDiscovery = HostDiscovery;
|
||||
export const SingleHostDiscovery = _HostDiscovery;
|
||||
|
||||
@@ -13,5 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { HostDiscovery, SingleHostDiscovery } from './HostDiscovery';
|
||||
export type { PluginEndpointDiscovery } from './types';
|
||||
export {
|
||||
HostDiscovery,
|
||||
SingleHostDiscovery,
|
||||
type PluginEndpointDiscovery,
|
||||
} from './HostDiscovery';
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 type { DiscoveryService as PluginEndpointDiscovery } from '@backstage/backend-plugin-api';
|
||||
@@ -61,6 +61,12 @@ export function createRouter() {
|
||||
}
|
||||
```
|
||||
|
||||
## FAQs
|
||||
|
||||
### Why am I getting `unknown` as the type for a response?
|
||||
|
||||
This can happen when you have a `charset` defined in your `response.content` section. Something like `response.content[ 'application/json; charset=utf-8:']` will cause this issue.
|
||||
|
||||
## INTERNAL
|
||||
|
||||
### Limitations
|
||||
|
||||
@@ -75,11 +75,18 @@ interface CookieObject extends ParameterObject {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
type CookieSchema<
|
||||
export type CookieParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = CookieSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
// @public (undocumented)
|
||||
type CookieSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableCookieObject>;
|
||||
|
||||
// @public
|
||||
export function createValidatedOpenApiRouter<T extends RequiredDoc>(
|
||||
@@ -99,16 +106,16 @@ type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = Extract<
|
||||
// @public (undocumented)
|
||||
type DocOperation<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = Doc['paths'][Path][Method];
|
||||
|
||||
// @public (undocumented)
|
||||
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'],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
Parameter extends keyof DocOperation<Doc, Path, Method>['parameters'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
@@ -142,32 +149,28 @@ type DocParameters<
|
||||
};
|
||||
|
||||
// @public
|
||||
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;
|
||||
}>;
|
||||
type DocPath<Doc extends PathDoc> = Extract<keyof Doc['paths'], string>;
|
||||
|
||||
// @public (undocumented)
|
||||
type DocPathMethod<
|
||||
Doc extends Pick<RequiredDoc, 'paths'>,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = keyof Doc['paths'][DocPath<Doc, Path>];
|
||||
Path extends DocPath<Doc>,
|
||||
> = keyof Doc['paths'][Path];
|
||||
|
||||
// @public (undocumented)
|
||||
type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
|
||||
Extract<keyof Doc['paths'], string>
|
||||
>;
|
||||
type DocPathTemplate<Doc extends PathDoc> = PathTemplate<DocPath<Doc>>;
|
||||
|
||||
// @public (undocumented)
|
||||
type DocPathTemplateMethod<
|
||||
Doc extends Pick<RequiredDoc, 'paths'>,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = keyof Doc['paths'][TemplateToDocPath<Doc, Path>];
|
||||
|
||||
// @public
|
||||
type DocRequestHandler<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = core.RequestHandler<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
@@ -179,8 +182,8 @@ type DocRequestHandler<
|
||||
// @public
|
||||
type DocRequestHandlerParams<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = core.RequestHandlerParams<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
@@ -204,14 +207,30 @@ interface DocRequestMatcher<
|
||||
| 'head',
|
||||
> {
|
||||
// (undocumented)
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
<
|
||||
Path extends MethodAwareDocPath<
|
||||
Doc,
|
||||
PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method
|
||||
>,
|
||||
>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
|
||||
...handlers: Array<
|
||||
DocRequestHandler<Doc, TemplateToDocPath<Doc, Path>, Method>
|
||||
>
|
||||
): T;
|
||||
// (undocumented)
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
<
|
||||
Path extends MethodAwareDocPath<
|
||||
Doc,
|
||||
PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method
|
||||
>,
|
||||
>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
|
||||
...handlers: Array<
|
||||
DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>
|
||||
>
|
||||
): T;
|
||||
}
|
||||
|
||||
@@ -238,11 +257,18 @@ interface HeaderObject extends ParameterObject {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
type HeaderSchema<
|
||||
export type HeaderParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = HeaderSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
// @public (undocumented)
|
||||
type HeaderSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableHeaderObject>;
|
||||
|
||||
// @public
|
||||
type Immutable<T> = T extends
|
||||
@@ -304,10 +330,12 @@ declare namespace internal {
|
||||
RequiredDoc,
|
||||
PathDoc,
|
||||
ValueOf,
|
||||
PathTemplate,
|
||||
DocPath,
|
||||
PathTemplate,
|
||||
TemplateToDocPath,
|
||||
DocPathTemplate,
|
||||
DocPathMethod,
|
||||
DocPathTemplateMethod,
|
||||
MethodAwareDocPath,
|
||||
DocOperation,
|
||||
ComponentTypes,
|
||||
@@ -362,7 +390,7 @@ declare namespace internal {
|
||||
RequestBody,
|
||||
RequestBodySchema,
|
||||
RequestBodyToJsonSchema,
|
||||
Response_2 as Response,
|
||||
Response_3 as Response,
|
||||
ResponseSchemas,
|
||||
ResponseBodyToJsonSchema,
|
||||
};
|
||||
@@ -394,14 +422,11 @@ type MapToSchema<
|
||||
// @public (undocumented)
|
||||
type MethodAwareDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = ValueOf<{
|
||||
[Template in Extract<
|
||||
keyof Doc['paths'],
|
||||
string
|
||||
>]: Path extends PathTemplate<Template>
|
||||
? Method extends DocPathMethod<Doc, Path>
|
||||
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
|
||||
? Method extends DocPathTemplateMethod<Doc, Path>
|
||||
? PathTemplate<Template>
|
||||
: never
|
||||
: never;
|
||||
@@ -466,11 +491,18 @@ interface PathObject extends ParameterObject {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
type PathSchema<
|
||||
export type PathParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = PathSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
// @public (undocumented)
|
||||
type PathSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutablePathObject>;
|
||||
|
||||
// @public
|
||||
type PathTemplate<Path extends string> =
|
||||
@@ -508,11 +540,26 @@ interface QueryObject extends ParameterObject {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
type QuerySchema<
|
||||
export type QueryParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = QuerySchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
// @public (undocumented)
|
||||
type QuerySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableQueryObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableQueryObject>;
|
||||
|
||||
// @public (undocumented)
|
||||
type Request_2<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = RequestBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
export { Request_2 as Request };
|
||||
|
||||
// @public (undocumented)
|
||||
type RequestBody<
|
||||
@@ -536,20 +583,16 @@ type RequestBody<
|
||||
// @public (undocumented)
|
||||
type RequestBodySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = RequestBody<
|
||||
Doc,
|
||||
DocPath<Doc, Path>,
|
||||
Method
|
||||
> extends ImmutableRequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
|
||||
> = RequestBody<Doc, Path, Method> extends ImmutableRequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, Path, Method>>
|
||||
: never;
|
||||
|
||||
// @public
|
||||
type RequestBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
|
||||
|
||||
@@ -568,8 +611,16 @@ type RequiredMap<
|
||||
// @public (undocumented)
|
||||
type Response_2<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = ResponseBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
export { Response_2 as Response };
|
||||
|
||||
// @public (undocumented)
|
||||
type Response_3<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
StatusCode extends keyof DocOperation<Doc, Path, Method>['responses'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
@@ -588,27 +639,27 @@ type Response_2<
|
||||
// @public
|
||||
type ResponseBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
|
||||
|
||||
// @public (undocumented)
|
||||
type ResponseSchemas<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = {
|
||||
[StatusCode in keyof DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
Method
|
||||
>['responses']]: Response_2<
|
||||
>['responses']]: Response_3<
|
||||
Doc,
|
||||
Path,
|
||||
Method,
|
||||
StatusCode
|
||||
> extends ImmutableResponseObject
|
||||
? ObjectWithContentSchema<Doc, Response_2<Doc, Path, Method, StatusCode>>
|
||||
? ObjectWithContentSchema<Doc, Response_3<Doc, Path, Method, StatusCode>>
|
||||
: never;
|
||||
};
|
||||
|
||||
@@ -625,6 +676,16 @@ type SchemaRef<Doc extends RequiredDoc, Schema> = Schema extends {
|
||||
[Key in keyof Schema]: SchemaRef<Doc, Schema[Key]>;
|
||||
};
|
||||
|
||||
// @public
|
||||
type TemplateToDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = ValueOf<{
|
||||
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
|
||||
? Template
|
||||
: never;
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
type ToTypeSafe<T> = UnknownIfNever<ConvertAll<TuplifyUnion<T>>[number]>;
|
||||
|
||||
|
||||
@@ -22,5 +22,13 @@
|
||||
import * as internal from './types';
|
||||
|
||||
export { internal };
|
||||
export type {
|
||||
Request,
|
||||
Response,
|
||||
QueryParameters,
|
||||
HeaderParameters,
|
||||
CookieParameters,
|
||||
PathParameters,
|
||||
} from './utility';
|
||||
export type { ApiRouter } from './router';
|
||||
export { createValidatedOpenApiRouter } from './stub';
|
||||
|
||||
@@ -18,15 +18,23 @@ import { createValidatedOpenApiRouter } from './stub';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import singlePathSpec from './___fixtures__/single-path';
|
||||
import { Response } from './utility';
|
||||
|
||||
describe('createRouter', () => {
|
||||
const pet: Response<typeof singlePathSpec, '/pet/:petId', 'get'> = {
|
||||
id: 1,
|
||||
name: 'rover',
|
||||
status: 'available',
|
||||
photoUrls: [],
|
||||
};
|
||||
|
||||
it('does NOT override originalUrl and basePath after execution', async () => {
|
||||
expect.assertions(2);
|
||||
const router = createValidatedOpenApiRouter(singlePathSpec);
|
||||
router.get('/pet/:petId', (req, res) => {
|
||||
expect(req.baseUrl).toBe('/pet-store');
|
||||
expect(req.originalUrl).toBe(`/pet-store/pet/${req.params.petId}`);
|
||||
res.send('');
|
||||
res.status(400).send();
|
||||
});
|
||||
|
||||
const appRouter = express();
|
||||
@@ -41,7 +49,7 @@ describe('createRouter', () => {
|
||||
const routerGetFn = jest.fn();
|
||||
router.get('/pet/:petId', (_, res) => {
|
||||
routerGetFn();
|
||||
res.send('');
|
||||
res.json(pet);
|
||||
});
|
||||
|
||||
const apiRouter = express.Router();
|
||||
@@ -58,7 +66,7 @@ describe('createRouter', () => {
|
||||
const router = createValidatedOpenApiRouter(singlePathSpec);
|
||||
router.get('/pet/:petId', (req, res) => {
|
||||
expect(typeof req.params.petId).toBe('integer');
|
||||
res.send('');
|
||||
res.json(pet);
|
||||
});
|
||||
|
||||
const apiRouter = express.Router();
|
||||
|
||||
@@ -42,6 +42,13 @@ export type PathDoc = Pick<ImmutableOpenAPIObject, 'paths'>;
|
||||
*/
|
||||
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}.
|
||||
*
|
||||
@@ -74,44 +81,46 @@ export type PathTemplate<Path extends string> =
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DocPath<
|
||||
export type TemplateToDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
> = ValueOf<{
|
||||
[Template in Extract<
|
||||
keyof Doc['paths'],
|
||||
string
|
||||
>]: Path extends PathTemplate<Template> ? Template : never;
|
||||
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
|
||||
? Template
|
||||
: never;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DocPathTemplate<Doc extends PathDoc> = PathTemplate<
|
||||
Extract<keyof Doc['paths'], string>
|
||||
>;
|
||||
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'][DocPath<Doc, Path>];
|
||||
> = keyof Doc['paths'][TemplateToDocPath<Doc, Path>];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type MethodAwareDocPath<
|
||||
Doc extends PathDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = ValueOf<{
|
||||
[Template in Extract<
|
||||
keyof Doc['paths'],
|
||||
string
|
||||
>]: Path extends PathTemplate<Template>
|
||||
? Method extends DocPathMethod<Doc, Path>
|
||||
[Template in DocPath<Doc>]: Path extends PathTemplate<Template>
|
||||
? Method extends DocPathTemplateMethod<Doc, Path>
|
||||
? PathTemplate<Template>
|
||||
: never
|
||||
: never;
|
||||
@@ -122,7 +131,7 @@ export type MethodAwareDocPath<
|
||||
*/
|
||||
export type DocOperation<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
> = Doc['paths'][Path][Method];
|
||||
|
||||
|
||||
@@ -14,7 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type core from 'express-serve-static-core';
|
||||
import { DocPathTemplate, MethodAwareDocPath, RequiredDoc } from './common';
|
||||
import {
|
||||
DocPath,
|
||||
DocPathMethod,
|
||||
MethodAwareDocPath,
|
||||
PathTemplate,
|
||||
RequiredDoc,
|
||||
TemplateToDocPath,
|
||||
} from './common';
|
||||
import { PathSchema, QuerySchema } from './params';
|
||||
import { RequestBodyToJsonSchema } from './requests';
|
||||
import { ResponseBodyToJsonSchema } from './responses';
|
||||
@@ -25,8 +32,8 @@ import { ResponseBodyToJsonSchema } from './responses';
|
||||
*/
|
||||
export type DocRequestHandler<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = core.RequestHandler<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
@@ -41,8 +48,8 @@ export type DocRequestHandler<
|
||||
*/
|
||||
export type DocRequestHandlerParams<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = core.RequestHandlerParams<
|
||||
PathSchema<Doc, Path, Method>,
|
||||
ResponseBodyToJsonSchema<Doc, Path, Method>,
|
||||
@@ -68,12 +75,28 @@ export interface DocRequestMatcher<
|
||||
| 'options'
|
||||
| 'head',
|
||||
> {
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
<
|
||||
Path extends MethodAwareDocPath<
|
||||
Doc,
|
||||
PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method
|
||||
>,
|
||||
>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandler<Doc, Path, Method>>
|
||||
...handlers: Array<
|
||||
DocRequestHandler<Doc, TemplateToDocPath<Doc, Path>, Method>
|
||||
>
|
||||
): T;
|
||||
<Path extends MethodAwareDocPath<Doc, DocPathTemplate<Doc>, Method>>(
|
||||
<
|
||||
Path extends MethodAwareDocPath<
|
||||
Doc,
|
||||
PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method
|
||||
>,
|
||||
>(
|
||||
path: Path,
|
||||
...handlers: Array<DocRequestHandlerParams<Doc, Path, Method>>
|
||||
...handlers: Array<
|
||||
DocRequestHandlerParams<Doc, TemplateToDocPath<Doc, Path>, Method>
|
||||
>
|
||||
): T;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
Filter,
|
||||
FullMap,
|
||||
MapDiscriminatedUnion,
|
||||
PathTemplate,
|
||||
RequiredDoc,
|
||||
SchemaRef,
|
||||
ValueOf,
|
||||
@@ -44,9 +43,9 @@ import { FromSchema, JSONSchema7 } from 'json-schema-to-ts';
|
||||
*/
|
||||
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'],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
Parameter extends keyof DocOperation<Doc, Path, Method>['parameters'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
Path,
|
||||
@@ -138,33 +137,33 @@ export type ParametersSchema<
|
||||
*/
|
||||
export type HeaderSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableHeaderObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableHeaderObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CookieSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableCookieObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableCookieObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type PathSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutablePathObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutablePathObject>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type QuerySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ParametersSchema<Doc, DocPath<Doc, Path>, Method, ImmutableQueryObject>;
|
||||
> = ParametersSchema<Doc, Path, Method, ImmutableQueryObject>;
|
||||
|
||||
@@ -26,8 +26,6 @@ import type {
|
||||
DocOperation,
|
||||
DocPath,
|
||||
DocPathMethod,
|
||||
DocPathTemplate,
|
||||
PathTemplate,
|
||||
ToTypeSafe,
|
||||
} from './common';
|
||||
import {
|
||||
@@ -61,14 +59,10 @@ export type RequestBody<
|
||||
*/
|
||||
export type RequestBodySchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = RequestBody<
|
||||
Doc,
|
||||
DocPath<Doc, Path>,
|
||||
Method
|
||||
> extends ImmutableRequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, DocPath<Doc, Path>, Method>>
|
||||
> = RequestBody<Doc, Path, Method> extends ImmutableRequestBodyObject
|
||||
? ObjectWithContentSchema<Doc, RequestBody<Doc, Path, Method>>
|
||||
: never;
|
||||
|
||||
/**
|
||||
@@ -77,6 +71,6 @@ export type RequestBodySchema<
|
||||
*/
|
||||
export type RequestBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<RequestBodySchema<Doc, Path, Method>>;
|
||||
|
||||
@@ -25,10 +25,9 @@ import type {
|
||||
RequiredDoc,
|
||||
DocOperation,
|
||||
DocPathMethod,
|
||||
DocPathTemplate,
|
||||
PathTemplate,
|
||||
ToTypeSafe,
|
||||
ValueOf,
|
||||
DocPath,
|
||||
} from './common';
|
||||
import { ImmutableReferenceObject, ImmutableResponseObject } from './immutable';
|
||||
|
||||
@@ -37,8 +36,8 @@ import { ImmutableReferenceObject, ImmutableResponseObject } from './immutable';
|
||||
*/
|
||||
export type Response<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends keyof Doc['paths'],
|
||||
Method extends keyof Doc['paths'][Path],
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
StatusCode extends keyof DocOperation<Doc, Path, Method>['responses'],
|
||||
> = DocOperation<
|
||||
Doc,
|
||||
@@ -59,7 +58,7 @@ export type Response<
|
||||
*/
|
||||
export type ResponseSchemas<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends DocPathTemplate<Doc>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = {
|
||||
[StatusCode in keyof DocOperation<Doc, Path, Method>['responses']]: Response<
|
||||
@@ -78,6 +77,6 @@ export type ResponseSchemas<
|
||||
*/
|
||||
export type ResponseBodyToJsonSchema<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Path extends DocPath<Doc>,
|
||||
Method extends DocPathMethod<Doc, Path>,
|
||||
> = ToTypeSafe<ValueOf<ResponseSchemas<Doc, Path, Method>>>;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 {
|
||||
CookieSchema,
|
||||
DocPathTemplateMethod,
|
||||
HeaderSchema,
|
||||
PathSchema,
|
||||
PathTemplate,
|
||||
QuerySchema,
|
||||
RequestBodyToJsonSchema,
|
||||
RequiredDoc,
|
||||
ResponseBodyToJsonSchema,
|
||||
TemplateToDocPath,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Response<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = ResponseBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Request<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = RequestBodyToJsonSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type HeaderParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = HeaderSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CookieParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = CookieSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type PathParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = PathSchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type QueryParameters<
|
||||
Doc extends RequiredDoc,
|
||||
Path extends PathTemplate<Extract<keyof Doc['paths'], string>>,
|
||||
Method extends DocPathTemplateMethod<Doc, Path>,
|
||||
> = QuerySchema<Doc, TemplateToDocPath<Doc, Path>, Method>;
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
MiddlewareFactory,
|
||||
createHttpServer,
|
||||
ExtendedHttpServer,
|
||||
HostDiscovery,
|
||||
DefaultRootHttpRouter,
|
||||
} from '@backstage/backend-app-api';
|
||||
import { HostDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
createServiceFactory,
|
||||
BackendFeature,
|
||||
|
||||
@@ -24,10 +24,10 @@ import { parseEntityFilterParams } from './parseEntityFilterParams';
|
||||
import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';
|
||||
import { parseEntityTransformParams } from './parseEntityTransformParams';
|
||||
import { spec } from '../../schema/openapi.generated';
|
||||
import { internal } from '@backstage/backend-openapi-utils';
|
||||
import { QueryParameters } from '@backstage/backend-openapi-utils';
|
||||
|
||||
export function parseQueryEntitiesParams(
|
||||
params: internal.QuerySchema<typeof spec, '/entities/by-query', 'get'>,
|
||||
params: QueryParameters<typeof spec, '/entities/by-query', 'get'>,
|
||||
): Omit<QueryEntitiesRequest, 'authorizationToken' | 'limit'> {
|
||||
const fields = parseEntityTransformParams(params);
|
||||
|
||||
|
||||
@@ -18158,9 +18158,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.3, @types/prop-types@npm:^15.7.5":
|
||||
version: 15.7.7
|
||||
resolution: "@types/prop-types@npm:15.7.7"
|
||||
checksum: 023b95f7dd82e1c594f51dcb93ec4c382600cef6eeee29a2ac7b782b92c0882eab8da16d4cbd6e18b39e85ac8d94ebf4ca02c6e248ce5b5fb4b16dbab5d82861
|
||||
version: 15.7.8
|
||||
resolution: "@types/prop-types@npm:15.7.8"
|
||||
checksum: 61dfad79da8b1081c450bab83b77935df487ae1cdd4660ec7df6be8e74725c15fa45cf486ce057addc956ca4ae78300b97091e2a25061133d1b9a1440bc896ae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -18299,13 +18299,13 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:^17":
|
||||
version: 17.0.66
|
||||
resolution: "@types/react@npm:17.0.66"
|
||||
version: 17.0.67
|
||||
resolution: "@types/react@npm:17.0.67"
|
||||
dependencies:
|
||||
"@types/prop-types": "*"
|
||||
"@types/scheduler": "*"
|
||||
csstype: ^3.0.2
|
||||
checksum: b364a98670ccee222dbd21ddd6cd1be78ad22bca40c7e1c073fff908121fd51fe1de0e9afc3d4ef8dfb6ceedfa28c5257dde9f23af0096f4d350e60c4b2f8858
|
||||
checksum: eb4915fc7d24e19fe6e98dfe98ade377423b1263e7dc001beda41b0298152fb5a410a55dde5def5ec0838156239a48e778d522da03d319ae5ecea80d455da604
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -18989,9 +18989,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@uiw/codemirror-extensions-basic-setup@npm:4.21.13":
|
||||
version: 4.21.13
|
||||
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.13"
|
||||
"@uiw/codemirror-extensions-basic-setup@npm:4.21.18":
|
||||
version: 4.21.18
|
||||
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.18"
|
||||
dependencies:
|
||||
"@codemirror/autocomplete": ^6.0.0
|
||||
"@codemirror/commands": ^6.0.0
|
||||
@@ -19008,19 +19008,19 @@ __metadata:
|
||||
"@codemirror/search": ">=6.0.0"
|
||||
"@codemirror/state": ">=6.0.0"
|
||||
"@codemirror/view": ">=6.0.0"
|
||||
checksum: 94a1ef335ed12fd1eb201e3d94892824d16a01d5a531c1962ed3b9a2d379dc3261abb4647208254a5a69763467b8a4f677ae0201fc8e67e5b60b1da9b12bae34
|
||||
checksum: c643eebc45e2067080c46e28fdb4ac40043e5890f4862bd45fe5bf816894b6c118ea901ca9ba1dce8862877622ebd793db6ec220491506355ed562a0dc0c4507
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@uiw/react-codemirror@npm:^4.9.3":
|
||||
version: 4.21.13
|
||||
resolution: "@uiw/react-codemirror@npm:4.21.13"
|
||||
version: 4.21.18
|
||||
resolution: "@uiw/react-codemirror@npm:4.21.18"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.18.6
|
||||
"@codemirror/commands": ^6.1.0
|
||||
"@codemirror/state": ^6.1.1
|
||||
"@codemirror/theme-one-dark": ^6.0.0
|
||||
"@uiw/codemirror-extensions-basic-setup": 4.21.13
|
||||
"@uiw/codemirror-extensions-basic-setup": 4.21.18
|
||||
codemirror: ^6.0.0
|
||||
peerDependencies:
|
||||
"@babel/runtime": ">=7.11.0"
|
||||
@@ -19030,7 +19030,7 @@ __metadata:
|
||||
codemirror: ">=6.0.0"
|
||||
react: ">=16.8.0"
|
||||
react-dom: ">=16.8.0"
|
||||
checksum: 5e8dafa3837eeb4a6909f463f92ac2dd4143a5db79bf03b3d1782759940e7de6fc459ac60d9f01c9ce2e37771e83d4894e236810a788b38eebf1f312659f2764
|
||||
checksum: ef3897e9901b98fd981d4b9db2ae8702aa7ffda4cf5cab3286963580606a6dde38e77a4314c12574b6f5f251d62b62e59f0a1a445009b5927b8500f4b5fd795c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -27462,7 +27462,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0":
|
||||
"graphql@npm:^16.0.0, graphql@npm:^16.8.1":
|
||||
version: 16.8.1
|
||||
resolution: "graphql@npm:16.8.1"
|
||||
checksum: 8d304b7b6f708c8c5cc164b06e92467dfe36aff6d4f2cf31dd19c4c2905a0e7b89edac4b7e225871131fd24e21460836b369de0c06532644d15b461d55b1ccc0
|
||||
@@ -33183,8 +33183,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1, msw@npm:^1.2.3, msw@npm:^1.3.1":
|
||||
version: 1.3.1
|
||||
resolution: "msw@npm:1.3.1"
|
||||
version: 1.3.2
|
||||
resolution: "msw@npm:1.3.2"
|
||||
dependencies:
|
||||
"@mswjs/cookies": ^0.2.2
|
||||
"@mswjs/interceptors": ^0.17.10
|
||||
@@ -33194,7 +33194,7 @@ __metadata:
|
||||
chalk: ^4.1.1
|
||||
chokidar: ^3.4.2
|
||||
cookie: ^0.4.2
|
||||
graphql: ^15.0.0 || ^16.0.0
|
||||
graphql: ^16.8.1
|
||||
headers-polyfill: 3.2.5
|
||||
inquirer: ^8.2.0
|
||||
is-node-process: ^1.2.0
|
||||
@@ -33212,7 +33212,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
msw: cli/index.js
|
||||
checksum: 5e3c9d2efff025ecaa55c48b0392179fb07253f26212fe884cca7cc4b5d62cebbfa937dc7249a91d432b1f64ebcce2a1e4938056264f277da62d62d072dfb883
|
||||
checksum: c2d4f7747f5806f0fd8d8cc3ca250ee1c2a7a6cd608de43f95bd072ba1fb13cdce0b52932ce9bf8f4a21b194d2815db535501e224ec8f7052593447fe1c0cb70
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user