UNPKG

kobp

Version:
171 lines 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.deriveApiSpec = void 0; require("reflect-metadata"); const __1 = require(".."); /** * Extract API specification from given router object */ const deriveApiSpec = (title, someOptions, builder, router) => { const options = { skipMethods: ['HEAD'], skipPaths: ['swagger'], version: '1.0.0', basePath: '', description: (desc) => desc, servers: [], availableTags: [], securitySchemes: {}, securityOnAllOperations: [], ...someOptions, }; const skipPaths = options.skipPaths; const skipMethods = new Set(options.skipMethods); const cleanPath = options.basePath ? (path) => path .replace(options.basePath, '') .replace(/^\/*/, '/') .replace(/:(\w+)/g, '{$1}') : (path) => path.replace(/^\/*/, '/').replace(/:(\w+)/g, '{$1}'); // make sure there is only one path const skipPathPredicate = typeof skipPaths === 'function' ? skipPaths : (path) => { for (const skipPath of skipPaths) { if (typeof skipPath === 'string') { return path.startsWith(skipPath); } return skipPath.test(path); } return false; }; const description = options.description; builder.addInfo({ title: title, version: options.version, description: typeof description === 'function' ? description("**NOTE**: The OpenAPI's specification is also available at [spec.json](./spec.json)") : description, }); for (const tag of options.availableTags) { builder.addTag(tag); } for (const server of options.servers) { builder.addServer(server); } for (const schemeName of Object.keys(options.securitySchemes)) { builder.addSecurityScheme(schemeName, options.securitySchemes[schemeName]); } for (const layer of router.stack) { // Skip the path if (skipPathPredicate(layer.path)) { continue; } // String array of path parameter keys const pathParameterKeys = [...layer.path.matchAll(/:(\w+)/g)].map((d) => d[1]); const methods = layer.methods; let pathItem = {}; for (const method of methods) { if (skipMethods.has(method)) { continue; } // Extract document data let opDoc = {}; // initialize operation document with default security if (options.securityOnAllOperations) { opDoc.security = options.securityOnAllOperations || []; } let validationSpecBuffer = {}; layer.stack.map((stack) => { // try to access the metadata defined within the stack const keys = Reflect.getMetadataKeys(stack).filter((k) => __1.ALL_METADATA_KEYS.has(k)); for (const key of keys) { // Found a meta of documentation node! if (key === __1.METADATA_KEYS.DOC_KEY) { const opSpecFn = Reflect.getMetadata(__1.METADATA_KEYS.DOC_KEY, stack); const builder = new __1.OperationDocumentBuilder({ ...opDoc, ...opSpecFn() }); // FIXME: Handle the path definition // merge? if (validationSpecBuffer.body) { // inject body builder.useBody({ required: true, content: { 'application/json': { schema: validationSpecBuffer.body, }, }, }); } // Add default doc first const undocumentedParams = new Set(pathParameterKeys); if (validationSpecBuffer.parameters) { const shape = validationSpecBuffer.parameters; for (const key of Object.keys(shape.properties)) { const { description } = shape.properties[key]; undocumentedParams.delete(key); builder.useParameter('path', key, { schema: shape.properties[key], description, required: true, }); } } if (undocumentedParams.size > 0) { for (const key of undocumentedParams) { builder.useParameter('path', key, { schema: { type: 'string', }, required: true, }); } } if (validationSpecBuffer.headers) { const shape = validationSpecBuffer.headers; for (const key of Object.keys(shape.properties)) { const { description } = shape.properties[key]; builder.useParameter('header', key, { schema: shape.properties[key], description, }); } } if (validationSpecBuffer.query) { const shape = validationSpecBuffer.query; for (const key of Object.keys(shape.properties)) { const { description } = shape.properties[key]; builder.useParameter('query', key, { schema: shape.properties[key], description, }); } } opDoc = builder.build(); } else if (key === __1.METADATA_KEYS.DOC_BODY_SHAPE_VALIDATION_KEY) { validationSpecBuffer.body = Reflect.getMetadata(__1.METADATA_KEYS.DOC_BODY_SHAPE_VALIDATION_KEY, stack); } else if (key === __1.METADATA_KEYS.DOC_HEADERS_SHAPE_VALIDATION_KEY) { validationSpecBuffer.headers = Reflect.getMetadata(__1.METADATA_KEYS.DOC_HEADERS_SHAPE_VALIDATION_KEY, stack); } else if (key === __1.METADATA_KEYS.DOC_PARAMS_SHAPE_VALIDATION_KEY) { validationSpecBuffer.parameters = Reflect.getMetadata(__1.METADATA_KEYS.DOC_PARAMS_SHAPE_VALIDATION_KEY, stack); } else if (key === __1.METADATA_KEYS.DOC_QUERY_SHAPE_VALIDATION_KEY) { validationSpecBuffer.query = Reflect.getMetadata(__1.METADATA_KEYS.DOC_QUERY_SHAPE_VALIDATION_KEY, stack); } } }); pathItem = { [method.toLowerCase()]: { responses: [], ...opDoc, }, }; } builder.addPath(cleanPath(layer.path), pathItem); } return builder.getSpec(); }; exports.deriveApiSpec = deriveApiSpec; //# sourceMappingURL=swagger.js.map