@samchon/openapi
Advanced tools
Comparing version 0.3.4 to 0.4.0-dev.20240711
@@ -177,5 +177,8 @@ import { OpenApi } from "./OpenApi"; | ||
/** | ||
* Description comment for the path parameter. | ||
* Original parameter info from the OpenAPI document. | ||
* | ||
* The `parameter` is a function returning the original | ||
* {@link OpenApi.IOperation.IParameter} from the {@link OpenAPI} document. | ||
*/ | ||
description?: string; | ||
parameter: () => OpenApi.IOperation.IParameter<Schema>; | ||
} | ||
@@ -198,2 +201,4 @@ /** | ||
schema: Schema; | ||
title: () => string | undefined; | ||
description: () => string | undefined; | ||
} | ||
@@ -207,2 +212,4 @@ /** | ||
schema: Schema; | ||
title: () => string | undefined; | ||
description: () => string | undefined; | ||
} | ||
@@ -230,2 +237,6 @@ /** | ||
/** | ||
* Description comment for the request/response body. | ||
*/ | ||
description: () => string | undefined; | ||
/** | ||
* Whether the body is encrypted or not. | ||
@@ -240,10 +251,10 @@ */ | ||
/** | ||
* Description comment for the exception. | ||
* Metadata of response body data type. | ||
*/ | ||
description?: string; | ||
schema: Schema; | ||
/** | ||
* Metadata of response body data type. | ||
* Description comment for the exception. | ||
*/ | ||
schema: Schema; | ||
response: () => OpenApi.IOperation.IResponse<Schema>; | ||
} | ||
} |
@@ -10,3 +10,3 @@ "use strict"; | ||
MigrateRouteConverter.convert = (props) => { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j; | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; | ||
//---- | ||
@@ -56,4 +56,5 @@ // REQUEST AND RESPONSE BODY | ||
OpenApiTypeChecker_1.OpenApiTypeChecker.isArray(p.schema)); | ||
const out = (elem) => (Object.assign(Object.assign({}, elem), { name: type, key: type, title: () => elem.title, description: () => elem.description })); | ||
if (objects.length === 1 && primitives.length === 0) | ||
return objects[0]; | ||
return out(parameters[0]); | ||
else if (objects.length > 1) { | ||
@@ -96,27 +97,29 @@ failures.push(`${type} typed parameters must be only one object type`); | ||
? null | ||
: emplaceReference({ | ||
document: props.document, | ||
name: StringUtil_1.StringUtil.pascal(`I/Api/${props.path}`) + | ||
"." + | ||
StringUtil_1.StringUtil.pascal(`${props.method}/${type}`), | ||
schema: { | ||
type: "object", | ||
properties: Object.fromEntries([ | ||
...new Map(entire | ||
.map((o) => { | ||
var _a; | ||
return Object.entries((_a = o.properties) !== null && _a !== void 0 ? _a : {}).map(([name, schema]) => { | ||
: out({ | ||
schema: emplaceReference({ | ||
document: props.document, | ||
name: StringUtil_1.StringUtil.pascal(`I/Api/${props.path}`) + | ||
"." + | ||
StringUtil_1.StringUtil.pascal(`${props.method}/${type}`), | ||
schema: { | ||
type: "object", | ||
properties: Object.fromEntries([ | ||
...new Map(entire | ||
.map((o) => { | ||
var _a; | ||
return [ | ||
name, | ||
Object.assign(Object.assign({}, schema), { description: (_a = schema.description) !== null && _a !== void 0 ? _a : schema.description }), | ||
]; | ||
}); | ||
}) | ||
.flat()), | ||
]), | ||
required: [ | ||
...new Set(entire.map((o) => { var _a; return (_a = o.required) !== null && _a !== void 0 ? _a : []; }).flat()), | ||
], | ||
}, | ||
return Object.entries((_a = o.properties) !== null && _a !== void 0 ? _a : {}).map(([name, schema]) => { | ||
var _a; | ||
return [ | ||
name, | ||
Object.assign(Object.assign({}, schema), { description: (_a = schema.description) !== null && _a !== void 0 ? _a : schema.description }), | ||
]; | ||
}); | ||
}) | ||
.flat()), | ||
]), | ||
required: [ | ||
...new Set(entire.map((o) => { var _a; return (_a = o.required) !== null && _a !== void 0 ? _a : []; }).flat()), | ||
], | ||
}, | ||
}), | ||
}); | ||
@@ -151,2 +154,20 @@ }); | ||
return failures; | ||
const parameters = ((_h = props.operation.parameters) !== null && _h !== void 0 ? _h : []) | ||
.filter((p) => p.in === "path") | ||
.map((p, i) => ({ | ||
// FILL KEY NAME IF NOT EXISTsS | ||
name: parameterNames[i], | ||
key: (() => { | ||
let key = StringUtil_1.StringUtil.normalize(parameterNames[i]); | ||
if (Escaper_1.Escaper.variable(key)) | ||
return key; | ||
while (true) { | ||
key = "_" + key; | ||
if (!parameterNames.some((s) => s === key)) | ||
return key; | ||
} | ||
})(), | ||
schema: p.schema, | ||
parameter: () => p, | ||
})); | ||
return { | ||
@@ -157,56 +178,47 @@ method: props.method, | ||
accessor: ["@lazy"], | ||
headers: headers | ||
? { | ||
name: "headers", | ||
key: "headers", | ||
schema: headers, | ||
} | ||
: null, | ||
parameters: ((_h = props.operation.parameters) !== null && _h !== void 0 ? _h : []) | ||
parameters: ((_j = props.operation.parameters) !== null && _j !== void 0 ? _j : []) | ||
.filter((p) => p.in === "path") | ||
.map((p, i) => { | ||
var _a; | ||
return ({ | ||
// FILL KEY NAME IF NOT EXISTsS | ||
name: parameterNames[i], | ||
key: (() => { | ||
let key = StringUtil_1.StringUtil.normalize(parameterNames[i]); | ||
if (Escaper_1.Escaper.variable(key)) | ||
.map((p, i) => ({ | ||
// FILL KEY NAME IF NOT EXISTsS | ||
name: parameterNames[i], | ||
key: (() => { | ||
let key = StringUtil_1.StringUtil.normalize(parameterNames[i]); | ||
if (Escaper_1.Escaper.variable(key)) | ||
return key; | ||
while (true) { | ||
key = "_" + key; | ||
if (!parameterNames.some((s) => s === key)) | ||
return key; | ||
while (true) { | ||
key = "_" + key; | ||
if (!parameterNames.some((s) => s === key)) | ||
return key; | ||
} | ||
})(), | ||
schema: Object.assign(Object.assign({}, p.schema), { description: (_a = p.schema.description) !== null && _a !== void 0 ? _a : p.description }), | ||
}); | ||
}), | ||
query: query | ||
? { | ||
name: "query", | ||
key: "query", | ||
schema: query, | ||
} | ||
: null, | ||
} | ||
})(), | ||
schema: p.schema, | ||
parameter: () => p, | ||
})), | ||
headers: headers || null, | ||
query: query || null, | ||
body: body, | ||
success: success, | ||
exceptions: Object.fromEntries(Object.entries((_j = props.operation.responses) !== null && _j !== void 0 ? _j : {}) | ||
exceptions: Object.fromEntries(Object.entries((_k = props.operation.responses) !== null && _k !== void 0 ? _k : {}) | ||
.filter(([key]) => key !== "200" && key !== "201" && key !== "default") | ||
.map(([key, value]) => { | ||
.map(([status, response]) => { | ||
var _a, _b, _c; | ||
return [ | ||
key, | ||
status, | ||
{ | ||
description: value.description, | ||
schema: (_c = (_b = (_a = value.content) === null || _a === void 0 ? void 0 : _a["application/json"]) === null || _b === void 0 ? void 0 : _b.schema) !== null && _c !== void 0 ? _c : {}, | ||
schema: (_c = (_b = (_a = response.content) === null || _a === void 0 ? void 0 : _a["application/json"]) === null || _b === void 0 ? void 0 : _b.schema) !== null && _c !== void 0 ? _c : {}, | ||
response: () => response, | ||
}, | ||
]; | ||
})), | ||
comment: () => writeDescription(props.operation), | ||
comment: () => writeRouteComment({ | ||
operation: props.operation, | ||
parameters, | ||
query: query || null, | ||
body: body || null, | ||
}), | ||
operation: () => props.operation, | ||
}; | ||
}; | ||
const writeDescription = (original) => { | ||
var _a, _b, _c, _d; | ||
const writeRouteComment = (props) => { | ||
var _a, _b, _c, _d, _e, _f; | ||
const commentTags = []; | ||
@@ -217,21 +229,30 @@ const add = (text) => { | ||
}; | ||
let description = (_a = original.description) !== null && _a !== void 0 ? _a : ""; | ||
if (original.summary) { | ||
const emended = original.summary.endsWith(".") | ||
? original.summary | ||
: original.summary + "."; | ||
if (!!description.length && !description.startsWith(original.summary)) | ||
let description = (_a = props.operation.description) !== null && _a !== void 0 ? _a : ""; | ||
if (props.operation.summary) { | ||
const emended = props.operation.summary.endsWith(".") | ||
? props.operation.summary | ||
: props.operation.summary + "."; | ||
if (!!description.length && | ||
!description.startsWith(props.operation.summary)) | ||
description = `${emended}\n${description}`; | ||
} | ||
for (const p of (_b = original.parameters) !== null && _b !== void 0 ? _b : []) | ||
if (p.description) | ||
add(`@param ${p.name} ${p.description}`); | ||
if ((_c = original.requestBody) === null || _c === void 0 ? void 0 : _c.description) | ||
add(`@param body ${original.requestBody.description}`); | ||
for (const security of (_d = original.security) !== null && _d !== void 0 ? _d : []) | ||
description = description | ||
.split("\n") | ||
.map((s) => s.trim()) | ||
.join("\n"); | ||
for (const p of (_b = props.parameters) !== null && _b !== void 0 ? _b : []) { | ||
const param = p.parameter(); | ||
if (param.description || param.title) { | ||
const text = ((_c = param.description) !== null && _c !== void 0 ? _c : param.title); | ||
add(`@param ${p.name} ${writeIndented(text, p.name.length + 8)}`); | ||
} | ||
} | ||
if ((_e = (_d = props.body) === null || _d === void 0 ? void 0 : _d.description()) === null || _e === void 0 ? void 0 : _e.length) | ||
add(`@param body ${writeIndented(props.body.description(), 12)}`); | ||
for (const security of (_f = props.operation.security) !== null && _f !== void 0 ? _f : []) | ||
for (const [name, scopes] of Object.entries(security)) | ||
add(`@security ${[name, ...scopes].join("")}`); | ||
if (original.tags) | ||
original.tags.forEach((name) => add(`@tag ${name}`)); | ||
if (original.deprecated) | ||
if (props.operation.tags) | ||
props.operation.tags.forEach((name) => add(`@tag ${name}`)); | ||
if (props.operation.deprecated) | ||
add("@deprecated"); | ||
@@ -244,2 +265,7 @@ return description.length | ||
}; | ||
const writeIndented = (text, spaces) => text | ||
.split("\n") | ||
.map((s) => s.trim()) | ||
.map((s, i) => (i === 0 ? s : `${" ".repeat(spaces)}${s}`)) | ||
.join("\n"); | ||
const emplaceBodySchema = (from) => (emplacer) => (meta) => { | ||
@@ -264,2 +290,3 @@ if (!(meta === null || meta === void 0 ? void 0 : meta.content)) | ||
"x-nestia-encrypted": meta["x-nestia-encrypted"], | ||
description: () => meta.description, | ||
}; | ||
@@ -279,2 +306,3 @@ } | ||
: {}, | ||
description: () => meta.description, | ||
}; | ||
@@ -289,2 +317,3 @@ } | ||
schema: { type: "string" }, | ||
description: () => meta.description, | ||
}; | ||
@@ -304,2 +333,3 @@ if (from === "request") { | ||
: {}, | ||
description: () => meta.description, | ||
}; | ||
@@ -306,0 +336,0 @@ } |
{ | ||
"name": "@samchon/openapi", | ||
"version": "0.3.4", | ||
"version": "0.4.0-dev.20240711", | ||
"description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -40,3 +40,3 @@ # `@samchon/openapi` | ||
Additionally, `@samchon/openapi` provides [`IMigrateDocument`](https://github.com/samchon/openapi/blob/master/src/IMigrateDocument.ts) for OpenAPI generators. | ||
Additionally, `@samchon/openapi` provides [`IMigrateDocument`](https://github.com/samchon/openapi/blob/master/src/IMigrateDocument.ts) for OpenAPI generators. If you're developing TypeScript, [`@nestia/editor`](https://nestia.io/docs/editor) would be the best project utilizing the [`IMigrateDocument`](https://github.com/samchon/openapi/blob/master/src/IMigrateDocument.ts) for the OpenAPI SDK generation. Otherwise, you wanna utilize OpenAPI document for OpenAI function calling, [`@wrtnio/openai-function-schema`](https://github.com/wrtnio/openai-function-schema/) has been prepared for you. | ||
@@ -43,0 +43,0 @@ |
@@ -197,5 +197,8 @@ import { OpenApi } from "./OpenApi"; | ||
/** | ||
* Description comment for the path parameter. | ||
* Original parameter info from the OpenAPI document. | ||
* | ||
* The `parameter` is a function returning the original | ||
* {@link OpenApi.IOperation.IParameter} from the {@link OpenAPI} document. | ||
*/ | ||
description?: string; | ||
parameter: () => OpenApi.IOperation.IParameter<Schema>; | ||
} | ||
@@ -223,2 +226,4 @@ | ||
schema: Schema; | ||
title: () => string | undefined; | ||
description: () => string | undefined; | ||
} | ||
@@ -235,2 +240,4 @@ | ||
schema: Schema; | ||
title: () => string | undefined; | ||
description: () => string | undefined; | ||
} | ||
@@ -269,2 +276,7 @@ | ||
/** | ||
* Description comment for the request/response body. | ||
*/ | ||
description: () => string | undefined; | ||
/** | ||
* Whether the body is encrypted or not. | ||
@@ -282,11 +294,11 @@ */ | ||
/** | ||
* Description comment for the exception. | ||
* Metadata of response body data type. | ||
*/ | ||
description?: string; | ||
schema: Schema; | ||
/** | ||
* Metadata of response body data type. | ||
* Description comment for the exception. | ||
*/ | ||
schema: Schema; | ||
response: () => OpenApi.IOperation.IResponse<Schema>; | ||
} | ||
} |
@@ -85,3 +85,17 @@ import { IMigrateRoute } from "../IMigrateRoute"; | ||
); | ||
if (objects.length === 1 && primitives.length === 0) return objects[0]; | ||
const out = (elem: { | ||
schema: OpenApi.IJsonSchema; | ||
title?: string; | ||
description?: string; | ||
}) => | ||
({ | ||
...elem, | ||
name: type, | ||
key: type, | ||
title: () => elem.title, | ||
description: () => elem.description, | ||
}) satisfies IMigrateRoute.IHeaders; | ||
if (objects.length === 1 && primitives.length === 0) | ||
return out(parameters[0]); | ||
else if (objects.length > 1) { | ||
@@ -131,33 +145,35 @@ failures.push(`${type} typed parameters must be only one object type`); | ||
? null | ||
: emplaceReference({ | ||
document: props.document, | ||
name: | ||
StringUtil.pascal(`I/Api/${props.path}`) + | ||
"." + | ||
StringUtil.pascal(`${props.method}/${type}`), | ||
schema: { | ||
type: "object", | ||
properties: Object.fromEntries([ | ||
...new Map<string, OpenApi.IJsonSchema>( | ||
entire | ||
.map((o) => | ||
Object.entries(o.properties ?? {}).map( | ||
([name, schema]) => | ||
[ | ||
name, | ||
{ | ||
...schema, | ||
description: | ||
schema.description ?? schema.description, | ||
} as OpenApi.IJsonSchema, | ||
] as const, | ||
), | ||
) | ||
.flat(), | ||
), | ||
]), | ||
required: [ | ||
...new Set(entire.map((o) => o.required ?? []).flat()), | ||
], | ||
}, | ||
: out({ | ||
schema: emplaceReference({ | ||
document: props.document, | ||
name: | ||
StringUtil.pascal(`I/Api/${props.path}`) + | ||
"." + | ||
StringUtil.pascal(`${props.method}/${type}`), | ||
schema: { | ||
type: "object", | ||
properties: Object.fromEntries([ | ||
...new Map<string, OpenApi.IJsonSchema>( | ||
entire | ||
.map((o) => | ||
Object.entries(o.properties ?? {}).map( | ||
([name, schema]) => | ||
[ | ||
name, | ||
{ | ||
...schema, | ||
description: | ||
schema.description ?? schema.description, | ||
} as OpenApi.IJsonSchema, | ||
] as const, | ||
), | ||
) | ||
.flat(), | ||
), | ||
]), | ||
required: [ | ||
...new Set(entire.map((o) => o.required ?? []).flat()), | ||
], | ||
}, | ||
}), | ||
}); | ||
@@ -205,2 +221,20 @@ }); | ||
const parameters: IMigrateRoute.IParameter[] = ( | ||
props.operation.parameters ?? [] | ||
) | ||
.filter((p) => p.in === "path") | ||
.map((p, i) => ({ | ||
// FILL KEY NAME IF NOT EXISTsS | ||
name: parameterNames[i], | ||
key: (() => { | ||
let key: string = StringUtil.normalize(parameterNames[i]); | ||
if (Escaper.variable(key)) return key; | ||
while (true) { | ||
key = "_" + key; | ||
if (!parameterNames.some((s) => s === key)) return key; | ||
} | ||
})(), | ||
schema: p.schema, | ||
parameter: () => p, | ||
})); | ||
return { | ||
@@ -211,9 +245,2 @@ method: props.method, | ||
accessor: ["@lazy"], | ||
headers: headers | ||
? { | ||
name: "headers", | ||
key: "headers", | ||
schema: headers, | ||
} | ||
: null, | ||
parameters: (props.operation.parameters ?? []) | ||
@@ -232,14 +259,7 @@ .filter((p) => p.in === "path") | ||
})(), | ||
schema: { | ||
...p!.schema, | ||
description: p!.schema.description ?? p!.description, | ||
}, | ||
schema: p.schema, | ||
parameter: () => p, | ||
})), | ||
query: query | ||
? { | ||
name: "query", | ||
key: "query", | ||
schema: query, | ||
} | ||
: null, | ||
headers: headers || null, | ||
query: query || null, | ||
body: body as IMigrateRoute.IBody | null, | ||
@@ -252,11 +272,17 @@ success: success as IMigrateRoute.IBody | null, | ||
) | ||
.map(([key, value]) => [ | ||
key, | ||
.map(([status, response]) => [ | ||
status, | ||
{ | ||
description: value.description, | ||
schema: value.content?.["application/json"]?.schema ?? {}, | ||
schema: response.content?.["application/json"]?.schema ?? {}, | ||
response: () => response, | ||
}, | ||
]), | ||
), | ||
comment: () => writeDescription(props.operation), | ||
comment: () => | ||
writeRouteComment({ | ||
operation: props.operation, | ||
parameters, | ||
query: query || null, | ||
body: body || null, | ||
}), | ||
operation: () => props.operation, | ||
@@ -266,3 +292,8 @@ }; | ||
const writeDescription = (original: OpenApi.IOperation): string => { | ||
const writeRouteComment = (props: { | ||
operation: OpenApi.IOperation; | ||
parameters: IMigrateRoute.IParameter[]; | ||
query: IMigrateRoute.IQuery | null; | ||
body: IMigrateRoute.IBody | null; | ||
}): string => { | ||
const commentTags: string[] = []; | ||
@@ -273,19 +304,33 @@ const add = (text: string) => { | ||
let description: string = original.description ?? ""; | ||
if (original.summary) { | ||
const emended: string = original.summary.endsWith(".") | ||
? original.summary | ||
: original.summary + "."; | ||
if (!!description.length && !description.startsWith(original.summary)) | ||
let description: string = props.operation.description ?? ""; | ||
if (props.operation.summary) { | ||
const emended: string = props.operation.summary.endsWith(".") | ||
? props.operation.summary | ||
: props.operation.summary + "."; | ||
if ( | ||
!!description.length && | ||
!description.startsWith(props.operation.summary) | ||
) | ||
description = `${emended}\n${description}`; | ||
} | ||
for (const p of original.parameters ?? []) | ||
if (p.description) add(`@param ${p.name} ${p.description}`); | ||
if (original.requestBody?.description) | ||
add(`@param body ${original.requestBody.description}`); | ||
for (const security of original.security ?? []) | ||
description = description | ||
.split("\n") | ||
.map((s) => s.trim()) | ||
.join("\n"); | ||
for (const p of props.parameters ?? []) { | ||
const param = p.parameter(); | ||
if (param.description || param.title) { | ||
const text: string = (param.description ?? param.title)!; | ||
add(`@param ${p.name} ${writeIndented(text, p.name.length + 8)}`); | ||
} | ||
} | ||
if (props.body?.description()?.length) | ||
add(`@param body ${writeIndented(props.body.description()!, 12)}`); | ||
for (const security of props.operation.security ?? []) | ||
for (const [name, scopes] of Object.entries(security)) | ||
add(`@security ${[name, ...scopes].join("")}`); | ||
if (original.tags) original.tags.forEach((name) => add(`@tag ${name}`)); | ||
if (original.deprecated) add("@deprecated"); | ||
if (props.operation.tags) | ||
props.operation.tags.forEach((name) => add(`@tag ${name}`)); | ||
if (props.operation.deprecated) add("@deprecated"); | ||
return description.length | ||
@@ -298,2 +343,9 @@ ? commentTags.length | ||
const writeIndented = (text: string, spaces: number): string => | ||
text | ||
.split("\n") | ||
.map((s) => s.trim()) | ||
.map((s, i) => (i === 0 ? s : `${" ".repeat(spaces)}${s}`)) | ||
.join("\n"); | ||
const emplaceBodySchema = | ||
@@ -334,2 +386,3 @@ (from: "request" | "response") => | ||
"x-nestia-encrypted": meta["x-nestia-encrypted"], | ||
description: () => meta.description, | ||
}; | ||
@@ -352,2 +405,3 @@ } | ||
: {}, | ||
description: () => meta.description, | ||
}; | ||
@@ -363,2 +417,3 @@ } | ||
schema: { type: "string" }, | ||
description: () => meta.description, | ||
}; | ||
@@ -381,2 +436,3 @@ | ||
: {}, | ||
description: () => meta.description, | ||
}; | ||
@@ -383,0 +439,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
707780
11288