@spec2ts/openapi
Version:
Utility to convert OpenAPI v3 specifications to Typescript using TypeScript native compiler
195 lines (194 loc) • 7.97 kB
JavaScript
;
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;