UNPKG

@nestia/sdk

Version:

Nestia SDK and Swagger generator

178 lines (168 loc) 6.4 kB
import { OpenApi } from "@samchon/openapi"; import { VariadicSingleton } from "tstl"; import { IJsDocTagInfo, IJsonSchemaCollection } from "typia"; import { JsonSchemasProgrammer } from "typia/lib/programmers/json/JsonSchemasProgrammer"; import { INestiaConfig } from "../../INestiaConfig"; import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { SwaggerDescriptionComposer } from "./SwaggerDescriptionComposer"; export namespace SwaggerOperationParameterComposer { export interface IProps<Parameter extends ITypedHttpRouteParameter> { config: Omit<INestiaConfig.ISwaggerConfig, "output">; document: OpenApi.IDocument; schema: OpenApi.IJsonSchema; jsDocTags: IJsDocTagInfo[]; parameter: Parameter; } export const compose = ( props: IProps<ITypedHttpRouteParameter>, ): OpenApi.IOperation.IParameter[] => props.parameter.category === "body" ? [] : props.parameter.category === "param" ? [path({ ...props, parameter: props.parameter })] : props.parameter.category === "query" ? query({ ...props, parameter: props.parameter }) : header({ ...props, parameter: props.parameter }); export const body = ( props: Omit<IProps<ITypedHttpRouteParameter.IBody>, "config" | "document">, ): OpenApi.IOperation.IRequestBody => { const description: string | undefined = SwaggerDescriptionComposer.descriptionFromJsDocTag({ jsDocTags: props.jsDocTags, tag: "param", parameter: props.parameter.name, }); return { description: props.parameter.encrypted ? `${warning.get(!!description)}${description ?? ""}` : description, content: { [props.parameter.contentType]: { schema: props.schema, example: props.parameter.example, examples: props.parameter.examples, }, }, required: props.parameter.metadata.isRequired(), ...(props.parameter.encrypted ? { "x-nestia-encrypted": true } : {}), }; }; export const path = ( props: Omit<IProps<ITypedHttpRouteParameter.IParam>, "config" | "document">, ): OpenApi.IOperation.IParameter => ({ name: props.parameter.field, in: "path", schema: props.schema, required: props.parameter.metadata.isRequired(), ...SwaggerDescriptionComposer.compose({ description: props.parameter.description ?? props.parameter.jsDocTags.find((tag) => tag.name === "description") ?.text?.[0].text ?? props.jsDocTags .find( (tag) => tag.name === "param" && tag.text?.[0].text === props.parameter.name, ) ?.text?.map((e) => e.text) .join("") .substring(props.parameter.name.length) ?? null, jsDocTags: props.parameter.jsDocTags, kind: "title", }), example: props.parameter.example, examples: props.parameter.examples, }); export const query = ( props: IProps<ITypedHttpRouteParameter.IQuery>, ): OpenApi.IOperation.IParameter[] => decomposible(props); export const header = ( props: IProps<ITypedHttpRouteParameter.IHeaders>, ): OpenApi.IOperation.IParameter[] => decomposible(props); const decomposible = ( props: IProps< ITypedHttpRouteParameter.IHeaders | ITypedHttpRouteParameter.IQuery >, ): OpenApi.IOperation.IParameter[] => { const param: OpenApi.IOperation.IParameter = { name: props.parameter.field ?? props.parameter.name, in: props.parameter.category === "query" ? "query" : "header", schema: props.schema, ...SwaggerDescriptionComposer.compose({ description: props.parameter.description ?? props.parameter.jsDocTags.find((tag) => tag.name === "description") ?.text?.[0].text ?? props.jsDocTags .find( (tag) => tag.name === "param" && tag.text?.[0].text === props.parameter.name, ) ?.text?.map((e) => e.text) .join("") .substring(props.parameter.name.length) ?? null, jsDocTags: props.jsDocTags, kind: "title", }), required: props.parameter.metadata.isRequired(), example: props.parameter.example, examples: props.parameter.examples, }; if ( props.config.decompose === false || props.parameter.metadata.objects.length === 0 ) return [param]; return props.parameter.metadata.objects[0].type.properties .filter((p) => p.jsDocTags.every( (tag) => tag.name !== "hidden" && tag.name !== "ignore", ), ) .map((p) => { const json: IJsonSchemaCollection = JsonSchemasProgrammer.write({ version: "3.1", metadatas: [p.value], }) as IJsonSchemaCollection; if (Object.keys(json.components.schemas ?? {}).length !== 0) { props.document.components ??= {}; props.document.components.schemas ??= {}; Object.assign( props.document.components.schemas, json.components.schemas, ); } return { name: p.key.constants[0].values[0].value as string, in: props.parameter.category === "query" ? "query" : "header", schema: json.schemas[0], required: p.value.isRequired(), ...SwaggerDescriptionComposer.compose({ description: p.description ?? null, jsDocTags: p.jsDocTags, kind: "title", }), }; }); }; } const warning = new VariadicSingleton((described: boolean): string => { const summary = "Request body must be encrypted."; const component = "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)"; const content: string[] = [ "## Warning", "", summary, "", `The request body data would be encrypted as "AES-128(256) / CBC mode / PKCS#5 Padding / Base64 Encoding", through the ${component} component.`, "", `Therefore, just utilize this swagger editor only for referencing. If you need to call the real API, using [SDK](https://github.com/samchon/nestia#software-development-kit) would be much better.`, ]; if (described === true) content.push("", "----------------", "", ""); return content.join("\n"); });