@samchon/openapi
Version:
OpenAPI definitions and converters for 'typia' and 'nestia'.
196 lines (192 loc) • 9.82 kB
JavaScript
import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker.mjs";
var OpenApiV3Downgrader;
(function(OpenApiV3Downgrader) {
OpenApiV3Downgrader.downgrade = input => {
const collection = OpenApiV3Downgrader.downgradeComponents(input.components);
return {
openapi: "3.0.0",
servers: input.servers,
info: input.info,
components: collection.downgraded,
paths: input.paths ? Object.fromEntries(Object.entries(input.paths).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, downgradePathItem(collection)(value) ]))) : undefined,
security: input.security,
tags: input.tags
};
};
const downgradePathItem = collection => pathItem => ({
...pathItem,
...pathItem.get ? {
get: downgradeOperation(collection)(pathItem.get)
} : undefined,
...pathItem.put ? {
put: downgradeOperation(collection)(pathItem.put)
} : undefined,
...pathItem.post ? {
post: downgradeOperation(collection)(pathItem.post)
} : undefined,
...pathItem.delete ? {
delete: downgradeOperation(collection)(pathItem.delete)
} : undefined,
...pathItem.options ? {
options: downgradeOperation(collection)(pathItem.options)
} : undefined,
...pathItem.head ? {
head: downgradeOperation(collection)(pathItem.head)
} : undefined,
...pathItem.patch ? {
patch: downgradeOperation(collection)(pathItem.patch)
} : undefined,
...pathItem.trace ? {
trace: downgradeOperation(collection)(pathItem.trace)
} : undefined
});
const downgradeOperation = collection => input => ({
...input,
parameters: input.parameters ? input.parameters.map(downgradeParameter(collection)) : undefined,
requestBody: input.requestBody ? downgradeRequestBody(collection)(input.requestBody) : undefined,
responses: input.responses ? Object.fromEntries(Object.entries(input.responses).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, downgradeResponse(collection)(value) ]))) : undefined
});
const downgradeParameter = collection => input => ({
...input,
schema: OpenApiV3Downgrader.downgradeSchema(collection)(input.schema)
});
const downgradeRequestBody = collection => input => ({
...input,
content: input.content ? downgradeContent(collection)(input.content) : undefined
});
const downgradeResponse = collection => input => ({
...input,
content: input.content ? downgradeContent(collection)(input.content) : undefined,
headers: input.headers ? Object.fromEntries(Object.entries(input.headers).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, {
...value,
schema: OpenApiV3Downgrader.downgradeSchema(collection)(value.schema)
} ]))) : undefined
});
const downgradeContent = collection => record => Object.fromEntries(Object.entries(record).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, {
...value,
schema: value?.schema ? OpenApiV3Downgrader.downgradeSchema(collection)(value.schema) : undefined
} ])));
OpenApiV3Downgrader.downgradeComponents = input => {
const collection = {
original: input,
downgraded: {
securitySchemes: input.securitySchemes
}
};
if (input.schemas) {
collection.downgraded.schemas = {};
for (const [key, value] of Object.entries(input.schemas)) if (value !== undefined) collection.downgraded.schemas[key] = OpenApiV3Downgrader.downgradeSchema(collection)(value);
}
return collection;
};
OpenApiV3Downgrader.downgradeSchema = collection => input => {
const nullable = isNullable(new Set)(collection.original)(input);
const union = [];
const attribute = {
title: input.title,
description: input.description,
example: input.example,
examples: input.examples,
...Object.fromEntries(Object.entries(input).filter((([key, value]) => key.startsWith("x-") && value !== undefined)))
};
const visit = schema => {
if (OpenApiTypeChecker.isBoolean(schema)) union.push({
type: "boolean"
}); else if (OpenApiTypeChecker.isBoolean(schema) || OpenApiTypeChecker.isInteger(schema) || OpenApiTypeChecker.isNumber(schema) || OpenApiTypeChecker.isString(schema) || OpenApiTypeChecker.isReference(schema)) union.push({
...schema
}); else if (OpenApiTypeChecker.isArray(schema)) union.push({
...schema,
items: OpenApiV3Downgrader.downgradeSchema(collection)(schema.items)
}); else if (OpenApiTypeChecker.isTuple(schema)) union.push({
...schema,
items: (() => {
if (schema.additionalItems === true) return {};
const elements = [ ...schema.prefixItems, ...typeof schema.additionalItems === "object" ? [ OpenApiV3Downgrader.downgradeSchema(collection)(schema.additionalItems) ] : [] ];
if (elements.length === 0) return {};
return {
oneOf: elements.map(OpenApiV3Downgrader.downgradeSchema(collection))
};
})(),
minItems: schema.prefixItems.length,
maxItems: !!schema.additionalItems === true ? undefined : schema.prefixItems.length,
...{
prefixItems: undefined,
additionalItems: undefined
}
}); else if (OpenApiTypeChecker.isObject(schema)) union.push({
...schema,
properties: schema.properties ? Object.fromEntries(Object.entries(schema.properties).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, OpenApiV3Downgrader.downgradeSchema(collection)(value) ]))) : undefined,
additionalProperties: typeof schema.additionalProperties === "object" ? OpenApiV3Downgrader.downgradeSchema(collection)(schema.additionalProperties) : schema.additionalProperties,
required: schema.required
}); else if (OpenApiTypeChecker.isOneOf(schema)) schema.oneOf.forEach(visit);
};
const visitConstant = schema => {
const insert = value => {
const matched = union.find((u => u.type === typeof value));
if (matched !== undefined) {
matched.enum ?? (matched.enum = []);
matched.enum.push(value);
} else union.push({
type: typeof value,
enum: [ value ]
});
};
if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const); else if (OpenApiTypeChecker.isOneOf(schema)) for (const u of schema.oneOf) if (OpenApiTypeChecker.isConstant(u)) insert(u.const);
};
visit(input);
visitConstant(input);
if (nullable === true) for (const u of union) if (OpenApiTypeChecker.isReference(u)) downgradeNullableReference(new Set)(collection)(u); else u.nullable = true;
if (nullable === true && union.length === 0) return {
type: "null",
...attribute
};
return {
...union.length === 0 ? {
type: undefined
} : union.length === 1 ? {
...union[0]
} : {
oneOf: union
},
...attribute
};
};
const downgradeNullableReference = visited => collection => schema => {
var _a;
const key = schema.$ref.split("/").pop();
if (key.endsWith(".Nullable")) return;
const found = collection.original.schemas?.[key];
if (found === undefined) return; else if (isNullable(visited)(collection.original)(found) === true) return; else if (collection.downgraded.schemas?.[`${key}.Nullable`] === undefined) {
(_a = collection.downgraded).schemas ?? (_a.schemas = {});
collection.downgraded.schemas[`${key}.Nullable`] = {};
collection.downgraded.schemas[`${key}.Nullable`] = OpenApiV3Downgrader.downgradeSchema(collection)(OpenApiTypeChecker.isOneOf(found) ? {
...found,
oneOf: [ ...found.oneOf, {
type: "null"
} ]
} : {
oneOf: [ found, {
type: "null"
} ],
title: found.title,
description: found.description,
example: found.example,
examples: found.examples,
...Object.fromEntries(Object.entries(found).filter((([key, value]) => key.startsWith("x-") && value !== undefined)))
});
}
schema.$ref += ".Nullable";
};
const isNullable = visited => components => schema => {
if (OpenApiTypeChecker.isNull(schema)) return true; else if (OpenApiTypeChecker.isReference(schema)) {
if (visited.has(schema.$ref)) return false;
visited.add(schema.$ref);
const key = schema.$ref.split("/").pop();
const next = components.schemas?.[key];
return next ? isNullable(visited)(components)(next) : false;
}
return OpenApiTypeChecker.isOneOf(schema) && schema.oneOf.some(isNullable(visited)(components));
};
})(OpenApiV3Downgrader || (OpenApiV3Downgrader = {}));
export { OpenApiV3Downgrader };
//# sourceMappingURL=OpenApiV3Downgrader.mjs.map