@samchon/openapi
Version:
OpenAPI definitions and converters for 'typia' and 'nestia'.
341 lines (337 loc) • 17.8 kB
JavaScript
import { OpenApiExclusiveEmender } from "../utils/OpenApiExclusiveEmender.mjs";
var OpenApiV3_1Emender;
(function(OpenApiV3_1Emender) {
OpenApiV3_1Emender.convert = input => {
if (input["x-samchon-emended-v4"] === true) return input;
return {
...input,
components: convertComponents(input.components ?? {}),
paths: input.paths ? Object.fromEntries(Object.entries(input.paths).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, convertPathItem(input)(value) ]))) : undefined,
webhooks: input.webhooks ? Object.fromEntries(Object.entries(input.webhooks).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, convertWebhooks(input)(value) ])).filter((([_, value]) => value !== undefined))) : undefined,
"x-samchon-emended-v4": true
};
};
const convertWebhooks = doc => webhook => {
if (!TypeChecker.isReference(webhook)) return convertPathItem(doc)(webhook);
const found = doc.components?.pathItems?.[webhook.$ref.split("/").pop() ?? ""];
return found ? convertPathItem(doc)(found) : undefined;
};
const convertPathItem = doc => pathItem => ({
...pathItem,
...pathItem.get ? {
get: convertOperation(doc)(pathItem)(pathItem.get)
} : undefined,
...pathItem.put ? {
put: convertOperation(doc)(pathItem)(pathItem.put)
} : undefined,
...pathItem.post ? {
post: convertOperation(doc)(pathItem)(pathItem.post)
} : undefined,
...pathItem.delete ? {
delete: convertOperation(doc)(pathItem)(pathItem.delete)
} : undefined,
...pathItem.options ? {
options: convertOperation(doc)(pathItem)(pathItem.options)
} : undefined,
...pathItem.head ? {
head: convertOperation(doc)(pathItem)(pathItem.head)
} : undefined,
...pathItem.patch ? {
patch: convertOperation(doc)(pathItem)(pathItem.patch)
} : undefined,
...pathItem.trace ? {
trace: convertOperation(doc)(pathItem)(pathItem.trace)
} : undefined
});
const convertOperation = doc => pathItem => input => ({
...input,
parameters: pathItem.parameters !== undefined || input.parameters !== undefined ? [ ...pathItem.parameters ?? [], ...input.parameters ?? [] ].map((p => {
if (!TypeChecker.isReference(p)) return convertParameter(doc.components ?? {})(p);
const found = p.$ref.startsWith("#/components/headers/") ? doc.components?.headers?.[p.$ref.split("/").pop() ?? ""] : doc.components?.parameters?.[p.$ref.split("/").pop() ?? ""];
return found !== undefined ? convertParameter(doc.components ?? {})({
...found,
in: "header"
}) : undefined;
})).filter(((_, v) => v !== undefined)) : undefined,
requestBody: input.requestBody ? convertRequestBody(doc)(input.requestBody) : undefined,
responses: input.responses ? Object.fromEntries(Object.entries(input.responses).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, convertResponse(doc)(value) ])).filter((([_, value]) => value !== undefined))) : undefined
});
const convertParameter = components => input => ({
...input,
schema: convertSchema(components)(input.schema),
examples: input.examples ? Object.fromEntries(Object.entries(input.examples).map((([key, value]) => [ key, TypeChecker.isReference(value) ? components.examples?.[value.$ref.split("/").pop() ?? ""] : value ])).filter((([_, v]) => v !== undefined))) : undefined
});
const convertRequestBody = doc => input => {
if (TypeChecker.isReference(input)) {
const found = doc.components?.requestBodies?.[input.$ref.split("/").pop() ?? ""];
if (found === undefined) return undefined;
input = found;
}
return {
...input,
content: input.content ? convertContent(doc.components ?? {})(input.content) : undefined
};
};
const convertResponse = doc => input => {
if (TypeChecker.isReference(input)) {
const found = doc.components?.responses?.[input.$ref.split("/").pop() ?? ""];
if (found === undefined) return undefined;
input = found;
}
return {
...input,
content: input.content ? convertContent(doc.components ?? {})(input.content) : undefined,
headers: input.headers ? Object.fromEntries(Object.entries(input.headers).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, (() => {
if (TypeChecker.isReference(value) === false) return convertParameter(doc.components ?? {})({
...value,
in: "header"
});
const found = value.$ref.startsWith("#/components/headers/") ? doc.components?.headers?.[value.$ref.split("/").pop() ?? ""] : undefined;
return found !== undefined ? convertParameter(doc.components ?? {})({
...found,
in: "header"
}) : undefined;
})() ])).filter((([_, v]) => v !== undefined))) : undefined
};
};
const convertContent = components => record => Object.fromEntries(Object.entries(record).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, {
...value,
schema: value.schema ? convertSchema(components)(value.schema) : undefined,
examples: value.examples ? Object.fromEntries(Object.entries(value.examples).map((([key, value]) => [ key, TypeChecker.isReference(value) ? components.examples?.[value.$ref.split("/").pop() ?? ""] : value ])).filter((([_, v]) => v !== undefined))) : undefined
} ])));
const convertComponents = input => ({
schemas: Object.fromEntries(Object.entries(input.schemas ?? {}).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, convertSchema(input)(value) ]))),
securitySchemes: input.securitySchemes
});
const convertSchema = components => input => {
const union = [];
const attribute = {
title: input.title,
description: input.description,
...Object.fromEntries(Object.entries(input).filter((([key, value]) => key.startsWith("x-") && value !== undefined))),
examples: Array.isArray(input.examples) ? Object.fromEntries(input.examples.map(((v, i) => [ `v${i}`, v ]))) : input.examples
};
const nullable = {
value: false,
default: undefined
};
const visit = schema => {
if (schema.nullable === true) {
nullable.value || (nullable.value = true);
if (schema.default === null) nullable.default = null;
}
if (Array.isArray(schema.enum) && schema.enum?.length && schema.enum?.some((e => e === null))) nullable.value || (nullable.value = true);
if (TypeChecker.isMixed(schema)) {
if (schema.const !== undefined) visit({
...schema,
...{
type: undefined,
oneOf: undefined,
anyOf: undefined,
allOf: undefined,
$ref: undefined
}
});
if (schema.oneOf !== undefined) visit({
...schema,
...{
type: undefined,
anyOf: undefined,
allOf: undefined,
$ref: undefined
}
});
if (schema.anyOf !== undefined) visit({
...schema,
...{
type: undefined,
oneOf: undefined,
allOf: undefined,
$ref: undefined
}
});
if (schema.allOf !== undefined) visit({
...schema,
...{
type: undefined,
oneOf: undefined,
anyOf: undefined,
$ref: undefined
}
});
for (const type of schema.type) if (type === "boolean" || type === "number" || type === "string") visit({
...schema,
...{
enum: schema.enum?.length && schema.enum.filter((e => e !== null)) ? schema.enum.filter((x => typeof x === type)) : undefined
},
type
}); else if (type === "integer") visit({
...schema,
...{
enum: schema.enum?.length && schema.enum.filter((e => e !== null)) ? schema.enum.filter((x => x !== null && typeof x === "number" && Number.isInteger(x))) : undefined
},
type
}); else visit({
...schema,
type
});
} else if (TypeChecker.isOneOf(schema)) schema.oneOf.forEach(visit); else if (TypeChecker.isAnyOf(schema)) schema.anyOf.forEach(visit); else if (TypeChecker.isAllOf(schema)) if (schema.allOf.length === 1) visit(schema.allOf[0]); else union.push(convertAllOfSchema(components)(schema)); else if (TypeChecker.isBoolean(schema)) if (schema.enum?.length && schema.enum.filter((e => e !== null)).length) for (const value of schema.enum.filter((e => e !== null))) union.push({
const: value,
...{
...schema,
type: undefined,
enum: undefined,
default: undefined
}
}); else union.push({
...schema,
default: schema.default ?? undefined,
...{
enum: undefined
}
}); else if (TypeChecker.isInteger(schema) || TypeChecker.isNumber(schema)) if (schema.enum?.length && schema.enum.filter((e => e !== null))) for (const value of schema.enum.filter((e => e !== null))) union.push({
const: value,
...{
...schema,
type: undefined,
enum: undefined,
default: undefined,
minimum: undefined,
maximum: undefined,
exclusiveMinimum: undefined,
exclusiveMaximum: undefined,
multipleOf: undefined
}
}); else union.push(OpenApiExclusiveEmender.emend({
...schema,
default: schema.default ?? undefined,
...{
enum: undefined
},
exclusiveMinimum: typeof schema.exclusiveMinimum === "boolean" ? schema.exclusiveMinimum === true ? schema.minimum : undefined : schema.exclusiveMinimum,
exclusiveMaximum: typeof schema.exclusiveMaximum === "boolean" ? schema.exclusiveMaximum === true ? schema.maximum : undefined : schema.exclusiveMaximum,
minimum: schema.exclusiveMinimum === true ? undefined : schema.minimum,
maximum: schema.exclusiveMaximum === true ? undefined : schema.maximum
})); else if (TypeChecker.isString(schema)) if (schema.enum?.length && schema.enum.filter((e => e !== null)).length) for (const value of schema.enum.filter((e => e !== null))) union.push({
const: value,
...{
...schema,
type: undefined,
enum: undefined,
default: undefined
}
}); else union.push({
...schema,
default: schema.default ?? undefined,
...{
enum: undefined
}
}); else if (TypeChecker.isArray(schema)) {
if (Array.isArray(schema.items)) union.push({
...schema,
...{
items: undefined,
prefixItems: schema.items.map(convertSchema(components)),
additionalItems: typeof schema.additionalItems === "object" && schema.additionalItems !== null ? convertSchema(components)(schema.additionalItems) : schema.additionalItems
}
}); else if (Array.isArray(schema.prefixItems)) union.push({
...schema,
...{
items: undefined,
prefixItems: schema.prefixItems.map(convertSchema(components)),
additionalItems: typeof schema.additionalItems === "object" && schema.additionalItems !== null ? convertSchema(components)(schema.additionalItems) : schema.additionalItems
}
}); else if (schema.items === undefined) union.push({
...schema,
...{
items: undefined,
prefixItems: []
}
}); else union.push({
...schema,
...{
items: convertSchema(components)(schema.items),
prefixItems: undefined,
additionalItems: undefined
}
});
} else if (TypeChecker.isObject(schema)) union.push({
...schema,
...{
properties: schema.properties ? Object.fromEntries(Object.entries(schema.properties).filter((([_, v]) => v !== undefined)).map((([key, value]) => [ key, convertSchema(components)(value) ]))) : {},
additionalProperties: schema.additionalProperties ? typeof schema.additionalProperties === "object" && schema.additionalProperties !== null ? convertSchema(components)(schema.additionalProperties) : schema.additionalProperties : undefined,
required: schema.required ?? []
}
}); else if (TypeChecker.isRecursiveReference(schema)) union.push({
...schema,
...{
$ref: schema.$recursiveRef,
$recursiveRef: undefined
}
}); else union.push(schema);
};
visit(input);
if (nullable.value === true && !union.some((e => e.type === "null"))) union.push({
type: "null",
default: nullable.default
});
return {
...union.length === 0 ? {
type: undefined
} : union.length === 1 ? {
...union[0]
} : {
oneOf: union.map((u => ({
...u,
nullable: undefined
})))
},
...attribute,
...{
nullable: undefined
}
};
};
const convertAllOfSchema = components => input => {
const objects = input.allOf.map((schema => retrieveObject(components)(schema)));
if (objects.some((obj => obj === null))) return {
type: undefined,
...{
allOf: undefined
}
};
return {
...input,
type: "object",
properties: Object.fromEntries(objects.map((o => Object.entries(o?.properties ?? {}))).flat().map((([key, value]) => [ key, convertSchema(components)(value) ]))),
...{
allOf: undefined,
required: [ ...new Set(objects.map((o => o?.required ?? [])).flat()) ]
}
};
};
const retrieveObject = components => (input, visited = new Set) => {
if (TypeChecker.isObject(input)) return input.properties !== undefined && !input.additionalProperties ? input : null; else if (visited.has(input)) return null; else visited.add(input);
if (TypeChecker.isReference(input)) return retrieveObject(components)(components.schemas?.[input.$ref.split("/").pop() ?? ""] ?? {}, visited); else if (TypeChecker.isRecursiveReference(input)) return retrieveObject(components)(components.schemas?.[input.$recursiveRef.split("/").pop() ?? ""] ?? {}, visited);
return null;
};
let TypeChecker;
(function(TypeChecker) {
TypeChecker.isConstant = schema => schema.const !== undefined;
TypeChecker.isBoolean = schema => schema.type === "boolean";
TypeChecker.isInteger = schema => schema.type === "integer";
TypeChecker.isNumber = schema => schema.type === "number";
TypeChecker.isString = schema => schema.type === "string";
TypeChecker.isArray = schema => schema.type === "array";
TypeChecker.isObject = schema => schema.type === "object";
TypeChecker.isReference = schema => schema.$ref !== undefined;
TypeChecker.isRecursiveReference = schema => schema.$recursiveRef !== undefined;
TypeChecker.isAllOf = schema => schema.allOf !== undefined;
TypeChecker.isAnyOf = schema => schema.anyOf !== undefined;
TypeChecker.isOneOf = schema => schema.oneOf !== undefined;
TypeChecker.isNullOnly = schema => schema.type === "null";
TypeChecker.isMixed = schema => Array.isArray(schema.type);
})(TypeChecker || (TypeChecker = {}));
})(OpenApiV3_1Emender || (OpenApiV3_1Emender = {}));
export { OpenApiV3_1Emender };
//# sourceMappingURL=OpenApiV3_1Emender.mjs.map