import openapi, { IExpressOpenApi } from '@unleash/express-openapi'; import { Express, RequestHandler, Response } from 'express'; import { IUnleashConfig } from '../types/option'; import { ApiOperation, createOpenApiSchema, JsonSchemaProps, removeJsonSchemaProps, SchemaId, } from '../openapi'; import { Logger } from '../logger'; import { validateSchema } from '../openapi/validate'; export class OpenApiService { private readonly config: IUnleashConfig; private readonly logger: Logger; private readonly api: IExpressOpenApi; constructor(config: IUnleashConfig) { this.config = config; this.logger = config.getLogger('openapi-service.ts'); this.api = openapi( this.docsPath(), createOpenApiSchema(config.server), { coerce: true }, ); } 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( schemas: Record, ): 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 && err.status && err.validationErrors) { res.status(err.status).json({ error: err.message, validation: err.validationErrors, }); } else { next(); } }); } respondWithValidation( status: number, res: Response, schema: SchemaId, data: T, ): void { const errors = validateSchema(schema, data); if (errors) { this.logger.warn('Invalid response:', errors); } res.status(status).json(data); } }