UNPKG

@spec2ts/openapi

Version:

Utility to convert OpenAPI v3 specifications to Typescript using TypeScript native compiler

195 lines (194 loc) 7.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createOpenApiResult = exports.addToOpenApiResult = exports.getOperationIdentifier = exports.getPathName = exports.getOperationName = exports.getResponseName = exports.getSchemaFromContent = exports.getParamType = exports.getContentDeclaration = exports.parseReference = exports.parseParameters = exports.parseOperation = exports.parsePathItem = void 0; const ts = require("typescript"); const core = require("@spec2ts/core"); const core_parser_1 = require("@spec2ts/jsonschema/lib/core-parser"); const VERBS = ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"]; function parsePathItem(path, item, context, result) { const baseParams = item.parameters && parseParameters(getPathName(path, context), item.parameters, undefined, context, result); Object.entries(item) .filter(([verb,]) => VERBS.includes(verb.toUpperCase())) .forEach(([verb, entry]) => parseOperation(path, verb, entry, baseParams, context, result)); } exports.parsePathItem = parsePathItem; function parseOperation(path, verb, operation, baseParams, context, result) { const name = getOperationName(verb, path, operation.operationId, context); if (operation.parameters) { parseParameters(name, operation.parameters, baseParams, context, result); } if (operation.requestBody) { const requestBody = (0, core_parser_1.resolveReference)(operation.requestBody, context); const decla = getContentDeclaration(name + "Body", requestBody.content, context); if (decla) { addToOpenApiResult(result, "body", decla); } } if (operation.responses) { const responses = (0, core_parser_1.resolveReference)(operation.responses, context); Object.entries(responses).forEach(([status, responseObj]) => { const response = (0, core_parser_1.resolveReference)(responseObj, context); const decla = getContentDeclaration(getResponseName(name, status, context), response.content, context); if (decla) { addToOpenApiResult(result, "responses", decla); } }); } } exports.parseOperation = parseOperation; function parseParameters(baseName, data, baseParams = {}, context, result) { const params = []; const query = []; const headers = []; const cookie = []; const res = {}; data.forEach(item => { item = (0, core_parser_1.resolveReference)(item, context); switch (item.in) { case "path": params.push(item); break; case "header": headers.push(item); break; case "query": query.push(item); break; case "cookie": cookie.push(item); break; } }); addParams(params, "params"); addParams(headers, "headers"); addParams(query, "query"); addParams(cookie, "cookie"); return res; function addParams(params, paramType) { if (!params.length) return; const name = baseName + (0, core_parser_1.pascalCase)(paramType); const type = getParamType(paramType, params, baseParams[paramType], context); addToOpenApiResult(result, paramType, core.createTypeOrInterfaceDeclaration({ modifiers: [core.modifier.export], name, type })); res[paramType] = ts.factory.createTypeReferenceNode(name, undefined); } } exports.parseParameters = parseParameters; function parseReference(ref, context) { const type = (0, core_parser_1.getTypeFromSchema)(ref.schema, (0, core_parser_1.createRefContext)(ref, context)); context.aliases.push(core.createTypeOrInterfaceDeclaration({ modifiers: [core.modifier.export], name: ref.name, type })); } exports.parseReference = parseReference; //#endregion //#region Utils function getContentDeclaration(name, content, context) { if (!content) return; content = (0, core_parser_1.resolveReference)(content, context); const schema = getSchemaFromContent(content); if (!schema) return; const type = (0, core_parser_1.getTypeFromSchema)(schema, context); return core.createTypeOrInterfaceDeclaration({ modifiers: [core.modifier.export], name, type }); } exports.getContentDeclaration = getContentDeclaration; function getParamType(paramType, data, baseType, context) { const required = []; const props = {}; data.forEach(m => { let name = m.name; if (paramType === "headers" && context.options.lowerHeaders) { name = name.toLowerCase(); } props[name] = m.schema || {}; if (m.required) { required.push(name); } }); const ctx = paramType === "query" && typeof context.options.enableDateForQueryParams !== "undefined" ? { ...context, options: { ...context.options, enableDate: context.options.enableDateForQueryParams } } : context; const type = (0, core_parser_1.getTypeFromProperties)(props, required, false, ctx); if (baseType) { return ts.factory.createIntersectionTypeNode([baseType, type]); } return type; } exports.getParamType = getParamType; function getSchemaFromContent(content) { var _a, _b, _c, _d; return ((_a = content === null || content === void 0 ? void 0 : content["application/json"]) === null || _a === void 0 ? void 0 : _a.schema) || ((_b = content === null || content === void 0 ? void 0 : content["application/x-www-form-urlencoded"]) === null || _b === void 0 ? void 0 : _b.schema) || ((_c = content === null || content === void 0 ? void 0 : content["multipart/form-data"]) === null || _c === void 0 ? void 0 : _c.schema) || ((_d = content === null || content === void 0 ? void 0 : content["*/*"]) === null || _d === void 0 ? void 0 : _d.schema); } exports.getSchemaFromContent = getSchemaFromContent; function getResponseName(operationName, statusCode, context) { let name = operationName + "Response"; const status = parseInt(statusCode); if (status >= 200 && status < 300) { const count = (context.names[name] = (context.names[name] || 0) + 1); if (count > 1) { name += statusCode; } } else if (!isNaN(status)) { name += statusCode; } else { // default name += (0, core_parser_1.pascalCase)(statusCode); } return name; } exports.getResponseName = getResponseName; function getOperationName(verb, path, operationId, context) { const id = getOperationIdentifier(operationId); if (id) { return id; } return getPathName(`${verb} ${path}`, context); } exports.getOperationName = getOperationName; function getPathName(path, context) { path = path.replace(/\{(.+?)\}/, "by $1").replace(/\{(.+?)\}/g, "and $1"); let name = (0, core_parser_1.pascalCase)(path); const count = (context.names[name] = (context.names[name] || 0) + 1); if (count > 1) { name += count; } return name; } exports.getPathName = getPathName; function getOperationIdentifier(id) { if (!id) return; if (id.match(/[^\w\s]/)) return; id = (0, core_parser_1.pascalCase)(id); if (core.isValidIdentifier(id)) return id; } exports.getOperationIdentifier = getOperationIdentifier; function addToOpenApiResult(result, prop, statement) { const statements = Array.isArray(statement) ? statement : [statement]; result[prop].push(...statements); result.all.push(...statements); } exports.addToOpenApiResult = addToOpenApiResult; function createOpenApiResult() { return { params: [], query: [], headers: [], body: [], responses: [], models: [], cookie: [], import: [], all: [] }; } exports.createOpenApiResult = createOpenApiResult;