mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-06 00:07:44 +01:00
78cf9d03aa
Switch the express-openapi implementation from our internal fork to the upstream version. We have upstreamed our changes and a new version has been released, so this should be the last step before we can retire our fork. Because some of the dependencies have been updated since our internal fork, we also need to update some of our error handling to reflect this.
104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import openapi, { IExpressOpenApi } from '@wesleytodd/openapi';
|
|
import { Express, RequestHandler, Response } from 'express';
|
|
import { IUnleashConfig } from '../types/option';
|
|
import {
|
|
createOpenApiSchema,
|
|
JsonSchemaProps,
|
|
removeJsonSchemaProps,
|
|
SchemaId,
|
|
} from '../openapi';
|
|
import { ApiOperation } from '../openapi/util/api-operation';
|
|
import { Logger } from '../logger';
|
|
import { validateSchema } from '../openapi/validate';
|
|
import { IFlagResolver } from '../types';
|
|
import { fromOpenApiValidationErrors } from '../error/bad-data-error';
|
|
|
|
export class OpenApiService {
|
|
private readonly config: IUnleashConfig;
|
|
|
|
private readonly logger: Logger;
|
|
|
|
private readonly api: IExpressOpenApi;
|
|
|
|
private flagResolver: IFlagResolver;
|
|
|
|
constructor(config: IUnleashConfig) {
|
|
this.config = config;
|
|
this.flagResolver = config.flagResolver;
|
|
this.logger = config.getLogger('openapi-service.ts');
|
|
|
|
this.api = openapi(
|
|
this.docsPath(),
|
|
createOpenApiSchema(config.server),
|
|
{
|
|
coerce: true,
|
|
extendRefs: true,
|
|
basePath: config.server.baseUriPath,
|
|
},
|
|
);
|
|
}
|
|
|
|
validPath(op: ApiOperation): RequestHandler {
|
|
return this.api.validPath(op);
|
|
}
|
|
|
|
useDocs(app: Express): void {
|
|
app.use(this.api);
|
|
app.use(this.docsPath(), this.api.swaggerui);
|
|
}
|
|
|
|
docsPath(): string {
|
|
const { baseUriPath = '' } = this.config.server ?? {};
|
|
return `${baseUriPath}/docs/openapi`;
|
|
}
|
|
|
|
registerCustomSchemas<T extends JsonSchemaProps>(
|
|
schemas: Record<string, T>,
|
|
): void {
|
|
Object.entries(schemas).forEach(([name, schema]) => {
|
|
this.api.schema(name, removeJsonSchemaProps(schema));
|
|
});
|
|
}
|
|
|
|
useErrorHandler(app: Express): void {
|
|
app.use((err, req, res, next) => {
|
|
if (err?.status && err.validationErrors) {
|
|
const apiError = fromOpenApiValidationErrors(
|
|
req.body,
|
|
err.validationErrors,
|
|
);
|
|
|
|
res.status(apiError.statusCode).json(apiError);
|
|
} else {
|
|
next(err);
|
|
}
|
|
});
|
|
}
|
|
|
|
respondWithValidation<T, S = SchemaId>(
|
|
status: number,
|
|
res: Response<T>,
|
|
schema: S,
|
|
data: T,
|
|
headers: { [header: string]: string } = {},
|
|
): void {
|
|
const errors = validateSchema<S>(schema, data);
|
|
|
|
if (errors) {
|
|
this.logger.debug(
|
|
'Invalid response:',
|
|
JSON.stringify(errors, null, 4),
|
|
);
|
|
if (this.flagResolver.isEnabled('strictSchemaValidation')) {
|
|
throw new Error(JSON.stringify(errors, null, 4));
|
|
}
|
|
}
|
|
|
|
Object.entries(headers).forEach(([header, value]) =>
|
|
res.header(header, value),
|
|
);
|
|
|
|
res.status(status).json(data);
|
|
}
|
|
}
|