From 92a27437abfc5a1ebb8e316e15d2f0c42bcef7d7 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Fri, 9 Aug 2024 23:29:27 +0900 Subject: [PATCH 01/21] Developing #980: to boost up `@nestia/sdk` performance. --- packages/core/package.json | 2 +- .../core/src/decorators/TypedException.ts | 52 +-- .../programmers/TypedExceptionProgrammer.ts | 71 ---- .../src/transformers/MethodTransformer.ts | 7 +- .../transformers/TypedExceptionTransformer.ts | 44 -- packages/sdk/package.json | 4 +- packages/sdk/src/NestiaSdkApplication.ts | 29 +- packages/sdk/src/analyses/ConfigAnalyzer.ts | 142 +++---- packages/sdk/src/analyses/GenericAnalyzer.ts | 4 +- packages/sdk/src/analyses/ImportAnalyzer.ts | 239 +++++------ .../src/analyses/ReflectControllerAnalyzer.ts | 174 +++----- .../analyses/ReflectHttpOperationAnalyzer.ts | 303 +++++++------- .../ReflectHttpOperationParameterAnalyzer.ts | 251 ++++++++++++ .../ReflectHttpOperationResponseAnalyzer.ts | 92 +++++ .../src/analyses/ReflectMetadataAnalyzer.ts | 19 +- .../ReflectWebSocketOperationAnalyzer.ts | 215 ++++++---- .../src/analyses/TypedControllerAnalyzer.ts | 92 ----- .../analyses/TypedHttpOperationAnalyzer.ts | 365 ----------------- .../TypedWebSocketOperationAnalyzer.ts | 375 ------------------ .../sdk/src/decorators/OperationMetadata.ts | 15 + .../executable/internal/NestiaConfigLoader.ts | 12 +- .../generates/internal/SdkAliasCollection.ts | 2 +- packages/sdk/src/structures/INestiaProject.ts | 4 +- .../sdk/src/structures/INestiaSdkInput.ts | 25 ++ .../sdk/src/structures/INormalizedInput.ts | 20 - .../sdk/src/structures/IOperationMetadata.ts | 34 ++ .../sdk/src/structures/IReflectController.ts | 8 +- .../src/structures/IReflectHttpOperation.ts | 86 +--- .../IReflectHttpOperationException.ts | 17 + .../IReflectHttpOperationParameter.ts | 76 ++++ .../IReflectHttpOperationSuccess.ts | 20 + packages/sdk/src/structures/IReflectType.ts | 4 + .../sdk/src/structures/IReflectTypeImport.ts | 4 + .../structures/IReflectWebSocketOperation.ts | 18 +- .../IReflectWebSocketOperationParameter.ts | 32 ++ packages/sdk/src/structures/ITypeTuple.ts | 6 - .../sdk/src/structures/ITypedHttpRoute.ts | 59 --- .../src/structures/ITypedWebSocketRoute.ts | 68 ---- packages/sdk/src/transform.ts | 9 + .../transformers/ISdkTransformerContext.ts | 8 + .../src/transformers/SdkMetadataProgrammer.ts | 205 ++++++++++ .../sdk/src/transformers/SdkTransformer.ts | 168 ++++++++ packages/sdk/src/utils/StringUtil.ts | 3 + test/features/app/swagger.json | 2 +- test/features/app/tsconfig.json | 5 +- test/package.json | 6 +- 46 files changed, 1548 insertions(+), 1848 deletions(-) delete mode 100644 packages/core/src/programmers/TypedExceptionProgrammer.ts delete mode 100644 packages/core/src/transformers/TypedExceptionTransformer.ts create mode 100644 packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts create mode 100644 packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts delete mode 100644 packages/sdk/src/analyses/TypedControllerAnalyzer.ts delete mode 100644 packages/sdk/src/analyses/TypedHttpOperationAnalyzer.ts delete mode 100644 packages/sdk/src/analyses/TypedWebSocketOperationAnalyzer.ts create mode 100644 packages/sdk/src/decorators/OperationMetadata.ts create mode 100644 packages/sdk/src/structures/INestiaSdkInput.ts delete mode 100644 packages/sdk/src/structures/INormalizedInput.ts create mode 100644 packages/sdk/src/structures/IOperationMetadata.ts create mode 100644 packages/sdk/src/structures/IReflectHttpOperationException.ts create mode 100644 packages/sdk/src/structures/IReflectHttpOperationParameter.ts create mode 100644 packages/sdk/src/structures/IReflectHttpOperationSuccess.ts create mode 100644 packages/sdk/src/structures/IReflectType.ts create mode 100644 packages/sdk/src/structures/IReflectTypeImport.ts create mode 100644 packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts delete mode 100644 packages/sdk/src/structures/ITypeTuple.ts delete mode 100644 packages/sdk/src/structures/ITypedHttpRoute.ts delete mode 100644 packages/sdk/src/structures/ITypedWebSocketRoute.ts create mode 100644 packages/sdk/src/transform.ts create mode 100644 packages/sdk/src/transformers/ISdkTransformerContext.ts create mode 100644 packages/sdk/src/transformers/SdkMetadataProgrammer.ts create mode 100644 packages/sdk/src/transformers/SdkTransformer.ts diff --git a/packages/core/package.json b/packages/core/package.json index 11b5bda97..42da7bc62 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^3.10.0", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.10.0.tgz", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^0.4.3", diff --git a/packages/core/src/decorators/TypedException.ts b/packages/core/src/decorators/TypedException.ts index 3bc87ad3b..a6851b0ac 100644 --- a/packages/core/src/decorators/TypedException.ts +++ b/packages/core/src/decorators/TypedException.ts @@ -10,16 +10,12 @@ * but only affects to the swagger documents generation. Also, it does not affect to * the SDK library generation yet, but will be used in the future. * - * @param status Status number or pattern like "2XX", "3XX", "4XX", "5XX" - * @param description Description about the exception + * @param props Properties for the exception * @returns Method decorator * * @author Jeongho Nam - https://github.com/samchon */ -export function TypedException( - status: number | "2XX" | "3XX" | "4XX" | "5XX", - description?: string | undefined, -): never; +export function TypedException(props: TypedException.IProps): never; /** * Exception decorator. @@ -32,38 +28,35 @@ export function TypedException( * the SDK library generation yet, but will be used in the future. * * @template T Type of the exception - * @param status Status number or pattern like "2XX", "3XX", "4XX", "5XX" - * @param description Description about the exception + * @param props Properties for the exception * @returns Method decorator * * @author Jeongho Nam - https://github.com/samchon */ export function TypedException( - status: number | "2XX" | "3XX" | "4XX" | "5XX", - description?: string | undefined, + props: TypedException.IProps, ): MethodDecorator; /** * @internal */ export function TypedException( - status: number | "2XX" | "3XX" | "4XX" | "5XX", - description?: string | undefined, - type?: string | undefined, + props: TypedException.IProps, ): MethodDecorator { return function TypedException( target: Object | T, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor, ) { - const array: IProps[] = (() => { - const oldbie: IProps[] | undefined = Reflect.getMetadata( - "nestia/TypedException", - (target as any)[propertyKey], - ); + const array: TypedException.IProps[] = (() => { + const oldbie: TypedException.IProps[] | undefined = + Reflect.getMetadata( + "nestia/TypedException", + (target as any)[propertyKey], + ); if (oldbie !== undefined) return oldbie; - const newbie: IProps[] = []; + const newbie: TypedException.IProps[] = []; Reflect.defineMetadata( "nestia/TypedException", newbie, @@ -71,20 +64,15 @@ export function TypedException( ); return newbie; })(); - array.push({ - status, - description, - type: type!, - }); + array.push(props); return descriptor; }; } - -/** - * @internal - */ -interface IProps { - status: number | "2XX" | "3XX" | "4XX" | "5XX"; - description?: string | undefined; - type: string; +export namespace TypedException { + export interface IProps { + status: number | "2XX" | "3XX" | "4XX" | "5XX"; + description?: string | undefined; + example?: T; + examples?: Record; + } } diff --git a/packages/core/src/programmers/TypedExceptionProgrammer.ts b/packages/core/src/programmers/TypedExceptionProgrammer.ts deleted file mode 100644 index 598830f05..000000000 --- a/packages/core/src/programmers/TypedExceptionProgrammer.ts +++ /dev/null @@ -1,71 +0,0 @@ -import ts from "typescript"; -import { JsonMetadataFactory } from "typia/lib/factories/JsonMetadataFactory"; -import { TypeFactory } from "typia/lib/factories/TypeFactory"; -import { TransformerError } from "typia/lib/transformers/TransformerError"; - -import { INestiaTransformProject } from "../options/INestiaTransformProject"; - -export namespace TypedExceptionProgrammer { - export const generate = - ({ checker }: INestiaTransformProject) => - (expression: ts.CallExpression): ts.CallExpression => { - // CHECK GENERIC ARGUMENT EXISTENCE - if (!expression.typeArguments?.[0]) - throw TransformerError.from("nestia.core.TypedException")([ - { - name: "uknown", - messages: [NOT_SPECIFIED], - explore: { - top: true, - object: null, - property: null, - nested: null, - escaped: false, - aliased: false, - }, - }, - ]); - // CHECK DUPLICATED TRNASFORMATION - else if (expression.arguments.length === 3) return expression; - - // GET TYPE INFO - const node: ts.TypeNode = expression.typeArguments[0]; - const type: ts.Type = checker.getTypeFromTypeNode(node); - - // VALIDATE TYPE - if (type.isTypeParameter()) - throw TransformerError.from("nestia.core.TypedException")([ - { - name: TypeFactory.getFullName(checker)(type), - messages: [NO_GENERIC_ARGUMENT], - explore: { - top: true, - object: null, - property: null, - nested: null, - escaped: false, - aliased: false, - }, - }, - ]); - JsonMetadataFactory.analyze("@nestia.core.TypedException")(checker)(type); - - // DO TRANSFORM - const name: string = TypeFactory.getFullName(checker)(type); - return ts.factory.updateCallExpression( - expression, - expression.expression, - expression.typeArguments, - [ - expression.arguments[0], - expression.arguments[1] ?? ts.factory.createIdentifier("undefined"), - ts.factory.createStringLiteral(name), - ], - ); - }; -} - -const NOT_SPECIFIED = - "Error on @nestia.core.TypedException(): generic argument is not specified."; -const NO_GENERIC_ARGUMENT = - "Error on @nestia.core.TypedException(): non-specified generic argument."; diff --git a/packages/core/src/transformers/MethodTransformer.ts b/packages/core/src/transformers/MethodTransformer.ts index 3d4be6d31..079759138 100644 --- a/packages/core/src/transformers/MethodTransformer.ts +++ b/packages/core/src/transformers/MethodTransformer.ts @@ -1,7 +1,6 @@ import ts from "typescript"; import { INestiaTransformProject } from "../options/INestiaTransformProject"; -import { TypedExceptionTransformer } from "./TypedExceptionTransformer"; import { TypedRouteTransformer } from "./TypedRouteTransformer"; import { WebSocketRouteTransformer } from "./WebSocketRouteTransformer"; @@ -24,10 +23,12 @@ export namespace MethodTransformer { if (escaped === undefined) return method; const operator = (decorator: ts.Decorator): ts.Decorator => { - decorator = TypedExceptionTransformer.transform(project)(decorator); decorator = TypedRouteTransformer.transform(project)(escaped)(decorator); - WebSocketRouteTransformer.validate(project)(decorator, method); + decorator = WebSocketRouteTransformer.validate(project)( + decorator, + method, + ); return decorator; }; if (ts.getDecorators !== undefined) diff --git a/packages/core/src/transformers/TypedExceptionTransformer.ts b/packages/core/src/transformers/TypedExceptionTransformer.ts deleted file mode 100644 index 730ff697e..000000000 --- a/packages/core/src/transformers/TypedExceptionTransformer.ts +++ /dev/null @@ -1,44 +0,0 @@ -import path from "path"; -import ts from "typescript"; - -import { INestiaTransformProject } from "../options/INestiaTransformProject"; -import { TypedExceptionProgrammer } from "../programmers/TypedExceptionProgrammer"; - -export namespace TypedExceptionTransformer { - export const transform = - (project: INestiaTransformProject) => - (decorator: ts.Decorator): ts.Decorator => { - if (!ts.isCallExpression(decorator.expression)) return decorator; - - // CHECK SIGNATURE - const signature: ts.Signature | undefined = - project.checker.getResolvedSignature(decorator.expression); - if (!signature || !signature.declaration) return decorator; - - // CHECK TO BE TRANSFORMED - const done: boolean = (() => { - // CHECK FILENAME - const location: string = path.resolve( - signature.declaration.getSourceFile().fileName, - ); - if (location.indexOf(LIB_PATH) === -1) return false; - - // CHECK DUPLICATED - return decorator.expression.arguments.length !== 3; - })(); - if (done === false) return decorator; - - // DO TRANSFORM - return ts.factory.createDecorator( - TypedExceptionProgrammer.generate(project)(decorator.expression), - ); - }; -} - -const LIB_PATH = path.join( - "@nestia", - "core", - "lib", - "decorators", - `TypedException.d.ts`, -); diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 9e656f5ed..e54ea772b 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "^3.10.0", - "@nestia/fetcher": "^3.10.0", + "@nestia/core": "../core/nestia-core-3.10.0.tgz", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.10.0.tgz", "@samchon/openapi": "^0.4.3", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", diff --git a/packages/sdk/src/NestiaSdkApplication.ts b/packages/sdk/src/NestiaSdkApplication.ts index 2cfc3b884..7f320401d 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -14,6 +14,7 @@ import { SdkGenerator } from "./generates/SdkGenerator"; import { SwaggerGenerator } from "./generates/SwaggerGenerator"; import { IErrorReport } from "./structures/IErrorReport"; import { INestiaProject } from "./structures/INestiaProject"; +import { INestiaSdkInput } from "./structures/INestiaSdkInput"; import { IReflectController } from "./structures/IReflectController"; import { ITypedHttpRoute } from "./structures/ITypedHttpRoute"; import { ITypedWebSocketRoute } from "./structures/ITypedWebSocketRoute"; @@ -125,7 +126,6 @@ export class NestiaSdkApplication { // ANALYZE REFLECTS //---- const unique: WeakSet = new WeakSet(); - const controllers: IReflectController[] = []; const project: INestiaProject = { config: this.config, input: await ConfigAnalyzer.input(this.config), @@ -135,17 +135,12 @@ export class NestiaSdkApplication { }; console.log("Analyzing reflections"); - await turnTransformError(false); - for (const include of (await ConfigAnalyzer.input(this.config)).include) - controllers.push( - ...(await ReflectControllerAnalyzer.analyze(project)( - unique, - include.file, - include.paths, - include.controller, - )), - ); - await turnTransformError(true); + const input: INestiaSdkInput = await ConfigAnalyzer.input(this.config); + const controllers: IReflectController[] = input.controllers + .map((c) => + ReflectControllerAnalyzer.analyze({ project, controller: c, unique }), + ) + .filter((c): c is IReflectController => c !== null); const agg: number = (() => { const set: Set = new Set(); @@ -306,13 +301,3 @@ const report_errors = } } }; - -// const VARIABLE = /[a-zA-Z_$0-9]/; -const turnTransformError = async (flag: boolean): Promise => { - try { - const modulo = await import( - "@nestia/core/lib/decorators/internal/NoTransformConfigureError" as string - ); - modulo.NoTransformConfigureError.throws = flag; - } catch {} -}; diff --git a/packages/sdk/src/analyses/ConfigAnalyzer.ts b/packages/sdk/src/analyses/ConfigAnalyzer.ts index 6efa2ace7..8330102dd 100644 --- a/packages/sdk/src/analyses/ConfigAnalyzer.ts +++ b/packages/sdk/src/analyses/ConfigAnalyzer.ts @@ -1,44 +1,39 @@ /// +import { DynamicModule } from "@nestia/core"; import { INestApplication, VersioningType } from "@nestjs/common"; import { MODULE_PATH } from "@nestjs/common/constants"; -import { NestContainer } from "@nestjs/core"; +import { NestContainer, NestFactory } from "@nestjs/core"; import { Module } from "@nestjs/core/injector/module"; -import fs from "fs"; import getFunctionLocation from "get-function-location"; -import path from "path"; -import { HashMap, Pair, Singleton } from "tstl"; +import { HashMap } from "tstl"; import { INestiaConfig } from "../INestiaConfig"; -import { SdkGenerator } from "../generates/SdkGenerator"; -import { INormalizedInput } from "../structures/INormalizedInput"; -import { ArrayUtil } from "../utils/ArrayUtil"; +import { INestiaSdkInput } from "../structures/INestiaSdkInput"; import { MapUtil } from "../utils/MapUtil"; -import { SourceFinder } from "../utils/SourceFinder"; export namespace ConfigAnalyzer { - export const input = (config: INestiaConfig): Promise => + export const input = async ( + config: INestiaConfig, + ): Promise => MapUtil.take(memory, config, async () => { - const input = config.input; - if (Array.isArray(input)) return transform_input(config)(input); - else if (typeof input === "function") - return analyze_application(await input()); - else if (typeof input === "object") - if (input === null) - throw new Error("Invalid input config. It can't be null."); - else return transform_input(config)(input.include, input.exclude); - else if (typeof input === "string") - return transform_input(config)([input]); - else throw new Error("Invalid input config."); + const app: INestApplication = + typeof config.input === "function" + ? await config.input() + : await NestFactory.create(await DynamicModule.mount(config.input), { + logger: false, + }); + return analyze_application(app); }); const analyze_application = async ( app: INestApplication, - ): Promise => { - const files: HashMap, Set> = new HashMap(); + ): Promise => { const container: NestContainer = (app as any).container as NestContainer; const modules: Module[] = [...container.getModules().values()].filter( (m) => !!m.controllers.size, ); + const unique: HashMap> = new HashMap(); + for (const m of modules) { const path: string = Reflect.getMetadata( @@ -47,29 +42,28 @@ export namespace ConfigAnalyzer { ) ?? Reflect.getMetadata(MODULE_PATH, m.metatype) ?? ""; - for (const controller of [...m.controllers.keys()]) { - const file: string | null = - (await getFunctionLocation(controller))?.source ?? null; - if (file === null) continue; - - const location: string = normalize_file(file); - if (location.length === 0) continue; - - const key: Pair = new Pair( - controller as Function, - location, - ); - files.take(key, () => new Set([])).add(path); - } + for (const controller of [...m.controllers.keys()]) + if (typeof controller === "function") + unique.take(controller, () => new Set()).add(path); + } + const controllers: INestiaSdkInput.IController[] = []; + for (const it of unique) { + const file: string | null = + (await getFunctionLocation(it.first))?.source ?? null; + if (file === null) continue; + const location: string = normalize_file(file); + if (location.length === 0) continue; + controllers.push({ + class: it.first, + prefixes: Array.from(it.second), + location, + }); } const versioning = (app as any).config?.versioningOptions; return { - include: files.toJSON().map((pair) => ({ - controller: pair.first.first, - file: decodeURIComponent(pair.first.second), - paths: [...pair.second.values()], - })), + application: app, + controllers, globalPrefix: typeof (app as any).config?.globalPrefix === "string" ? { @@ -89,61 +83,15 @@ export namespace ConfigAnalyzer { }, }; }; - - const normalize_file = (str: string) => - str.substring( - str.startsWith("file:///") - ? process.cwd()[0] === "/" - ? 7 - : 8 - : str.startsWith("file://") - ? 7 - : 0, - ); - - const transform_input = - (config: INestiaConfig) => - async (include: string[], exclude?: string[]) => ({ - include: ( - await SourceFinder.find({ - include, - exclude, - filter: filter(config), - }) - ).map((file) => ({ - paths: [""], - file, - })), - }); - - const filter = - (config: INestiaConfig) => - async (location: string): Promise => - location.endsWith(".ts") && - !location.endsWith(".d.ts") && - (config.output === undefined || - (location.indexOf(path.join(config.output, "functional")) === -1 && - (await ( - await bundler.get(config.output) - )(location))) === false); } - -const memory = new Map>(); -const bundler = new Singleton(async (output: string) => { - const assets: string[] = await fs.promises.readdir(SdkGenerator.BUNDLE_PATH); - const tuples: Pair[] = await ArrayUtil.asyncMap( - assets, - async (file) => { - const relative: string = path.join(output, file); - const location: string = path.join(SdkGenerator.BUNDLE_PATH, file); - const stats: fs.Stats = await fs.promises.stat(location); - return new Pair(relative, stats.isDirectory()); - }, +const memory = new Map>(); +const normalize_file = (str: string) => + str.substring( + str.startsWith("file:///") + ? process.cwd()[0] === "/" + ? 7 + : 8 + : str.startsWith("file://") + ? 7 + : 0, ); - return async (file: string): Promise => { - for (const it of tuples) - if (it.second === false && file === it.first) return true; - else if (it.second === true && file.indexOf(it.first) === 0) return true; - return false; - }; -}); diff --git a/packages/sdk/src/analyses/GenericAnalyzer.ts b/packages/sdk/src/analyses/GenericAnalyzer.ts index 2243f0d21..e3bccbcf3 100644 --- a/packages/sdk/src/analyses/GenericAnalyzer.ts +++ b/packages/sdk/src/analyses/GenericAnalyzer.ts @@ -1,13 +1,11 @@ import ts from "typescript"; export namespace GenericAnalyzer { - export type Dictionary = WeakMap; - export function analyze( checker: ts.TypeChecker, classNode: ts.ClassDeclaration, ): Dictionary { - const dict: Dictionary = new WeakMap(); + const dict: WeakMap = new WeakMap(); explore(checker, dict, classNode); return dict; } diff --git a/packages/sdk/src/analyses/ImportAnalyzer.ts b/packages/sdk/src/analyses/ImportAnalyzer.ts index c2a525f03..93a59f1de 100644 --- a/packages/sdk/src/analyses/ImportAnalyzer.ts +++ b/packages/sdk/src/analyses/ImportAnalyzer.ts @@ -1,57 +1,61 @@ -import { HashMap, HashSet } from "tstl"; import ts from "typescript"; -import { ITypeTuple } from "../structures/ITypeTuple"; -import { GenericAnalyzer } from "./GenericAnalyzer"; +import { IReflectType } from "../structures/IReflectType"; +import { IReflectTypeImport } from "../structures/IReflectTypeImport"; +import { MapUtil } from "../utils/MapUtil"; export namespace ImportAnalyzer { export interface IOutput { - features: [string, string[]][]; - alias: string; + imports: IReflectTypeImport[]; + type: IReflectType | null; } - - export type Dictionary = HashMap>; - - export const analyze = - (checker: ts.TypeChecker) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: Dictionary; - type: ts.Type; - }): ITypeTuple | null => { - const type: ts.Type = get_type(checker)(props.type); - explore_escaped_name(checker)({ - ...props, - type, - }); - try { - return { + export const analyze = ( + checker: ts.TypeChecker, + generics: WeakMap, + type: ts.Type, + ): IOutput => { + const imports: Map> = new Map(); + try { + type = escape(checker, type); + return { + type: explore({ + checker, + generics, + imports, type, - typeName: explore_escaped_name(checker)({ - ...props, - type, - }), - }; - } catch { - return null; - } - }; + }), + imports: [...imports].map(([file, instances]) => ({ + file, + instances: Array.from(instances), + })), + }; + } catch { + return { + imports: [], + type: null, + }; + } + }; - /* --------------------------------------------------------- - TYPE - --------------------------------------------------------- */ - const get_type = - (checker: ts.TypeChecker) => - (type: ts.Type): ts.Type => { - const symbol: ts.Symbol | undefined = type.getSymbol(); - return symbol && get_name(symbol) === "Promise" - ? escape_promise(checker)(type) - : type; - }; + export const unique = ( + imports: IReflectTypeImport[], + ): IReflectTypeImport[] => { + const map: Map> = new Map(); + imports.forEach(({ file, instances }) => { + const set: Set = MapUtil.take(map, file, () => new Set()); + instances.forEach((instance) => set.add(instance)); + }); + return [...map].map(([file, instances]) => ({ + file, + instances: Array.from(instances), + })); + }; - const escape_promise = - (checker: ts.TypeChecker) => - (type: ts.Type): ts.Type => { + /* --------------------------------------------------------- + TYPE + --------------------------------------------------------- */ + const escape = (checker: ts.TypeChecker, type: ts.Type): ts.Type => { + if (type.symbol && getName(type.symbol) === "Promise") { const generic: readonly ts.Type[] = checker.getTypeArguments( type as ts.TypeReference, ); @@ -59,96 +63,101 @@ export namespace ImportAnalyzer { throw new Error( "Error on ImportAnalyzer.analyze(): invalid promise type.", ); - return generic[0]; - }; + type = generic[0]; + } + return type; + }; - const get_name = (symbol: ts.Symbol): string => - explore_name( + const getName = (symbol: ts.Symbol): string => + exploreName( symbol.escapedName.toString(), symbol.getDeclarations()?.[0]?.parent, ); /* --------------------------------------------------------- - ESCAPED TEXT WITH IMPORT STATEMENTS - --------------------------------------------------------- */ - const explore_escaped_name = - (checker: ts.TypeChecker) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: Dictionary; - type: ts.Type; - }): string => { - //---- - // CONDITIONAL BRANCHES - //---- - // DECOMPOSE GENERIC ARGUMENT - let type: ts.Type = props.type; - while (props.generics.has(type) === true) - type = props.generics.get(type)!; + ESCAPED TEXT WITH IMPORT STATEMENTS + --------------------------------------------------------- */ + const explore = (props: { + checker: ts.TypeChecker; + generics: WeakMap; + imports: Map>; + type: ts.Type; + }): IReflectType => { + //---- + // CONDITIONAL BRANCHES + //---- + // DECOMPOSE GENERIC ARGUMENT + let type: ts.Type = props.type; + while (props.generics.has(type) === true) type = props.generics.get(type)!; - // PRIMITIVE - const symbol: ts.Symbol | undefined = - type.aliasSymbol ?? type.getSymbol(); + // PRIMITIVE + const symbol: ts.Symbol | undefined = type.aliasSymbol ?? type.symbol; - // UNION OR INTERSECT - if (type.aliasSymbol === undefined && type.isUnionOrIntersection()) { - const joiner: string = type.isIntersection() ? " & " : " | "; - return type.types + // UNION OR INTERSECT + if (type.aliasSymbol === undefined && type.isUnionOrIntersection()) { + const joiner: string = type.isIntersection() ? " & " : " | "; + return { + name: type.types .map((child) => - explore_escaped_name(checker)({ + explore({ ...props, type: child, }), ) - .join(joiner); - } - // NO SYMBOL - else if (symbol === undefined) - return checker.typeToString( + .join(joiner), + }; + } + // NO SYMBOL + else if (symbol === undefined) + return { + name: props.checker.typeToString( type, undefined, ts.TypeFormatFlags.NoTruncation, - ); + ), + }; - //---- - // SPECIALIZATION - //---- - const name: string = get_name(symbol); - const sourceFile: ts.SourceFile | undefined = - symbol.declarations?.[0]?.getSourceFile(); - if (sourceFile === undefined) return name; - else if (sourceFile.fileName.indexOf("typescript/lib") === -1) { - const set: HashSet = props.imports.take( - sourceFile.fileName, - () => new HashSet(), - ); - set.insert(name.split(".")[0]); - } + //---- + // SPECIALIZATION + //---- + const name: string = getName(symbol); + const sourceFile: ts.SourceFile | undefined = + symbol.declarations?.[0]?.getSourceFile(); + if (sourceFile === undefined) return { name }; + else if (sourceFile.fileName.indexOf("typescript/lib") === -1) { + const set: Set = MapUtil.take( + props.imports, + sourceFile.fileName, + () => new Set(), + ); + set.add(name.split(".")[0]); + } - // CHECK GENERIC - const generic: readonly ts.Type[] = type.aliasSymbol - ? type.aliasTypeArguments ?? [] - : checker.getTypeArguments(type as ts.TypeReference); - return generic.length - ? name === "Promise" - ? explore_escaped_name(checker)({ - ...props, - type: generic[0], - }) - : `${name}<${generic - .map((child) => - explore_escaped_name(checker)({ - ...props, - type: child, - }), - ) - .join(", ")}>` - : name; - }; + // CHECK GENERIC + const generic: readonly ts.Type[] = type.aliasSymbol + ? type.aliasTypeArguments ?? [] + : props.checker.getTypeArguments(type as ts.TypeReference); + return generic.length + ? name === "Promise" + ? explore({ + ...props, + type: generic[0], + }) + : { + name, + typeArguments: generic.map((child) => + explore({ + ...props, + type: child, + }), + ), + } + : { name }; + }; - const explore_name = (name: string, decl?: ts.Node): string => + const exploreName = (name: string, decl?: ts.Node): string => decl && ts.isModuleBlock(decl) - ? explore_name( + ? exploreName( `${decl.parent.name.getFullText().trim()}.${name}`, decl.parent.parent, ) diff --git a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts index b202ce97a..68742bedf 100644 --- a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts @@ -5,6 +5,8 @@ import { } from "@nestjs/common/constants"; import { INestiaProject } from "../structures/INestiaProject"; +import { INestiaSdkInput } from "../structures/INestiaSdkInput"; +import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; @@ -13,132 +15,76 @@ import { ReflectHttpOperationAnalyzer } from "./ReflectHttpOperationAnalyzer"; import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer"; import { ReflectWebSocketOperationAnalyzer } from "./ReflectWebSocketOperationAnalyzer"; -type IModule = { - [key: string]: any; -}; - export namespace ReflectControllerAnalyzer { - export const analyze = - (project: INestiaProject) => - async ( - unique: WeakSet, - file: string, - prefixes: string[], - target?: Function, - ): Promise => { - const module: IModule = await (async () => { - try { - return await import(file); - } catch (exp) { - console.log( - ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", - ); - console.log(`Error on "${file}" file. Check your code.`); - console.log(exp); - console.log( - ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", - ); - process.exit(-1); - } - })(); - const ret: IReflectController[] = []; - - for (const [key, value] of Object.entries(module)) { - if (typeof value !== "function" || unique.has(value)) continue; - else if ((target ?? value) !== value) continue; - else unique.add(value); - - const result: IReflectController | null = _Analyze_controller(project)({ - file, - name: key, - creator: value, - prefixes, - }); - if (result !== null) ret.push(result); - } - return ret; - }; - + export interface IProps { + project: INestiaProject; + controller: INestiaSdkInput.IController; + unique: WeakSet; + } /* --------------------------------------------------------- - CONTROLLER - --------------------------------------------------------- */ - const _Analyze_controller = - (project: INestiaProject) => - (props: { - file: string; - name: string; - creator: any; - prefixes: string[]; - }): IReflectController | null => { - //---- - // VALIDATIONS - //---- - // MUST BE TYPE OF A CREATOR WHO HAS THE CONSTRUCTOR - if ( - !( - props.creator instanceof Function && - props.creator.constructor instanceof Function - ) - ) - return null; - // MUST HAVE THOSE MATADATA - else if ( - ArrayUtil.has( - Reflect.getMetadataKeys(props.creator), - PATH_METADATA, - HOST_METADATA, - SCOPE_OPTIONS_METADATA, - ) === false - ) - return null; + CONTROLLER + --------------------------------------------------------- */ + export const analyze = (props: IProps): IReflectController | null => { + // MUST BE TYPE OF A CREATOR WHO HAS THE CONSTRUCTOR + if ( + ArrayUtil.has( + Reflect.getMetadataKeys(props.controller.class), + PATH_METADATA, + HOST_METADATA, + SCOPE_OPTIONS_METADATA, + ) === false + ) + return null; - //---- - // CONSTRUCTION - //---- - // BASIC INFO - const meta: IReflectController = { - constructor: props.creator, - prototype: props.creator.prototype, - file: props.file, - name: props.name, - operations: [], - prefixes: props.prefixes, - paths: ReflectMetadataAnalyzer.paths(props.creator).filter((str) => { + // BASIC INFO + const controller: IReflectController = { + class: props.controller.class, + file: props.controller.location, + operations: [], + prefixes: props.controller.prefixes, + paths: ReflectMetadataAnalyzer.paths(props.controller.class).filter( + (str) => { if (str.includes("*") === true) { - project.warnings.push({ - file: props.file, - controller: props.name, + props.project.warnings.push({ + file: props.controller.location, + controller: props.controller.class.name, function: null, message: "@nestia/sdk does not compose wildcard controller.", }); return false; } return true; - }), - versions: ReflectMetadataAnalyzer.versions(props.creator), - security: ReflectMetadataAnalyzer.securities(props.creator), - swaggerTgas: - Reflect.getMetadata("swagger/apiUseTags", props.creator) ?? [], - }; - - // PARSE CHILDREN DATA - for (const [key, value] of _Get_prototype_entries(props.creator)) { - if (typeof value !== "function") continue; - const next = { - controller: meta, - name: key, - function: value, - }; - const child: IReflectHttpOperation | IReflectWebSocketOperation | null = - ReflectWebSocketOperationAnalyzer.analyze(project)(next) ?? - ReflectHttpOperationAnalyzer.analyze(project)(next); - if (child !== null) meta.operations.push(child); - } - - // RETURNS - return meta; + }, + ), + versions: ReflectMetadataAnalyzer.versions(props.controller.class), + security: ReflectMetadataAnalyzer.securities(props.controller.class), + tags: + Reflect.getMetadata("swagger/apiUseTags", props.controller.class) ?? [], }; + // OPERATORS + for (const [key, value] of _Get_prototype_entries(props.controller.class)) { + if (typeof value !== "function") continue; + const metadata: IOperationMetadata | undefined = Reflect.getMetadata( + "nestia/OperationMetadata", + value, + ); + if (metadata === undefined) continue; + const next: ReflectHttpOperationAnalyzer.IProps = { + project: props.project, + controller: controller, + name: key, + function: value, + metadata, + }; + const child: IReflectHttpOperation | IReflectWebSocketOperation | null = + ReflectWebSocketOperationAnalyzer.analyze(next) ?? + ReflectHttpOperationAnalyzer.analyze(next); + if (child !== null) controller.operations.push(child); + } + return controller; + }; + function _Get_prototype_entries(creator: any): Array<[string, unknown]> { const keyList = Object.getOwnPropertyNames(creator.prototype); const entries: Array<[string, unknown]> = keyList.map((key) => [ diff --git a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts index 0524195ea..58a2e1522 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -1,193 +1,172 @@ -import { SwaggerExample } from "@nestia/core"; import { - HEADERS_METADATA, - HTTP_CODE_METADATA, INTERCEPTORS_METADATA, METHOD_METADATA, PATH_METADATA, - ROUTE_ARGS_METADATA, } from "@nestjs/common/constants"; import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum"; import { ranges } from "tstl"; import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; +import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; +import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; +import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess"; import { ParamCategory } from "../structures/ParamCategory"; import { ArrayUtil } from "../utils/ArrayUtil"; +import { ImportAnalyzer } from "./ImportAnalyzer"; import { PathAnalyzer } from "./PathAnalyzer"; +import { ReflectHttpOperationParameterAnalyzer } from "./ReflectHttpOperationParameterAnalyzer"; +import { ReflectHttpOperationResponseAnalyzer } from "./ReflectHttpOperationResponseAnalyzer"; import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer"; export namespace ReflectHttpOperationAnalyzer { - export const analyze = - (project: INestiaProject) => - (props: { - controller: IReflectController; - function: Function; - name: string; - }): IReflectHttpOperation | null => { - if ( - ArrayUtil.has( - Reflect.getMetadataKeys(props.function), - PATH_METADATA, - METHOD_METADATA, - ) === false - ) - return null; - - const errors: IErrorReport[] = []; - - //---- - // CONSTRUCTION - //---- - // BASIC INFO - const encrypted: boolean = hasInterceptor("EncryptedRouteInterceptor")( - props.function, - ); - const query: boolean = hasInterceptor("TypedQueryRouteInterceptor")( - props.function, - ); - const method: string = - METHODS[Reflect.getMetadata(METHOD_METADATA, props.function)]; - if (method === undefined || method === "OPTIONS") return null; - - const parameters: IReflectHttpOperation.IParameter[] = (() => { - const nestParameters: NestParameters | undefined = Reflect.getMetadata( - ROUTE_ARGS_METADATA, - props.controller.constructor, - props.name, - ); - if (nestParameters === undefined) return []; + export interface IProps { + project: INestiaProject; + controller: IReflectController; + function: Function; + name: string; + metadata: IOperationMetadata; + } + export const analyze = (props: IProps): IReflectHttpOperation | null => { + if ( + ArrayUtil.has( + Reflect.getMetadataKeys(props.function), + PATH_METADATA, + METHOD_METADATA, + ) === false + ) + return null; - const output: IReflectHttpOperation.IParameter[] = []; - for (const tuple of Object.entries(nestParameters)) { - const child: IReflectHttpOperation.IParameter | null = - _Analyze_http_parameter(...tuple); - if (child !== null) output.push(child); - } - const examples: SwaggerExample.IData[] = - Reflect.getMetadata( - "nestia/SwaggerExample/Parameters", - props.controller.prototype, - props.name, - ) ?? []; - for (const p of output) { - const e = examples.find((elem) => elem.index === p.index); - if (e !== undefined) { - p.example = e.example; - p.examples = e.examples; - } - } - return output.sort((x, y) => x.index - y.index); - })(); + const errors: IErrorReport[] = []; + const method: string = + METHODS[Reflect.getMetadata(METHOD_METADATA, props.function)]; + if (method === undefined || method === "OPTIONS") return null; - // VALIDATE BODY - const body: IReflectHttpOperation.IParameter | undefined = - parameters.find((param) => param.category === "body"); - if (body !== undefined && (method === "GET" || method === "HEAD")) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: `"body" parameter cannot be used in the "${method}" method.`, - }); + const parameters: IReflectHttpOperationParameter[] = + ReflectHttpOperationParameterAnalyzer.analyze({ + controller: props.controller, + metadata: props.metadata, + httpMethod: method, + function: props.function, + functionName: props.name, + report: (message) => + errors.push({ + file: props.controller.file, + controller: props.controller.class.name, + function: props.name, + message, + }), + isError: () => errors.length !== 0, + }); + const success: IReflectHttpOperationSuccess | null = (() => { + const localErrors: IErrorReport[] = []; + const success = ReflectHttpOperationResponseAnalyzer.analyze({ + controller: props.controller, + function: props.function, + functionName: props.name, + httpMethod: method, + metadata: props.metadata, + report: (message) => + localErrors.push({ + file: props.controller.file, + controller: props.controller.class.name, + function: props.name, + message, + }), + isError: () => localErrors.length !== 0, + }); + if (localErrors.length) { + errors.push(...localErrors); return null; } + return success; + })(); + if (errors.length) { + props.project.errors.push(...errors); + return null; + } else if (success === null) return null; - // DO CONSTRUCT - const example: SwaggerExample.IData | undefined = - Reflect.getMetadata("nestia/SwaggerExample/Response", props.function); - const meta: IReflectHttpOperation = { - protocol: "http", - function: props.function, - name: props.name, - method: method === "ALL" ? "POST" : method, - paths: ReflectMetadataAnalyzer.paths(props.function).filter((str) => { - if (str.includes("*") === true) { - project.warnings.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: "@nestia/sdk does not compose wildcard method.", - }); - return false; - } - return true; - }), - versions: ReflectMetadataAnalyzer.versions(props.function), - parameters, - status: Reflect.getMetadata(HTTP_CODE_METADATA, props.function), - encrypted, - contentType: encrypted - ? "text/plain" - : query - ? "application/x-www-form-urlencoded" - : Reflect.getMetadata(HEADERS_METADATA, props.function)?.find( - (h: Record) => - typeof h?.name === "string" && - typeof h?.value === "string" && - h.name.toLowerCase() === "content-type", - )?.value ?? "application/json", - security: ReflectMetadataAnalyzer.securities(props.function), - exceptions: ReflectMetadataAnalyzer.exceptions(props.function), - swaggerTags: [ - ...new Set([ - ...props.controller.swaggerTgas, - ...(Reflect.getMetadata("swagger/apiUseTags", props.function) ?? - []), - ]), - ], - ...(example - ? { example: example.example, examples: example.examples } - : {}), - }; - - // VALIDATE PATH ARGUMENTS - for (const controllerLocation of props.controller.paths) - for (const metaLocation of meta.paths) { - // NORMALIZE LOCATION - const location: string = PathAnalyzer.join( - controllerLocation, - metaLocation, - ); - if (location.includes("*")) continue; + // DO CONSTRUCT + const operation: IReflectHttpOperation = { + protocol: "http", + function: props.function, + name: props.name, + method: method === "ALL" ? "POST" : method, + paths: ReflectMetadataAnalyzer.paths(props.function).filter((str) => { + if (str.includes("*") === true) { + props.project.warnings.push({ + file: props.controller.file, + controller: props.controller.class.name, + function: props.name, + message: "@nestia/sdk does not compose wildcard method.", + }); + return false; + } + return true; + }), + versions: ReflectMetadataAnalyzer.versions(props.function), + parameters, + success, + security: ReflectMetadataAnalyzer.securities(props.function), + exceptions: {} as any, + tags: Reflect.getMetadata("swagger/apiUseTags", props.function), + imports: ImportAnalyzer.unique( + [ + ...props.metadata.parameters + .filter((x) => parameters.some((y) => x.index === y.index)) + .map((x) => x.imports), + ...props.metadata.success.imports, + ].flat(), + ), + }; - // LIST UP PARAMETERS - const binded: string[] | null = PathAnalyzer.parameters(location); - if (binded === null) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: `invalid path (${JSON.stringify(location)})`, - }); - continue; - } - const parameters: string[] = meta.parameters - .filter((param) => param.category === "param") - .map((param) => param.field!) - .sort(); + // VALIDATE PATH ARGUMENTS + for (const controllerLocation of props.controller.paths) + for (const metaLocation of operation.paths) { + // NORMALIZE LOCATION + const location: string = PathAnalyzer.join( + controllerLocation, + metaLocation, + ); + if (location.includes("*")) continue; - // DO VALIDATE - if (ranges.equal(binded.sort(), parameters) === false) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( - ", ", - )}], parameters: [${parameters.join(", ")}]).`, - }); + // LIST UP PARAMETERS + const binded: string[] | null = PathAnalyzer.parameters(location); + if (binded === null) { + props.project.errors.push({ + file: props.controller.file, + controller: props.controller.class.name, + function: props.name, + message: `invalid path (${JSON.stringify(location)})`, + }); + continue; } + const parameters: string[] = operation.parameters + .filter((param) => param.kind === "param") + .map((param) => param.field!) + .sort(); - // RETURNS - if (errors.length) { - project.errors.push(...errors); - return null; + // DO VALIDATE + if (ranges.equal(binded.sort(), parameters) === false) + errors.push({ + file: props.controller.file, + controller: props.controller.class.name, + function: props.name, + message: `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( + ", ", + )}], parameters: [${parameters.join(", ")}]).`, + }); } - return meta; - }; + + // RETURNS + if (errors.length) { + props.project.errors.push(...errors); + return null; + } + return operation; + }; function _Analyze_http_parameter( key: string, diff --git a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts new file mode 100644 index 000000000..1b53487cb --- /dev/null +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -0,0 +1,251 @@ +import { ROUTE_ARGS_METADATA } from "@nestjs/common/constants"; +import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum"; + +import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { IReflectController } from "../structures/IReflectController"; +import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; +import { IReflectTypeImport } from "../structures/IReflectTypeImport"; + +export namespace ReflectHttpOperationParameterAnalyzer { + export interface IContext { + controller: IReflectController; + function: Function; + functionName: string; + httpMethod: string; + metadata: IOperationMetadata; + report: (message: string) => void; + isError: () => boolean; + } + export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => { + const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] = + analyzePreconfigured(ctx); + const imports: IReflectTypeImport[] = []; + const parameters: IReflectHttpOperationParameter[] = preconfigured + .map((p): IReflectHttpOperationParameter | null => { + // METADATA INFO + const matched: IOperationMetadata.IParameter | undefined = + ctx.metadata.parameters.find((x) => x.index === p.index); + + // VALIDATE TYPE + if (matched === undefined) + ctx.report(`Unable to find parameter type of the ${p.index} (th).`); + else if (matched.schema === null || matched.type === null) + ctx.report( + `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, + ); + + // VALIDATE KIND + if (p.kind === "body" && p.field) + ctx.report(`@Body() must not have a field name.`); + else if (p.kind === "param" && !p.field) + ctx.report(`@Param() must have a field name.`); + + if (ctx.isError()) return null; + else if ( + matched === undefined || + matched.schema === null || + matched.type === null || + matched.required === false + ) + return null; + + // COMPOSITION + imports.push(...matched.imports); + if (p.kind === "param") + return { + kind: p.kind, + index: p.index, + field: p.field!, + name: matched.name, + type: matched.type, + schema: matched.schema, + components: matched.components, + }; + else if (p.kind === "query" || p.kind === "headers") + return { + kind: p.kind, + index: p.index, + field: p.field ?? null, + name: matched.name, + type: matched.type, + schema: matched.schema, + components: matched.components, + }; + else if (p.kind === "body") + return { + kind: p.kind, + index: p.index, + encrypted: !!p.encrypted, + contentType: p.contentType, + name: matched.name, + type: matched.type, + schema: matched.schema, + components: matched.components, + }; + else { + ctx.report(`Unknown kind of the parameter.`); + return null; + } + }) + .filter((x): x is IReflectHttpOperationParameter => x !== null); + + //---- + // POST VALIDATIONS + //---- + // GET AND HEAD METHOD + if ( + (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") && + parameters.some((x) => x.kind === "body") + ) + ctx.report(`@Body() is not allowed in the ${ctx.httpMethod} method.`); + + // FIND DUPLICATED BODY + if (parameters.filter((x) => x.kind === "body").length > 1) + ctx.report(`Duplicated @Body() is not allowed.`); + if ( + parameters.filter((x) => x.kind === "query" && x.field === null).length > + 1 + ) + ctx.report(`Duplicated @Query() without field name is not allowed.`); + if ( + parameters.filter((x) => x.kind === "headers" && x.field === null) + .length > 1 + ) + ctx.report(`Duplicated @Headers() without field name is not allowed.`); + + // FIND DUPLICATED FIELDS + if ( + isUnique( + parameters.filter((x) => x.kind === "param").map((x) => x.field), + ) === false + ) + ctx.report(`Duplicated field names of path are not allowed.`); + if ( + isUnique( + parameters + .filter((x) => x.kind === "query") + .filter((x) => x.field !== null) + .map((x) => x.field!), + ) === false + ) + ctx.report(`Duplicated field names of query are not allowed.`); + if ( + isUnique( + parameters + .filter((x) => x.kind === "headers") + .filter((x) => x.field !== null) + .map((x) => x.field!), + ) === false + ) + ctx.report(`Duplicated field names of headers are not allowed.`); + return parameters; + }; + + const analyzePreconfigured = ( + props: IContext, + ): IReflectHttpOperationParameter.IPreconfigured[] => { + const dict: NestParameters | undefined = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + props.controller.constructor, + props.functionName, + ); + if (dict === undefined) return []; + return Object.entries(dict) + .map(([key, param]) => analyzeHttpParameter(key, param)) + .filter( + (x): x is IReflectHttpOperationParameter.IPreconfigured => x !== null, + ) + .sort((x, y) => x.index - y.index); + }; + + const analyzeHttpParameter = ( + key: string, + param: INestParam, + ): IReflectHttpOperationParameter.IPreconfigured | null => { + const symbol: string = key.split(":")[0]; + if (symbol.indexOf("__custom") !== -1) return analyzeCustomParameter(param); + + const kind: IReflectHttpOperationParameter.IPreconfigured["kind"] | null = + getNestParamType(Number(symbol[0]) as RouteParamtypes); + if (kind === null) return null; + if (kind === "body") + return { + kind: "body", + index: param.index, + field: param.data, + contentType: "application/json", + }; + else + return { + kind, + index: param.index, + field: param.data, + }; + }; + + const analyzeCustomParameter = ( + param: INestParam, + ): IReflectHttpOperationParameter.IPreconfigured | null => { + if (param.factory === undefined) return null; + else if ( + param.factory.name === "EncryptedBody" || + param.factory.name === "PlainBody" || + param.factory.name === "TypedQueryBody" || + param.factory.name === "TypedBody" || + param.factory.name === "TypedFormDataBody" + ) + return { + kind: "body", + index: param.index, + encrypted: param.factory.name === "EncryptedBody", + contentType: + param.factory.name === "PlainBody" || + param.factory.name === "EncryptedBody" + ? "text/plain" + : param.factory.name === "TypedQueryBody" + ? "application/x-www-form-urlencoded" + : param.factory.name === "TypedFormDataBody" + ? "multipart/form-data" + : "application/json", + }; + else if (param.factory.name === "TypedHeaders") + return { + kind: "headers", + index: param.index, + field: param.data, + }; + else if (param.factory.name === "TypedParam") + return { + kind: "param", + index: param.index, + field: param.data, + }; + else if (param.factory.name === "TypedQuery") + return { + kind: "query", + index: param.index, + field: undefined, + }; + else return null; + }; + + const isUnique = (values: string[]) => new Set(values).size === values.length; +} + +type NestParameters = { + [key: string]: INestParam; +}; +interface INestParam { + name: string; + index: number; + factory?: (...args: any) => any; + data: string | undefined; +} + +const getNestParamType = (value: RouteParamtypes) => { + if (value === RouteParamtypes.BODY) return "body"; + else if (value === RouteParamtypes.HEADERS) return "headers"; + else if (value === RouteParamtypes.QUERY) return "query"; + else if (value === RouteParamtypes.PARAM) return "param"; + return null; +}; diff --git a/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts new file mode 100644 index 000000000..02c00f22b --- /dev/null +++ b/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts @@ -0,0 +1,92 @@ +import { + HEADERS_METADATA, + HTTP_CODE_METADATA, + INTERCEPTORS_METADATA, +} from "@nestjs/common/constants"; +import typia from "typia"; + +import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { IReflectController } from "../structures/IReflectController"; +import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess"; + +export namespace ReflectHttpOperationResponseAnalyzer { + export interface IContext { + controller: IReflectController; + function: Function; + functionName: string; + httpMethod: string; + metadata: IOperationMetadata; + report: (message: string) => void; + isError: () => boolean; + } + + export const analyze = ( + ctx: IContext, + ): IReflectHttpOperationSuccess | null => { + const encrypted: boolean = hasInterceptor({ + name: "EncryptedRouteInterceptor", + function: ctx.function, + }); + const contentType: string | null = encrypted + ? "text/plain" + : hasInterceptor({ + name: "TypedQueryRouteInterceptor", + function: ctx.function, + }) + ? "application/x-www-form-urlencoded" + : Reflect.getMetadata(HEADERS_METADATA, ctx.function)?.find( + (h: Record) => + typeof h?.name === "string" && + typeof h?.value === "string" && + h.name.toLowerCase() === "content-type", + )?.value ?? (ctx.httpMethod === "HEAD" ? null : "application/json"); + + if ( + ctx.metadata.success.type === null || + ctx.metadata.success.schema === null + ) + ctx.report(`Failed to analyze the return type.`); + if (ctx.httpMethod === "HEAD" && contentType !== null) + ctx.report(`HEAD method must not have a content type.`); + if ( + typia.is(contentType) === + false + ) + ctx.report( + `@nestia/sdk does not support ${JSON.stringify(contentType)} content type.`, + ); + + if (ctx.isError()) return null; + else if ( + ctx.metadata.success.type === null || + ctx.metadata.success.schema === null || + !typia.is(contentType) + ) + return null; + return { + contentType: contentType, + encrypted, + status: + getStatus(ctx.function) ?? (ctx.httpMethod === "POST" ? 201 : 200), + type: ctx.metadata.success.type, + schema: ctx.metadata.success.schema, + components: ctx.metadata.success.components, + }; + }; + + const getStatus = (func: Function): number | null => { + const text = Reflect.getMetadata(HTTP_CODE_METADATA, func); + if (text === undefined) return null; + const value: number = Number(text); + return isNaN(value) ? null : value; + }; + + const hasInterceptor = (props: { + name: string; + function: Function; + }): boolean => { + const meta = Reflect.getMetadata(INTERCEPTORS_METADATA, props.function); + if (Array.isArray(meta) === false) return false; + return meta.some((elem) => elem?.constructor?.name === props.name); + }; +} diff --git a/packages/sdk/src/analyses/ReflectMetadataAnalyzer.ts b/packages/sdk/src/analyses/ReflectMetadataAnalyzer.ts index ebd846fa3..a2117da39 100644 --- a/packages/sdk/src/analyses/ReflectMetadataAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectMetadataAnalyzer.ts @@ -2,27 +2,10 @@ import { VERSION_NEUTRAL } from "@nestjs/common"; import { PATH_METADATA, VERSION_METADATA } from "@nestjs/common/constants"; import { VersionValue } from "@nestjs/common/interfaces"; -import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; import { SecurityAnalyzer } from "./SecurityAnalyzer"; export namespace ReflectMetadataAnalyzer { - export const exceptions = ( - value: any, - ): Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - IReflectHttpOperation.IException - > => { - const entire: IReflectHttpOperation.IException[] | undefined = - Reflect.getMetadata("nestia/TypedException", value); - return Object.fromEntries( - (entire ?? []).map((exp) => [exp.status, exp]), - ) as Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - IReflectHttpOperation.IException - >; - }; - - export const paths = (target: any): string[] => { + export const paths = (target: Function): string[] => { const value: string | string[] = Reflect.getMetadata(PATH_METADATA, target); if (typeof value === "string") return [value]; else if (value.length === 0) return [""]; diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index 22fa5e7a4..033c1cca7 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -2,95 +2,156 @@ import { ranges } from "tstl"; import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; +import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; +import { IReflectTypeImport } from "../structures/IReflectTypeImport"; import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; +import { IReflectWebSocketOperationParameter } from "../structures/IReflectWebSocketOperationParameter"; +import { StringUtil } from "../utils/StringUtil"; +import { ImportAnalyzer } from "./ImportAnalyzer"; import { PathAnalyzer } from "./PathAnalyzer"; import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer"; export namespace ReflectWebSocketOperationAnalyzer { - export const analyze = - (project: INestiaProject) => - (props: { - controller: IReflectController; - function: Function; - name: string; - }): IReflectWebSocketOperation | null => { - const route: { paths: string[] } | undefined = Reflect.getMetadata( - "nestia/WebSocketRoute", - props.function, - ); - if (route === undefined) return null; + export interface IProps { + project: INestiaProject; + controller: IReflectController; + function: Function; + name: string; + metadata: IOperationMetadata; + } + export const analyze = (ctx: IProps): IReflectWebSocketOperation | null => { + const route: { paths: string[] } | undefined = Reflect.getMetadata( + "nestia/WebSocketRoute", + ctx.function, + ); + if (route === undefined) return null; - const errors: IErrorReport[] = []; - const parameters: IReflectWebSocketOperation.IParameter[] = ( + const errors: IErrorReport[] = []; + const report = (message: string): null => { + errors.push({ + file: ctx.controller.file, + controller: ctx.controller.class.name, + function: ctx.name, + message, + }); + return null; + }; + const preconfigured: IReflectWebSocketOperationParameter.IPreconfigured[] = + ( (Reflect.getMetadata( "nestia/WebSocketRoute/Parameters", - props.controller.prototype, - props.name, - ) ?? []) as IReflectWebSocketOperation.IParameter[] + ctx.controller.class.prototype, + ctx.name, + ) ?? []) as IReflectWebSocketOperationParameter[] ).sort((a, b) => a.index - b.index); - if (parameters.find((p) => (p.category === "acceptor") === undefined)) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: "@WebSocketRoute.Acceptor() is essentially required", - }); - if (parameters.length !== props.function.length) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: [ - "Every parameters must be one of below:", - " - @WebSocketRoute.Acceptor()", - " - @WebSocketRoute.Driver()", - " - @WebSocketRoute.Header()", - " - @WebSocketRoute.Param()", - " - @WebSocketRoute.Query()", - ].join("\n"), - }); + if (preconfigured.find((p) => (p.kind === "acceptor") === undefined)) + report("@WebSocketRoute.Acceptor() is essentially required"); + if (preconfigured.length !== ctx.function.length) + report( + [ + "Every parameters must be one of below:", + " - @WebSocketRoute.Acceptor()", + " - @WebSocketRoute.Driver()", + " - @WebSocketRoute.Header()", + " - @WebSocketRoute.Param()", + " - @WebSocketRoute.Query()", + ].join("\n"), + ); + + const imports: IReflectTypeImport[] = []; + const parameters: IReflectWebSocketOperationParameter[] = preconfigured + .map((p) => { + // METADATA INFO + const matched: IOperationMetadata.IParameter | undefined = + ctx.metadata.parameters.find((m) => p.index === m.index); + + // VALIDATE PARAMETER + if (matched === undefined) + return report( + `Unable to find parameter type of the ${p.index} (th).`, + ); + else if (matched.schema === null || matched.type === null) + return report( + `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, + ); + else if ( + p.kind === "param" && + !(p as IReflectWebSocketOperationParameter.IParamParameter).field + ?.length + ) + return report(`@WebSocketRoute.Param() must have a field name.`); + else if ( + p.kind === "acceptor" && + matched.type?.typeArguments?.length !== 3 + ) + return `@WebSocketRoute.Acceptor() must have three type arguments.`; + else if ( + p.kind === "driver" && + matched.type?.typeArguments?.length !== 1 + ) + return report( + `@WebSocketRoute.Driver() must have one type argument.`, + ); + + // COMPLETE COMPOSITION + imports.push(...matched.imports); + if (p.kind === "acceptor" || p.kind === "driver" || p.kind === "query") + return { + ...p, + name: matched.name, + type: matched.type, + }; + else if (p.kind === "param") + return { + ...p, + kind: "param", + field: p.field!, + name: matched.name, + type: matched.type, + } satisfies IReflectWebSocketOperationParameter.IParamParameter; + + // UNKNOWN TYPE, MAYBE NEW FEATURE + return report( + `@WebSocketRoute.${StringUtil.capitalize(p.kind)}() has not been supported yet. How about upgrading the nestia packages?`, + ); + }) + .filter((p): p is IReflectWebSocketOperationParameter => !!p); - const fields: string[] = parameters - .filter((p) => p.category === "param") - .map((p) => p.field) - .sort(); - for (const cLoc of props.controller.paths) - for (const mLoc of route.paths) { - const location: string = PathAnalyzer.join(cLoc, mLoc); - if (location.includes("*")) continue; + const fields: string[] = preconfigured + .filter((p) => p.kind === "param") + .map((p) => p.field ?? "") + .filter((field): field is string => !!field?.length) + .sort(); + for (const cLoc of ctx.controller.paths) + for (const mLoc of route.paths) { + const location: string = PathAnalyzer.join(cLoc, mLoc); + if (location.includes("*")) continue; - const binded: string[] | null = PathAnalyzer.parameters(location); - if (binded === null) { - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: `invalid path (${JSON.stringify(location)})`, - }); - continue; - } - if (ranges.equal(binded.sort(), fields) === false) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.name, - message: `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( - ", ", - )}], parameters: [${fields.join(", ")}]).`, - }); - } - if (errors.length) { - project.errors.push(...errors); - return null; + const binded: string[] | null = PathAnalyzer.parameters(location); + if (binded === null) + report(`invalid path (${JSON.stringify(location)})`); + else if (ranges.equal(binded.sort(), fields) === false) + report( + `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( + ", ", + )}], parameters: [${fields.join(", ")}]).`, + ); } - return { - protocol: "websocket", - target: props.function, - name: props.name, - paths: route.paths, - versions: ReflectMetadataAnalyzer.versions(props.function), - parameters, - }; + if (errors.length) { + ctx.project.errors.push(...errors); + return null; + } + return { + protocol: "websocket", + name: ctx.name, + paths: route.paths, + function: ctx.function, + versions: ReflectMetadataAnalyzer.versions(ctx.function), + parameters, + imports: ImportAnalyzer.unique(imports), + description: ctx.metadata.description ?? null, + jsDocTags: ctx.metadata.jsDocTags, }; + }; } diff --git a/packages/sdk/src/analyses/TypedControllerAnalyzer.ts b/packages/sdk/src/analyses/TypedControllerAnalyzer.ts deleted file mode 100644 index b13e9fed9..000000000 --- a/packages/sdk/src/analyses/TypedControllerAnalyzer.ts +++ /dev/null @@ -1,92 +0,0 @@ -import ts from "typescript"; - -import { INestiaProject } from "../structures/INestiaProject"; -import { IReflectController } from "../structures/IReflectController"; -import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; -import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; -import { GenericAnalyzer } from "./GenericAnalyzer"; -import { TypedHttpOperationAnalyzer } from "./TypedHttpOperationAnalyzer"; -import { TypedWebSocketOperationAnalyzer } from "./TypedWebSocketOperationAnalyzer"; - -export namespace TypedControllerAnalyzer { - export const analyze = - (project: INestiaProject) => - async ( - sourceFile: ts.SourceFile, - controller: IReflectController, - ): Promise> => { - // FIND CONTROLLER CLASS - const ret: Array = []; - ts.forEachChild(sourceFile, (node) => { - if ( - ts.isClassDeclaration(node) && - node.name?.escapedText === controller.name - ) { - // ANALYZE THE CONTROLLER - ret.push(..._Analyze_controller(project)(controller, node)); - return; - } - }); - return ret; - }; - - /* --------------------------------------------------------- - CLASS - --------------------------------------------------------- */ - const _Analyze_controller = - (project: INestiaProject) => - ( - controller: IReflectController, - classNode: ts.ClassDeclaration, - ): Array => { - const classType: ts.InterfaceType = project.checker.getTypeAtLocation( - classNode, - ) as ts.InterfaceType; - const generics: GenericAnalyzer.Dictionary = GenericAnalyzer.analyze( - project.checker, - classNode, - ); - - const ret: Array = []; - for (const symbol of classType.getProperties()) { - // GET METHOD DECLARATION - const declaration: ts.Declaration | undefined = (symbol.declarations || - [])[0]; - if (!declaration || !ts.isMethodDeclaration(declaration)) continue; - - // IDENTIFIER MUST BE - const identifier = declaration.name; - if (!ts.isIdentifier(identifier)) continue; - - // ANALYZED WITH THE REFLECTED-FUNCTION - const operation: - | IReflectHttpOperation - | IReflectWebSocketOperation - | undefined = controller.operations.find( - (f) => f.name === identifier.escapedText, - ); - if (operation === undefined) continue; - - const routes: ITypedHttpRoute[] | ITypedWebSocketRoute[] = - operation.protocol === "http" - ? TypedHttpOperationAnalyzer.analyze(project)({ - controller, - generics, - operation, - declaration, - symbol, - }) - : TypedWebSocketOperationAnalyzer.analyze(project)({ - controller, - operation, - declaration, - symbol, - generics, - }); - ret.push(...routes); - } - return ret; - }; -} diff --git a/packages/sdk/src/analyses/TypedHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/TypedHttpOperationAnalyzer.ts deleted file mode 100644 index 1f1643a09..000000000 --- a/packages/sdk/src/analyses/TypedHttpOperationAnalyzer.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { RequestMethod } from "@nestjs/common"; -import path from "path"; -import { HashMap } from "tstl"; -import ts from "typescript"; -import { CommentFactory } from "typia/lib/factories/CommentFactory"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; - -import { IErrorReport } from "../structures/IErrorReport"; -import { INestiaProject } from "../structures/INestiaProject"; -import { IReflectController } from "../structures/IReflectController"; -import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; -import { ITypeTuple } from "../structures/ITypeTuple"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { PathUtil } from "../utils/PathUtil"; -import { VersioningStrategy } from "../utils/VersioningStrategy"; -import { ExceptionAnalyzer } from "./ExceptionAnalyzer"; -import { GenericAnalyzer } from "./GenericAnalyzer"; -import { ImportAnalyzer } from "./ImportAnalyzer"; -import { PathAnalyzer } from "./PathAnalyzer"; -import { SecurityAnalyzer } from "./SecurityAnalyzer"; - -export namespace TypedHttpOperationAnalyzer { - export const analyze = - (project: INestiaProject) => - (props: { - controller: IReflectController; - operation: IReflectHttpOperation; - declaration: ts.MethodDeclaration; - symbol: ts.Symbol; - generics: GenericAnalyzer.Dictionary; - }): ITypedHttpRoute[] => { - // CHECK TYPE - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const signature: ts.Signature | undefined = - project.checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; - if (signature === undefined) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: "unable to get the type signature.", - }); - return []; - } - - // SKIP @IGNORE TAG - const jsDocTags = signature.getJsDocTags(); - if (jsDocTags.some((tag) => tag.name === "ignore")) return []; - - // EXPLORE CHILDREN TYPES - const importDict: ImportAnalyzer.Dictionary = new HashMap(); - const parameters: Array = - props.operation.parameters.map( - (param) => - _Analyze_parameter(project)({ - generics: props.generics, - imports: importDict, - controller: props.controller, - function: props.operation.name, - parameter: param, - symbol: signature.getParameters()[param.index], - })!, - ); - const outputType: ITypeTuple | null = ImportAnalyzer.analyze( - project.checker, - )({ - generics: props.generics, - imports: importDict, - type: signature.getReturnType(), - }); - if (outputType === null) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: "unknown return type.", - }); - return []; - } else if ( - props.operation.method === "HEAD" && - outputType.typeName !== "void" && - outputType.typeName !== "undefined" - ) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: `HEAD method must return void type.`, - }); - return []; - } - - const exceptions = ExceptionAnalyzer.analyze(project)({ - generics: props.generics, - imports: project.config.propagate === true ? importDict : new HashMap(), - controller: props.controller, - operation: props.operation, - declaration: props.declaration, - }); - const imports: [string, string[]][] = importDict - .toJSON() - .map((pair) => [pair.first, pair.second.toJSON()]); - - // CONSIDER SECURITY TAGS - const security: Record[] = SecurityAnalyzer.merge( - ...props.controller.security, - ...props.operation.security, - ...jsDocTags - .filter((tag) => tag.name === "security") - .map((tag) => - tag.text === undefined - ? [{}] - : tag.text.map((text) => { - const line: string[] = text.text - .split(" ") - .filter((s) => s.trim()) - .filter((s) => !!s.length); - if (line.length === 0) return {}; - return { - [line[0]]: line.slice(1), - }; - }), - ) - .flat(), - ); - - // CONSTRUCT COMMON DATA - const common: Omit = { - ...props.operation, - controller: props.controller, - parameters: parameters.filter( - (p) => p !== null, - ) as ITypedHttpRoute.IParameter[], - output: { - type: outputType.type, - typeName: outputType.typeName, - contentType: props.operation.contentType, - }, - imports, - status: props.operation.status, - location: (() => { - const file = props.declaration.getSourceFile(); - const { line, character } = file.getLineAndCharacterOfPosition( - props.declaration.pos, - ); - return `${path.relative(process.cwd(), file.fileName)}:${line + 1}:${ - character + 1 - }`; - })(), - description: CommentFactory.description(props.symbol), - operationId: jsDocTags - .find(({ name }) => name === "operationId") - ?.text?.[0].text.split(" ")[0] - .trim(), - jsDocTags: jsDocTags, - setHeaders: jsDocTags - .filter( - (t) => - t.text?.length && - t.text[0].text && - (t.name === "setHeader" || t.name === "assignHeaders"), - ) - .map((t) => - t.name === "setHeader" - ? { - type: "setter", - source: t.text![0].text.split(" ")[0].trim(), - target: t.text![0].text.split(" ")[1]?.trim(), - } - : { - type: "assigner", - source: t.text![0].text, - }, - ), - security, - exceptions, - example: props.operation.example, - examples: props.operation.examples, - }; - - // CONFIGURE PATHS - const globalPrefix = project.input.globalPrefix ?? { - prefix: "", - }; - const pathList: Set = new Set(); - const versions: string[] = VersioningStrategy.merge(project)([ - ...(props.controller.versions ?? []), - ...(props.operation.versions ?? []), - ]); - for (const v of versions) - for (const prefix of wrapPaths(props.controller.prefixes)) - for (const cPath of wrapPaths(props.controller.paths)) - for (const filePath of wrapPaths(props.operation.paths)) - pathList.add( - PathAnalyzer.join( - globalPrefix.prefix, - v, - prefix, - cPath, - filePath, - ), - ); - return [...pathList] - .filter((path) => { - const escaped: string | null = PathAnalyzer.escape(path); - if (escaped === null) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: `unable to escape the path "${path}".`, - }); - return false; - } else if (Array.isArray(globalPrefix.exclude)) - return globalPrefix.exclude.some((e) => - typeof e === "string" - ? !RegExp(e).test(path) - : RegExp(e.path).test(path) && - (enumToMethod(e.method) === "all" || - enumToMethod(e.method) === - props.operation.method.toLowerCase()) && - (e.version === undefined || - versions.some((v) => v === e.version)), - ); - return true; - }) - .map((path) => ({ - ...common, - path: PathAnalyzer.escape(path)!, - accessors: [...PathUtil.accessors(path), props.operation.name], - })); - }; - - const _Analyze_parameter = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - function: string; - parameter: IReflectHttpOperation.IParameter; - symbol: ts.Symbol; - }): ITypedHttpRoute.IParameter | null => { - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const name: string = props.symbol.getEscapedName().toString(); - const optional: boolean = !!project.checker.symbolToParameterDeclaration( - props.symbol, - undefined, - undefined, - )?.questionToken; - - const errors: IErrorReport[] = []; - - // DO NOT SUPPORT BODY PARAMETER - if ( - props.parameter.category === "body" && - props.parameter.field !== undefined - ) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: - `nestia does not support body field specification. ` + - `Therefore, erase the "${name}" parameter and ` + - `re-define a new body decorator accepting full structured message.`, - }); - if (optional === true && props.parameter.category !== "query") - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: - `nestia does not support optional parameter except query parameter. ` + - `Therefore, erase question mark on the "${name}" parameter, ` + - `or re-define a new method without the "${name}" parameter.`, - }); - if ( - optional === true && - props.parameter.category === "query" && - props.parameter.field === undefined - ) { - const everyPropertiesAreOptional: boolean = (() => { - const res = MetadataFactory.analyze(project.checker)({ - escape: false, - constant: true, - absorb: true, - })(new MetadataCollection())(type); - if (res.success === false) return false; - const m = res.data; - return ( - m.size() === 1 && - m.objects.length === 1 && - m.objects[0]!.properties.every((p) => p.value.optional === true) - ); - })(); - if (everyPropertiesAreOptional === false) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: - `nestia does not support optional query parameter exception field specification or every properties are optional case. ` + - `Therefore, erase question mark on the "${name}" parameter, ` + - `or re-define re-define parameters for each query parameters.`, - }); - } - - // GET TYPE NAME - const tuple: ITypeTuple | null = ImportAnalyzer.analyze(project.checker)({ - generics: props.generics, - imports: props.imports, - type, - }); - if (tuple === null) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `unknown parameter type from ${JSON.stringify(name)}.`, - }); - if (errors.length) { - project.errors.push(...errors); - return null; - } - return { - ...props.parameter, - name, - optional, - type: tuple!.type, - typeName: tuple!.typeName, - description: CommentFactory.description(props.symbol), - jsDocTags: props.symbol.getJsDocTags(), - }; - }; -} - -const enumToMethod = (v: RequestMethod) => - v === RequestMethod.GET - ? "get" - : v === RequestMethod.POST - ? "post" - : v === RequestMethod.PUT - ? "put" - : v === RequestMethod.DELETE - ? "delete" - : v === RequestMethod.PATCH - ? "patch" - : v === RequestMethod.ALL - ? "all" - : v === RequestMethod.OPTIONS - ? "options" - : v === RequestMethod.HEAD - ? "head" - : "search"; - -const wrapPaths = (paths: string[]): string[] => - paths.length === 0 ? [""] : paths; diff --git a/packages/sdk/src/analyses/TypedWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/TypedWebSocketOperationAnalyzer.ts deleted file mode 100644 index 092481f5d..000000000 --- a/packages/sdk/src/analyses/TypedWebSocketOperationAnalyzer.ts +++ /dev/null @@ -1,375 +0,0 @@ -import path from "path"; -import { HashMap } from "tstl"; -import ts from "typescript"; -import { CommentFactory } from "typia/lib/factories/CommentFactory"; - -import { IErrorReport } from "../structures/IErrorReport"; -import { INestiaProject } from "../structures/INestiaProject"; -import { IReflectController } from "../structures/IReflectController"; -import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; -import { ITypeTuple } from "../structures/ITypeTuple"; -import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; -import { PathUtil } from "../utils/PathUtil"; -import { VersioningStrategy } from "../utils/VersioningStrategy"; -import { GenericAnalyzer } from "./GenericAnalyzer"; -import { ImportAnalyzer } from "./ImportAnalyzer"; -import { PathAnalyzer } from "./PathAnalyzer"; - -export namespace TypedWebSocketOperationAnalyzer { - export const analyze = - (project: INestiaProject) => - (props: { - controller: IReflectController; - operation: IReflectWebSocketOperation; - declaration: ts.MethodDeclaration; - symbol: ts.Symbol; - generics: GenericAnalyzer.Dictionary; - }): ITypedWebSocketRoute[] => { - // CHECK TYPE - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const signature: ts.Signature | undefined = - project.checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; - if (signature === undefined) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: "unable to get the type signature.", - }); - return []; - } - - // SKIP @IGNORE TAG - const jsDocTags = signature.getJsDocTags(); - if (jsDocTags.some((tag) => tag.name === "ignore")) return []; - - // EXPLORE CHILDREN TYPES - const importDict: ImportAnalyzer.Dictionary = new HashMap(); - const errors: IErrorReport[] = []; - const parameters: Array = - props.operation.parameters.map( - (param) => - _Analyze_parameter({ - ...project, - errors, - })({ - generics: props.generics, - imports: importDict, - controller: props.controller, - function: props.operation.name, - parameter: param, - symbol: signature.getParameters()[param.index], - })!, - ); - if (errors.length) { - project.errors.push(...errors); - return []; - } - - const imports: [string, string[]][] = importDict - .toJSON() - .map((pair) => [pair.first, pair.second.toJSON()]); - const common: Omit = { - ...props.operation, - controller: props.controller, - parameters, - imports, - location: (() => { - const file = props.declaration.getSourceFile(); - const { line, character } = file.getLineAndCharacterOfPosition( - props.declaration.pos, - ); - return `${path.relative(process.cwd(), file.fileName)}:${line + 1}:${ - character + 1 - }`; - })(), - description: CommentFactory.description(props.symbol), - jsDocTags, - }; - - // CONFIGURE PATHS - const pathList: Set = new Set(); - const versions: string[] = VersioningStrategy.merge(project)([ - ...(props.controller.versions ?? []), - ...(props.operation.versions ?? []), - ]); - for (const v of versions) - for (const prefix of wrapPaths(props.controller.prefixes)) - for (const cPath of wrapPaths(props.controller.paths)) - for (const filePath of wrapPaths(props.operation.paths)) - pathList.add( - PathAnalyzer.join( - project.input.globalPrefix?.prefix ?? "", - v, - prefix, - cPath, - filePath, - ), - ); - - return [...pathList] - .filter((path) => { - const escaped: string | null = PathAnalyzer.escape(path); - if (escaped === null) - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: `unable to escape the path "${path}".`, - }); - return escaped !== null; - }) - .map((path) => ({ - ...common, - path: PathAnalyzer.escape(path)!, - accessors: [...PathUtil.accessors(path), props.operation.name], - })); - }; - - const _Analyze_parameter = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - function: string; - parameter: IReflectWebSocketOperation.IParameter; - symbol: ts.Symbol; - }): ITypedWebSocketRoute.IParameter => { - if (props.parameter.category === "acceptor") - return _Analyze_acceptor(project)(props); - else if (props.parameter.category === "driver") - return _Analyze_driver(project)(props); - - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const name: string = props.symbol.getEscapedName().toString(); - - // VALIDATIONS - const errors: IErrorReport[] = []; - const tuple: ITypeTuple | null = ImportAnalyzer.analyze(project.checker)({ - generics: props.generics, - imports: props.imports, - type, - }); - if (tuple === null) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `unknown parameter type from "${props.function}@${name}".`, - }); - else if ( - tuple.typeName === "__type" || - tuple.typeName === "__object" || - tuple.typeName.startsWith("__type.") || - tuple.typeName.startsWith("__object.") - ) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `implicit (unnamed) parameter type from "${props.function}@${name}".`, - }); - _Check_optional({ - ...project, - errors, - })({ - ...props, - parameter: { - name, - symbol: props.symbol, - }, - }); - if (errors.length) { - project.errors.push(...errors); - return null!; - } - return { - ...props.parameter, - category: props.parameter.category, - name, - type: tuple!.type, - typeName: tuple!.typeName, - }; - }; - - const _Analyze_acceptor = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - function: string; - parameter: IReflectWebSocketOperation.IParameter; - symbol: ts.Symbol; - }): ITypedWebSocketRoute.IAcceptorParameter => { - // VALIDATIONS - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const name: string = props.symbol.getEscapedName().toString(); - const generics: readonly ts.Type[] = - type.aliasTypeArguments ?? - project.checker.getTypeArguments(type as ts.TypeReference) ?? - []; - - const errors: IErrorReport[] = []; - _Check_optional({ - ...project, - errors, - })({ - ...props, - parameter: { - name, - symbol: props.symbol, - }, - }); - if (generics.length !== 3) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `@WebSocketRoute.Acceptor() must have three type arguments of WebSocketAcceptor`, - }); - const [header, provider, listener] = [ - "header", - "provider", - "listener", - ].map((key, i) => { - const tuple: ITypeTuple | null = ImportAnalyzer.analyze( - project.checker, - )({ - generics: props.generics, - imports: props.imports, - type: generics[i], - }); - if (tuple === null) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `unable to analyze the "${key}" argument type of WebSocketAcceptor.`, - }); - return tuple!; - }); - - if (errors.length) { - project.errors.push(...errors); - return null!; - } - return { - ...props.parameter, - category: "acceptor", - name, - header, - provider, - listener: listener, - }; - }; - - const _Analyze_driver = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - function: string; - parameter: IReflectWebSocketOperation.IParameter; - symbol: ts.Symbol; - }): ITypedWebSocketRoute.IDriverParameter => { - // VALIDATIONS - const type: ts.Type = project.checker.getTypeOfSymbolAtLocation( - props.symbol, - props.symbol.valueDeclaration!, - ); - const name: string = props.symbol.getEscapedName().toString(); - const generics: readonly ts.Type[] = - type.aliasTypeArguments ?? - project.checker.getTypeArguments(type as ts.TypeReference) ?? - []; - - const errors: IErrorReport[] = []; - _Check_optional({ - ...project, - errors, - })({ - ...props, - parameter: { - name, - symbol: props.symbol, - }, - }); - const tuple: ITypeTuple = (() => { - if (generics.length !== 1) { - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `@WebSocketRoute.Driver() must have one type argument of WebDriver`, - }); - return null!; - } else { - const tuple: ITypeTuple | null = ImportAnalyzer.analyze( - project.checker, - )({ - generics: props.generics, - imports: props.imports, - type: generics[0], - }); - if (tuple === null) - errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `unable to analyze the "type" argument of WebDriver.`, - }); - return tuple!; - } - })(); - if (errors.length) { - project.errors.push(...errors); - return null!; - } - return { - ...props.parameter, - category: "driver", - name, - type: tuple.type, - typeName: tuple.typeName, - }; - }; - - const _Check_optional = - (project: INestiaProject) => - (props: { - controller: IReflectController; - function: string; - parameter: { - name: string; - symbol: ts.Symbol; - }; - }) => { - const optional: boolean = !!project.checker.symbolToParameterDeclaration( - props.parameter.symbol, - undefined, - undefined, - )?.questionToken; - if (optional === true) - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.function, - message: `@WebSocketRoute() does not allow optional parameter, but be detected from ${props.function}@${props.parameter.symbol.getEscapedName().toString()}.`, - }); - }; -} - -const wrapPaths = (paths: string[]): string[] => - paths.length === 0 ? [""] : paths; diff --git a/packages/sdk/src/decorators/OperationMetadata.ts b/packages/sdk/src/decorators/OperationMetadata.ts new file mode 100644 index 000000000..1e8be432d --- /dev/null +++ b/packages/sdk/src/decorators/OperationMetadata.ts @@ -0,0 +1,15 @@ +import { IOperationMetadata } from "../structures/IOperationMetadata"; + +export function OperationMetadata( + metadata: IOperationMetadata, +): MethodDecorator { + return function OperationMetadata(target, propertyKey, descriptor) { + Reflect.defineMetadata( + "nestia/OperationMetadata", + metadata, + target, + propertyKey, + ); + return descriptor; + }; +} diff --git a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts index 5682cd884..8ed41b899 100644 --- a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts +++ b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts @@ -32,15 +32,21 @@ export namespace NestiaConfigLoader { export const config = async ( file: string, - rawCompilerOptions: Record, + compilerOptions: Record, ): Promise => { if (fs.existsSync(path.resolve(file)) === false) throw new Error(`Unable to find "${file}" file.`); register({ emit: false, - compilerOptions: rawCompilerOptions, - require: rawCompilerOptions.baseUrl + compilerOptions: { + ...compilerOptions, + plugins: [ + ...(compilerOptions.plugins ?? []), + { transform: "@nestia/sdk/lib/transform" }, + ], + }, + require: compilerOptions.baseUrl ? ["tsconfig-paths/register"] : undefined, }); diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index b08ba9e0a..7bcea755b 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -76,7 +76,7 @@ export namespace SdkAliasCollection { if (project.config.propagate !== true) { const node: ts.TypeNode = name(project)(importer)(route.output); const type = project.checker.getTypeAtLocation(node); - const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0; + const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0; if ( project.config.clone === true || diff --git a/packages/sdk/src/structures/INestiaProject.ts b/packages/sdk/src/structures/INestiaProject.ts index 86a0da847..ed88d4042 100644 --- a/packages/sdk/src/structures/INestiaProject.ts +++ b/packages/sdk/src/structures/INestiaProject.ts @@ -2,11 +2,11 @@ import ts from "typescript"; import { INestiaConfig } from "../INestiaConfig"; import { IErrorReport } from "./IErrorReport"; -import { INormalizedInput } from "./INormalizedInput"; +import { INestiaSdkInput } from "./INestiaSdkInput"; export interface INestiaProject { config: INestiaConfig; - input: INormalizedInput; + input: INestiaSdkInput; checker: ts.TypeChecker; errors: IErrorReport[]; warnings: IErrorReport[]; diff --git a/packages/sdk/src/structures/INestiaSdkInput.ts b/packages/sdk/src/structures/INestiaSdkInput.ts new file mode 100644 index 000000000..01a332334 --- /dev/null +++ b/packages/sdk/src/structures/INestiaSdkInput.ts @@ -0,0 +1,25 @@ +import { + INestApplication, + RouteInfo, + VersionValue, +} from "@nestjs/common/interfaces"; + +export interface INestiaSdkInput { + application: INestApplication; + controllers: INestiaSdkInput.IController[]; + globalPrefix?: { + prefix: string; + exclude?: Array; + }; + versioning?: { + prefix: string; + defaultVersion?: VersionValue; + }; +} +export namespace INestiaSdkInput { + export interface IController { + class: Function; + location: string; + prefixes: string[]; + } +} diff --git a/packages/sdk/src/structures/INormalizedInput.ts b/packages/sdk/src/structures/INormalizedInput.ts deleted file mode 100644 index 5555ad133..000000000 --- a/packages/sdk/src/structures/INormalizedInput.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RouteInfo, VersionValue } from "@nestjs/common/interfaces"; - -export interface INormalizedInput { - include: INormalizedInput.IInput[]; - globalPrefix?: { - prefix: string; - exclude?: Array; - }; - versioning?: { - prefix: string; - defaultVersion?: VersionValue; - }; -} -export namespace INormalizedInput { - export interface IInput { - paths: string[]; - file: string; - controller?: Function; - } -} diff --git a/packages/sdk/src/structures/IOperationMetadata.ts b/packages/sdk/src/structures/IOperationMetadata.ts new file mode 100644 index 000000000..3daac686f --- /dev/null +++ b/packages/sdk/src/structures/IOperationMetadata.ts @@ -0,0 +1,34 @@ +import { IJsDocTagInfo, Primitive } from "typia"; +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; + +import { IReflectType } from "./IReflectType"; +import { IReflectTypeImport } from "./IReflectTypeImport"; + +export interface IOperationMetadata { + parameters: IOperationMetadata.IParameter[]; + success: IOperationMetadata.IResponse; + exceptions: Record; + description: string | null; + jsDocTags: IJsDocTagInfo[]; +} +export namespace IOperationMetadata { + export interface IParameter extends IResponse { + name: string; + index: number; + } + export interface IResponse { + imports: IReflectTypeImport[]; + components: IMetadataComponents; + primitive: ISchema | null; + resolved: ISchema | null; + type: IReflectType | null; + required: boolean; + errors: Primitive[]; + } + export interface ISchema { + components: IMetadataComponents; + value: IMetadata; + } +} diff --git a/packages/sdk/src/structures/IReflectController.ts b/packages/sdk/src/structures/IReflectController.ts index 0b5050262..0986c867a 100644 --- a/packages/sdk/src/structures/IReflectController.ts +++ b/packages/sdk/src/structures/IReflectController.ts @@ -4,14 +4,12 @@ import { IReflectHttpOperation } from "./IReflectHttpOperation"; import { IReflectWebSocketOperation } from "./IReflectWebSocketOperation"; export interface IReflectController { - constructor: Function; - prototype: any; - file: string; - name: string; + class: Function; prefixes: string[]; paths: string[]; + file: string; versions: Array | undefined; operations: Array; security: Record[]; - swaggerTgas: string[]; + tags: string[]; } diff --git a/packages/sdk/src/structures/IReflectHttpOperation.ts b/packages/sdk/src/structures/IReflectHttpOperation.ts index c01e44e07..e7c20cce2 100644 --- a/packages/sdk/src/structures/IReflectHttpOperation.ts +++ b/packages/sdk/src/structures/IReflectHttpOperation.ts @@ -1,6 +1,9 @@ import { VERSION_NEUTRAL } from "@nestjs/common/interfaces"; -import { ParamCategory } from "./ParamCategory"; +import { IReflectHttpOperationException } from "./IReflectHttpOperationException"; +import { IReflectHttpOperationParameter } from "./IReflectHttpOperationParameter"; +import { IReflectHttpOperationSuccess } from "./IReflectHttpOperationSuccess"; +import { IReflectTypeImport } from "./IReflectTypeImport"; export interface IReflectHttpOperation { protocol: "http"; @@ -9,82 +12,13 @@ export interface IReflectHttpOperation { method: string; paths: string[]; versions: Array | undefined; - encrypted: boolean; - parameters: IReflectHttpOperation.IParameter[]; - status?: number; - type?: string; - contentType: "application/json" | "text/plain"; - security: Record[]; + parameters: IReflectHttpOperationParameter[]; + success: IReflectHttpOperationSuccess; exceptions: Record< number | "2XX" | "3XX" | "4XX" | "5XX", - IReflectHttpOperation.IException + IReflectHttpOperationException >; - example?: any; - examples?: Record; - swaggerTags: string[]; -} -export namespace IReflectHttpOperation { - export type IParameter = - | ICommonParameter - | IQueryParameter - | IHeadersParameter - | IBodyParameter - | IPathParameter; - export interface ICommonParameter { - custom: false; - category: ParamCategory; - index: number; - name: string; - field: string | undefined; - example?: any; - examples?: Record; - } - export interface IHeadersParameter { - custom: true; - category: "headers"; - index: number; - name: string; - field: string | undefined; - example?: any; - examples?: Record; - } - export interface IQueryParameter { - custom: true; - category: "query"; - index: number; - name: string; - field: string | undefined; - example?: any; - examples?: Record; - } - export interface IBodyParameter { - custom: true; - category: "body"; - index: number; - name: string; - field: string | undefined; - encrypted: boolean; - contentType: - | "application/json" - | "application/x-www-form-urlencoded" - | "multipart/form-data" - | "text/plain"; - example?: any; - examples?: Record; - } - export interface IPathParameter { - custom: true; - category: "param"; - index: number; - name: string; - field: string | undefined; - example?: any; - examples?: Record; - } - - export interface IException { - type: string; - status: number | "2XX" | "3XX" | "4XX" | "5XX"; - description: string | undefined; - } + security: Record[]; + tags: string[]; + imports: IReflectTypeImport[]; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationException.ts b/packages/sdk/src/structures/IReflectHttpOperationException.ts new file mode 100644 index 000000000..51f73949f --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationException.ts @@ -0,0 +1,17 @@ +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; + +import { IReflectType } from "./IReflectType"; + +export interface IReflectHttpOperationException { + // BASIC PROPERTIES + status: number | "2XX" | "3XX" | "4XX" | "5XX"; + description: string | null; + example: any; + examples: Record; + + // REFLECTED PROPERTIES + type: IReflectType; + schema: IMetadata; + components: IMetadataComponents; +} diff --git a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts new file mode 100644 index 000000000..8180915a8 --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts @@ -0,0 +1,76 @@ +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; + +import { IReflectType } from "./IReflectType"; + +export type IReflectHttpOperationParameter = + | IReflectHttpOperationParameter.IBody + | IReflectHttpOperationParameter.IHeaders + | IReflectHttpOperationParameter.IParam + | IReflectHttpOperationParameter.IQuery; +export namespace IReflectHttpOperationParameter { + export interface IBody extends IBase<"body"> { + contentType: + | "application/json" + | "application/x-www-form-urlencoded" + | "multipart/form-data" + | "text/plain"; + encrypted: boolean; + } + export interface IHeaders extends IBase<"headers"> { + field: string | null; + } + export interface IParam extends IBase<"param"> { + field: string; + } + export interface IQuery extends IBase<"query"> { + field: string | null; + } + interface IBase { + kind: Kind; + name: string; + index: number; + type: IReflectType; + schema: IMetadata; + components: IMetadataComponents; + example?: any; + examples?: Record; + } + + /** + * @internal + */ + export type IPreconfigured = + | IPreconfigured.IBody + | IPreconfigured.IHeaders + | IPreconfigured.IParam + | IPreconfigured.IQuery; + + /** + * @internal + */ + export namespace IPreconfigured { + export interface IBody extends IBase<"body"> { + field?: string; + encrypted?: boolean; + contentType: + | "application/json" + | "application/x-www-form-urlencoded" + | "multipart/form-data" + | "text/plain"; + } + export interface IHeaders extends IBase<"headers"> { + field?: string; + } + export interface IParam extends IBase<"param"> { + field?: string; + } + export interface IQuery extends IBase<"query"> { + field?: string; + } + interface IBase { + kind: Kind; + index: number; + } + } +} diff --git a/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts new file mode 100644 index 000000000..12443f8f1 --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts @@ -0,0 +1,20 @@ +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; + +import { IReflectType } from "./IReflectType"; + +export interface IReflectHttpOperationSuccess { + components: IMetadataComponents; + type: IReflectType; + schema: IMetadata; + status: number; + contentType: + | "application/json" + | "text/plain" + | "application/x-www-form-urlencoded" + | "application/json" + | null; + encrypted: boolean; + example?: any; + examples?: Record; +} diff --git a/packages/sdk/src/structures/IReflectType.ts b/packages/sdk/src/structures/IReflectType.ts new file mode 100644 index 000000000..3c9f6e3d0 --- /dev/null +++ b/packages/sdk/src/structures/IReflectType.ts @@ -0,0 +1,4 @@ +export interface IReflectType { + name: string; + typeArguments?: IReflectType[]; +} diff --git a/packages/sdk/src/structures/IReflectTypeImport.ts b/packages/sdk/src/structures/IReflectTypeImport.ts new file mode 100644 index 000000000..2ec9b5e5b --- /dev/null +++ b/packages/sdk/src/structures/IReflectTypeImport.ts @@ -0,0 +1,4 @@ +export interface IReflectTypeImport { + file: string; + instances: string[]; +} diff --git a/packages/sdk/src/structures/IReflectWebSocketOperation.ts b/packages/sdk/src/structures/IReflectWebSocketOperation.ts index 4d6a98668..8aa9a8235 100644 --- a/packages/sdk/src/structures/IReflectWebSocketOperation.ts +++ b/packages/sdk/src/structures/IReflectWebSocketOperation.ts @@ -1,17 +1,17 @@ import { VERSION_NEUTRAL } from "@nestjs/common"; +import ts from "typescript"; + +import { IReflectTypeImport } from "./IReflectTypeImport"; +import { IReflectWebSocketOperationParameter } from "./IReflectWebSocketOperationParameter"; export interface IReflectWebSocketOperation { protocol: "websocket"; - target: Function; name: string; paths: string[]; + function: Function; versions: Array | undefined; - parameters: IReflectWebSocketOperation.IParameter[]; -} -export namespace IReflectWebSocketOperation { - export interface IParameter { - category: "acceptor" | "driver" | "header" | "param" | "query"; - field: string; - index: number; - } + parameters: IReflectWebSocketOperationParameter[]; + imports: IReflectTypeImport[]; + description: string | null; + jsDocTags: ts.JSDocTagInfo[]; } diff --git a/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts new file mode 100644 index 000000000..aff91ec35 --- /dev/null +++ b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts @@ -0,0 +1,32 @@ +import { IReflectType } from "./IReflectType"; + +export type IReflectWebSocketOperationParameter = + | IReflectWebSocketOperationParameter.IAcceptorParameter + | IReflectWebSocketOperationParameter.IDriverParameter + | IReflectWebSocketOperationParameter.IHeaderParameter + | IReflectWebSocketOperationParameter.IParamParameter + | IReflectWebSocketOperationParameter.IQueryParameter; +export namespace IReflectWebSocketOperationParameter { + export type IAcceptorParameter = IBase<"acceptor">; + export type IDriverParameter = IBase<"driver">; + export type IHeaderParameter = IBase<"header">; + export type IQueryParameter = IBase<"query">; + export interface IParamParameter extends IBase<"param"> { + field: string; + } + interface IBase { + kind: Kind; + name: string; + index: number; + type: IReflectType; + } + + /** + * @internal + */ + export interface IPreconfigured { + kind: "acceptor" | "driver" | "header" | "param" | "query"; + index: number; + field?: string; + } +} diff --git a/packages/sdk/src/structures/ITypeTuple.ts b/packages/sdk/src/structures/ITypeTuple.ts deleted file mode 100644 index 16f5dff38..000000000 --- a/packages/sdk/src/structures/ITypeTuple.ts +++ /dev/null @@ -1,6 +0,0 @@ -import ts from "typescript"; - -export interface ITypeTuple { - type: ts.Type; - typeName: string; -} diff --git a/packages/sdk/src/structures/ITypedHttpRoute.ts b/packages/sdk/src/structures/ITypedHttpRoute.ts deleted file mode 100644 index d034d5772..000000000 --- a/packages/sdk/src/structures/ITypedHttpRoute.ts +++ /dev/null @@ -1,59 +0,0 @@ -import ts from "typescript"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; - -import { IReflectController } from "./IReflectController"; -import { IReflectHttpOperation } from "./IReflectHttpOperation"; - -export interface ITypedHttpRoute { - protocol: "http"; - controller: IReflectController; - function: Function; - name: string; - method: string; - path: string; - encrypted: boolean; - status?: number; - - accessors: string[]; - parameters: ITypedHttpRoute.IParameter[]; - imports: [string, string[]][]; - output: ITypedHttpRoute.IOutput; - - location: string; - description?: string; - operationId?: string; - jsDocTags: ts.JSDocTagInfo[]; - setHeaders: Array< - | { type: "setter"; source: string; target?: string } - | { type: "assigner"; source: string } - >; - security: Record[]; - exceptions: Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - ITypedHttpRoute.IOutput - >; - example?: any; - examples?: Record; - swaggerTags: string[]; -} - -export namespace ITypedHttpRoute { - export type IParameter = IReflectHttpOperation.IParameter & { - optional: boolean; - type: ts.Type; - typeName: string; - metadata?: Metadata; - description?: string; - jsDocTags: ts.JSDocTagInfo[]; - }; - export interface IOutput { - type: ts.Type; - typeName: string; - metadata?: Metadata; - description?: string; - contentType: - | "application/x-www-form-urlencoded" - | "application/json" - | "text/plain"; - } -} diff --git a/packages/sdk/src/structures/ITypedWebSocketRoute.ts b/packages/sdk/src/structures/ITypedWebSocketRoute.ts deleted file mode 100644 index 5d9372604..000000000 --- a/packages/sdk/src/structures/ITypedWebSocketRoute.ts +++ /dev/null @@ -1,68 +0,0 @@ -import ts from "typescript"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; - -import { IReflectController } from "./IReflectController"; -import { IReflectWebSocketOperation } from "./IReflectWebSocketOperation"; -import { ITypeTuple } from "./ITypeTuple"; - -export interface ITypedWebSocketRoute { - protocol: "websocket"; - controller: IReflectController; - name: string; - path: string; - - accessors: string[]; - parameters: ITypedWebSocketRoute.IParameter[]; - imports: [string, string[]][]; - - location: string; - description?: string; - jsDocTags: ts.JSDocTagInfo[]; -} -export namespace ITypedWebSocketRoute { - export type IParameter = - | IAcceptorParameter - | IDriverParameter - | IHeaderParameter - | IPathParameter - | IQueryParameter; - export interface IAcceptorParameter - extends Omit { - category: "acceptor"; - name: string; - header: ITypeTuple; - provider: ITypeTuple; - listener: ITypeTuple; - } - export interface IDriverParameter - extends Omit { - category: "driver"; - name: string; - type: ts.Type; - typeName: string; - } - export interface IHeaderParameter - extends Omit { - category: "header"; - name: string; - type: ts.Type; - typeName: string; - metadata?: Metadata; - } - export interface IPathParameter - extends Omit { - category: "param"; - name: string; - type: ts.Type; - typeName: string; - metadata?: Metadata; - } - export interface IQueryParameter - extends Omit { - category: "query"; - name: string; - type: ts.Type; - typeName: string; - metadata?: Metadata; - } -} diff --git a/packages/sdk/src/transform.ts b/packages/sdk/src/transform.ts new file mode 100644 index 000000000..8df960f56 --- /dev/null +++ b/packages/sdk/src/transform.ts @@ -0,0 +1,9 @@ +import ts from "typescript"; + +import { SdkTransformer } from "./transformers/SdkTransformer"; + +export const transform = ( + program: ts.Program, +): ts.TransformerFactory => + SdkTransformer.transformFile(program.getTypeChecker()); +export default transform; diff --git a/packages/sdk/src/transformers/ISdkTransformerContext.ts b/packages/sdk/src/transformers/ISdkTransformerContext.ts new file mode 100644 index 000000000..500a6ea2a --- /dev/null +++ b/packages/sdk/src/transformers/ISdkTransformerContext.ts @@ -0,0 +1,8 @@ +import ts from "typescript"; +import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; + +export interface ISdkTransformerContext { + checker: ts.TypeChecker; + api: ts.TransformationContext; + collection: MetadataCollection; +} diff --git a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts new file mode 100644 index 000000000..097fef77d --- /dev/null +++ b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts @@ -0,0 +1,205 @@ +import ts from "typescript"; +import { CommentFactory } from "typia/lib/factories/CommentFactory"; +import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { TypeFactory } from "typia/lib/factories/TypeFactory"; +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { ImportAnalyzer } from "../analyses/ImportAnalyzer"; +import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { ISdkTransformerContext } from "./ISdkTransformerContext"; + +export namespace SdkMetadataProgrammer { + export interface IProps { + context: ISdkTransformerContext; + generics: WeakMap; + node: ts.MethodDeclaration; + } + export const write = (p: IProps): IOperationMetadata => { + const symbol: ts.Symbol | undefined = p.context.checker.getSymbolAtLocation( + p.node, + ); + const signature: ts.Signature | undefined = + p.context.checker.getSignatureFromDeclaration(p.node); + return { + parameters: p.node.parameters.map((parameter, index) => + writeParameter({ + context: p.context, + generics: p.generics, + parameter, + index, + }), + ), + success: writeResponse({ + context: p.context, + generics: p.generics, + type: getReturnType({ + checker: p.context.checker, + signature, + }), + }), + exceptions: {}, // @todo + description: (symbol && CommentFactory.description(symbol)) ?? null, + jsDocTags: signature?.getJsDocTags() ?? [], + }; + }; + + const writeParameter = (props: { + context: ISdkTransformerContext; + generics: WeakMap; + parameter: ts.ParameterDeclaration; + index: number; + }): IOperationMetadata.IParameter => ({ + ...writeType({ + ...props, + type: + props.context.checker.getTypeFromTypeNode( + props.parameter.type ?? TypeFactory.keyword("any"), + ) ?? null, + required: props.parameter.questionToken === undefined, + }), + name: props.parameter.name.getText(), + index: props.index, + }); + + const writeResponse = (props: { + context: ISdkTransformerContext; + generics: WeakMap; + type: ts.Type | null; + }): IOperationMetadata.IResponse => + writeType({ + ...props, + required: true, + }); + + const writeType = (p: { + context: ISdkTransformerContext; + generics: WeakMap; + type: ts.Type | null; + required: boolean; + }): IOperationMetadata.IResponse => { + const result = MetadataFactory.analyze( + p.context.checker, + p.context.api, + )({ + escape: true, + constant: true, + absorb: false, + })(p.context.collection)(p.type); + if (result.success === false) result.errors; + const analyzed: ImportAnalyzer.IOutput = p.type + ? ImportAnalyzer.analyze(p.context.checker, p.generics, p.type) + : { + type: { name: "any" }, + imports: [], + }; + return { + ...analyzed, + ...(result.success + ? { + ...writeSchema({ + collection: p.context.collection, + metadata: result.data, + }), + errors: [], + } + : { + schema: null, + components: { + objects: [], + arrays: [], + tuples: [], + aliases: [], + } satisfies IMetadataComponents, + errors: result.errors, + }), + required: !( + p.required === false || + (result.success && result.data.isRequired() === false) + ), + }; + }; + + const writeSchema = (p: { + collection: MetadataCollection; + metadata: Metadata; + }): { + components: IMetadataComponents; + schema: IMetadata; + } => { + const visited: Set = iterateVisited(p.metadata); + return { + components: { + objects: p.collection + .objects() + .filter((o) => visited.has(o.name)) + .map((o) => o.toJSON()), + aliases: p.collection + .aliases() + .filter((a) => visited.has(a.name)) + .map((a) => a.toJSON()), + arrays: p.collection + .arrays() + .filter((a) => visited.has(a.name)) + .map((a) => a.toJSON()), + tuples: p.collection + .tuples() + .filter((t) => visited.has(t.name)) + .map((t) => t.toJSON()), + }, + schema: p.metadata.toJSON(), + }; + }; + + const getReturnType = (p: { + checker: ts.TypeChecker; + signature: ts.Signature | undefined; + }): ts.Type | null => { + const type: ts.Type | null = p.signature?.getReturnType() ?? null; + if (type === null) return null; + else if (type.symbol?.name === "Promise") { + const generic: readonly ts.Type[] = p.checker.getTypeArguments( + type as ts.TypeReference, + ); + return generic[0] ?? null; + } + return type; + }; +} + +const iterateVisited = (metdata: Metadata): Set => { + const names: Set = new Set(); + const visited: WeakSet = new WeakSet(); + const iterate = (metadata: Metadata): void => { + if (visited.has(metadata)) return; + visited.add(metadata); + for (const alias of metadata.aliases) { + names.add(alias.name); + iterate(alias.value); + } + for (const array of metadata.arrays) { + names.add(array.type.name); + iterate(array.type.value); + } + for (const tuple of metadata.tuples) { + names.add(tuple.type.name); + tuple.type.elements.map(iterate); + } + for (const object of metadata.objects) { + names.add(object.name); + object.properties.map((p) => { + iterate(p.key); + iterate(p.value); + }); + } + if (metadata.escaped) { + iterate(metadata.escaped.original); + iterate(metadata.escaped.returns); + } + if (metadata.rest) iterate(metadata.rest); + }; + iterate(metdata); + return names; +}; diff --git a/packages/sdk/src/transformers/SdkTransformer.ts b/packages/sdk/src/transformers/SdkTransformer.ts new file mode 100644 index 000000000..8389db0c2 --- /dev/null +++ b/packages/sdk/src/transformers/SdkTransformer.ts @@ -0,0 +1,168 @@ +import { HashSet, hash } from "tstl"; +import ts from "typescript"; +import { LiteralFactory } from "typia/lib/factories/LiteralFactory"; +import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; +import { TypeFactory } from "typia/lib/factories/TypeFactory"; + +import { GenericAnalyzer } from "../analyses/GenericAnalyzer"; +import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { ISdkTransformerContext } from "./ISdkTransformerContext"; +import { SdkMetadataProgrammer } from "./SdkMetadataProgrammer"; + +export namespace SdkTransformer { + export const transformFile = + (checker: ts.TypeChecker) => (api: ts.TransformationContext) => { + const context: ISdkTransformerContext = { + checker, + api, + collection: new MetadataCollection({ + replace: MetadataCollection.replace, + }), + }; + return (file: ts.SourceFile): ts.SourceFile => { + if (file.isDeclarationFile === true) return file; + const visitor: IVisitor = { + done: false, + visited: new HashSet(), + }; + file = ts.visitEachChild( + file, + (node) => + transformNode({ + context, + visitor, + node, + }), + api, + ); + if (visitor.done === false) return file; + return ts.factory.updateSourceFile(file, [ + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + false, + undefined, + ts.factory.createNamespaceImport( + ts.factory.createIdentifier("__OperationMetadata"), + ), + ), + ts.factory.createStringLiteral( + "@nestia/sdk/lib/decorators/OperationMetadata", + ), + undefined, + ), + ...file.statements, + ]); + }; + }; + + interface IVisitor { + done: boolean; + visited: HashSet; + } + + const transformNode = (props: { + context: ISdkTransformerContext; + visitor: IVisitor; + node: ts.Node; + }): ts.Node => + ts.isClassDeclaration(props.node) + ? transformClass({ + ...props, + node: props.node, + }) + : props.node; + + const transformClass = (props: { + context: ISdkTransformerContext; + visitor: IVisitor; + node: ts.ClassDeclaration; + }): ts.ClassDeclaration => { + const generics: WeakMap = GenericAnalyzer.analyze( + props.context.checker, + props.node, + ); + return ts.factory.updateClassDeclaration( + props.node, + props.node.modifiers, + props.node.name, + props.node.typeParameters, + props.node.heritageClauses, + props.node.members.map((m) => + ts.isMethodDeclaration(m) + ? transformMethod({ + ...props, + generics, + class: props.node, + node: m, + }) + : m, + ), + ); + }; + + const transformMethod = (props: { + context: ISdkTransformerContext; + visitor: IVisitor; + class: ts.ClassDeclaration; + generics: WeakMap; + node: ts.MethodDeclaration; + }): ts.MethodDeclaration => { + const decorators: readonly ts.Decorator[] | undefined = ts.getDecorators + ? ts.getDecorators(props.node) + : (props.node as any).decorators; + if (!decorators?.length) return props.node; + + const key: MethodKey = new MethodKey( + props.class.name?.getText() ?? "", + props.node.name.getText(), + ); + props.visitor.done ||= true; + if (props.visitor.visited.has(key)) return props.node; + else props.visitor.visited.insert(key); + + const metadata: IOperationMetadata = SdkMetadataProgrammer.write(props); + return ts.factory.updateMethodDeclaration( + props.node, + [ + ...(props.node.modifiers ?? []), + ts.factory.createDecorator( + ts.factory.createCallExpression( + ts.factory.createIdentifier( + "__OperationMetadata.OperationMetadata", + ), + undefined, + [ + ts.factory.createAsExpression( + LiteralFactory.generate(metadata), + TypeFactory.keyword("any"), + ), + ], + ), + ), + ], + props.node.asteriskToken, + props.node.name, + props.node.questionToken, + props.node.typeParameters, + props.node.parameters, + props.node.type, + props.node.body, + ); + }; +} + +class MethodKey { + public constructor( + public readonly className: string, + public readonly methodName: string, + ) {} + + public equals(o: MethodKey): boolean { + return this.className === o.className && this.methodName === o.methodName; + } + + public hashCode(): number { + return hash(this.className, this.methodName); + } +} diff --git a/packages/sdk/src/utils/StringUtil.ts b/packages/sdk/src/utils/StringUtil.ts index 3fcc64c70..92aa5c9ee 100644 --- a/packages/sdk/src/utils/StringUtil.ts +++ b/packages/sdk/src/utils/StringUtil.ts @@ -1,4 +1,7 @@ export namespace StringUtil { + export const capitalize = (text: string): string => + text.charAt(0).toUpperCase() + text.slice(1); + export const escapeDuplicate = (keep: string[]) => (change: string): string => diff --git a/test/features/app/swagger.json b/test/features/app/swagger.json index ac9c7b00c..4b5e8ad73 100644 --- a/test/features/app/swagger.json +++ b/test/features/app/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.10.0","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app/tsconfig.json b/test/features/app/tsconfig.json index c33dfa28f..1487dbe83 100644 --- a/test/features/app/tsconfig.json +++ b/test/features/app/tsconfig.json @@ -45,9 +45,9 @@ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ + "outDir": "./bin", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ - "noEmit": true, /* Disable emitting files from a compilation. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ @@ -93,6 +93,7 @@ { "transform": "typescript-transform-paths" }, { "transform": "typia/lib/transform" }, { "transform": "@nestia/core/lib/transform" }, + { "transform": "@nestia/sdk/lib/transform" }, // @todo -> must be removed ], } } \ No newline at end of file diff --git a/test/package.json b/test/package.json index b9266615b..25a05634e 100644 --- a/test/package.json +++ b/test/package.json @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "^3.10.0", + "@nestia/sdk": "../packages/sdk/nestia-sdk-3.10.0.tgz", "@nestjs/swagger": "^7.1.2", "@samchon/openapi": "^0.4.3", "@types/express": "^4.17.17", @@ -40,9 +40,9 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "^3.10.0", + "@nestia/core": "../packages/core/nestia-core-3.10.0.tgz", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "^3.10.0", + "@nestia/fetcher": "../packages/fetcher/nestia-fetcher-3.10.0.tgz", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/platform-express": "^10.3.5", From e38832bd6ef2b10df956707c7096819590c13c43 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Sun, 11 Aug 2024 03:34:30 +0900 Subject: [PATCH 02/21] Completion of reflect analyzer --- .../core/src/decorators/TypedException.ts | 58 ++++- .../src/analyses/ReflectControllerAnalyzer.ts | 7 +- .../analyses/ReflectHttpOperationAnalyzer.ts | 161 ++------------ .../ReflectHttpOperationParameterAnalyzer.ts | 210 +++++++++++------- .../ReflectHttpOperationResponseAnalyzer.ts | 53 +++-- .../ReflectWebSocketOperationAnalyzer.ts | 43 ++-- .../sdk/src/decorators/OperationMetadata.ts | 2 +- packages/sdk/src/structures/IErrorReport.ts | 6 - packages/sdk/src/structures/INestiaProject.ts | 6 +- .../IReflectHttpOperationException.ts | 2 + .../IReflectHttpOperationParameter.ts | 4 +- .../IReflectHttpOperationSuccess.ts | 6 +- .../src/structures/IReflectOperationError.ts | 9 + .../IOperationMetadata.ts | 24 +- .../src/transformers/SdkMetadataProgrammer.ts | 132 ++++++----- .../sdk/src/transformers/SdkTransformer.ts | 2 +- .../src/transformers/TextPlainValidator.ts | 17 ++ 17 files changed, 398 insertions(+), 344 deletions(-) delete mode 100644 packages/sdk/src/structures/IErrorReport.ts create mode 100644 packages/sdk/src/structures/IReflectOperationError.ts rename packages/sdk/src/{structures => transformers}/IOperationMetadata.ts (60%) create mode 100644 packages/sdk/src/transformers/TextPlainValidator.ts diff --git a/packages/core/src/decorators/TypedException.ts b/packages/core/src/decorators/TypedException.ts index a6851b0ac..9304c8965 100644 --- a/packages/core/src/decorators/TypedException.ts +++ b/packages/core/src/decorators/TypedException.ts @@ -12,11 +12,35 @@ * * @param props Properties for the exception * @returns Method decorator - * * @author Jeongho Nam - https://github.com/samchon */ export function TypedException(props: TypedException.IProps): never; +/** + * > You must configure the generic argument `T` + * + * Exception decorator. + * + * `TypedException` is a decorator function describing HTTP exception and its type + * which could be occured in the method. + * + * For reference, this decorator function does not affect to the method's behavior, + * but only affects to the swagger documents generation. Also, it does not affect to + * the SDK library generation yet, but will be used in the future. + * + * @param status Status number or pattern like "2XX", "3XX", "4XX", "5XX" + * @param description Description about the exception + * @returns Method decorator + * + * @deprecated Use {@link TypedException.IProps} typed function instead. + * This typed function is deprecated and will be removed in the next major update. + * @author Jeongho Nam - https://github.com/samchon + */ +export function TypedException( + status: number | "2XX" | "3XX" | "4XX" | "5XX", + description?: string | undefined, +): never; + /** * Exception decorator. * @@ -38,11 +62,37 @@ export function TypedException( ): MethodDecorator; /** - * @internal + * Exception decorator. + * + * `TypedException` is a decorator function describing HTTP exception and its type + * which could be occured in the method. + * + * For reference, this decorator function does not affect to the method's behavior, + * but only affects to the swagger documents generation. Also, it does not affect to + * the SDK library generation yet, but will be used in the future. + * + * @template T Type of the exception + * @param status Status number or pattern like "2XX", "3XX", "4XX", "5XX" + * @param description Description about the exception + * @returns Method decorator + * + * @deprecated Use {@link TypedException.IProps} typed function instead. + * This typed function is deprecated and will be removed in the next major update. + * @author Jeongho Nam - https://github.com/samchon */ export function TypedException( - props: TypedException.IProps, -): MethodDecorator { + status: number | "2XX" | "3XX" | "4XX" | "5XX", + description?: string | undefined, +): MethodDecorator; + +/** + * @internal + */ +export function TypedException(...args: any[]): MethodDecorator { + const props: TypedException.IProps = + typeof args[0] === "object" + ? args[0] + : { status: args[0], description: args[1] }; return function TypedException( target: Object | T, propertyKey: string | symbol, diff --git a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts index 68742bedf..9ef7eee69 100644 --- a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts @@ -6,10 +6,10 @@ import { import { INestiaProject } from "../structures/INestiaProject"; import { INestiaSdkInput } from "../structures/INestiaSdkInput"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; import { ArrayUtil } from "../utils/ArrayUtil"; import { ReflectHttpOperationAnalyzer } from "./ReflectHttpOperationAnalyzer"; import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer"; @@ -47,9 +47,10 @@ export namespace ReflectControllerAnalyzer { if (str.includes("*") === true) { props.project.warnings.push({ file: props.controller.location, - controller: props.controller.class.name, + class: props.controller.class.name, function: null, - message: "@nestia/sdk does not compose wildcard controller.", + from: null, + contents: ["@nestia/sdk does not compose wildcard controller."], }); return false; } diff --git a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts index 58a2e1522..e38a3b7f6 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -1,19 +1,13 @@ -import { - INTERCEPTORS_METADATA, - METHOD_METADATA, - PATH_METADATA, -} from "@nestjs/common/constants"; -import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum"; +import { METHOD_METADATA, PATH_METADATA } from "@nestjs/common/constants"; import { ranges } from "tstl"; -import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess"; -import { ParamCategory } from "../structures/ParamCategory"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; import { ArrayUtil } from "../utils/ArrayUtil"; import { ImportAnalyzer } from "./ImportAnalyzer"; import { PathAnalyzer } from "./PathAnalyzer"; @@ -39,7 +33,7 @@ export namespace ReflectHttpOperationAnalyzer { ) return null; - const errors: IErrorReport[] = []; + const errors: IReflectOperationError[] = []; const method: string = METHODS[Reflect.getMetadata(METHOD_METADATA, props.function)]; if (method === undefined || method === "OPTIONS") return null; @@ -51,31 +45,17 @@ export namespace ReflectHttpOperationAnalyzer { httpMethod: method, function: props.function, functionName: props.name, - report: (message) => - errors.push({ - file: props.controller.file, - controller: props.controller.class.name, - function: props.name, - message, - }), - isError: () => errors.length !== 0, + errors, }); const success: IReflectHttpOperationSuccess | null = (() => { - const localErrors: IErrorReport[] = []; + const localErrors: IReflectOperationError[] = []; const success = ReflectHttpOperationResponseAnalyzer.analyze({ controller: props.controller, function: props.function, functionName: props.name, httpMethod: method, metadata: props.metadata, - report: (message) => - localErrors.push({ - file: props.controller.file, - controller: props.controller.class.name, - function: props.name, - message, - }), - isError: () => localErrors.length !== 0, + errors, }); if (localErrors.length) { errors.push(...localErrors); @@ -98,9 +78,10 @@ export namespace ReflectHttpOperationAnalyzer { if (str.includes("*") === true) { props.project.warnings.push({ file: props.controller.file, - controller: props.controller.class.name, + class: props.controller.class.name, function: props.name, - message: "@nestia/sdk does not compose wildcard method.", + from: "", + contents: ["@nestia/sdk does not compose wildcard method."], }); return false; } @@ -137,9 +118,10 @@ export namespace ReflectHttpOperationAnalyzer { if (binded === null) { props.project.errors.push({ file: props.controller.file, - controller: props.controller.class.name, + class: props.controller.class.name, function: props.name, - message: `invalid path (${JSON.stringify(location)})`, + from: "{parameters}", + contents: [`invalid path (${JSON.stringify(location)})`], }); continue; } @@ -152,11 +134,14 @@ export namespace ReflectHttpOperationAnalyzer { if (ranges.equal(binded.sort(), parameters) === false) errors.push({ file: props.controller.file, - controller: props.controller.class.name, + class: props.controller.class.name, function: props.name, - message: `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( - ", ", - )}], parameters: [${parameters.join(", ")}]).`, + from: "{parameters}", + contents: [ + `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( + ", ", + )}], parameters: [${parameters.join(", ")}]).`, + ], }); } @@ -167,114 +152,8 @@ export namespace ReflectHttpOperationAnalyzer { } return operation; }; - - function _Analyze_http_parameter( - key: string, - param: INestParam, - ): IReflectHttpOperation.IParameter | null { - const symbol: string = key.split(":")[0]; - if (symbol.indexOf("__custom") !== -1) - return _Analyze_http_custom_parameter(param); - - const typeIndex: RouteParamtypes = Number(symbol[0]) as RouteParamtypes; - if (isNaN(typeIndex) === true) return null; - - const type: ParamCategory | undefined = getNestParamType(typeIndex); - if (type === undefined) return null; - - return { - custom: false, - name: key, - category: type, - index: param.index, - field: param.data, - }; - } - - function _Analyze_http_custom_parameter( - param: INestParam, - ): IReflectHttpOperation.IParameter | null { - if (param.factory === undefined) return null; - else if ( - param.factory.name === "EncryptedBody" || - param.factory.name === "PlainBody" || - param.factory.name === "TypedQueryBody" || - param.factory.name === "TypedBody" || - param.factory.name === "TypedFormDataBody" - ) - return { - custom: true, - category: "body", - index: param.index, - name: param.name, - field: param.data, - encrypted: param.factory.name === "EncryptedBody", - contentType: - param.factory.name === "PlainBody" || - param.factory.name === "EncryptedBody" - ? "text/plain" - : param.factory.name === "TypedQueryBody" - ? "application/x-www-form-urlencoded" - : param.factory.name === "TypedFormDataBody" - ? "multipart/form-data" - : "application/json", - }; - else if (param.factory.name === "TypedHeaders") - return { - custom: true, - category: "headers", - name: param.name, - index: param.index, - field: param.data, - }; - else if (param.factory.name === "TypedParam") - return { - custom: true, - category: "param", - name: param.name, - index: param.index, - field: param.data, - }; - else if (param.factory.name === "TypedQuery") - return { - custom: true, - name: param.name, - category: "query", - index: param.index, - field: undefined, - }; - else return null; - } -} - -interface INestParam { - name: string; - index: number; - factory?: (...args: any) => any; - data: string | undefined; } -type NestParameters = { - [key: string]: INestParam; -}; - -const hasInterceptor = - (name: string) => - (proto: any): boolean => { - const meta = Reflect.getMetadata(INTERCEPTORS_METADATA, proto); - if (Array.isArray(meta) === false) return false; - return meta.some((elem) => elem?.constructor?.name === name); - }; - -// https://github.com/nestjs/nest/blob/master/packages/common/enums/route-paramtypes.enum.ts -const getNestParamType = (value: RouteParamtypes) => { - if (value === RouteParamtypes.BODY) return "body"; - else if (value === RouteParamtypes.HEADERS) return "headers"; - else if (value === RouteParamtypes.QUERY) return "query"; - else if (value === RouteParamtypes.PARAM) return "param"; - return undefined; -}; - // node_modules/@nestjs/common/lib/enums/request-method.enum.ts const METHODS = [ "GET", diff --git a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts index 1b53487cb..ad980e15e 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -1,10 +1,17 @@ import { ROUTE_ARGS_METADATA } from "@nestjs/common/constants"; import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum"; +import { JsonMetadataFactory } from "typia/lib/factories/JsonMetadataFactory"; +import { HttpFormDataProgrammer } from "typia/lib/programmers/http/HttpFormDataProgrammer"; +import { HttpHeadersProgrammer } from "typia/lib/programmers/http/HttpHeadersProgrammer"; +import { HttpParameterProgrammer } from "typia/lib/programmers/http/HttpParameterProgrammer"; +import { HttpQueryProgrammer } from "typia/lib/programmers/http/HttpQueryProgrammer"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; import { IReflectTypeImport } from "../structures/IReflectTypeImport"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; +import { TextPlainValidator } from "../transformers/TextPlainValidator"; export namespace ReflectHttpOperationParameterAnalyzer { export interface IContext { @@ -13,41 +20,130 @@ export namespace ReflectHttpOperationParameterAnalyzer { functionName: string; httpMethod: string; metadata: IOperationMetadata; - report: (message: string) => void; - isError: () => boolean; + errors: IReflectOperationError[]; } export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => { const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] = analyzePreconfigured(ctx); const imports: IReflectTypeImport[] = []; + const errors: IReflectOperationError[] = []; + + //---- + // FIND CONTRADICTIONS + //---- + // GET AND HEAD METHOD + const contradictErrors: string[] = []; + const contradict = (message: string) => { + contradictErrors.push(message); + }; + if ( + (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") && + preconfigured.some((x) => x.kind === "body") + ) + contradict(`@Body() is not allowed in the ${ctx.httpMethod} method.`); + + // FIND DUPLICATED BODY + if ( + preconfigured.filter((x) => x.kind === "body" && x.field === undefined) + .length > 1 + ) + contradict(`Duplicated @Body() is not allowed.`); + if ( + preconfigured.filter((x) => x.kind === "query" && x.field === undefined) + .length > 1 + ) + contradict(`Duplicated @Query() without field name is not allowed.`); + if ( + preconfigured.filter((x) => x.kind === "headers" && x.field === undefined) + .length > 1 + ) + contradict(`Duplicated @Headers() without field name is not allowed.`); + + // FIND DUPLICATED FIELDS + if ( + isUnique( + preconfigured + .filter((x) => x.kind === "param") + .map((x) => x.field) + .filter((field) => field !== undefined), + ) === false + ) + contradict(`Duplicated field names of path are not allowed.`); + if ( + isUnique( + preconfigured + .filter((x) => x.kind === "query") + .map((x) => x.field) + .filter((field) => field !== undefined), + ) === false + ) + contradict(`Duplicated field names of query are not allowed.`); + if ( + isUnique( + preconfigured + .filter((x) => x.kind === "headers") + .map((x) => x.field) + .filter((field) => field !== undefined), + ) === false + ) + contradict(`Duplicated field names of headers are not allowed.`); + if (contradictErrors.length) + errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.functionName, + from: "", + contents: contradictErrors, + }); + + //---- + // COMPOSE PARAMETERS + //---- const parameters: IReflectHttpOperationParameter[] = preconfigured .map((p): IReflectHttpOperationParameter | null => { // METADATA INFO + const pErrorContents: Array = []; const matched: IOperationMetadata.IParameter | undefined = ctx.metadata.parameters.find((x) => x.index === p.index); + const report = () => { + errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.functionName, + from: `parameter ${matched ? JSON.stringify(matched.name) : `of ${p.index} th`}`, + contents: pErrorContents, + }); + return null; + }; // VALIDATE TYPE if (matched === undefined) - ctx.report(`Unable to find parameter type of the ${p.index} (th).`); - else if (matched.schema === null || matched.type === null) - ctx.report( - `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, - ); - - // VALIDATE KIND - if (p.kind === "body" && p.field) - ctx.report(`@Body() must not have a field name.`); - else if (p.kind === "param" && !p.field) - ctx.report(`@Param() must have a field name.`); - - if (ctx.isError()) return null; + pErrorContents.push(`Unable to find parameter type.`); + else if (matched.type === null) + pErrorContents.push(`Failed to get the type info.`); + + // CONSIDER KIND + const schema: IOperationMetadata.ISchema | null = (() => { + if (matched === undefined) return null; + const result = + p.kind === "body" && + (p.contentType === "application/json" || p.encrypted === true) + ? matched.primitive + : matched.resolved; + return result.success ? result.data : null; + })(); + if (p.kind === "body" && p.field !== undefined) + pErrorContents.push(`@Body() must not have a field name.`); + else if (p.kind === "param" && p.field === undefined) + pErrorContents.push(`@Param() must have a field name.`); + + if (pErrorContents.length) return report(); else if ( matched === undefined || - matched.schema === null || matched.type === null || - matched.required === false + schema === null ) - return null; + return null; // unreachable // COMPOSITION imports.push(...matched.imports); @@ -58,8 +154,8 @@ export namespace ReflectHttpOperationParameterAnalyzer { field: p.field!, name: matched.name, type: matched.type, - schema: matched.schema, - components: matched.components, + validate: HttpParameterProgrammer.validate, + ...schema, }; else if (p.kind === "query" || p.kind === "headers") return { @@ -68,8 +164,11 @@ export namespace ReflectHttpOperationParameterAnalyzer { field: p.field ?? null, name: matched.name, type: matched.type, - schema: matched.schema, - components: matched.components, + validate: + p.kind === "query" + ? HttpQueryProgrammer.validate + : HttpHeadersProgrammer.validate, + ...schema, }; else if (p.kind === "body") return { @@ -79,65 +178,24 @@ export namespace ReflectHttpOperationParameterAnalyzer { contentType: p.contentType, name: matched.name, type: matched.type, - schema: matched.schema, - components: matched.components, + validate: + p.contentType === "application/json" || p.encrypted === true + ? JsonMetadataFactory.validate + : p.contentType === "application/x-www-form-urlencoded" + ? HttpQueryProgrammer.validate + : p.contentType === "multipart/form-data" + ? HttpFormDataProgrammer.validate + : TextPlainValidator.validate, + ...schema, }; else { - ctx.report(`Unknown kind of the parameter.`); - return null; + pErrorContents.push(`Unknown kind of the parameter.`); + return report(); } }) .filter((x): x is IReflectHttpOperationParameter => x !== null); - //---- - // POST VALIDATIONS - //---- - // GET AND HEAD METHOD - if ( - (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") && - parameters.some((x) => x.kind === "body") - ) - ctx.report(`@Body() is not allowed in the ${ctx.httpMethod} method.`); - - // FIND DUPLICATED BODY - if (parameters.filter((x) => x.kind === "body").length > 1) - ctx.report(`Duplicated @Body() is not allowed.`); - if ( - parameters.filter((x) => x.kind === "query" && x.field === null).length > - 1 - ) - ctx.report(`Duplicated @Query() without field name is not allowed.`); - if ( - parameters.filter((x) => x.kind === "headers" && x.field === null) - .length > 1 - ) - ctx.report(`Duplicated @Headers() without field name is not allowed.`); - - // FIND DUPLICATED FIELDS - if ( - isUnique( - parameters.filter((x) => x.kind === "param").map((x) => x.field), - ) === false - ) - ctx.report(`Duplicated field names of path are not allowed.`); - if ( - isUnique( - parameters - .filter((x) => x.kind === "query") - .filter((x) => x.field !== null) - .map((x) => x.field!), - ) === false - ) - ctx.report(`Duplicated field names of query are not allowed.`); - if ( - isUnique( - parameters - .filter((x) => x.kind === "headers") - .filter((x) => x.field !== null) - .map((x) => x.field!), - ) === false - ) - ctx.report(`Duplicated field names of headers are not allowed.`); + if (errors.length) ctx.errors.push(...errors); return parameters; }; diff --git a/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts index 02c00f22b..59ca9bfcc 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts @@ -4,10 +4,14 @@ import { INTERCEPTORS_METADATA, } from "@nestjs/common/constants"; import typia from "typia"; +import { JsonMetadataFactory } from "typia/lib/factories/JsonMetadataFactory"; +import { HttpQueryProgrammer } from "typia/lib/programmers/http/HttpQueryProgrammer"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; +import { TextPlainValidator } from "../transformers/TextPlainValidator"; export namespace ReflectHttpOperationResponseAnalyzer { export interface IContext { @@ -16,13 +20,24 @@ export namespace ReflectHttpOperationResponseAnalyzer { functionName: string; httpMethod: string; metadata: IOperationMetadata; - report: (message: string) => void; - isError: () => boolean; + errors: IReflectOperationError[]; } export const analyze = ( ctx: IContext, ): IReflectHttpOperationSuccess | null => { + const errors: Array = []; + const report = () => { + ctx.errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.functionName, + from: "return", + contents: errors, + }); + return null; + }; + const encrypted: boolean = hasInterceptor({ name: "EncryptedRouteInterceptor", function: ctx.function, @@ -41,25 +56,25 @@ export namespace ReflectHttpOperationResponseAnalyzer { h.name.toLowerCase() === "content-type", )?.value ?? (ctx.httpMethod === "HEAD" ? null : "application/json"); - if ( - ctx.metadata.success.type === null || - ctx.metadata.success.schema === null - ) - ctx.report(`Failed to analyze the return type.`); + const schema = + contentType === "application/json" + ? ctx.metadata.success.primitive + : ctx.metadata.success.resolved; + if (schema.success === false) errors.push(...schema.errors); if (ctx.httpMethod === "HEAD" && contentType !== null) - ctx.report(`HEAD method must not have a content type.`); + errors.push(`HEAD method must not have a content type.`); if ( typia.is(contentType) === false ) - ctx.report( + errors.push( `@nestia/sdk does not support ${JSON.stringify(contentType)} content type.`, ); - if (ctx.isError()) return null; + if (errors.length) return report(); else if ( ctx.metadata.success.type === null || - ctx.metadata.success.schema === null || + schema.success === false || !typia.is(contentType) ) return null; @@ -69,8 +84,18 @@ export namespace ReflectHttpOperationResponseAnalyzer { status: getStatus(ctx.function) ?? (ctx.httpMethod === "POST" ? 201 : 200), type: ctx.metadata.success.type, - schema: ctx.metadata.success.schema, - components: ctx.metadata.success.components, + ...schema.data, + validate: + contentType === "application/json" || encrypted === true + ? JsonMetadataFactory.validate + : contentType === "application/x-www-form-urlencoded" + ? HttpQueryProgrammer.validate + : contentType === "text/plain" + ? TextPlainValidator.validate + : (meta) => + meta.size() + ? ["HEAD method must not have any return value."] + : [], }; }; diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index 033c1cca7..de7e83e14 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -1,12 +1,11 @@ import { ranges } from "tstl"; -import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; import { IReflectController } from "../structures/IReflectController"; import { IReflectTypeImport } from "../structures/IReflectTypeImport"; import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; import { IReflectWebSocketOperationParameter } from "../structures/IReflectWebSocketOperationParameter"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; import { StringUtil } from "../utils/StringUtil"; import { ImportAnalyzer } from "./ImportAnalyzer"; import { PathAnalyzer } from "./PathAnalyzer"; @@ -27,16 +26,8 @@ export namespace ReflectWebSocketOperationAnalyzer { ); if (route === undefined) return null; - const errors: IErrorReport[] = []; - const report = (message: string): null => { - errors.push({ - file: ctx.controller.file, - controller: ctx.controller.class.name, - function: ctx.name, - message, - }); - return null; - }; + // @todo -> detailing is required + const errors: string[] = []; const preconfigured: IReflectWebSocketOperationParameter.IPreconfigured[] = ( (Reflect.getMetadata( @@ -46,9 +37,9 @@ export namespace ReflectWebSocketOperationAnalyzer { ) ?? []) as IReflectWebSocketOperationParameter[] ).sort((a, b) => a.index - b.index); if (preconfigured.find((p) => (p.kind === "acceptor") === undefined)) - report("@WebSocketRoute.Acceptor() is essentially required"); + errors.push("@WebSocketRoute.Acceptor() is essentially required"); if (preconfigured.length !== ctx.function.length) - report( + errors.push( [ "Every parameters must be one of below:", " - @WebSocketRoute.Acceptor()", @@ -68,11 +59,11 @@ export namespace ReflectWebSocketOperationAnalyzer { // VALIDATE PARAMETER if (matched === undefined) - return report( + return errors.push( `Unable to find parameter type of the ${p.index} (th).`, ); - else if (matched.schema === null || matched.type === null) - return report( + else if (matched.type === null) + return errors.push( `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, ); else if ( @@ -80,7 +71,7 @@ export namespace ReflectWebSocketOperationAnalyzer { !(p as IReflectWebSocketOperationParameter.IParamParameter).field ?.length ) - return report(`@WebSocketRoute.Param() must have a field name.`); + return errors.push(`@WebSocketRoute.Param() must have a field name.`); else if ( p.kind === "acceptor" && matched.type?.typeArguments?.length !== 3 @@ -90,7 +81,7 @@ export namespace ReflectWebSocketOperationAnalyzer { p.kind === "driver" && matched.type?.typeArguments?.length !== 1 ) - return report( + return errors.push( `@WebSocketRoute.Driver() must have one type argument.`, ); @@ -112,7 +103,7 @@ export namespace ReflectWebSocketOperationAnalyzer { } satisfies IReflectWebSocketOperationParameter.IParamParameter; // UNKNOWN TYPE, MAYBE NEW FEATURE - return report( + return errors.push( `@WebSocketRoute.${StringUtil.capitalize(p.kind)}() has not been supported yet. How about upgrading the nestia packages?`, ); }) @@ -130,16 +121,22 @@ export namespace ReflectWebSocketOperationAnalyzer { const binded: string[] | null = PathAnalyzer.parameters(location); if (binded === null) - report(`invalid path (${JSON.stringify(location)})`); + errors.push(`invalid path (${JSON.stringify(location)})`); else if (ranges.equal(binded.sort(), fields) === false) - report( + errors.push( `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( ", ", )}], parameters: [${fields.join(", ")}]).`, ); } if (errors.length) { - ctx.project.errors.push(...errors); + ctx.project.errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.function.name, + from: ctx.name, + contents: errors, + }); return null; } return { diff --git a/packages/sdk/src/decorators/OperationMetadata.ts b/packages/sdk/src/decorators/OperationMetadata.ts index 1e8be432d..77420b164 100644 --- a/packages/sdk/src/decorators/OperationMetadata.ts +++ b/packages/sdk/src/decorators/OperationMetadata.ts @@ -1,4 +1,4 @@ -import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; export function OperationMetadata( metadata: IOperationMetadata, diff --git a/packages/sdk/src/structures/IErrorReport.ts b/packages/sdk/src/structures/IErrorReport.ts deleted file mode 100644 index ad784e645..000000000 --- a/packages/sdk/src/structures/IErrorReport.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface IErrorReport { - file: string; - controller: string; - function: string | null; - message: string; -} diff --git a/packages/sdk/src/structures/INestiaProject.ts b/packages/sdk/src/structures/INestiaProject.ts index ed88d4042..c5cba56ee 100644 --- a/packages/sdk/src/structures/INestiaProject.ts +++ b/packages/sdk/src/structures/INestiaProject.ts @@ -1,13 +1,13 @@ import ts from "typescript"; import { INestiaConfig } from "../INestiaConfig"; -import { IErrorReport } from "./IErrorReport"; import { INestiaSdkInput } from "./INestiaSdkInput"; +import { IReflectOperationError } from "./IReflectOperationError"; export interface INestiaProject { config: INestiaConfig; input: INestiaSdkInput; checker: ts.TypeChecker; - errors: IErrorReport[]; - warnings: IErrorReport[]; + errors: IReflectOperationError[]; + warnings: IReflectOperationError[]; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationException.ts b/packages/sdk/src/structures/IReflectHttpOperationException.ts index 51f73949f..d6d3ccb08 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationException.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationException.ts @@ -1,3 +1,4 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; @@ -14,4 +15,5 @@ export interface IReflectHttpOperationException { type: IReflectType; schema: IMetadata; components: IMetadataComponents; + validate: MetadataFactory.Validator; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts index 8180915a8..44205e00b 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts @@ -1,3 +1,4 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; @@ -31,8 +32,9 @@ export namespace IReflectHttpOperationParameter { name: string; index: number; type: IReflectType; - schema: IMetadata; + metadata: IMetadata; components: IMetadataComponents; + validate: MetadataFactory.Validator; example?: any; examples?: Record; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts index 12443f8f1..8cb0cee48 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts @@ -1,12 +1,11 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; import { IReflectType } from "./IReflectType"; export interface IReflectHttpOperationSuccess { - components: IMetadataComponents; type: IReflectType; - schema: IMetadata; status: number; contentType: | "application/json" @@ -15,6 +14,9 @@ export interface IReflectHttpOperationSuccess { | "application/json" | null; encrypted: boolean; + components: IMetadataComponents; + metadata: IMetadata; + validate: MetadataFactory.Validator; example?: any; examples?: Record; } diff --git a/packages/sdk/src/structures/IReflectOperationError.ts b/packages/sdk/src/structures/IReflectOperationError.ts new file mode 100644 index 000000000..ad3733869 --- /dev/null +++ b/packages/sdk/src/structures/IReflectOperationError.ts @@ -0,0 +1,9 @@ +import { IOperationMetadata } from "../transformers/IOperationMetadata"; + +export interface IReflectOperationError { + file: string; + class: string; + function: string | null; + from: string | null; + contents: Array; +} diff --git a/packages/sdk/src/structures/IOperationMetadata.ts b/packages/sdk/src/transformers/IOperationMetadata.ts similarity index 60% rename from packages/sdk/src/structures/IOperationMetadata.ts rename to packages/sdk/src/transformers/IOperationMetadata.ts index 3daac686f..766fa8ced 100644 --- a/packages/sdk/src/structures/IOperationMetadata.ts +++ b/packages/sdk/src/transformers/IOperationMetadata.ts @@ -1,10 +1,10 @@ -import { IJsDocTagInfo, Primitive } from "typia"; -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { IJsDocTagInfo } from "typia"; import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; +import { ValidationPipe } from "typia/lib/typings/ValidationPipe"; -import { IReflectType } from "./IReflectType"; -import { IReflectTypeImport } from "./IReflectTypeImport"; +import { IReflectType } from "../structures/IReflectType"; +import { IReflectTypeImport } from "../structures/IReflectTypeImport"; export interface IOperationMetadata { parameters: IOperationMetadata.IParameter[]; @@ -19,16 +19,18 @@ export namespace IOperationMetadata { index: number; } export interface IResponse { - imports: IReflectTypeImport[]; - components: IMetadataComponents; - primitive: ISchema | null; - resolved: ISchema | null; type: IReflectType | null; - required: boolean; - errors: Primitive[]; + imports: IReflectTypeImport[]; + primitive: ValidationPipe; + resolved: ValidationPipe; } export interface ISchema { components: IMetadataComponents; - value: IMetadata; + metadata: IMetadata; + } + export interface IError { + name: string; + accessor: string | null; + messages: string[]; } } diff --git a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts index 097fef77d..8db0ab6c4 100644 --- a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts +++ b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts @@ -3,12 +3,13 @@ import { CommentFactory } from "typia/lib/factories/CommentFactory"; import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; -import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; -import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; +import { ValidationPipe } from "typia/lib/typings/ValidationPipe"; +import { Escaper } from "typia/lib/utils/Escaper"; import { ImportAnalyzer } from "../analyses/ImportAnalyzer"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { IOperationMetadata } from "./IOperationMetadata"; import { ISdkTransformerContext } from "./ISdkTransformerContext"; export namespace SdkMetadataProgrammer { @@ -80,76 +81,78 @@ export namespace SdkMetadataProgrammer { type: ts.Type | null; required: boolean; }): IOperationMetadata.IResponse => { - const result = MetadataFactory.analyze( - p.context.checker, - p.context.api, - )({ - escape: true, - constant: true, - absorb: false, - })(p.context.collection)(p.type); - if (result.success === false) result.errors; const analyzed: ImportAnalyzer.IOutput = p.type ? ImportAnalyzer.analyze(p.context.checker, p.generics, p.type) : { type: { name: "any" }, imports: [], }; + const [primitive, resolved] = [true, false].map((escape) => + MetadataFactory.analyze( + p.context.checker, + p.context.api, + )({ + escape, + constant: true, + absorb: true, + })(p.context.collection)(p.type), + ); return { ...analyzed, - ...(result.success - ? { - ...writeSchema({ - collection: p.context.collection, - metadata: result.data, - }), - errors: [], - } - : { - schema: null, - components: { - objects: [], - arrays: [], - tuples: [], - aliases: [], - } satisfies IMetadataComponents, - errors: result.errors, - }), - required: !( - p.required === false || - (result.success && result.data.isRequired() === false) - ), + primitive: writeSchema({ + collection: p.context.collection, + result: primitive, + }), + resolved: writeSchema({ + collection: p.context.collection, + result: resolved, + }), }; }; const writeSchema = (p: { collection: MetadataCollection; - metadata: Metadata; - }): { - components: IMetadataComponents; - schema: IMetadata; - } => { - const visited: Set = iterateVisited(p.metadata); + result: ValidationPipe; + }): ValidationPipe => { + if (p.result.success === false) + return { + success: false, + errors: p.result.errors.map((e) => ({ + name: e.name, + accessor: + e.explore.object !== null + ? join({ + object: e.explore.object, + key: e.explore.property, + }) + : null, + messages: e.messages, + })), + }; + const visited: Set = iterateVisited(p.result.data); return { - components: { - objects: p.collection - .objects() - .filter((o) => visited.has(o.name)) - .map((o) => o.toJSON()), - aliases: p.collection - .aliases() - .filter((a) => visited.has(a.name)) - .map((a) => a.toJSON()), - arrays: p.collection - .arrays() - .filter((a) => visited.has(a.name)) - .map((a) => a.toJSON()), - tuples: p.collection - .tuples() - .filter((t) => visited.has(t.name)) - .map((t) => t.toJSON()), + success: true, + data: { + components: { + objects: p.collection + .objects() + .filter((o) => visited.has(o.name)) + .map((o) => o.toJSON()), + aliases: p.collection + .aliases() + .filter((a) => visited.has(a.name)) + .map((a) => a.toJSON()), + arrays: p.collection + .arrays() + .filter((a) => visited.has(a.name)) + .map((a) => a.toJSON()), + tuples: p.collection + .tuples() + .filter((t) => visited.has(t.name)) + .map((t) => t.toJSON()), + }, + metadata: p.result.data.toJSON(), }, - schema: p.metadata.toJSON(), }; }; @@ -203,3 +206,16 @@ const iterateVisited = (metdata: Metadata): Set => { iterate(metdata); return names; }; + +const join = ({ + object, + key, +}: { + object: MetadataObject; + key: string | object | null; +}) => { + if (key === null) return object.name; + else if (typeof key === "object") return `${object.name}[key]`; + else if (Escaper.variable(key)) return `${object.name}.${key}`; + return `${object.name}[${JSON.stringify(key)}]`; +}; diff --git a/packages/sdk/src/transformers/SdkTransformer.ts b/packages/sdk/src/transformers/SdkTransformer.ts index 8389db0c2..e2f6bbf22 100644 --- a/packages/sdk/src/transformers/SdkTransformer.ts +++ b/packages/sdk/src/transformers/SdkTransformer.ts @@ -5,7 +5,7 @@ import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { GenericAnalyzer } from "../analyses/GenericAnalyzer"; -import { IOperationMetadata } from "../structures/IOperationMetadata"; +import { IOperationMetadata } from "./IOperationMetadata"; import { ISdkTransformerContext } from "./ISdkTransformerContext"; import { SdkMetadataProgrammer } from "./SdkMetadataProgrammer"; diff --git a/packages/sdk/src/transformers/TextPlainValidator.ts b/packages/sdk/src/transformers/TextPlainValidator.ts new file mode 100644 index 000000000..2068ff41c --- /dev/null +++ b/packages/sdk/src/transformers/TextPlainValidator.ts @@ -0,0 +1,17 @@ +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +export namespace TextPlainValidator { + export const validate = (metadata: Metadata): string[] => { + const expected: number = + metadata.atomics.filter((a) => a.type === "string").length + + metadata.constants + .filter((c) => c.type === "string") + .map((c) => c.values.length) + .reduce((a, b) => a + b, 0) + + metadata.templates.length + + metadata.natives.filter((n) => n === "String").length; + if (metadata.size() === 0 || metadata.size() !== expected) + return [`Only string type is allowed in the "text/plain" content type.`]; + return []; + }; +} From b1d9ef077d6ccb9f3ca0596eaa6287050d951a5a Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Mon, 12 Aug 2024 23:03:02 +0900 Subject: [PATCH 03/21] Succeded to revive swagger generator, but SDK not yet. --- benchmark/package.json | 2 +- package.json | 2 +- packages/benchmark/package.json | 2 +- packages/core/package.json | 10 +- packages/e2e/package.json | 2 +- packages/fetcher/package.json | 2 +- packages/migrate/package.json | 2 +- packages/sdk/package.json | 14 +- packages/sdk/src/NestiaSdkApplication.ts | 305 +++++----- .../sdk/src/analyses/ExceptionAnalyzer.ts | 284 +++++----- packages/sdk/src/analyses/GenericAnalyzer.ts | 4 +- .../src/analyses/ReflectControllerAnalyzer.ts | 3 +- .../analyses/ReflectHttpOperationAnalyzer.ts | 8 +- .../ReflectHttpOperationParameterAnalyzer.ts | 8 +- .../ReflectWebSocketOperationAnalyzer.ts | 8 +- .../src/analyses/TypedHttpRouteAnalyzer.ts | 177 ++++++ .../analyses/TypedWebSocketRouteAnalyzer.ts | 17 + .../executable/internal/NestiaConfigLoader.ts | 13 +- .../executable/internal/NestiaSdkCommand.ts | 5 +- packages/sdk/src/generates/CloneGenerator.ts | 119 ++-- packages/sdk/src/generates/E2eGenerator.ts | 41 +- packages/sdk/src/generates/OpenAiGenerator.ts | 89 ++- packages/sdk/src/generates/SdkGenerator.ts | 222 ++++---- .../sdk/src/generates/SwaggerGenerator.ts | 530 ++++-------------- .../generates/internal/E2eFileProgrammer.ts | 23 +- .../generates/internal/SdkAliasCollection.ts | 59 +- .../internal/SdkDistributionComposer.ts | 15 +- .../generates/internal/SdkFileProgrammer.ts | 25 +- .../internal/SdkHttpCloneProgrammer.ts | 284 +++++----- .../internal/SdkHttpFunctionProgrammer.ts | 77 +-- .../internal/SdkHttpNamespaceProgrammer.ts | 528 +++++++++-------- .../internal/SdkHttpRouteProgrammer.ts | 18 +- .../internal/SdkHttpSimulationProgrammer.ts | 50 +- .../SdkWebSocketNamespaceProgrammer.ts | 430 +++++++------- .../internal/SdkWebSocketRouteProgrammer.ts | 30 +- ...rator.ts => SwaggerDescriptionComposer.ts} | 30 +- .../internal/SwaggerOperationComposer.ts | 90 +++ .../SwaggerOperationParameterComposer.ts | 173 ++++++ .../SwaggerOperationResponseComposer.ts | 110 ++++ .../internal/SwaggerSchemaGenerator.ts | 473 ---------------- .../internal/SwaggerSchemaValidator.ts | 206 ------- .../sdk/src/structures/IReflectApplication.ts | 8 + .../src/structures/IReflectHttpOperation.ts | 4 + .../IReflectHttpOperationException.ts | 2 +- .../IReflectHttpOperationParameter.ts | 3 + .../src/structures/IReflectOperationError.ts | 17 + .../IReflectWebSocketOperationParameter.ts | 26 +- packages/sdk/src/structures/ISwaggerError.ts | 8 - .../src/structures/ISwaggerLazyProperty.ts | 7 - .../sdk/src/structures/ISwaggerLazySchema.ts | 7 - .../sdk/src/structures/ITypedApplication.ts | 8 + .../sdk/src/structures/ITypedHttpRoute.ts | 29 + .../structures/ITypedHttpRouteException.ts | 15 + .../structures/ITypedHttpRouteParameter.ts | 41 ++ .../src/structures/ITypedHttpRouteSuccess.ts | 22 + .../src/structures/ITypedWebSocketRoute.ts | 20 + .../ITypedWebSocketRouteParameter.ts | 3 + .../src/transformers/IOperationMetadata.ts | 2 + .../src/transformers/SdkMetadataProgrammer.ts | 30 +- packages/sdk/src/utils/StringUtil.ts | 2 +- test/features/app/nestia.config.ts | 1 + .../api/functional/articles/comments/index.ts | 204 ------- .../app/src/api/functional/articles/index.ts | 7 - .../src/api/functional/bbs/articles/index.ts | 201 ------- .../app/src/api/functional/bbs/index.ts | 7 - .../app/src/api/functional/health/index.ts | 35 -- test/features/app/src/api/functional/index.ts | 10 - .../src/api/functional/performance/index.ts | 39 -- .../test_api_articles_comments_at.ts | 19 - .../test_api_articles_comments_index.ts | 20 - .../test_api_articles_comments_store.ts | 19 - .../test_api_articles_comments_update.ts | 20 - .../api/automated/test_api_bbs_articles_at.ts | 15 - .../automated/test_api_bbs_articles_index.ts | 18 - .../automated/test_api_bbs_articles_store.ts | 17 - .../automated/test_api_bbs_articles_update.ts | 19 - .../api/automated/test_api_health_get.ts | 8 - .../api/automated/test_api_performance_get.ts | 11 - test/features/app/swagger.json | 1 - test/features/app/tsconfig.json | 2 +- .../packages/api/package.json | 2 +- .../packages/api/package.json | 2 +- .../distribute-json/packages/api/package.json | 2 +- .../distribute/packages/api/package.json | 2 +- test/package.json | 10 +- website/package.json | 2 +- 86 files changed, 2213 insertions(+), 3224 deletions(-) create mode 100644 packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts create mode 100644 packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts rename packages/sdk/src/generates/internal/{SwaggerDescriptionGenerator.ts => SwaggerDescriptionComposer.ts} (56%) create mode 100644 packages/sdk/src/generates/internal/SwaggerOperationComposer.ts create mode 100644 packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts create mode 100644 packages/sdk/src/generates/internal/SwaggerOperationResponseComposer.ts delete mode 100644 packages/sdk/src/generates/internal/SwaggerSchemaGenerator.ts delete mode 100644 packages/sdk/src/generates/internal/SwaggerSchemaValidator.ts create mode 100644 packages/sdk/src/structures/IReflectApplication.ts delete mode 100644 packages/sdk/src/structures/ISwaggerError.ts delete mode 100644 packages/sdk/src/structures/ISwaggerLazyProperty.ts delete mode 100644 packages/sdk/src/structures/ISwaggerLazySchema.ts create mode 100644 packages/sdk/src/structures/ITypedApplication.ts create mode 100644 packages/sdk/src/structures/ITypedHttpRoute.ts create mode 100644 packages/sdk/src/structures/ITypedHttpRouteException.ts create mode 100644 packages/sdk/src/structures/ITypedHttpRouteParameter.ts create mode 100644 packages/sdk/src/structures/ITypedHttpRouteSuccess.ts create mode 100644 packages/sdk/src/structures/ITypedWebSocketRoute.ts create mode 100644 packages/sdk/src/structures/ITypedWebSocketRouteParameter.ts delete mode 100644 test/features/app/src/api/functional/articles/comments/index.ts delete mode 100644 test/features/app/src/api/functional/articles/index.ts delete mode 100644 test/features/app/src/api/functional/bbs/articles/index.ts delete mode 100644 test/features/app/src/api/functional/bbs/index.ts delete mode 100644 test/features/app/src/api/functional/health/index.ts delete mode 100644 test/features/app/src/api/functional/index.ts delete mode 100644 test/features/app/src/api/functional/performance/index.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_articles_comments_index.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_articles_comments_store.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_articles_comments_update.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_health_get.ts delete mode 100644 test/features/app/src/test/features/api/automated/test_api_performance_get.ts delete mode 100644 test/features/app/swagger.json diff --git a/benchmark/package.json b/benchmark/package.json index 5cdf3830c..76f790086 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -42,7 +42,7 @@ "reflect-metadata": "^0.2.2", "tgrid": "^1.0.3", "tstl": "^3.0.0", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" }, "devDependencies": { "@types/autocannon": "^7.9.0", diff --git a/package.json b/package.json index 21f8ea747..21443feda 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@nestia/station", - "version": "3.10.0", + "version": "3.11.0-dev.20240811", "description": "Nestia station", "scripts": { "build": "node build/index.js", diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json index 5aa5e2eb3..18d613dbf 100644 --- a/packages/benchmark/package.json +++ b/packages/benchmark/package.json @@ -34,7 +34,7 @@ "ts-patch": "^3.2.1", "typescript": "^5.5.4", "typescript-transform-paths": "^3.4.7", - "typia": "^6.7.0", + "typia": "^6.8.0-dev.20240812", "uuid": "^10.0.0" }, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 42da7bc62..58e77b5f6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "3.10.0", + "version": "3.11.0-dev.20240811", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "../fetcher/nestia-fetcher-3.10.0.tgz", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^0.4.3", @@ -49,16 +49,16 @@ "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.3", "tgrid": "^1.0.0", - "typia": "^6.7.0", + "typia": "^6.8.0-dev.20240812", "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=3.10.0", + "@nestia/fetcher": ">=3.11.0-dev.20240811", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.3", - "typia": ">=6.7.0 <7.0.0" + "typia": ">=6.8.0-dev.20240812 <7.0.0" }, "devDependencies": { "@fastify/multipart": "^8.1.0", diff --git a/packages/e2e/package.json b/packages/e2e/package.json index bda2c1502..ec85772d7 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -41,7 +41,7 @@ "ts-patch": "^3.2.1", "typescript": "^5.5.3", "typescript-transform-paths": "^3.4.7", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" }, "files": [ "lib", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 438a19d15..47be88ecb 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "3.10.0", + "version": "3.11.0-dev.20240811", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 26d1a78a5..7c1db7563 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -71,7 +71,7 @@ "prettier": "^3.2.5", "tstl": "^3.0.0", "typescript": "^5.5.4", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" }, "files": [ "lib", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e54ea772b..01906022f 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "3.10.0", + "version": "3.11.0-dev.20240811", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "../core/nestia-core-3.10.0.tgz", - "@nestia/fetcher": "../fetcher/nestia-fetcher-3.10.0.tgz", + "@nestia/core": "../core/nestia-core-3.11.0-dev.20240811.tgz", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", "@samchon/openapi": "^0.4.3", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", @@ -45,16 +45,16 @@ "tsconfck": "^2.1.2", "tsconfig-paths": "^4.1.1", "tstl": "^3.0.0", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" }, "peerDependencies": { - "@nestia/core": ">=3.10.0", - "@nestia/fetcher": ">=3.10.0", + "@nestia/core": ">=3.11.0-dev.20240811", + "@nestia/fetcher": ">=3.11.0-dev.20240811", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", "ts-node": ">=10.6.0", - "typia": ">=6.7.0 <7.0.0" + "typia": ">=6.8.0-dev.20240812 <7.0.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/packages/sdk/src/NestiaSdkApplication.ts b/packages/sdk/src/NestiaSdkApplication.ts index 7f320401d..5600355d5 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -1,30 +1,30 @@ -import transform from "@nestia/core/lib/transform"; import fs from "fs"; import path from "path"; -import ts from "typescript"; +import { HashSet, Pair, TreeMap } from "tstl"; +import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary"; import { INestiaConfig } from "./INestiaConfig"; -import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer"; import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer"; +import { PathAnalyzer } from "./analyses/PathAnalyzer"; import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer"; -import { TypedControllerAnalyzer } from "./analyses/TypedControllerAnalyzer"; +import { TypedHttpRouteAnalyzer } from "./analyses/TypedHttpRouteAnalyzer"; +import { TypedWebSocketRouteAnalyzer } from "./analyses/TypedWebSocketRouteAnalyzer"; import { E2eGenerator } from "./generates/E2eGenerator"; import { OpenAiGenerator } from "./generates/OpenAiGenerator"; import { SdkGenerator } from "./generates/SdkGenerator"; import { SwaggerGenerator } from "./generates/SwaggerGenerator"; -import { IErrorReport } from "./structures/IErrorReport"; import { INestiaProject } from "./structures/INestiaProject"; -import { INestiaSdkInput } from "./structures/INestiaSdkInput"; import { IReflectController } from "./structures/IReflectController"; +import { IReflectOperationError } from "./structures/IReflectOperationError"; +import { ITypedApplication } from "./structures/ITypedApplication"; import { ITypedHttpRoute } from "./structures/ITypedHttpRoute"; import { ITypedWebSocketRoute } from "./structures/ITypedWebSocketRoute"; -import { MapUtil } from "./utils/MapUtil"; +import { IOperationMetadata } from "./transformers/IOperationMetadata"; +import { StringUtil } from "./utils/StringUtil"; +import { VersioningStrategy } from "./utils/VersioningStrategy"; export class NestiaSdkApplication { - public constructor( - private readonly config: INestiaConfig, - private readonly compilerOptions: ts.CompilerOptions, - ) {} + public constructor(private readonly config: INestiaConfig) {} public async e2e(): Promise { if (!this.config.output) @@ -50,11 +50,11 @@ export class NestiaSdkApplication { await validate("e2e")(this.config.e2e); print_title("Nestia E2E Generator"); - await this.generate((project) => async (routes) => { - await SdkGenerator.generate(project)(routes); - await E2eGenerator.generate(project)( - routes.filter((r) => r.protocol === "http") as ITypedHttpRoute[], - ); + await this.generate({ + generate: async (app) => { + await SdkGenerator.generate(app); + await E2eGenerator.generate(app); + }, }); } @@ -72,7 +72,10 @@ export class NestiaSdkApplication { ); print_title("Nestia SDK Generator"); - await this.generate(SdkGenerator.generate); + await this.generate({ + generate: SdkGenerator.generate, + validate: SdkGenerator.validate, + }); } public async swagger(): Promise { @@ -92,7 +95,9 @@ export class NestiaSdkApplication { ); print_title("Nestia Swagger Generator"); - await this.generate(SwaggerGenerator.generate); + await this.generate({ + generate: SwaggerGenerator.generate, + }); } public async openai(): Promise { @@ -112,16 +117,15 @@ export class NestiaSdkApplication { ); print_title("Nestia OpenAI Function Calling Schema Generator"); - await this.generate(OpenAiGenerator.generate); + await this.generate({ + generate: OpenAiGenerator.generate, + }); } - private async generate( - archiver: ( - project: INestiaProject, - ) => ( - routes: Array, - ) => Promise, - ): Promise { + private async generate(props: { + generate: (app: ITypedApplication) => Promise; + validate?: (app: ITypedApplication) => IReflectOperationError[]; + }): Promise { //---- // ANALYZE REFLECTS //---- @@ -135,23 +139,36 @@ export class NestiaSdkApplication { }; console.log("Analyzing reflections"); - const input: INestiaSdkInput = await ConfigAnalyzer.input(this.config); - const controllers: IReflectController[] = input.controllers + const controllers: IReflectController[] = project.input.controllers .map((c) => ReflectControllerAnalyzer.analyze({ project, controller: c, unique }), ) .filter((c): c is IReflectController => c !== null); + if (project.warnings.length) + report({ + type: "warning", + errors: project.warnings, + }); + if (project.errors.length) + return report({ + type: "error", + errors: project.errors, + }); + const agg: number = (() => { - const set: Set = new Set(); - for (const c of controllers) - for (const cPath of c.paths) - for (const op of c.operations) - for (const fPath of op.paths) - set.add( - `${op.protocol === "http" ? `${op.method}::` : ""}${cPath}/${fPath}`, + const set: HashSet> = new HashSet(); + for (const controller of controllers) + for (const controllerPath of controller.paths) + for (const operation of controller.operations) + for (const operationPath of operation.paths) + set.insert( + new Pair( + `${controllerPath}/${operationPath}`, + operation.protocol === "http" ? operation.method : "", + ), ); - return set.size; + return set.size(); })(); console.log(` - controllers: #${controllers.length}`); @@ -169,82 +186,62 @@ export class NestiaSdkApplication { //---- // ANALYZE TYPESCRIPT CODE //---- - console.log("Analyzing source codes"); - - const program: ts.Program = ts.createProgram( - controllers.map((c) => c.file), - this.compilerOptions, - ); - project.checker = program.getTypeChecker(); - - const diagnostics: ts.Diagnostic[] = []; - ts.transform( - program - .getSourceFiles() - .filter((file) => false === file.isDeclarationFile), - [ - transform( - program, - ((this.compilerOptions.plugins as any) ?? []).find( - (p: any) => p.transform === "@nestia/core/lib/transform", - ) ?? {}, - { - addDiagnostic: (diag) => diagnostics.push(diag), - }, - ), - ], - program.getCompilerOptions(), - ); - - const routeList: Array = []; - for (const c of controllers) { - const file: ts.SourceFile | undefined = program.getSourceFile(c.file); - if (file === undefined) continue; - routeList.push( - ...(await TypedControllerAnalyzer.analyze(project)(file, c)), - ); - } + console.log("Analyzing soure codes"); - // TRACE ERRORS - for (const diag of diagnostics) { - const file: string = diag.file - ? path.relative(diag.file.fileName, process.cwd()) - : "(unknown file)"; - const category: string = - diag.category === ts.DiagnosticCategory.Warning - ? "warning" - : diag.category === ts.DiagnosticCategory.Error - ? "error" - : diag.category === ts.DiagnosticCategory.Suggestion - ? "suggestion" - : diag.category === ts.DiagnosticCategory.Message - ? "message" - : "unkown"; - const [line, pos] = diag.file - ? (() => { - const lines: string[] = diag - .file!.text.substring(0, diag.start) - .split("\n"); - if (lines.length === 0) return [0, 0]; - return [lines.length, lines.at(-1)!.length + 1]; - })() - : [0, 0]; - console.error( - `${file}:${line}:${pos} - ${category} TS${diag.code}: ${diag.messageText}`, - ); - } - if (diagnostics.length) process.exit(-1); - - // REPORT ERRORS - if (project.errors.length) { - report_errors("error")(project.errors); - process.exit(-1); - } - if (project.warnings.length) report_errors("warning")(project.warnings); + // METADATA COMPONENTS + const collection: IMetadataDictionary = + TypedHttpRouteAnalyzer.dictionary(controllers); - // DO GENERATE - AccessorAnalyzer.analyze(routeList); - await archiver(project)(routeList); + // CONVERT TO TYPED OPERATIONS + const globalPrefix: string = project.input.globalPrefix?.prefix ?? ""; + const routes: Array = []; + for (const c of controllers) + for (const o of c.operations) { + const pathList: Set = new Set(); + const versions: string[] = VersioningStrategy.merge(project)([ + ...(c.versions ?? []), + ...(o.versions ?? []), + ]); + for (const v of versions) + for (const prefix of wrapPaths(c.prefixes)) + for (const cPath of wrapPaths(c.paths)) + for (const filePath of wrapPaths(o.paths)) + pathList.add( + PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath), + ); + if (o.protocol === "http") + routes.push( + ...TypedHttpRouteAnalyzer.analyze({ + controller: c, + errors: project.errors, + dictionary: collection, + operation: o, + paths: Array.from(pathList), + }), + ); + else if (o.protocol === "websocket") + routes.push( + ...TypedWebSocketRouteAnalyzer.analyze({ + controller: c, + operation: o, + paths: Array.from(pathList), + }), + ); + } + if (props.validate !== undefined) + props.validate({ + project, + routes, + }); + if (project.errors.length) + return report({ + type: "error", + errors: project.errors, + }); + await props.generate({ + project, + routes, + }); } } @@ -270,34 +267,62 @@ const print_title = (str: string): void => { // return true; // }; -const report_errors = - (type: "error" | "warning") => - (errors: IErrorReport[]): void => { - // key: file - // key: controller - // key: function - // value: message - const map: Map>>> = new Map(); - for (const e of errors) { - const file = MapUtil.take(map, e.file, () => new Map()); - const controller = MapUtil.take(file, e.controller, () => new Map()); - const func = MapUtil.take(controller, e.function, () => new Set()); - func.add(e.message); - } +const report = (props: { + type: "error" | "warning"; + errors: IReflectOperationError[]; +}): void => { + const map: TreeMap< + IReflectOperationError.Key, + Array + > = new TreeMap(); + for (const e of props.errors) + map.take(new IReflectOperationError.Key(e), () => []).push(...e.contents); - console.log(""); - print_title(`Nestia ${type[0].toUpperCase()}${type.slice(1)} Report`); - for (const [file, cMap] of map) { - for (const [controller, fMap] of cMap) - for (const [func, messages] of fMap) { - const location: string = path.relative(process.cwd(), file); - console.log( - `${location} - ${ - func !== null ? `${controller}.${func}()` : controller - }`, - ); - for (const msg of messages) console.log(` - ${msg}`); - console.log(""); - } - } - }; + console.log(""); + print_title(`Nestia ${StringUtil.capitalize(props.type)} Report`); + + for (const { + first: { error }, + second: contents, + } of map) { + if (error.contents.length === 0) continue; + const location: string = path.relative(process.cwd(), error.file); + const message: string = [ + `${location} - `, + error.class, + ...(error.function !== null ? [`.${error.function}()`] : [""]), + ...(error.from !== null ? [` from ${error.from}`] : [""]), + ":\n", + contents + .map((c) => { + if (typeof c === "string") return ` - ${c}`; + else + return [ + c.accessor + ? ` - ${c.name}: ` + : ` - ${c.name} (${c.accessor}): `, + ...c.messages.map((msg) => ` - ${msg}`), + ].join("\n"); + }) + .join("\n"), + ].join(""); + console.log(message); + } + + // for (const [file, cMap] of map) { + // for (const [controller, fMap] of cMap) + // for (const [func, messages] of fMap) { + // const location: string = path.relative(process.cwd(), file); + // console.log( + // `${location} - ${ + // func !== null ? `${controller}.${func}()` : controller + // }`, + // ); + // for (const msg of messages) console.log(` - ${msg}`); + // console.log(""); + // } + // } +}; + +const wrapPaths = (paths: string[]): string[] => + paths.length === 0 ? [""] : paths; diff --git a/packages/sdk/src/analyses/ExceptionAnalyzer.ts b/packages/sdk/src/analyses/ExceptionAnalyzer.ts index 388b5857f..a05bc60bc 100644 --- a/packages/sdk/src/analyses/ExceptionAnalyzer.ts +++ b/packages/sdk/src/analyses/ExceptionAnalyzer.ts @@ -1,154 +1,154 @@ -import path from "path"; -import ts from "typescript"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +// import path from "path"; +// import ts from "typescript"; +// import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; +// import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +// import { Metadata } from "typia/lib/schemas/metadata/Metadata"; -import { INestiaProject } from "../structures/INestiaProject"; -import { IReflectController } from "../structures/IReflectController"; -import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; -import { ITypeTuple } from "../structures/ITypeTuple"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { GenericAnalyzer } from "./GenericAnalyzer"; -import { ImportAnalyzer } from "./ImportAnalyzer"; +// import { INestiaProject } from "../structures/INestiaProject"; +// import { IReflectController } from "../structures/IReflectController"; +// import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; +// import { ITypeTuple } from "../structures/ITypeTuple"; +// import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; +// import { GenericAnalyzer } from "./GenericAnalyzer"; +// import { ImportAnalyzer } from "./ImportAnalyzer"; -export namespace ExceptionAnalyzer { - export const analyze = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - operation: IReflectHttpOperation; - declaration: ts.MethodDeclaration; - }): Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - ITypedHttpRoute.IOutput - > => { - const output: Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - ITypedHttpRoute.IOutput - > = {} as any; - for (const decorator of props.declaration.modifiers ?? []) - if (ts.isDecorator(decorator)) - analyzeTyped(project)({ - ...props, - output, - decorator, - }); - return output; - }; +// export namespace ExceptionAnalyzer { +// export const analyze = +// (project: INestiaProject) => +// (props: { +// generics: GenericAnalyzer.Dictionary; +// imports: ImportAnalyzer.Dictionary; +// controller: IReflectController; +// operation: IReflectHttpOperation; +// declaration: ts.MethodDeclaration; +// }): Record< +// number | "2XX" | "3XX" | "4XX" | "5XX", +// ITypedHttpRoute.IOutput +// > => { +// const output: Record< +// number | "2XX" | "3XX" | "4XX" | "5XX", +// ITypedHttpRoute.IOutput +// > = {} as any; +// for (const decorator of props.declaration.modifiers ?? []) +// if (ts.isDecorator(decorator)) +// analyzeTyped(project)({ +// ...props, +// output, +// decorator, +// }); +// return output; +// }; - const analyzeTyped = - (project: INestiaProject) => - (props: { - generics: GenericAnalyzer.Dictionary; - imports: ImportAnalyzer.Dictionary; - controller: IReflectController; - operation: IReflectHttpOperation; - output: Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - ITypedHttpRoute.IOutput - >; - decorator: ts.Decorator; - }): boolean => { - // CHECK DECORATOR - if (!ts.isCallExpression(props.decorator.expression)) return false; - else if ((props.decorator.expression.typeArguments ?? []).length !== 1) - return false; +// const analyzeTyped = +// (project: INestiaProject) => +// (props: { +// generics: GenericAnalyzer.Dictionary; +// imports: ImportAnalyzer.Dictionary; +// controller: IReflectController; +// operation: IReflectHttpOperation; +// output: Record< +// number | "2XX" | "3XX" | "4XX" | "5XX", +// ITypedHttpRoute.IOutput +// >; +// decorator: ts.Decorator; +// }): boolean => { +// // CHECK DECORATOR +// if (!ts.isCallExpression(props.decorator.expression)) return false; +// else if ((props.decorator.expression.typeArguments ?? []).length !== 1) +// return false; - // CHECK SIGNATURE - const signature: ts.Signature | undefined = - project.checker.getResolvedSignature(props.decorator.expression); - if (!signature || !signature.declaration) return false; - else if ( - path - .resolve(signature.declaration.getSourceFile().fileName) - .indexOf(TYPED_EXCEPTION_PATH) === -1 - ) - return false; +// // CHECK SIGNATURE +// const signature: ts.Signature | undefined = +// project.checker.getResolvedSignature(props.decorator.expression); +// if (!signature || !signature.declaration) return false; +// else if ( +// path +// .resolve(signature.declaration.getSourceFile().fileName) +// .indexOf(TYPED_EXCEPTION_PATH) === -1 +// ) +// return false; - // GET TYPE INFO - const status: string | null = getStatus(project.checker)( - props.decorator.expression.arguments[0] ?? null, - ); - if (status === null) return false; +// // GET TYPE INFO +// const status: string | null = getStatus(project.checker)( +// props.decorator.expression.arguments[0] ?? null, +// ); +// if (status === null) return false; - const node: ts.TypeNode = props.decorator.expression.typeArguments![0]; - const type: ts.Type = project.checker.getTypeFromTypeNode(node); - if (type.isTypeParameter()) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: "TypedException() without generic argument specification.", - }); - return false; - } +// const node: ts.TypeNode = props.decorator.expression.typeArguments![0]; +// const type: ts.Type = project.checker.getTypeFromTypeNode(node); +// if (type.isTypeParameter()) { +// project.errors.push({ +// file: props.controller.file, +// controller: props.controller.name, +// function: props.operation.name, +// message: "TypedException() without generic argument specification.", +// }); +// return false; +// } - const tuple: ITypeTuple | null = ImportAnalyzer.analyze(project.checker)({ - generics: props.generics, - imports: props.imports, - type, - }); - if (tuple === null) { - project.errors.push({ - file: props.controller.file, - controller: props.controller.name, - function: props.operation.name, - message: `TypeException() with unknown type on ${status} status.`, - }); - return false; - } +// const tuple: ITypeTuple | null = ImportAnalyzer.analyze(project.checker)({ +// generics: props.generics, +// imports: props.imports, +// type, +// }); +// if (tuple === null) { +// project.errors.push({ +// file: props.controller.file, +// controller: props.controller.name, +// function: props.operation.name, +// message: `TypeException() with unknown type on ${status} status.`, +// }); +// return false; +// } - // DO ASSIGN - const matched: IReflectHttpOperation.IException[] = Object.entries( - props.operation.exceptions, - ) - .filter(([key]) => status === key) - .map(([_key, value]) => value); - for (const m of matched) - props.output[m.status] = { - type: tuple.type, - typeName: tuple.typeName, - contentType: "application/json", - description: m.description, - }; - return true; - }; +// // DO ASSIGN +// const matched: IReflectHttpOperation.IException[] = Object.entries( +// props.operation.exceptions, +// ) +// .filter(([key]) => status === key) +// .map(([_key, value]) => value); +// for (const m of matched) +// props.output[m.status] = { +// type: tuple.type, +// typeName: tuple.typeName, +// contentType: "application/json", +// description: m.description, +// }; +// return true; +// }; - const getStatus = - (checker: ts.TypeChecker) => - (expression: ts.Expression | null): string | null => { - if (expression === null) return null; +// const getStatus = +// (checker: ts.TypeChecker) => +// (expression: ts.Expression | null): string | null => { +// if (expression === null) return null; - const type: ts.Type = checker.getTypeAtLocation(expression); - const result = MetadataFactory.analyze(checker)({ - escape: true, - constant: true, - absorb: true, - })(new MetadataCollection())(type); - if (false === result.success) return null; +// const type: ts.Type = checker.getTypeAtLocation(expression); +// const result = MetadataFactory.analyze(checker)({ +// escape: true, +// constant: true, +// absorb: true, +// })(new MetadataCollection())(type); +// if (false === result.success) return null; - const meta: Metadata = result.data; - if (meta.constants.length === 1) - return meta.constants[0].values[0].value.toString(); - else if (meta.escaped && meta.escaped.returns.constants.length === 1) - return meta.escaped.returns.constants[0].values[0].value.toString(); - else if (ts.isStringLiteral(expression)) return expression.text; - else if (ts.isNumericLiteral(expression)) { - const value: number = Number(expression.text.split("_").join("")); - if (false === isNaN(value)) return value.toString(); - } - return null; - }; -} +// const meta: Metadata = result.data; +// if (meta.constants.length === 1) +// return meta.constants[0].values[0].value.toString(); +// else if (meta.escaped && meta.escaped.returns.constants.length === 1) +// return meta.escaped.returns.constants[0].values[0].value.toString(); +// else if (ts.isStringLiteral(expression)) return expression.text; +// else if (ts.isNumericLiteral(expression)) { +// const value: number = Number(expression.text.split("_").join("")); +// if (false === isNaN(value)) return value.toString(); +// } +// return null; +// }; +// } -const TYPED_EXCEPTION_PATH = path.join( - "node_modules", - "@nestia", - "core", - "lib", - "decorators", - "TypedException.d.ts", -); +// const TYPED_EXCEPTION_PATH = path.join( +// "node_modules", +// "@nestia", +// "core", +// "lib", +// "decorators", +// "TypedException.d.ts", +// ); diff --git a/packages/sdk/src/analyses/GenericAnalyzer.ts b/packages/sdk/src/analyses/GenericAnalyzer.ts index e3bccbcf3..0fd6e399f 100644 --- a/packages/sdk/src/analyses/GenericAnalyzer.ts +++ b/packages/sdk/src/analyses/GenericAnalyzer.ts @@ -4,7 +4,7 @@ export namespace GenericAnalyzer { export function analyze( checker: ts.TypeChecker, classNode: ts.ClassDeclaration, - ): Dictionary { + ): WeakMap { const dict: WeakMap = new WeakMap(); explore(checker, dict, classNode); return dict; @@ -12,7 +12,7 @@ export namespace GenericAnalyzer { function explore( checker: ts.TypeChecker, - dict: Dictionary, + dict: WeakMap, classNode: ts.ClassDeclaration, ): void { if (classNode.heritageClauses === undefined) return; diff --git a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts index 9ef7eee69..104ca7410 100644 --- a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts @@ -68,7 +68,8 @@ export namespace ReflectControllerAnalyzer { if (typeof value !== "function") continue; const metadata: IOperationMetadata | undefined = Reflect.getMetadata( "nestia/OperationMetadata", - value, + props.controller.class.prototype, + key, ); if (metadata === undefined) continue; const next: ReflectHttpOperationAnalyzer.IProps = { diff --git a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts index e38a3b7f6..954af7d84 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -92,7 +92,7 @@ export namespace ReflectHttpOperationAnalyzer { success, security: ReflectMetadataAnalyzer.securities(props.function), exceptions: {} as any, - tags: Reflect.getMetadata("swagger/apiUseTags", props.function), + tags: Reflect.getMetadata("swagger/apiUseTags", props.function) ?? [], imports: ImportAnalyzer.unique( [ ...props.metadata.parameters @@ -101,6 +101,12 @@ export namespace ReflectHttpOperationAnalyzer { ...props.metadata.success.imports, ].flat(), ), + description: props.metadata.description, + jsDocTags: props.metadata.jsDocTags, + operationId: props.metadata.jsDocTags + .find(({ name }) => name === "operationId") + ?.text?.[0].text.split(" ")[0] + .trim(), }; // VALIDATE PATH ARGUMENTS diff --git a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts index ad980e15e..8d0501a59 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -155,6 +155,8 @@ export namespace ReflectHttpOperationParameterAnalyzer { name: matched.name, type: matched.type, validate: HttpParameterProgrammer.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, ...schema, }; else if (p.kind === "query" || p.kind === "headers") @@ -168,6 +170,8 @@ export namespace ReflectHttpOperationParameterAnalyzer { p.kind === "query" ? HttpQueryProgrammer.validate : HttpHeadersProgrammer.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, ...schema, }; else if (p.kind === "body") @@ -186,6 +190,8 @@ export namespace ReflectHttpOperationParameterAnalyzer { : p.contentType === "multipart/form-data" ? HttpFormDataProgrammer.validate : TextPlainValidator.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, ...schema, }; else { @@ -204,7 +210,7 @@ export namespace ReflectHttpOperationParameterAnalyzer { ): IReflectHttpOperationParameter.IPreconfigured[] => { const dict: NestParameters | undefined = Reflect.getMetadata( ROUTE_ARGS_METADATA, - props.controller.constructor, + props.controller.class, props.functionName, ); if (dict === undefined) return []; diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index de7e83e14..d4a9a1f3f 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -68,8 +68,7 @@ export namespace ReflectWebSocketOperationAnalyzer { ); else if ( p.kind === "param" && - !(p as IReflectWebSocketOperationParameter.IParamParameter).field - ?.length + !(p as IReflectWebSocketOperationParameter.IParam).field?.length ) return errors.push(`@WebSocketRoute.Param() must have a field name.`); else if ( @@ -100,7 +99,10 @@ export namespace ReflectWebSocketOperationAnalyzer { field: p.field!, name: matched.name, type: matched.type, - } satisfies IReflectWebSocketOperationParameter.IParamParameter; + imports: matched.imports, + description: matched.description, + jsDocTags: matched.jsDocTags, + } satisfies IReflectWebSocketOperationParameter.IParam; // UNKNOWN TYPE, MAYBE NEW FEATURE return errors.push( diff --git a/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts new file mode 100644 index 000000000..62613eb37 --- /dev/null +++ b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts @@ -0,0 +1,177 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; +import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; +import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { MetadataComponents } from "typia/lib/schemas/metadata/MetadataComponents"; +import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; +import { Escaper } from "typia/lib/utils/Escaper"; + +import { IReflectController } from "../structures/IReflectController"; +import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; +import { ITypedHttpRouteException } from "../structures/ITypedHttpRouteException"; +import { ITypedHttpRouteParameter } from "../structures/ITypedHttpRouteParameter"; +import { ITypedHttpRouteSuccess } from "../structures/ITypedHttpRouteSuccess"; + +export namespace TypedHttpRouteAnalyzer { + export const dictionary = ( + controllers: IReflectController[], + ): IMetadataDictionary => { + const individual: IMetadataComponents[] = []; + for (const c of controllers) + for (const o of c.operations) { + if (o.protocol !== "http") continue; + if (o.success) individual.push(o.success.components); + for (const p of o.parameters) individual.push(p.components); + for (const e of Object.values(o.exceptions)) + individual.push(e.components); + } + const components: MetadataComponents = MetadataComponents.from({ + objects: Object.values( + Object.fromEntries( + individual.map((c) => c.objects.map((o) => [o.name, o])).flat(), + ), + ), + arrays: Object.values( + Object.fromEntries( + individual.map((c) => c.arrays.map((a) => [a.name, a])).flat(), + ), + ), + tuples: Object.values( + Object.fromEntries( + individual.map((c) => c.tuples.map((t) => [t.name, t])).flat(), + ), + ), + aliases: Object.values( + Object.fromEntries( + individual.map((c) => c.aliases.map((a) => [a.name, a])).flat(), + ), + ), + }); + return components.dictionary; + }; + + export const analyze = (props: { + controller: IReflectController; + errors: IReflectOperationError[]; + dictionary: IMetadataDictionary; + operation: IReflectHttpOperation; + paths: string[]; + }): ITypedHttpRoute[] => { + const errors: IReflectOperationError[] = []; + const cast = ( + next: { + metadata: IMetadata; + validate: MetadataFactory.Validator; + }, + from: string, + escape: boolean, + ): Metadata => { + const metadata: Metadata = Metadata.from(next.metadata, props.dictionary); + const metaErrors: MetadataFactory.IError[] = MetadataFactory.validate()({ + escape, + constant: true, + absorb: true, + validate: next.validate, + })(next.validate)(metadata); + if (metaErrors.length) + errors.push({ + file: props.controller.file, + class: props.controller.class.name, + function: props.operation.name, + from, + contents: metaErrors.map((e) => ({ + name: e.name, + accessor: + e.explore.object !== null + ? join({ + object: e.explore.object, + key: e.explore.property, + }) + : null, + messages: e.messages, + })), + }); + return metadata; + }; + const exceptions: Record< + number | "2XX" | "3XX" | "4XX" | "5XX", + ITypedHttpRouteException + > = Object.fromEntries( + Object.entries(props.operation.exceptions).map(([key, value]) => [ + key as any, + { + status: value.status, + description: value.description, + example: value.example, + examples: value.examples, + type: value.type, + metadata: cast(value, `exception (status: ${key})`, true), + }, + ]), + ); + const parameters: ITypedHttpRouteParameter[] = + props.operation.parameters.map((p) => ({ + ...p, + metadata: cast( + p, + `parameter (name: ${JSON.stringify(p.name)})`, + p.kind === "body" && + (p.contentType === "application/json" || p.encrypted === true), + ), + })); + const success: ITypedHttpRouteSuccess = { + ...props.operation.success, + metadata: cast( + props.operation.success, + "success", + props.operation.success.encrypted || + props.operation.success.contentType === "application/json", + ), + setHeaders: props.operation.jsDocTags + .filter( + (t) => + t.text?.length && + t.text[0].text && + (t.name === "setHeader" || t.name === "assignHeaders"), + ) + .map((t) => + t.name === "setHeader" + ? { + type: "setter", + source: t.text![0].text.split(" ")[0].trim(), + target: t.text![0].text.split(" ")[1]?.trim(), + } + : { + type: "assigner", + source: t.text![0].text, + }, + ), + }; + if (errors.length) return []; + return props.paths.map((path) => ({ + ...props.operation, + controller: props.controller, + path, + accessors: ["@lazy"], + exceptions, + parameters, + success, + })); + }; +} + +const join = ({ + object, + key, +}: { + object: MetadataObject; + key: string | object | null; +}) => { + if (key === null) return object.name; + else if (typeof key === "object") return `${object.name}[key]`; + else if (Escaper.variable(key)) return `${object.name}.${key}`; + return `${object.name}[${JSON.stringify(key)}]`; +}; diff --git a/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts new file mode 100644 index 000000000..63548175c --- /dev/null +++ b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts @@ -0,0 +1,17 @@ +import { IReflectController } from "../structures/IReflectController"; +import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; +import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; + +export namespace TypedWebSocketRouteAnalyzer { + export const analyze = (props: { + controller: IReflectController; + operation: IReflectWebSocketOperation; + paths: string[]; + }): ITypedWebSocketRoute[] => + props.paths.map((path) => ({ + ...props.operation, + controller: props.controller, + path, + accessors: ["@lazy"], + })); +} diff --git a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts index 8ed41b899..7e4477b65 100644 --- a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts +++ b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts @@ -37,14 +37,19 @@ export namespace NestiaConfigLoader { if (fs.existsSync(path.resolve(file)) === false) throw new Error(`Unable to find "${file}" file.`); + const plugins: any[] = compilerOptions.plugins ?? []; + if (typia.is(plugins) === false) + throw new Error(`invalid "compilerOptions.plugins" data.`); + if ( + plugins.some((x: any) => x.transform === "@nestia/sdk/lib/transform") === + false + ) + plugins.push({ transform: "@nestia/sdk/lib/transform" }); register({ emit: false, compilerOptions: { ...compilerOptions, - plugins: [ - ...(compilerOptions.plugins ?? []), - { transform: "@nestia/sdk/lib/transform" }, - ], + plugins, }, require: compilerOptions.baseUrl ? ["tsconfig-paths/register"] diff --git a/packages/sdk/src/executable/internal/NestiaSdkCommand.ts b/packages/sdk/src/executable/internal/NestiaSdkCommand.ts index 8c9c896f0..026d64a76 100644 --- a/packages/sdk/src/executable/internal/NestiaSdkCommand.ts +++ b/packages/sdk/src/executable/internal/NestiaSdkCommand.ts @@ -34,10 +34,7 @@ export namespace NestiaSdkCommand { ); // GENERATE - const app: NestiaSdkApplication = new NestiaSdkApplication( - config, - command.options, - ); + const app: NestiaSdkApplication = new NestiaSdkApplication(config); await task(app); }; diff --git a/packages/sdk/src/generates/CloneGenerator.ts b/packages/sdk/src/generates/CloneGenerator.ts index 33a91f00c..e04147520 100644 --- a/packages/sdk/src/generates/CloneGenerator.ts +++ b/packages/sdk/src/generates/CloneGenerator.ts @@ -1,64 +1,65 @@ -import fs from "fs"; -import ts from "typescript"; +// import fs from "fs"; +// import ts from "typescript"; -import { INestiaProject } from "../structures/INestiaProject"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { FilePrinter } from "./internal/FilePrinter"; -import { ImportDictionary } from "./internal/ImportDictionary"; -import { SdkHttpCloneProgrammer } from "./internal/SdkHttpCloneProgrammer"; +// import { INestiaProject } from "../structures/INestiaProject"; +// import { ITypedApplication } from "../structures/ITypedApplication"; +// import { FilePrinter } from "./internal/FilePrinter"; +// import { ImportDictionary } from "./internal/ImportDictionary"; +// // import { SdkHttpCloneProgrammer } from "./internal/SdkHttpCloneProgrammer"; -export namespace CloneGenerator { - export const write = - (project: INestiaProject) => - async (routes: ITypedHttpRoute[]): Promise => { - const dict: Map = - SdkHttpCloneProgrammer.write(project)(routes); - if (dict.size === 0) return; - try { - await fs.promises.mkdir(`${project.config.output}/structures`); - } catch {} - for (const [key, value] of dict) await writeDtoFile(project)(key, value); - }; +// export namespace CloneGenerator { +// export const write = async (app: ITypedApplication): Promise => { +// // const dict: Map = +// // SdkHttpCloneProgrammer.write(app.project)( +// // app.routes.filter((r) => r.protocol === "http"), +// // ); +// if (dict.size === 0) return; +// try { +// await fs.promises.mkdir(`${app.project.config.output}/structures`); +// } catch {} +// for (const [key, value] of dict) +// await writeDtoFile(app.project)(key, value); +// }; - const writeDtoFile = - (project: INestiaProject) => - async ( - key: string, - value: SdkHttpCloneProgrammer.IModule, - ): Promise => { - const location: string = `${project.config.output}/structures/${key}.ts`; - const importer: ImportDictionary = new ImportDictionary(location); - const statements: ts.Statement[] = iterate(importer)(value); - if (statements.length === 0) return; +// const writeDtoFile = +// (project: INestiaProject) => +// async ( +// key: string, +// value: SdkHttpCloneProgrammer.IModule, +// ): Promise => { +// const location: string = `${project.config.output}/structures/${key}.ts`; +// const importer: ImportDictionary = new ImportDictionary(location); +// const statements: ts.Statement[] = iterate(importer)(value); +// if (statements.length === 0) return; - await FilePrinter.write({ - location, - statements: [ - ...importer.toStatements(`${project.config.output}/structures`), - ...(importer.empty() ? [] : [FilePrinter.enter()]), - ...statements, - ], - }); - }; +// await FilePrinter.write({ +// location, +// statements: [ +// ...importer.toStatements(`${project.config.output}/structures`), +// ...(importer.empty() ? [] : [FilePrinter.enter()]), +// ...statements, +// ], +// }); +// }; - const iterate = - (importer: ImportDictionary) => - (modulo: SdkHttpCloneProgrammer.IModule): ts.Statement[] => { - const output: ts.Statement[] = []; - if (modulo.programmer !== null) output.push(modulo.programmer(importer)); - if (modulo.children.size) { - const internal: ts.Statement[] = []; - for (const child of modulo.children.values()) - internal.push(...iterate(importer)(child)); - output.push( - ts.factory.createModuleDeclaration( - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.factory.createIdentifier(modulo.name), - ts.factory.createModuleBlock(internal), - ts.NodeFlags.Namespace, - ), - ); - } - return output; - }; -} +// const iterate = +// (importer: ImportDictionary) => +// (modulo: SdkHttpCloneProgrammer.IModule): ts.Statement[] => { +// const output: ts.Statement[] = []; +// if (modulo.programmer !== null) output.push(modulo.programmer(importer)); +// if (modulo.children.size) { +// const internal: ts.Statement[] = []; +// for (const child of modulo.children.values()) +// internal.push(...iterate(importer)(child)); +// output.push( +// ts.factory.createModuleDeclaration( +// [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], +// ts.factory.createIdentifier(modulo.name), +// ts.factory.createModuleBlock(internal), +// ts.NodeFlags.Namespace, +// ), +// ); +// } +// return output; +// }; +// } diff --git a/packages/sdk/src/generates/E2eGenerator.ts b/packages/sdk/src/generates/E2eGenerator.ts index 9531486e3..7e47e1ff8 100644 --- a/packages/sdk/src/generates/E2eGenerator.ts +++ b/packages/sdk/src/generates/E2eGenerator.ts @@ -3,32 +3,31 @@ import path from "path"; import { ConfigAnalyzer } from "../analyses/ConfigAnalyzer"; import { INestiaProject } from "../structures/INestiaProject"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; +import { ITypedApplication } from "../structures/ITypedApplication"; import { E2eFileProgrammer } from "./internal/E2eFileProgrammer"; export namespace E2eGenerator { - export const generate = - (project: INestiaProject) => - async (routeList: ITypedHttpRoute[]): Promise => { - console.log("Generating E2E Test Functions"); - - // PREPARE DIRECTORIES - const output: string = path.resolve(project.config.e2e!); - await mkdir(output); - await mkdir(path.join(output, "features")); - await mkdir(path.join(output, "features", "api")); - await mkdir(path.join(output, "features", "api", "automated")); - - // GENERATE TEST INDEX FILE - await index(project)(path.join(project.config.e2e!, "index.ts")); - - // GENERATE EACH TEST FILES - for (const route of routeList) - await E2eFileProgrammer.generate(project)({ - api: path.resolve(project.config.output!), + export const generate = async (app: ITypedApplication): Promise => { + console.log("Generating E2E Test Functions"); + + // PREPARE DIRECTORIES + const output: string = path.resolve(app.project.config.e2e!); + await mkdir(output); + await mkdir(path.join(output, "features")); + await mkdir(path.join(output, "features", "api")); + await mkdir(path.join(output, "features", "api", "automated")); + + // GENERATE TEST INDEX FILE + await index(app.project)(path.join(app.project.config.e2e!, "index.ts")); + + // GENERATE EACH TEST FILES + for (const route of app.routes) + if (route.protocol === "http") + await E2eFileProgrammer.generate(app.project)({ + api: path.resolve(app.project.config.output!), current: path.join(output, "features", "api", "automated"), })(route); - }; + }; const index = (project: INestiaProject) => diff --git a/packages/sdk/src/generates/OpenAiGenerator.ts b/packages/sdk/src/generates/OpenAiGenerator.ts index 562b0c0e9..1f310f731 100644 --- a/packages/sdk/src/generates/OpenAiGenerator.ts +++ b/packages/sdk/src/generates/OpenAiGenerator.ts @@ -6,55 +6,54 @@ import { import fs from "fs"; import path from "path"; -import { INestiaProject } from "../structures/INestiaProject"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; +import { ITypedApplication } from "../structures/ITypedApplication"; import { SwaggerGenerator } from "./SwaggerGenerator"; export namespace OpenAiGenerator { - export const generate = - (project: INestiaProject) => - async ( - routes: Array, - ): Promise => { - console.log("Generating OpenAI Function Calling Document"); + export const generate = async (app: ITypedApplication): Promise => { + console.log("Generating OpenAI Function Calling Document"); - const config = project.config.openai!; - const parsed: path.ParsedPath = path.parse(config.output); - const directory: string = path.dirname(parsed.dir); - if (fs.existsSync(directory) === false) - try { - await fs.promises.mkdir(directory); - } catch {} - if (fs.existsSync(directory) === false) - throw new Error( - `Error on NestiaApplication.openai(): failed to create output directory: ${directory}`, - ); + const config = app.project.config.openai; + if (config === undefined) + throw new Error("OpenAI configuration is not defined"); - const location: string = !!parsed.ext - ? path.resolve(config.output) - : path.join(path.resolve(config.output), "openai.json"); - const swagger: OpenApi.IDocument = await SwaggerGenerator.compose({ - ...project, - config: { - ...project.config, - swagger: project.config.swagger ?? { output: "" }, - }, - })(routes); - const document: IOpenAiDocument = OpenAiComposer.document({ - swagger, - options: config, - }); - await fs.promises.writeFile( - location, - !config.beautify - ? JSON.stringify(document) - : JSON.stringify( - document, - null, - typeof config.beautify === "number" ? config.beautify : 2, - ), - "utf8", + const parsed: path.ParsedPath = path.parse(config.output); + const directory: string = path.dirname(parsed.dir); + if (fs.existsSync(directory) === false) + try { + await fs.promises.mkdir(directory); + } catch {} + if (fs.existsSync(directory) === false) + throw new Error( + `Error on NestiaApplication.openai(): failed to create output directory: ${directory}`, ); - }; + + const location: string = !!parsed.ext + ? path.resolve(config.output) + : path.join(path.resolve(config.output), "openai.json"); + const swagger: OpenApi.IDocument = await SwaggerGenerator.compose({ + config: app.project.config.swagger ?? { output: "" }, + routes: app.routes.filter((route) => route.protocol === "http"), + document: { + openapi: "3.1.0", + components: {}, + "x-samchon-emended": true, + }, + }); + const document: IOpenAiDocument = OpenAiComposer.document({ + swagger, + options: config, + }); + await fs.promises.writeFile( + location, + !config.beautify + ? JSON.stringify(document) + : JSON.stringify( + document, + null, + typeof config.beautify === "number" ? config.beautify : 2, + ), + "utf8", + ); + }; } diff --git a/packages/sdk/src/generates/SdkGenerator.ts b/packages/sdk/src/generates/SdkGenerator.ts index 0edf7c417..2b6a1fb5f 100644 --- a/packages/sdk/src/generates/SdkGenerator.ts +++ b/packages/sdk/src/generates/SdkGenerator.ts @@ -1,148 +1,120 @@ import fs from "fs"; import NodePath from "path"; -import path from "path"; import { IPointer } from "tstl"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -import { INestiaProject } from "../structures/INestiaProject"; -import { ISwaggerError } from "../structures/ISwaggerError"; +import { INestiaConfig } from "../INestiaConfig"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { IReflectType } from "../structures/IReflectType"; +import { ITypedApplication } from "../structures/ITypedApplication"; import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; -import { CloneGenerator } from "./CloneGenerator"; -import { SwaggerGenerator } from "./SwaggerGenerator"; +// import { CloneGenerator } from "./CloneGenerator"; import { SdkDistributionComposer } from "./internal/SdkDistributionComposer"; import { SdkFileProgrammer } from "./internal/SdkFileProgrammer"; export namespace SdkGenerator { - export const generate = - (project: INestiaProject) => - async ( - routes: Array, - ): Promise => { - console.log("Generating SDK Library"); + export const generate = async (app: ITypedApplication): Promise => { + if (app.project.config.output === undefined) + throw new Error("Output directory is not defined."); - // VALIDATE THROUGH SWAGGER GENERATOR - const errors: ISwaggerError[] = []; - const validate = SwaggerGenerator.generate_route({ - config: { output: "" }, - checker: project.checker, - collection: new MetadataCollection({ - replace: MetadataCollection.replace, - }), - lazyProperties: [], - lazySchemas: [], - errors, - swagger: await SwaggerGenerator.initialize( - project.config.swagger ?? { output: "" }, - ), - }); - for (const r of routes) - if (r.protocol === "http") { - validate(r); - if (project.config.clone !== true) validateImplicity(project)(r); - } - if (errors.length) { - for (const e of errors) - console.error( - `${path.relative(process.cwd(), e.route.location)}:${ - e.route.controller.name - }.${e.route.name}:${ - e.from - } - error TS(@nestia/sdk): invalid type detected.\n\n` + - e.messages.map((m) => ` - ${m}`).join("\n"), - "\n\n", - ); - throw new TypeError("Invalid type detected"); - } else if (project.errors.length) { - for (const e of project.errors) - console.error( - `${path.relative(process.cwd(), e.file)}:${ - e.controller - }.${e.function}: error TS(@nestia/sdk): ${e.message}`, - ); - throw new TypeError("Invalid type detected"); - } + // PREPARE NEW DIRECTORIES + console.log("Generating SDK Library"); + try { + await fs.promises.mkdir(app.project.config.output); + } catch {} - // PREPARE NEW DIRECTORIES - try { - await fs.promises.mkdir(project.config.output!); - } catch {} + // BUNDLING + const bundle: string[] = await fs.promises.readdir(BUNDLE_PATH); + for (const file of bundle) { + const current: string = `${BUNDLE_PATH}/${file}`; + const target: string = `${app.project.config.output}/${file}`; + const stats: fs.Stats = await fs.promises.stat(current); - // BUNDLING - const bundle: string[] = await fs.promises.readdir(BUNDLE_PATH); - for (const file of bundle) { - const current: string = `${BUNDLE_PATH}/${file}`; - const target: string = `${project.config.output}/${file}`; - const stats: fs.Stats = await fs.promises.stat(current); - - if (stats.isFile() === true) { - const content: string = await fs.promises.readFile(current, "utf8"); - if (fs.existsSync(target) === false) - await fs.promises.writeFile(target, content, "utf8"); - else if (BUNDLE_CHANGES[file] !== undefined) { - const r: IPointer = { - value: await fs.promises.readFile(target, "utf8"), - }; - for (const [before, after] of BUNDLE_CHANGES[file]) - r.value = r.value.replace(before, after); - await fs.promises.writeFile(target, r.value, "utf8"); - } + if (stats.isFile() === true) { + const content: string = await fs.promises.readFile(current, "utf8"); + if (fs.existsSync(target) === false) + await fs.promises.writeFile(target, content, "utf8"); + else if (BUNDLE_CHANGES[file] !== undefined) { + const r: IPointer = { + value: await fs.promises.readFile(target, "utf8"), + }; + for (const [before, after] of BUNDLE_CHANGES[file]) + r.value = r.value.replace(before, after); + await fs.promises.writeFile(target, r.value, "utf8"); } } + } - // STRUCTURES - if (project.config.clone) - await CloneGenerator.write(project)( - routes.filter((r) => r.protocol === "http") as ITypedHttpRoute[], - ); + // // STRUCTURES + // if (app.project.config.clone === true) await CloneGenerator.write(app); - // FUNCTIONAL - await SdkFileProgrammer.generate(project)(routes); + // FUNCTIONAL + await SdkFileProgrammer.generate(app); - // DISTRIBUTION - if (project.config.distribute !== undefined) - await SdkDistributionComposer.compose( - project.config, - routes.some((r) => r.protocol === "websocket"), - ); - }; + // DISTRIBUTION + if (app.project.config.distribute !== undefined) + await SdkDistributionComposer.compose({ + config: app.project.config, + websocket: app.routes.some((r) => r.protocol === "websocket"), + }); + }; - const validateImplicity = - (project: INestiaProject) => - (route: ITypedHttpRoute): void => { - for (const p of route.parameters) { - if (isImplicitType(p.typeName)) - project.errors.push({ - file: route.location, - controller: route.controller.name, - function: route.name, - message: `implicit (unnamed) parameter type from "${route.name}@${p.name}".`, - }); - } - if (project.config.propagate !== true) - for (const [key, value] of Object.entries(route.exceptions)) - if (isImplicitType(value.typeName)) - project.errors.push({ - file: route.location, - controller: route.controller.name, - function: route.name, - message: `implicit (unnamed) exception type of ${key} status from "${route.name}".`, - }); - if (isImplicitType(route.output.typeName)) - project.errors.push({ - file: route.location, - controller: route.controller.name, - function: route.name, - message: `implicit (unnamed) return type from "${route.name}".`, + export const validate = ( + app: ITypedApplication, + ): IReflectOperationError[] => { + const errors: IReflectOperationError[] = []; + if (app.project.config.clone === true) return errors; + for (const route of app.routes) + if (route.protocol === "http") + validateImplicity({ + config: app.project.config, + errors, + route, + }); + return errors; + }; + + const validateImplicity = (props: { + config: INestiaConfig; + errors: IReflectOperationError[]; + route: ITypedHttpRoute; + }): void => { + for (const p of props.route.parameters) { + if (isImplicitType(p.type)) + props.errors.push({ + file: props.route.controller.file, + class: props.route.controller.class.name, + function: props.route.name, + from: `parameter ${JSON.stringify(p.name)}`, + contents: [`implicit (unnamed) parameter type.`], }); - }; + } + if (props.config.propagate === true) + for (const [key, value] of Object.entries(props.route.exceptions)) + if (isImplicitType(value.type)) + props.errors.push({ + file: props.route.controller.file, + class: props.route.controller.class.name, + function: props.route.name, + from: `exception ${JSON.stringify(key)}`, + contents: [`implicit (unnamed) exception type.`], + }); + if (isImplicitType(props.route.success.type)) + props.errors.push({ + file: props.route.controller.file, + class: props.route.controller.class.name, + function: props.route.name, + from: "success", + contents: [`implicit (unnamed) return type.`], + }); + }; - const isImplicitType = (typeName: string): boolean => - typeName === "__type" || - typeName === "__object" || - typeName.startsWith("__type.") || - typeName.startsWith("__object.") || - typeName.includes("readonly ["); + const isImplicitType = (type: IReflectType): boolean => + type.name === "__type" || + type.name === "__object" || + type.name.startsWith("__type.") || + type.name.startsWith("__object.") || + type.name.includes("readonly [") || + (!!type.typeArguments?.length && type.typeArguments.some(isImplicitType)); export const BUNDLE_PATH = NodePath.join( __dirname, diff --git a/packages/sdk/src/generates/SwaggerGenerator.ts b/packages/sdk/src/generates/SwaggerGenerator.ts index 46ff5e779..66f1355a6 100644 --- a/packages/sdk/src/generates/SwaggerGenerator.ts +++ b/packages/sdk/src/generates/SwaggerGenerator.ts @@ -2,311 +2,101 @@ import { OpenApi, OpenApiV3, SwaggerV2 } from "@samchon/openapi"; import fs from "fs"; import path from "path"; import { Singleton } from "tstl"; -import ts from "typescript"; import typia, { IJsonApplication } from "typia"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; import { INestiaConfig } from "../INestiaConfig"; -import { INestiaProject } from "../structures/INestiaProject"; -import { ISwaggerError } from "../structures/ISwaggerError"; -import { ISwaggerLazyProperty } from "../structures/ISwaggerLazyProperty"; -import { ISwaggerLazySchema } from "../structures/ISwaggerLazySchema"; +import { ITypedApplication } from "../structures/ITypedApplication"; import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; +import { ITypedHttpRouteParameter } from "../structures/ITypedHttpRouteParameter"; import { FileRetriever } from "../utils/FileRetriever"; -import { MapUtil } from "../utils/MapUtil"; -import { SwaggerDescriptionGenerator } from "./internal/SwaggerDescriptionGenerator"; -import { SwaggerSchemaGenerator } from "./internal/SwaggerSchemaGenerator"; +import { SwaggerOperationComposer } from "./internal/SwaggerOperationComposer"; export namespace SwaggerGenerator { - export interface IProps { - config: INestiaConfig.ISwaggerConfig; - checker: ts.TypeChecker; - collection: MetadataCollection; - lazySchemas: Array; - lazyProperties: Array; - errors: ISwaggerError[]; - swagger: OpenApi.IDocument; - } - - export const generate = - (project: INestiaProject) => - async ( - routeList: Array, - ): Promise => { - console.log("Generating Swagger Document"); - - // VALIDATE SECURITY - const config = project.config.swagger!; - const httpRoutes: ITypedHttpRoute[] = routeList.filter( - (route): route is ITypedHttpRoute => route.protocol === "http", - ) as ITypedHttpRoute[]; - validate_security(config)(httpRoutes); - - // PREPARE ASSETS - const parsed: path.ParsedPath = path.parse(config.output); - const directory: string = path.dirname(parsed.dir); - if (fs.existsSync(directory) === false) - try { - await fs.promises.mkdir(directory); - } catch {} - if (fs.existsSync(directory) === false) - throw new Error( - `Error on NestiaApplication.swagger(): failed to create output directory: ${directory}`, - ); - - // COMPOSE SWAGGER DOCUMENT - const location: string = !!parsed.ext - ? path.resolve(config.output) - : path.join(path.resolve(config.output), "swagger.json"); - const swagger: OpenApi.IDocument = await compose(project)(routeList); - - // DO GENERATE - const document: - | OpenApi.IDocument - | SwaggerV2.IDocument - | OpenApiV3.IDocument = - config.openapi === "2.0" - ? OpenApi.downgrade(swagger, config.openapi as "2.0") - : config.openapi === "3.0" - ? OpenApi.downgrade(swagger, config.openapi as "3.0") - : swagger; - await fs.promises.writeFile( - location, - !config.beautify - ? JSON.stringify(document) - : JSON.stringify( - document, - null, - typeof config.beautify === "number" ? config.beautify : 2, - ), - "utf8", + export const generate = async (app: ITypedApplication): Promise => { + // GET CONFIGURATION + if (app.project.config.swagger === undefined) + throw new Error("Swagger configuration is not defined."); + const config: INestiaConfig.ISwaggerConfig = app.project.config.swagger; + + // TARGET LOCATION + const parsed: path.ParsedPath = path.parse(config.output); + const directory: string = path.dirname(parsed.dir); + if (fs.existsSync(directory) === false) + try { + await fs.promises.mkdir(directory); + } catch {} + if (fs.existsSync(directory) === false) + throw new Error( + `Error on NestiaApplication.swagger(): failed to create output directory: ${directory}`, ); - }; - - export const compose = - (project: INestiaProject) => - async ( - routeList: Array, - ): Promise => { - // VALIDATE SECURITY - const config = project.config.swagger!; - const httpRoutes: ITypedHttpRoute[] = routeList.filter( - (route): route is ITypedHttpRoute => route.protocol === "http", - ) as ITypedHttpRoute[]; - validate_security(config)(httpRoutes); - - const collection: MetadataCollection = new MetadataCollection({ - replace: MetadataCollection.replace, - }); - - // CONSTRUCT SWAGGER DOCUMENTS - const errors: ISwaggerError[] = []; - const lazySchemas: Array = []; - const lazyProperties: Array = []; - const swagger: OpenApi.IDocument = await initialize(config); - const pathDict: Map< - string, - Record - > = new Map(); - - for (const route of httpRoutes) { - if (route.jsDocTags.find((tag) => tag.name === "internal")) continue; - - const path: Record = MapUtil.take( - pathDict, - get_path(route.path, route.parameters), - () => ({}), - ); - path[route.method.toLowerCase()] = generate_route({ - config, - checker: project.checker, - collection, - lazySchemas, - lazyProperties, - errors, - swagger, - })(route); - } - swagger.paths = {}; - for (const [path, routes] of pathDict) swagger.paths[path] = routes; - - // FILL JSON-SCHEMAS - const application: IJsonApplication<"3.1"> = - JsonApplicationProgrammer.write("3.1")( - lazySchemas.map(({ metadata }) => metadata), - ) as IJsonApplication<"3.1">; - swagger.components = { - ...(swagger.components ?? {}), - ...(application.components ?? {}), - }; - lazySchemas.forEach(({ schema }, index) => { - Object.assign(schema, application.schemas[index]!); - }); - for (const p of lazyProperties) - Object.assign( - p.schema, - ( - application.components.schemas?.[ - p.object - ] as OpenApi.IJsonSchema.IObject - )?.properties?.[p.property] ?? {}, - ); - - // CONFIGURE SECURITY - if (config.security) fill_security(config.security, swagger); - - // REPORT ERRORS - if (errors.length) { - for (const e of errors) - console.error( - `${path.relative(process.cwd(), e.route.location)}:${ - e.route.controller.name - }.${e.route.name}:${ - e.from - } - error TS(@nestia/sdk): invalid type detected.\n\n` + - e.messages.map((m) => ` - ${m}`).join("\n"), - "\n\n", - ); - throw new TypeError("Invalid type detected"); - } - - // SWAGGER CUSTOMIZER - const customizer = { - at: new Singleton(() => { - const functor: Map = new Map(); - for (const route of httpRoutes) { - const method: OpenApi.Method = - route.method.toLowerCase() as OpenApi.Method; - const path: string = get_path(route.path, route.parameters); - const operation: OpenApi.IOperation | undefined = - swagger.paths?.[path]?.[method]; - if (operation === undefined) continue; - functor.set(route.function, { - method, - path, - route: operation, - }); - } - return functor; - }), - get: new Singleton( - () => - (key: Accessor): OpenApi.IOperation | undefined => { - const method: OpenApi.Method = - key.method.toLowerCase() as OpenApi.Method; - const path: string = - "/" + - key.path - .split("/") - .filter((str) => !!str.length) - .map((str) => - str.startsWith(":") ? `{${str.substring(1)}}` : str, - ) - .join("/"); - return swagger.paths?.[path]?.[method]; - }, - ), - }; - for (const route of httpRoutes) { - if ( - false === - Reflect.hasMetadata( - "nestia/SwaggerCustomizer", - route.controller.prototype, - route.name, - ) - ) - continue; - - const path: string = get_path(route.path, route.parameters); - const method: OpenApi.Method = - route.method.toLowerCase() as OpenApi.Method; - const target: OpenApi.IOperation | undefined = - swagger.paths?.[path]?.[method]; - if (target === undefined) continue; - - const closure: Function | Function[] = Reflect.getMetadata( - "nestia/SwaggerCustomizer", - route.controller.prototype, - route.name, - ); - const array: Function[] = Array.isArray(closure) ? closure : [closure]; - for (const fn of array) - fn({ - route: target, - method, - path, - swagger, - at: (func: Function) => customizer.at.get().get(func), - get: (accessor: Accessor) => customizer.get.get()(accessor), - }); - } - return swagger; - }; - - const validate_security = - (config: INestiaConfig.ISwaggerConfig) => - (routeList: ITypedHttpRoute[]): void | never => { - const securityMap: Map< - string, - { scheme: OpenApi.ISecurityScheme; scopes: Set } - > = new Map(); - for (const [key, value] of Object.entries(config.security ?? {})) - securityMap.set(key, { - scheme: emend_security(value), - scopes: - value.type === "oauth2" - ? new Set([ - ...Object.keys(value.flows.authorizationCode?.scopes ?? {}), - ...Object.keys(value.flows.implicit?.scopes ?? {}), - ...Object.keys(value.flows.password?.scopes ?? {}), - ...Object.keys(value.flows.clientCredentials?.scopes ?? {}), - ]) - : new Set(), - }); - - const validate = - (reporter: (str: string) => void) => - (key: string, scopes: string[]) => { - const security = securityMap.get(key); - if (security === undefined) - return reporter(`target security scheme "${key}" does not exists.`); - else if (scopes.length === 0) return; - else if (security.scheme.type !== "oauth2") - return reporter( - `target security scheme "${key}" is not "oauth2" type, but you've configured the scopes.`, - ); - for (const s of scopes) - if (security.scopes.has(s) === false) - reporter( - `target security scheme "${key}" does not have a specific scope "${s}".`, - ); - }; - - const violations: string[] = []; - for (const route of routeList) - for (const record of route.security) - for (const [key, scopes] of Object.entries(record)) - validate((str) => - violations.push( - ` - ${str} (${route.controller.name}.${route.name}() at "${route.location}")`, - ), - )(key, scopes); + const location: string = !!parsed.ext + ? path.resolve(config.output) + : path.join(path.resolve(config.output), "swagger.json"); + + // COMPOSE SWAGGER DOCUMENT + const document: OpenApi.IDocument = compose({ + config, + routes: app.routes.filter((route) => route.protocol === "http"), + document: await initialize(config), + }); + const specified: + | OpenApi.IDocument + | SwaggerV2.IDocument + | OpenApiV3.IDocument = + config.openapi === "2.0" + ? OpenApi.downgrade(document, config.openapi as "2.0") + : config.openapi === "3.0" + ? OpenApi.downgrade(document, config.openapi as "3.0") + : document; + await fs.promises.writeFile( + location, + !config.beautify + ? JSON.stringify(specified) + : JSON.stringify( + specified, + null, + typeof config.beautify === "number" ? config.beautify : 2, + ), + "utf8", + ); + }; - if (violations.length) - throw new Error( - `Error on NestiaApplication.swagger(): invalid security specification. Check your "nestia.config.ts" file's "swagger.security" property, or each controller methods.\n` + - `\n` + - `List of violations:\n` + - violations.join("\n"), - ); - }; + export const compose = (props: { + config: INestiaConfig.ISwaggerConfig; + routes: ITypedHttpRoute[]; + document: OpenApi.IDocument; + }): OpenApi.IDocument => { + // GATHER METADATA + const metadatas: Metadata[] = props.routes + .filter((r) => r.protocol === "http") + .map((r) => [ + r.success.metadata, + ...r.parameters.map((p) => p.metadata), + ...Object.values(r.exceptions).map((e) => e.metadata), + ]) + .flat() + .filter((m) => m.size() !== 0); + + // COMPOSE JSON SCHEMAS + const json: IJsonApplication = JsonApplicationProgrammer.write("3.1")( + metadatas, + ) as IJsonApplication; + const dict: WeakMap = new WeakMap(); + json.schemas.forEach((schema, i) => dict.set(metadatas[i], schema)); + const schema = (metadata: Metadata): OpenApi.IJsonSchema | undefined => + dict.get(metadata); + + // COMPOSE DOCUMENT + const document: OpenApi.IDocument = props.document; + document.components.schemas ??= {}; + Object.assign(document.components.schemas, json.components.schemas); + document.paths = writePaths({ ...props, schema }); + + return document; + }; - /* --------------------------------------------------------- - INITIALIZERS - --------------------------------------------------------- */ export const initialize = async ( config: INestiaConfig.ISwaggerConfig, ): Promise => { @@ -378,134 +168,42 @@ export namespace SwaggerGenerator { paths: {}, components: { schemas: {}, + securitySchemes: config.security, }, tags: config.tags ?? [], "x-samchon-emended": true, }; }; - function get_path( - path: string, - parameters: ITypedHttpRoute.IParameter[], - ): string { - const filtered: ITypedHttpRoute.IParameter[] = parameters.filter( - (param) => param.category === "param" && !!param.field, + const writePaths = (props: { + config: INestiaConfig.ISwaggerConfig; + document: OpenApi.IDocument; + schema: (metadata: Metadata) => OpenApi.IJsonSchema | undefined; + routes: ITypedHttpRoute[]; + }): Record => { + const output: Record = {}; + for (const r of props.routes) { + const path: string = getPath(r); + output[path] ??= {}; + output[path][r.method.toLowerCase() as "get"] = + SwaggerOperationComposer.compose({ + ...props, + route: r, + }); + } + return output; + }; + + const getPath = (route: { + path: string; + parameters: ITypedHttpRouteParameter[]; + }): string => { + let str: string = route.path; + const filtered: ITypedHttpRouteParameter.IParam[] = route.parameters.filter( + (param) => param.kind === "param", ); for (const param of filtered) - path = path.replace(`:${param.field}`, `{${param.field}}`); - return path; - } - - export const generate_route = - (props: IProps) => - (route: ITypedHttpRoute): OpenApi.IOperation => { - // FIND REQUEST BODY - const body = route.parameters.find((param) => param.category === "body"); - - // CONSTRUCT SUMMARY & DESCRIPTION - const getJsDocTexts = (name: string): string[] => - route.jsDocTags - .filter( - (tag) => - tag.name === name && - tag.text && - tag.text.find( - (elem) => elem.kind === "text" && elem.text.length, - ) !== undefined, - ) - .map((tag) => tag.text!.find((elem) => elem.kind === "text")!.text); - const { summary, description } = SwaggerDescriptionGenerator.generate({ - description: route.description, - jsDocTags: route.jsDocTags, - kind: "summary", - }); - const deprecated = route.jsDocTags.find( - (tag) => tag.name === "deprecated", - ); - - // CONSTRUCT TAGS - const tagSet: Set = new Set([ - ...route.swaggerTags, - ...getJsDocTexts("tag").map((tag) => tag.split(" ")[0]), - ]); - props.swagger.tags ??= []; - for (const tag of tagSet) - if (props.swagger.tags!.find((elem) => elem.name === tag) === undefined) - props.swagger.tags!.push({ name: tag }); - for (const texts of getJsDocTexts("tag")) { - const [name, ...description] = texts.split(" "); - if (description.length) - props.swagger.tags!.find( - (elem) => elem.name === name, - )!.description ??= description.join(" "); - } - - // FINALIZE - return { - deprecated: deprecated ? true : undefined, - tags: [...tagSet], - operationId: - route.operationId ?? - props.config.operationId?.({ - class: route.controller.name, - function: route.name, - method: route.method as "GET", - path: route.path, - }), - parameters: route.parameters - .filter((param) => param.category !== "body") - .map((param) => SwaggerSchemaGenerator.parameter(props)(route)(param)) - .flat(), - requestBody: body - ? SwaggerSchemaGenerator.body(props)(route)(body) - : undefined, - responses: SwaggerSchemaGenerator.response(props)(route), - summary, - description, - security: route.security.length ? route.security : undefined, - ...(props.config.additional === true - ? { - "x-nestia-namespace": [ - ...route.path - .split("/") - .filter((str) => str.length && str[0] !== ":"), - route.name, - ].join("."), - "x-nestia-jsDocTags": route.jsDocTags, - "x-nestia-method": route.method, - } - : {}), - }; - }; - - function fill_security( - security: Required["security"], - swagger: OpenApi.IDocument, - ): void { - swagger.components.securitySchemes = {}; - for (const [key, value] of Object.entries(security)) - swagger.components.securitySchemes[key] = emend_security(value); - } - - function emend_security( - input: OpenApi.ISecurityScheme, - ): OpenApi.ISecurityScheme { - if (input.type === "apiKey") - return { - ...input, - in: input.in ?? "header", - name: input.name ?? "Authorization", - }; - return input; - } -} - -interface Accessor { - method: string; - path: string; -} -interface Endpoint { - method: string; - path: string; - route: OpenApi.IOperation; + str = str.replace(`:${param.field}`, `{${param.field}}`); + return str; + }; } diff --git a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts index d5a35e91a..a65315022 100644 --- a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts @@ -7,7 +7,6 @@ import { FilePrinter } from "./FilePrinter"; import { ImportDictionary } from "./ImportDictionary"; import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace E2eFileProgrammer { export const generate = @@ -19,9 +18,9 @@ export namespace E2eFileProgrammer { ); if (project.config.clone !== true) for (const tuple of route.imports) - for (const instance of tuple[1]) + for (const instance of tuple.instances) importer.internal({ - file: tuple[0], + file: tuple.file, type: true, instance, }); @@ -67,7 +66,7 @@ export namespace E2eFileProgrammer { (importer: ImportDictionary) => (route: ITypedHttpRoute) => { const headers = route.parameters.find( - (p) => p.category === "headers" && p.field === undefined, + (p) => p.kind === "headers" && p.field === undefined, ); const connection = headers ? ts.factory.createObjectLiteralExpression( @@ -91,7 +90,7 @@ export namespace E2eFileProgrammer { SdkImportWizard.typia(importer), ), )("random"), - [getTypeName(project)(importer)(headers)], + [SdkAliasCollection.name(headers.type)], undefined, ), ), @@ -111,13 +110,13 @@ export namespace E2eFileProgrammer { [ connection, ...route.parameters - .filter((p) => p.category !== "headers") + .filter((p) => p.kind !== "headers") .map((p) => ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(SdkImportWizard.typia(importer)), )("random"), - [getTypeName(project)(importer)(p)], + [SdkAliasCollection.name(p.type)], undefined, ), ), @@ -151,7 +150,7 @@ export namespace E2eFileProgrammer { "output", undefined, project.config.propagate !== true && - route.output.typeName === "void" + route.success.type.name === "void" ? undefined : SdkAliasCollection.output(project)(importer)(route), ts.factory.createAwaitExpression(caller), @@ -168,11 +167,3 @@ export namespace E2eFileProgrammer { const getFunctionName = (route: ITypedHttpRoute): string => ["test", "api", ...route.accessors].join("_"); - -const getTypeName = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index 7bcea755b..a4b762ff1 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -2,25 +2,24 @@ import ts from "typescript"; import typia from "typia"; import { INestiaProject } from "../../structures/INestiaProject"; -import { IReflectHttpOperation } from "../../structures/IReflectHttpOperation"; +import { IReflectHttpOperationParameter } from "../../structures/IReflectHttpOperationParameter"; +import { IReflectType } from "../../structures/IReflectType"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { ImportDictionary } from "./ImportDictionary"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkAliasCollection { - export const name = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput): ts.TypeNode => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); + export const name = (type: IReflectType): ts.TypeNode => + ts.factory.createTypeReferenceNode( + type.name, + type.typeArguments ? type.typeArguments.map(name) : undefined, + ); export const headers = (project: INestiaProject) => (importer: ImportDictionary) => - (param: ITypedHttpRoute.IParameter): ts.TypeNode => { - const type: ts.TypeNode = name(project)(importer)(param); + (param: ITypedHttpRouteParameter.IHeaders): ts.TypeNode => { + const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ @@ -35,8 +34,8 @@ export namespace SdkAliasCollection { export const query = (project: INestiaProject) => (importer: ImportDictionary) => - (param: ITypedHttpRoute.IParameter): ts.TypeNode => { - const type: ts.TypeNode = name(project)(importer)(param); + (param: ITypedHttpRouteParameter.IQuery): ts.TypeNode => { + const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ @@ -51,8 +50,8 @@ export namespace SdkAliasCollection { export const input = (project: INestiaProject) => (importer: ImportDictionary) => - (param: ITypedHttpRoute.IParameter): ts.TypeNode => { - const type: ts.TypeNode = name(project)(importer)(param); + (param: ITypedHttpRouteParameter): ts.TypeNode => { + const type: ts.TypeNode = name(param); if (project.config.clone === true || project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( @@ -60,10 +59,11 @@ export namespace SdkAliasCollection { type: true, library: "@nestia/fetcher", instance: - typia.is(param) && - param.contentType === "multipart/form-data" - ? "Resolved" - : "Primitive", + typia.is(param) && + (param.contentType === "application/json" || + param.encrypted === true) + ? "Primitive" + : "Resolved", }), [type], ); @@ -74,7 +74,7 @@ export namespace SdkAliasCollection { (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { if (project.config.propagate !== true) { - const node: ts.TypeNode = name(project)(importer)(route.output); + const node: ts.TypeNode = name(route.success.type); const type = project.checker.getTypeAtLocation(node); const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0; @@ -92,9 +92,10 @@ export namespace SdkAliasCollection { type: true, library: "@nestia/fetcher", instance: - route.output.contentType === "application/x-www-form-urlencoded" - ? "Resolved" - : "Primitive", + route.success.contentType === "application/json" && + route.success.encrypted === true + ? "Primitive" + : "Resolved", }), [node], ); @@ -102,12 +103,14 @@ export namespace SdkAliasCollection { const branches: IBranch[] = [ { - status: String(route.status ?? (route.method === "POST" ? 201 : 200)), - type: name(project)(importer)(route.output), + status: String( + route.success.status ?? (route.method === "POST" ? 201 : 200), + ), + type: name(route.success.type), }, ...Object.entries(route.exceptions).map(([status, value]) => ({ status, - type: name(project)(importer)(value), + type: name(value.type), })), ]; return ts.factory.createTypeReferenceNode( @@ -127,10 +130,10 @@ export namespace SdkAliasCollection { ), ), ), - ...(route.status + ...(route.success.status ? [ ts.factory.createLiteralTypeNode( - ts.factory.createNumericLiteral(route.status), + ts.factory.createNumericLiteral(route.success.status), ), ] : []), diff --git a/packages/sdk/src/generates/internal/SdkDistributionComposer.ts b/packages/sdk/src/generates/internal/SdkDistributionComposer.ts index 0464d0f2c..4fbd49cc4 100644 --- a/packages/sdk/src/generates/internal/SdkDistributionComposer.ts +++ b/packages/sdk/src/generates/internal/SdkDistributionComposer.ts @@ -6,13 +6,16 @@ import typia from "typia"; import { INestiaConfig } from "../../INestiaConfig"; export namespace SdkDistributionComposer { - export const compose = async (config: INestiaConfig, websocket: boolean) => { - if (!fs.existsSync(config.distribute!)) - await fs.promises.mkdir(config.distribute!); + export const compose = async (props: { + config: INestiaConfig; + websocket: boolean; + }) => { + if (!fs.existsSync(props.config.distribute!)) + await fs.promises.mkdir(props.config.distribute!); const root: string = process.cwd(); - const output: string = path.resolve(config.output!); - process.chdir(config.distribute!); + const output: string = path.resolve(props.config.output!); + process.chdir(props.config.distribute!); const exit = () => process.chdir(root); if (await configured()) return exit(); @@ -31,7 +34,7 @@ export namespace SdkDistributionComposer { execute("npm install --save-dev rimraf"); execute(`npm install --save @nestia/fetcher@${v.version}`); execute(`npm install --save typia@${v.typia}`); - if (websocket) execute(`npm install --save tgrid@${v.tgrid}`); + if (props.websocket) execute(`npm install --save tgrid@${v.tgrid}`); execute("npx typia setup --manager npm"); exit(); diff --git a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts index 024743a76..c167f7971 100644 --- a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts @@ -2,6 +2,7 @@ import fs from "fs"; import ts from "typescript"; import { INestiaProject } from "../../structures/INestiaProject"; +import { ITypedApplication } from "../../structures/ITypedApplication"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute"; import { MapUtil } from "../../utils/MapUtil"; @@ -15,18 +16,14 @@ export namespace SdkFileProgrammer { /* --------------------------------------------------------- CONSTRUCTOR --------------------------------------------------------- */ - export const generate = - (project: INestiaProject) => - async ( - routeList: Array, - ): Promise => { - // CONSTRUCT FOLDER TREE - const root: SdkRouteDirectory = new SdkRouteDirectory(null, "functional"); - for (const route of routeList) emplace(root)(route); + export const generate = async (app: ITypedApplication): Promise => { + // CONSTRUCT FOLDER TREE + const root: SdkRouteDirectory = new SdkRouteDirectory(null, "functional"); + for (const route of app.routes) emplace(root)(route); - // ITERATE FILES - await iterate(project)(root)(project.config.output + "/functional"); - }; + // ITERATE FILES + await iterate(app.project)(root)(`${app.project.config.output}/functional`); + }; const emplace = (directory: SdkRouteDirectory) => @@ -80,16 +77,16 @@ export namespace SdkFileProgrammer { directory.routes.forEach((route, i) => { if (project.config.clone !== true || route.protocol === "websocket") for (const tuple of route.imports) - for (const instance of tuple[1]) + for (const instance of tuple.instances) importer.internal({ - file: tuple[0], + file: tuple.file, instance, type: true, }); statements.push( ...(route.protocol === "http" ? SdkHttpRouteProgrammer.write(project)(importer)(route) - : SdkWebSocketRouteProgrammer.write(project)(importer)(route)), + : SdkWebSocketRouteProgrammer.write(importer)(route)), ); if (i !== directory.routes.length - 1) statements.push(FilePrinter.enter()); diff --git a/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts index a451d7d08..f749270bb 100644 --- a/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts @@ -1,154 +1,154 @@ -import { IPointer } from "tstl"; -import ts from "typescript"; -import { IJsDocTagInfo } from "typia"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; -import { MetadataAlias } from "typia/lib/schemas/metadata/MetadataAlias"; -import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic"; -import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; +// import { IPointer } from "tstl"; +// import ts from "typescript"; +// import { IJsDocTagInfo } from "typia"; +// import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; +// import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +// import { MetadataAlias } from "typia/lib/schemas/metadata/MetadataAlias"; +// import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic"; +// import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; -import { INestiaProject } from "../../structures/INestiaProject"; -import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; -import { MapUtil } from "../../utils/MapUtil"; -import { FilePrinter } from "./FilePrinter"; -import { ImportDictionary } from "./ImportDictionary"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; +// import { INestiaProject } from "../../structures/INestiaProject"; +// import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +// import { MapUtil } from "../../utils/MapUtil"; +// import { FilePrinter } from "./FilePrinter"; +// import { ImportDictionary } from "./ImportDictionary"; +// import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; -export namespace SdkHttpCloneProgrammer { - export interface IModule { - name: string; - children: Map; - programmer: - | null - | ((importer: ImportDictionary) => ts.TypeAliasDeclaration); - } +// export namespace SdkHttpCloneProgrammer { +// export interface IModule { +// name: string; +// children: Map; +// programmer: +// | null +// | ((importer: ImportDictionary) => ts.TypeAliasDeclaration); +// } - export const write = - (project: INestiaProject) => - (routes: ITypedHttpRoute[]): Map => { - const collection = new MetadataCollection({ - replace: MetadataCollection.replace, - }); - for (const r of routes) { - for (const p of r.parameters) { - const res = MetadataFactory.analyze(project.checker)({ - escape: false, - constant: true, - absorb: false, - })(collection)(p.type); - if (res.success) p.metadata = res.data; - } - for (const e of Object.values(r.exceptions)) { - const res = MetadataFactory.analyze(project.checker)({ - escape: true, - constant: true, - absorb: false, - })(collection)(e.type); - if (res.success) e.metadata = res.data; - } - const res = MetadataFactory.analyze(project.checker)({ - escape: true, - constant: true, - absorb: false, - })(collection)(r.output.type); - if (res.success) r.output.metadata = res.data; - } +// export const write = +// (project: INestiaProject) => +// (routes: ITypedHttpRoute[]): Map => { +// const collection = new MetadataCollection({ +// replace: MetadataCollection.replace, +// }); +// for (const r of routes) { +// for (const p of r.parameters) { +// const res = MetadataFactory.analyze(project.checker)({ +// escape: false, +// constant: true, +// absorb: false, +// })(collection)(p.type); +// if (res.success) p.metadata = res.data; +// } +// for (const e of Object.values(r.exceptions)) { +// const res = MetadataFactory.analyze(project.checker)({ +// escape: true, +// constant: true, +// absorb: false, +// })(collection)(e.type); +// if (res.success) e.metadata = res.data; +// } +// const res = MetadataFactory.analyze(project.checker)({ +// escape: true, +// constant: true, +// absorb: false, +// })(collection)(r.output.type); +// if (res.success) r.success.metadata = res.data; +// } - const dict: Map = new Map(); - for (const alias of collection.aliases()) - if (isNamedDeclaration(alias.name)) - prepare(dict)(alias.name)((importer) => - write_alias(project)(importer)(alias), - ); - for (const object of collection.objects()) - if (isNamedDeclaration(object.name)) - prepare(dict)(object.name)((importer) => - write_object(project)(importer)(object), - ); - return dict; - }; +// const dict: Map = new Map(); +// for (const alias of collection.aliases()) +// if (isNamedDeclaration(alias.name)) +// prepare(dict)(alias.name)((importer) => +// write_alias(project)(importer)(alias), +// ); +// for (const object of collection.objects()) +// if (isNamedDeclaration(object.name)) +// prepare(dict)(object.name)((importer) => +// write_object(project)(importer)(object), +// ); +// return dict; +// }; - const prepare = - (dict: Map) => - (name: string) => - (programmer: (importer: ImportDictionary) => ts.TypeAliasDeclaration) => { - const accessors: string[] = name.split("."); - const modulo: IPointer = { value: null! }; +// const prepare = +// (dict: Map) => +// (name: string) => +// (programmer: (importer: ImportDictionary) => ts.TypeAliasDeclaration) => { +// const accessors: string[] = name.split("."); +// const modulo: IPointer = { value: null! }; - accessors.forEach((acc, i) => { - modulo.value = MapUtil.take(dict, acc, () => ({ - name: acc, - children: new Map(), - programmer: null, - })); - if (i === accessors.length - 1) modulo.value.programmer = programmer; - dict = modulo.value.children; - }); - return modulo!; - }; +// accessors.forEach((acc, i) => { +// modulo.value = MapUtil.take(dict, acc, () => ({ +// name: acc, +// children: new Map(), +// programmer: null, +// })); +// if (i === accessors.length - 1) modulo.value.programmer = programmer; +// dict = modulo.value.children; +// }); +// return modulo!; +// }; - const write_alias = - (project: INestiaProject) => - (importer: ImportDictionary) => - (alias: MetadataAlias): ts.TypeAliasDeclaration => - FilePrinter.description( - ts.factory.createTypeAliasDeclaration( - [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], - alias.name.split(".").at(-1)!, - [], - SdkTypeProgrammer.write(project)(importer)(alias.value), - ), - writeComment([])(alias.description, alias.jsDocTags), - ); +// const write_alias = +// (project: INestiaProject) => +// (importer: ImportDictionary) => +// (alias: MetadataAlias): ts.TypeAliasDeclaration => +// FilePrinter.description( +// ts.factory.createTypeAliasDeclaration( +// [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], +// alias.name.split(".").at(-1)!, +// [], +// SdkTypeProgrammer.write(project)(importer)(alias.value), +// ), +// writeComment([])(alias.description, alias.jsDocTags), +// ); - const write_object = - (project: INestiaProject) => - (importer: ImportDictionary) => - (object: MetadataObject): ts.TypeAliasDeclaration => { - return FilePrinter.description( - ts.factory.createTypeAliasDeclaration( - [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], - object.name.split(".").at(-1)!, - [], - SdkTypeProgrammer.write_object(project)(importer)(object), - ), - writeComment([])(object.description ?? null, object.jsDocTags), - ); - }; -} +// const write_object = +// (project: INestiaProject) => +// (importer: ImportDictionary) => +// (object: MetadataObject): ts.TypeAliasDeclaration => { +// return FilePrinter.description( +// ts.factory.createTypeAliasDeclaration( +// [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], +// object.name.split(".").at(-1)!, +// [], +// SdkTypeProgrammer.write_object(project)(importer)(object), +// ), +// writeComment([])(object.description ?? null, object.jsDocTags), +// ); +// }; +// } -const isNamedDeclaration = (name: string) => - name !== "object" && - name !== "__type" && - !name.startsWith("__type.") && - name !== "__object" && - !name.startsWith("__object."); +// const isNamedDeclaration = (name: string) => +// name !== "object" && +// name !== "__type" && +// !name.startsWith("__type.") && +// name !== "__object" && +// !name.startsWith("__object."); -const writeComment = - (atomics: MetadataAtomic[]) => - (description: string | null, jsDocTags: IJsDocTagInfo[]): string => { - const lines: string[] = []; - if (description?.length) - lines.push(...description.split("\n").map((s) => `${s}`)); +// const writeComment = +// (atomics: MetadataAtomic[]) => +// (description: string | null, jsDocTags: IJsDocTagInfo[]): string => { +// const lines: string[] = []; +// if (description?.length) +// lines.push(...description.split("\n").map((s) => `${s}`)); - const filtered: IJsDocTagInfo[] = - !!atomics.length && !!jsDocTags?.length - ? jsDocTags.filter( - (tag) => - !atomics.some((a) => - a.tags.some((r) => r.some((t) => t.kind === tag.name)), - ), - ) - : jsDocTags ?? []; +// const filtered: IJsDocTagInfo[] = +// !!atomics.length && !!jsDocTags?.length +// ? jsDocTags.filter( +// (tag) => +// !atomics.some((a) => +// a.tags.some((r) => r.some((t) => t.kind === tag.name)), +// ), +// ) +// : jsDocTags ?? []; - if (description?.length && filtered.length) lines.push(""); - if (filtered.length) - lines.push( - ...filtered.map((t) => - t.text?.length - ? `@${t.name} ${t.text.map((e) => e.text).join("")}` - : `@${t.name}`, - ), - ); - return lines.join("\n"); - }; +// if (description?.length && filtered.length) lines.push(""); +// if (filtered.length) +// lines.push( +// ...filtered.map((t) => +// t.text?.length +// ? `@${t.name} ${t.text.map((e) => e.text).join("")}` +// : `@${t.name}`, +// ), +// ); +// return lines.join("\n"); +// }; diff --git a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts index 0161a143e..5760fe096 100644 --- a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts @@ -1,15 +1,14 @@ import ts from "typescript"; -import typia from "typia"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; import { INestiaConfig } from "../../INestiaConfig"; import { INestiaProject } from "../../structures/INestiaProject"; -import { IReflectHttpOperation } from "../../structures/IReflectHttpOperation"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { StringUtil } from "../../utils/StringUtil"; import { ImportDictionary } from "./ImportDictionary"; +import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkHttpFunctionProgrammer { export const write = @@ -18,9 +17,9 @@ export namespace SdkHttpFunctionProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.FunctionDeclaration => ts.factory.createFunctionDeclaration( @@ -42,13 +41,13 @@ export namespace SdkHttpFunctionProgrammer { ), ), ...route.parameters - .filter((p) => p.category !== "headers") + .filter((p) => p.kind !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], undefined, p.name, - p.optional + p.metadata.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, project.config.primitive !== false && @@ -56,12 +55,12 @@ export namespace SdkHttpFunctionProgrammer { ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) - : getTypeName(project)(importer)(p), + : SdkAliasCollection.name(p.type), ), ), ], ts.factory.createTypeReferenceNode("Promise", [ - getReturnType(project.config)(route), + SdkAliasCollection.name(route.success.type), ]), ts.factory.createBlock( write_body(project.config)(importer)(route, props), @@ -75,34 +74,21 @@ export namespace SdkHttpFunctionProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.Statement[] => { - const encrypted: boolean = - route.encrypted === true || - (props.input !== undefined && - props.input.custom === true && - props.input.category === "body" && - props.input.encrypted === true); - const contentType: string | undefined = - props.input !== undefined - ? typia.is(props.input) - ? props.input.contentType - : "application/json" - : undefined; - const caller = () => ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier( - SdkImportWizard.Fetcher(encrypted)(importer), + SdkImportWizard.Fetcher(!!props.input?.encrypted)(importer), ), )(config.propagate ? "propagate" : "fetch"), undefined, [ - contentType && contentType !== "multipart/form-data" + props.input?.contentType !== "multipart/form-data" ? ts.factory.createObjectLiteralExpression( [ ts.factory.createSpreadAssignment( @@ -119,7 +105,9 @@ export namespace SdkHttpFunctionProgrammer { ), ts.factory.createPropertyAssignment( ts.factory.createStringLiteral("Content-Type"), - ts.factory.createStringLiteral(contentType), + ts.factory.createStringLiteral( + props.input?.contentType ?? "application/json", + ), ), ], true, @@ -152,9 +140,7 @@ export namespace SdkHttpFunctionProgrammer { )("path"), undefined, route.parameters - .filter( - (p) => p.category === "param" || p.category === "query", - ) + .filter((p) => p.kind === "param" || p.kind === "query") .map((p) => ts.factory.createIdentifier(p.name)), ), ), @@ -165,7 +151,7 @@ export namespace SdkHttpFunctionProgrammer { ? [ts.factory.createIdentifier(props.input.name)] : []), ...(config.json && - typia.is(props.input) && + props.input !== undefined && (props.input.contentType === "application/json" || props.input.encrypted === true) ? [ts.factory.createIdentifier(`${route.name}.stringify`)] @@ -183,7 +169,7 @@ export namespace SdkHttpFunctionProgrammer { [ ts.factory.createIdentifier("connection"), ...route.parameters - .filter((p) => p.category !== "headers") + .filter((p) => p.kind !== "headers") .map((p) => ts.factory.createIdentifier(p.name)), ], ), @@ -196,7 +182,7 @@ export namespace SdkHttpFunctionProgrammer { return [ ...(config.assert ? route.parameters - .filter((p) => p.category !== "headers") + .filter((p) => p.kind !== "headers") .map((p) => ts.factory.createExpressionStatement( ts.factory.createCallExpression( @@ -215,7 +201,7 @@ export namespace SdkHttpFunctionProgrammer { ), ) : []), - ...(route.setHeaders.length === 0 + ...(route.success.setHeaders.length === 0 ? [ts.factory.createReturnStatement(output(false))] : write_set_headers(config)(route)(output(true))), ]; @@ -240,7 +226,7 @@ export namespace SdkHttpFunctionProgrammer { ts.factory.createToken(ts.SyntaxKind.QuestionQuestionEqualsToken), ts.factory.createObjectLiteralExpression([]), ), - ...route.setHeaders.map((tuple) => + ...route.success.setHeaders.map((tuple) => tuple.type === "assigner" ? ts.factory.createCallExpression( ts.factory.createIdentifier("Object.assign"), @@ -267,7 +253,7 @@ export namespace SdkHttpFunctionProgrammer { ts.factory.createVariableDeclaration( output, undefined, - getReturnType(config)(route), + SdkAliasCollection.name(route.success.type), condition, ), ], @@ -289,18 +275,3 @@ export namespace SdkHttpFunctionProgrammer { ]; }; } - -const getTypeName = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); - -const getReturnType = (config: INestiaConfig) => (route: ITypedHttpRoute) => - ts.factory.createTypeReferenceNode( - config.propagate !== true && route.output.typeName === "void" - ? "void" - : `${route.name}.Output`, - ); diff --git a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts index b5b371023..c9738e052 100644 --- a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts @@ -1,5 +1,4 @@ import ts from "typescript"; -import typia from "typia"; import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; import { LiteralFactory } from "typia/lib/factories/LiteralFactory"; @@ -7,14 +6,13 @@ import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { Escaper } from "typia/lib/utils/Escaper"; import { INestiaProject } from "../../structures/INestiaProject"; -import { IReflectHttpOperation } from "../../structures/IReflectHttpOperation"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { FilePrinter } from "./FilePrinter"; import { ImportDictionary } from "./ImportDictionary"; import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkHttpSimulationProgrammer } from "./SdkHttpSimulationProgrammer"; import { SdkImportWizard } from "./SdkImportWizard"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkHttpNamespaceProgrammer { export const write = @@ -23,9 +21,9 @@ export namespace SdkHttpNamespaceProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.ModuleDeclaration => { const types = write_types(project)(importer)(route, props); @@ -37,7 +35,7 @@ export namespace SdkHttpNamespaceProgrammer { ...(types.length ? [FilePrinter.enter()] : []), write_metadata(importer)(route, props), FilePrinter.enter(), - write_path(project)(importer)(route, props), + write_path(route, props), ...(project.config.simulate ? [ SdkHttpSimulationProgrammer.random(project)(importer)(route), @@ -48,7 +46,7 @@ export namespace SdkHttpNamespaceProgrammer { ] : []), ...(project.config.json && - typia.is(props.input) && + props.input !== undefined && (props.input.contentType === "application/json" || props.input.encrypted === true) ? [write_stringify(project)(importer)] @@ -64,9 +62,9 @@ export namespace SdkHttpNamespaceProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.TypeAliasDeclaration[] => { const array: ts.TypeAliasDeclaration[] = []; @@ -94,8 +92,11 @@ export namespace SdkHttpNamespaceProgrammer { "Input", SdkAliasCollection.input(project)(importer)(props.input), ); - if (project.config.propagate === true || route.output.typeName !== "void") - declare("Output", SdkAliasCollection.output(project)(importer)(route)); + if ( + project.config.propagate === true || + route.success.type.name !== "void" + ) + declare("Output", SdkAliasCollection.name(route.success.type)); return array; }; @@ -104,9 +105,9 @@ export namespace SdkHttpNamespaceProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.VariableStatement => constant("METADATA")( @@ -125,9 +126,7 @@ export namespace SdkHttpNamespaceProgrammer { "request", props.input ? LiteralFactory.generate( - typia.is( - props.input, - ) + props.input !== undefined ? { type: props.input.contentType, encrypted: !!props.input.encrypted, @@ -143,18 +142,18 @@ export namespace SdkHttpNamespaceProgrammer { "response", route.method !== "HEAD" ? LiteralFactory.generate({ - type: route.output.contentType, - encrypted: !!route.encrypted, + type: route.success.contentType, + encrypted: !!route.success.encrypted, }) : ts.factory.createNull(), ), ts.factory.createPropertyAssignment( "status", - route.status !== undefined - ? ExpressionFactory.number(route.status) + route.success.status !== null + ? ExpressionFactory.number(route.success.status) : ts.factory.createNull(), ), - ...(route.output.contentType === + ...(route.success.contentType === "application/x-www-form-urlencoded" ? [ ts.factory.createPropertyAssignment( @@ -163,11 +162,7 @@ export namespace SdkHttpNamespaceProgrammer { ts.factory.createIdentifier( `${SdkImportWizard.typia(importer)}.http.createAssertQuery`, ), - [ - ts.factory.createTypeReferenceNode( - route.output.typeName, - ), - ], + [SdkAliasCollection.name(route.success.type)], undefined, ), ), @@ -182,278 +177,266 @@ export namespace SdkHttpNamespaceProgrammer { ), ); - const write_path = - (project: INestiaProject) => - (importer: ImportDictionary) => - ( - route: ITypedHttpRoute, - props: { - query: ITypedHttpRoute.IParameter | undefined; - }, - ): ts.VariableStatement => { - const g = { - total: [ - ...route.parameters.filter( - (param) => param.category === "param" || param.category === "query", - ), - ], - query: route.parameters.filter( - (param) => param.category === "query" && param.field !== undefined, + const write_path = ( + route: ITypedHttpRoute, + props: { + query: ITypedHttpRouteParameter.IQuery | undefined; + }, + ): ts.VariableStatement => { + const g = { + total: [ + ...route.parameters.filter( + (param) => param.kind === "param" || param.kind === "query", ), - path: route.parameters.filter((param) => param.category === "param"), - }; - const out = (body: ts.ConciseBody) => - constant("path")( - ts.factory.createArrowFunction( - [], - [], - g.total.map((p) => - IdentifierFactory.parameter( - p.name, - p === props.query - ? p.optional - ? ts.factory.createUnionTypeNode([ - ts.factory.createTypeReferenceNode( - `${route.name}.Query`, - ), - ts.factory.createTypeReferenceNode("undefined"), - ]) - : ts.factory.createTypeReferenceNode(`${route.name}.Query`) - : getType(project)(importer)(p), - ), + ], + query: route.parameters + .filter((param) => param.kind === "query") + .filter((param) => param.field !== undefined), + path: route.parameters.filter((param) => param.kind === "param"), + }; + const out = (body: ts.ConciseBody) => + constant("path")( + ts.factory.createArrowFunction( + [], + [], + g.total.map((p) => + IdentifierFactory.parameter( + p.name, + p === props.query + ? p.metadata.optional + ? ts.factory.createUnionTypeNode([ + ts.factory.createTypeReferenceNode(`${route.name}.Query`), + ts.factory.createTypeReferenceNode("undefined"), + ]) + : ts.factory.createTypeReferenceNode(`${route.name}.Query`) + : SdkAliasCollection.name(p.type), ), - undefined, - undefined, - body, ), - ); - if (g.total.length === 0) - return out(ts.factory.createStringLiteral(route.path)); + undefined, + undefined, + body, + ), + ); + if (g.total.length === 0) + return out(ts.factory.createStringLiteral(route.path)); - const template = () => { - const splitted: string[] = route.path.split(":"); - if (splitted.length === 1) - return ts.factory.createStringLiteral(route.path); - return ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(splitted[0]), - splitted.slice(1).map((s, i, arr) => { - const name: string = s.split("/")[0]; - return ts.factory.createTemplateSpan( - ts.factory.createCallExpression( - ts.factory.createIdentifier("encodeURIComponent"), - undefined, - [ - ts.factory.createBinaryExpression( - ts.factory.createIdentifier( - g.path.find((p) => p.field === name)!.name, - ), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createStringLiteral("null"), + const template = () => { + const splitted: string[] = route.path.split(":"); + if (splitted.length === 1) + return ts.factory.createStringLiteral(route.path); + return ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(splitted[0]), + splitted.slice(1).map((s, i, arr) => { + const name: string = s.split("/")[0]; + return ts.factory.createTemplateSpan( + ts.factory.createCallExpression( + ts.factory.createIdentifier("encodeURIComponent"), + undefined, + [ + ts.factory.createBinaryExpression( + ts.factory.createIdentifier( + g.path.find((p) => p.field === name)!.name, ), - ], - ), - (i !== arr.length - 1 - ? ts.factory.createTemplateMiddle - : ts.factory.createTemplateTail)(s.substring(name.length)), - ); - }), - ); - }; - if (props.query === undefined && g.query.length === 0) - return out(template()); + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createStringLiteral("null"), + ), + ], + ), + (i !== arr.length - 1 + ? ts.factory.createTemplateMiddle + : ts.factory.createTemplateTail)(s.substring(name.length)), + ); + }), + ); + }; + if (props.query === undefined && g.query.length === 0) + return out(template()); - const block = (expr: ts.Expression) => { - const computeName = (str: string): string => - g.total - .filter((p) => p.category !== "headers") - .find((p) => p.name === str) !== undefined - ? computeName("_" + str) - : str; - const variables: string = computeName("variables"); - return ts.factory.createBlock( - [ - local(variables)("URLSearchParams")( - ts.factory.createNewExpression( - ts.factory.createIdentifier("URLSearchParams"), - [], - [], - ), + const block = (expr: ts.Expression) => { + const computeName = (str: string): string => + g.total.find((p) => p.name === str) !== undefined + ? computeName("_" + str) + : str; + const variables: string = computeName("variables"); + return ts.factory.createBlock( + [ + local(variables)("URLSearchParams")( + ts.factory.createNewExpression( + ts.factory.createIdentifier("URLSearchParams"), + [], + [], ), - ts.factory.createForOfStatement( + ), + ts.factory.createForOfStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createArrayBindingPattern([ + ts.factory.createBindingElement( + undefined, + undefined, + ts.factory.createIdentifier("key"), + undefined, + ), + ts.factory.createBindingElement( + undefined, + undefined, + ts.factory.createIdentifier("value"), + undefined, + ), + ]), + undefined, + undefined, + undefined, + ), + ], + ts.NodeFlags.Const, + ), + ts.factory.createCallExpression( + ts.factory.createIdentifier("Object.entries"), undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createArrayBindingPattern([ - ts.factory.createBindingElement( - undefined, - undefined, - ts.factory.createIdentifier("key"), + [ts.factory.createAsExpression(expr, TypeFactory.keyword("any"))], + ), + ts.factory.createIfStatement( + ts.factory.createStrictEquality( + ts.factory.createIdentifier("undefined"), + ts.factory.createIdentifier("value"), + ), + ts.factory.createContinueStatement(), + ts.factory.createIfStatement( + ts.factory.createCallExpression( + ts.factory.createIdentifier("Array.isArray"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("value"), + ts.factory.createIdentifier("forEach"), + ), + undefined, + [ + ts.factory.createArrowFunction( undefined, - ), - ts.factory.createBindingElement( undefined, + [IdentifierFactory.parameter("elem")], undefined, - ts.factory.createIdentifier("value"), undefined, + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("append"), + undefined, + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("elem")], + ), + ], + ), ), - ]), - undefined, - undefined, - undefined, + ], ), - ], - ts.NodeFlags.Const, - ), - ts.factory.createCallExpression( - ts.factory.createIdentifier("Object.entries"), - undefined, - [ - ts.factory.createAsExpression( - expr, - TypeFactory.keyword("any"), - ), - ], - ), - ts.factory.createIfStatement( - ts.factory.createStrictEquality( - ts.factory.createIdentifier("undefined"), - ts.factory.createIdentifier("value"), ), - ts.factory.createContinueStatement(), - ts.factory.createIfStatement( + ts.factory.createExpressionStatement( ts.factory.createCallExpression( - ts.factory.createIdentifier("Array.isArray"), + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("set"), undefined, - [ts.factory.createIdentifier("value")], - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier("value"), - ts.factory.createIdentifier("forEach"), + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("value")], ), - undefined, - [ - ts.factory.createArrowFunction( - undefined, - undefined, - [IdentifierFactory.parameter("elem")], - undefined, - undefined, - ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("append"), - undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("elem")], - ), - ], - ), - ), - ], - ), + ], + ), + ), + ), + ), + ), + local("location")("string")(template()), + ts.factory.createReturnStatement( + ts.factory.createConditionalExpression( + ts.factory.createStrictEquality( + ExpressionFactory.number(0), + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("size"), + ), + undefined, + ts.factory.createIdentifier("location"), + undefined, + ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(""), + [ + ts.factory.createTemplateSpan( + ts.factory.createIdentifier("location"), + ts.factory.createTemplateMiddle("?"), ), - ts.factory.createExpressionStatement( + ts.factory.createTemplateSpan( ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(variables), - )("set"), + )("toString"), + undefined, undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("value")], - ), - ], ), + ts.factory.createTemplateTail(""), ), - ), - ), - ), - local("location")("string")(template()), - ts.factory.createReturnStatement( - ts.factory.createConditionalExpression( - ts.factory.createStrictEquality( - ExpressionFactory.number(0), - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("size"), - ), - undefined, - ts.factory.createIdentifier("location"), - undefined, - ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(""), - [ - ts.factory.createTemplateSpan( - ts.factory.createIdentifier("location"), - ts.factory.createTemplateMiddle("?"), - ), - ts.factory.createTemplateSpan( - ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("toString"), - undefined, - undefined, - ), - ts.factory.createTemplateTail(""), - ), - ], - ), + ], ), ), - ], - true, - ); - }; - if (props.query !== undefined && g.query.length === 0) - return out( - block( - props.query.optional - ? ts.factory.createBinaryExpression( - ts.factory.createIdentifier(props.query.name), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createObjectLiteralExpression([], false), - ) - : ts.factory.createIdentifier(props.query.name), ), - ); + ], + true, + ); + }; + if (props.query !== undefined && g.query.length === 0) return out( block( - ts.factory.createObjectLiteralExpression( - [ - ...(props.query - ? [ - ts.factory.createSpreadAssignment( - ts.factory.createIdentifier(props.query.name), - ), - ] - : []), - ...g.query.map((q) => - q.name === q.field - ? ts.factory.createShorthandPropertyAssignment(q.name) - : ts.factory.createPropertyAssignment( - Escaper.variable(q.field!) - ? q.field! - : ts.factory.createStringLiteral(q.field!), - ts.factory.createIdentifier(q.name), - ), - ), - ], - true, - ), + props.query.metadata.optional + ? ts.factory.createBinaryExpression( + ts.factory.createIdentifier(props.query.name), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false), + ) + : ts.factory.createIdentifier(props.query.name), ), ); - }; + return out( + block( + ts.factory.createObjectLiteralExpression( + [ + ...(props.query + ? [ + ts.factory.createSpreadAssignment( + ts.factory.createIdentifier(props.query.name), + ), + ] + : []), + ...g.query.map((q) => + q.name === q.field + ? ts.factory.createShorthandPropertyAssignment(q.name) + : ts.factory.createPropertyAssignment( + Escaper.variable(q.field!) + ? q.field! + : ts.factory.createStringLiteral(q.field!), + ts.factory.createIdentifier(q.name), + ), + ), + ], + true, + ), + ), + ); + }; const write_stringify = (project: INestiaProject) => @@ -513,10 +496,3 @@ const constant = (name: string) => (expression: ts.Expression) => ts.NodeFlags.Const, ), ); -const getType = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); diff --git a/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts index 623a40fbc..57ff4cb6b 100644 --- a/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts @@ -14,13 +14,13 @@ export namespace SdkHttpRouteProgrammer { (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.Statement[] => { const props = { - headers: route.parameters.find( - (p) => p.category === "headers" && p.field === undefined, - ), - query: route.parameters.find( - (p) => p.category === "query" && p.field === undefined, - ), - input: route.parameters.find((p) => p.category === "body"), + headers: route.parameters + .filter((p) => p.kind === "headers") + .find((p) => p.field === undefined), + query: route.parameters + .filter((p) => p.kind === "query") + .find((p) => p.field === undefined), + input: route.parameters.find((p) => p.kind === "body"), }; return [ FilePrinter.description( @@ -40,7 +40,7 @@ export namespace SdkHttpRouteProgrammer { // PARMAETERS for (const p of route.parameters) { - if (p.category === "headers") continue; + if (p.kind === "headers") continue; const description: string | undefined = p.description ?? @@ -109,7 +109,7 @@ export namespace SdkHttpRouteProgrammer { ...(descriptionComments.length && tagComments.length ? [""] : []), ...tagComments, ...(descriptionComments.length && tagComments.length ? [""] : []), - `@controller ${route.controller.name}.${route.name}`, + `@controller ${route.controller.class.name}.${route.name}`, `@path ${route.method} ${route.path}`, `@nestia Generated by Nestia - https://github.com/samchon/nestia`, ].join("\n"); diff --git a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts index d476ee2c8..481cd5ee6 100644 --- a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts @@ -7,10 +7,10 @@ import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { INestiaProject } from "../../structures/INestiaProject"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { ImportDictionary } from "./ImportDictionary"; import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkHttpSimulationProgrammer { export const random = @@ -62,13 +62,13 @@ export namespace SdkHttpSimulationProgrammer { ( route: ITypedHttpRoute, props: { - headers: ITypedHttpRoute.IParameter | undefined; - query: ITypedHttpRoute.IParameter | undefined; - input: ITypedHttpRoute.IParameter | undefined; + headers: ITypedHttpRouteParameter.IHeaders | undefined; + query: ITypedHttpRouteParameter.IQuery | undefined; + input: ITypedHttpRouteParameter.IBody | undefined; }, ): ts.VariableStatement => { const output: boolean = - project.config.propagate === true || route.output.typeName !== "void"; + project.config.propagate === true || route.success.type.name !== "void"; const caller = () => ts.factory.createCallExpression( ts.factory.createIdentifier("random"), @@ -105,7 +105,7 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createTypeReferenceNode( SdkImportWizard.IConnection(importer), route.parameters.some( - (p) => p.category === "headers" && p.field === undefined, + (p) => p.kind === "headers" && p.field === undefined, ) ? [ ts.factory.createTypeReferenceNode( @@ -116,13 +116,13 @@ export namespace SdkHttpSimulationProgrammer { ), ), ...route.parameters - .filter((p) => p.category !== "headers") + .filter((p) => p.kind !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], undefined, p.name, - p.optional + p.metadata.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, project.config.primitive !== false && @@ -130,7 +130,7 @@ export namespace SdkHttpSimulationProgrammer { ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) - : getTypeName(project)(importer)(p), + : SdkAliasCollection.name(p.type), ), ), ], @@ -150,14 +150,14 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createPropertyAssignment( "status", ExpressionFactory.number( - route.status ?? + route.success.status ?? (route.method === "POST" ? 201 : 200), ), ), ts.factory.createPropertyAssignment( "headers", LiteralFactory.generate({ - "Content-Type": route.output.contentType, + "Content-Type": route.success.contentType, }), ), ts.factory.createPropertyAssignment("data", caller()), @@ -177,9 +177,7 @@ export namespace SdkHttpSimulationProgrammer { (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.Statement[] => { - const parameters = route.parameters.filter( - (p) => p.category !== "headers", - ); + const parameters = route.parameters.filter((p) => p.kind !== "headers"); if (parameters.length === 0) return []; const typia = SdkImportWizard.typia(importer); @@ -213,16 +211,14 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createIdentifier("path"), undefined, route.parameters - .filter( - (p) => p.category === "param" || p.category === "query", - ) + .filter((p) => p.kind === "param" || p.kind === "query") .map((p) => ts.factory.createIdentifier(p.name)), ), ), ts.factory.createPropertyAssignment( "contentType", ts.factory.createIdentifier( - JSON.stringify(route.output.contentType), + JSON.stringify(route.success.contentType), ), ), ], @@ -237,8 +233,8 @@ export namespace SdkHttpSimulationProgrammer { (() => { const base = IdentifierFactory.access( ts.factory.createIdentifier("assert"), - )(p.category); - if (p.category !== "param") return base; + )(p.kind); + if (p.kind !== "param") return base; return ts.factory.createCallExpression(base, undefined, [ ts.factory.createStringLiteral(p.name), ]); @@ -256,11 +252,7 @@ export namespace SdkHttpSimulationProgrammer { "assert", ), undefined, - [ - ts.factory.createIdentifier( - p.category === "headers" ? "connection.headers" : p.name, - ), - ], + [ts.factory.createIdentifier(p.name)], ), ), ], @@ -353,11 +345,3 @@ const constant = (name: string) => (expression: ts.Expression) => ts.NodeFlags.Const, ), ); - -const getTypeName = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); diff --git a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts index 0480ee7ce..e93086f36 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts @@ -3,15 +3,14 @@ import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; -import { INestiaProject } from "../../structures/INestiaProject"; import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute"; +import { ITypedWebSocketRouteParameter } from "../../structures/ITypedWebSocketRouteParameter"; import { FilePrinter } from "./FilePrinter"; import { ImportDictionary } from "./ImportDictionary"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; +import { SdkAliasCollection } from "./SdkAliasCollection"; export namespace SdkWebSocketNamespaceProgrammer { export const write = - (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedWebSocketRoute): ts.ModuleDeclaration => ts.factory.createModuleDeclaration( @@ -20,7 +19,7 @@ export namespace SdkWebSocketNamespaceProgrammer { ts.factory.createModuleBlock([ ...writeTypes(importer)(route), FilePrinter.enter(), - writePath(project)(importer)(route), + writePath(route), ]), ts.NodeFlags.Namespace, ); @@ -75,264 +74,240 @@ export namespace SdkWebSocketNamespaceProgrammer { ]), ); - const acceptor: ITypedWebSocketRoute.IAcceptorParameter = - route.parameters.find( - (x) => x.category === "acceptor", - ) as ITypedWebSocketRoute.IAcceptorParameter; - const query = route.parameters.find((x) => x.category === "query") as - | ITypedWebSocketRoute.IQueryParameter - | undefined; - + const acceptor: ITypedWebSocketRouteParameter.IAcceptor = + route.parameters.find((x) => x.kind === "acceptor")!; + const query: ITypedWebSocketRouteParameter.IQuery | undefined = + route.parameters.find((x) => x.kind === "query"); declare( "Header", - ts.factory.createTypeReferenceNode( - ( - (route.parameters.find((x) => x.category === "header") as - | ITypedWebSocketRoute.IHeaderParameter - | undefined) ?? acceptor.header - ).typeName, + SdkAliasCollection.name( + (route.parameters.find((x) => x.kind === "header")?.type ?? + acceptor.type.typeArguments?.[0])!, ), ); declare( "Provider", - ts.factory.createTypeReferenceNode( - ( - (route.parameters.find((x) => x.category === "driver") as - | ITypedWebSocketRoute.IDriverParameter - | undefined) ?? acceptor.listener - ).typeName, - ), + SdkAliasCollection.name(acceptor.type.typeArguments?.[1]!), ); declare( "Listener", - ts.factory.createTypeReferenceNode(acceptor.provider.typeName), + SdkAliasCollection.name(acceptor.type.typeArguments?.[2]!), ); - if (query) - declare("Query", ts.factory.createTypeReferenceNode(query.typeName)); + if (query) declare("Query", SdkAliasCollection.name(query.type)); return output; }; - const writePath = - (project: INestiaProject) => - (importer: ImportDictionary) => - (route: ITypedWebSocketRoute): ts.VariableStatement => { - const pathParams: ITypedWebSocketRoute.IPathParameter[] = - route.parameters.filter( - (p) => p.category === "param", - ) as ITypedWebSocketRoute.IPathParameter[]; - const query: ITypedWebSocketRoute.IQueryParameter | undefined = - route.parameters.find((p) => p.category === "query") as - | ITypedWebSocketRoute.IQueryParameter - | undefined; - const total: Array< - | ITypedWebSocketRoute.IPathParameter - | ITypedWebSocketRoute.IQueryParameter - > = [...pathParams, ...(query ? [query] : [])]; - const out = (body: ts.ConciseBody) => - constant("path")( - ts.factory.createArrowFunction( - [], - [], - total.map((p) => - IdentifierFactory.parameter( - p.name, - p === query - ? ts.factory.createTypeReferenceNode(`${route.name}.Query`) - : getType(project)(importer)(p), - ), + const writePath = (route: ITypedWebSocketRoute): ts.VariableStatement => { + const pathParams: ITypedWebSocketRouteParameter.IParam[] = + route.parameters.filter( + (p) => p.kind === "param", + ) as ITypedWebSocketRouteParameter.IParam[]; + const query: ITypedWebSocketRouteParameter.IQuery | undefined = + route.parameters.find((p) => p.kind === "query"); + const total: Array< + | ITypedWebSocketRouteParameter.IParam + | ITypedWebSocketRouteParameter.IQuery + > = [...pathParams, ...(query ? [query] : [])]; + const out = (body: ts.ConciseBody) => + constant("path")( + ts.factory.createArrowFunction( + [], + [], + total.map((p) => + IdentifierFactory.parameter( + p.name, + p === query + ? ts.factory.createTypeReferenceNode(`${route.name}.Query`) + : SdkAliasCollection.name(p), ), - undefined, - undefined, - body, ), - ); - if (total.length === 0) - return out(ts.factory.createStringLiteral(route.path)); + undefined, + undefined, + body, + ), + ); + if (total.length === 0) + return out(ts.factory.createStringLiteral(route.path)); - const template = () => { - const splitted: string[] = route.path.split(":"); - if (splitted.length === 1) - return ts.factory.createStringLiteral(route.path); - return ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(splitted[0]), - splitted.slice(1).map((s, i, arr) => { - const name: string = s.split("/")[0]; - return ts.factory.createTemplateSpan( - ts.factory.createCallExpression( - ts.factory.createIdentifier("encodeURIComponent"), - undefined, - [ - ts.factory.createBinaryExpression( - ts.factory.createIdentifier( - pathParams.find((p) => p.field === name)!.name, - ), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createStringLiteral("null"), + const template = () => { + const splitted: string[] = route.path.split(":"); + if (splitted.length === 1) + return ts.factory.createStringLiteral(route.path); + return ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(splitted[0]), + splitted.slice(1).map((s, i, arr) => { + const name: string = s.split("/")[0]; + return ts.factory.createTemplateSpan( + ts.factory.createCallExpression( + ts.factory.createIdentifier("encodeURIComponent"), + undefined, + [ + ts.factory.createBinaryExpression( + ts.factory.createIdentifier( + pathParams.find((p) => p.field === name)!.name, ), - ], - ), - (i !== arr.length - 1 - ? ts.factory.createTemplateMiddle - : ts.factory.createTemplateTail)(s.substring(name.length)), - ); - }), - ); - }; - if (query === undefined) return out(template()); + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createStringLiteral("null"), + ), + ], + ), + (i !== arr.length - 1 + ? ts.factory.createTemplateMiddle + : ts.factory.createTemplateTail)(s.substring(name.length)), + ); + }), + ); + }; + if (query === undefined) return out(template()); - const block = (expr: ts.Expression) => { - const computeName = (str: string): string => - total.find((p) => p.name === str) !== undefined - ? computeName("_" + str) - : str; - const variables: string = computeName("variables"); - return ts.factory.createBlock( - [ - local(variables)("URLSearchParams")( - ts.factory.createNewExpression( - ts.factory.createIdentifier("URLSearchParams"), - [], - [], - ), + const block = (expr: ts.Expression) => { + const computeName = (str: string): string => + total.find((p) => p.name === str) !== undefined + ? computeName("_" + str) + : str; + const variables: string = computeName("variables"); + return ts.factory.createBlock( + [ + local(variables)("URLSearchParams")( + ts.factory.createNewExpression( + ts.factory.createIdentifier("URLSearchParams"), + [], + [], ), - ts.factory.createForOfStatement( + ), + ts.factory.createForOfStatement( + undefined, + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createArrayBindingPattern([ + ts.factory.createBindingElement( + undefined, + undefined, + ts.factory.createIdentifier("key"), + undefined, + ), + ts.factory.createBindingElement( + undefined, + undefined, + ts.factory.createIdentifier("value"), + undefined, + ), + ]), + undefined, + undefined, + undefined, + ), + ], + ts.NodeFlags.Const, + ), + ts.factory.createCallExpression( + ts.factory.createIdentifier("Object.entries"), undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createArrayBindingPattern([ - ts.factory.createBindingElement( - undefined, - undefined, - ts.factory.createIdentifier("key"), + [ts.factory.createAsExpression(expr, TypeFactory.keyword("any"))], + ), + ts.factory.createIfStatement( + ts.factory.createStrictEquality( + ts.factory.createIdentifier("undefined"), + ts.factory.createIdentifier("value"), + ), + ts.factory.createContinueStatement(), + ts.factory.createIfStatement( + ts.factory.createCallExpression( + ts.factory.createIdentifier("Array.isArray"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("value"), + ts.factory.createIdentifier("forEach"), + ), + undefined, + [ + ts.factory.createArrowFunction( undefined, - ), - ts.factory.createBindingElement( undefined, + [IdentifierFactory.parameter("elem")], undefined, - ts.factory.createIdentifier("value"), undefined, + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("append"), + undefined, + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("elem")], + ), + ], + ), ), - ]), - undefined, - undefined, - undefined, + ], ), - ], - ts.NodeFlags.Const, - ), - ts.factory.createCallExpression( - ts.factory.createIdentifier("Object.entries"), - undefined, - [ - ts.factory.createAsExpression( - expr, - TypeFactory.keyword("any"), - ), - ], - ), - ts.factory.createIfStatement( - ts.factory.createStrictEquality( - ts.factory.createIdentifier("undefined"), - ts.factory.createIdentifier("value"), ), - ts.factory.createContinueStatement(), - ts.factory.createIfStatement( + ts.factory.createExpressionStatement( ts.factory.createCallExpression( - ts.factory.createIdentifier("Array.isArray"), + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("set"), undefined, - [ts.factory.createIdentifier("value")], - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier("value"), - ts.factory.createIdentifier("forEach"), + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("value")], ), - undefined, - [ - ts.factory.createArrowFunction( - undefined, - undefined, - [IdentifierFactory.parameter("elem")], - undefined, - undefined, - ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("append"), - undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("elem")], - ), - ], - ), - ), - ], - ), + ], + ), + ), + ), + ), + ), + local("location")("string")(template()), + ts.factory.createReturnStatement( + ts.factory.createConditionalExpression( + ts.factory.createStrictEquality( + ExpressionFactory.number(0), + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("size"), + ), + undefined, + ts.factory.createIdentifier("location"), + undefined, + ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(""), + [ + ts.factory.createTemplateSpan( + ts.factory.createIdentifier("location"), + ts.factory.createTemplateMiddle("?"), ), - ts.factory.createExpressionStatement( + ts.factory.createTemplateSpan( ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(variables), - )("set"), + )("toString"), + undefined, undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("value")], - ), - ], ), + ts.factory.createTemplateTail(""), ), - ), - ), - ), - local("location")("string")(template()), - ts.factory.createReturnStatement( - ts.factory.createConditionalExpression( - ts.factory.createStrictEquality( - ExpressionFactory.number(0), - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("size"), - ), - undefined, - ts.factory.createIdentifier("location"), - undefined, - ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(""), - [ - ts.factory.createTemplateSpan( - ts.factory.createIdentifier("location"), - ts.factory.createTemplateMiddle("?"), - ), - ts.factory.createTemplateSpan( - ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("toString"), - undefined, - undefined, - ), - ts.factory.createTemplateTail(""), - ), - ], - ), + ], ), ), - ], - true, - ); - }; - return out(block(ts.factory.createIdentifier(query.name))); + ), + ], + true, + ); }; + return out(block(ts.factory.createIdentifier(query.name))); + }; } const local = (name: string) => (type: string) => (expression: ts.Expression) => @@ -365,14 +340,3 @@ const constant = (name: string) => (expression: ts.Expression) => ts.NodeFlags.Const, ), ); -const getType = - (project: INestiaProject) => - (importer: ImportDictionary) => - ( - p: - | ITypedWebSocketRoute.IPathParameter - | ITypedWebSocketRoute.IQueryParameter, - ) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); diff --git a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts index 92bde7dcf..98f7732de 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts @@ -2,24 +2,22 @@ import ts from "typescript"; import { IJsDocTagInfo } from "typia"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; -import { INestiaProject } from "../../structures/INestiaProject"; import { ITypedWebSocketRoute } from "../../structures/ITypedWebSocketRoute"; import { FilePrinter } from "./FilePrinter"; import { ImportDictionary } from "./ImportDictionary"; +import { SdkAliasCollection } from "./SdkAliasCollection"; import { SdkImportWizard } from "./SdkImportWizard"; -import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; import { SdkWebSocketNamespaceProgrammer } from "./SdkWebSocketNamespaceProgrammer"; export namespace SdkWebSocketRouteProgrammer { export const write = - (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedWebSocketRoute): ts.Statement[] => [ FilePrinter.description( - writeFunction(project)(importer)(route), + writeFunction(importer)(route), writeDescription(route), ), - SdkWebSocketNamespaceProgrammer.write(project)(importer)(route), + SdkWebSocketNamespaceProgrammer.write(importer)(route), ]; const writeDescription = (route: ITypedWebSocketRoute): string => { @@ -33,7 +31,7 @@ export namespace SdkWebSocketRouteProgrammer { (tag) => tag.name !== "param" || route.parameters - .filter((p) => p.category === "param" || p.category === "query") + .filter((p) => p.kind === "param" || p.kind === "query") .some((p) => p.name === tag.text?.[0]?.text), ); if (tags.length !== 0) { @@ -48,7 +46,7 @@ export namespace SdkWebSocketRouteProgrammer { // POSTFIX if (!!comments.length) comments.push(""); comments.push( - `@controller ${route.controller.name}.${route.name}`, + `@controller ${route.controller.class.name}.${route.name}`, `@path ${route.path}`, `@nestia Generated by Nestia - https://github.com/samchon/nestia`, ); @@ -56,7 +54,6 @@ export namespace SdkWebSocketRouteProgrammer { }; const writeFunction = - (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedWebSocketRoute): ts.FunctionDeclaration => ts.factory.createFunctionDeclaration( @@ -76,12 +73,12 @@ export namespace SdkWebSocketRouteProgrammer { ), ), ...route.parameters - .filter((p) => p.category === "param" || p.category === "query") + .filter((p) => p.kind === "param" || p.kind === "query") .map((p) => IdentifierFactory.parameter( p.name, - p.category === "param" - ? getPathParameterType(project)(importer)(p) + p.kind === "param" + ? SdkAliasCollection.name(p.type) : ts.factory.createTypeReferenceNode(`${route.name}.Query`), ), ), @@ -155,9 +152,7 @@ export namespace SdkWebSocketRouteProgrammer { ), [], route.parameters - .filter( - (p) => p.category === "param" || p.category === "query", - ) + .filter((p) => p.kind === "param" || p.kind === "query") .map((x) => ts.factory.createIdentifier(x.name)), ), ), @@ -268,10 +263,3 @@ const joinPath = (caller: ts.Expression) => ts.factory.createTemplateTail("", ""), ), ]); -const getPathParameterType = - (project: INestiaProject) => - (importer: ImportDictionary) => - (p: ITypedWebSocketRoute.IPathParameter) => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); diff --git a/packages/sdk/src/generates/internal/SwaggerDescriptionGenerator.ts b/packages/sdk/src/generates/internal/SwaggerDescriptionComposer.ts similarity index 56% rename from packages/sdk/src/generates/internal/SwaggerDescriptionGenerator.ts rename to packages/sdk/src/generates/internal/SwaggerDescriptionComposer.ts index 5d6a65ffc..77a754415 100644 --- a/packages/sdk/src/generates/internal/SwaggerDescriptionGenerator.ts +++ b/packages/sdk/src/generates/internal/SwaggerDescriptionComposer.ts @@ -1,8 +1,8 @@ import { IJsDocTagInfo } from "typia"; -export namespace SwaggerDescriptionGenerator { - export const generate = (props: { - description?: string; +export namespace SwaggerDescriptionComposer { + export const compose = (props: { + description: string | null; jsDocTags: IJsDocTagInfo[]; kind: Kind; }): Kind extends "summary" @@ -14,7 +14,7 @@ export namespace SwaggerDescriptionGenerator { name: props.kind, }); if (explicit?.length) return explicit; - else if (props.description === undefined) return undefined; + else if (!props.description?.length) return undefined; const index: number = props.description.indexOf("\n"); const top: string = ( @@ -28,7 +28,27 @@ export namespace SwaggerDescriptionGenerator { } as any; }; - const getJsDocTexts = (props: { + export const descriptionFromJsDocTag = (props: { + jsDocTags: IJsDocTagInfo[]; + tag: string; + parameter?: string; + }): string | undefined => { + const parametric: (elem: IJsDocTagInfo) => boolean = props.parameter + ? (tag) => + tag.text!.find( + (elem) => + elem.kind === "parameterName" && elem.text === props.parameter, + ) !== undefined + : () => true; + const tag: IJsDocTagInfo | undefined = props.jsDocTags.find( + (tag) => tag.name === props.tag && tag.text && parametric(tag), + ); + return tag && tag.text + ? tag.text.find((elem) => elem.kind === "text")?.text + : undefined; + }; + + export const getJsDocTexts = (props: { jsDocTags: IJsDocTagInfo[]; name: string; }): string[] => diff --git a/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts new file mode 100644 index 000000000..d63d55c4a --- /dev/null +++ b/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts @@ -0,0 +1,90 @@ +import { OpenApi } from "@samchon/openapi"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { INestiaConfig } from "../../INestiaConfig"; +import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; +import { SwaggerDescriptionComposer } from "./SwaggerDescriptionComposer"; +import { SwaggerOperationParameterComposer } from "./SwaggerOperationParameterComposer"; +import { SwaggerOperationResponseComposer } from "./SwaggerOperationResponseComposer"; + +export namespace SwaggerOperationComposer { + export const compose = (props: { + config: INestiaConfig.ISwaggerConfig; + document: OpenApi.IDocument; + schema: (metadata: Metadata) => OpenApi.IJsonSchema | undefined; + route: ITypedHttpRoute; + }): OpenApi.IOperation => { + // FIND REQUEST BODY + const body: ITypedHttpRouteParameter.IBody | undefined = + props.route.parameters.find((param) => param.kind === "body"); + + // COMPOSE TAGS + const tags: Set = new Set([ + ...props.route.tags, + ...SwaggerDescriptionComposer.getJsDocTexts({ + jsDocTags: props.route.jsDocTags, + name: "tag", + }).map((t) => t.split(" ")[0]), + ]); + if (tags.size) { + props.document.tags ??= []; + for (const t of tags) + if (props.document.tags.find((elem) => elem.name === t) === undefined) + props.document.tags.push({ name: t }); + for (const texts of SwaggerDescriptionComposer.getJsDocTexts({ + jsDocTags: props.route.jsDocTags, + name: "tag", + })) { + const [name, ...description] = texts.split(" "); + if (description.length) + props.document.tags.find( + (elem) => elem.name === name, + )!.description ??= description.join(" "); + } + } + + // FINALIZE + return { + ...SwaggerDescriptionComposer.compose({ + description: props.route.description, + jsDocTags: props.route.jsDocTags, + kind: "summary", + }), + deprecated: props.route.jsDocTags.some((tag) => tag.name === "deprecated") + ? true + : undefined, + tags: Array.from(tags), + operationId: + props.route.operationId ?? + props.config.operationId?.({ + class: props.route.controller.class.name, + function: props.route.name, + method: props.route.method as "GET", + path: props.route.path, + }), + parameters: props.route.parameters + .map((p) => + SwaggerOperationParameterComposer.compose({ + config: props.config, + document: props.document, + schema: props.schema(p.metadata)!, + parameter: p, + jsDocTags: props.route.jsDocTags, + }), + ) + .flat(), + requestBody: body + ? SwaggerOperationParameterComposer.body({ + schema: props.schema(body.metadata)!, + jsDocTags: props.route.jsDocTags, + parameter: body, + }) + : undefined, + responses: SwaggerOperationResponseComposer.compose({ + schema: props.schema, + route: props.route, + }), + }; + }; +} diff --git a/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts new file mode 100644 index 000000000..1d5aaed9a --- /dev/null +++ b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts @@ -0,0 +1,173 @@ +import { OpenApi } from "@samchon/openapi"; +import { VariadicSingleton } from "tstl"; +import { IJsDocTagInfo, IJsonApplication } from "typia"; +import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer"; + +import { INestiaConfig } from "../../INestiaConfig"; +import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; +import { SwaggerDescriptionComposer } from "./SwaggerDescriptionComposer"; + +export namespace SwaggerOperationParameterComposer { + export interface IProps { + config: INestiaConfig.ISwaggerConfig; + document: OpenApi.IDocument; + schema: OpenApi.IJsonSchema; + jsDocTags: IJsDocTagInfo[]; + parameter: Parameter; + } + + export const compose = ( + props: IProps, + ): OpenApi.IOperation.IParameter[] => + props.parameter.kind === "body" + ? [] + : props.parameter.kind === "param" + ? [path({ ...props, parameter: props.parameter })] + : props.parameter.kind === "query" + ? query({ ...props, parameter: props.parameter }) + : header({ ...props, parameter: props.parameter }); + + export const body = ( + props: Omit, "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, "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", + }), + }); + + export const query = ( + props: IProps, + ): OpenApi.IOperation.IParameter[] => decomposible(props); + + export const header = ( + props: IProps, + ): 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.kind === "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", + }), + 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].properties + .filter((p) => + p.jsDocTags.every( + (tag) => tag.name !== "hidden" && tag.name !== "ignore", + ), + ) + .map((p) => { + const json: IJsonApplication = JsonApplicationProgrammer.write("3.1")([ + p.value, + ]) as IJsonApplication; + 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.kind === "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"); +}); diff --git a/packages/sdk/src/generates/internal/SwaggerOperationResponseComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationResponseComposer.ts new file mode 100644 index 000000000..d798ad24d --- /dev/null +++ b/packages/sdk/src/generates/internal/SwaggerOperationResponseComposer.ts @@ -0,0 +1,110 @@ +import { OpenApi } from "@samchon/openapi"; +import { VariadicSingleton } from "tstl"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { StringUtil } from "../../utils/StringUtil"; +import { SwaggerDescriptionComposer } from "./SwaggerDescriptionComposer"; + +export namespace SwaggerOperationResponseComposer { + export const compose = (props: { + schema: (metadata: Metadata) => OpenApi.IJsonSchema | undefined; + route: ITypedHttpRoute; + }): Record => { + const output: Record = {}; + // FROM DECORATOR + for (const [status, error] of Object.entries(props.route.exceptions)) + output[status] = { + description: error.description ?? undefined, + content: { + "application/json": { + schema: props.schema(error.metadata), + example: error.example, + examples: error.examples, + }, + }, + }; + + // FROM COMMENTS + for (const tag of props.route.jsDocTags) { + if (tag.name !== "throw" && tag.name !== "throws") continue; + const text: string | undefined = tag.text?.find( + (elem) => elem.kind === "text", + )?.text; + if (text === undefined) continue; + + const elements: string[] = text.split(" ").map((str) => str.trim()); + const status: string = elements[0]; + if ( + isNaN(Number(status)) && + status !== "2XX" && + status !== "3XX" && + status !== "4XX" && + status !== "5XX" + ) + continue; + + const description: string | undefined = + elements.length === 1 ? undefined : elements.slice(1).join(" "); + const oldbie = output[status]; + if (description && oldbie !== undefined) + oldbie.description ??= description; + else if (oldbie === undefined) + output[status] = { + description, + content: { + "application/json": { + schema: {}, + }, + }, + }; + } + + // SUCESS + const description: string | undefined = + SwaggerDescriptionComposer.descriptionFromJsDocTag({ + jsDocTags: props.route.jsDocTags, + tag: "returns", + }) ?? + SwaggerDescriptionComposer.descriptionFromJsDocTag({ + jsDocTags: props.route.jsDocTags, + tag: "return", + }); + output[ + props.route.success.status ?? + (props.route.method.toLowerCase() === "post" ? 201 : 200) + ] = { + description: props.route.success.encrypted + ? `${warning.get(!!description, props.route.method)}${description ?? ""}` + : description, + content: props.route.success.contentType + ? { + [props.route.success.contentType]: { + schema: props.schema(props.route.success.metadata), + example: props.route.success.example, + examples: props.route.success.examples, + }, + } + : undefined, + ...(props.route.success.encrypted ? { "x-nestia-encrypted": true } : {}), + }; + return output; + }; +} + +const warning = new VariadicSingleton((described: boolean, method: string) => { + const summary: string = "Response data have been encrypted."; + const component: string = `[EncryptedRoute.${StringUtil.capitalize(method)}](https://github.com/samchon/@nestia/core#encryptedroute)`; + + const content: string[] = [ + "## Warning", + "", + summary, + "", + `The response 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"); +}); diff --git a/packages/sdk/src/generates/internal/SwaggerSchemaGenerator.ts b/packages/sdk/src/generates/internal/SwaggerSchemaGenerator.ts deleted file mode 100644 index 8e15751bc..000000000 --- a/packages/sdk/src/generates/internal/SwaggerSchemaGenerator.ts +++ /dev/null @@ -1,473 +0,0 @@ -import { OpenApi } from "@samchon/openapi"; -import { Singleton, VariadicSingleton } from "tstl"; -import ts from "typescript"; -import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; -import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; -import { ValidationPipe } from "typia/lib/typings/ValidationPipe"; - -import { INestiaConfig } from "../../INestiaConfig"; -import { ISwaggerError } from "../../structures/ISwaggerError"; -import { ISwaggerLazyProperty } from "../../structures/ISwaggerLazyProperty"; -import { ISwaggerLazySchema } from "../../structures/ISwaggerLazySchema"; -import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; -import { SwaggerDescriptionGenerator } from "./SwaggerDescriptionGenerator"; -import { SwaggerSchemaValidator } from "./SwaggerSchemaValidator"; - -export namespace SwaggerSchemaGenerator { - export interface IProps { - config: INestiaConfig.ISwaggerConfig; - checker: ts.TypeChecker; - collection: MetadataCollection; - lazySchemas: Array; - lazyProperties: Array; - errors: ISwaggerError[]; - } - - export const response = - (props: IProps) => - (route: ITypedHttpRoute): Record => { - const output: Record = {}; - - //---- - // EXCEPTION STATUSES - //---- - // FROM DECORATOR - for (const [status, exp] of Object.entries(route.exceptions)) { - const result = MetadataFactory.analyze(props.checker)({ - escape: true, - constant: true, - absorb: false, - validate: JsonApplicationProgrammer.validate, - })(props.collection)(exp.type); - if (result.success === false) - props.errors.push( - ...result.errors.map((e) => ({ - ...e, - route, - from: `response(status: ${status})`, - })), - ); - - output[status] = { - description: exp.description ?? "", - content: { - "application/json": { - schema: coalesce(props)(result), - }, - }, - }; - } - - // FROM COMMENT TAGS -> ANY - for (const tag of route.jsDocTags) { - if (tag.name !== "throw" && tag.name !== "throws") continue; - - const text: string | undefined = tag.text?.find( - (elem) => elem.kind === "text", - )?.text; - if (text === undefined) continue; - - const elements: string[] = text.split(" ").map((str) => str.trim()); - const status: string = elements[0]; - if ( - isNaN(Number(status)) && - status !== "2XX" && - status !== "3XX" && - status !== "4XX" && - status !== "5XX" - ) - continue; - - const description: string | undefined = - elements.length === 1 ? undefined : elements.slice(1).join(" "); - const oldbie = output[status]; - if (description && oldbie !== undefined) - oldbie.description = description; - else if (oldbie === undefined) - output[status] = { - description: description ?? "", - content: { - "application/json": { - schema: {}, - }, - }, - }; - } - - //---- - // SUCCESS - //---- - // STATUS - const status: string = - route.status !== undefined - ? String(route.status) - : route.method === "POST" - ? "201" - : "200"; - - // SCHEMA - const result = MetadataFactory.analyze(props.checker)({ - escape: true, - constant: true, - absorb: false, - validate: (meta) => { - const bigint: boolean = - meta.atomics.some((a) => a.type === "bigint") || - meta.constants.some((a) => a.type === "bigint"); - return bigint ? ["bigint type is not allowed."] : []; - }, - })(props.collection)(route.output.type); - if (result.success === false) - props.errors.push( - ...result.errors.map((e) => ({ - ...e, - route, - from: "response", - })), - ); - - // DO ASSIGN - const description = - describe(route, "return") ?? describe(route, "returns"); - output[status] = { - description: route.encrypted - ? `${warning.get(!!description, "response", route.method)}${ - description ?? "" - }` - : description ?? "", - content: - route.output.typeName === "void" - ? undefined - : { - [route.output.contentType]: { - schema: coalesce(props)(result), - ...{ - example: route.example, - examples: route.examples, - }, - }, - }, - ...(props.config.additional === true - ? { - "x-nestia-encrypted": route.encrypted, - } - : route.encrypted === true - ? { - "x-nestia-encrypted": true, - } - : {}), - }; - return output; - }; - - export const body = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter): OpenApi.IOperation.IRequestBody => { - // ANALZE TYPE WITH VALIDATION - const result = MetadataFactory.analyze(props.checker)({ - escape: true, - constant: true, - absorb: true, - validate: (meta) => { - const bigint: boolean = - meta.atomics.some((a) => a.type === "bigint") || - meta.constants.some((a) => a.type === "bigint"); - return bigint ? ["bigint type is not allowed."] : []; - }, - })(props.collection)(param.type); - if (result.success === false) - props.errors.push( - ...result.errors.map((e) => ({ - ...e, - route, - from: param.name, - })), - ); - - // LIST UP PROPERTIES - const contentType = - param.custom && param.category === "body" - ? param.contentType - : "application/json"; - const encrypted: boolean = - param.custom && param.category === "body" && param.encrypted; - const description: string | undefined = describe( - route, - "param", - param.name, - ); - - // RETURNS WITH LAZY CONSTRUCTION - const schema: OpenApi.IJsonSchema = coalesce(props)(result); - return { - description: encrypted - ? `${warning.get(!!description, "request")}${description ?? ""}` - : description, - content: { - [contentType]: { - schema, - ...{ - example: param.example, - examples: param.examples, - }, - }, - }, - required: true, - ...(props.config.additional === true - ? { - "x-nestia-encrypted": encrypted, - } - : encrypted === true - ? { - "x-nestia-encrypted": true, - } - : {}), - }; - }; - - export const parameter = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter): OpenApi.IOperation.IParameter[] => - param.category === "headers" - ? headers(props)(route)(param) - : param.category === "param" - ? [path(props)(route)(param)] - : query(props)(route)(param); - - const path = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter): OpenApi.IOperation.IParameter => { - // ANALZE TYPE WITH VALIDATION - const result = MetadataFactory.analyze(props.checker)({ - escape: false, - constant: true, - absorb: true, - validate: SwaggerSchemaValidator.path, - })(props.collection)(param.type); - if (result.success === false) - props.errors.push( - ...result.errors.map((e) => ({ - ...e, - route, - from: param.name, - })), - ); - - // RETURNS WITH LAZY CONSTRUCTION - return lazy(props)(route)(param, result); - }; - - const headers = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter): OpenApi.IOperation.IParameter[] => - decomposible(props)(route)(param)( - MetadataFactory.analyze(props.checker)({ - escape: false, - constant: true, - absorb: true, - validate: param.custom ? SwaggerSchemaValidator.headers : undefined, - })(props.collection)(param.type), - ); - - const query = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter): OpenApi.IOperation.IParameter[] => - decomposible(props)(route)(param)( - MetadataFactory.analyze(props.checker)({ - escape: false, - constant: true, - absorb: true, - validate: param.custom ? SwaggerSchemaValidator.query : undefined, - })(props.collection)(param.type), - ); - - const decomposible = - (props: IProps) => - (route: ITypedHttpRoute) => - (param: ITypedHttpRoute.IParameter) => - ( - result: ValidationPipe, - ): OpenApi.IOperation.IParameter[] => { - const decoded: OpenApi.IOperation.IParameter = lazy(props)(route)( - param, - result, - ); - if (result.success === false) { - props.errors.push( - ...result.errors.map((e) => ({ - ...e, - route, - from: param.name, - })), - ); - return [decoded]; - } else if ( - props.config.decompose === false || - result.data.objects.length === 0 - ) - return [decoded]; - - return result.data.objects[0].properties - .filter((p) => p.jsDocTags.every((tag) => tag.name !== "hidden")) - .map((p) => { - const schema: OpenApi.IJsonSchema = {}; - props.lazyProperties.push({ - schema, - object: result.data.objects[0].name, - property: p.key.constants[0].values[0].value as string, - }); - return { - name: p.key.constants[0].values[0].value as string, - in: - param.category === "headers" - ? "header" - : (param.category as "path"), - schema, - required: p.value.isRequired(), - ...SwaggerDescriptionGenerator.generate({ - description: p.description ?? undefined, - jsDocTags: p.jsDocTags, - kind: "title", - }), - } satisfies OpenApi.IOperation.IParameter; - }); - }; - - const lazy = - (props: IProps) => - (route: ITypedHttpRoute) => - ( - p: ITypedHttpRoute.IParameter, - result: ValidationPipe, - ): OpenApi.IOperation.IParameter => { - const schema: OpenApi.IJsonSchema = coalesce(props)(result); - return { - name: p.field ?? p.name, - in: - p.category === "headers" - ? "header" - : p.category === "param" - ? "path" - : "query", - schema, - required: result.success ? result.data.isRequired() : true, - ...SwaggerDescriptionGenerator.generate({ - description: - p.description ?? - p.jsDocTags.find((tag) => tag.name === "description")?.text?.[0] - .text ?? - route.jsDocTags - .find( - (tag) => tag.name === "param" && tag.text?.[0].text === p.name, - ) - ?.text?.map((e) => e.text) - .join("") - .substring(p.name.length), - jsDocTags: p.jsDocTags, - kind: "title", - }), - ...{ - example: p.example, - examples: p.examples, - }, - }; - }; - - const coalesce = - (props: IProps) => - ( - result: ValidationPipe, - ): OpenApi.IJsonSchema => { - const schema: OpenApi.IJsonSchema = {} as any; - props.lazySchemas.push({ - metadata: result.success ? result.data : any.get(), - schema, - }); - return schema; - }; - - const describe = ( - route: ITypedHttpRoute, - tagName: string, - parameterName?: string, - ): string | undefined => { - const parametric: (elem: ts.JSDocTagInfo) => boolean = parameterName - ? (tag) => - tag.text!.find( - (elem) => - elem.kind === "parameterName" && elem.text === parameterName, - ) !== undefined - : () => true; - - const tag: ts.JSDocTagInfo | undefined = route.jsDocTags.find( - (tag) => tag.name === tagName && tag.text && parametric(tag), - ); - return tag && tag.text - ? tag.text.find((elem) => elem.kind === "text")?.text - : undefined; - }; -} - -const warning = new VariadicSingleton( - (described: boolean, type: "request" | "response", method?: string) => { - const summary = - type === "request" - ? "Request body must be encrypted." - : "Response data have been encrypted."; - const component = - type === "request" - ? "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)" - : `[EncryptedRoute.${method![0].toUpperCase()}.${method! - .substring(1) - .toLowerCase()}](https://github.com/samchon/@nestia/core#encryptedroute)`; - - const content: string[] = [ - "## Warning", - "", - summary, - "", - `The ${type} 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"); - }, -); - -const any = new Singleton(() => - Metadata.from( - { - any: true, - required: true, - optional: false, - nullable: false, - functional: false, - atomics: [], - constants: [], - templates: [], - escaped: null, - rest: null, - arrays: [], - tuples: [], - objects: [], - aliases: [], - natives: [], - sets: [], - maps: [], - }, - { - aliases: new Map(), - arrays: new Map(), - tuples: new Map(), - objects: new Map(), - }, - ), -); diff --git a/packages/sdk/src/generates/internal/SwaggerSchemaValidator.ts b/packages/sdk/src/generates/internal/SwaggerSchemaValidator.ts deleted file mode 100644 index 42348fc43..000000000 --- a/packages/sdk/src/generates/internal/SwaggerSchemaValidator.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; -import { MetadataArray } from "typia/lib/schemas/metadata/MetadataArray"; - -export namespace SwaggerSchemaValidator { - export const path = (meta: Metadata): string[] => { - const errors: string[] = []; - const insert = (msg: string) => errors.push(msg); - - if (meta.any) insert("do not allow any type"); - if (meta.isRequired() === false) insert("do not allow undefindable type"); - - const atomics = CoreMetadataUtil.atomics(meta); - const expected: number = - meta.atomics.length + - meta.templates.length + - meta.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0); - if (meta.size() !== expected || atomics.size === 0) - insert("only atomic or constant types are allowed"); - if (atomics.size > 1) insert("do not allow union type"); - - return errors; - }; - - export const query = ( - meta: Metadata, - explore: MetadataFactory.IExplore, - ): string[] => { - const errors: string[] = []; - const insert = (msg: string) => errors.push(msg); - - if (explore.top === true) { - // TOP MUST BE ONLY OBJECT - if (meta.objects.length !== 1 || meta.bucket() !== 1) - insert("only one object type is allowed."); - if (meta.nullable === true) insert("query parameters cannot be null."); - if (meta.isRequired() === false) { - const everyPropertiesAreOptional: boolean = - meta.size() === 1 && - meta.objects.length === 1 && - meta.objects[0].properties.every((p) => p.value.optional); - if (everyPropertiesAreOptional === false) - insert( - "query parameters can be optional only when every properties are optional.", - ); - } - } else if ( - explore.nested !== null && - explore.nested instanceof MetadataArray - ) { - const atomics = CoreMetadataUtil.atomics(meta); - const expected: number = - meta.atomics.length + - meta.templates.length + - meta.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0); - if (atomics.size > 1) insert("union type is not allowed in array."); - if (meta.nullable) insert("nullable type is not allowed in array."); - if (meta.isRequired() === false) - insert("optional type is not allowed in array."); - if (meta.size() !== expected) - insert("only atomic or constant types are allowed in array."); - } else if (explore.object && explore.property !== null) { - //---- - // COMMON - //---- - // PROPERTY MUST BE SOLE - if (typeof explore.property === "object") - insert("dynamic property is not allowed."); - // DO NOT ALLOW TUPLE TYPE - if (meta.tuples.length) insert("tuple type is not allowed."); - // DO NOT ALLOW UNION TYPE - if (CoreMetadataUtil.isUnion(meta)) insert("union type is not allowed."); - // DO NOT ALLOW NESTED OBJECT - if ( - meta.objects.length || - meta.sets.length || - meta.maps.length || - meta.natives.length - ) - insert("nested object type is not allowed."); - - //---- - // ARRAY CASES - //---- - const isArray: boolean = meta.arrays.length > 1 || meta.tuples.length > 1; - // ARRAY TYPE MUST BE REQUIRED - if (isArray && meta.isRequired() === false) - insert("optional type is not allowed when array."); - // SET-COOKIE MUST BE ARRAY - if (explore.property === "set-cookie" && !isArray) - insert("set-cookie property must be array."); - } - return errors; - }; - - export const headers = ( - meta: Metadata, - explore: MetadataFactory.IExplore, - ): string[] => { - const errors: string[] = []; - const insert = (msg: string) => errors.push(msg); - - if (explore.top === true) { - // TOP MUST BE ONLY OBJECT - if (meta.objects.length !== 1 || meta.bucket() !== 1) - insert("only one object type is allowed."); - if (meta.nullable === true) insert("headers cannot be null."); - if (meta.isRequired() === false) insert("headers cannot be null."); - } else if ( - explore.nested !== null && - explore.nested instanceof MetadataArray - ) { - const atomics = CoreMetadataUtil.atomics(meta); - const expected: number = - meta.atomics.length + - meta.templates.length + - meta.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0); - if (atomics.size > 1) insert("union type is not allowed in array."); - if (meta.nullable) insert("nullable type is not allowed in array."); - if (meta.isRequired() === false) insert("optional type is not allowed."); - if (meta.size() !== expected) - insert("only atomic or constant types are allowed in array."); - } else if (explore.object && explore.property !== null) { - //---- - // COMMON - //---- - // PROPERTY MUST BE SOLE - if (typeof explore.property === "object") - insert("dynamic property is not allowed."); - // DO NOT ALLOW TUPLE TYPE - if (meta.tuples.length) insert("tuple type is not allowed."); - // DO NOT ALLOW UNION TYPE - if (CoreMetadataUtil.isUnion(meta)) insert("union type is not allowed."); - // DO NOT ALLOW NESTED OBJECT - if ( - meta.objects.length || - meta.sets.length || - meta.maps.length || - meta.natives.length - ) - insert("nested object type is not allowed."); - // DO NOT ALLOW NULLABLE - if (meta.nullable) insert("nullable type is not allowed."); - - //---- - // ARRAY CASES - //---- - const isArray: boolean = meta.arrays.length > 1; - // ARRAY TYPE MUST BE REQUIRED - if (isArray && meta.isRequired() === false) - insert("optional type is not allowed when array."); - // SET-COOKIE MUST BE ARRAY - if (explore.property === "set-cookie" && !isArray) - insert("set-cookie property must be array."); - // MUST BE SINGULAR CASE - if ( - typeof explore.property === "string" && - SINGULAR.has(explore.property) && - isArray - ) - insert("property cannot be array."); - } - return errors; - }; -} - -namespace CoreMetadataUtil { - export const atomics = ( - meta: Metadata, - ): Set<"boolean" | "bigint" | "number" | "string"> => - new Set([ - ...meta.atomics.map((a) => a.type), - ...meta.constants.map((c) => c.type), - ...(meta.templates.length ? (["string"] as const) : []), - ]); - - export const isUnion = (meta: Metadata): boolean => - atomics(meta).size + - meta.arrays.length + - meta.tuples.length + - meta.natives.length + - meta.maps.length + - meta.objects.length > - 1; -} - -const SINGULAR: Set = new Set([ - "age", - "authorization", - "content-length", - "content-type", - "etag", - "expires", - "from", - "host", - "if-modified-since", - "if-unmodified-since", - "last-modified", - "location", - "max-forwards", - "proxy-authorization", - "referer", - "retry-after", - "server", - "user-agent", -]); diff --git a/packages/sdk/src/structures/IReflectApplication.ts b/packages/sdk/src/structures/IReflectApplication.ts new file mode 100644 index 000000000..07d80d547 --- /dev/null +++ b/packages/sdk/src/structures/IReflectApplication.ts @@ -0,0 +1,8 @@ +import { INestApplication } from "@nestjs/common"; + +import { IReflectController } from "./IReflectController"; + +export interface IReflectApplication { + application: INestApplication; + controllers: IReflectController[]; +} diff --git a/packages/sdk/src/structures/IReflectHttpOperation.ts b/packages/sdk/src/structures/IReflectHttpOperation.ts index e7c20cce2..8d3b936cd 100644 --- a/packages/sdk/src/structures/IReflectHttpOperation.ts +++ b/packages/sdk/src/structures/IReflectHttpOperation.ts @@ -1,4 +1,5 @@ import { VERSION_NEUTRAL } from "@nestjs/common/interfaces"; +import { IJsDocTagInfo } from "typia"; import { IReflectHttpOperationException } from "./IReflectHttpOperationException"; import { IReflectHttpOperationParameter } from "./IReflectHttpOperationParameter"; @@ -21,4 +22,7 @@ export interface IReflectHttpOperation { security: Record[]; tags: string[]; imports: IReflectTypeImport[]; + operationId: string | undefined; + description: string | null; + jsDocTags: IJsDocTagInfo[]; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationException.ts b/packages/sdk/src/structures/IReflectHttpOperationException.ts index d6d3ccb08..de6addc35 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationException.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationException.ts @@ -13,7 +13,7 @@ export interface IReflectHttpOperationException { // REFLECTED PROPERTIES type: IReflectType; - schema: IMetadata; + metadata: IMetadata; components: IMetadataComponents; validate: MetadataFactory.Validator; } diff --git a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts index 44205e00b..2443e447c 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts @@ -1,3 +1,4 @@ +import { IJsDocTagInfo } from "typia"; import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; import { IMetadata } from "typia/lib/schemas/metadata/IMetadata"; import { IMetadataComponents } from "typia/lib/schemas/metadata/IMetadataComponents"; @@ -37,6 +38,8 @@ export namespace IReflectHttpOperationParameter { validate: MetadataFactory.Validator; example?: any; examples?: Record; + description: string | null; + jsDocTags: IJsDocTagInfo[]; } /** diff --git a/packages/sdk/src/structures/IReflectOperationError.ts b/packages/sdk/src/structures/IReflectOperationError.ts index ad3733869..16b447e92 100644 --- a/packages/sdk/src/structures/IReflectOperationError.ts +++ b/packages/sdk/src/structures/IReflectOperationError.ts @@ -1,3 +1,5 @@ +import { IComparable } from "tstl"; + import { IOperationMetadata } from "../transformers/IOperationMetadata"; export interface IReflectOperationError { @@ -7,3 +9,18 @@ export interface IReflectOperationError { from: string | null; contents: Array; } +export namespace IReflectOperationError { + export class Key implements Pick, "less"> { + public constructor(public readonly error: IReflectOperationError) {} + + public less(obj: Key): boolean { + if (this.error.file !== obj.error.file) + return this.error.file < obj.error.file; + else if (this.error.class !== obj.error.class) + return this.error.class < obj.error.class; + else if (this.error.function !== obj.error.function) + return (this.error.function ?? "") < (obj.error.function ?? ""); + return (this.error.from ?? "") < (obj.error.from ?? ""); + } + } +} diff --git a/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts index aff91ec35..6a460082b 100644 --- a/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts +++ b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts @@ -1,17 +1,20 @@ +import { IJsDocTagInfo } from "typia"; + import { IReflectType } from "./IReflectType"; +import { IReflectTypeImport } from "./IReflectTypeImport"; export type IReflectWebSocketOperationParameter = - | IReflectWebSocketOperationParameter.IAcceptorParameter - | IReflectWebSocketOperationParameter.IDriverParameter - | IReflectWebSocketOperationParameter.IHeaderParameter - | IReflectWebSocketOperationParameter.IParamParameter - | IReflectWebSocketOperationParameter.IQueryParameter; + | IReflectWebSocketOperationParameter.IAcceptor + | IReflectWebSocketOperationParameter.IDriver + | IReflectWebSocketOperationParameter.IHeader + | IReflectWebSocketOperationParameter.IParam + | IReflectWebSocketOperationParameter.IQuery; export namespace IReflectWebSocketOperationParameter { - export type IAcceptorParameter = IBase<"acceptor">; - export type IDriverParameter = IBase<"driver">; - export type IHeaderParameter = IBase<"header">; - export type IQueryParameter = IBase<"query">; - export interface IParamParameter extends IBase<"param"> { + export type IAcceptor = IBase<"acceptor">; + export type IDriver = IBase<"driver">; + export type IHeader = IBase<"header">; + export type IQuery = IBase<"query">; + export interface IParam extends IBase<"param"> { field: string; } interface IBase { @@ -19,6 +22,9 @@ export namespace IReflectWebSocketOperationParameter { name: string; index: number; type: IReflectType; + imports: IReflectTypeImport[]; + description: string | null; + jsDocTags: IJsDocTagInfo[]; } /** diff --git a/packages/sdk/src/structures/ISwaggerError.ts b/packages/sdk/src/structures/ISwaggerError.ts deleted file mode 100644 index f67f96dcf..000000000 --- a/packages/sdk/src/structures/ISwaggerError.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; - -import { ITypedHttpRoute } from "./ITypedHttpRoute"; - -export interface ISwaggerError extends MetadataFactory.IError { - route: ITypedHttpRoute; - from: string; -} diff --git a/packages/sdk/src/structures/ISwaggerLazyProperty.ts b/packages/sdk/src/structures/ISwaggerLazyProperty.ts deleted file mode 100644 index 8d12eafe5..000000000 --- a/packages/sdk/src/structures/ISwaggerLazyProperty.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { OpenApi } from "@samchon/openapi"; - -export interface ISwaggerLazyProperty { - schema: OpenApi.IJsonSchema; - object: string; - property: string; -} diff --git a/packages/sdk/src/structures/ISwaggerLazySchema.ts b/packages/sdk/src/structures/ISwaggerLazySchema.ts deleted file mode 100644 index e3793a39b..000000000 --- a/packages/sdk/src/structures/ISwaggerLazySchema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { OpenApi } from "@samchon/openapi"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; - -export interface ISwaggerLazySchema { - metadata: Metadata; - schema: OpenApi.IJsonSchema; -} diff --git a/packages/sdk/src/structures/ITypedApplication.ts b/packages/sdk/src/structures/ITypedApplication.ts new file mode 100644 index 000000000..2442ec2cf --- /dev/null +++ b/packages/sdk/src/structures/ITypedApplication.ts @@ -0,0 +1,8 @@ +import { INestiaProject } from "./INestiaProject"; +import { ITypedHttpRoute } from "./ITypedHttpRoute"; +import { ITypedWebSocketRoute } from "./ITypedWebSocketRoute"; + +export interface ITypedApplication { + project: INestiaProject; + routes: Array; +} diff --git a/packages/sdk/src/structures/ITypedHttpRoute.ts b/packages/sdk/src/structures/ITypedHttpRoute.ts new file mode 100644 index 000000000..c35c388dc --- /dev/null +++ b/packages/sdk/src/structures/ITypedHttpRoute.ts @@ -0,0 +1,29 @@ +import { IJsDocTagInfo } from "typia"; + +import { IReflectController } from "./IReflectController"; +import { IReflectTypeImport } from "./IReflectTypeImport"; +import { ITypedHttpRouteException } from "./ITypedHttpRouteException"; +import { ITypedHttpRouteParameter } from "./ITypedHttpRouteParameter"; +import { ITypedHttpRouteSuccess } from "./ITypedHttpRouteSuccess"; + +export interface ITypedHttpRoute { + protocol: "http"; + function: Function; + controller: IReflectController; + name: string; + method: string; + path: string; + accessors: string[]; + parameters: ITypedHttpRouteParameter[]; + success: ITypedHttpRouteSuccess; + exceptions: Record< + number | "2XX" | "3XX" | "4XX" | "5XX", + ITypedHttpRouteException + >; + security: Record[]; + tags: string[]; + imports: IReflectTypeImport[]; + description: string | null; + jsDocTags: IJsDocTagInfo[]; + operationId: string | undefined; +} diff --git a/packages/sdk/src/structures/ITypedHttpRouteException.ts b/packages/sdk/src/structures/ITypedHttpRouteException.ts new file mode 100644 index 000000000..2d53c5bb2 --- /dev/null +++ b/packages/sdk/src/structures/ITypedHttpRouteException.ts @@ -0,0 +1,15 @@ +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { IReflectType } from "./IReflectType"; + +export interface ITypedHttpRouteException { + // BASIC PROPERTIES + status: number | "2XX" | "3XX" | "4XX" | "5XX"; + description: string | null; + example: any; + examples: Record; + + // REFLECTED PROPERTIES + type: IReflectType; + metadata: Metadata; +} diff --git a/packages/sdk/src/structures/ITypedHttpRouteParameter.ts b/packages/sdk/src/structures/ITypedHttpRouteParameter.ts new file mode 100644 index 000000000..8ee1c1e60 --- /dev/null +++ b/packages/sdk/src/structures/ITypedHttpRouteParameter.ts @@ -0,0 +1,41 @@ +import { IJsDocTagInfo } from "typia"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { IReflectType } from "./IReflectType"; + +export type ITypedHttpRouteParameter = + | ITypedHttpRouteParameter.IBody + | ITypedHttpRouteParameter.IHeaders + | ITypedHttpRouteParameter.IParam + | ITypedHttpRouteParameter.IQuery; +export namespace ITypedHttpRouteParameter { + export interface IBody extends IBase<"body"> { + contentType: + | "application/json" + | "application/x-www-form-urlencoded" + | "multipart/form-data" + | "text/plain"; + encrypted: boolean; + } + export interface IHeaders extends IBase<"headers"> { + field: string | null; + } + export interface IParam extends IBase<"param"> { + field: string; + } + export interface IQuery extends IBase<"query"> { + field: string | null; + } + + interface IBase { + kind: Kind; + name: string; + index: number; + type: IReflectType; + metadata: Metadata; + example?: any; + examples?: Record; + description: string | null; + jsDocTags: IJsDocTagInfo[]; + } +} diff --git a/packages/sdk/src/structures/ITypedHttpRouteSuccess.ts b/packages/sdk/src/structures/ITypedHttpRouteSuccess.ts new file mode 100644 index 000000000..0b2ce6b89 --- /dev/null +++ b/packages/sdk/src/structures/ITypedHttpRouteSuccess.ts @@ -0,0 +1,22 @@ +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { IReflectType } from "./IReflectType"; + +export interface ITypedHttpRouteSuccess { + type: IReflectType; + status: number | null; + contentType: + | "application/json" + | "text/plain" + | "application/x-www-form-urlencoded" + | "application/json" + | null; + encrypted: boolean; + metadata: Metadata; + example?: any; + examples?: Record; + setHeaders: Array< + | { type: "setter"; source: string; target?: string } + | { type: "assigner"; source: string } + >; +} diff --git a/packages/sdk/src/structures/ITypedWebSocketRoute.ts b/packages/sdk/src/structures/ITypedWebSocketRoute.ts new file mode 100644 index 000000000..bb9278783 --- /dev/null +++ b/packages/sdk/src/structures/ITypedWebSocketRoute.ts @@ -0,0 +1,20 @@ +import { VERSION_NEUTRAL } from "@nestjs/common"; +import ts from "typescript"; + +import { IReflectController } from "./IReflectController"; +import { IReflectTypeImport } from "./IReflectTypeImport"; +import { ITypedWebSocketRouteParameter } from "./ITypedWebSocketRouteParameter"; + +export interface ITypedWebSocketRoute { + protocol: "websocket"; + controller: IReflectController; + name: string; + path: string; + accessors: string[]; + function: Function; + versions: Array | undefined; + parameters: ITypedWebSocketRouteParameter[]; + imports: IReflectTypeImport[]; + description: string | null; + jsDocTags: ts.JSDocTagInfo[]; +} diff --git a/packages/sdk/src/structures/ITypedWebSocketRouteParameter.ts b/packages/sdk/src/structures/ITypedWebSocketRouteParameter.ts new file mode 100644 index 000000000..bde9fa2db --- /dev/null +++ b/packages/sdk/src/structures/ITypedWebSocketRouteParameter.ts @@ -0,0 +1,3 @@ +import { IReflectWebSocketOperationParameter } from "./IReflectWebSocketOperationParameter"; + +export import ITypedWebSocketRouteParameter = IReflectWebSocketOperationParameter; diff --git a/packages/sdk/src/transformers/IOperationMetadata.ts b/packages/sdk/src/transformers/IOperationMetadata.ts index 766fa8ced..eca834dcf 100644 --- a/packages/sdk/src/transformers/IOperationMetadata.ts +++ b/packages/sdk/src/transformers/IOperationMetadata.ts @@ -17,6 +17,8 @@ export namespace IOperationMetadata { export interface IParameter extends IResponse { name: string; index: number; + description: string | null; + jsDocTags: IJsDocTagInfo[]; } export interface IResponse { type: IReflectType | null; diff --git a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts index 8db0ab6c4..d9caf307d 100644 --- a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts +++ b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts @@ -52,18 +52,24 @@ export namespace SdkMetadataProgrammer { generics: WeakMap; parameter: ts.ParameterDeclaration; index: number; - }): IOperationMetadata.IParameter => ({ - ...writeType({ - ...props, - type: - props.context.checker.getTypeFromTypeNode( - props.parameter.type ?? TypeFactory.keyword("any"), - ) ?? null, - required: props.parameter.questionToken === undefined, - }), - name: props.parameter.name.getText(), - index: props.index, - }); + }): IOperationMetadata.IParameter => { + const symbol: ts.Symbol | undefined = + props.context.checker.getSymbolAtLocation(props.parameter); + return { + ...writeType({ + ...props, + type: + props.context.checker.getTypeFromTypeNode( + props.parameter.type ?? TypeFactory.keyword("any"), + ) ?? null, + required: props.parameter.questionToken === undefined, + }), + name: props.parameter.name.getText(), + index: props.index, + description: (symbol && CommentFactory.description(symbol)) ?? null, + jsDocTags: symbol?.getJsDocTags() ?? [], + }; + }; const writeResponse = (props: { context: ISdkTransformerContext; diff --git a/packages/sdk/src/utils/StringUtil.ts b/packages/sdk/src/utils/StringUtil.ts index 92aa5c9ee..500a418cf 100644 --- a/packages/sdk/src/utils/StringUtil.ts +++ b/packages/sdk/src/utils/StringUtil.ts @@ -1,6 +1,6 @@ export namespace StringUtil { export const capitalize = (text: string): string => - text.charAt(0).toUpperCase() + text.slice(1); + text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); export const escapeDuplicate = (keep: string[]) => diff --git a/test/features/app/nestia.config.ts b/test/features/app/nestia.config.ts index c35d60a14..d388c9e22 100644 --- a/test/features/app/nestia.config.ts +++ b/test/features/app/nestia.config.ts @@ -15,6 +15,7 @@ export const NESTIA_CONFIG: INestiaConfig = { e2e: "src/test", swagger: { output: "swagger.json", + beautify: true, security: { bearer: { type: "apiKey", diff --git a/test/features/app/src/api/functional/articles/comments/index.ts b/test/features/app/src/api/functional/articles/comments/index.ts deleted file mode 100644 index 1fc0a9721..000000000 --- a/test/features/app/src/api/functional/articles/comments/index.ts +++ /dev/null @@ -1,204 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.articles.comments - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; -import type { Format } from "typia/lib/tags/Format"; - -import type { IBbsComment } from "../../../structures/IBbsComment"; -import type { IPage } from "../../../structures/IPage"; - -/** - * @controller BbsCommentsController.index - * @path GET /:section/articles/:articleId/comments - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function index( - connection: IConnection, - section: string, - articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); -} -export namespace index { - export type Query = Resolved; - export type Output = Primitive>; - - export const METADATA = { - method: "GET", - path: "/:section/articles/:articleId/comments", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = ( - section: string, - articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; -} - -/** - * @controller BbsCommentsController.at - * @path GET /:section/articles/:articleId/comments/:id - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function at( - connection: IConnection, - section: string, - articleId: string & Format<"uuid">, - id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); -} -export namespace at { - export type Output = Primitive; - - export const METADATA = { - method: "GET", - path: "/:section/articles/:articleId/comments/:id", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = ( - section: string, - articleId: string & Format<"uuid">, - id: string & Format<"uuid">, - ) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; -} - -/** - * @controller BbsCommentsController.store - * @path POST /:section/articles/:articleId/comments - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function store( - connection: IConnection, - section: string, - articleId: string & Format<"uuid">, - input: store.Input, -): Promise { - return PlainFetcher.fetch( - { - ...connection, - headers: { - ...connection.headers, - "Content-Type": "application/json", - }, - }, - { - ...store.METADATA, - template: store.METADATA.path, - path: store.path(section, articleId), - }, - input, - ); -} -export namespace store { - export type Input = Primitive; - export type Output = Primitive; - - export const METADATA = { - method: "POST", - path: "/:section/articles/:articleId/comments", - request: { - type: "application/json", - encrypted: false, - }, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = (section: string, articleId: string & Format<"uuid">) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; -} - -/** - * @controller BbsCommentsController.update - * @path PUT /:section/articles/:articleId/comments/:id - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function update( - connection: IConnection, - section: string, - articleId: string & Format<"uuid">, - id: string & Format<"uuid">, - input: update.Input, -): Promise { - return PlainFetcher.fetch( - { - ...connection, - headers: { - ...connection.headers, - "Content-Type": "application/json", - }, - }, - { - ...update.METADATA, - template: update.METADATA.path, - path: update.path(section, articleId, id), - }, - input, - ); -} -export namespace update { - export type Input = Primitive; - export type Output = Primitive; - - export const METADATA = { - method: "PUT", - path: "/:section/articles/:articleId/comments/:id", - request: { - type: "application/json", - encrypted: false, - }, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = ( - section: string, - articleId: string & Format<"uuid">, - id: string & Format<"uuid">, - ) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; -} diff --git a/test/features/app/src/api/functional/articles/index.ts b/test/features/app/src/api/functional/articles/index.ts deleted file mode 100644 index 32910012d..000000000 --- a/test/features/app/src/api/functional/articles/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.articles - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -export * as comments from "./comments"; diff --git a/test/features/app/src/api/functional/bbs/articles/index.ts b/test/features/app/src/api/functional/bbs/articles/index.ts deleted file mode 100644 index 5efa486fc..000000000 --- a/test/features/app/src/api/functional/bbs/articles/index.ts +++ /dev/null @@ -1,201 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.bbs.articles - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; -import type { Format } from "typia/lib/tags/Format"; - -import type { IBbsArticle } from "../../../structures/IBbsArticle"; -import type { IPage } from "../../../structures/IPage"; - -/** - * @controller BbsArticlesController.index - * @path GET /bbs/:section/articles - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function index( - connection: IConnection, - section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); -} -export namespace index { - export type Query = Resolved; - export type Output = Primitive>; - - export const METADATA = { - method: "GET", - path: "/bbs/:section/articles", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; -} - -/** - * @controller BbsArticlesController.at - * @path GET /bbs/:section/articles/:id - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function at( - connection: IConnection, - section: string, - id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); -} -export namespace at { - export type Output = Primitive; - - export const METADATA = { - method: "GET", - path: "/bbs/:section/articles/:id", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = (section: string, id: string & Format<"uuid">) => - `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`; -} - -/** - * Store a new article. - * - * @param section Section code - * @param input Content to store - * @returns Newly archived article - * - * @controller BbsArticlesController.store - * @path POST /bbs/:section/articles - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function store( - connection: IConnection, - section: string, - input: store.Input, -): Promise { - return PlainFetcher.fetch( - { - ...connection, - headers: { - ...connection.headers, - "Content-Type": "application/json", - }, - }, - { - ...store.METADATA, - template: store.METADATA.path, - path: store.path(section), - }, - input, - ); -} -export namespace store { - export type Input = Primitive; - export type Output = Primitive; - - export const METADATA = { - method: "POST", - path: "/bbs/:section/articles", - request: { - type: "application/json", - encrypted: false, - }, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = (section: string) => - `/bbs/${encodeURIComponent(section ?? "null")}/articles`; -} - -/** - * Update an article. - * - * @param section Section code - * @param id Target article ID - * @param input Content to update - * @returns Updated content - * - * @controller BbsArticlesController.update - * @path PUT /bbs/:section/articles/:id - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function update( - connection: IConnection, - section: string, - id: string & Format<"uuid">, - input: update.Input, -): Promise { - return PlainFetcher.fetch( - { - ...connection, - headers: { - ...connection.headers, - "Content-Type": "application/json", - }, - }, - { - ...update.METADATA, - template: update.METADATA.path, - path: update.path(section, id), - }, - input, - ); -} -export namespace update { - export type Input = Primitive; - export type Output = Primitive; - - export const METADATA = { - method: "PUT", - path: "/bbs/:section/articles/:id", - request: { - type: "application/json", - encrypted: false, - }, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = (section: string, id: string & Format<"uuid">) => - `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`; -} diff --git a/test/features/app/src/api/functional/bbs/index.ts b/test/features/app/src/api/functional/bbs/index.ts deleted file mode 100644 index 7a891f888..000000000 --- a/test/features/app/src/api/functional/bbs/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.bbs - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -export * as articles from "./articles"; diff --git a/test/features/app/src/api/functional/health/index.ts b/test/features/app/src/api/functional/health/index.ts deleted file mode 100644 index d5a9f9925..000000000 --- a/test/features/app/src/api/functional/health/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.health - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; - -/** - * @controller HealthController.get - * @path GET /health - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); -} -export namespace get { - export const METADATA = { - method: "GET", - path: "/health", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = () => "/health"; -} diff --git a/test/features/app/src/api/functional/index.ts b/test/features/app/src/api/functional/index.ts deleted file mode 100644 index a11a25ddb..000000000 --- a/test/features/app/src/api/functional/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -export * as health from "./health"; -export * as performance from "./performance"; -export * as bbs from "./bbs"; -export * as articles from "./articles"; diff --git a/test/features/app/src/api/functional/performance/index.ts b/test/features/app/src/api/functional/performance/index.ts deleted file mode 100644 index 2e71dbb40..000000000 --- a/test/features/app/src/api/functional/performance/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.performance - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; - -import type { IPerformance } from "../../structures/IPerformance"; - -/** - * @controller PerformanceController.get - * @path GET /performance - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); -} -export namespace get { - export type Output = Primitive; - - export const METADATA = { - method: "GET", - path: "/performance", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = () => "/performance"; -} diff --git a/test/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts b/test/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts deleted file mode 100644 index c315c2a7d..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsComment } from "../../../../api/structures/IBbsComment"; - -export const test_api_articles_comments_at = async ( - connection: api.IConnection, -) => { - const output: Primitive = - await api.functional.articles.comments.at( - connection, - typia.random(), - typia.random>(), - typia.random>(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_articles_comments_index.ts b/test/features/app/src/test/features/api/automated/test_api_articles_comments_index.ts deleted file mode 100644 index f86166b32..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_articles_comments_index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsComment } from "../../../../api/structures/IBbsComment"; -import type { IPage } from "../../../../api/structures/IPage"; - -export const test_api_articles_comments_index = async ( - connection: api.IConnection, -) => { - const output: Primitive> = - await api.functional.articles.comments.index( - connection, - typia.random(), - typia.random>(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_articles_comments_store.ts b/test/features/app/src/test/features/api/automated/test_api_articles_comments_store.ts deleted file mode 100644 index f8e5d9242..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_articles_comments_store.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsComment } from "../../../../api/structures/IBbsComment"; - -export const test_api_articles_comments_store = async ( - connection: api.IConnection, -) => { - const output: Primitive = - await api.functional.articles.comments.store( - connection, - typia.random(), - typia.random>(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_articles_comments_update.ts b/test/features/app/src/test/features/api/automated/test_api_articles_comments_update.ts deleted file mode 100644 index 3b8c24e13..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_articles_comments_update.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsComment } from "../../../../api/structures/IBbsComment"; - -export const test_api_articles_comments_update = async ( - connection: api.IConnection, -) => { - const output: Primitive = - await api.functional.articles.comments.update( - connection, - typia.random(), - typia.random>(), - typia.random>(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts deleted file mode 100644 index 2d6c92f3f..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; - -export const test_api_bbs_articles_at = async (connection: api.IConnection) => { - const output: Primitive = await api.functional.bbs.articles.at( - connection, - typia.random(), - typia.random>(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts deleted file mode 100644 index 04a597311..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; - -import api from "../../../../api"; -import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; -import type { IPage } from "../../../../api/structures/IPage"; - -export const test_api_bbs_articles_index = async ( - connection: api.IConnection, -) => { - const output: Primitive> = - await api.functional.bbs.articles.index( - connection, - typia.random(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts deleted file mode 100644 index 4508b92e1..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; - -import api from "../../../../api"; -import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; - -export const test_api_bbs_articles_store = async ( - connection: api.IConnection, -) => { - const output: Primitive = - await api.functional.bbs.articles.store( - connection, - typia.random(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts deleted file mode 100644 index 80c5bfc49..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; -import type { Format } from "typia/lib/tags/Format"; - -import api from "../../../../api"; -import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; - -export const test_api_bbs_articles_update = async ( - connection: api.IConnection, -) => { - const output: Primitive = - await api.functional.bbs.articles.update( - connection, - typia.random(), - typia.random>(), - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_health_get.ts b/test/features/app/src/test/features/api/automated/test_api_health_get.ts deleted file mode 100644 index 8766b1129..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_health_get.ts +++ /dev/null @@ -1,8 +0,0 @@ -import typia from "typia"; - -import api from "../../../../api"; - -export const test_api_health_get = async (connection: api.IConnection) => { - const output = await api.functional.health.get(connection); - typia.assert(output); -}; diff --git a/test/features/app/src/test/features/api/automated/test_api_performance_get.ts b/test/features/app/src/test/features/api/automated/test_api_performance_get.ts deleted file mode 100644 index 54cdfd218..000000000 --- a/test/features/app/src/test/features/api/automated/test_api_performance_get.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; - -import api from "../../../../api"; -import type { IPerformance } from "../../../../api/structures/IPerformance"; - -export const test_api_performance_get = async (connection: api.IConnection) => { - const output: Primitive = - await api.functional.performance.get(connection); - typia.assert(output); -}; diff --git a/test/features/app/swagger.json b/test/features/app/swagger.json deleted file mode 100644 index 4b5e8ad73..000000000 --- a/test/features/app/swagger.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.10.0","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app/tsconfig.json b/test/features/app/tsconfig.json index 1487dbe83..e6a4868b1 100644 --- a/test/features/app/tsconfig.json +++ b/test/features/app/tsconfig.json @@ -93,7 +93,7 @@ { "transform": "typescript-transform-paths" }, { "transform": "typia/lib/transform" }, { "transform": "@nestia/core/lib/transform" }, - { "transform": "@nestia/sdk/lib/transform" }, // @todo -> must be removed + // { "transform": "@nestia/sdk/lib/transform" }, // @todo -> must be removed ], } } \ No newline at end of file diff --git a/test/features/distribute-assert-json/packages/api/package.json b/test/features/distribute-assert-json/packages/api/package.json index df0e66139..5217e0f5c 100644 --- a/test/features/distribute-assert-json/packages/api/package.json +++ b/test/features/distribute-assert-json/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" } } \ No newline at end of file diff --git a/test/features/distribute-assert/packages/api/package.json b/test/features/distribute-assert/packages/api/package.json index df0e66139..5217e0f5c 100644 --- a/test/features/distribute-assert/packages/api/package.json +++ b/test/features/distribute-assert/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" } } \ No newline at end of file diff --git a/test/features/distribute-json/packages/api/package.json b/test/features/distribute-json/packages/api/package.json index df0e66139..5217e0f5c 100644 --- a/test/features/distribute-json/packages/api/package.json +++ b/test/features/distribute-json/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" } } \ No newline at end of file diff --git a/test/features/distribute/packages/api/package.json b/test/features/distribute/packages/api/package.json index df0e66139..5217e0f5c 100644 --- a/test/features/distribute/packages/api/package.json +++ b/test/features/distribute/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" } } \ No newline at end of file diff --git a/test/package.json b/test/package.json index 25a05634e..bc09093a5 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@samchon/nestia-test", - "version": "3.10.0", + "version": "3.11.0-dev.20240811", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "../packages/sdk/nestia-sdk-3.10.0.tgz", + "@nestia/sdk": "../packages/sdk/nestia-sdk-3.11.0-dev.20240811.tgz", "@nestjs/swagger": "^7.1.2", "@samchon/openapi": "^0.4.3", "@types/express": "^4.17.17", @@ -40,16 +40,16 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "../packages/core/nestia-core-3.10.0.tgz", + "@nestia/core": "../packages/core/nestia-core-3.11.0-dev.20240811.tgz", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "../packages/fetcher/nestia-fetcher-3.10.0.tgz", + "@nestia/fetcher": "../packages/fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/platform-express": "^10.3.5", "@nestjs/platform-fastify": "^10.3.5", "tgrid": "^1.0.3", "tstl": "^3.0.0", - "typia": "^6.7.0", + "typia": "^6.8.0-dev.20240812", "uuid": "^9.0.1" } } \ No newline at end of file diff --git a/website/package.json b/website/package.json index 40d298641..e78b7e390 100644 --- a/website/package.json +++ b/website/package.json @@ -34,7 +34,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-mui-fileuploader": "^0.5.2", - "typia": "^6.7.0" + "typia": "^6.8.0-dev.20240812" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", From d57922b7ef9ad94a4c771c9cc741049e5a943060 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 01:12:01 +0900 Subject: [PATCH 04/21] Succeeded to revive SDK generator --- packages/sdk/package.json | 2 +- packages/sdk/src/NestiaSdkApplication.ts | 3 + packages/sdk/src/analyses/ImportAnalyzer.ts | 12 +- .../src/analyses/TypedHttpRouteAnalyzer.ts | 3 +- .../analyses/TypedWebSocketRouteAnalyzer.ts | 3 +- .../generates/internal/SdkAliasCollection.ts | 19 +- .../internal/SdkHttpNamespaceProgrammer.ts | 34 ++- packages/sdk/tsconfig.test.json | 85 +++++++ .../functional/bbs/articles/comments/index.ts | 211 ++++++++++++++++++ .../src/api/functional/bbs/articles/index.ts | 204 +++++++++++++++++ .../app/src/api/functional/bbs/index.ts | 7 + .../app/src/api/functional/health/index.ts | 44 ++++ test/features/app/src/api/functional/index.ts | 9 + .../src/api/functional/performance/index.ts | 48 ++++ .../src/controllers/BbsCommentsController.ts | 2 +- test/package.json | 2 +- 16 files changed, 645 insertions(+), 43 deletions(-) create mode 100644 packages/sdk/tsconfig.test.json create mode 100644 test/features/app/src/api/functional/bbs/articles/comments/index.ts create mode 100644 test/features/app/src/api/functional/bbs/articles/index.ts create mode 100644 test/features/app/src/api/functional/bbs/index.ts create mode 100644 test/features/app/src/api/functional/health/index.ts create mode 100644 test/features/app/src/api/functional/index.ts create mode 100644 test/features/app/src/api/functional/performance/index.ts diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 01906022f..0d1489b60 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -9,7 +9,7 @@ }, "scripts": { "build": "rimraf lib && tsc", - "dev": "rimraf lib && tsc --watch", + "dev": "tsc -p tsconfig.test.json --watch", "eslint": "eslint ./**/*.ts", "prepare": "ts-patch install && typia patch" }, diff --git a/packages/sdk/src/NestiaSdkApplication.ts b/packages/sdk/src/NestiaSdkApplication.ts index 5600355d5..f9dda1d17 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -4,6 +4,7 @@ import { HashSet, Pair, TreeMap } from "tstl"; import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary"; import { INestiaConfig } from "./INestiaConfig"; +import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer"; import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer"; import { PathAnalyzer } from "./analyses/PathAnalyzer"; import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer"; @@ -228,6 +229,8 @@ export class NestiaSdkApplication { }), ); } + AccessorAnalyzer.analyze(routes); + if (props.validate !== undefined) props.validate({ project, diff --git a/packages/sdk/src/analyses/ImportAnalyzer.ts b/packages/sdk/src/analyses/ImportAnalyzer.ts index 93a59f1de..894c039ae 100644 --- a/packages/sdk/src/analyses/ImportAnalyzer.ts +++ b/packages/sdk/src/analyses/ImportAnalyzer.ts @@ -55,7 +55,7 @@ export namespace ImportAnalyzer { TYPE --------------------------------------------------------- */ const escape = (checker: ts.TypeChecker, type: ts.Type): ts.Type => { - if (type.symbol && getName(type.symbol) === "Promise") { + if (type.symbol && getNameOfSymbol(type.symbol) === "Promise") { const generic: readonly ts.Type[] = checker.getTypeArguments( type as ts.TypeReference, ); @@ -68,7 +68,7 @@ export namespace ImportAnalyzer { return type; }; - const getName = (symbol: ts.Symbol): string => + const getNameOfSymbol = (symbol: ts.Symbol): string => exploreName( symbol.escapedName.toString(), symbol.getDeclarations()?.[0]?.parent, @@ -104,6 +104,7 @@ export namespace ImportAnalyzer { type: child, }), ) + .map(getName) .join(joiner), }; } @@ -120,7 +121,7 @@ export namespace ImportAnalyzer { //---- // SPECIALIZATION //---- - const name: string = getName(symbol); + const name: string = getNameOfSymbol(symbol); const sourceFile: ts.SourceFile | undefined = symbol.declarations?.[0]?.getSourceFile(); if (sourceFile === undefined) return { name }; @@ -163,3 +164,8 @@ export namespace ImportAnalyzer { ) : name; } + +const getName = (type: IReflectType): string => + type.typeArguments + ? `${type.name}<${type.typeArguments.map(getName).join(", ")}>` + : type.name; diff --git a/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts index 62613eb37..60228f398 100644 --- a/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts +++ b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts @@ -14,6 +14,7 @@ import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; import { ITypedHttpRouteException } from "../structures/ITypedHttpRouteException"; import { ITypedHttpRouteParameter } from "../structures/ITypedHttpRouteParameter"; import { ITypedHttpRouteSuccess } from "../structures/ITypedHttpRouteSuccess"; +import { PathUtil } from "../utils/PathUtil"; export namespace TypedHttpRouteAnalyzer { export const dictionary = ( @@ -155,7 +156,7 @@ export namespace TypedHttpRouteAnalyzer { ...props.operation, controller: props.controller, path, - accessors: ["@lazy"], + accessors: [...PathUtil.accessors(path), props.operation.name], exceptions, parameters, success, diff --git a/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts index 63548175c..8b1432359 100644 --- a/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts +++ b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts @@ -1,6 +1,7 @@ import { IReflectController } from "../structures/IReflectController"; import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation"; import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute"; +import { PathUtil } from "../utils/PathUtil"; export namespace TypedWebSocketRouteAnalyzer { export const analyze = (props: { @@ -12,6 +13,6 @@ export namespace TypedWebSocketRouteAnalyzer { ...props.operation, controller: props.controller, path, - accessors: ["@lazy"], + accessors: [...PathUtil.accessors(path), props.operation.name], })); } diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index a4b762ff1..2881416c0 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -74,30 +74,19 @@ export namespace SdkAliasCollection { (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { if (project.config.propagate !== true) { - const node: ts.TypeNode = name(route.success.type); - const type = project.checker.getTypeAtLocation(node); - const filter = (flag: ts.TypeFlags) => (type.flags & flag) !== 0; - - if ( - project.config.clone === true || - project.config.primitive === false || - filter(ts.TypeFlags.Undefined) || - filter(ts.TypeFlags.Never) || - filter(ts.TypeFlags.Void) || - filter(ts.TypeFlags.VoidLike) - ) - return node; + if (project.config.clone === true || project.config.primitive === false) + return name(route.success.type); return ts.factory.createTypeReferenceNode( importer.external({ type: true, library: "@nestia/fetcher", instance: - route.success.contentType === "application/json" && + route.success.contentType === "application/json" || route.success.encrypted === true ? "Primitive" : "Resolved", }), - [node], + [name(route.success.type)], ); } diff --git a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts index c9738e052..152e43dd1 100644 --- a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts @@ -35,7 +35,7 @@ export namespace SdkHttpNamespaceProgrammer { ...(types.length ? [FilePrinter.enter()] : []), write_metadata(importer)(route, props), FilePrinter.enter(), - write_path(route, props), + write_path(route, props.query), ...(project.config.simulate ? [ SdkHttpSimulationProgrammer.random(project)(importer)(route), @@ -92,11 +92,8 @@ export namespace SdkHttpNamespaceProgrammer { "Input", SdkAliasCollection.input(project)(importer)(props.input), ); - if ( - project.config.propagate === true || - route.success.type.name !== "void" - ) - declare("Output", SdkAliasCollection.name(route.success.type)); + if (route.success.type.name !== "void") + declare("Output", SdkAliasCollection.output(project)(importer)(route)); return array; }; @@ -179,9 +176,7 @@ export namespace SdkHttpNamespaceProgrammer { const write_path = ( route: ITypedHttpRoute, - props: { - query: ITypedHttpRouteParameter.IQuery | undefined; - }, + query: ITypedHttpRouteParameter.IQuery | undefined, ): ts.VariableStatement => { const g = { total: [ @@ -191,7 +186,7 @@ export namespace SdkHttpNamespaceProgrammer { ], query: route.parameters .filter((param) => param.kind === "query") - .filter((param) => param.field !== undefined), + .filter((param) => param.field !== null), path: route.parameters.filter((param) => param.kind === "param"), }; const out = (body: ts.ConciseBody) => @@ -202,8 +197,8 @@ export namespace SdkHttpNamespaceProgrammer { g.total.map((p) => IdentifierFactory.parameter( p.name, - p === props.query - ? p.metadata.optional + p === query + ? p.metadata.isRequired() === false ? ts.factory.createUnionTypeNode([ ts.factory.createTypeReferenceNode(`${route.name}.Query`), ts.factory.createTypeReferenceNode("undefined"), @@ -249,8 +244,7 @@ export namespace SdkHttpNamespaceProgrammer { }), ); }; - if (props.query === undefined && g.query.length === 0) - return out(template()); + if (query === undefined && g.query.length === 0) return out(template()); const block = (expr: ts.Expression) => { const computeName = (str: string): string => @@ -398,26 +392,26 @@ export namespace SdkHttpNamespaceProgrammer { true, ); }; - if (props.query !== undefined && g.query.length === 0) + if (query !== undefined && g.query.length === 0) return out( block( - props.query.metadata.optional + query.metadata.isRequired() === false ? ts.factory.createBinaryExpression( - ts.factory.createIdentifier(props.query.name), + ts.factory.createIdentifier(query.name), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createObjectLiteralExpression([], false), ) - : ts.factory.createIdentifier(props.query.name), + : ts.factory.createIdentifier(query.name), ), ); return out( block( ts.factory.createObjectLiteralExpression( [ - ...(props.query + ...(query ? [ ts.factory.createSpreadAssignment( - ts.factory.createIdentifier(props.query.name), + ts.factory.createIdentifier(query.name), ), ] : []), diff --git a/packages/sdk/tsconfig.test.json b/packages/sdk/tsconfig.test.json new file mode 100644 index 000000000..d68b0efed --- /dev/null +++ b/packages/sdk/tsconfig.test.json @@ -0,0 +1,85 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": [ + "DOM", + "ES2015" + ], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "../../test/node_modules/@nestia/sdk/lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + "types": [ + "node", + "reflect-metadata" + ], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "stripInternal": true, + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "plugins": [ + { + "transform": "typia/lib/transform", + "functional": true, + } + ], + "newLine": "LF", + }, + "include": ["src"] +} diff --git a/test/features/app/src/api/functional/bbs/articles/comments/index.ts b/test/features/app/src/api/functional/bbs/articles/comments/index.ts new file mode 100644 index 000000000..78b01174a --- /dev/null +++ b/test/features/app/src/api/functional/bbs/articles/comments/index.ts @@ -0,0 +1,211 @@ +/** + * @packageDocumentation + * @module api.functional.bbs.articles.comments + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; +import type { Format } from "typia/lib/tags/Format"; + +import type { IBbsComment } from "../../../../structures/IBbsComment"; +import type { IPage } from "../../../../structures/IPage"; + +/** + * @controller BbsCommentsController.index + * @path GET /bbs/:section/articles/:articleId/comments + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function index( + connection: IConnection, + section: string, + articleId: string & Format<"uuid">, + query: IPage.IRequest, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); +} +export namespace index { + export type Output = Primitive>; + + export const METADATA = { + method: "GET", + path: "/bbs/:section/articles/:articleId/comments", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = ( + section: string, + articleId: string & Format<"uuid">, + query: IPage.IRequest, + ) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; +} + +/** + * @controller BbsCommentsController.at + * @path GET /bbs/:section/articles/:articleId/comments/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function at( + connection: IConnection, + section: string, + articleId: string & Format<"uuid">, + id: string & Format<"uuid">, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); +} +export namespace at { + export type Output = Primitive; + + export const METADATA = { + method: "GET", + path: "/bbs/:section/articles/:articleId/comments/:id", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = ( + section: string, + articleId: string & Format<"uuid">, + id: string & Format<"uuid">, + ) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; +} + +/** + * @controller BbsCommentsController.store + * @path POST /bbs/:section/articles/:articleId/comments + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function store( + connection: IConnection, + section: string, + articleId: string & Format<"uuid">, + input: store.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...store.METADATA, + template: store.METADATA.path, + path: store.path(section, articleId), + }, + input, + ); +} +export namespace store { + export type Input = Resolved; + export type Output = Primitive; + + export const METADATA = { + method: "POST", + path: "/bbs/:section/articles/:articleId/comments", + request: { + type: "application/json", + encrypted: false, + }, + response: { + type: "application/json", + encrypted: false, + }, + status: 201, + } as const; + + export const path = (section: string, articleId: string & Format<"uuid">) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; +} + +/** + * @controller BbsCommentsController.update + * @path PUT /bbs/:section/articles/:articleId/comments/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function update( + connection: IConnection, + section: string, + articleId: string & Format<"uuid">, + id: string & Format<"uuid">, + input: update.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...update.METADATA, + template: update.METADATA.path, + path: update.path(section, articleId, id), + }, + input, + ); +} +export namespace update { + export type Input = Resolved; + export type Output = Primitive; + + export const METADATA = { + method: "PUT", + path: "/bbs/:section/articles/:articleId/comments/:id", + request: { + type: "application/json", + encrypted: false, + }, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = ( + section: string, + articleId: string & Format<"uuid">, + id: string & Format<"uuid">, + ) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; +} diff --git a/test/features/app/src/api/functional/bbs/articles/index.ts b/test/features/app/src/api/functional/bbs/articles/index.ts new file mode 100644 index 000000000..74df03a7d --- /dev/null +++ b/test/features/app/src/api/functional/bbs/articles/index.ts @@ -0,0 +1,204 @@ +/** + * @packageDocumentation + * @module api.functional.bbs.articles + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; +import type { Format } from "typia/lib/tags/Format"; + +import type { IBbsArticle } from "../../../structures/IBbsArticle"; +import type { IPage } from "../../../structures/IPage"; + +export * as comments from "./comments"; + +/** + * @controller BbsArticlesController.index + * @path GET /bbs/:section/articles + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function index( + connection: IConnection, + section: string, + query: IPage.IRequest, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); +} +export namespace index { + export type Output = Primitive>; + + export const METADATA = { + method: "GET", + path: "/bbs/:section/articles", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = (section: string, query: IPage.IRequest) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles`; +} + +/** + * @controller BbsArticlesController.at + * @path GET /bbs/:section/articles/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function at( + connection: IConnection, + section: string, + id: string & Format<"uuid">, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); +} +export namespace at { + export type Output = Primitive; + + export const METADATA = { + method: "GET", + path: "/bbs/:section/articles/:id", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = (section: string, id: string & Format<"uuid">) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`; +} + +/** + * @param section Section code + * @param input Content to store + * @returns Newly archived article + * @controller BbsArticlesController.store + * @path POST /bbs/:section/articles + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function store( + connection: IConnection, + section: string, + input: store.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...store.METADATA, + template: store.METADATA.path, + path: store.path(section), + }, + input, + ); +} +export namespace store { + export type Input = Resolved; + export type Output = Primitive; + + export const METADATA = { + method: "POST", + path: "/bbs/:section/articles", + request: { + type: "application/json", + encrypted: false, + }, + response: { + type: "application/json", + encrypted: false, + }, + status: 201, + } as const; + + export const path = (section: string) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles`; +} + +/** + * @param section Section code + * @param id Target article ID + * @param input Content to update + * @returns Updated content + * @controller BbsArticlesController.update + * @path PUT /bbs/:section/articles/:id + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function update( + connection: IConnection, + section: string, + id: string & Format<"uuid">, + input: update.Input, +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...update.METADATA, + template: update.METADATA.path, + path: update.path(section, id), + }, + input, + ); +} +export namespace update { + export type Input = Resolved; + export type Output = Primitive; + + export const METADATA = { + method: "PUT", + path: "/bbs/:section/articles/:id", + request: { + type: "application/json", + encrypted: false, + }, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = (section: string, id: string & Format<"uuid">) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(id ?? "null")}`; +} diff --git a/test/features/app/src/api/functional/bbs/index.ts b/test/features/app/src/api/functional/bbs/index.ts new file mode 100644 index 000000000..7a891f888 --- /dev/null +++ b/test/features/app/src/api/functional/bbs/index.ts @@ -0,0 +1,7 @@ +/** + * @packageDocumentation + * @module api.functional.bbs + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +export * as articles from "./articles"; diff --git a/test/features/app/src/api/functional/health/index.ts b/test/features/app/src/api/functional/health/index.ts new file mode 100644 index 000000000..0218f7f3a --- /dev/null +++ b/test/features/app/src/api/functional/health/index.ts @@ -0,0 +1,44 @@ +/** + * @packageDocumentation + * @module api.functional.health + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; + +/** + * @controller HealthController.get + * @path GET /health + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); +} +export namespace get { + export const METADATA = { + method: "GET", + path: "/health", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = () => "/health"; +} diff --git a/test/features/app/src/api/functional/index.ts b/test/features/app/src/api/functional/index.ts new file mode 100644 index 000000000..acdfad8d4 --- /dev/null +++ b/test/features/app/src/api/functional/index.ts @@ -0,0 +1,9 @@ +/** + * @packageDocumentation + * @module api.functional + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +export * as health from "./health"; +export * as performance from "./performance"; +export * as bbs from "./bbs"; diff --git a/test/features/app/src/api/functional/performance/index.ts b/test/features/app/src/api/functional/performance/index.ts new file mode 100644 index 000000000..749a475c8 --- /dev/null +++ b/test/features/app/src/api/functional/performance/index.ts @@ -0,0 +1,48 @@ +/** + * @packageDocumentation + * @module api.functional.performance + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; + +import type { IPerformance } from "../../structures/IPerformance"; + +/** + * @controller PerformanceController.get + * @path GET /performance + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); +} +export namespace get { + export type Output = Primitive; + + export const METADATA = { + method: "GET", + path: "/performance", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = () => "/performance"; +} diff --git a/test/features/app/src/controllers/BbsCommentsController.ts b/test/features/app/src/controllers/BbsCommentsController.ts index be32d656e..164e7743d 100644 --- a/test/features/app/src/controllers/BbsCommentsController.ts +++ b/test/features/app/src/controllers/BbsCommentsController.ts @@ -5,7 +5,7 @@ import typia, { tags } from "typia"; import { IBbsComment } from "@api/lib/structures/IBbsComment"; import { IPage } from "@api/lib/structures/IPage"; -@Controller(":section/articles/:articleId/comments") +@Controller("bbs/:section/articles/:articleId/comments") export class BbsCommentsController { @core.TypedRoute.Get() public async index( diff --git a/test/package.json b/test/package.json index bc09093a5..5512069d2 100644 --- a/test/package.json +++ b/test/package.json @@ -49,7 +49,7 @@ "@nestjs/platform-fastify": "^10.3.5", "tgrid": "^1.0.3", "tstl": "^3.0.0", - "typia": "^6.8.0-dev.20240812", + "typia": "6.8.0-dev.20240812", "uuid": "^9.0.1" } } \ No newline at end of file From bbe10695cb847a57183b4f3d1cae22c4dd071114 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 02:38:03 +0900 Subject: [PATCH 05/21] Succeeded but there's a comment problem --- package.json | 2 +- packages/core/package.json | 8 +- packages/core/src/decorators/DynamicModule.ts | 6 +- .../core/src/decorators/EncryptedModule.ts | 6 +- .../internal/NoTransformConfigureError.ts | 3 - .../decorators/internal/load_controller.ts | 3 +- .../core/src/transformers/FileTransformer.ts | 2 +- .../transformers/WebSocketRouteTransformer.ts | 2 +- packages/core/src/utils/SourceFinder.ts | 1 + packages/core/tsconfig.test.json | 85 ++ packages/fetcher/package.json | 2 +- packages/sdk/package.json | 10 +- packages/sdk/src/analyses/ConfigAnalyzer.ts | 14 +- packages/sdk/src/analyses/ImportAnalyzer.ts | 12 +- .../analyses/ReflectHttpOperationAnalyzer.ts | 2 +- .../ReflectHttpOperationParameterAnalyzer.ts | 64 +- .../ReflectWebSocketOperationAnalyzer.ts | 23 +- .../src/analyses/TypedHttpRouteAnalyzer.ts | 2 +- .../executable/internal/NestiaConfigLoader.ts | 19 +- .../sdk/src/generates/SwaggerGenerator.ts | 2 +- .../generates/internal/E2eFileProgrammer.ts | 4 +- .../generates/internal/SdkAliasCollection.ts | 2 +- .../internal/SdkHttpFunctionProgrammer.ts | 10 +- .../internal/SdkHttpNamespaceProgrammer.ts | 6 +- .../internal/SdkHttpRouteProgrammer.ts | 8 +- .../internal/SdkHttpSimulationProgrammer.ts | 16 +- .../SdkWebSocketNamespaceProgrammer.ts | 10 +- .../internal/SdkWebSocketRouteProgrammer.ts | 10 +- .../internal/SwaggerOperationComposer.ts | 2 +- .../SwaggerOperationParameterComposer.ts | 10 +- .../IReflectHttpOperationParameter.ts | 8 +- .../IReflectWebSocketOperationParameter.ts | 6 +- .../structures/ITypedHttpRouteParameter.ts | 4 +- .../all/src/api/functional/all/index.ts | 13 +- .../all/src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/all/swagger.json | 41 +- .../x/bbs/articles/comments/index.ts | 79 +- .../api/functional/x/bbs/articles/index.ts | 83 +- .../src/api/functional/x/calculate/index.ts | 6 +- .../src/api/functional/x/health/index.ts | 21 +- .../src/api/functional/x/performance/index.ts | 23 +- test/features/app-globalPrefix/swagger.json | 2 +- .../functional/bbs/articles/comments/index.ts | 4 +- .../src/api/functional/bbs/articles/index.ts | 4 +- .../api/automated/test_api_bbs_articles_at.ts | 15 + .../test_api_bbs_articles_comments_at.ts | 19 + .../test_api_bbs_articles_comments_index.ts | 20 + .../test_api_bbs_articles_comments_store.ts | 19 + .../test_api_bbs_articles_comments_update.ts | 20 + .../automated/test_api_bbs_articles_index.ts | 18 + .../automated/test_api_bbs_articles_store.ts | 17 + .../automated/test_api_bbs_articles_update.ts | 19 + .../api/automated/test_api_health_get.ts | 8 + .../api/automated/test_api_performance_get.ts | 11 + test/features/app/swagger.json | 842 ++++++++++++++++++ test/package.json | 8 +- 57 files changed, 1431 insertions(+), 269 deletions(-) create mode 100644 packages/core/tsconfig.test.json create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_index.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_store.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_update.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_health_get.ts create mode 100644 test/features/app/src/test/features/api/automated/test_api_performance_get.ts create mode 100644 test/features/app/swagger.json diff --git a/package.json b/package.json index 21443feda..912d2474e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@nestia/station", - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-2", "description": "Nestia station", "scripts": { "build": "node build/index.js", diff --git a/packages/core/package.json b/packages/core/package.json index 58e77b5f6..b6de9babe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-2", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -11,7 +11,7 @@ }, "scripts": { "build": "rimraf lib && tsc", - "dev": "npm run build -- --watch", + "dev": "tsc -p tsconfig.test.json --watch", "eslint": "eslint ./**/*.ts", "eslint:fix": "eslint ./**/*.ts --fix", "prepare": "ts-patch install && typia patch" @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", + "@nestia/fetcher": "^3.11.0-dev.20240813-2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^0.4.3", @@ -53,7 +53,7 @@ "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=3.11.0-dev.20240811", + "@nestia/fetcher": ">=3.11.0-dev.20240813-2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/core/src/decorators/DynamicModule.ts b/packages/core/src/decorators/DynamicModule.ts index 5f53c9ddf..4fb2eda1d 100644 --- a/packages/core/src/decorators/DynamicModule.ts +++ b/packages/core/src/decorators/DynamicModule.ts @@ -27,9 +27,13 @@ export namespace DynamicModule { export async function mount( path: string | string[] | { include: string[]; exclude?: string[] }, metadata: Omit = {}, + isTsNode?: boolean, ): Promise { // LOAD CONTROLLERS - const controllers: Creator[] = await load_controllers(path); + const controllers: Creator[] = await load_controllers( + path, + isTsNode, + ); // RETURN WITH DECORATING @Module({ ...metadata, controllers }) diff --git a/packages/core/src/decorators/EncryptedModule.ts b/packages/core/src/decorators/EncryptedModule.ts index 0323cc1c5..a0a082083 100644 --- a/packages/core/src/decorators/EncryptedModule.ts +++ b/packages/core/src/decorators/EncryptedModule.ts @@ -63,9 +63,13 @@ export namespace EncryptedModule { path: string | string[] | { include: string[]; exclude?: string[] }, password: IEncryptionPassword | IEncryptionPassword.Closure, options: Omit[0], "controllers"> = {}, + isTsNode?: boolean, ): Promise { // LOAD CONTROLLERS - const controllers: Creator[] = await load_controllers(path); + const controllers: Creator[] = await load_controllers( + path, + isTsNode, + ); // RETURNS WITH DECORATING @EncryptedModule( diff --git a/packages/core/src/decorators/internal/NoTransformConfigureError.ts b/packages/core/src/decorators/internal/NoTransformConfigureError.ts index ec3d47ca1..ad6d771e0 100644 --- a/packages/core/src/decorators/internal/NoTransformConfigureError.ts +++ b/packages/core/src/decorators/internal/NoTransformConfigureError.ts @@ -1,6 +1,3 @@ -/** - * @internal - */ export function NoTransformConfigureError(method: string): never { if (NoTransformConfigureError.throws === true) throw new Error( diff --git a/packages/core/src/decorators/internal/load_controller.ts b/packages/core/src/decorators/internal/load_controller.ts index ad922def3..a295720a0 100644 --- a/packages/core/src/decorators/internal/load_controller.ts +++ b/packages/core/src/decorators/internal/load_controller.ts @@ -8,6 +8,7 @@ import { SourceFinder } from "../../utils/SourceFinder"; */ export const load_controllers = async ( path: string | string[] | { include: string[]; exclude?: string[] }, + isTsNode?: boolean, ): Promise[]> => { const sources: string[] = await SourceFinder.find({ include: Array.isArray(path) @@ -20,7 +21,7 @@ export const load_controllers = async ( ? path.exclude ?? [] : [], filter: - EXTENSION === "ts" + isTsNode === true || EXTENSION === "ts" ? (file) => file.substring(file.length - 3) === ".ts" && file.substring(file.length - 5) !== ".d.ts" diff --git a/packages/core/src/transformers/FileTransformer.ts b/packages/core/src/transformers/FileTransformer.ts index fed0a1760..50a8cbe40 100644 --- a/packages/core/src/transformers/FileTransformer.ts +++ b/packages/core/src/transformers/FileTransformer.ts @@ -46,7 +46,7 @@ export namespace FileTransformer { (node as any).parent ??= file; // REPORT DIAGNOSTIC - const diagnostic = ts.createDiagnosticForNode(node, { + const diagnostic = (ts as any).createDiagnosticForNode(node, { key: exp.code, category: ts.DiagnosticCategory.Error, message: exp.message, diff --git a/packages/core/src/transformers/WebSocketRouteTransformer.ts b/packages/core/src/transformers/WebSocketRouteTransformer.ts index 97c0b7452..7026f6445 100644 --- a/packages/core/src/transformers/WebSocketRouteTransformer.ts +++ b/packages/core/src/transformers/WebSocketRouteTransformer.ts @@ -20,7 +20,7 @@ export namespace WebSocketRouteTransformer { const report = (node: ts.Node, message: string) => { errors.push( - ts.createDiagnosticForNode(node, { + (ts as any).createDiagnosticForNode(node, { category: ts.DiagnosticCategory.Error, key: "nestia.core.WebSocketRoute", code: "(nestia.core.WebSocketRoute)" as any, diff --git a/packages/core/src/utils/SourceFinder.ts b/packages/core/src/utils/SourceFinder.ts index 69d8ac463..1593c9dc6 100644 --- a/packages/core/src/utils/SourceFinder.ts +++ b/packages/core/src/utils/SourceFinder.ts @@ -18,6 +18,7 @@ export namespace SourceFinder { (input: string[]) => async (closure: (location: string) => void): Promise => { for (const pattern of input) { + console.log(path.resolve(pattern)); for (const file of await _Glob(path.resolve(pattern))) { const stats: fs.Stats = await fs.promises.stat(file); if (stats.isDirectory() === true) diff --git a/packages/core/tsconfig.test.json b/packages/core/tsconfig.test.json new file mode 100644 index 000000000..9ab4e16bd --- /dev/null +++ b/packages/core/tsconfig.test.json @@ -0,0 +1,85 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": [ + "DOM", + "ES2015" + ], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "../../test/node_modules/@nestia/core/lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + "types": [ + "node", + "reflect-metadata" + ], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "stripInternal": true, + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "plugins": [ + { + "transform": "typia/lib/transform", + "functional": true, + } + ], + "newLine": "LF", + }, + "include": ["src"] +} diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 47be88ecb..8cfffc9c3 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-2", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0d1489b60..ae2221d98 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-2", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "../core/nestia-core-3.11.0-dev.20240811.tgz", - "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", + "@nestia/core": "^3.11.0-dev.20240813-2", + "@nestia/fetcher": "^3.11.0-dev.20240813-2", "@samchon/openapi": "^0.4.3", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", @@ -48,8 +48,8 @@ "typia": "^6.8.0-dev.20240812" }, "peerDependencies": { - "@nestia/core": ">=3.11.0-dev.20240811", - "@nestia/fetcher": ">=3.11.0-dev.20240811", + "@nestia/core": ">=3.11.0-dev.20240813-2", + "@nestia/fetcher": ">=3.11.0-dev.20240813-2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/sdk/src/analyses/ConfigAnalyzer.ts b/packages/sdk/src/analyses/ConfigAnalyzer.ts index 8330102dd..765d03d64 100644 --- a/packages/sdk/src/analyses/ConfigAnalyzer.ts +++ b/packages/sdk/src/analyses/ConfigAnalyzer.ts @@ -14,16 +14,20 @@ import { MapUtil } from "../utils/MapUtil"; export namespace ConfigAnalyzer { export const input = async ( config: INestiaConfig, - ): Promise => - MapUtil.take(memory, config, async () => { + ): Promise => { + return MapUtil.take(memory, config, async () => { const app: INestApplication = typeof config.input === "function" ? await config.input() - : await NestFactory.create(await DynamicModule.mount(config.input), { - logger: false, - }); + : await NestFactory.create( + await DynamicModule.mount(config.input, {}, true as any), + { + logger: false, + }, + ); return analyze_application(app); }); + }; const analyze_application = async ( app: INestApplication, diff --git a/packages/sdk/src/analyses/ImportAnalyzer.ts b/packages/sdk/src/analyses/ImportAnalyzer.ts index 894c039ae..484dab72e 100644 --- a/packages/sdk/src/analyses/ImportAnalyzer.ts +++ b/packages/sdk/src/analyses/ImportAnalyzer.ts @@ -104,7 +104,7 @@ export namespace ImportAnalyzer { type: child, }), ) - .map(getName) + .map(getEscapedText) .join(joiner), }; } @@ -163,9 +163,9 @@ export namespace ImportAnalyzer { decl.parent.parent, ) : name; -} -const getName = (type: IReflectType): string => - type.typeArguments - ? `${type.name}<${type.typeArguments.map(getName).join(", ")}>` - : type.name; + const getEscapedText = (type: IReflectType): string => + type.typeArguments + ? `${type.name}<${type.typeArguments.map(getEscapedText).join(", ")}>` + : type.name; +} diff --git a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts index 954af7d84..ad6998ecd 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -132,7 +132,7 @@ export namespace ReflectHttpOperationAnalyzer { continue; } const parameters: string[] = operation.parameters - .filter((param) => param.kind === "param") + .filter((param) => param.category === "param") .map((param) => param.field!) .sort(); diff --git a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts index 8d0501a59..57ee27503 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -38,24 +38,27 @@ export namespace ReflectHttpOperationParameterAnalyzer { }; if ( (ctx.httpMethod === "GET" || ctx.httpMethod === "HEAD") && - preconfigured.some((x) => x.kind === "body") + preconfigured.some((x) => x.category === "body") ) contradict(`@Body() is not allowed in the ${ctx.httpMethod} method.`); // FIND DUPLICATED BODY if ( - preconfigured.filter((x) => x.kind === "body" && x.field === undefined) - .length > 1 + preconfigured.filter( + (x) => x.category === "body" && x.field === undefined, + ).length > 1 ) contradict(`Duplicated @Body() is not allowed.`); if ( - preconfigured.filter((x) => x.kind === "query" && x.field === undefined) - .length > 1 + preconfigured.filter( + (x) => x.category === "query" && x.field === undefined, + ).length > 1 ) contradict(`Duplicated @Query() without field name is not allowed.`); if ( - preconfigured.filter((x) => x.kind === "headers" && x.field === undefined) - .length > 1 + preconfigured.filter( + (x) => x.category === "headers" && x.field === undefined, + ).length > 1 ) contradict(`Duplicated @Headers() without field name is not allowed.`); @@ -63,7 +66,7 @@ export namespace ReflectHttpOperationParameterAnalyzer { if ( isUnique( preconfigured - .filter((x) => x.kind === "param") + .filter((x) => x.category === "param") .map((x) => x.field) .filter((field) => field !== undefined), ) === false @@ -72,7 +75,7 @@ export namespace ReflectHttpOperationParameterAnalyzer { if ( isUnique( preconfigured - .filter((x) => x.kind === "query") + .filter((x) => x.category === "query") .map((x) => x.field) .filter((field) => field !== undefined), ) === false @@ -81,7 +84,7 @@ export namespace ReflectHttpOperationParameterAnalyzer { if ( isUnique( preconfigured - .filter((x) => x.kind === "headers") + .filter((x) => x.category === "headers") .map((x) => x.field) .filter((field) => field !== undefined), ) === false @@ -126,15 +129,15 @@ export namespace ReflectHttpOperationParameterAnalyzer { const schema: IOperationMetadata.ISchema | null = (() => { if (matched === undefined) return null; const result = - p.kind === "body" && + p.category === "body" && (p.contentType === "application/json" || p.encrypted === true) ? matched.primitive : matched.resolved; return result.success ? result.data : null; })(); - if (p.kind === "body" && p.field !== undefined) + if (p.category === "body" && p.field !== undefined) pErrorContents.push(`@Body() must not have a field name.`); - else if (p.kind === "param" && p.field === undefined) + else if (p.category === "param" && p.field === undefined) pErrorContents.push(`@Param() must have a field name.`); if (pErrorContents.length) return report(); @@ -147,9 +150,9 @@ export namespace ReflectHttpOperationParameterAnalyzer { // COMPOSITION imports.push(...matched.imports); - if (p.kind === "param") + if (p.category === "param") return { - kind: p.kind, + category: p.category, index: p.index, field: p.field!, name: matched.name, @@ -159,24 +162,24 @@ export namespace ReflectHttpOperationParameterAnalyzer { jsDocTags: matched.jsDocTags, ...schema, }; - else if (p.kind === "query" || p.kind === "headers") + else if (p.category === "query" || p.category === "headers") return { - kind: p.kind, + category: p.category, index: p.index, field: p.field ?? null, name: matched.name, type: matched.type, validate: - p.kind === "query" + p.category === "query" ? HttpQueryProgrammer.validate : HttpHeadersProgrammer.validate, description: matched.description, jsDocTags: matched.jsDocTags, ...schema, }; - else if (p.kind === "body") + else if (p.category === "body") return { - kind: p.kind, + category: p.category, index: p.index, encrypted: !!p.encrypted, contentType: p.contentType, @@ -229,19 +232,20 @@ export namespace ReflectHttpOperationParameterAnalyzer { const symbol: string = key.split(":")[0]; if (symbol.indexOf("__custom") !== -1) return analyzeCustomParameter(param); - const kind: IReflectHttpOperationParameter.IPreconfigured["kind"] | null = - getNestParamType(Number(symbol[0]) as RouteParamtypes); - if (kind === null) return null; - if (kind === "body") + const category: + | IReflectHttpOperationParameter.IPreconfigured["category"] + | null = getNestParamType(Number(symbol[0]) as RouteParamtypes); + if (category === null) return null; + if (category === "body") return { - kind: "body", + category: "body", index: param.index, field: param.data, contentType: "application/json", }; else return { - kind, + category, index: param.index, field: param.data, }; @@ -259,7 +263,7 @@ export namespace ReflectHttpOperationParameterAnalyzer { param.factory.name === "TypedFormDataBody" ) return { - kind: "body", + category: "body", index: param.index, encrypted: param.factory.name === "EncryptedBody", contentType: @@ -274,19 +278,19 @@ export namespace ReflectHttpOperationParameterAnalyzer { }; else if (param.factory.name === "TypedHeaders") return { - kind: "headers", + category: "headers", index: param.index, field: param.data, }; else if (param.factory.name === "TypedParam") return { - kind: "param", + category: "param", index: param.index, field: param.data, }; else if (param.factory.name === "TypedQuery") return { - kind: "query", + category: "query", index: param.index, field: undefined, }; diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index d4a9a1f3f..97d5b00f2 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -36,7 +36,7 @@ export namespace ReflectWebSocketOperationAnalyzer { ctx.name, ) ?? []) as IReflectWebSocketOperationParameter[] ).sort((a, b) => a.index - b.index); - if (preconfigured.find((p) => (p.kind === "acceptor") === undefined)) + if (preconfigured.find((p) => (p.category === "acceptor") === undefined)) errors.push("@WebSocketRoute.Acceptor() is essentially required"); if (preconfigured.length !== ctx.function.length) errors.push( @@ -67,17 +67,17 @@ export namespace ReflectWebSocketOperationAnalyzer { `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, ); else if ( - p.kind === "param" && + p.category === "param" && !(p as IReflectWebSocketOperationParameter.IParam).field?.length ) return errors.push(`@WebSocketRoute.Param() must have a field name.`); else if ( - p.kind === "acceptor" && + p.category === "acceptor" && matched.type?.typeArguments?.length !== 3 ) return `@WebSocketRoute.Acceptor() must have three type arguments.`; else if ( - p.kind === "driver" && + p.category === "driver" && matched.type?.typeArguments?.length !== 1 ) return errors.push( @@ -86,16 +86,20 @@ export namespace ReflectWebSocketOperationAnalyzer { // COMPLETE COMPOSITION imports.push(...matched.imports); - if (p.kind === "acceptor" || p.kind === "driver" || p.kind === "query") + if ( + p.category === "acceptor" || + p.category === "driver" || + p.category === "query" + ) return { ...p, name: matched.name, type: matched.type, }; - else if (p.kind === "param") + else if (p.category === "param") return { ...p, - kind: "param", + category: "param", field: p.field!, name: matched.name, type: matched.type, @@ -105,14 +109,15 @@ export namespace ReflectWebSocketOperationAnalyzer { } satisfies IReflectWebSocketOperationParameter.IParam; // UNKNOWN TYPE, MAYBE NEW FEATURE + console.log(p); return errors.push( - `@WebSocketRoute.${StringUtil.capitalize(p.kind)}() has not been supported yet. How about upgrading the nestia packages?`, + `@WebSocketRoute.${StringUtil.capitalize(p.category)}() has not been supported yet. How about upgrading the nestia packages?`, ); }) .filter((p): p is IReflectWebSocketOperationParameter => !!p); const fields: string[] = preconfigured - .filter((p) => p.kind === "param") + .filter((p) => p.category === "param") .map((p) => p.field ?? "") .filter((field): field is string => !!field?.length) .sort(); diff --git a/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts index 60228f398..dc42057b8 100644 --- a/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts +++ b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts @@ -119,7 +119,7 @@ export namespace TypedHttpRouteAnalyzer { metadata: cast( p, `parameter (name: ${JSON.stringify(p.name)})`, - p.kind === "body" && + p.category === "body" && (p.contentType === "application/json" || p.encrypted === true), ), })); diff --git a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts index 7e4477b65..38afcb3af 100644 --- a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts +++ b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts @@ -37,14 +37,17 @@ export namespace NestiaConfigLoader { if (fs.existsSync(path.resolve(file)) === false) throw new Error(`Unable to find "${file}" file.`); - const plugins: any[] = compilerOptions.plugins ?? []; - if (typia.is(plugins) === false) - throw new Error(`invalid "compilerOptions.plugins" data.`); - if ( - plugins.some((x: any) => x.transform === "@nestia/sdk/lib/transform") === - false - ) - plugins.push({ transform: "@nestia/sdk/lib/transform" }); + const plugins: any[] = [ + ...typia + .assert(compilerOptions.plugins ?? []) + .filter( + (x: any) => + x.transform !== "@nestia/core/lib/transform" && + x.transform !== "@nestia/sdk/lib/transform", + ), + { transform: "@nestia/core/lib/transform" }, + { transform: "@nestia/sdk/lib/transform" }, + ]; register({ emit: false, compilerOptions: { diff --git a/packages/sdk/src/generates/SwaggerGenerator.ts b/packages/sdk/src/generates/SwaggerGenerator.ts index 66f1355a6..35b2974b9 100644 --- a/packages/sdk/src/generates/SwaggerGenerator.ts +++ b/packages/sdk/src/generates/SwaggerGenerator.ts @@ -200,7 +200,7 @@ export namespace SwaggerGenerator { }): string => { let str: string = route.path; const filtered: ITypedHttpRouteParameter.IParam[] = route.parameters.filter( - (param) => param.kind === "param", + (param) => param.category === "param", ); for (const param of filtered) str = str.replace(`:${param.field}`, `{${param.field}}`); diff --git a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts index a65315022..f761ddc40 100644 --- a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts @@ -66,7 +66,7 @@ export namespace E2eFileProgrammer { (importer: ImportDictionary) => (route: ITypedHttpRoute) => { const headers = route.parameters.find( - (p) => p.kind === "headers" && p.field === undefined, + (p) => p.category === "headers" && p.field === undefined, ); const connection = headers ? ts.factory.createObjectLiteralExpression( @@ -110,7 +110,7 @@ export namespace E2eFileProgrammer { [ connection, ...route.parameters - .filter((p) => p.kind !== "headers") + .filter((p) => p.category !== "headers") .map((p) => ts.factory.createCallExpression( IdentifierFactory.access( diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index 2881416c0..4eca54eab 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -51,7 +51,7 @@ export namespace SdkAliasCollection { (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter): ts.TypeNode => { - const type: ts.TypeNode = name(param); + const type: ts.TypeNode = name(param.type); if (project.config.clone === true || project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( diff --git a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts index 5760fe096..88cd1c315 100644 --- a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts @@ -41,7 +41,7 @@ export namespace SdkHttpFunctionProgrammer { ), ), ...route.parameters - .filter((p) => p.kind !== "headers") + .filter((p) => p.category !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], @@ -140,7 +140,9 @@ export namespace SdkHttpFunctionProgrammer { )("path"), undefined, route.parameters - .filter((p) => p.kind === "param" || p.kind === "query") + .filter( + (p) => p.category === "param" || p.category === "query", + ) .map((p) => ts.factory.createIdentifier(p.name)), ), ), @@ -169,7 +171,7 @@ export namespace SdkHttpFunctionProgrammer { [ ts.factory.createIdentifier("connection"), ...route.parameters - .filter((p) => p.kind !== "headers") + .filter((p) => p.category !== "headers") .map((p) => ts.factory.createIdentifier(p.name)), ], ), @@ -182,7 +184,7 @@ export namespace SdkHttpFunctionProgrammer { return [ ...(config.assert ? route.parameters - .filter((p) => p.kind !== "headers") + .filter((p) => p.category !== "headers") .map((p) => ts.factory.createExpressionStatement( ts.factory.createCallExpression( diff --git a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts index 152e43dd1..7d59d49c9 100644 --- a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts @@ -181,13 +181,13 @@ export namespace SdkHttpNamespaceProgrammer { const g = { total: [ ...route.parameters.filter( - (param) => param.kind === "param" || param.kind === "query", + (param) => param.category === "param" || param.category === "query", ), ], query: route.parameters - .filter((param) => param.kind === "query") + .filter((param) => param.category === "query") .filter((param) => param.field !== null), - path: route.parameters.filter((param) => param.kind === "param"), + path: route.parameters.filter((param) => param.category === "param"), }; const out = (body: ts.ConciseBody) => constant("path")( diff --git a/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts index 57ff4cb6b..83b8bf598 100644 --- a/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts @@ -15,12 +15,12 @@ export namespace SdkHttpRouteProgrammer { (route: ITypedHttpRoute): ts.Statement[] => { const props = { headers: route.parameters - .filter((p) => p.kind === "headers") + .filter((p) => p.category === "headers") .find((p) => p.field === undefined), query: route.parameters - .filter((p) => p.kind === "query") + .filter((p) => p.category === "query") .find((p) => p.field === undefined), - input: route.parameters.find((p) => p.kind === "body"), + input: route.parameters.find((p) => p.category === "body"), }; return [ FilePrinter.description( @@ -40,7 +40,7 @@ export namespace SdkHttpRouteProgrammer { // PARMAETERS for (const p of route.parameters) { - if (p.kind === "headers") continue; + if (p.category === "headers") continue; const description: string | undefined = p.description ?? diff --git a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts index 481cd5ee6..900b1146b 100644 --- a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts @@ -105,7 +105,7 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createTypeReferenceNode( SdkImportWizard.IConnection(importer), route.parameters.some( - (p) => p.kind === "headers" && p.field === undefined, + (p) => p.category === "headers" && p.field === undefined, ) ? [ ts.factory.createTypeReferenceNode( @@ -116,7 +116,7 @@ export namespace SdkHttpSimulationProgrammer { ), ), ...route.parameters - .filter((p) => p.kind !== "headers") + .filter((p) => p.category !== "headers") .map((p) => ts.factory.createParameterDeclaration( [], @@ -177,7 +177,9 @@ export namespace SdkHttpSimulationProgrammer { (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.Statement[] => { - const parameters = route.parameters.filter((p) => p.kind !== "headers"); + const parameters = route.parameters.filter( + (p) => p.category !== "headers", + ); if (parameters.length === 0) return []; const typia = SdkImportWizard.typia(importer); @@ -211,7 +213,9 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createIdentifier("path"), undefined, route.parameters - .filter((p) => p.kind === "param" || p.kind === "query") + .filter( + (p) => p.category === "param" || p.category === "query", + ) .map((p) => ts.factory.createIdentifier(p.name)), ), ), @@ -233,8 +237,8 @@ export namespace SdkHttpSimulationProgrammer { (() => { const base = IdentifierFactory.access( ts.factory.createIdentifier("assert"), - )(p.kind); - if (p.kind !== "param") return base; + )(p.category); + if (p.category !== "param") return base; return ts.factory.createCallExpression(base, undefined, [ ts.factory.createStringLiteral(p.name), ]); diff --git a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts index e93086f36..93f14efff 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts @@ -75,13 +75,13 @@ export namespace SdkWebSocketNamespaceProgrammer { ); const acceptor: ITypedWebSocketRouteParameter.IAcceptor = - route.parameters.find((x) => x.kind === "acceptor")!; + route.parameters.find((x) => x.category === "acceptor")!; const query: ITypedWebSocketRouteParameter.IQuery | undefined = - route.parameters.find((x) => x.kind === "query"); + route.parameters.find((x) => x.category === "query"); declare( "Header", SdkAliasCollection.name( - (route.parameters.find((x) => x.kind === "header")?.type ?? + (route.parameters.find((x) => x.category === "header")?.type ?? acceptor.type.typeArguments?.[0])!, ), ); @@ -100,10 +100,10 @@ export namespace SdkWebSocketNamespaceProgrammer { const writePath = (route: ITypedWebSocketRoute): ts.VariableStatement => { const pathParams: ITypedWebSocketRouteParameter.IParam[] = route.parameters.filter( - (p) => p.kind === "param", + (p) => p.category === "param", ) as ITypedWebSocketRouteParameter.IParam[]; const query: ITypedWebSocketRouteParameter.IQuery | undefined = - route.parameters.find((p) => p.kind === "query"); + route.parameters.find((p) => p.category === "query"); const total: Array< | ITypedWebSocketRouteParameter.IParam | ITypedWebSocketRouteParameter.IQuery diff --git a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts index 98f7732de..46f3366c7 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts @@ -31,7 +31,7 @@ export namespace SdkWebSocketRouteProgrammer { (tag) => tag.name !== "param" || route.parameters - .filter((p) => p.kind === "param" || p.kind === "query") + .filter((p) => p.category === "param" || p.category === "query") .some((p) => p.name === tag.text?.[0]?.text), ); if (tags.length !== 0) { @@ -73,11 +73,11 @@ export namespace SdkWebSocketRouteProgrammer { ), ), ...route.parameters - .filter((p) => p.kind === "param" || p.kind === "query") + .filter((p) => p.category === "param" || p.category === "query") .map((p) => IdentifierFactory.parameter( p.name, - p.kind === "param" + p.category === "param" ? SdkAliasCollection.name(p.type) : ts.factory.createTypeReferenceNode(`${route.name}.Query`), ), @@ -152,7 +152,9 @@ export namespace SdkWebSocketRouteProgrammer { ), [], route.parameters - .filter((p) => p.kind === "param" || p.kind === "query") + .filter( + (p) => p.category === "param" || p.category === "query", + ) .map((x) => ts.factory.createIdentifier(x.name)), ), ), diff --git a/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts index d63d55c4a..930bae522 100644 --- a/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts +++ b/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts @@ -17,7 +17,7 @@ export namespace SwaggerOperationComposer { }): OpenApi.IOperation => { // FIND REQUEST BODY const body: ITypedHttpRouteParameter.IBody | undefined = - props.route.parameters.find((param) => param.kind === "body"); + props.route.parameters.find((param) => param.category === "body"); // COMPOSE TAGS const tags: Set = new Set([ diff --git a/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts index 1d5aaed9a..6862ce5e9 100644 --- a/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts +++ b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts @@ -19,11 +19,11 @@ export namespace SwaggerOperationParameterComposer { export const compose = ( props: IProps, ): OpenApi.IOperation.IParameter[] => - props.parameter.kind === "body" + props.parameter.category === "body" ? [] - : props.parameter.kind === "param" + : props.parameter.category === "param" ? [path({ ...props, parameter: props.parameter })] - : props.parameter.kind === "query" + : props.parameter.category === "query" ? query({ ...props, parameter: props.parameter }) : header({ ...props, parameter: props.parameter }); @@ -94,7 +94,7 @@ export namespace SwaggerOperationParameterComposer { ): OpenApi.IOperation.IParameter[] => { const param: OpenApi.IOperation.IParameter = { name: props.parameter.field ?? props.parameter.name, - in: props.parameter.kind === "query" ? "query" : "header", + in: props.parameter.category === "query" ? "query" : "header", schema: props.schema, ...SwaggerDescriptionComposer.compose({ description: @@ -142,7 +142,7 @@ export namespace SwaggerOperationParameterComposer { } return { name: p.key.constants[0].values[0].value as string, - in: props.parameter.kind === "query" ? "query" : "header", + in: props.parameter.category === "query" ? "query" : "header", schema: json.schemas[0], required: p.value.isRequired(), ...SwaggerDescriptionComposer.compose({ diff --git a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts index 2443e447c..e3e70bb56 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts @@ -28,8 +28,8 @@ export namespace IReflectHttpOperationParameter { export interface IQuery extends IBase<"query"> { field: string | null; } - interface IBase { - kind: Kind; + interface IBase { + category: Category; name: string; index: number; type: IReflectType; @@ -73,8 +73,8 @@ export namespace IReflectHttpOperationParameter { export interface IQuery extends IBase<"query"> { field?: string; } - interface IBase { - kind: Kind; + interface IBase { + category: Category; index: number; } } diff --git a/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts index 6a460082b..1d50fd749 100644 --- a/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts +++ b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts @@ -17,8 +17,8 @@ export namespace IReflectWebSocketOperationParameter { export interface IParam extends IBase<"param"> { field: string; } - interface IBase { - kind: Kind; + interface IBase { + category: Category; name: string; index: number; type: IReflectType; @@ -31,7 +31,7 @@ export namespace IReflectWebSocketOperationParameter { * @internal */ export interface IPreconfigured { - kind: "acceptor" | "driver" | "header" | "param" | "query"; + category: "acceptor" | "driver" | "header" | "param" | "query"; index: number; field?: string; } diff --git a/packages/sdk/src/structures/ITypedHttpRouteParameter.ts b/packages/sdk/src/structures/ITypedHttpRouteParameter.ts index 8ee1c1e60..018be8f29 100644 --- a/packages/sdk/src/structures/ITypedHttpRouteParameter.ts +++ b/packages/sdk/src/structures/ITypedHttpRouteParameter.ts @@ -27,8 +27,8 @@ export namespace ITypedHttpRouteParameter { field: string | null; } - interface IBase { - kind: Kind; + interface IBase { + category: Category; name: string; index: number; type: IReflectType; diff --git a/test/features/all/src/api/functional/all/index.ts b/test/features/all/src/api/functional/all/index.ts index 9bc5cc256..59bfcb163 100644 --- a/test/features/all/src/api/functional/all/index.ts +++ b/test/features/all/src/api/functional/all/index.ts @@ -4,21 +4,16 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; /** - * Store an article. - * - * Create an article, and returns it. - * * @param input Content to store * @returns Newly archived article * @author Samchon * @warning This is an fake API - * * @controller AllController.store * @path POST /all * @nestia Generated by Nestia - https://github.com/samchon/nestia @@ -26,7 +21,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -44,7 +39,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -58,7 +53,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/all"; diff --git a/test/features/all/src/api/functional/health/index.ts b/test/features/all/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/all/src/api/functional/health/index.ts +++ b/test/features/all/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/all/src/api/functional/performance/index.ts b/test/features/all/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/all/src/api/functional/performance/index.ts +++ b/test/features/all/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/all/swagger.json b/test/features/all/swagger.json index d532be12b..d00c79aa5 100644 --- a/test/features/all/swagger.json +++ b/test/features/all/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.9.0-dev.20240728", + "version": "3.11.0-dev.20240811", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -31,7 +31,7 @@ "required": true }, "responses": { - "201": { + "200": { "description": "Newly archived article", "content": { "application/json": { @@ -41,9 +41,7 @@ } } } - }, - "summary": "Store an article", - "description": "Store an article.\n\nCreate an article, and returns it." + } } }, "/health": { @@ -52,7 +50,9 @@ "parameters": [], "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } } } @@ -63,7 +63,6 @@ "parameters": [], "responses": { "200": { - "description": "", "content": { "application/json": { "schema": { @@ -78,9 +77,17 @@ }, "components": { "schemas": { - "IBbsArticle.IStore": { + "IBbsArticle": { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, "title": { "type": "string", "minLength": 3, @@ -97,6 +104,8 @@ } }, "required": [ + "id", + "created_at", "title", "body", "files" @@ -139,17 +148,9 @@ "url" ] }, - "IBbsArticle": { + "IBbsArticle.IStore": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, "title": { "type": "string", "minLength": 3, @@ -166,8 +167,6 @@ } }, "required": [ - "id", - "created_at", "title", "body", "files" @@ -309,9 +308,7 @@ }, "securitySchemes": { "bearer": { - "type": "apiKey", - "in": "header", - "name": "Authorization" + "type": "apiKey" } } }, diff --git a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts index 6490e85c1..e2b4ce073 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/x/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/x/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts index bfe1c0912..939446671 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -21,16 +21,24 @@ export * as comments from "./comments"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -41,21 +49,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/x/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/x/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -67,12 +65,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -85,7 +92,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -93,12 +100,9 @@ export namespace at { } /** - * Store a new article. - * * @param section Section code * @param input Content to store * @returns Newly archived article - * * @controller BbsArticlesController.store * @path POST /x/bbs/:section/articles * @nestia Generated by Nestia - https://github.com/samchon/nestia @@ -107,7 +111,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -125,7 +129,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -139,7 +143,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -147,13 +151,10 @@ export namespace store { } /** - * Update an article. - * * @param section Section code * @param id Target article ID * @param input Content to update * @returns Updated content - * * @controller BbsArticlesController.update * @path PUT /x/bbs/:section/articles/:id * @nestia Generated by Nestia - https://github.com/samchon/nestia @@ -163,7 +164,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -181,7 +182,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -195,7 +196,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts index 5bee01f13..69d8a275b 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts @@ -7,6 +7,8 @@ import type { IConnection } from "@nestia/fetcher"; import { WebSocketConnector } from "tgrid"; import type { Driver } from "tgrid"; +import type { WebSocketAcceptor } from "tgrid/lib/protocols/web/WebSocketAcceptor"; +import type { Driver } from "tgrid/lib/typings/Driver"; import type { ICalculator } from "../../../structures/ICalculator"; import type { IListener } from "../../../structures/IListener"; @@ -41,8 +43,8 @@ export namespace connect { driver: Driver; }; export type Header = IPrecision; - export type Provider = IListener; - export type Listener = ICalculator; + export type Provider = ICalculator; + export type Listener = IListener; export const path = () => "/x/calculate"; } diff --git a/test/features/app-globalPrefix/src/api/functional/x/health/index.ts b/test/features/app-globalPrefix/src/api/functional/x/health/index.ts index e5cb19fb9..43c707ee8 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/health/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/x/health"; diff --git a/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts b/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts index 9cddf48b3..8930f8901 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../../structures/IPerformance"; * @path GET /x/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/x/performance"; diff --git a/test/features/app-globalPrefix/swagger.json b/test/features/app-globalPrefix/swagger.json index e219348f3..4cf2ddf94 100644 --- a/test/features/app-globalPrefix/swagger.json +++ b/test/features/app-globalPrefix/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/x/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/x/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/x/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/x/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/x/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240811","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/x/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/x/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/x/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app/src/api/functional/bbs/articles/comments/index.ts b/test/features/app/src/api/functional/bbs/articles/comments/index.ts index 78b01174a..18c7e901b 100644 --- a/test/features/app/src/api/functional/bbs/articles/comments/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/comments/index.ts @@ -135,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Resolved; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -185,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Resolved; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { diff --git a/test/features/app/src/api/functional/bbs/articles/index.ts b/test/features/app/src/api/functional/bbs/articles/index.ts index 74df03a7d..31f355288 100644 --- a/test/features/app/src/api/functional/bbs/articles/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/index.ts @@ -129,7 +129,7 @@ export async function store( ); } export namespace store { - export type Input = Resolved; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -182,7 +182,7 @@ export async function update( ); } export namespace update { - export type Input = Resolved; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts new file mode 100644 index 000000000..2d6c92f3f --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_at.ts @@ -0,0 +1,15 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; + +export const test_api_bbs_articles_at = async (connection: api.IConnection) => { + const output: Primitive = await api.functional.bbs.articles.at( + connection, + typia.random(), + typia.random>(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts new file mode 100644 index 000000000..4b173be66 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts @@ -0,0 +1,19 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsComment } from "../../../../api/structures/IBbsComment"; + +export const test_api_bbs_articles_comments_at = async ( + connection: api.IConnection, +) => { + const output: Primitive = + await api.functional.bbs.articles.comments.at( + connection, + typia.random(), + typia.random>(), + typia.random>(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_index.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_index.ts new file mode 100644 index 000000000..c6fc4fa69 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_index.ts @@ -0,0 +1,20 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsComment } from "../../../../api/structures/IBbsComment"; +import type { IPage } from "../../../../api/structures/IPage"; + +export const test_api_bbs_articles_comments_index = async ( + connection: api.IConnection, +) => { + const output: Primitive> = + await api.functional.bbs.articles.comments.index( + connection, + typia.random(), + typia.random>(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_store.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_store.ts new file mode 100644 index 000000000..54fcb409f --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_store.ts @@ -0,0 +1,19 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsComment } from "../../../../api/structures/IBbsComment"; + +export const test_api_bbs_articles_comments_store = async ( + connection: api.IConnection, +) => { + const output: Primitive = + await api.functional.bbs.articles.comments.store( + connection, + typia.random(), + typia.random>(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_update.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_update.ts new file mode 100644 index 000000000..3c23d742b --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_update.ts @@ -0,0 +1,20 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsComment } from "../../../../api/structures/IBbsComment"; + +export const test_api_bbs_articles_comments_update = async ( + connection: api.IConnection, +) => { + const output: Primitive = + await api.functional.bbs.articles.comments.update( + connection, + typia.random(), + typia.random>(), + typia.random>(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts new file mode 100644 index 000000000..04a597311 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_index.ts @@ -0,0 +1,18 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; +import type { IPage } from "../../../../api/structures/IPage"; + +export const test_api_bbs_articles_index = async ( + connection: api.IConnection, +) => { + const output: Primitive> = + await api.functional.bbs.articles.index( + connection, + typia.random(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts new file mode 100644 index 000000000..4508b92e1 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_store.ts @@ -0,0 +1,17 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; + +export const test_api_bbs_articles_store = async ( + connection: api.IConnection, +) => { + const output: Primitive = + await api.functional.bbs.articles.store( + connection, + typia.random(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts new file mode 100644 index 000000000..80c5bfc49 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_update.ts @@ -0,0 +1,19 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; +import type { Format } from "typia/lib/tags/Format"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; + +export const test_api_bbs_articles_update = async ( + connection: api.IConnection, +) => { + const output: Primitive = + await api.functional.bbs.articles.update( + connection, + typia.random(), + typia.random>(), + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_health_get.ts b/test/features/app/src/test/features/api/automated/test_api_health_get.ts new file mode 100644 index 000000000..8766b1129 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_health_get.ts @@ -0,0 +1,8 @@ +import typia from "typia"; + +import api from "../../../../api"; + +export const test_api_health_get = async (connection: api.IConnection) => { + const output = await api.functional.health.get(connection); + typia.assert(output); +}; diff --git a/test/features/app/src/test/features/api/automated/test_api_performance_get.ts b/test/features/app/src/test/features/api/automated/test_api_performance_get.ts new file mode 100644 index 000000000..54cdfd218 --- /dev/null +++ b/test/features/app/src/test/features/api/automated/test_api_performance_get.ts @@ -0,0 +1,11 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; + +import api from "../../../../api"; +import type { IPerformance } from "../../../../api/structures/IPerformance"; + +export const test_api_performance_get = async (connection: api.IConnection) => { + const output: Primitive = + await api.functional.performance.get(connection); + typia.assert(output); +}; diff --git a/test/features/app/swagger.json b/test/features/app/swagger.json new file mode 100644 index 000000000..01421092f --- /dev/null +++ b/test/features/app/swagger.json @@ -0,0 +1,842 @@ +{ + "openapi": "3.1.0", + "servers": [ + { + "url": "https://github.com/samchon/nestia", + "description": "insert your server url" + } + ], + "info": { + "version": "3.11.0-dev.20240811", + "title": "@samchon/nestia-test", + "description": "Test program of Nestia", + "license": { + "name": "MIT" + } + }, + "paths": { + "/health": { + "get": { + "tags": [], + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": {} + } + } + } + } + }, + "/performance": { + "get": { + "tags": [], + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IPerformance" + } + } + } + } + } + } + }, + "/bbs/{section}/articles": { + "get": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "page", + "in": "query", + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, + "required": false + }, + { + "name": "limit", + "in": "query", + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, + "required": false + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IPageIBbsArticle.ISummary" + } + } + } + } + } + }, + "post": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true, + "description": " Section code" + } + ], + "requestBody": { + "description": "Content to store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsArticle.IStore" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Newly archived article", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsArticle" + } + } + } + } + } + } + }, + "/bbs/{section}/articles/{id}": { + "get": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsArticle" + } + } + } + } + } + }, + "put": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true, + "description": " Section code" + }, + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "description": " Target article ID" + } + ], + "requestBody": { + "description": "Content to update", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsArticle.IStore" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Updated content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsArticle" + } + } + } + } + } + } + }, + "/bbs/{section}/articles/{articleId}/comments": { + "get": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "articleId", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "page", + "in": "query", + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, + "required": false + }, + { + "name": "limit", + "in": "query", + "schema": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, + "required": false + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IPageIBbsComment" + } + } + } + } + } + }, + "post": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "articleId", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsComment.IStore" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsComment" + } + } + } + } + } + } + }, + "/bbs/{section}/articles/{articleId}/comments/{id}": { + "get": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "articleId", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsComment" + } + } + } + } + } + }, + "put": { + "tags": [], + "parameters": [ + { + "name": "section", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "articleId", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsComment.IStore" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IBbsComment" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "IPerformance": { + "type": "object", + "properties": { + "cpu": { + "$ref": "#/components/schemas/process.global.NodeJS.CpuUsage" + }, + "memory": { + "$ref": "#/components/schemas/process.global.NodeJS.MemoryUsage" + }, + "resource": { + "$ref": "#/components/schemas/process.global.NodeJS.ResourceUsage" + } + }, + "required": [ + "cpu", + "memory", + "resource" + ], + "description": "Performance info." + }, + "process.global.NodeJS.CpuUsage": { + "type": "object", + "properties": { + "user": { + "type": "number" + }, + "system": { + "type": "number" + } + }, + "required": [ + "user", + "system" + ] + }, + "process.global.NodeJS.MemoryUsage": { + "type": "object", + "properties": { + "rss": { + "type": "number" + }, + "heapTotal": { + "type": "number" + }, + "heapUsed": { + "type": "number" + }, + "external": { + "type": "number" + }, + "arrayBuffers": { + "type": "number" + } + }, + "required": [ + "rss", + "heapTotal", + "heapUsed", + "external", + "arrayBuffers" + ] + }, + "process.global.NodeJS.ResourceUsage": { + "type": "object", + "properties": { + "fsRead": { + "type": "number" + }, + "fsWrite": { + "type": "number" + }, + "involuntaryContextSwitches": { + "type": "number" + }, + "ipcReceived": { + "type": "number" + }, + "ipcSent": { + "type": "number" + }, + "majorPageFault": { + "type": "number" + }, + "maxRSS": { + "type": "number" + }, + "minorPageFault": { + "type": "number" + }, + "sharedMemorySize": { + "type": "number" + }, + "signalsCount": { + "type": "number" + }, + "swappedOut": { + "type": "number" + }, + "systemCPUTime": { + "type": "number" + }, + "unsharedDataSize": { + "type": "number" + }, + "unsharedStackSize": { + "type": "number" + }, + "userCPUTime": { + "type": "number" + }, + "voluntaryContextSwitches": { + "type": "number" + } + }, + "required": [ + "fsRead", + "fsWrite", + "involuntaryContextSwitches", + "ipcReceived", + "ipcSent", + "majorPageFault", + "maxRSS", + "minorPageFault", + "sharedMemorySize", + "signalsCount", + "swappedOut", + "systemCPUTime", + "unsharedDataSize", + "unsharedStackSize", + "userCPUTime", + "voluntaryContextSwitches" + ] + }, + "IPageIBbsArticle.ISummary": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IBbsArticle.ISummary" + } + }, + "pagination": { + "$ref": "#/components/schemas/IPage.IPagination" + } + }, + "required": [ + "data", + "pagination" + ] + }, + "IBbsArticle.ISummary": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "section": { + "type": "string" + }, + "writer": { + "type": "string" + }, + "title": { + "type": "string", + "minLength": 3, + "maxLength": 50 + }, + "created_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "id", + "section", + "writer", + "title", + "created_at" + ] + }, + "IPage.IPagination": { + "type": "object", + "properties": { + "current": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "records": { + "type": "integer" + }, + "pages": { + "type": "integer" + } + }, + "required": [ + "current", + "limit", + "records", + "pages" + ], + "description": "Page information." + }, + "IPage.IRequest": { + "type": "object", + "properties": { + "page": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ] + } + }, + "description": "Page request data" + }, + "IBbsArticle": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "section": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "title": { + "type": "string", + "minLength": 3, + "maxLength": 50 + }, + "body": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } + } + }, + "required": [ + "id", + "section", + "created_at", + "title", + "body", + "files" + ] + }, + "IAttachmentFile": { + "type": "object", + "properties": { + "name": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "maxLength": 255 + } + ] + }, + "extension": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "maxLength": 8 + } + ] + }, + "url": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uri" + } + ] + } + }, + "required": [ + "name", + "extension", + "url" + ] + }, + "IBbsArticle.IStore": { + "type": "object", + "properties": { + "title": { + "type": "string", + "minLength": 3, + "maxLength": 50 + }, + "body": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } + } + }, + "required": [ + "title", + "body", + "files" + ] + }, + "IPageIBbsComment": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IBbsComment" + } + }, + "pagination": { + "$ref": "#/components/schemas/IPage.IPagination" + } + }, + "required": [ + "data", + "pagination" + ] + }, + "IBbsComment": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "body": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } + } + }, + "required": [ + "id", + "created_at", + "body", + "files" + ] + }, + "IBbsComment.IStore": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } + } + }, + "required": [ + "body", + "files" + ] + } + }, + "securitySchemes": { + "bearer": { + "type": "apiKey" + } + } + }, + "tags": [], + "x-samchon-emended": true +} \ No newline at end of file diff --git a/test/package.json b/test/package.json index 5512069d2..6c91b7528 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@samchon/nestia-test", - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240812", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "../packages/sdk/nestia-sdk-3.11.0-dev.20240811.tgz", + "@nestia/sdk": "^3.11.0-dev.20240812", "@nestjs/swagger": "^7.1.2", "@samchon/openapi": "^0.4.3", "@types/express": "^4.17.17", @@ -40,9 +40,9 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "../packages/core/nestia-core-3.11.0-dev.20240811.tgz", + "@nestia/core": "^3.11.0-dev.20240812", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "../packages/fetcher/nestia-fetcher-3.11.0-dev.20240811.tgz", + "@nestia/fetcher": "^3.11.0-dev.20240812", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/platform-express": "^10.3.5", From 832170fc2396b819eb00ba4c427513404264b577 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 11:37:00 +0900 Subject: [PATCH 06/21] Fixed {@link T} comment parsing bug --- package.json | 2 +- packages/core/package.json | 6 +- packages/fetcher/package.json | 2 +- packages/sdk/package.json | 10 +-- .../src/transformers/SdkMetadataProgrammer.ts | 50 +++++------ .../sdk/src/transformers/SdkTransformer.ts | 16 ++++ packages/sdk/tsconfig.shopping.json | 85 +++++++++++++++++++ test/package.json | 8 +- 8 files changed, 138 insertions(+), 41 deletions(-) create mode 100644 packages/sdk/tsconfig.shopping.json diff --git a/package.json b/package.json index 912d2474e..f6341c7ed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@nestia/station", - "version": "3.11.0-dev.20240813-2", + "version": "3.11.0-dev.20240813-11", "description": "Nestia station", "scripts": { "build": "node build/index.js", diff --git a/packages/core/package.json b/packages/core/package.json index b6de9babe..394c1dcb3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "3.11.0-dev.20240813-2", + "version": "3.11.0-dev.20240813-10", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^3.11.0-dev.20240813-2", + "@nestia/fetcher": "^3.11.0-dev.20240813-10", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^0.4.3", @@ -53,7 +53,7 @@ "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=3.11.0-dev.20240813-2", + "@nestia/fetcher": ">=3.11.0-dev.20240813-10", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 8cfffc9c3..1fc78f6ec 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "3.11.0-dev.20240813-2", + "version": "3.11.0-dev.20240813-10", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index ae2221d98..c561b4f9c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "3.11.0-dev.20240813-2", + "version": "3.11.0-dev.20240813-10", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "^3.11.0-dev.20240813-2", - "@nestia/fetcher": "^3.11.0-dev.20240813-2", + "@nestia/core": "^3.11.0-dev.20240813-10", + "@nestia/fetcher": "^3.11.0-dev.20240813-10", "@samchon/openapi": "^0.4.3", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", @@ -48,8 +48,8 @@ "typia": "^6.8.0-dev.20240812" }, "peerDependencies": { - "@nestia/core": ">=3.11.0-dev.20240813-2", - "@nestia/fetcher": ">=3.11.0-dev.20240813-2", + "@nestia/core": ">=3.11.0-dev.20240813-10", + "@nestia/fetcher": ">=3.11.0-dev.20240813-10", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts index d9caf307d..ad7a3c00c 100644 --- a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts +++ b/packages/sdk/src/transformers/SdkMetadataProgrammer.ts @@ -17,35 +17,31 @@ export namespace SdkMetadataProgrammer { context: ISdkTransformerContext; generics: WeakMap; node: ts.MethodDeclaration; + symbol: ts.Symbol | undefined; } - export const write = (p: IProps): IOperationMetadata => { - const symbol: ts.Symbol | undefined = p.context.checker.getSymbolAtLocation( - p.node, - ); - const signature: ts.Signature | undefined = - p.context.checker.getSignatureFromDeclaration(p.node); - return { - parameters: p.node.parameters.map((parameter, index) => - writeParameter({ - context: p.context, - generics: p.generics, - parameter, - index, - }), - ), - success: writeResponse({ + export const write = (p: IProps): IOperationMetadata => ({ + parameters: p.node.parameters.map((parameter, index) => + writeParameter({ context: p.context, generics: p.generics, - type: getReturnType({ - checker: p.context.checker, - signature, - }), + parameter, + index, }), - exceptions: {}, // @todo - description: (symbol && CommentFactory.description(symbol)) ?? null, - jsDocTags: signature?.getJsDocTags() ?? [], - }; - }; + ), + success: writeResponse({ + context: p.context, + generics: p.generics, + type: getReturnType({ + checker: p.context.checker, + type: p.node.type + ? p.context.checker.getTypeFromTypeNode(p.node.type) + : null, + }), + }), + exceptions: {}, // @todo + jsDocTags: p.symbol?.getJsDocTags() ?? [], + description: p.symbol ? CommentFactory.description(p.symbol) ?? null : null, + }); const writeParameter = (props: { context: ISdkTransformerContext; @@ -164,9 +160,9 @@ export namespace SdkMetadataProgrammer { const getReturnType = (p: { checker: ts.TypeChecker; - signature: ts.Signature | undefined; + type: ts.Type | null; }): ts.Type | null => { - const type: ts.Type | null = p.signature?.getReturnType() ?? null; + const type: ts.Type | null = p.type; if (type === null) return null; else if (type.symbol?.name === "Promise") { const generic: readonly ts.Type[] = p.checker.getTypeArguments( diff --git a/packages/sdk/src/transformers/SdkTransformer.ts b/packages/sdk/src/transformers/SdkTransformer.ts index e2f6bbf22..b3de45ac5 100644 --- a/packages/sdk/src/transformers/SdkTransformer.ts +++ b/packages/sdk/src/transformers/SdkTransformer.ts @@ -82,6 +82,20 @@ export namespace SdkTransformer { props.context.checker, props.node, ); + + // TO AVOID COMMENT COMPILATION BUG + const symbolDict: Map = new Map(); + const classType: ts.InterfaceType = props.context.checker.getTypeAtLocation( + props.node, + ) as ts.InterfaceType; + for (const symbol of classType.getProperties()) { + const declaration: ts.Declaration | undefined = (symbol.declarations || + [])[0]; + if (!declaration || !ts.isMethodDeclaration(declaration)) continue; + const identifier = declaration.name; + if (!ts.isIdentifier(identifier)) continue; + symbolDict.set(identifier.escapedText.toString(), symbol); + } return ts.factory.updateClassDeclaration( props.node, props.node.modifiers, @@ -95,6 +109,7 @@ export namespace SdkTransformer { generics, class: props.node, node: m, + symbol: symbolDict.get(m.name.getText()), }) : m, ), @@ -107,6 +122,7 @@ export namespace SdkTransformer { class: ts.ClassDeclaration; generics: WeakMap; node: ts.MethodDeclaration; + symbol: ts.Symbol | undefined; }): ts.MethodDeclaration => { const decorators: readonly ts.Decorator[] | undefined = ts.getDecorators ? ts.getDecorators(props.node) diff --git a/packages/sdk/tsconfig.shopping.json b/packages/sdk/tsconfig.shopping.json new file mode 100644 index 000000000..549143945 --- /dev/null +++ b/packages/sdk/tsconfig.shopping.json @@ -0,0 +1,85 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": [ + "DOM", + "ES2015" + ], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "D:/github/samchon/shopping-backend@master/node_modules/@nestia/sdk/lib", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + "types": [ + "node", + "reflect-metadata" + ], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "stripInternal": true, + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "plugins": [ + { + "transform": "typia/lib/transform", + "functional": true, + } + ], + "newLine": "LF", + }, + "include": ["src"] +} diff --git a/test/package.json b/test/package.json index 6c91b7528..49e2e0a64 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@samchon/nestia-test", - "version": "3.11.0-dev.20240812", + "version": "3.11.0-dev.20240813-10", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "^3.11.0-dev.20240812", + "@nestia/sdk": "^3.11.0-dev.20240813-10", "@nestjs/swagger": "^7.1.2", "@samchon/openapi": "^0.4.3", "@types/express": "^4.17.17", @@ -40,9 +40,9 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "^3.11.0-dev.20240812", + "@nestia/core": "^3.11.0-dev.20240813-10", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "^3.11.0-dev.20240812", + "@nestia/fetcher": "^3.11.0-dev.20240813-10", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/platform-express": "^10.3.5", From 5a9a6253e599886707ec69fe93cc686890a27922 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 12:02:08 +0900 Subject: [PATCH 07/21] Fix websocket SDK mis-importing problem. --- packages/core/package.json | 6 +++--- packages/fetcher/package.json | 2 +- packages/sdk/package.json | 10 +++++----- .../analyses/ReflectWebSocketOperationAnalyzer.ts | 11 ++++++++++- .../sdk/src/generates/internal/ImportDictionary.ts | 2 +- .../internal/SdkWebSocketRouteProgrammer.ts | 12 +++++------- test/features/all/src/api/functional/all/index.ts | 5 +++++ test/features/all/swagger.json | 4 +++- .../src/api/functional/x/bbs/articles/index.ts | 6 ++++++ .../src/api/functional/x/calculate/index.ts | 2 -- test/features/app-globalPrefix/swagger.json | 2 +- .../app/src/api/functional/bbs/articles/index.ts | 6 ++++++ test/features/app/swagger.json | 6 +++++- test/package.json | 8 ++++---- 14 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 394c1dcb3..d70262b34 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "3.11.0-dev.20240813-10", + "version": "3.11.0-dev.20240813-11", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^3.11.0-dev.20240813-10", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240813-11.tgz", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^0.4.3", @@ -53,7 +53,7 @@ "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=3.11.0-dev.20240813-10", + "@nestia/fetcher": ">=3.11.0-dev.20240813-11", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 1fc78f6ec..6918b17f9 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "3.11.0-dev.20240813-10", + "version": "3.11.0-dev.20240813-11", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c561b4f9c..49f9b936a 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "3.11.0-dev.20240813-10", + "version": "3.11.0-dev.20240813-11", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "^3.11.0-dev.20240813-10", - "@nestia/fetcher": "^3.11.0-dev.20240813-10", + "@nestia/core": "../core/nestia-core-3.11.0-dev.20240813-11.tgz", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240813-11.tgz", "@samchon/openapi": "^0.4.3", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", @@ -48,8 +48,8 @@ "typia": "^6.8.0-dev.20240812" }, "peerDependencies": { - "@nestia/core": ">=3.11.0-dev.20240813-10", - "@nestia/fetcher": ">=3.11.0-dev.20240813-10", + "@nestia/core": ">=3.11.0-dev.20240813-11", + "@nestia/fetcher": ">=3.11.0-dev.20240813-11", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index 97d5b00f2..30f8d0ada 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -85,7 +85,16 @@ export namespace ReflectWebSocketOperationAnalyzer { ); // COMPLETE COMPOSITION - imports.push(...matched.imports); + imports.push( + ...matched.imports.filter( + (i) => + !( + i.file.includes("tgrid/lib") && + (i.file.endsWith("Driver.d.ts") || + i.file.endsWith("WebSocketAcceptor.d.ts")) + ), + ), + ); if ( p.category === "acceptor" || p.category === "driver" || diff --git a/packages/sdk/src/generates/internal/ImportDictionary.ts b/packages/sdk/src/generates/internal/ImportDictionary.ts index a08123925..637d39fe5 100644 --- a/packages/sdk/src/generates/internal/ImportDictionary.ts +++ b/packages/sdk/src/generates/internal/ImportDictionary.ts @@ -16,7 +16,7 @@ export class ImportDictionary { public external(props: ImportDictionary.IExternalProps): string { const composition: IComposition = this.components_.take( - new Pair(props.library, props.type), + new Pair(`node_modules/${props.library}`, props.type), () => ({ location: `node_modules/${props.library}`, elements: new HashSet(), diff --git a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts index 46f3366c7..226d9c9da 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketRouteProgrammer.ts @@ -164,13 +164,11 @@ export namespace SdkWebSocketRouteProgrammer { ), local("driver")( ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier( - importer.external({ - type: true, - library: "tgrid", - instance: "Driver", - }), - ), + importer.external({ + type: true, + library: "tgrid", + instance: "Driver", + }), [ts.factory.createTypeReferenceNode(`${route.name}.Listener`)], ), )( diff --git a/test/features/all/src/api/functional/all/index.ts b/test/features/all/src/api/functional/all/index.ts index 59bfcb163..caf3f3615 100644 --- a/test/features/all/src/api/functional/all/index.ts +++ b/test/features/all/src/api/functional/all/index.ts @@ -10,10 +10,15 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; /** + * Store an article. + * + * Create an article, and returns it. + * * @param input Content to store * @returns Newly archived article * @author Samchon * @warning This is an fake API + * * @controller AllController.store * @path POST /all * @nestia Generated by Nestia - https://github.com/samchon/nestia diff --git a/test/features/all/swagger.json b/test/features/all/swagger.json index d00c79aa5..2fe5a9bfa 100644 --- a/test/features/all/swagger.json +++ b/test/features/all/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-11", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -17,6 +17,8 @@ "paths": { "/all": { "post": { + "summary": "Store an article", + "description": "Store an article.\n\nCreate an article, and returns it.", "tags": [], "parameters": [], "requestBody": { diff --git a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts index 939446671..ba572c1e9 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts @@ -100,9 +100,12 @@ export namespace at { } /** + * Store a new article. + * * @param section Section code * @param input Content to store * @returns Newly archived article + * * @controller BbsArticlesController.store * @path POST /x/bbs/:section/articles * @nestia Generated by Nestia - https://github.com/samchon/nestia @@ -151,10 +154,13 @@ export namespace store { } /** + * Update an article. + * * @param section Section code * @param id Target article ID * @param input Content to update * @returns Updated content + * * @controller BbsArticlesController.update * @path PUT /x/bbs/:section/articles/:id * @nestia Generated by Nestia - https://github.com/samchon/nestia diff --git a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts index 69d8a275b..634451752 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts @@ -7,8 +7,6 @@ import type { IConnection } from "@nestia/fetcher"; import { WebSocketConnector } from "tgrid"; import type { Driver } from "tgrid"; -import type { WebSocketAcceptor } from "tgrid/lib/protocols/web/WebSocketAcceptor"; -import type { Driver } from "tgrid/lib/typings/Driver"; import type { ICalculator } from "../../../structures/ICalculator"; import type { IListener } from "../../../structures/IListener"; diff --git a/test/features/app-globalPrefix/swagger.json b/test/features/app-globalPrefix/swagger.json index 4cf2ddf94..53875f796 100644 --- a/test/features/app-globalPrefix/swagger.json +++ b/test/features/app-globalPrefix/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240811","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/x/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/x/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/x/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/x/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/x/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/x/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/x/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app/src/api/functional/bbs/articles/index.ts b/test/features/app/src/api/functional/bbs/articles/index.ts index 31f355288..f91291ef9 100644 --- a/test/features/app/src/api/functional/bbs/articles/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/index.ts @@ -100,9 +100,12 @@ export namespace at { } /** + * Store a new article. + * * @param section Section code * @param input Content to store * @returns Newly archived article + * * @controller BbsArticlesController.store * @path POST /bbs/:section/articles * @nestia Generated by Nestia - https://github.com/samchon/nestia @@ -151,10 +154,13 @@ export namespace store { } /** + * Update an article. + * * @param section Section code * @param id Target article ID * @param input Content to update * @returns Updated content + * * @controller BbsArticlesController.update * @path PUT /bbs/:section/articles/:id * @nestia Generated by Nestia - https://github.com/samchon/nestia diff --git a/test/features/app/swagger.json b/test/features/app/swagger.json index 01421092f..00208379d 100644 --- a/test/features/app/swagger.json +++ b/test/features/app/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.11.0-dev.20240811", + "version": "3.11.0-dev.20240813-11", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -101,6 +101,8 @@ } }, "post": { + "summary": "Store a new article", + "description": "Store a new article.", "tags": [], "parameters": [ { @@ -173,6 +175,8 @@ } }, "put": { + "summary": "Update an article", + "description": "Update an article.", "tags": [], "parameters": [ { diff --git a/test/package.json b/test/package.json index 49e2e0a64..606d8341f 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@samchon/nestia-test", - "version": "3.11.0-dev.20240813-10", + "version": "3.11.0-dev.20240813-11", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "^3.11.0-dev.20240813-10", + "@nestia/sdk": "../packages/sdk/nestia-sdk-3.11.0-dev.20240813-11.tgz", "@nestjs/swagger": "^7.1.2", "@samchon/openapi": "^0.4.3", "@types/express": "^4.17.17", @@ -40,9 +40,9 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "^3.11.0-dev.20240813-10", + "@nestia/core": "../packages/core/nestia-core-3.11.0-dev.20240813-11.tgz", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "^3.11.0-dev.20240813-10", + "@nestia/fetcher": "../packages/fetcher/nestia-fetcher-3.11.0-dev.20240813-11.tgz", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/platform-express": "^10.3.5", From a75b4d174dd2a92efec4e340a0f84eff0786de88 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 12:14:18 +0900 Subject: [PATCH 08/21] Fix WebSocket SDK reversed type problem. --- .../sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts | 1 - .../generates/internal/SdkWebSocketNamespaceProgrammer.ts | 8 ++++++-- .../src/api/functional/x/calculate/index.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index 30f8d0ada..e68577468 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -118,7 +118,6 @@ export namespace ReflectWebSocketOperationAnalyzer { } satisfies IReflectWebSocketOperationParameter.IParam; // UNKNOWN TYPE, MAYBE NEW FEATURE - console.log(p); return errors.push( `@WebSocketRoute.${StringUtil.capitalize(p.category)}() has not been supported yet. How about upgrading the nestia packages?`, ); diff --git a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts index 93f14efff..69773074d 100644 --- a/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts @@ -78,6 +78,8 @@ export namespace SdkWebSocketNamespaceProgrammer { route.parameters.find((x) => x.category === "acceptor")!; const query: ITypedWebSocketRouteParameter.IQuery | undefined = route.parameters.find((x) => x.category === "query"); + const driver: ITypedWebSocketRouteParameter.IDriver | undefined = + route.parameters.find((x) => x.category === "driver"); declare( "Header", SdkAliasCollection.name( @@ -87,11 +89,13 @@ export namespace SdkWebSocketNamespaceProgrammer { ); declare( "Provider", - SdkAliasCollection.name(acceptor.type.typeArguments?.[1]!), + SdkAliasCollection.name( + driver?.type.typeArguments?.[0] ?? acceptor.type.typeArguments?.[2]!, + ), ); declare( "Listener", - SdkAliasCollection.name(acceptor.type.typeArguments?.[2]!), + SdkAliasCollection.name(acceptor.type.typeArguments?.[1]!), ); if (query) declare("Query", SdkAliasCollection.name(query.type)); return output; diff --git a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts index 634451752..5bee01f13 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/calculate/index.ts @@ -41,8 +41,8 @@ export namespace connect { driver: Driver; }; export type Header = IPrecision; - export type Provider = ICalculator; - export type Listener = IListener; + export type Provider = IListener; + export type Listener = ICalculator; export const path = () => "/x/calculate"; } From 6fe0438c20a2b77917cdde77b7ce8f018f62c496 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 16:14:47 +0900 Subject: [PATCH 09/21] 1st completion (maybe?) --- benchmark/package.json | 2 +- packages/benchmark/package.json | 2 +- packages/core/package.json | 6 +- packages/e2e/package.json | 2 +- packages/fetcher/package.json | 2 +- packages/migrate/package.json | 4 +- packages/sdk/assets/bundle/api/Resolved.ts | 1 + packages/sdk/assets/bundle/api/module.ts | 1 + packages/sdk/package.json | 6 +- packages/sdk/src/NestiaSdkApplication.ts | 36 +-- .../analyses/ReflectHttpOperationAnalyzer.ts | 10 +- .../ReflectHttpOperationExceptionAnalyzer.ts | 71 +++++ .../ReflectHttpOperationParameterAnalyzer.ts | 3 - .../executable/internal/NestiaSdkCommand.ts | 5 +- packages/sdk/src/executable/sdk.ts | 2 + packages/sdk/src/generates/CloneGenerator.ts | 121 +++++---- packages/sdk/src/generates/SdkGenerator.ts | 6 +- .../generates/internal/E2eFileProgrammer.ts | 7 + .../generates/internal/SdkAliasCollection.ts | 5 +- .../generates/internal/SdkFileProgrammer.ts | 15 +- .../internal/SdkHttpCloneProgrammer.ts | 257 ++++++++---------- .../internal/SdkHttpCloneReferencer.ts | 61 +++++ .../internal/SdkHttpFunctionProgrammer.ts | 10 +- .../internal/SdkHttpNamespaceProgrammer.ts | 5 +- .../internal/SdkHttpSimulationProgrammer.ts | 45 +-- .../src/structures/IReflectHttpOperation.ts | 5 +- .../IReflectHttpOperationException.ts | 4 +- .../sdk/src/structures/ITypedApplication.ts | 3 + packages/sdk/src/transform.ts | 4 +- .../src/transformers/IOperationMetadata.ts | 8 +- ....ts => ISdkOperationTransformerContext.ts} | 2 +- ...rogrammer.ts => SdkOperationProgrammer.ts} | 60 ++-- ...nsformer.ts => SdkOperationTransformer.ts} | 51 +++- packages/sdk/src/utils/MetadataUtil.ts | 26 ++ packages/sdk/src/utils/StringUtil.ts | 8 + packages/sdk/tsconfig.shopping.json | 85 ------ test/features/all/src/api/Resolved.ts | 1 + .../all/src/api/functional/all/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- test/features/all/src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../api/v1/articles/comments/index.ts | 79 +++--- .../functional/api/v1/bbs/articles/index.ts | 77 +++--- .../functional/api/v1/common/health/index.ts | 21 +- .../functional/api/v1/performance/index.ts | 25 +- .../api/v2/articles/comments/index.ts | 79 +++--- .../functional/api/v2/bbs/articles/index.ts | 77 +++--- .../functional/api/v2/common/health/index.ts | 21 +- .../functional/api/v2/performance/index.ts | 25 +- .../api/v3/articles/comments/index.ts | 79 +++--- .../functional/api/v3/bbs/articles/index.ts | 77 +++--- .../functional/api/v3/common/health/index.ts | 21 +- .../functional/api/v3/performance/index.ts | 25 +- .../src/api/module.ts | 1 + .../swagger.json | 2 +- .../src/api/Resolved.ts | 1 + .../api/internal/bbs/articles/index.ts | 48 ++-- .../api/internal/v1/bbs/articles/index.ts | 65 +++-- .../api/internal/v1/health/index.ts | 21 +- .../api/internal/v1/performance/index.ts | 25 +- .../api/internal/v2/bbs/articles/index.ts | 48 ++-- .../src/api/module.ts | 1 + .../app-globalPrefix-versionUri/swagger.json | 2 +- .../app-globalPrefix/src/api/Resolved.ts | 1 + .../x/bbs/articles/comments/index.ts | 8 +- .../api/functional/x/bbs/articles/index.ts | 8 +- .../src/api/functional/x/performance/index.ts | 4 +- .../app-globalPrefix/src/api/module.ts | 1 + .../app-routerModule/src/api/Resolved.ts | 1 + .../api/functional/articles/comments/index.ts | 79 +++--- .../src/api/functional/bbs/articles/index.ts | 77 +++--- .../src/api/functional/common/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- .../app-routerModule/src/api/module.ts | 1 + test/features/app-routerModule/swagger.json | 2 +- .../app-versionHeader/src/api/Resolved.ts | 1 + .../functional/bbs/articles/comments/index.ts | 79 +++--- .../src/api/functional/bbs/articles/index.ts | 77 +++--- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- .../app-versionHeader/src/api/module.ts | 1 + test/features/app-versionHeader/swagger.json | 2 +- .../app-versionUri/src/api/Resolved.ts | 1 + .../src/api/functional/bbs/articles/index.ts | 48 ++-- .../api/functional/v1/bbs/articles/index.ts | 65 +++-- .../src/api/functional/v1/health/index.ts | 21 +- .../api/functional/v1/performance/index.ts | 25 +- .../api/functional/v2/bbs/articles/index.ts | 48 ++-- .../features/app-versionUri/src/api/module.ts | 1 + test/features/app-versionUri/swagger.json | 2 +- test/features/app/src/api/Resolved.ts | 1 + .../functional/bbs/articles/comments/index.ts | 8 +- .../src/api/functional/bbs/articles/index.ts | 8 +- .../src/api/functional/performance/index.ts | 4 +- test/features/app/src/api/module.ts | 1 + test/features/beautify-4/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 12 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- test/features/beautify-4/src/api/module.ts | 1 + test/features/beautify-4/swagger.json | 56 ++-- .../beautify-false/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 12 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- .../features/beautify-false/src/api/module.ts | 1 + test/features/beautify-false/swagger.json | 2 +- test/features/beautify/src/api/Resolved.ts | 1 + .../beautify/src/api/functional/body/index.ts | 12 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- test/features/beautify/src/api/module.ts | 1 + test/features/beautify/swagger.json | 56 ++-- .../body-config-assert/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 56 ---- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/index.ts | 9 - .../src/api/functional/performance/index.ts | 39 --- .../body-config-assert/src/api/module.ts | 1 + .../api/automated/test_api_body_store.ts | 13 - .../api/automated/test_api_health_get.ts | 8 - .../api/automated/test_api_performance_get.ts | 11 - test/features/body-config-assert/swagger.json | 1 - .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-assertClone/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-assertEquals/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-assertPrune/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/body-config-equals/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/body-config-is/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-validate/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-validateClone/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-validateEquals/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-config-validatePrune/swagger.json | 2 +- .../src/api/functional/body/index.ts | 12 +- .../google/drives/images/upload/index.ts | 12 +- .../body-generic-default/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/body-manual-assert/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/body-manual-is/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- .../body-manual-validate/swagger.json | 2 +- test/features/body/src/api/Resolved.ts | 1 + .../body/src/api/functional/body/index.ts | 12 +- .../body/src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 25 +- test/features/body/src/api/module.ts | 1 + test/features/body/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/cli-config-project/swagger.json | 2 +- .../src/api/functional/body/index.ts | 8 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/cli-config/swagger.json | 2 +- .../src/api/functional/health/index.ts | 21 +- .../src/api/functional/performance/index.ts | 23 +- test/features/cli-project/swagger.json | 2 +- .../src/api/functional/health/index.ts | 46 +++- .../api/functional/partial_dto_test/index.ts | 52 +++- .../partial_interface/index.ts | 28 +- .../partial_dto_test/partial_type/index.ts | 41 ++- .../src/api/structures/IOriginal.ts | 5 +- .../src/api/structures/ISomething.ts | 4 + ...ed_atoriginal_optionalundefinable_attr.ts} | 4 +- ...ed_atoriginal_optionalundefinable_attrd.ts | 16 -- .../controllers/PartialDTOTestController.ts | 6 + .../api/automated/test_api_health_get.ts | 9 +- .../test_api_partial_dto_test_original.ts | 9 +- ...test_partial_interface_partialInterface.ts | 9 +- ...rtial_dto_test_partial_type_partialType.ts | 15 +- .../swagger.json | 2 +- .../packages/api/package.json | 2 +- .../packages/api/package.json | 2 +- .../distribute-json/packages/api/package.json | 2 +- .../distribute/packages/api/package.json | 2 +- test/features/exception/swagger.json | 166 ++++++----- test/package.json | 4 +- website/package.json | 4 +- 212 files changed, 2433 insertions(+), 1778 deletions(-) create mode 100644 packages/sdk/assets/bundle/api/Resolved.ts create mode 100644 packages/sdk/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts create mode 100644 packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts rename packages/sdk/src/transformers/{ISdkTransformerContext.ts => ISdkOperationTransformerContext.ts} (79%) rename packages/sdk/src/transformers/{SdkMetadataProgrammer.ts => SdkOperationProgrammer.ts} (81%) rename packages/sdk/src/transformers/{SdkTransformer.ts => SdkOperationTransformer.ts} (76%) create mode 100644 packages/sdk/src/utils/MetadataUtil.ts delete mode 100644 packages/sdk/tsconfig.shopping.json create mode 100644 test/features/all/src/api/Resolved.ts create mode 100644 test/features/app-globalPrefix-versionUri-routerModule/src/api/Resolved.ts create mode 100644 test/features/app-globalPrefix-versionUri/src/api/Resolved.ts create mode 100644 test/features/app-globalPrefix/src/api/Resolved.ts create mode 100644 test/features/app-routerModule/src/api/Resolved.ts create mode 100644 test/features/app-versionHeader/src/api/Resolved.ts create mode 100644 test/features/app-versionUri/src/api/Resolved.ts create mode 100644 test/features/app/src/api/Resolved.ts create mode 100644 test/features/beautify-4/src/api/Resolved.ts create mode 100644 test/features/beautify-false/src/api/Resolved.ts create mode 100644 test/features/beautify/src/api/Resolved.ts create mode 100644 test/features/body-config-assert/src/api/Resolved.ts delete mode 100644 test/features/body-config-assert/src/api/functional/body/index.ts delete mode 100644 test/features/body-config-assert/src/api/functional/index.ts delete mode 100644 test/features/body-config-assert/src/api/functional/performance/index.ts delete mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts delete mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts delete mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts delete mode 100644 test/features/body-config-assert/swagger.json create mode 100644 test/features/body/src/api/Resolved.ts create mode 100644 test/features/clone-and-exact-optional-property/src/api/structures/ISomething.ts rename test/features/clone-and-exact-optional-property/src/api/structures/{PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb.ts => PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr.ts} (82%) delete mode 100644 test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd.ts diff --git a/benchmark/package.json b/benchmark/package.json index 76f790086..7ecbd6e83 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -42,7 +42,7 @@ "reflect-metadata": "^0.2.2", "tgrid": "^1.0.3", "tstl": "^3.0.0", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" }, "devDependencies": { "@types/autocannon": "^7.9.0", diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json index 18d613dbf..d0b8c70be 100644 --- a/packages/benchmark/package.json +++ b/packages/benchmark/package.json @@ -34,7 +34,7 @@ "ts-patch": "^3.2.1", "typescript": "^5.5.4", "typescript-transform-paths": "^3.4.7", - "typia": "^6.8.0-dev.20240812", + "typia": "^6.8.0", "uuid": "^10.0.0" }, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index d70262b34..b316196f3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -39,7 +39,7 @@ "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240813-11.tgz", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.4", "detect-ts-node": "^1.0.5", "get-function-location": "^2.0.0", "glob": "^7.2.0", @@ -49,7 +49,7 @@ "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.3", "tgrid": "^1.0.0", - "typia": "^6.8.0-dev.20240812", + "typia": "^6.8.0", "ws": "^7.5.3" }, "peerDependencies": { @@ -58,7 +58,7 @@ "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.3", - "typia": ">=6.8.0-dev.20240812 <7.0.0" + "typia": ">=6.8.0 <7.0.0" }, "devDependencies": { "@fastify/multipart": "^8.1.0", diff --git a/packages/e2e/package.json b/packages/e2e/package.json index ec85772d7..f22254af6 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -41,7 +41,7 @@ "ts-patch": "^3.2.1", "typescript": "^5.5.3", "typescript-transform-paths": "^3.4.7", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" }, "files": [ "lib", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 6918b17f9..4d190ab6d 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -43,6 +43,6 @@ "src" ], "dependencies": { - "@samchon/openapi": "^0.4.3" + "@samchon/openapi": "^0.4.4" } } \ No newline at end of file diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 7c1db7563..1b0215bcb 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -65,13 +65,13 @@ "typescript-transform-paths": "^3.4.6" }, "dependencies": { - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.4", "commander": "10.0.0", "inquirer": "8.2.5", "prettier": "^3.2.5", "tstl": "^3.0.0", "typescript": "^5.5.4", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" }, "files": [ "lib", diff --git a/packages/sdk/assets/bundle/api/Resolved.ts b/packages/sdk/assets/bundle/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/packages/sdk/assets/bundle/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/packages/sdk/assets/bundle/api/module.ts b/packages/sdk/assets/bundle/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/packages/sdk/assets/bundle/api/module.ts +++ b/packages/sdk/assets/bundle/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 49f9b936a..2d74e3c40 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -34,7 +34,7 @@ "dependencies": { "@nestia/core": "../core/nestia-core-3.11.0-dev.20240813-11.tgz", "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240813-11.tgz", - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.4", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", "get-function-location": "^2.0.0", @@ -45,7 +45,7 @@ "tsconfck": "^2.1.2", "tsconfig-paths": "^4.1.1", "tstl": "^3.0.0", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" }, "peerDependencies": { "@nestia/core": ">=3.11.0-dev.20240813-11", @@ -54,7 +54,7 @@ "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", "ts-node": ">=10.6.0", - "typia": ">=6.8.0-dev.20240812 <7.0.0" + "typia": ">=6.8.0 <7.0.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/packages/sdk/src/NestiaSdkApplication.ts b/packages/sdk/src/NestiaSdkApplication.ts index f9dda1d17..4d3679edb 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -27,6 +27,24 @@ import { VersioningStrategy } from "./utils/VersioningStrategy"; export class NestiaSdkApplication { public constructor(private readonly config: INestiaConfig) {} + public async all(): Promise { + if ( + !this.config.output && + !this.config.swagger?.output && + !this.config.openai?.output + ) + throw new Error("Error on NestiaApplication.all(): nothing to generate."); + print_title("Nestia All Generator"); + await this.generate({ + generate: async (app) => { + if (this.config.output) await SdkGenerator.generate(app); + if (this.config.e2e) await E2eGenerator.generate(app); + if (this.config.swagger) await SwaggerGenerator.generate(app); + if (this.config.openai) await OpenAiGenerator.generate(app); + }, + }); + } + public async e2e(): Promise { if (!this.config.output) throw new Error( @@ -234,6 +252,7 @@ export class NestiaSdkApplication { if (props.validate !== undefined) props.validate({ project, + collection, routes, }); if (project.errors.length) @@ -243,6 +262,7 @@ export class NestiaSdkApplication { }); await props.generate({ project, + collection, routes, }); } @@ -254,22 +274,6 @@ const print_title = (str: string): void => { console.log("-----------------------------------------------------------"); }; -// const is_implicit_return_typed = (route: ITypedHttpRoute): boolean => { -// const name: string = route.output.typeName; -// if (name === "void") return false; -// else if (name.indexOf("readonly [") !== -1) return true; - -// const pos: number = name.indexOf("__object"); -// if (pos === -1) return false; - -// const before: number = pos - 1; -// const after: number = pos + "__object".length; -// for (const i of [before, after]) -// if (name[i] === undefined) continue; -// else if (VARIABLE.test(name[i])) return false; -// return true; -// }; - const report = (props: { type: "error" | "warning"; errors: IReflectOperationError[]; diff --git a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts index ad6998ecd..6a34a9633 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -11,6 +11,7 @@ import { IOperationMetadata } from "../transformers/IOperationMetadata"; import { ArrayUtil } from "../utils/ArrayUtil"; import { ImportAnalyzer } from "./ImportAnalyzer"; import { PathAnalyzer } from "./PathAnalyzer"; +import { ReflectHttpOperationExceptionAnalyzer } from "./ReflectHttpOperationExceptionAnalyzer"; import { ReflectHttpOperationParameterAnalyzer } from "./ReflectHttpOperationParameterAnalyzer"; import { ReflectHttpOperationResponseAnalyzer } from "./ReflectHttpOperationResponseAnalyzer"; import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer"; @@ -91,7 +92,14 @@ export namespace ReflectHttpOperationAnalyzer { parameters, success, security: ReflectMetadataAnalyzer.securities(props.function), - exceptions: {} as any, + exceptions: ReflectHttpOperationExceptionAnalyzer.analyze({ + controller: props.controller, + function: props.function, + functionName: props.name, + httpMethod: method, + metadata: props.metadata, + errors, + }), tags: Reflect.getMetadata("swagger/apiUseTags", props.function) ?? [], imports: ImportAnalyzer.unique( [ diff --git a/packages/sdk/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts new file mode 100644 index 000000000..cdf9ddcb7 --- /dev/null +++ b/packages/sdk/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts @@ -0,0 +1,71 @@ +import { TypedException } from "@nestia/core"; +import { JsonMetadataFactory } from "typia/lib/factories/JsonMetadataFactory"; + +import { IReflectController } from "../structures/IReflectController"; +import { IReflectHttpOperationException } from "../structures/IReflectHttpOperationException"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; + +export namespace ReflectHttpOperationExceptionAnalyzer { + export interface IContext { + controller: IReflectController; + function: Function; + functionName: string; + httpMethod: string; + metadata: IOperationMetadata; + errors: IReflectOperationError[]; + } + export const analyze = ( + ctx: IContext, + ): Record => { + const preconfigured: TypedException.IProps[] = analyzePreconfigured( + ctx.function, + ) + .slice() + .reverse(); + const errors: IReflectOperationError[] = []; + const exceptions: IReflectHttpOperationException[] = preconfigured + .map((pre, i) => { + const matched: IOperationMetadata.IException | null = + ctx.metadata.exceptions[i]; + if (matched === undefined) { + errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.functionName, + from: `exception (status: ${pre.status})`, + contents: ["Unable to find exception type."], + }); + return null; + } else if (matched.type === null) { + errors.push({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.functionName, + from: `exception (status: ${pre.status})`, + contents: ["Failed to get the type info."], + }); + } + const schema: IOperationMetadata.ISchema | null = matched.primitive + .success + ? matched.primitive.data + : null; + if (schema === null || matched.type === null) return null; // unreachable + return { + status: pre.status, + description: pre.description ?? null, + example: pre.example, + examples: pre.examples, + type: matched.type, + ...schema, + validate: JsonMetadataFactory.validate, + } satisfies IReflectHttpOperationException; + }) + .filter((e) => e !== null); + if (errors.length) ctx.errors.push(...errors); + return Object.fromEntries(exceptions.map((e) => [e.status, e])); + }; + + const analyzePreconfigured = (func: Function): TypedException.IProps[] => + Reflect.getMetadata("nestia/TypedException", func) ?? []; +} diff --git a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts index 57ee27503..574deb954 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -9,7 +9,6 @@ import { HttpQueryProgrammer } from "typia/lib/programmers/http/HttpQueryProgram import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; import { IReflectOperationError } from "../structures/IReflectOperationError"; -import { IReflectTypeImport } from "../structures/IReflectTypeImport"; import { IOperationMetadata } from "../transformers/IOperationMetadata"; import { TextPlainValidator } from "../transformers/TextPlainValidator"; @@ -25,7 +24,6 @@ export namespace ReflectHttpOperationParameterAnalyzer { export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => { const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] = analyzePreconfigured(ctx); - const imports: IReflectTypeImport[] = []; const errors: IReflectOperationError[] = []; //---- @@ -149,7 +147,6 @@ export namespace ReflectHttpOperationParameterAnalyzer { return null; // unreachable // COMPOSITION - imports.push(...matched.imports); if (p.category === "param") return { category: p.category, diff --git a/packages/sdk/src/executable/internal/NestiaSdkCommand.ts b/packages/sdk/src/executable/internal/NestiaSdkCommand.ts index 026d64a76..21cad4a71 100644 --- a/packages/sdk/src/executable/internal/NestiaSdkCommand.ts +++ b/packages/sdk/src/executable/internal/NestiaSdkCommand.ts @@ -9,12 +9,13 @@ export namespace NestiaSdkCommand { export const swagger = () => main((app) => app.swagger()); export const openai = () => main((app) => app.openai()); export const e2e = () => main((app) => app.e2e()); + export const all = () => main((app) => app.all()); const main = async (task: (app: NestiaSdkApplication) => Promise) => { - await generate(task); + await _Generate(task); }; - const generate = async ( + const _Generate = async ( task: (app: NestiaSdkApplication) => Promise, ) => { // LOAD CONFIG INFO diff --git a/packages/sdk/src/executable/sdk.ts b/packages/sdk/src/executable/sdk.ts index 931d0cdc8..fc71dad0d 100644 --- a/packages/sdk/src/executable/sdk.ts +++ b/packages/sdk/src/executable/sdk.ts @@ -18,6 +18,7 @@ npx @nestia/sdk [command] [options?] 4. npx @nestia/sdk swagger --config? [config file] --project? [project file] 5. npx @nestia/sdk openai --config? [config file] --project? [project file] 6. npx @nestia/sdk e2e --config? [config file] --project? [project file] + 7. npx @nestia/sdk generate --config? [config file] --project? [project file] `; function halt(desc: string): never { @@ -65,6 +66,7 @@ async function main() { else if (type === "swagger") await execute((c) => c.swagger()); else if (type === "openai") await execute((c) => c.openai()); else if (type === "e2e") await execute((c) => c.e2e()); + else if (type === "all") await execute((c) => c.all()); else halt(USAGE); process.exit(0); diff --git a/packages/sdk/src/generates/CloneGenerator.ts b/packages/sdk/src/generates/CloneGenerator.ts index e04147520..b5241e51a 100644 --- a/packages/sdk/src/generates/CloneGenerator.ts +++ b/packages/sdk/src/generates/CloneGenerator.ts @@ -1,65 +1,66 @@ -// import fs from "fs"; -// import ts from "typescript"; +import fs from "fs"; +import ts from "typescript"; -// import { INestiaProject } from "../structures/INestiaProject"; -// import { ITypedApplication } from "../structures/ITypedApplication"; -// import { FilePrinter } from "./internal/FilePrinter"; -// import { ImportDictionary } from "./internal/ImportDictionary"; -// // import { SdkHttpCloneProgrammer } from "./internal/SdkHttpCloneProgrammer"; +import { INestiaProject } from "../structures/INestiaProject"; +import { ITypedApplication } from "../structures/ITypedApplication"; +import { FilePrinter } from "./internal/FilePrinter"; +import { ImportDictionary } from "./internal/ImportDictionary"; +import { SdkHttpCloneProgrammer } from "./internal/SdkHttpCloneProgrammer"; +import { SdkHttpCloneReferencer } from "./internal/SdkHttpCloneReferencer"; -// export namespace CloneGenerator { -// export const write = async (app: ITypedApplication): Promise => { -// // const dict: Map = -// // SdkHttpCloneProgrammer.write(app.project)( -// // app.routes.filter((r) => r.protocol === "http"), -// // ); -// if (dict.size === 0) return; -// try { -// await fs.promises.mkdir(`${app.project.config.output}/structures`); -// } catch {} -// for (const [key, value] of dict) -// await writeDtoFile(app.project)(key, value); -// }; +export namespace CloneGenerator { + export const write = async (app: ITypedApplication): Promise => { + const dict: Map = + SdkHttpCloneProgrammer.write(app); + if (dict.size === 0) return; -// const writeDtoFile = -// (project: INestiaProject) => -// async ( -// key: string, -// value: SdkHttpCloneProgrammer.IModule, -// ): Promise => { -// const location: string = `${project.config.output}/structures/${key}.ts`; -// const importer: ImportDictionary = new ImportDictionary(location); -// const statements: ts.Statement[] = iterate(importer)(value); -// if (statements.length === 0) return; + SdkHttpCloneReferencer.replace(app); + try { + await fs.promises.mkdir(`${app.project.config.output}/structures`); + } catch {} + for (const [key, value] of dict) + await writeDtoFile(app.project)(key, value); + }; -// await FilePrinter.write({ -// location, -// statements: [ -// ...importer.toStatements(`${project.config.output}/structures`), -// ...(importer.empty() ? [] : [FilePrinter.enter()]), -// ...statements, -// ], -// }); -// }; + const writeDtoFile = + (project: INestiaProject) => + async ( + key: string, + value: SdkHttpCloneProgrammer.IModule, + ): Promise => { + const location: string = `${project.config.output}/structures/${key}.ts`; + const importer: ImportDictionary = new ImportDictionary(location); + const statements: ts.Statement[] = iterate(importer)(value); + if (statements.length === 0) return; -// const iterate = -// (importer: ImportDictionary) => -// (modulo: SdkHttpCloneProgrammer.IModule): ts.Statement[] => { -// const output: ts.Statement[] = []; -// if (modulo.programmer !== null) output.push(modulo.programmer(importer)); -// if (modulo.children.size) { -// const internal: ts.Statement[] = []; -// for (const child of modulo.children.values()) -// internal.push(...iterate(importer)(child)); -// output.push( -// ts.factory.createModuleDeclaration( -// [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], -// ts.factory.createIdentifier(modulo.name), -// ts.factory.createModuleBlock(internal), -// ts.NodeFlags.Namespace, -// ), -// ); -// } -// return output; -// }; -// } + await FilePrinter.write({ + location, + statements: [ + ...importer.toStatements(`${project.config.output}/structures`), + ...(importer.empty() ? [] : [FilePrinter.enter()]), + ...statements, + ], + }); + }; + + const iterate = + (importer: ImportDictionary) => + (modulo: SdkHttpCloneProgrammer.IModule): ts.Statement[] => { + const output: ts.Statement[] = []; + if (modulo.programmer !== null) output.push(modulo.programmer(importer)); + if (modulo.children.size) { + const internal: ts.Statement[] = []; + for (const child of modulo.children.values()) + internal.push(...iterate(importer)(child)); + output.push( + ts.factory.createModuleDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier(modulo.name), + ts.factory.createModuleBlock(internal), + ts.NodeFlags.Namespace, + ), + ); + } + return output; + }; +} diff --git a/packages/sdk/src/generates/SdkGenerator.ts b/packages/sdk/src/generates/SdkGenerator.ts index 2b6a1fb5f..a8b969415 100644 --- a/packages/sdk/src/generates/SdkGenerator.ts +++ b/packages/sdk/src/generates/SdkGenerator.ts @@ -7,7 +7,7 @@ import { IReflectOperationError } from "../structures/IReflectOperationError"; import { IReflectType } from "../structures/IReflectType"; import { ITypedApplication } from "../structures/ITypedApplication"; import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; -// import { CloneGenerator } from "./CloneGenerator"; +import { CloneGenerator } from "./CloneGenerator"; import { SdkDistributionComposer } from "./internal/SdkDistributionComposer"; import { SdkFileProgrammer } from "./internal/SdkFileProgrammer"; @@ -44,8 +44,8 @@ export namespace SdkGenerator { } } - // // STRUCTURES - // if (app.project.config.clone === true) await CloneGenerator.write(app); + // STRUCTURES + if (app.project.config.clone === true) await CloneGenerator.write(app); // FUNCTIONAL await SdkFileProgrammer.generate(app); diff --git a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts index f761ddc40..988559851 100644 --- a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts @@ -30,6 +30,13 @@ export namespace E2eFileProgrammer { instance: null, name: "api", }); + for (const tuple of route.imports) + for (const instance of tuple.instances) + importer.internal({ + type: true, + file: tuple.file, + instance, + }); const functor = generate_function(project)(importer)(route); await FilePrinter.write({ diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index 4eca54eab..69c754d68 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -1,5 +1,6 @@ import ts from "typescript"; import typia from "typia"; +import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { INestiaProject } from "../../structures/INestiaProject"; import { IReflectHttpOperationParameter } from "../../structures/IReflectHttpOperationParameter"; @@ -74,7 +75,9 @@ export namespace SdkAliasCollection { (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { if (project.config.propagate !== true) { - if (project.config.clone === true || project.config.primitive === false) + if (route.success.metadata.size() === 0) + return TypeFactory.keyword("void"); + else if (project.config.primitive === false) return name(route.success.type); return ts.factory.createTypeReferenceNode( importer.external({ diff --git a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts index c167f7971..502f3b8f6 100644 --- a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts @@ -75,14 +75,13 @@ export namespace SdkFileProgrammer { `${outDir}/index.ts`, ); directory.routes.forEach((route, i) => { - if (project.config.clone !== true || route.protocol === "websocket") - for (const tuple of route.imports) - for (const instance of tuple.instances) - importer.internal({ - file: tuple.file, - instance, - type: true, - }); + for (const tuple of route.imports) + for (const instance of tuple.instances) + importer.internal({ + file: tuple.file, + instance, + type: true, + }); statements.push( ...(route.protocol === "http" ? SdkHttpRouteProgrammer.write(project)(importer)(route) diff --git a/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts index f749270bb..742b3cc46 100644 --- a/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts @@ -1,154 +1,125 @@ -// import { IPointer } from "tstl"; -// import ts from "typescript"; -// import { IJsDocTagInfo } from "typia"; -// import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -// import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; -// import { MetadataAlias } from "typia/lib/schemas/metadata/MetadataAlias"; -// import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic"; -// import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; +import { IPointer } from "tstl"; +import ts from "typescript"; +import { IJsDocTagInfo } from "typia"; +import { MetadataAlias } from "typia/lib/schemas/metadata/MetadataAlias"; +import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic"; +import { MetadataObject } from "typia/lib/schemas/metadata/MetadataObject"; -// import { INestiaProject } from "../../structures/INestiaProject"; -// import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; -// import { MapUtil } from "../../utils/MapUtil"; -// import { FilePrinter } from "./FilePrinter"; -// import { ImportDictionary } from "./ImportDictionary"; -// import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; +import { INestiaProject } from "../../structures/INestiaProject"; +import { ITypedApplication } from "../../structures/ITypedApplication"; +import { MapUtil } from "../../utils/MapUtil"; +import { StringUtil } from "../../utils/StringUtil"; +import { FilePrinter } from "./FilePrinter"; +import { ImportDictionary } from "./ImportDictionary"; +import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; -// export namespace SdkHttpCloneProgrammer { -// export interface IModule { -// name: string; -// children: Map; -// programmer: -// | null -// | ((importer: ImportDictionary) => ts.TypeAliasDeclaration); -// } +export namespace SdkHttpCloneProgrammer { + export interface IModule { + name: string; + children: Map; + programmer: + | null + | ((importer: ImportDictionary) => ts.TypeAliasDeclaration); + } -// export const write = -// (project: INestiaProject) => -// (routes: ITypedHttpRoute[]): Map => { -// const collection = new MetadataCollection({ -// replace: MetadataCollection.replace, -// }); -// for (const r of routes) { -// for (const p of r.parameters) { -// const res = MetadataFactory.analyze(project.checker)({ -// escape: false, -// constant: true, -// absorb: false, -// })(collection)(p.type); -// if (res.success) p.metadata = res.data; -// } -// for (const e of Object.values(r.exceptions)) { -// const res = MetadataFactory.analyze(project.checker)({ -// escape: true, -// constant: true, -// absorb: false, -// })(collection)(e.type); -// if (res.success) e.metadata = res.data; -// } -// const res = MetadataFactory.analyze(project.checker)({ -// escape: true, -// constant: true, -// absorb: false, -// })(collection)(r.output.type); -// if (res.success) r.success.metadata = res.data; -// } + export const write = (app: ITypedApplication): Map => { + // COMPOSE THE DICTIONARY + const dict: Map = new Map(); + for (const [k, v] of app.collection.objects.entries()) + if (StringUtil.isImplicit(k) === false) + prepare({ + dict, + name: k, + programmer: (importer) => write_object(app.project)(importer)(v), + }); + for (const [k, v] of app.collection.aliases.entries()) + if (StringUtil.isImplicit(k) === false) + prepare({ + dict, + name: k, + programmer: (importer) => write_alias(app.project)(importer)(v), + }); -// const dict: Map = new Map(); -// for (const alias of collection.aliases()) -// if (isNamedDeclaration(alias.name)) -// prepare(dict)(alias.name)((importer) => -// write_alias(project)(importer)(alias), -// ); -// for (const object of collection.objects()) -// if (isNamedDeclaration(object.name)) -// prepare(dict)(object.name)((importer) => -// write_object(project)(importer)(object), -// ); -// return dict; -// }; + return dict; + }; -// const prepare = -// (dict: Map) => -// (name: string) => -// (programmer: (importer: ImportDictionary) => ts.TypeAliasDeclaration) => { -// const accessors: string[] = name.split("."); -// const modulo: IPointer = { value: null! }; + const prepare = (props: { + dict: Map; + name: string; + programmer: (importer: ImportDictionary) => ts.TypeAliasDeclaration; + }) => { + let next: Map = props.dict; + const accessors: string[] = props.name.split("."); + const modulo: IPointer = { value: null! }; -// accessors.forEach((acc, i) => { -// modulo.value = MapUtil.take(dict, acc, () => ({ -// name: acc, -// children: new Map(), -// programmer: null, -// })); -// if (i === accessors.length - 1) modulo.value.programmer = programmer; -// dict = modulo.value.children; -// }); -// return modulo!; -// }; + accessors.forEach((acc, i) => { + modulo.value = MapUtil.take(next, acc, () => ({ + name: acc, + children: new Map(), + programmer: null, + })); + if (i === accessors.length - 1) + modulo.value.programmer = props.programmer; + next = modulo.value.children; + }); + return modulo!; + }; -// const write_alias = -// (project: INestiaProject) => -// (importer: ImportDictionary) => -// (alias: MetadataAlias): ts.TypeAliasDeclaration => -// FilePrinter.description( -// ts.factory.createTypeAliasDeclaration( -// [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], -// alias.name.split(".").at(-1)!, -// [], -// SdkTypeProgrammer.write(project)(importer)(alias.value), -// ), -// writeComment([])(alias.description, alias.jsDocTags), -// ); + const write_alias = + (project: INestiaProject) => + (importer: ImportDictionary) => + (alias: MetadataAlias): ts.TypeAliasDeclaration => + FilePrinter.description( + ts.factory.createTypeAliasDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + alias.name.split(".").at(-1)!, + [], + SdkTypeProgrammer.write(project)(importer)(alias.value), + ), + writeComment([])(alias.description, alias.jsDocTags), + ); -// const write_object = -// (project: INestiaProject) => -// (importer: ImportDictionary) => -// (object: MetadataObject): ts.TypeAliasDeclaration => { -// return FilePrinter.description( -// ts.factory.createTypeAliasDeclaration( -// [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], -// object.name.split(".").at(-1)!, -// [], -// SdkTypeProgrammer.write_object(project)(importer)(object), -// ), -// writeComment([])(object.description ?? null, object.jsDocTags), -// ); -// }; -// } + const write_object = + (project: INestiaProject) => + (importer: ImportDictionary) => + (object: MetadataObject): ts.TypeAliasDeclaration => { + return FilePrinter.description( + ts.factory.createTypeAliasDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + object.name.split(".").at(-1)!, + [], + SdkTypeProgrammer.write_object(project)(importer)(object), + ), + writeComment([])(object.description ?? null, object.jsDocTags), + ); + }; +} -// const isNamedDeclaration = (name: string) => -// name !== "object" && -// name !== "__type" && -// !name.startsWith("__type.") && -// name !== "__object" && -// !name.startsWith("__object."); +const writeComment = + (atomics: MetadataAtomic[]) => + (description: string | null, jsDocTags: IJsDocTagInfo[]): string => { + const lines: string[] = []; + if (description?.length) + lines.push(...description.split("\n").map((s) => `${s}`)); -// const writeComment = -// (atomics: MetadataAtomic[]) => -// (description: string | null, jsDocTags: IJsDocTagInfo[]): string => { -// const lines: string[] = []; -// if (description?.length) -// lines.push(...description.split("\n").map((s) => `${s}`)); + const filtered: IJsDocTagInfo[] = + !!atomics.length && !!jsDocTags?.length + ? jsDocTags.filter( + (tag) => + !atomics.some((a) => + a.tags.some((r) => r.some((t) => t.kind === tag.name)), + ), + ) + : jsDocTags ?? []; -// const filtered: IJsDocTagInfo[] = -// !!atomics.length && !!jsDocTags?.length -// ? jsDocTags.filter( -// (tag) => -// !atomics.some((a) => -// a.tags.some((r) => r.some((t) => t.kind === tag.name)), -// ), -// ) -// : jsDocTags ?? []; - -// if (description?.length && filtered.length) lines.push(""); -// if (filtered.length) -// lines.push( -// ...filtered.map((t) => -// t.text?.length -// ? `@${t.name} ${t.text.map((e) => e.text).join("")}` -// : `@${t.name}`, -// ), -// ); -// return lines.join("\n"); -// }; + if (description?.length && filtered.length) lines.push(""); + if (filtered.length) + lines.push( + ...filtered.map((t) => + t.text?.length + ? `@${t.name} ${t.text.map((e) => e.text).join("")}` + : `@${t.name}`, + ), + ); + return lines.join("\n"); + }; diff --git a/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts new file mode 100644 index 000000000..7d8dc322d --- /dev/null +++ b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts @@ -0,0 +1,61 @@ +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { ITypedApplication } from "../../structures/ITypedApplication"; +import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; +import { StringUtil } from "../../utils/StringUtil"; + +export namespace SdkHttpCloneReferencer { + export const replace = (app: ITypedApplication): void => { + const directory: string = `${app.project.config.output}/structures`; + for (const route of app.routes) + if (route.protocol === "http") + visitRoute({ + directory, + route, + }); + }; + + const visitRoute = (props: { + directory: string; + route: ITypedHttpRoute; + }): void => { + const unique: Set = new Set(); + for (const p of props.route.parameters) + visitType({ + unique, + metadata: p.metadata, + name: (name) => (p.type = { name }), + }); + for (const v of Object.values(props.route.exceptions)) + visitType({ + unique, + metadata: v.metadata, + name: (name) => (v.type = { name }), + }); + visitType({ + unique, + metadata: props.route.success.metadata, + name: (name) => (props.route.success.type = { name }), + }); + props.route.imports = Array.from(unique).map((str) => ({ + file: `${props.directory}/${str}`, + instances: [str], + })); + }; + + const visitType = (p: { + unique: Set; + metadata: Metadata; + name: (key: string) => void; + }): void => { + const enroll = (key: string) => { + if (key.length && StringUtil.isImplicit(key) === false) + p.unique.add(key.split(".")[0]); + }; + for (const alias of p.metadata.aliases) enroll(alias.name); + for (const array of p.metadata.arrays) enroll(array.type.name); + for (const tuple of p.metadata.tuples) enroll(tuple.type.name); + for (const object of p.metadata.objects) enroll(object.name); + p.name(p.metadata.getName()); + }; +} diff --git a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts index 88cd1c315..59c342efa 100644 --- a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts @@ -1,5 +1,6 @@ import ts from "typescript"; import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory"; +import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { INestiaConfig } from "../../INestiaConfig"; import { INestiaProject } from "../../structures/INestiaProject"; @@ -60,7 +61,7 @@ export namespace SdkHttpFunctionProgrammer { ), ], ts.factory.createTypeReferenceNode("Promise", [ - SdkAliasCollection.name(route.success.type), + SdkAliasCollection.output(project)(importer)(route), ]), ts.factory.createBlock( write_body(project.config)(importer)(route, props), @@ -86,7 +87,12 @@ export namespace SdkHttpFunctionProgrammer { SdkImportWizard.Fetcher(!!props.input?.encrypted)(importer), ), )(config.propagate ? "propagate" : "fetch"), - undefined, + config.propagate + ? route.method.toLowerCase() === "get" || + route.method.toLowerCase() === "head" + ? [TypeFactory.keyword("any")] + : [TypeFactory.keyword("any"), TypeFactory.keyword("any")] + : undefined, [ props.input?.contentType !== "multipart/form-data" ? ts.factory.createObjectLiteralExpression( diff --git a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts index 7d59d49c9..0c9d3bb59 100644 --- a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts @@ -92,7 +92,10 @@ export namespace SdkHttpNamespaceProgrammer { "Input", SdkAliasCollection.input(project)(importer)(props.input), ); - if (route.success.type.name !== "void") + if ( + project.config.propagate === true || + route.success.metadata.size() !== 0 + ) declare("Output", SdkAliasCollection.output(project)(importer)(route)); return array; }; diff --git a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts index 900b1146b..a75054c43 100644 --- a/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpSimulationProgrammer.ts @@ -141,28 +141,31 @@ export namespace SdkHttpSimulationProgrammer { ...assert(project)(importer)(route), ts.factory.createReturnStatement( project.config.propagate - ? ts.factory.createObjectLiteralExpression( - [ - ts.factory.createPropertyAssignment( - "success", - ts.factory.createTrue(), - ), - ts.factory.createPropertyAssignment( - "status", - ExpressionFactory.number( - route.success.status ?? - (route.method === "POST" ? 201 : 200), + ? ts.factory.createAsExpression( + ts.factory.createObjectLiteralExpression( + [ + ts.factory.createPropertyAssignment( + "success", + ts.factory.createTrue(), ), - ), - ts.factory.createPropertyAssignment( - "headers", - LiteralFactory.generate({ - "Content-Type": route.success.contentType, - }), - ), - ts.factory.createPropertyAssignment("data", caller()), - ], - true, + ts.factory.createPropertyAssignment( + "status", + ExpressionFactory.number( + route.success.status ?? + (route.method === "POST" ? 201 : 200), + ), + ), + ts.factory.createPropertyAssignment( + "headers", + LiteralFactory.generate({ + "Content-Type": route.success.contentType, + }), + ), + ts.factory.createPropertyAssignment("data", caller()), + ], + true, + ), + ts.factory.createTypeReferenceNode("Output"), ) : caller(), ), diff --git a/packages/sdk/src/structures/IReflectHttpOperation.ts b/packages/sdk/src/structures/IReflectHttpOperation.ts index 8d3b936cd..be6e5c664 100644 --- a/packages/sdk/src/structures/IReflectHttpOperation.ts +++ b/packages/sdk/src/structures/IReflectHttpOperation.ts @@ -15,10 +15,7 @@ export interface IReflectHttpOperation { versions: Array | undefined; parameters: IReflectHttpOperationParameter[]; success: IReflectHttpOperationSuccess; - exceptions: Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - IReflectHttpOperationException - >; + exceptions: Record; security: Record[]; tags: string[]; imports: IReflectTypeImport[]; diff --git a/packages/sdk/src/structures/IReflectHttpOperationException.ts b/packages/sdk/src/structures/IReflectHttpOperationException.ts index de6addc35..5ff3da68c 100644 --- a/packages/sdk/src/structures/IReflectHttpOperationException.ts +++ b/packages/sdk/src/structures/IReflectHttpOperationException.ts @@ -8,8 +8,8 @@ export interface IReflectHttpOperationException { // BASIC PROPERTIES status: number | "2XX" | "3XX" | "4XX" | "5XX"; description: string | null; - example: any; - examples: Record; + example?: any; + examples?: Record; // REFLECTED PROPERTIES type: IReflectType; diff --git a/packages/sdk/src/structures/ITypedApplication.ts b/packages/sdk/src/structures/ITypedApplication.ts index 2442ec2cf..5a498b941 100644 --- a/packages/sdk/src/structures/ITypedApplication.ts +++ b/packages/sdk/src/structures/ITypedApplication.ts @@ -1,8 +1,11 @@ +import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary"; + import { INestiaProject } from "./INestiaProject"; import { ITypedHttpRoute } from "./ITypedHttpRoute"; import { ITypedWebSocketRoute } from "./ITypedWebSocketRoute"; export interface ITypedApplication { project: INestiaProject; + collection: IMetadataDictionary; routes: Array; } diff --git a/packages/sdk/src/transform.ts b/packages/sdk/src/transform.ts index 8df960f56..3c3ad7d89 100644 --- a/packages/sdk/src/transform.ts +++ b/packages/sdk/src/transform.ts @@ -1,9 +1,9 @@ import ts from "typescript"; -import { SdkTransformer } from "./transformers/SdkTransformer"; +import { SdkOperationTransformer } from "./transformers/SdkOperationTransformer"; export const transform = ( program: ts.Program, ): ts.TransformerFactory => - SdkTransformer.transformFile(program.getTypeChecker()); + SdkOperationTransformer.transformFile(program.getTypeChecker()); export default transform; diff --git a/packages/sdk/src/transformers/IOperationMetadata.ts b/packages/sdk/src/transformers/IOperationMetadata.ts index eca834dcf..b2000ccd0 100644 --- a/packages/sdk/src/transformers/IOperationMetadata.ts +++ b/packages/sdk/src/transformers/IOperationMetadata.ts @@ -9,7 +9,7 @@ import { IReflectTypeImport } from "../structures/IReflectTypeImport"; export interface IOperationMetadata { parameters: IOperationMetadata.IParameter[]; success: IOperationMetadata.IResponse; - exceptions: Record; + exceptions: IOperationMetadata.IResponse[]; description: string | null; jsDocTags: IJsDocTagInfo[]; } @@ -26,6 +26,12 @@ export namespace IOperationMetadata { primitive: ValidationPipe; resolved: ValidationPipe; } + export interface IException { + type: IReflectType | null; + imports: IReflectTypeImport[]; + primitive: ValidationPipe; + } + export interface ISchema { components: IMetadataComponents; metadata: IMetadata; diff --git a/packages/sdk/src/transformers/ISdkTransformerContext.ts b/packages/sdk/src/transformers/ISdkOperationTransformerContext.ts similarity index 79% rename from packages/sdk/src/transformers/ISdkTransformerContext.ts rename to packages/sdk/src/transformers/ISdkOperationTransformerContext.ts index 500a6ea2a..4d6d46de8 100644 --- a/packages/sdk/src/transformers/ISdkTransformerContext.ts +++ b/packages/sdk/src/transformers/ISdkOperationTransformerContext.ts @@ -1,7 +1,7 @@ import ts from "typescript"; import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; -export interface ISdkTransformerContext { +export interface ISdkOperationTransformerContext { checker: ts.TypeChecker; api: ts.TransformationContext; collection: MetadataCollection; diff --git a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts b/packages/sdk/src/transformers/SdkOperationProgrammer.ts similarity index 81% rename from packages/sdk/src/transformers/SdkMetadataProgrammer.ts rename to packages/sdk/src/transformers/SdkOperationProgrammer.ts index ad7a3c00c..6899b2649 100644 --- a/packages/sdk/src/transformers/SdkMetadataProgrammer.ts +++ b/packages/sdk/src/transformers/SdkOperationProgrammer.ts @@ -9,15 +9,17 @@ import { ValidationPipe } from "typia/lib/typings/ValidationPipe"; import { Escaper } from "typia/lib/utils/Escaper"; import { ImportAnalyzer } from "../analyses/ImportAnalyzer"; +import { MetadataUtil } from "../utils/MetadataUtil"; import { IOperationMetadata } from "./IOperationMetadata"; -import { ISdkTransformerContext } from "./ISdkTransformerContext"; +import { ISdkOperationTransformerContext } from "./ISdkOperationTransformerContext"; -export namespace SdkMetadataProgrammer { +export namespace SdkOperationProgrammer { export interface IProps { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; generics: WeakMap; node: ts.MethodDeclaration; symbol: ts.Symbol | undefined; + exceptions: ts.TypeNode[]; } export const write = (p: IProps): IOperationMetadata => ({ parameters: p.node.parameters.map((parameter, index) => @@ -38,13 +40,19 @@ export namespace SdkMetadataProgrammer { : null, }), }), - exceptions: {}, // @todo + exceptions: p.exceptions.map((e) => + writeResponse({ + context: p.context, + generics: p.generics, + type: p.context.checker.getTypeFromTypeNode(e), + }), + ), jsDocTags: p.symbol?.getJsDocTags() ?? [], description: p.symbol ? CommentFactory.description(p.symbol) ?? null : null, }); const writeParameter = (props: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; generics: WeakMap; parameter: ts.ParameterDeclaration; index: number; @@ -68,7 +76,7 @@ export namespace SdkMetadataProgrammer { }; const writeResponse = (props: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; generics: WeakMap; type: ts.Type | null; }): IOperationMetadata.IResponse => @@ -78,7 +86,7 @@ export namespace SdkMetadataProgrammer { }); const writeType = (p: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; generics: WeakMap; type: ts.Type | null; required: boolean; @@ -174,38 +182,14 @@ export namespace SdkMetadataProgrammer { }; } -const iterateVisited = (metdata: Metadata): Set => { +const iterateVisited = (metadata: Metadata): Set => { const names: Set = new Set(); - const visited: WeakSet = new WeakSet(); - const iterate = (metadata: Metadata): void => { - if (visited.has(metadata)) return; - visited.add(metadata); - for (const alias of metadata.aliases) { - names.add(alias.name); - iterate(alias.value); - } - for (const array of metadata.arrays) { - names.add(array.type.name); - iterate(array.type.value); - } - for (const tuple of metadata.tuples) { - names.add(tuple.type.name); - tuple.type.elements.map(iterate); - } - for (const object of metadata.objects) { - names.add(object.name); - object.properties.map((p) => { - iterate(p.key); - iterate(p.value); - }); - } - if (metadata.escaped) { - iterate(metadata.escaped.original); - iterate(metadata.escaped.returns); - } - if (metadata.rest) iterate(metadata.rest); - }; - iterate(metdata); + MetadataUtil.visit((m) => { + for (const alias of m.aliases) names.add(alias.name); + for (const array of m.arrays) names.add(array.type.name); + for (const tuple of m.tuples) names.add(tuple.type.name); + for (const object of m.objects) names.add(object.name); + })(metadata); return names; }; diff --git a/packages/sdk/src/transformers/SdkTransformer.ts b/packages/sdk/src/transformers/SdkOperationTransformer.ts similarity index 76% rename from packages/sdk/src/transformers/SdkTransformer.ts rename to packages/sdk/src/transformers/SdkOperationTransformer.ts index b3de45ac5..12b242eb7 100644 --- a/packages/sdk/src/transformers/SdkTransformer.ts +++ b/packages/sdk/src/transformers/SdkOperationTransformer.ts @@ -1,3 +1,4 @@ +import path from "path"; import { HashSet, hash } from "tstl"; import ts from "typescript"; import { LiteralFactory } from "typia/lib/factories/LiteralFactory"; @@ -6,13 +7,13 @@ import { TypeFactory } from "typia/lib/factories/TypeFactory"; import { GenericAnalyzer } from "../analyses/GenericAnalyzer"; import { IOperationMetadata } from "./IOperationMetadata"; -import { ISdkTransformerContext } from "./ISdkTransformerContext"; -import { SdkMetadataProgrammer } from "./SdkMetadataProgrammer"; +import { ISdkOperationTransformerContext } from "./ISdkOperationTransformerContext"; +import { SdkOperationProgrammer } from "./SdkOperationProgrammer"; -export namespace SdkTransformer { +export namespace SdkOperationTransformer { export const transformFile = (checker: ts.TypeChecker) => (api: ts.TransformationContext) => { - const context: ISdkTransformerContext = { + const context: ISdkOperationTransformerContext = { checker, api, collection: new MetadataCollection({ @@ -62,7 +63,7 @@ export namespace SdkTransformer { } const transformNode = (props: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; visitor: IVisitor; node: ts.Node; }): ts.Node => @@ -74,7 +75,7 @@ export namespace SdkTransformer { : props.node; const transformClass = (props: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; visitor: IVisitor; node: ts.ClassDeclaration; }): ts.ClassDeclaration => { @@ -117,7 +118,7 @@ export namespace SdkTransformer { }; const transformMethod = (props: { - context: ISdkTransformerContext; + context: ISdkOperationTransformerContext; visitor: IVisitor; class: ts.ClassDeclaration; generics: WeakMap; @@ -137,7 +138,13 @@ export namespace SdkTransformer { if (props.visitor.visited.has(key)) return props.node; else props.visitor.visited.insert(key); - const metadata: IOperationMetadata = SdkMetadataProgrammer.write(props); + const metadata: IOperationMetadata = SdkOperationProgrammer.write({ + ...props, + exceptions: getExceptionTypes({ + checker: props.context.checker, + decorators, + }), + }); return ts.factory.updateMethodDeclaration( props.node, [ @@ -166,6 +173,26 @@ export namespace SdkTransformer { props.node.body, ); }; + + const getExceptionTypes = (props: { + checker: ts.TypeChecker; + decorators: readonly ts.Decorator[]; + }) => + props.decorators + .map((deco) => { + if (false === ts.isCallExpression(deco.expression)) return null; + const signature: ts.Signature | undefined = + props.checker.getResolvedSignature(deco.expression); + if (signature === undefined) return null; + else if (!signature.declaration) return null; + const location: string = path.resolve( + signature.declaration.getSourceFile()?.fileName ?? "", + ); + if (location.includes(TYPED_EXCEPTION_PATH) === false) return null; + else if (deco.expression.typeArguments?.length !== 1) return null; + return deco.expression.typeArguments[0]; + }) + .filter((t) => t !== null); } class MethodKey { @@ -182,3 +209,11 @@ class MethodKey { return hash(this.className, this.methodName); } } + +const TYPED_EXCEPTION_PATH = path.join( + "@nestia", + "core", + "lib", + "decorators", + `TypedException.d.ts`, +); diff --git a/packages/sdk/src/utils/MetadataUtil.ts b/packages/sdk/src/utils/MetadataUtil.ts new file mode 100644 index 000000000..a34f59a8b --- /dev/null +++ b/packages/sdk/src/utils/MetadataUtil.ts @@ -0,0 +1,26 @@ +import { Metadata as metadata } from "typia/lib/schemas/metadata/Metadata"; + +export namespace MetadataUtil { + export const visit = (closure: (m: metadata) => unknown) => { + const visited: WeakSet = new WeakSet(); + const iterate = (metadata: metadata): void => { + if (visited.has(metadata)) return; + visited.add(metadata); + closure(metadata); + for (const alias of metadata.aliases) iterate(alias.value); + for (const array of metadata.arrays) iterate(array.type.value); + for (const tuple of metadata.tuples) tuple.type.elements.map(iterate); + for (const object of metadata.objects) + object.properties.forEach((p) => { + iterate(p.key); + iterate(p.value); + }); + if (metadata.escaped) { + iterate(metadata.escaped.original); + iterate(metadata.escaped.returns); + } + if (metadata.rest) iterate(metadata.rest); + }; + return iterate; + }; +} diff --git a/packages/sdk/src/utils/StringUtil.ts b/packages/sdk/src/utils/StringUtil.ts index 500a418cf..496a734b9 100644 --- a/packages/sdk/src/utils/StringUtil.ts +++ b/packages/sdk/src/utils/StringUtil.ts @@ -6,4 +6,12 @@ export namespace StringUtil { (keep: string[]) => (change: string): string => keep.includes(change) ? escapeDuplicate(keep)(`_${change}`) : change; + + export const isImplicit = (str: string) => + str === "object" || + str === "__type" || + str === "__object" || + str.startsWith("__type.") || + str.startsWith("__object.") || + str.includes("readonly ["); } diff --git a/packages/sdk/tsconfig.shopping.json b/packages/sdk/tsconfig.shopping.json deleted file mode 100644 index 549143945..000000000 --- a/packages/sdk/tsconfig.shopping.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": [ - "DOM", - "ES2015" - ], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "D:/github/samchon/shopping-backend@master/node_modules/@nestia/sdk/lib", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - "types": [ - "node", - "reflect-metadata" - ], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - "stripInternal": true, - - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ - "plugins": [ - { - "transform": "typia/lib/transform", - "functional": true, - } - ], - "newLine": "LF", - }, - "include": ["src"] -} diff --git a/test/features/all/src/api/Resolved.ts b/test/features/all/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/all/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/all/src/api/functional/all/index.ts b/test/features/all/src/api/functional/all/index.ts index caf3f3615..e4a3df69a 100644 --- a/test/features/all/src/api/functional/all/index.ts +++ b/test/features/all/src/api/functional/all/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -26,7 +26,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/all/src/api/functional/performance/index.ts b/test/features/all/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/all/src/api/functional/performance/index.ts +++ b/test/features/all/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/all/src/api/module.ts b/test/features/all/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/all/src/api/module.ts +++ b/test/features/all/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/Resolved.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/articles/comments/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/articles/comments/index.ts index ead973d98..f2045fa93 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/articles/comments/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v1/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/api/v1/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/bbs/articles/index.ts index 7dae76e91..6eddf3d76 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -105,7 +112,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -123,7 +130,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -137,7 +144,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -161,7 +168,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -179,7 +186,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -193,7 +200,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/common/health/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/common/health/index.ts index 3dc3958f7..1e5477a1e 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/common/health/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/common/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v1/common/health"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/performance/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/performance/index.ts index d3e1201a2..a16df1b62 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/performance/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v1/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @path GET /api/v1/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v1/performance"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/articles/comments/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/articles/comments/index.ts index 957e3c5ca..c8e0e8f7c 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/articles/comments/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v2/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/api/v2/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/bbs/articles/index.ts index 83dfd8096..7b331bb58 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -105,7 +112,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -123,7 +130,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -137,7 +144,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -161,7 +168,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -179,7 +186,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -193,7 +200,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/common/health/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/common/health/index.ts index 4335c2daf..8b057ea17 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/common/health/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/common/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v2/common/health"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/performance/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/performance/index.ts index c337aae63..1358b2b60 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/performance/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v2/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @path GET /api/v2/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v2/performance"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/articles/comments/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/articles/comments/index.ts index b48cb677f..35dcaf924 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/articles/comments/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v3/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/api/v3/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/bbs/articles/index.ts index 496f9e02c..d256a0cdc 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/v3/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/v3/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -105,7 +112,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -123,7 +130,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -137,7 +144,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -161,7 +168,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -179,7 +186,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -193,7 +200,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/common/health/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/common/health/index.ts index 1ba7c1788..fbd6621d7 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/common/health/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/common/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v3/common/health"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/performance/index.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/performance/index.ts index 051a468a7..bd3fe5a70 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/performance/index.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/functional/api/v3/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @path GET /api/v3/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/v3/performance"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/src/api/module.ts b/test/features/app-globalPrefix-versionUri-routerModule/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/src/api/module.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-globalPrefix-versionUri-routerModule/swagger.json b/test/features/app-globalPrefix-versionUri-routerModule/swagger.json index 20903970a..03d2a9796 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/swagger.json +++ b/test/features/app-globalPrefix-versionUri-routerModule/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/api/v1/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/api/v2/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/api/v3/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/api/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v2/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v3/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/api/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/api/v3/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/api/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/api/v2/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/api/v3/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/api/v1/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v2/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v3/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v1/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v2/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v3/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/api/v1/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/api/v2/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/api/v3/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/api/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v2/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v3/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v3/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v2/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v3/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/v1/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v2/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v3/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v1/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v2/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/api/v3/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app-globalPrefix-versionUri/src/api/Resolved.ts b/test/features/app-globalPrefix-versionUri/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-globalPrefix-versionUri/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/bbs/articles/index.ts index 67c65ffb4..181e6d66c 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../../../../structures/IBbsArticle"; @@ -18,16 +18,24 @@ import type { IPage } from "../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -38,21 +46,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/internal/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/internal/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -70,7 +68,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -88,7 +86,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -102,7 +100,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => diff --git a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/bbs/articles/index.ts index cc8ed2e2f..89d52f700 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/internal/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/internal/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/health/index.ts b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/health/index.ts index 97734c7f4..f1d6a08d7 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/health/index.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/internal/v1/health"; diff --git a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/performance/index.ts b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/performance/index.ts index eb35d8e08..d5c9c0911 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/performance/index.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v1/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../../../../structures/IPerformance"; * @path GET /api/internal/v1/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/api/internal/v1/performance"; diff --git a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v2/bbs/articles/index.ts b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v2/bbs/articles/index.ts index 508864aea..8309a8393 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v2/bbs/articles/index.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/functional/api/internal/v2/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/api/internal/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/api/internal/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -73,7 +71,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -91,7 +89,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -105,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-globalPrefix-versionUri/src/api/module.ts b/test/features/app-globalPrefix-versionUri/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-globalPrefix-versionUri/src/api/module.ts +++ b/test/features/app-globalPrefix-versionUri/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-globalPrefix-versionUri/swagger.json b/test/features/app-globalPrefix-versionUri/swagger.json index cefc84a44..3241db647 100644 --- a/test/features/app-globalPrefix-versionUri/swagger.json +++ b/test/features/app-globalPrefix-versionUri/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/api/internal/v1/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/api/internal/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/internal/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/api/internal/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/api/internal/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/api/internal/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/internal/v2/bbs/{section}/articles/{id}":{"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/api/internal/v1/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/api/internal/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/api/internal/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/api/internal/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/api/internal/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/internal/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/api/internal/v2/bbs/{section}/articles/{id}":{"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app-globalPrefix/src/api/Resolved.ts b/test/features/app-globalPrefix/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-globalPrefix/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts index e2b4ce073..5795c6835 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/comments/index.ts @@ -21,7 +21,7 @@ export async function index( section: string, articleId: string & Format<"uuid">, query: IPage.IRequest, -): Promise> { +): Promise>> { return PlainFetcher.fetch( { ...connection, @@ -69,7 +69,7 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -117,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -167,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts index ba572c1e9..86bfee956 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/bbs/articles/index.ts @@ -22,7 +22,7 @@ export async function index( connection: IConnection, section: string, query: IPage.IRequest, -): Promise> { +): Promise>> { return PlainFetcher.fetch( { ...connection, @@ -65,7 +65,7 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -114,7 +114,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -170,7 +170,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts b/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts index 8930f8901..dd7fec7f3 100644 --- a/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts +++ b/test/features/app-globalPrefix/src/api/functional/x/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../../structures/IPerformance"; * @path GET /x/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app-globalPrefix/src/api/module.ts b/test/features/app-globalPrefix/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-globalPrefix/src/api/module.ts +++ b/test/features/app-globalPrefix/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-routerModule/src/api/Resolved.ts b/test/features/app-routerModule/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-routerModule/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-routerModule/src/api/functional/articles/comments/index.ts b/test/features/app-routerModule/src/api/functional/articles/comments/index.ts index 3f707329e..302fbae5e 100644 --- a/test/features/app-routerModule/src/api/functional/articles/comments/index.ts +++ b/test/features/app-routerModule/src/api/functional/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-routerModule/src/api/functional/bbs/articles/index.ts b/test/features/app-routerModule/src/api/functional/bbs/articles/index.ts index 5efa486fc..6591f864c 100644 --- a/test/features/app-routerModule/src/api/functional/bbs/articles/index.ts +++ b/test/features/app-routerModule/src/api/functional/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -105,7 +112,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -123,7 +130,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -137,7 +144,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -161,7 +168,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -179,7 +186,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -193,7 +200,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-routerModule/src/api/functional/common/health/index.ts b/test/features/app-routerModule/src/api/functional/common/health/index.ts index 27dac86cc..272d87414 100644 --- a/test/features/app-routerModule/src/api/functional/common/health/index.ts +++ b/test/features/app-routerModule/src/api/functional/common/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/common/health"; diff --git a/test/features/app-routerModule/src/api/functional/performance/index.ts b/test/features/app-routerModule/src/api/functional/performance/index.ts index 2e71dbb40..37ca258d8 100644 --- a/test/features/app-routerModule/src/api/functional/performance/index.ts +++ b/test/features/app-routerModule/src/api/functional/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/app-routerModule/src/api/module.ts b/test/features/app-routerModule/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-routerModule/src/api/module.ts +++ b/test/features/app-routerModule/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-routerModule/swagger.json b/test/features/app-routerModule/swagger.json index a84ea2c9c..3bcd6c248 100644 --- a/test/features/app-routerModule/swagger.json +++ b/test/features/app-routerModule/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/common/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app-versionHeader/src/api/Resolved.ts b/test/features/app-versionHeader/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-versionHeader/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-versionHeader/src/api/functional/bbs/articles/comments/index.ts b/test/features/app-versionHeader/src/api/functional/bbs/articles/comments/index.ts index fd7e89e56..59975a8a9 100644 --- a/test/features/app-versionHeader/src/api/functional/bbs/articles/comments/index.ts +++ b/test/features/app-versionHeader/src/api/functional/bbs/articles/comments/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -20,16 +20,24 @@ export async function index( connection: IConnection, section: string, articleId: string & Format<"uuid">, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, articleId, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -40,25 +48,15 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( section: string, articleId: string & Format<"uuid">, - query: index.Query, - ) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + query: IPage.IRequest, + ) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** @@ -71,12 +69,21 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, articleId, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, articleId, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -89,7 +96,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -110,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -128,7 +135,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -142,7 +149,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -160,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -178,7 +185,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -192,7 +199,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( diff --git a/test/features/app-versionHeader/src/api/functional/bbs/articles/index.ts b/test/features/app-versionHeader/src/api/functional/bbs/articles/index.ts index 100ca0358..f139f5744 100644 --- a/test/features/app-versionHeader/src/api/functional/bbs/articles/index.ts +++ b/test/features/app-versionHeader/src/api/functional/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -21,16 +21,24 @@ export * as comments from "./comments"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -41,21 +49,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -67,12 +65,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -85,7 +92,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -107,7 +114,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -125,7 +132,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -139,7 +146,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -163,7 +170,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -181,7 +188,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -195,7 +202,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-versionHeader/src/api/functional/health/index.ts b/test/features/app-versionHeader/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/app-versionHeader/src/api/functional/health/index.ts +++ b/test/features/app-versionHeader/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/app-versionHeader/src/api/functional/performance/index.ts b/test/features/app-versionHeader/src/api/functional/performance/index.ts index 2e71dbb40..37ca258d8 100644 --- a/test/features/app-versionHeader/src/api/functional/performance/index.ts +++ b/test/features/app-versionHeader/src/api/functional/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/app-versionHeader/src/api/module.ts b/test/features/app-versionHeader/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-versionHeader/src/api/module.ts +++ b/test/features/app-versionHeader/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-versionHeader/swagger.json b/test/features/app-versionHeader/swagger.json index 89c2b1fc6..10dfa0a8a 100644 --- a/test/features/app-versionHeader/swagger.json +++ b/test/features/app-versionHeader/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}},"/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}},"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/bbs/{section}/articles/{articleId}/comments":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsComment"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}},"/bbs/{section}/articles/{articleId}/comments/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}},"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"articleId","in":"path","schema":{"type":"string","format":"uuid"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsComment"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IPageIBbsComment":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsComment"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsComment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","body","files"]},"IBbsComment.IStore":{"type":"object","properties":{"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app-versionUri/src/api/Resolved.ts b/test/features/app-versionUri/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app-versionUri/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app-versionUri/src/api/functional/bbs/articles/index.ts b/test/features/app-versionUri/src/api/functional/bbs/articles/index.ts index 7631a5fc3..6ca5fc7e4 100644 --- a/test/features/app-versionUri/src/api/functional/bbs/articles/index.ts +++ b/test/features/app-versionUri/src/api/functional/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../../structures/IBbsArticle"; @@ -18,16 +18,24 @@ import type { IPage } from "../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -38,21 +46,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -70,7 +68,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -88,7 +86,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -102,7 +100,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => diff --git a/test/features/app-versionUri/src/api/functional/v1/bbs/articles/index.ts b/test/features/app-versionUri/src/api/functional/v1/bbs/articles/index.ts index 50c41d9e7..1cf351c78 100644 --- a/test/features/app-versionUri/src/api/functional/v1/bbs/articles/index.ts +++ b/test/features/app-versionUri/src/api/functional/v1/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/v1/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -65,12 +63,21 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(section, id), + }, + ); } export namespace at { export type Output = Primitive; @@ -83,7 +90,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-versionUri/src/api/functional/v1/health/index.ts b/test/features/app-versionUri/src/api/functional/v1/health/index.ts index 085da34a5..5ff23dfce 100644 --- a/test/features/app-versionUri/src/api/functional/v1/health/index.ts +++ b/test/features/app-versionUri/src/api/functional/v1/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/v1/health"; diff --git a/test/features/app-versionUri/src/api/functional/v1/performance/index.ts b/test/features/app-versionUri/src/api/functional/v1/performance/index.ts index ab54cb7e9..19368647e 100644 --- a/test/features/app-versionUri/src/api/functional/v1/performance/index.ts +++ b/test/features/app-versionUri/src/api/functional/v1/performance/index.ts @@ -14,12 +14,23 @@ import type { IPerformance } from "../../../structures/IPerformance"; * @path GET /v1/performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +43,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/v1/performance"; diff --git a/test/features/app-versionUri/src/api/functional/v2/bbs/articles/index.ts b/test/features/app-versionUri/src/api/functional/v2/bbs/articles/index.ts index 03cd9ec2b..ee273367f 100644 --- a/test/features/app-versionUri/src/api/functional/v2/bbs/articles/index.ts +++ b/test/features/app-versionUri/src/api/functional/v2/bbs/articles/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -19,16 +19,24 @@ import type { IPage } from "../../../../structures/IPage"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + query: IPage.IRequest, +): Promise>> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; export type Output = Primitive>; export const METADATA = { @@ -39,21 +47,11 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/v2/bbs/${encodeURIComponent(section ?? "null")}/articles`; } /** @@ -73,7 +71,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -91,7 +89,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -105,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => diff --git a/test/features/app-versionUri/src/api/module.ts b/test/features/app-versionUri/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app-versionUri/src/api/module.ts +++ b/test/features/app-versionUri/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/app-versionUri/swagger.json b/test/features/app-versionUri/swagger.json index 44af2c337..051d06955 100644 --- a/test/features/app-versionUri/swagger.json +++ b/test/features/app-versionUri/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/v1/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store a new article","description":"Store a new article."}},"/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/v2/bbs/{section}/articles/{id}":{"put":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Update an article","description":"Update an article."}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/v1/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/v1/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/v1/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/v2/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}}},"/bbs/{section}/articles":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"page","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false},{"name":"limit","in":"query","schema":{"oneOf":[{"type":"null"},{"type":"integer"}]},"required":false}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPageIBbsArticle.ISummary"}}}}}},"post":{"summary":"Store a new article","description":"Store a new article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"}],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/v1/bbs/{section}/articles/{id}":{"get":{"tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/v2/bbs/{section}/articles/{id}":{"put":{"summary":"Update an article","description":"Update an article.","tags":[],"parameters":[{"name":"section","in":"path","schema":{"type":"string"},"required":true,"description":" Section code"},{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true,"description":" Target article ID"}],"requestBody":{"description":"Content to update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":"Updated content","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IPageIBbsArticle.ISummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IBbsArticle.ISummary"}},"pagination":{"$ref":"#/components/schemas/IPage.IPagination"}},"required":["data","pagination"]},"IBbsArticle.ISummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"writer":{"type":"string"},"title":{"type":"string","minLength":3,"maxLength":50},"created_at":{"type":"string","format":"date-time"}},"required":["id","section","writer","title","created_at"]},"IPage.IPagination":{"type":"object","properties":{"current":{"type":"integer"},"limit":{"type":"integer"},"records":{"type":"integer"},"pages":{"type":"integer"}},"required":["current","limit","records","pages"],"description":"Page information."},"IPage.IRequest":{"type":"object","properties":{"page":{"oneOf":[{"type":"null"},{"type":"integer"}]},"limit":{"oneOf":[{"type":"null"},{"type":"integer"}]}},"description":"Page request data"},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"section":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","section","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"oneOf":[{"type":"null"},{"type":"string","format":"uri"}]}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/app/src/api/Resolved.ts b/test/features/app/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/app/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/app/src/api/functional/bbs/articles/comments/index.ts b/test/features/app/src/api/functional/bbs/articles/comments/index.ts index 18c7e901b..59975a8a9 100644 --- a/test/features/app/src/api/functional/bbs/articles/comments/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/comments/index.ts @@ -21,7 +21,7 @@ export async function index( section: string, articleId: string & Format<"uuid">, query: IPage.IRequest, -): Promise> { +): Promise>> { return PlainFetcher.fetch( { ...connection, @@ -69,7 +69,7 @@ export async function at( section: string, articleId: string & Format<"uuid">, id: string & Format<"uuid">, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -117,7 +117,7 @@ export async function store( section: string, articleId: string & Format<"uuid">, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -167,7 +167,7 @@ export async function update( articleId: string & Format<"uuid">, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app/src/api/functional/bbs/articles/index.ts b/test/features/app/src/api/functional/bbs/articles/index.ts index f91291ef9..f139f5744 100644 --- a/test/features/app/src/api/functional/bbs/articles/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/index.ts @@ -22,7 +22,7 @@ export async function index( connection: IConnection, section: string, query: IPage.IRequest, -): Promise> { +): Promise>> { return PlainFetcher.fetch( { ...connection, @@ -65,7 +65,7 @@ export async function at( connection: IConnection, section: string, id: string & Format<"uuid">, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -114,7 +114,7 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -170,7 +170,7 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app/src/api/functional/performance/index.ts b/test/features/app/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/app/src/api/functional/performance/index.ts +++ b/test/features/app/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/app/src/api/module.ts b/test/features/app/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/app/src/api/module.ts +++ b/test/features/app/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/beautify-4/src/api/Resolved.ts b/test/features/beautify-4/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/beautify-4/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/beautify-4/src/api/functional/body/index.ts b/test/features/beautify-4/src/api/functional/body/index.ts index f6fc67d54..a482717fa 100644 --- a/test/features/beautify-4/src/api/functional/body/index.ts +++ b/test/features/beautify-4/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -43,7 +43,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -57,7 +57,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; @@ -90,7 +90,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive>; + export type Input = Resolved>; export const METADATA = { method: "PUT", @@ -103,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => diff --git a/test/features/beautify-4/src/api/functional/health/index.ts b/test/features/beautify-4/src/api/functional/health/index.ts index f9e1d3ac1..c08d3076e 100644 --- a/test/features/beautify-4/src/api/functional/health/index.ts +++ b/test/features/beautify-4/src/api/functional/health/index.ts @@ -21,11 +21,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -36,7 +45,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/beautify-4/src/api/functional/performance/index.ts b/test/features/beautify-4/src/api/functional/performance/index.ts index e569c8019..1217397aa 100644 --- a/test/features/beautify-4/src/api/functional/performance/index.ts +++ b/test/features/beautify-4/src/api/functional/performance/index.ts @@ -23,12 +23,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -41,7 +52,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/beautify-4/src/api/module.ts b/test/features/beautify-4/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/beautify-4/src/api/module.ts +++ b/test/features/beautify-4/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/beautify-4/swagger.json b/test/features/beautify-4/swagger.json index dc3953a16..0ddc5957b 100644 --- a/test/features/beautify-4/swagger.json +++ b/test/features/beautify-4/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.9.0-dev.20240728", + "version": "3.11.0-dev.20240813-11", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -17,6 +17,8 @@ "paths": { "/health": { "get": { + "summary": "Health check API", + "description": "Health check API.\n\nJust for health checking API liveness.", "tags": [ "system", "health" @@ -24,15 +26,17 @@ "parameters": [], "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } - }, - "summary": "Health check API", - "description": "Health check API.\n\nJust for health checking API liveness." + } } }, "/performance": { "get": { + "summary": "Get server performance info", + "description": "Get server performance info.", "tags": [ "system", "performance" @@ -49,13 +53,13 @@ } } } - }, - "summary": "Get server performance info", - "description": "Get server performance info." + } } }, "/body": { "post": { + "summary": "Store an article", + "description": "Store an article.", "tags": [], "parameters": [], "requestBody": { @@ -80,9 +84,7 @@ } } } - }, - "summary": "Store an article", - "description": "Store an article." + } } }, "/body/{id}": { @@ -111,7 +113,9 @@ }, "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } } } @@ -252,9 +256,17 @@ "voluntaryContextSwitches" ] }, - "IBbsArticle.IStore": { + "IBbsArticle": { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, "title": { "type": "string", "minLength": 3, @@ -271,6 +283,8 @@ } }, "required": [ + "id", + "created_at", "title", "body", "files" @@ -313,17 +327,9 @@ "url" ] }, - "IBbsArticle": { + "IBbsArticle.IStore": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, "title": { "type": "string", "minLength": 3, @@ -340,8 +346,6 @@ } }, "required": [ - "id", - "created_at", "title", "body", "files" @@ -370,9 +374,7 @@ }, "securitySchemes": { "bearer": { - "type": "apiKey", - "in": "header", - "name": "Authorization" + "type": "apiKey" } } }, diff --git a/test/features/beautify-false/src/api/Resolved.ts b/test/features/beautify-false/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/beautify-false/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/beautify-false/src/api/functional/body/index.ts b/test/features/beautify-false/src/api/functional/body/index.ts index f6fc67d54..a482717fa 100644 --- a/test/features/beautify-false/src/api/functional/body/index.ts +++ b/test/features/beautify-false/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -43,7 +43,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -57,7 +57,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; @@ -90,7 +90,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive>; + export type Input = Resolved>; export const METADATA = { method: "PUT", @@ -103,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => diff --git a/test/features/beautify-false/src/api/functional/health/index.ts b/test/features/beautify-false/src/api/functional/health/index.ts index f9e1d3ac1..c08d3076e 100644 --- a/test/features/beautify-false/src/api/functional/health/index.ts +++ b/test/features/beautify-false/src/api/functional/health/index.ts @@ -21,11 +21,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -36,7 +45,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/beautify-false/src/api/functional/performance/index.ts b/test/features/beautify-false/src/api/functional/performance/index.ts index e569c8019..1217397aa 100644 --- a/test/features/beautify-false/src/api/functional/performance/index.ts +++ b/test/features/beautify-false/src/api/functional/performance/index.ts @@ -23,12 +23,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -41,7 +52,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/beautify-false/src/api/module.ts b/test/features/beautify-false/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/beautify-false/src/api/module.ts +++ b/test/features/beautify-false/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/beautify-false/swagger.json b/test/features/beautify-false/swagger.json index 83291dbf2..9fc7e6233 100644 --- a/test/features/beautify-false/swagger.json +++ b/test/features/beautify-false/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":""}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness.","tags":["system","health"],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"summary":"Get server performance info","description":"Get server performance info.","tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"summary":"Store an article","description":"Store an article.","tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/beautify/src/api/Resolved.ts b/test/features/beautify/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/beautify/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/beautify/src/api/functional/body/index.ts b/test/features/beautify/src/api/functional/body/index.ts index f6fc67d54..a482717fa 100644 --- a/test/features/beautify/src/api/functional/body/index.ts +++ b/test/features/beautify/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -43,7 +43,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -57,7 +57,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; @@ -90,7 +90,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive>; + export type Input = Resolved>; export const METADATA = { method: "PUT", @@ -103,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => diff --git a/test/features/beautify/src/api/functional/health/index.ts b/test/features/beautify/src/api/functional/health/index.ts index f9e1d3ac1..c08d3076e 100644 --- a/test/features/beautify/src/api/functional/health/index.ts +++ b/test/features/beautify/src/api/functional/health/index.ts @@ -21,11 +21,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -36,7 +45,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/beautify/src/api/functional/performance/index.ts b/test/features/beautify/src/api/functional/performance/index.ts index e569c8019..1217397aa 100644 --- a/test/features/beautify/src/api/functional/performance/index.ts +++ b/test/features/beautify/src/api/functional/performance/index.ts @@ -23,12 +23,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -41,7 +52,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/beautify/src/api/module.ts b/test/features/beautify/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/beautify/src/api/module.ts +++ b/test/features/beautify/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/beautify/swagger.json b/test/features/beautify/swagger.json index 713c0fe4a..751916896 100644 --- a/test/features/beautify/swagger.json +++ b/test/features/beautify/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.9.0-dev.20240728", + "version": "3.11.0-dev.20240813-11", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -17,6 +17,8 @@ "paths": { "/health": { "get": { + "summary": "Health check API", + "description": "Health check API.\n\nJust for health checking API liveness.", "tags": [ "system", "health" @@ -24,15 +26,17 @@ "parameters": [], "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } - }, - "summary": "Health check API", - "description": "Health check API.\n\nJust for health checking API liveness." + } } }, "/performance": { "get": { + "summary": "Get server performance info", + "description": "Get server performance info.", "tags": [ "system", "performance" @@ -49,13 +53,13 @@ } } } - }, - "summary": "Get server performance info", - "description": "Get server performance info." + } } }, "/body": { "post": { + "summary": "Store an article", + "description": "Store an article.", "tags": [], "parameters": [], "requestBody": { @@ -80,9 +84,7 @@ } } } - }, - "summary": "Store an article", - "description": "Store an article." + } } }, "/body/{id}": { @@ -111,7 +113,9 @@ }, "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } } } @@ -252,9 +256,17 @@ "voluntaryContextSwitches" ] }, - "IBbsArticle.IStore": { + "IBbsArticle": { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, "title": { "type": "string", "minLength": 3, @@ -271,6 +283,8 @@ } }, "required": [ + "id", + "created_at", "title", "body", "files" @@ -313,17 +327,9 @@ "url" ] }, - "IBbsArticle": { + "IBbsArticle.IStore": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, "title": { "type": "string", "minLength": 3, @@ -340,8 +346,6 @@ } }, "required": [ - "id", - "created_at", "title", "body", "files" @@ -370,9 +374,7 @@ }, "securitySchemes": { "bearer": { - "type": "apiKey", - "in": "header", - "name": "Authorization" + "type": "apiKey" } } }, diff --git a/test/features/body-config-assert/src/api/Resolved.ts b/test/features/body-config-assert/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-assert/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-assert/src/api/functional/body/index.ts b/test/features/body-config-assert/src/api/functional/body/index.ts deleted file mode 100644 index cbdd94d58..000000000 --- a/test/features/body-config-assert/src/api/functional/body/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.body - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; - -import type { IBbsArticle } from "../../structures/IBbsArticle"; - -/** - * @controller TypedBodyController.store - * @path POST /body - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function store( - connection: IConnection, - input: store.Input, -): Promise { - return PlainFetcher.fetch( - { - ...connection, - headers: { - ...connection.headers, - "Content-Type": "application/json", - }, - }, - { - ...store.METADATA, - template: store.METADATA.path, - path: store.path(), - }, - input, - ); -} -export namespace store { - export type Input = Primitive; - export type Output = Primitive; - - export const METADATA = { - method: "POST", - path: "/body", - request: { - type: "application/json", - encrypted: false, - }, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = () => "/body"; -} diff --git a/test/features/body-config-assert/src/api/functional/health/index.ts b/test/features/body-config-assert/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-assert/src/api/functional/health/index.ts +++ b/test/features/body-config-assert/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-assert/src/api/functional/index.ts b/test/features/body-config-assert/src/api/functional/index.ts deleted file mode 100644 index 945cd4c67..000000000 --- a/test/features/body-config-assert/src/api/functional/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -export * as health from "./health"; -export * as performance from "./performance"; -export * as body from "./body"; diff --git a/test/features/body-config-assert/src/api/functional/performance/index.ts b/test/features/body-config-assert/src/api/functional/performance/index.ts deleted file mode 100644 index 2e71dbb40..000000000 --- a/test/features/body-config-assert/src/api/functional/performance/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.performance - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; - -import type { IPerformance } from "../../structures/IPerformance"; - -/** - * @controller PerformanceController.get - * @path GET /performance - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); -} -export namespace get { - export type Output = Primitive; - - export const METADATA = { - method: "GET", - path: "/performance", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = () => "/performance"; -} diff --git a/test/features/body-config-assert/src/api/module.ts b/test/features/body-config-assert/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-assert/src/api/module.ts +++ b/test/features/body-config-assert/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts deleted file mode 100644 index 25e6fe0a2..000000000 --- a/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; - -import api from "../../../../api"; -import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; - -export const test_api_body_store = async (connection: api.IConnection) => { - const output: Primitive = await api.functional.body.store( - connection, - typia.random(), - ); - typia.assert(output); -}; diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts deleted file mode 100644 index 8766b1129..000000000 --- a/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts +++ /dev/null @@ -1,8 +0,0 @@ -import typia from "typia"; - -import api from "../../../../api"; - -export const test_api_health_get = async (connection: api.IConnection) => { - const output = await api.functional.health.get(connection); - typia.assert(output); -}; diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts deleted file mode 100644 index 54cdfd218..000000000 --- a/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Primitive } from "@nestia/fetcher"; -import typia from "typia"; - -import api from "../../../../api"; -import type { IPerformance } from "../../../../api/structures/IPerformance"; - -export const test_api_performance_get = async (connection: api.IConnection) => { - const output: Primitive = - await api.functional.performance.get(connection); - typia.assert(output); -}; diff --git a/test/features/body-config-assert/swagger.json b/test/features/body-config-assert/swagger.json deleted file mode 100644 index 3a0eaff86..000000000 --- a/test/features/body-config-assert/swagger.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-assertClone/src/api/functional/body/index.ts b/test/features/body-config-assertClone/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-assertClone/src/api/functional/body/index.ts +++ b/test/features/body-config-assertClone/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-assertClone/src/api/functional/health/index.ts b/test/features/body-config-assertClone/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-assertClone/src/api/functional/health/index.ts +++ b/test/features/body-config-assertClone/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-assertClone/src/api/functional/performance/index.ts b/test/features/body-config-assertClone/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-assertClone/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertClone/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-assertClone/swagger.json b/test/features/body-config-assertClone/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-assertClone/swagger.json +++ b/test/features/body-config-assertClone/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-assertEquals/src/api/functional/body/index.ts b/test/features/body-config-assertEquals/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-assertEquals/src/api/functional/body/index.ts +++ b/test/features/body-config-assertEquals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-assertEquals/src/api/functional/health/index.ts b/test/features/body-config-assertEquals/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-assertEquals/src/api/functional/health/index.ts +++ b/test/features/body-config-assertEquals/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-assertEquals/src/api/functional/performance/index.ts b/test/features/body-config-assertEquals/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-assertEquals/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertEquals/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-assertEquals/swagger.json b/test/features/body-config-assertEquals/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-assertEquals/swagger.json +++ b/test/features/body-config-assertEquals/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-assertPrune/src/api/functional/body/index.ts b/test/features/body-config-assertPrune/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-assertPrune/src/api/functional/body/index.ts +++ b/test/features/body-config-assertPrune/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-assertPrune/src/api/functional/health/index.ts b/test/features/body-config-assertPrune/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-assertPrune/src/api/functional/health/index.ts +++ b/test/features/body-config-assertPrune/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-assertPrune/src/api/functional/performance/index.ts b/test/features/body-config-assertPrune/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-assertPrune/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertPrune/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-assertPrune/swagger.json b/test/features/body-config-assertPrune/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-assertPrune/swagger.json +++ b/test/features/body-config-assertPrune/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-equals/src/api/functional/body/index.ts b/test/features/body-config-equals/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-equals/src/api/functional/body/index.ts +++ b/test/features/body-config-equals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-equals/src/api/functional/health/index.ts b/test/features/body-config-equals/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-equals/src/api/functional/health/index.ts +++ b/test/features/body-config-equals/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-equals/src/api/functional/performance/index.ts b/test/features/body-config-equals/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-equals/src/api/functional/performance/index.ts +++ b/test/features/body-config-equals/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-equals/swagger.json b/test/features/body-config-equals/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-equals/swagger.json +++ b/test/features/body-config-equals/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-is/src/api/functional/body/index.ts b/test/features/body-config-is/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-is/src/api/functional/body/index.ts +++ b/test/features/body-config-is/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-is/src/api/functional/health/index.ts b/test/features/body-config-is/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-is/src/api/functional/health/index.ts +++ b/test/features/body-config-is/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-is/src/api/functional/performance/index.ts b/test/features/body-config-is/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-is/src/api/functional/performance/index.ts +++ b/test/features/body-config-is/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-is/swagger.json b/test/features/body-config-is/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-is/swagger.json +++ b/test/features/body-config-is/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-validate/src/api/functional/body/index.ts b/test/features/body-config-validate/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-validate/src/api/functional/body/index.ts +++ b/test/features/body-config-validate/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-validate/src/api/functional/health/index.ts b/test/features/body-config-validate/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-validate/src/api/functional/health/index.ts +++ b/test/features/body-config-validate/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-validate/src/api/functional/performance/index.ts b/test/features/body-config-validate/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-validate/src/api/functional/performance/index.ts +++ b/test/features/body-config-validate/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-validate/swagger.json b/test/features/body-config-validate/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-validate/swagger.json +++ b/test/features/body-config-validate/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-validateClone/src/api/functional/body/index.ts b/test/features/body-config-validateClone/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-validateClone/src/api/functional/body/index.ts +++ b/test/features/body-config-validateClone/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-validateClone/src/api/functional/health/index.ts b/test/features/body-config-validateClone/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-validateClone/src/api/functional/health/index.ts +++ b/test/features/body-config-validateClone/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-validateClone/src/api/functional/performance/index.ts b/test/features/body-config-validateClone/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-validateClone/src/api/functional/performance/index.ts +++ b/test/features/body-config-validateClone/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-validateClone/swagger.json b/test/features/body-config-validateClone/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-validateClone/swagger.json +++ b/test/features/body-config-validateClone/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-validateEquals/src/api/functional/body/index.ts b/test/features/body-config-validateEquals/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-validateEquals/src/api/functional/body/index.ts +++ b/test/features/body-config-validateEquals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-validateEquals/src/api/functional/health/index.ts b/test/features/body-config-validateEquals/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-validateEquals/src/api/functional/health/index.ts +++ b/test/features/body-config-validateEquals/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-validateEquals/src/api/functional/performance/index.ts b/test/features/body-config-validateEquals/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-validateEquals/src/api/functional/performance/index.ts +++ b/test/features/body-config-validateEquals/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-validateEquals/swagger.json b/test/features/body-config-validateEquals/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-validateEquals/swagger.json +++ b/test/features/body-config-validateEquals/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-validatePrune/src/api/functional/body/index.ts b/test/features/body-config-validatePrune/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-config-validatePrune/src/api/functional/body/index.ts +++ b/test/features/body-config-validatePrune/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-config-validatePrune/src/api/functional/health/index.ts b/test/features/body-config-validatePrune/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-config-validatePrune/src/api/functional/health/index.ts +++ b/test/features/body-config-validatePrune/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-config-validatePrune/src/api/functional/performance/index.ts b/test/features/body-config-validatePrune/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-config-validatePrune/src/api/functional/performance/index.ts +++ b/test/features/body-config-validatePrune/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-config-validatePrune/swagger.json b/test/features/body-config-validatePrune/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-config-validatePrune/swagger.json +++ b/test/features/body-config-validatePrune/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-generic-default/src/api/functional/body/index.ts b/test/features/body-generic-default/src/api/functional/body/index.ts index db930f490..488f177c9 100644 --- a/test/features/body-generic-default/src/api/functional/body/index.ts +++ b/test/features/body-generic-default/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -43,7 +43,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive>; + export type Input = Resolved>; export type Output = Primitive>; export const METADATA = { @@ -57,7 +57,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; @@ -90,7 +90,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive>>; + export type Input = Resolved>>; export const METADATA = { method: "PUT", @@ -103,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => diff --git a/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts b/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts index 0c8af138b..95c4ffb7a 100644 --- a/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts +++ b/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IGoogleDriveFile } from "../../../../../structures/IGoogleDriveFile"; @@ -29,7 +29,7 @@ export async function single( connection: IConnection, accountCode: string, input: single.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -47,7 +47,7 @@ export async function single( ); } export namespace single { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -61,7 +61,7 @@ export namespace single { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (accountCode: string) => @@ -95,7 +95,7 @@ export async function activate( ); } export namespace activate { - export type Input = Primitive>; + export type Input = Resolved>; export const METADATA = { method: "POST", @@ -108,7 +108,7 @@ export namespace activate { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (accountCode: string) => diff --git a/test/features/body-generic-default/swagger.json b/test/features/body-generic-default/swagger.json index 2c9d0313e..eeb1a620c 100644 --- a/test/features/body-generic-default/swagger.json +++ b/test/features/body-generic-default/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/google/{accountCode}/drives/images/upload/single":{"post":{"tags":["Google"],"parameters":[{"name":"accountCode","in":"path","schema":{"type":"string"},"required":true,"description":" 구글 계정명"}],"requestBody":{"description":"단일 이미지 파일 업로드 정보","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleDriveImageSingleUpload"}}},"required":true},"responses":{"201":{"description":"업로드 완료된 구글 드라이브 파일 정보","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleDriveFile"}}}}},"summary":"단일 이미지 파일 업로드","description":"단일 이미지 파일 업로드.\n\n단 하나의 이미지 파일을 구글 드라이브에 개별 업로드한다."}},"/google/{accountCode}/drives/images/upload/activate":{"post":{"tags":[],"parameters":[{"name":"accountCode","in":"path","schema":{"type":"string"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleTokenActivategoogle-authnever"}}},"required":true},"responses":{"201":{"description":""}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStorestring"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticlestring"}}}}},"summary":"Store an article","description":"Store an article."}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStorestring"}}},"required":true},"responses":{"200":{"description":""}}}}},"components":{"schemas":{"IGoogleDriveImageSingleUpload":{"type":"object","properties":{"token":{"type":"string","x-wrtn-secret-key":"google-auth-key","title":"구글 사용자 인증 키","description":"구글 사용자 인증 키.\n\n구글 드라이브에 이미지 파일을 업로드하기 위하여, 구글 사용자 인증이 선행되어야 한다.\n본 필드값에는, 바로 그 사전 인증하여 발급받은 사용자 인증 키를 할당해주어야 함.\n그리고 그 인증 키는, read 및 write scope 에 대하여 대응 가능하여야 한다."},"url":{"oneOf":[{"type":"string","format":"uri","contentMediaType":"image/png"},{"type":"string","format":"uri","contentMediaType":"image/jpg"}],"title":"이미지 파일 경로","description":"이미지 파일 경로.\n\nWorkflow Editor 상 Inspector 내지 Chat Agent 의 File Uploader 의하여 구성됨."},"location":{"type":"string","title":"파일 경로","description":"이미지 파일이 위치할 경로, 파일명 및 확장자는 제외."},"name":{"type":"string","x-wrtn-placeholder":"파일명을 입력해주세요.","title":"파일명","description":"파일명.\n\n확장자가 제외된, 순수 파일명.\n\n{@link url} 의 실제 파일명과 다르게 업로드 가능."},"extension":{"oneOf":[{"const":"jpg"},{"const":"png"}],"title":"이미지 확장자","description":"이미지 확장자."}},"required":["token","url","location","name","extension"],"description":"구글 드라이브에의 이미지 업로드 DTO.\n\n구글 드라이브에 단일 이미지를 업로드할 때 사용하는 DTO. 만일 복수의 이미지를\n동시에 업로드하고 싶다면, `IGoogleDriveImageMultipleUpload` DTO 및 관련\nAPI 함수를 사용하도록 할 것."},"IGoogleDriveFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"location":{"type":"string"},"name":{"type":"string"},"extension":{"oneOf":[{"type":"null"},{"type":"string"}]},"url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","location","name","extension","url","created_at","updated_at"]},"IGoogleTokenActivategoogle-authnever":{"type":"object","properties":{"token":{"type":"string","x-wrtn-secret-key":"google-auth"}},"required":["token"]},"IBbsArticle.IStorestring":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","format","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticlestring":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","format","files"]},"PartialIBbsArticle.IStorestring":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[{"name":"Google"}],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/google/{accountCode}/drives/images/upload/single":{"post":{"summary":"단일 이미지 파일 업로드","description":"단일 이미지 파일 업로드.\n\n단 하나의 이미지 파일을 구글 드라이브에 개별 업로드한다.","tags":["Google"],"parameters":[{"name":"accountCode","in":"path","schema":{"type":"string"},"required":true,"description":" 구글 계정명"}],"requestBody":{"description":"단일 이미지 파일 업로드 정보","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleDriveImageSingleUpload"}}},"required":true},"responses":{"201":{"description":"업로드 완료된 구글 드라이브 파일 정보","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleDriveFile"}}}}}}},"/google/{accountCode}/drives/images/upload/activate":{"post":{"tags":[],"parameters":[{"name":"accountCode","in":"path","schema":{"type":"string"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IGoogleTokenActivategoogle-authnever"}}},"required":true},"responses":{"201":{"content":{"application/json":{}}}}}},"/body":{"post":{"summary":"Store an article","description":"Store an article.","tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStorestring"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticlestring"}}}}}}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStorestring"}}},"required":true},"responses":{"200":{"content":{"application/json":{}}}}}}},"components":{"schemas":{"IGoogleDriveFile":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"location":{"type":"string"},"name":{"type":"string"},"extension":{"oneOf":[{"type":"null"},{"type":"string"}]},"url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","location","name","extension","url","created_at","updated_at"]},"IGoogleDriveImageSingleUpload":{"type":"object","properties":{"token":{"type":"string","x-wrtn-secret-key":"google-auth-key","title":"구글 사용자 인증 키","description":"구글 사용자 인증 키.\n\n구글 드라이브에 이미지 파일을 업로드하기 위하여, 구글 사용자 인증이 선행되어야 한다.\n본 필드값에는, 바로 그 사전 인증하여 발급받은 사용자 인증 키를 할당해주어야 함.\n그리고 그 인증 키는, read 및 write scope 에 대하여 대응 가능하여야 한다."},"url":{"oneOf":[{"type":"string","format":"uri","contentMediaType":"image/png"},{"type":"string","format":"uri","contentMediaType":"image/jpg"}],"title":"이미지 파일 경로","description":"이미지 파일 경로.\n\nWorkflow Editor 상 Inspector 내지 Chat Agent 의 File Uploader 의하여 구성됨."},"location":{"type":"string","title":"파일 경로","description":"이미지 파일이 위치할 경로, 파일명 및 확장자는 제외."},"name":{"type":"string","x-wrtn-placeholder":"파일명을 입력해주세요.","title":"파일명","description":"파일명.\n\n확장자가 제외된, 순수 파일명.\n\n{@link url} 의 실제 파일명과 다르게 업로드 가능."},"extension":{"oneOf":[{"const":"jpg"},{"const":"png"}],"title":"이미지 확장자","description":"이미지 확장자."}},"required":["token","url","location","name","extension"],"description":"구글 드라이브에의 이미지 업로드 DTO.\n\n구글 드라이브에 단일 이미지를 업로드할 때 사용하는 DTO. 만일 복수의 이미지를\n동시에 업로드하고 싶다면, `IGoogleDriveImageMultipleUpload` DTO 및 관련\nAPI 함수를 사용하도록 할 것."},"IGoogleTokenActivategoogle-authnever":{"type":"object","properties":{"token":{"type":"string","x-wrtn-secret-key":"google-auth"}},"required":["token"]},"IBbsArticlestring":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","format","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStorestring":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","format","files"]},"PartialIBbsArticle.IStorestring":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"format":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[{"name":"Google"}],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-manual-assert/src/api/functional/body/index.ts b/test/features/body-manual-assert/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-manual-assert/src/api/functional/body/index.ts +++ b/test/features/body-manual-assert/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-manual-assert/src/api/functional/health/index.ts b/test/features/body-manual-assert/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-manual-assert/src/api/functional/health/index.ts +++ b/test/features/body-manual-assert/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-manual-assert/src/api/functional/performance/index.ts b/test/features/body-manual-assert/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-manual-assert/src/api/functional/performance/index.ts +++ b/test/features/body-manual-assert/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-manual-assert/swagger.json b/test/features/body-manual-assert/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-manual-assert/swagger.json +++ b/test/features/body-manual-assert/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-manual-is/src/api/functional/body/index.ts b/test/features/body-manual-is/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-manual-is/src/api/functional/body/index.ts +++ b/test/features/body-manual-is/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-manual-is/src/api/functional/health/index.ts b/test/features/body-manual-is/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-manual-is/src/api/functional/health/index.ts +++ b/test/features/body-manual-is/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-manual-is/src/api/functional/performance/index.ts b/test/features/body-manual-is/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-manual-is/src/api/functional/performance/index.ts +++ b/test/features/body-manual-is/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-manual-is/swagger.json b/test/features/body-manual-is/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-manual-is/swagger.json +++ b/test/features/body-manual-is/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-manual-validate/src/api/functional/body/index.ts b/test/features/body-manual-validate/src/api/functional/body/index.ts index cbdd94d58..11634cdc1 100644 --- a/test/features/body-manual-validate/src/api/functional/body/index.ts +++ b/test/features/body-manual-validate/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return PlainFetcher.fetch( { ...connection, @@ -35,7 +35,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -49,7 +49,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/body-manual-validate/src/api/functional/health/index.ts b/test/features/body-manual-validate/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/body-manual-validate/src/api/functional/health/index.ts +++ b/test/features/body-manual-validate/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body-manual-validate/src/api/functional/performance/index.ts b/test/features/body-manual-validate/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/body-manual-validate/src/api/functional/performance/index.ts +++ b/test/features/body-manual-validate/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body-manual-validate/swagger.json b/test/features/body-manual-validate/swagger.json index 3a0eaff86..82b3eb72b 100644 --- a/test/features/body-manual-validate/swagger.json +++ b/test/features/body-manual-validate/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body/src/api/Resolved.ts b/test/features/body/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body/src/api/functional/body/index.ts b/test/features/body/src/api/functional/body/index.ts index f6fc67d54..a482717fa 100644 --- a/test/features/body/src/api/functional/body/index.ts +++ b/test/features/body/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, @@ -43,7 +43,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -57,7 +57,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; @@ -90,7 +90,7 @@ export async function update( ); } export namespace update { - export type Input = Primitive>; + export type Input = Resolved>; export const METADATA = { method: "PUT", @@ -103,7 +103,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => diff --git a/test/features/body/src/api/functional/health/index.ts b/test/features/body/src/api/functional/health/index.ts index f9e1d3ac1..c08d3076e 100644 --- a/test/features/body/src/api/functional/health/index.ts +++ b/test/features/body/src/api/functional/health/index.ts @@ -21,11 +21,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -36,7 +45,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/body/src/api/functional/performance/index.ts b/test/features/body/src/api/functional/performance/index.ts index e569c8019..1217397aa 100644 --- a/test/features/body/src/api/functional/performance/index.ts +++ b/test/features/body/src/api/functional/performance/index.ts @@ -23,12 +23,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -41,7 +52,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/body/src/api/module.ts b/test/features/body/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body/src/api/module.ts +++ b/test/features/body/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body/swagger.json b/test/features/body/swagger.json index 83291dbf2..9fc7e6233 100644 --- a/test/features/body/swagger.json +++ b/test/features/body/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":""}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness.","tags":["system","health"],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"summary":"Get server performance info","description":"Get server performance info.","tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"summary":"Store an article","description":"Store an article.","tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"content":{"application/json":{}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/cli-config-project/src/api/functional/body/index.ts b/test/features/cli-config-project/src/api/functional/body/index.ts index ab651405e..76c310425 100644 --- a/test/features/cli-config-project/src/api/functional/body/index.ts +++ b/test/features/cli-config-project/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -26,7 +26,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return !!connection.simulate ? store.simulate(connection, input) : PlainFetcher.fetch( @@ -46,7 +46,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -60,7 +60,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/cli-config-project/src/api/functional/health/index.ts b/test/features/cli-config-project/src/api/functional/health/index.ts index 3e3a0b3b0..dba160cca 100644 --- a/test/features/cli-config-project/src/api/functional/health/index.ts +++ b/test/features/cli-config-project/src/api/functional/health/index.ts @@ -24,11 +24,20 @@ import typia from "typia"; export async function get(connection: IConnection): Promise { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -39,7 +48,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/cli-config-project/src/api/functional/performance/index.ts b/test/features/cli-config-project/src/api/functional/performance/index.ts index df538a4d6..d8b8906ba 100644 --- a/test/features/cli-config-project/src/api/functional/performance/index.ts +++ b/test/features/cli-config-project/src/api/functional/performance/index.ts @@ -24,14 +24,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -44,7 +53,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/cli-config-project/swagger.json b/test/features/cli-config-project/swagger.json index ef022e1b5..d2031ecd9 100644 --- a/test/features/cli-config-project/swagger.json +++ b/test/features/cli-config-project/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness.","tags":["system","health"],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"summary":"Get server performance info","description":"Get server performance info.","tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"summary":"Store an article","description":"Store an article.","tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/cli-config/src/api/functional/body/index.ts b/test/features/cli-config/src/api/functional/body/index.ts index ab651405e..76c310425 100644 --- a/test/features/cli-config/src/api/functional/body/index.ts +++ b/test/features/cli-config/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -26,7 +26,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise { return !!connection.simulate ? store.simulate(connection, input) : PlainFetcher.fetch( @@ -46,7 +46,7 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { @@ -60,7 +60,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/body"; diff --git a/test/features/cli-config/src/api/functional/health/index.ts b/test/features/cli-config/src/api/functional/health/index.ts index 3e3a0b3b0..dba160cca 100644 --- a/test/features/cli-config/src/api/functional/health/index.ts +++ b/test/features/cli-config/src/api/functional/health/index.ts @@ -24,11 +24,20 @@ import typia from "typia"; export async function get(connection: IConnection): Promise { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -39,7 +48,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/cli-config/src/api/functional/performance/index.ts b/test/features/cli-config/src/api/functional/performance/index.ts index df538a4d6..d8b8906ba 100644 --- a/test/features/cli-config/src/api/functional/performance/index.ts +++ b/test/features/cli-config/src/api/functional/performance/index.ts @@ -24,14 +24,23 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -44,7 +53,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/cli-config/swagger.json b/test/features/cli-config/swagger.json index ef022e1b5..d2031ecd9 100644 --- a/test/features/cli-config/swagger.json +++ b/test/features/cli-config/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness.","tags":["system","health"],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"summary":"Get server performance info","description":"Get server performance info.","tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"summary":"Store an article","description":"Store an article.","tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[{"name":"system"},{"name":"health"},{"name":"performance"}],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/cli-project/src/api/functional/health/index.ts b/test/features/cli-project/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/cli-project/src/api/functional/health/index.ts +++ b/test/features/cli-project/src/api/functional/health/index.ts @@ -13,11 +13,20 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export const METADATA = { @@ -28,7 +37,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; diff --git a/test/features/cli-project/src/api/functional/performance/index.ts b/test/features/cli-project/src/api/functional/performance/index.ts index 2e71dbb40..749a475c8 100644 --- a/test/features/cli-project/src/api/functional/performance/index.ts +++ b/test/features/cli-project/src/api/functional/performance/index.ts @@ -14,12 +14,21 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { - return PlainFetcher.fetch(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); +export async function get(connection: IConnection): Promise { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { export type Output = Primitive; @@ -32,7 +41,7 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance"; diff --git a/test/features/cli-project/swagger.json b/test/features/cli-project/swagger.json index 6dd2589df..247073c50 100644 --- a/test/features/cli-project/swagger.json +++ b/test/features/cli-project/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/clone-and-exact-optional-property/src/api/functional/health/index.ts b/test/features/clone-and-exact-optional-property/src/api/functional/health/index.ts index 747b90307..e83a79f4c 100644 --- a/test/features/clone-and-exact-optional-property/src/api/functional/health/index.ts +++ b/test/features/clone-and-exact-optional-property/src/api/functional/health/index.ts @@ -13,19 +13,38 @@ import typia from "typia"; * @path GET /health * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise< + IPropagation< + { + 200: undefined; + }, + 200 + > +> { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.propagate(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { - export type Output = IPropagation<{ - 200: undefined; - }>; + export type Output = IPropagation< + { + 200: undefined; + }, + 200 + >; export const METADATA = { method: "GET", @@ -35,13 +54,12 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -54,6 +72,6 @@ export namespace get { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/index.ts b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/index.ts index 34768ff93..bc66faa1c 100644 --- a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/index.ts +++ b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/index.ts @@ -4,7 +4,12 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -18,21 +23,38 @@ export * as partial_type from "./partial_type"; * @path GET /partial-dto-test/original * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function original( - connection: IConnection, -): Promise { +export async function original(connection: IConnection): Promise< + IPropagation< + { + 200: IOriginal; + }, + 200 + > +> { return !!connection.simulate ? original.simulate(connection) - : PlainFetcher.propagate(connection, { - ...original.METADATA, - template: original.METADATA.path, - path: original.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...original.METADATA, + template: original.METADATA.path, + path: original.path(), + }, + ); } export namespace original { - export type Output = IPropagation<{ - 200: IOriginal; - }>; + export type Output = IPropagation< + { + 200: IOriginal; + }, + 200 + >; export const METADATA = { method: "GET", @@ -42,13 +64,13 @@ export namespace original { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/partial-dto-test/original"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -61,6 +83,6 @@ export namespace original { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_interface/index.ts b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_interface/index.ts index 5f6d2842e..085d5144d 100644 --- a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_interface/index.ts +++ b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_interface/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -25,10 +26,17 @@ import type { IPartialInterface } from "../../../structures/IPartialInterface"; export async function partialInterface( connection: IConnection, body: partialInterface.Input, -): Promise { +): Promise< + IPropagation< + { + 201: IPartialInterface; + }, + 201 + > +> { return !!connection.simulate ? partialInterface.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -46,9 +54,12 @@ export async function partialInterface( } export namespace partialInterface { export type Input = IOriginal.IPartialInterface; - export type Output = IPropagation<{ - 201: IPartialInterface; - }>; + export type Output = IPropagation< + { + 201: IPartialInterface; + }, + 201 + >; export const METADATA = { method: "POST", @@ -61,13 +72,14 @@ export namespace partialInterface { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/partial-dto-test/partial-interface"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, body: partialInterface.Input, @@ -100,6 +112,6 @@ export namespace partialInterface { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_type/index.ts b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_type/index.ts index fd99c90ac..dea1949f5 100644 --- a/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_type/index.ts +++ b/test/features/clone-and-exact-optional-property/src/api/functional/partial_dto_test/partial_type/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -14,8 +15,8 @@ import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb } from "../../../structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb"; -import type { PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd } from "../../../structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd"; +import type { PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr } from "../../../structures/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr"; +import type { PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr } from "../../../structures/PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr"; /** * @controller PartialDTOTestController.partialType @@ -25,10 +26,17 @@ import type { PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_at export async function partialType( connection: IConnection, body: partialType.Input, -): Promise { +): Promise< + IPropagation< + { + 201: PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr; + }, + 201 + > +> { return !!connection.simulate ? partialType.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -46,10 +54,13 @@ export async function partialType( } export namespace partialType { export type Input = - PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd; - export type Output = IPropagation<{ - 201: PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb; - }>; + PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr; + export type Output = IPropagation< + { + 201: PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr; + }, + 201 + >; export const METADATA = { method: "POST", @@ -62,16 +73,18 @@ export namespace partialType { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/partial-dto-test/partial-type"; export const random = ( g?: Partial, - ): Resolved => - typia.random( - g, - ); + ): Resolved< + Primitive + > => + typia.random< + Primitive + >(g); export const simulate = ( connection: IConnection, body: partialType.Input, @@ -104,6 +117,6 @@ export namespace partialType { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-exact-optional-property/src/api/structures/IOriginal.ts b/test/features/clone-and-exact-optional-property/src/api/structures/IOriginal.ts index 612bffeee..896abf4e0 100644 --- a/test/features/clone-and-exact-optional-property/src/api/structures/IOriginal.ts +++ b/test/features/clone-and-exact-optional-property/src/api/structures/IOriginal.ts @@ -1,5 +1,7 @@ import type { Format } from "typia/lib/tags/Format"; +import type { ISomething } from "./ISomething"; + export type IOriginal = { a: string; b: string; @@ -9,13 +11,14 @@ export type IOriginal = { created_at: null | (string & Format<"date-time">); original_optional?: undefined | boolean; undefinable_attr?: undefined | string; + something?: null | undefined | ISomething; }; export namespace IOriginal { export type IPartialInterface = { + c?: undefined | string; email?: null | undefined | (string & Format<"email">); created_at?: null | undefined | (string & Format<"date-time">); original_optional?: undefined | boolean; undefinable_attr?: undefined | string; - c?: undefined | string; }; } diff --git a/test/features/clone-and-exact-optional-property/src/api/structures/ISomething.ts b/test/features/clone-and-exact-optional-property/src/api/structures/ISomething.ts new file mode 100644 index 000000000..ae6c19bc9 --- /dev/null +++ b/test/features/clone-and-exact-optional-property/src/api/structures/ISomething.ts @@ -0,0 +1,4 @@ +export type ISomething = { + a: string; + b: boolean; +}; diff --git a/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb.ts b/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr.ts similarity index 82% rename from test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb.ts rename to test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr.ts index 3d425c462..e271e8fe0 100644 --- a/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb.ts +++ b/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr.ts @@ -3,14 +3,14 @@ import type { Format } from "typia/lib/tags/Format"; /** * Make all properties in T optional */ -export type PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb = +export type PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr = /** * Make all properties in T optional */ { + b?: undefined | string; email?: null | undefined | (string & Format<"email">); created_at?: null | undefined | (string & Format<"date-time">); original_optional?: undefined | boolean; undefinable_attr?: undefined | string; - b?: undefined | string; }; diff --git a/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd.ts b/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd.ts deleted file mode 100644 index 3441d4d01..000000000 --- a/test/features/clone-and-exact-optional-property/src/api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Format } from "typia/lib/tags/Format"; - -/** - * Make all properties in T optional - */ -export type PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd = - /** - * Make all properties in T optional - */ - { - email?: null | undefined | (string & Format<"email">); - created_at?: null | undefined | (string & Format<"date-time">); - original_optional?: undefined | boolean; - undefinable_attr?: undefined | string; - d?: undefined | string; - }; diff --git a/test/features/clone-and-exact-optional-property/src/controllers/PartialDTOTestController.ts b/test/features/clone-and-exact-optional-property/src/controllers/PartialDTOTestController.ts index b57b420e1..a20138dac 100644 --- a/test/features/clone-and-exact-optional-property/src/controllers/PartialDTOTestController.ts +++ b/test/features/clone-and-exact-optional-property/src/controllers/PartialDTOTestController.ts @@ -39,6 +39,12 @@ interface IOriginal { created_at: (string & typia.tags.Format<"date-time">) | null; original_optional?: boolean; undefinable_attr: string | undefined; + something?: ISomething | null; +} + +interface ISomething { + a: string; + b: boolean; } interface IPartialInterface diff --git a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_health_get.ts b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_health_get.ts index 13ab6f844..76f8dbd47 100644 --- a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_health_get.ts +++ b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_health_get.ts @@ -4,8 +4,11 @@ import typia from "typia"; import api from "../../../../api"; export const test_api_health_get = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: undefined; - }> = await api.functional.health.get(connection); + const output: IPropagation< + { + 200: undefined; + }, + 200 + > = await api.functional.health.get(connection); typia.assert(output); }; diff --git a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_original.ts b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_original.ts index 5068670c7..a3619dccf 100644 --- a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_original.ts +++ b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_original.ts @@ -7,8 +7,11 @@ import type { IOriginal } from "../../../../api/structures/IOriginal"; export const test_api_partial_dto_test_original = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IOriginal; - }> = await api.functional.partial_dto_test.original(connection); + const output: IPropagation< + { + 200: IOriginal; + }, + 200 + > = await api.functional.partial_dto_test.original(connection); typia.assert(output); }; diff --git a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_interface_partialInterface.ts b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_interface_partialInterface.ts index 39c5a1599..6d31189a0 100644 --- a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_interface_partialInterface.ts +++ b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_interface_partialInterface.ts @@ -7,9 +7,12 @@ import type { IPartialInterface } from "../../../../api/structures/IPartialInter export const test_api_partial_dto_test_partial_interface_partialInterface = async (connection: api.IConnection) => { - const output: IPropagation<{ - 201: IPartialInterface; - }> = + const output: IPropagation< + { + 201: IPartialInterface; + }, + 201 + > = await api.functional.partial_dto_test.partial_interface.partialInterface( connection, typia.random(), diff --git a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_type_partialType.ts b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_type_partialType.ts index ef643cd82..ddf85a758 100644 --- a/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_type_partialType.ts +++ b/test/features/clone-and-exact-optional-property/src/test/features/api/automated/test_api_partial_dto_test_partial_type_partialType.ts @@ -2,17 +2,20 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb } from "../../../../api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb"; -import type { PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd } from "../../../../api/structures/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd"; +import type { PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr } from "../../../../api/structures/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr"; +import type { PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr } from "../../../../api/structures/PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr"; export const test_api_partial_dto_test_partial_type_partialType = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb; - }> = await api.functional.partial_dto_test.partial_type.partialType( + const output: IPropagation< + { + 201: PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr; + }, + 201 + > = await api.functional.partial_dto_test.partial_type.partialType( connection, - typia.random(), + typia.random(), ); typia.assert(output); }; diff --git a/test/features/clone-and-exact-optional-property/swagger.json b/test/features/clone-and-exact-optional-property/swagger.json index 4817b79b4..934edbbdd 100644 --- a/test/features/clone-and-exact-optional-property/swagger.json +++ b/test/features/clone-and-exact-optional-property/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.9.0-dev.20240728","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":""}}}},"/partial-dto-test/original":{"get":{"tags":[],"parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal"}}}}}}},"/partial-dto-test/partial-interface":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal.IPartialInterface"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPartialInterface"}}}}}}},"/partial-dto-test/partial-type":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd"}}},"required":true},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb"}}}}}}}},"components":{"schemas":{"IOriginal":{"type":"object","properties":{"a":{"type":"string"},"b":{"type":"string"},"c":{"type":"string"},"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"required":["a","b","c","d","email","created_at"]},"IOriginal.IPartialInterface":{"type":"object","properties":{"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"},"c":{"type":"string"}}},"IPartialInterface":{"type":"object","properties":{"a":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrd":{"type":"object","properties":{"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"},"d":{"type":"string"}},"description":"Make all properties in T optional"},"PartialPickIOriginalemailcreated_atoriginal_optionalundefinable_attrb":{"type":"object","properties":{"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"},"b":{"type":"string"}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/partial-dto-test/original":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal"}}}}}}},"/partial-dto-test/partial-interface":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal.IPartialInterface"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPartialInterface"}}}}}}},"/partial-dto-test/partial-type":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr"}}}}}}}},"components":{"schemas":{"IOriginal":{"type":"object","properties":{"a":{"type":"string"},"b":{"type":"string"},"c":{"type":"string"},"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"required":["a","b","c","d","email","created_at"]},"IPartialInterface":{"type":"object","properties":{"a":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"IOriginal.IPartialInterface":{"type":"object","properties":{"c":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"b":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"},"PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/distribute-assert-json/packages/api/package.json b/test/features/distribute-assert-json/packages/api/package.json index 5217e0f5c..2941ddcce 100644 --- a/test/features/distribute-assert-json/packages/api/package.json +++ b/test/features/distribute-assert-json/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" } } \ No newline at end of file diff --git a/test/features/distribute-assert/packages/api/package.json b/test/features/distribute-assert/packages/api/package.json index 5217e0f5c..2941ddcce 100644 --- a/test/features/distribute-assert/packages/api/package.json +++ b/test/features/distribute-assert/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" } } \ No newline at end of file diff --git a/test/features/distribute-json/packages/api/package.json b/test/features/distribute-json/packages/api/package.json index 5217e0f5c..2941ddcce 100644 --- a/test/features/distribute-json/packages/api/package.json +++ b/test/features/distribute-json/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" } } \ No newline at end of file diff --git a/test/features/distribute/packages/api/package.json b/test/features/distribute/packages/api/package.json index 5217e0f5c..2941ddcce 100644 --- a/test/features/distribute/packages/api/package.json +++ b/test/features/distribute/packages/api/package.json @@ -33,6 +33,6 @@ }, "dependencies": { "@nestia/fetcher": "^3.9.0-dev.20240728", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" } } \ No newline at end of file diff --git a/test/features/exception/swagger.json b/test/features/exception/swagger.json index 7ae9ce62c..6c3d6095f 100644 --- a/test/features/exception/swagger.json +++ b/test/features/exception/swagger.json @@ -7,7 +7,7 @@ } ], "info": { - "version": "3.9.0-dev.20240728", + "version": "3.11.0-dev.20240813-11", "title": "@samchon/nestia-test", "description": "Test program of Nestia", "license": { @@ -40,7 +40,6 @@ }, "responses": { "201": { - "description": "", "content": { "application/json": { "schema": { @@ -54,7 +53,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypeGuardErrorany" + "$ref": "#/components/schemas/IInternalServerError" } } } @@ -64,17 +63,16 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/INotFound" + "$ref": "#/components/schemas/IUnprocessibleEntity" } } } }, "428": { - "description": "", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IUnprocessibleEntity" + "$ref": "#/components/schemas/INotFound" } } } @@ -84,7 +82,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IInternalServerError" + "$ref": "#/components/schemas/TypeGuardErrorany" } } } @@ -107,19 +105,18 @@ ], "responses": { "200": { - "description": "", "content": { "application/json": { "schema": { "oneOf": [ - { - "$ref": "#/components/schemas/IBbsArticle" - }, { "$ref": "#/components/schemas/INotFound" }, { "$ref": "#/components/schemas/IUnprocessibleEntity" + }, + { + "$ref": "#/components/schemas/IBbsArticle" } ] } @@ -174,7 +171,6 @@ }, "responses": { "201": { - "description": "", "content": { "application/json": { "schema": { @@ -243,7 +239,6 @@ }, "responses": { "201": { - "description": "", "content": { "application/json": { "schema": { @@ -257,7 +252,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypeGuardErrorany" + "$ref": "#/components/schemas/IInternalServerError" } } } @@ -267,7 +262,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/INotFound" + "$ref": "#/components/schemas/IUnprocessibleEntity" } } } @@ -277,7 +272,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IUnprocessibleEntity" + "$ref": "#/components/schemas/INotFound" } } } @@ -287,7 +282,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IInternalServerError" + "$ref": "#/components/schemas/TypeGuardErrorany" } } } @@ -301,7 +296,9 @@ "parameters": [], "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } } } @@ -312,7 +309,6 @@ "parameters": [], "responses": { "200": { - "description": "", "content": { "application/json": { "schema": { @@ -327,9 +323,17 @@ }, "components": { "schemas": { - "IBbsArticle.IStore": { + "IBbsArticle": { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, "title": { "type": "string", "minLength": 3, @@ -346,6 +350,8 @@ } }, "required": [ + "id", + "created_at", "title", "body", "files" @@ -388,55 +394,50 @@ "url" ] }, - "TypeGuardErrorany": { + "IBbsArticle.IStore": { "type": "object", "properties": { - "method": { - "type": "string" - }, - "path": { - "type": "string" - }, - "expected": { - "type": "string" - }, - "value": {}, - "fake_expected_typed_value_": {}, - "name": { - "type": "string" + "title": { + "type": "string", + "minLength": 3, + "maxLength": 50 }, - "message": { + "body": { "type": "string" }, - "stack": { - "type": "string" + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IAttachmentFile" + } } }, "required": [ - "method", - "expected", - "value", - "name", - "message" + "title", + "body", + "files" ] }, - "INotFound": { + "IInternalServerError": { "type": "object", "properties": { - "schema": { + "name": { "type": "string" }, - "table": { + "message": { "type": "string" }, - "id": { - "type": "string" + "stack": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "schema", - "table", - "id" + "name", + "message", + "stack" ] }, "IUnprocessibleEntity": { @@ -450,60 +451,55 @@ "reason" ] }, - "IInternalServerError": { + "INotFound": { "type": "object", "properties": { - "name": { + "schema": { "type": "string" }, - "message": { + "table": { "type": "string" }, - "stack": { - "type": "array", - "items": { - "type": "string" - } + "id": { + "type": "string" } }, "required": [ - "name", - "message", - "stack" + "schema", + "table", + "id" ] }, - "IBbsArticle": { + "TypeGuardErrorany": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" + "method": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time" + "path": { + "type": "string" }, - "title": { - "type": "string", - "minLength": 3, - "maxLength": 50 + "expected": { + "type": "string" }, - "body": { + "value": {}, + "fake_expected_typed_value_": {}, + "name": { "type": "string" }, - "files": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IAttachmentFile" - } + "message": { + "type": "string" + }, + "stack": { + "type": "string" } }, "required": [ - "id", - "created_at", - "title", - "body", - "files" + "method", + "expected", + "value", + "name", + "message" ] }, "IExceptional.Something": { @@ -675,9 +671,7 @@ }, "securitySchemes": { "bearer": { - "type": "apiKey", - "in": "header", - "name": "Authorization" + "type": "apiKey" } } }, diff --git a/test/package.json b/test/package.json index 606d8341f..10311fea8 100644 --- a/test/package.json +++ b/test/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@nestia/sdk": "../packages/sdk/nestia-sdk-3.11.0-dev.20240813-11.tgz", "@nestjs/swagger": "^7.1.2", - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.4", "@types/express": "^4.17.17", "@types/node": "20.11.16", "@types/uuid": "^9.0.8", @@ -49,7 +49,7 @@ "@nestjs/platform-fastify": "^10.3.5", "tgrid": "^1.0.3", "tstl": "^3.0.0", - "typia": "6.8.0-dev.20240812", + "typia": "6.8.0", "uuid": "^9.0.1" } } \ No newline at end of file diff --git a/website/package.json b/website/package.json index e78b7e390..635788f41 100644 --- a/website/package.json +++ b/website/package.json @@ -24,7 +24,7 @@ "@mui/material": "5.15.6", "@mui/system": "5.15.6", "@nestia/migrate": "^0.16.0", - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.4", "@stackblitz/sdk": "^1.9.0", "js-yaml": "^4.1.0", "next": "14.2.5", @@ -34,7 +34,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-mui-fileuploader": "^0.5.2", - "typia": "^6.8.0-dev.20240812" + "typia": "^6.8.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", From 7b7a10ee7482cdbfa2577edfb5f7207d74b4c622 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 21:49:06 +0900 Subject: [PATCH 10/21] Fix SDK clone mode problem --- .../src/api/functional/body/index.ts | 56 +++++++++++++++++++ .../src/api/functional/index.ts | 9 +++ .../src/api/functional/performance/index.ts | 50 +++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 test/features/body-config-assert/src/api/functional/body/index.ts create mode 100644 test/features/body-config-assert/src/api/functional/index.ts create mode 100644 test/features/body-config-assert/src/api/functional/performance/index.ts diff --git a/test/features/body-config-assert/src/api/functional/body/index.ts b/test/features/body-config-assert/src/api/functional/body/index.ts new file mode 100644 index 000000000..e3c672e50 --- /dev/null +++ b/test/features/body-config-assert/src/api/functional/body/index.ts @@ -0,0 +1,56 @@ +/** + * @packageDocumentation + * @module api.functional.body + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; + +import type { IBbsArticle } from "../../structures/IBbsArticle"; + +/** + * @controller TypedBodyController.store + * @path POST /body + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function store( + connection: IConnection, + input: store.Input, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...store.METADATA, + template: store.METADATA.path, + path: store.path(), + }, + input, + ); +} +export namespace store { + export type Input = Resolved; + export type Output = Primitive; + + export const METADATA = { + method: "POST", + path: "/body", + request: { + type: "application/json", + encrypted: false, + }, + response: { + type: "application/json", + encrypted: false, + }, + status: 201, + } as const; + + export const path = () => "/body"; +} diff --git a/test/features/body-config-assert/src/api/functional/index.ts b/test/features/body-config-assert/src/api/functional/index.ts new file mode 100644 index 000000000..945cd4c67 --- /dev/null +++ b/test/features/body-config-assert/src/api/functional/index.ts @@ -0,0 +1,9 @@ +/** + * @packageDocumentation + * @module api.functional + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +export * as health from "./health"; +export * as performance from "./performance"; +export * as body from "./body"; diff --git a/test/features/body-config-assert/src/api/functional/performance/index.ts b/test/features/body-config-assert/src/api/functional/performance/index.ts new file mode 100644 index 000000000..37ca258d8 --- /dev/null +++ b/test/features/body-config-assert/src/api/functional/performance/index.ts @@ -0,0 +1,50 @@ +/** + * @packageDocumentation + * @module api.functional.performance + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +//================================================================ +import type { IConnection, Primitive } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; + +import type { IPerformance } from "../../structures/IPerformance"; + +/** + * @controller PerformanceController.get + * @path GET /performance + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function get( + connection: IConnection, +): Promise> { + return PlainFetcher.fetch( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); +} +export namespace get { + export type Output = Primitive; + + export const METADATA = { + method: "GET", + path: "/performance", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = () => "/performance"; +} From fc3228dbac00adcdaec4ec90e940c853a6236f49 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 13 Aug 2024 21:49:24 +0900 Subject: [PATCH 11/21] Fix SDK clone mode problem --- packages/cli/package.json | 2 +- packages/cli/src/index.ts | 4 +- .../generates/internal/SdkAliasCollection.ts | 29 +- .../generates/internal/SdkFileProgrammer.ts | 15 +- .../internal/SdkHttpCloneReferencer.ts | 10 + .../internal/SdkHttpFunctionProgrammer.ts | 4 +- .../internal/SdkHttpNamespaceProgrammer.ts | 477 +-- .../api/automated/test_api_body_store.ts | 13 + .../api/automated/test_api_health_get.ts | 8 + .../api/automated/test_api_performance_get.ts | 11 + test/features/body-config-assert/swagger.json | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-config-assertClone/src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-config-assertPrune/src/api/module.ts | 1 + .../body-config-equals/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-config-equals/src/api/module.ts | 1 + .../body-config-is/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../features/body-config-is/src/api/module.ts | 1 + .../body-config-validate/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-config-validate/src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../src/api/module.ts | 1 + .../body-generic-default/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../google/drives/images/upload/index.ts | 4 +- .../body-generic-default/src/api/module.ts | 1 + .../body-manual-assert/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-manual-assert/src/api/module.ts | 1 + .../body-manual-is/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../features/body-manual-is/src/api/module.ts | 1 + .../body-manual-validate/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/performance/index.ts | 4 +- .../body-manual-validate/src/api/module.ts | 1 + .../cli-config-project/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/health/index.ts | 7 +- .../src/api/functional/performance/index.ts | 4 +- .../cli-config-project/src/api/module.ts | 1 + test/features/cli-config/src/api/Resolved.ts | 1 + .../src/api/functional/body/index.ts | 4 +- .../src/api/functional/health/index.ts | 7 +- .../src/api/functional/performance/index.ts | 4 +- test/features/cli-config/src/api/module.ts | 1 + test/features/cli-project/src/api/Resolved.ts | 1 + .../src/api/functional/performance/index.ts | 4 +- test/features/cli-project/src/api/module.ts | 1 + .../src/api/Resolved.ts | 1 + .../src/api/module.ts | 1 + .../swagger.json | 2 +- .../clone-and-propagate/src/api/Resolved.ts | 1 + .../api/functional/arrayRecursive/index.ts | 114 +- .../arrayRecursiveUnionExplicit/index.ts | 143 +- .../arrayRecursiveUnionImplicit/index.ts | 229 +- .../src/api/functional/arraySimple/index.ts | 114 +- .../src/api/functional/bbs/articles/index.ts | 121 +- .../src/api/functional/health/index.ts | 46 +- .../src/api/functional/multipart/index.ts | 29 +- .../src/api/functional/objectLiteral/index.ts | 51 +- .../functional/objectLiteral/literal/index.ts | 100 +- .../src/api/functional/objectSimple/index.ts | 113 +- .../functional/objectUnionExplicit/index.ts | 99 +- .../objectUnionImplicitControllere/index.ts | 99 +- .../src/api/functional/performance/index.ts | 144 +- .../functional/sellers/authenticate/index.ts | 105 +- .../sellers/authenticate/password/index.ts | 29 +- .../src/api/functional/template/index.ts | 113 +- .../tupleHierarchicalController/index.ts | 222 +- .../functional/tupleRestController/index.ts | 53 +- .../src/api/functional/users/oauth/index.ts | 70 +- .../src/api/functional/users/user/index.ts | 85 +- .../clone-and-propagate/src/api/module.ts | 1 + .../src/api/structures/IAuthentication.ts | 5 +- .../src/api/structures/ICircle.ts | 2 +- .../src/api/structures/IDirectory.ts | 24 +- .../src/api/structures/IImageFile.ts | 13 - .../src/api/structures/ILine.ts | 4 +- .../src/api/structures/IPage.ts | 22 +- .../src/api/structures/IPoint.ts | 8 +- .../src/api/structures/IPolygon.ts | 4 +- .../src/api/structures/IPolyline.ts | 7 +- .../src/api/structures/IRectangle.ts | 8 +- .../src/api/structures/ISeller.ts | 14 +- .../src/api/structures/ISharedDirectory.ts | 15 +- .../src/api/structures/IShortcut.ts | 24 +- .../src/api/structures/ITextFile.ts | 11 - .../src/api/structures/ITriangle.ts | 6 +- .../src/api/structures/IUser.ts | 5 +- .../src/api/structures/IZipFile.ts | 11 - .../src/api/structures/ObjectLietral.ts | 6 +- ...Usernameemailoptional_attrnullable_attr.ts | 15 + .../src/api/structures/Template.ts | 4 +- ...test_api_arrayRecursiveUnionExplicit_at.ts | 15 +- ...t_api_arrayRecursiveUnionExplicit_index.ts | 9 +- ...t_api_arrayRecursiveUnionExplicit_store.ts | 17 +- ...test_api_arrayRecursiveUnionImplicit_at.ts | 22 +- ...t_api_arrayRecursiveUnionImplicit_index.ts | 9 +- ...t_api_arrayRecursiveUnionImplicit_store.ts | 31 +- .../automated/test_api_arrayRecursive_at.ts | 9 +- .../test_api_arrayRecursive_index.ts | 11 +- .../test_api_arrayRecursive_store.ts | 9 +- .../api/automated/test_api_arraySimple_at.ts | 10 +- .../automated/test_api_arraySimple_index.ts | 9 +- .../automated/test_api_arraySimple_store.ts | 9 +- .../automated/test_api_bbs_articles_index.ts | 9 +- .../automated/test_api_bbs_articles_store.ts | 9 +- .../automated/test_api_bbs_articles_update.ts | 10 +- .../api/automated/test_api_health_get.ts | 9 +- .../api/automated/test_api_multipart_post.ts | 9 +- .../automated/test_api_objectLiteral_index.ts | 11 +- ...test_api_objectLiteral_literal_literals.ts | 20 +- .../api/automated/test_api_objectSimple_at.ts | 9 +- .../automated/test_api_objectSimple_index.ts | 11 +- .../automated/test_api_objectSimple_store.ts | 9 +- .../test_api_objectUnionExplicit_get.ts | 9 +- ..._api_objectUnionImplicitControllere_get.ts | 9 +- .../api/automated/test_api_performance_cpu.ts | 9 +- .../automated/test_api_performance_memory.ts | 9 +- .../test_api_performance_resource.ts | 9 +- .../test_api_sellers_authenticate_exit.ts | 9 +- .../test_api_sellers_authenticate_join.ts | 9 +- .../test_api_sellers_authenticate_login.ts | 9 +- ...pi_sellers_authenticate_password_change.ts | 9 +- .../api/automated/test_api_template_at.ts | 9 +- .../api/automated/test_api_template_index.ts | 11 +- .../api/automated/test_api_template_store.ts | 12 +- ...test_api_tupleHierarchicalController_at.ts | 9 +- ...t_api_tupleHierarchicalController_index.ts | 11 +- ...t_api_tupleHierarchicalController_store.ts | 9 +- .../test_api_tupleRestController_get.ts | 9 +- .../test_api_users_oauth_getOauthProfile.ts | 11 +- .../test_api_users_user_updateUserProfile.ts | 13 +- .../features/clone-and-propagate/swagger.json | 2597 ++++++----------- test/package.json | 7 +- 163 files changed, 3299 insertions(+), 2783 deletions(-) create mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts create mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts create mode 100644 test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts create mode 100644 test/features/body-config-assert/swagger.json create mode 100644 test/features/body-config-assertClone/src/api/Resolved.ts create mode 100644 test/features/body-config-assertEquals/src/api/Resolved.ts create mode 100644 test/features/body-config-assertPrune/src/api/Resolved.ts create mode 100644 test/features/body-config-equals/src/api/Resolved.ts create mode 100644 test/features/body-config-is/src/api/Resolved.ts create mode 100644 test/features/body-config-validate/src/api/Resolved.ts create mode 100644 test/features/body-config-validateClone/src/api/Resolved.ts create mode 100644 test/features/body-config-validateEquals/src/api/Resolved.ts create mode 100644 test/features/body-config-validatePrune/src/api/Resolved.ts create mode 100644 test/features/body-generic-default/src/api/Resolved.ts create mode 100644 test/features/body-manual-assert/src/api/Resolved.ts create mode 100644 test/features/body-manual-is/src/api/Resolved.ts create mode 100644 test/features/body-manual-validate/src/api/Resolved.ts create mode 100644 test/features/cli-config-project/src/api/Resolved.ts create mode 100644 test/features/cli-config/src/api/Resolved.ts create mode 100644 test/features/cli-project/src/api/Resolved.ts create mode 100644 test/features/clone-and-exact-optional-property/src/api/Resolved.ts create mode 100644 test/features/clone-and-propagate/src/api/Resolved.ts create mode 100644 test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailoptional_attrnullable_attr.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 0361a8a24..8dc67eed5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "nestia", - "version": "5.6.0", + "version": "5.7.0", "description": "Nestia CLI tool", "main": "bin/index.js", "bin": { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index c340c04bf..f59b17ee3 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -12,6 +12,7 @@ npx nestia [command] [options?] 7. npx nestia swagger 8. npx nestia openai 9. npx nestia e2e + 10. npx nestia all `; function halt(desc: string): never { @@ -46,7 +47,8 @@ async function main(): Promise { type === "sdk" || type === "openai" || type === "swagger" || - type === "e2e" + type === "e2e" || + type === "all" ) { try { require.resolve("@nestia/sdk/lib/executable/sdk"); diff --git a/packages/sdk/src/generates/internal/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index 69c754d68..db92c0927 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -1,6 +1,7 @@ import ts from "typescript"; import typia from "typia"; import { TypeFactory } from "typia/lib/factories/TypeFactory"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; import { INestiaProject } from "../../structures/INestiaProject"; import { IReflectHttpOperationParameter } from "../../structures/IReflectHttpOperationParameter"; @@ -8,6 +9,7 @@ import { IReflectType } from "../../structures/IReflectType"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter"; import { ImportDictionary } from "./ImportDictionary"; +import { SdkTypeProgrammer } from "./SdkTypeProgrammer"; export namespace SdkAliasCollection { export const name = (type: IReflectType): ts.TypeNode => @@ -16,10 +18,18 @@ export namespace SdkAliasCollection { type.typeArguments ? type.typeArguments.map(name) : undefined, ); + export const from = + (project: INestiaProject) => + (importer: ImportDictionary) => + (metadata: Metadata) => + SdkTypeProgrammer.write(project)(importer)(metadata); + export const headers = (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter.IHeaders): ts.TypeNode => { + if (project.config.clone === true) + return from(project)(importer)(param.metadata); const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( @@ -36,6 +46,8 @@ export namespace SdkAliasCollection { (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter.IQuery): ts.TypeNode => { + if (project.config.clone === true) + return from(project)(importer)(param.metadata); const type: ts.TypeNode = name(param); if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( @@ -52,9 +64,10 @@ export namespace SdkAliasCollection { (project: INestiaProject) => (importer: ImportDictionary) => (param: ITypedHttpRouteParameter): ts.TypeNode => { + if (project.config.clone === true) + return from(project)(importer)(param.metadata); const type: ts.TypeNode = name(param.type); - if (project.config.clone === true || project.config.primitive === false) - return type; + if (project.config.primitive === false) return type; return ts.factory.createTypeReferenceNode( importer.external({ type: true, @@ -74,11 +87,15 @@ export namespace SdkAliasCollection { (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { + const schema = (p: { metadata: Metadata; type: IReflectType }) => + project.config.clone === true + ? from(project)(importer)(p.metadata) + : name(p.type); if (project.config.propagate !== true) { if (route.success.metadata.size() === 0) return TypeFactory.keyword("void"); else if (project.config.primitive === false) - return name(route.success.type); + return schema(route.success); return ts.factory.createTypeReferenceNode( importer.external({ type: true, @@ -89,7 +106,7 @@ export namespace SdkAliasCollection { ? "Primitive" : "Resolved", }), - [name(route.success.type)], + [schema(route.success)], ); } @@ -98,11 +115,11 @@ export namespace SdkAliasCollection { status: String( route.success.status ?? (route.method === "POST" ? 201 : 200), ), - type: name(route.success.type), + type: schema(route.success), }, ...Object.entries(route.exceptions).map(([status, value]) => ({ status, - type: name(value.type), + type: schema(value), })), ]; return ts.factory.createTypeReferenceNode( diff --git a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts index 502f3b8f6..3996d9801 100644 --- a/packages/sdk/src/generates/internal/SdkFileProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkFileProgrammer.ts @@ -75,13 +75,14 @@ export namespace SdkFileProgrammer { `${outDir}/index.ts`, ); directory.routes.forEach((route, i) => { - for (const tuple of route.imports) - for (const instance of tuple.instances) - importer.internal({ - file: tuple.file, - instance, - type: true, - }); + if (!(project.config.clone === true && route.protocol === "http")) + for (const tuple of route.imports) + for (const instance of tuple.instances) + importer.internal({ + file: tuple.file, + instance, + type: true, + }); statements.push( ...(route.protocol === "http" ? SdkHttpRouteProgrammer.write(project)(importer)(route) diff --git a/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts index 7d8dc322d..bc7239419 100644 --- a/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts @@ -1,5 +1,6 @@ import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { IReflectType } from "../../structures/IReflectType"; import { ITypedApplication } from "../../structures/ITypedApplication"; import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute"; import { StringUtil } from "../../utils/StringUtil"; @@ -24,17 +25,20 @@ export namespace SdkHttpCloneReferencer { visitType({ unique, metadata: p.metadata, + type: p.type, name: (name) => (p.type = { name }), }); for (const v of Object.values(props.route.exceptions)) visitType({ unique, metadata: v.metadata, + type: v.type, name: (name) => (v.type = { name }), }); visitType({ unique, metadata: props.route.success.metadata, + type: props.route.success.type, name: (name) => (props.route.success.type = { name }), }); props.route.imports = Array.from(unique).map((str) => ({ @@ -46,6 +50,7 @@ export namespace SdkHttpCloneReferencer { const visitType = (p: { unique: Set; metadata: Metadata; + type: IReflectType; name: (key: string) => void; }): void => { const enroll = (key: string) => { @@ -59,3 +64,8 @@ export namespace SdkHttpCloneReferencer { p.name(p.metadata.getName()); }; } + +const getFullText = (type: IReflectType): string => + type.typeArguments === undefined + ? type.name + : `${type.name}<${type.typeArguments.map(getFullText).join(", ")}>`; diff --git a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts index 59c342efa..87f02b9a2 100644 --- a/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpFunctionProgrammer.ts @@ -56,7 +56,9 @@ export namespace SdkHttpFunctionProgrammer { ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) - : SdkAliasCollection.name(p.type), + : project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p.type), ), ), ], diff --git a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts index 0c9d3bb59..cfd7570fc 100644 --- a/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpNamespaceProgrammer.ts @@ -33,9 +33,9 @@ export namespace SdkHttpNamespaceProgrammer { ts.factory.createModuleBlock([ ...types, ...(types.length ? [FilePrinter.enter()] : []), - write_metadata(importer)(route, props), + write_metadata(project)(importer)(route, props), FilePrinter.enter(), - write_path(route, props.query), + write_path(project)(importer)(route, props.query), ...(project.config.simulate ? [ SdkHttpSimulationProgrammer.random(project)(importer)(route), @@ -101,6 +101,7 @@ export namespace SdkHttpNamespaceProgrammer { }; const write_metadata = + (project: INestiaProject) => (importer: ImportDictionary) => ( route: ITypedHttpRoute, @@ -162,7 +163,13 @@ export namespace SdkHttpNamespaceProgrammer { ts.factory.createIdentifier( `${SdkImportWizard.typia(importer)}.http.createAssertQuery`, ), - [SdkAliasCollection.name(route.success.type)], + [ + project.config.clone === true + ? SdkAliasCollection.from(project)(importer)( + route.success.metadata, + ) + : SdkAliasCollection.name(route.success.type), + ], undefined, ), ), @@ -177,263 +184,275 @@ export namespace SdkHttpNamespaceProgrammer { ), ); - const write_path = ( - route: ITypedHttpRoute, - query: ITypedHttpRouteParameter.IQuery | undefined, - ): ts.VariableStatement => { - const g = { - total: [ - ...route.parameters.filter( - (param) => param.category === "param" || param.category === "query", - ), - ], - query: route.parameters - .filter((param) => param.category === "query") - .filter((param) => param.field !== null), - path: route.parameters.filter((param) => param.category === "param"), - }; - const out = (body: ts.ConciseBody) => - constant("path")( - ts.factory.createArrowFunction( - [], - [], - g.total.map((p) => - IdentifierFactory.parameter( - p.name, - p === query - ? p.metadata.isRequired() === false - ? ts.factory.createUnionTypeNode([ - ts.factory.createTypeReferenceNode(`${route.name}.Query`), - ts.factory.createTypeReferenceNode("undefined"), - ]) - : ts.factory.createTypeReferenceNode(`${route.name}.Query`) - : SdkAliasCollection.name(p.type), + const write_path = + (project: INestiaProject) => + (importer: ImportDictionary) => + ( + route: ITypedHttpRoute, + query: ITypedHttpRouteParameter.IQuery | undefined, + ): ts.VariableStatement => { + const g = { + total: [ + ...route.parameters.filter( + (param) => param.category === "param" || param.category === "query", + ), + ], + query: route.parameters + .filter((param) => param.category === "query") + .filter((param) => param.field !== null), + path: route.parameters.filter((param) => param.category === "param"), + }; + const out = (body: ts.ConciseBody) => + constant("path")( + ts.factory.createArrowFunction( + [], + [], + g.total.map((p) => + IdentifierFactory.parameter( + p.name, + p === query + ? p.metadata.isRequired() === false + ? ts.factory.createUnionTypeNode([ + ts.factory.createTypeReferenceNode( + `${route.name}.Query`, + ), + ts.factory.createTypeReferenceNode("undefined"), + ]) + : ts.factory.createTypeReferenceNode(`${route.name}.Query`) + : project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p.type), + ), ), + undefined, + undefined, + body, ), - undefined, - undefined, - body, - ), - ); - if (g.total.length === 0) - return out(ts.factory.createStringLiteral(route.path)); + ); + if (g.total.length === 0) + return out(ts.factory.createStringLiteral(route.path)); - const template = () => { - const splitted: string[] = route.path.split(":"); - if (splitted.length === 1) - return ts.factory.createStringLiteral(route.path); - return ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(splitted[0]), - splitted.slice(1).map((s, i, arr) => { - const name: string = s.split("/")[0]; - return ts.factory.createTemplateSpan( - ts.factory.createCallExpression( - ts.factory.createIdentifier("encodeURIComponent"), - undefined, - [ - ts.factory.createBinaryExpression( - ts.factory.createIdentifier( - g.path.find((p) => p.field === name)!.name, + const template = () => { + const splitted: string[] = route.path.split(":"); + if (splitted.length === 1) + return ts.factory.createStringLiteral(route.path); + return ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(splitted[0]), + splitted.slice(1).map((s, i, arr) => { + const name: string = s.split("/")[0]; + return ts.factory.createTemplateSpan( + ts.factory.createCallExpression( + ts.factory.createIdentifier("encodeURIComponent"), + undefined, + [ + ts.factory.createBinaryExpression( + ts.factory.createIdentifier( + g.path.find((p) => p.field === name)!.name, + ), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createStringLiteral("null"), ), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createStringLiteral("null"), - ), - ], - ), - (i !== arr.length - 1 - ? ts.factory.createTemplateMiddle - : ts.factory.createTemplateTail)(s.substring(name.length)), - ); - }), - ); - }; - if (query === undefined && g.query.length === 0) return out(template()); + ], + ), + (i !== arr.length - 1 + ? ts.factory.createTemplateMiddle + : ts.factory.createTemplateTail)(s.substring(name.length)), + ); + }), + ); + }; + if (query === undefined && g.query.length === 0) return out(template()); - const block = (expr: ts.Expression) => { - const computeName = (str: string): string => - g.total.find((p) => p.name === str) !== undefined - ? computeName("_" + str) - : str; - const variables: string = computeName("variables"); - return ts.factory.createBlock( - [ - local(variables)("URLSearchParams")( - ts.factory.createNewExpression( - ts.factory.createIdentifier("URLSearchParams"), - [], - [], - ), - ), - ts.factory.createForOfStatement( - undefined, - ts.factory.createVariableDeclarationList( - [ - ts.factory.createVariableDeclaration( - ts.factory.createArrayBindingPattern([ - ts.factory.createBindingElement( - undefined, - undefined, - ts.factory.createIdentifier("key"), - undefined, - ), - ts.factory.createBindingElement( - undefined, - undefined, - ts.factory.createIdentifier("value"), - undefined, - ), - ]), - undefined, - undefined, - undefined, - ), - ], - ts.NodeFlags.Const, + const block = (expr: ts.Expression) => { + const computeName = (str: string): string => + g.total.find((p) => p.name === str) !== undefined + ? computeName("_" + str) + : str; + const variables: string = computeName("variables"); + return ts.factory.createBlock( + [ + local(variables)("URLSearchParams")( + ts.factory.createNewExpression( + ts.factory.createIdentifier("URLSearchParams"), + [], + [], + ), ), - ts.factory.createCallExpression( - ts.factory.createIdentifier("Object.entries"), + ts.factory.createForOfStatement( undefined, - [ts.factory.createAsExpression(expr, TypeFactory.keyword("any"))], - ), - ts.factory.createIfStatement( - ts.factory.createStrictEquality( - ts.factory.createIdentifier("undefined"), - ts.factory.createIdentifier("value"), - ), - ts.factory.createContinueStatement(), - ts.factory.createIfStatement( - ts.factory.createCallExpression( - ts.factory.createIdentifier("Array.isArray"), - undefined, - [ts.factory.createIdentifier("value")], - ), - ts.factory.createExpressionStatement( - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier("value"), - ts.factory.createIdentifier("forEach"), - ), - undefined, - [ - ts.factory.createArrowFunction( + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createArrayBindingPattern([ + ts.factory.createBindingElement( undefined, undefined, - [IdentifierFactory.parameter("elem")], + ts.factory.createIdentifier("key"), undefined, + ), + ts.factory.createBindingElement( + undefined, + undefined, + ts.factory.createIdentifier("value"), undefined, - ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("append"), - undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("elem")], - ), - ], - ), ), - ], + ]), + undefined, + undefined, + undefined, ), + ], + ts.NodeFlags.Const, + ), + ts.factory.createCallExpression( + ts.factory.createIdentifier("Object.entries"), + undefined, + [ + ts.factory.createAsExpression( + expr, + TypeFactory.keyword("any"), + ), + ], + ), + ts.factory.createIfStatement( + ts.factory.createStrictEquality( + ts.factory.createIdentifier("undefined"), + ts.factory.createIdentifier("value"), ), - ts.factory.createExpressionStatement( + ts.factory.createContinueStatement(), + ts.factory.createIfStatement( ts.factory.createCallExpression( - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("set"), + ts.factory.createIdentifier("Array.isArray"), undefined, - [ - ts.factory.createIdentifier("key"), - ts.factory.createCallExpression( - ts.factory.createIdentifier("String"), - undefined, - [ts.factory.createIdentifier("value")], - ), - ], + [ts.factory.createIdentifier("value")], ), - ), - ), - ), - ), - local("location")("string")(template()), - ts.factory.createReturnStatement( - ts.factory.createConditionalExpression( - ts.factory.createStrictEquality( - ExpressionFactory.number(0), - IdentifierFactory.access( - ts.factory.createIdentifier(variables), - )("size"), - ), - undefined, - ts.factory.createIdentifier("location"), - undefined, - ts.factory.createTemplateExpression( - ts.factory.createTemplateHead(""), - [ - ts.factory.createTemplateSpan( - ts.factory.createIdentifier("location"), - ts.factory.createTemplateMiddle("?"), + ts.factory.createExpressionStatement( + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier("value"), + ts.factory.createIdentifier("forEach"), + ), + undefined, + [ + ts.factory.createArrowFunction( + undefined, + undefined, + [IdentifierFactory.parameter("elem")], + undefined, + undefined, + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("append"), + undefined, + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("elem")], + ), + ], + ), + ), + ], + ), ), - ts.factory.createTemplateSpan( + ts.factory.createExpressionStatement( ts.factory.createCallExpression( IdentifierFactory.access( ts.factory.createIdentifier(variables), - )("toString"), - undefined, + )("set"), undefined, + [ + ts.factory.createIdentifier("key"), + ts.factory.createCallExpression( + ts.factory.createIdentifier("String"), + undefined, + [ts.factory.createIdentifier("value")], + ), + ], ), - ts.factory.createTemplateTail(""), ), - ], + ), ), ), + local("location")("string")(template()), + ts.factory.createReturnStatement( + ts.factory.createConditionalExpression( + ts.factory.createStrictEquality( + ExpressionFactory.number(0), + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("size"), + ), + undefined, + ts.factory.createIdentifier("location"), + undefined, + ts.factory.createTemplateExpression( + ts.factory.createTemplateHead(""), + [ + ts.factory.createTemplateSpan( + ts.factory.createIdentifier("location"), + ts.factory.createTemplateMiddle("?"), + ), + ts.factory.createTemplateSpan( + ts.factory.createCallExpression( + IdentifierFactory.access( + ts.factory.createIdentifier(variables), + )("toString"), + undefined, + undefined, + ), + ts.factory.createTemplateTail(""), + ), + ], + ), + ), + ), + ], + true, + ); + }; + if (query !== undefined && g.query.length === 0) + return out( + block( + query.metadata.isRequired() === false + ? ts.factory.createBinaryExpression( + ts.factory.createIdentifier(query.name), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + ts.factory.createObjectLiteralExpression([], false), + ) + : ts.factory.createIdentifier(query.name), ), - ], - true, - ); - }; - if (query !== undefined && g.query.length === 0) + ); return out( block( - query.metadata.isRequired() === false - ? ts.factory.createBinaryExpression( - ts.factory.createIdentifier(query.name), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createObjectLiteralExpression([], false), - ) - : ts.factory.createIdentifier(query.name), + ts.factory.createObjectLiteralExpression( + [ + ...(query + ? [ + ts.factory.createSpreadAssignment( + ts.factory.createIdentifier(query.name), + ), + ] + : []), + ...g.query.map((q) => + q.name === q.field + ? ts.factory.createShorthandPropertyAssignment(q.name) + : ts.factory.createPropertyAssignment( + Escaper.variable(q.field!) + ? q.field! + : ts.factory.createStringLiteral(q.field!), + ts.factory.createIdentifier(q.name), + ), + ), + ], + true, + ), ), ); - return out( - block( - ts.factory.createObjectLiteralExpression( - [ - ...(query - ? [ - ts.factory.createSpreadAssignment( - ts.factory.createIdentifier(query.name), - ), - ] - : []), - ...g.query.map((q) => - q.name === q.field - ? ts.factory.createShorthandPropertyAssignment(q.name) - : ts.factory.createPropertyAssignment( - Escaper.variable(q.field!) - ? q.field! - : ts.factory.createStringLiteral(q.field!), - ts.factory.createIdentifier(q.name), - ), - ), - ], - true, - ), - ), - ); - }; + }; const write_stringify = (project: INestiaProject) => diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts new file mode 100644 index 000000000..25e6fe0a2 --- /dev/null +++ b/test/features/body-config-assert/src/test/features/api/automated/test_api_body_store.ts @@ -0,0 +1,13 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; + +import api from "../../../../api"; +import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; + +export const test_api_body_store = async (connection: api.IConnection) => { + const output: Primitive = await api.functional.body.store( + connection, + typia.random(), + ); + typia.assert(output); +}; diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts new file mode 100644 index 000000000..8766b1129 --- /dev/null +++ b/test/features/body-config-assert/src/test/features/api/automated/test_api_health_get.ts @@ -0,0 +1,8 @@ +import typia from "typia"; + +import api from "../../../../api"; + +export const test_api_health_get = async (connection: api.IConnection) => { + const output = await api.functional.health.get(connection); + typia.assert(output); +}; diff --git a/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts b/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts new file mode 100644 index 000000000..54cdfd218 --- /dev/null +++ b/test/features/body-config-assert/src/test/features/api/automated/test_api_performance_get.ts @@ -0,0 +1,11 @@ +import type { Primitive } from "@nestia/fetcher"; +import typia from "typia"; + +import api from "../../../../api"; +import type { IPerformance } from "../../../../api/structures/IPerformance"; + +export const test_api_performance_get = async (connection: api.IConnection) => { + const output: Primitive = + await api.functional.performance.get(connection); + typia.assert(output); +}; diff --git a/test/features/body-config-assert/swagger.json b/test/features/body-config-assert/swagger.json new file mode 100644 index 000000000..82b3eb72b --- /dev/null +++ b/test/features/body-config-assert/swagger.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/performance":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}}}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["id","created_at","title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"oneOf":[{"type":"null"},{"type":"string","maxLength":255}]},"extension":{"oneOf":[{"type":"null"},{"type":"string","minLength":1,"maxLength":8}]},"url":{"type":"string","format":"uri"}},"required":["name","extension","url"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","minLength":3,"maxLength":50},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"required":["title","body","files"]}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/body-config-assertClone/src/api/Resolved.ts b/test/features/body-config-assertClone/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-assertClone/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-assertClone/src/api/functional/body/index.ts b/test/features/body-config-assertClone/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-assertClone/src/api/functional/body/index.ts +++ b/test/features/body-config-assertClone/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertClone/src/api/functional/performance/index.ts b/test/features/body-config-assertClone/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-assertClone/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertClone/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertClone/src/api/module.ts b/test/features/body-config-assertClone/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-assertClone/src/api/module.ts +++ b/test/features/body-config-assertClone/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-assertEquals/src/api/Resolved.ts b/test/features/body-config-assertEquals/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-assertEquals/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-assertEquals/src/api/functional/body/index.ts b/test/features/body-config-assertEquals/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-assertEquals/src/api/functional/body/index.ts +++ b/test/features/body-config-assertEquals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertEquals/src/api/functional/performance/index.ts b/test/features/body-config-assertEquals/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-assertEquals/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertEquals/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertEquals/src/api/module.ts b/test/features/body-config-assertEquals/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-assertEquals/src/api/module.ts +++ b/test/features/body-config-assertEquals/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-assertPrune/src/api/Resolved.ts b/test/features/body-config-assertPrune/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-assertPrune/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-assertPrune/src/api/functional/body/index.ts b/test/features/body-config-assertPrune/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-assertPrune/src/api/functional/body/index.ts +++ b/test/features/body-config-assertPrune/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertPrune/src/api/functional/performance/index.ts b/test/features/body-config-assertPrune/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-assertPrune/src/api/functional/performance/index.ts +++ b/test/features/body-config-assertPrune/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-assertPrune/src/api/module.ts b/test/features/body-config-assertPrune/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-assertPrune/src/api/module.ts +++ b/test/features/body-config-assertPrune/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-equals/src/api/Resolved.ts b/test/features/body-config-equals/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-equals/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-equals/src/api/functional/body/index.ts b/test/features/body-config-equals/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-equals/src/api/functional/body/index.ts +++ b/test/features/body-config-equals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-equals/src/api/functional/performance/index.ts b/test/features/body-config-equals/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-equals/src/api/functional/performance/index.ts +++ b/test/features/body-config-equals/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-equals/src/api/module.ts b/test/features/body-config-equals/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-equals/src/api/module.ts +++ b/test/features/body-config-equals/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-is/src/api/Resolved.ts b/test/features/body-config-is/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-is/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-is/src/api/functional/body/index.ts b/test/features/body-config-is/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-is/src/api/functional/body/index.ts +++ b/test/features/body-config-is/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-is/src/api/functional/performance/index.ts b/test/features/body-config-is/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-is/src/api/functional/performance/index.ts +++ b/test/features/body-config-is/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-is/src/api/module.ts b/test/features/body-config-is/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-is/src/api/module.ts +++ b/test/features/body-config-is/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-validate/src/api/Resolved.ts b/test/features/body-config-validate/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-validate/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-validate/src/api/functional/body/index.ts b/test/features/body-config-validate/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-validate/src/api/functional/body/index.ts +++ b/test/features/body-config-validate/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validate/src/api/functional/performance/index.ts b/test/features/body-config-validate/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-validate/src/api/functional/performance/index.ts +++ b/test/features/body-config-validate/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validate/src/api/module.ts b/test/features/body-config-validate/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-validate/src/api/module.ts +++ b/test/features/body-config-validate/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-validateClone/src/api/Resolved.ts b/test/features/body-config-validateClone/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-validateClone/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-validateClone/src/api/functional/body/index.ts b/test/features/body-config-validateClone/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-validateClone/src/api/functional/body/index.ts +++ b/test/features/body-config-validateClone/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validateClone/src/api/functional/performance/index.ts b/test/features/body-config-validateClone/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-validateClone/src/api/functional/performance/index.ts +++ b/test/features/body-config-validateClone/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validateClone/src/api/module.ts b/test/features/body-config-validateClone/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-validateClone/src/api/module.ts +++ b/test/features/body-config-validateClone/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-validateEquals/src/api/Resolved.ts b/test/features/body-config-validateEquals/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-validateEquals/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-validateEquals/src/api/functional/body/index.ts b/test/features/body-config-validateEquals/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-validateEquals/src/api/functional/body/index.ts +++ b/test/features/body-config-validateEquals/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validateEquals/src/api/functional/performance/index.ts b/test/features/body-config-validateEquals/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-validateEquals/src/api/functional/performance/index.ts +++ b/test/features/body-config-validateEquals/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validateEquals/src/api/module.ts b/test/features/body-config-validateEquals/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-validateEquals/src/api/module.ts +++ b/test/features/body-config-validateEquals/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-config-validatePrune/src/api/Resolved.ts b/test/features/body-config-validatePrune/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-config-validatePrune/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-config-validatePrune/src/api/functional/body/index.ts b/test/features/body-config-validatePrune/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-config-validatePrune/src/api/functional/body/index.ts +++ b/test/features/body-config-validatePrune/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validatePrune/src/api/functional/performance/index.ts b/test/features/body-config-validatePrune/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-config-validatePrune/src/api/functional/performance/index.ts +++ b/test/features/body-config-validatePrune/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-config-validatePrune/src/api/module.ts b/test/features/body-config-validatePrune/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-config-validatePrune/src/api/module.ts +++ b/test/features/body-config-validatePrune/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-generic-default/src/api/Resolved.ts b/test/features/body-generic-default/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-generic-default/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-generic-default/src/api/functional/body/index.ts b/test/features/body-generic-default/src/api/functional/body/index.ts index 488f177c9..c1a287cbb 100644 --- a/test/features/body-generic-default/src/api/functional/body/index.ts +++ b/test/features/body-generic-default/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -25,7 +25,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise> { +): Promise>> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts b/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts index 95c4ffb7a..c83c18c3e 100644 --- a/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts +++ b/test/features/body-generic-default/src/api/functional/google/drives/images/upload/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IGoogleDriveFile } from "../../../../../structures/IGoogleDriveFile"; @@ -29,7 +29,7 @@ export async function single( connection: IConnection, accountCode: string, input: single.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-generic-default/src/api/module.ts b/test/features/body-generic-default/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-generic-default/src/api/module.ts +++ b/test/features/body-generic-default/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-manual-assert/src/api/Resolved.ts b/test/features/body-manual-assert/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-manual-assert/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-manual-assert/src/api/functional/body/index.ts b/test/features/body-manual-assert/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-manual-assert/src/api/functional/body/index.ts +++ b/test/features/body-manual-assert/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-assert/src/api/functional/performance/index.ts b/test/features/body-manual-assert/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-manual-assert/src/api/functional/performance/index.ts +++ b/test/features/body-manual-assert/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-assert/src/api/module.ts b/test/features/body-manual-assert/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-manual-assert/src/api/module.ts +++ b/test/features/body-manual-assert/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-manual-is/src/api/Resolved.ts b/test/features/body-manual-is/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-manual-is/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-manual-is/src/api/functional/body/index.ts b/test/features/body-manual-is/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-manual-is/src/api/functional/body/index.ts +++ b/test/features/body-manual-is/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-is/src/api/functional/performance/index.ts b/test/features/body-manual-is/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-manual-is/src/api/functional/performance/index.ts +++ b/test/features/body-manual-is/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-is/src/api/module.ts b/test/features/body-manual-is/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-manual-is/src/api/module.ts +++ b/test/features/body-manual-is/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/body-manual-validate/src/api/Resolved.ts b/test/features/body-manual-validate/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/body-manual-validate/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/body-manual-validate/src/api/functional/body/index.ts b/test/features/body-manual-validate/src/api/functional/body/index.ts index 11634cdc1..e3c672e50 100644 --- a/test/features/body-manual-validate/src/api/functional/body/index.ts +++ b/test/features/body-manual-validate/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { IBbsArticle } from "../../structures/IBbsArticle"; @@ -17,7 +17,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-validate/src/api/functional/performance/index.ts b/test/features/body-manual-validate/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/body-manual-validate/src/api/functional/performance/index.ts +++ b/test/features/body-manual-validate/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/body-manual-validate/src/api/module.ts b/test/features/body-manual-validate/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/body-manual-validate/src/api/module.ts +++ b/test/features/body-manual-validate/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/cli-config-project/src/api/Resolved.ts b/test/features/cli-config-project/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/cli-config-project/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/cli-config-project/src/api/functional/body/index.ts b/test/features/cli-config-project/src/api/functional/body/index.ts index 76c310425..46a106b52 100644 --- a/test/features/cli-config-project/src/api/functional/body/index.ts +++ b/test/features/cli-config-project/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -26,7 +26,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return !!connection.simulate ? store.simulate(connection, input) : PlainFetcher.fetch( diff --git a/test/features/cli-config-project/src/api/functional/health/index.ts b/test/features/cli-config-project/src/api/functional/health/index.ts index dba160cca..459a0668e 100644 --- a/test/features/cli-config-project/src/api/functional/health/index.ts +++ b/test/features/cli-config-project/src/api/functional/health/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import type { IConnection, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -52,9 +52,8 @@ export namespace get { } as const; export const path = () => "/health"; - export const random = ( - g?: Partial, - ): Resolved> => typia.random>(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = (connection: IConnection): void => { return random( "object" === typeof connection.simulate && null !== connection.simulate diff --git a/test/features/cli-config-project/src/api/functional/performance/index.ts b/test/features/cli-config-project/src/api/functional/performance/index.ts index d8b8906ba..5be63fd49 100644 --- a/test/features/cli-config-project/src/api/functional/performance/index.ts +++ b/test/features/cli-config-project/src/api/functional/performance/index.ts @@ -24,7 +24,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return !!connection.simulate ? get.simulate(connection) : PlainFetcher.fetch( diff --git a/test/features/cli-config-project/src/api/module.ts b/test/features/cli-config-project/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/cli-config-project/src/api/module.ts +++ b/test/features/cli-config-project/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/cli-config/src/api/Resolved.ts b/test/features/cli-config/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/cli-config/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/cli-config/src/api/functional/body/index.ts b/test/features/cli-config/src/api/functional/body/index.ts index 76c310425..46a106b52 100644 --- a/test/features/cli-config/src/api/functional/body/index.ts +++ b/test/features/cli-config/src/api/functional/body/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Resolved, Primitive } from "@nestia/fetcher"; +import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -26,7 +26,7 @@ import type { IBbsArticle } from "../../structures/IBbsArticle"; export async function store( connection: IConnection, input: store.Input, -): Promise { +): Promise> { return !!connection.simulate ? store.simulate(connection, input) : PlainFetcher.fetch( diff --git a/test/features/cli-config/src/api/functional/health/index.ts b/test/features/cli-config/src/api/functional/health/index.ts index dba160cca..459a0668e 100644 --- a/test/features/cli-config/src/api/functional/health/index.ts +++ b/test/features/cli-config/src/api/functional/health/index.ts @@ -4,7 +4,7 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, Primitive, Resolved } from "@nestia/fetcher"; +import type { IConnection, Resolved } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -52,9 +52,8 @@ export namespace get { } as const; export const path = () => "/health"; - export const random = ( - g?: Partial, - ): Resolved> => typia.random>(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = (connection: IConnection): void => { return random( "object" === typeof connection.simulate && null !== connection.simulate diff --git a/test/features/cli-config/src/api/functional/performance/index.ts b/test/features/cli-config/src/api/functional/performance/index.ts index d8b8906ba..5be63fd49 100644 --- a/test/features/cli-config/src/api/functional/performance/index.ts +++ b/test/features/cli-config/src/api/functional/performance/index.ts @@ -24,7 +24,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return !!connection.simulate ? get.simulate(connection) : PlainFetcher.fetch( diff --git a/test/features/cli-config/src/api/module.ts b/test/features/cli-config/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/cli-config/src/api/module.ts +++ b/test/features/cli-config/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/cli-project/src/api/Resolved.ts b/test/features/cli-project/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/cli-project/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/cli-project/src/api/functional/performance/index.ts b/test/features/cli-project/src/api/functional/performance/index.ts index 749a475c8..37ca258d8 100644 --- a/test/features/cli-project/src/api/functional/performance/index.ts +++ b/test/features/cli-project/src/api/functional/performance/index.ts @@ -14,7 +14,9 @@ import type { IPerformance } from "../../structures/IPerformance"; * @path GET /performance * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get( + connection: IConnection, +): Promise> { return PlainFetcher.fetch( { ...connection, diff --git a/test/features/cli-project/src/api/module.ts b/test/features/cli-project/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/cli-project/src/api/module.ts +++ b/test/features/cli-project/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/clone-and-exact-optional-property/src/api/Resolved.ts b/test/features/clone-and-exact-optional-property/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/clone-and-exact-optional-property/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/clone-and-exact-optional-property/src/api/module.ts b/test/features/clone-and-exact-optional-property/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/clone-and-exact-optional-property/src/api/module.ts +++ b/test/features/clone-and-exact-optional-property/src/api/module.ts @@ -1,5 +1,6 @@ export type * from "./IConnection"; export type * from "./Primitive"; +export type * from "./Resolved"; export * from "./HttpError"; export * as functional from "./functional"; diff --git a/test/features/clone-and-exact-optional-property/swagger.json b/test/features/clone-and-exact-optional-property/swagger.json index 934edbbdd..7200017b9 100644 --- a/test/features/clone-and-exact-optional-property/swagger.json +++ b/test/features/clone-and-exact-optional-property/swagger.json @@ -1 +1 @@ -{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/partial-dto-test/original":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal"}}}}}}},"/partial-dto-test/partial-interface":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal.IPartialInterface"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPartialInterface"}}}}}}},"/partial-dto-test/partial-type":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr"}}}}}}}},"components":{"schemas":{"IOriginal":{"type":"object","properties":{"a":{"type":"string"},"b":{"type":"string"},"c":{"type":"string"},"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"required":["a","b","c","d","email","created_at"]},"IPartialInterface":{"type":"object","properties":{"a":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"IOriginal.IPartialInterface":{"type":"object","properties":{"c":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"b":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"},"PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file +{"openapi":"3.1.0","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"3.11.0-dev.20240813-11","title":"@samchon/nestia-test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{}}}}}},"/partial-dto-test/original":{"get":{"tags":[],"parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal"}}}}}}},"/partial-dto-test/partial-interface":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IOriginal.IPartialInterface"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPartialInterface"}}}}}}},"/partial-dto-test/partial-type":{"post":{"tags":[],"parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr"}}}}}}}},"components":{"schemas":{"IOriginal":{"type":"object","properties":{"a":{"type":"string"},"b":{"type":"string"},"c":{"type":"string"},"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"},"something":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ISomething"}]}},"required":["a","b","c","d","email","created_at"]},"ISomething":{"type":"object","properties":{"a":{"type":"string"},"b":{"type":"boolean"}},"required":["a","b"]},"IPartialInterface":{"type":"object","properties":{"a":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"IOriginal.IPartialInterface":{"type":"object","properties":{"c":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}}},"PartialPickIOriginalbemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"b":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"},"PartialPickIOriginaldemailcreated_atoriginal_optionalundefinable_attr":{"type":"object","properties":{"d":{"type":"string"},"email":{"oneOf":[{"type":"null"},{"type":"string","format":"email"}]},"created_at":{"oneOf":[{"type":"null"},{"type":"string","format":"date-time"}]},"original_optional":{"type":"boolean"},"undefinable_attr":{"type":"string"}},"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey"}}},"tags":[],"x-samchon-emended":true} \ No newline at end of file diff --git a/test/features/clone-and-propagate/src/api/Resolved.ts b/test/features/clone-and-propagate/src/api/Resolved.ts new file mode 100644 index 000000000..a4f457e60 --- /dev/null +++ b/test/features/clone-and-propagate/src/api/Resolved.ts @@ -0,0 +1 @@ +export type { Resolved } from "@nestia/fetcher"; diff --git a/test/features/clone-and-propagate/src/api/functional/arrayRecursive/index.ts b/test/features/clone-and-propagate/src/api/functional/arrayRecursive/index.ts index 3eb28faaa..48d58f373 100644 --- a/test/features/clone-and-propagate/src/api/functional/arrayRecursive/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/arrayRecursive/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -21,19 +22,38 @@ import type { ICategory } from "../../structures/ICategory"; * @path GET /arrayRecursive * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: ICategory[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: ICategory[]; - }>; + export type Output = IPropagation< + { + 200: ICategory[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -43,13 +63,14 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/arrayRecursive"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -62,7 +83,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -74,19 +95,38 @@ export namespace index { export async function at( connection: IConnection, id: number, -): Promise { +): Promise< + IPropagation< + { + 200: ICategory; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: ICategory; - }>; + export type Output = IPropagation< + { + 200: ICategory; + }, + 200 + >; export const METADATA = { method: "GET", @@ -96,14 +136,14 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/arrayRecursive/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -133,7 +173,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -145,10 +185,17 @@ export namespace at { export async function store( connection: IConnection, body: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: ICategory; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -166,9 +213,12 @@ export async function store( } export namespace store { export type Input = ICategory; - export type Output = IPropagation<{ - 201: ICategory; - }>; + export type Output = IPropagation< + { + 201: ICategory; + }, + 201 + >; export const METADATA = { method: "POST", @@ -181,13 +231,13 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arrayRecursive"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -220,6 +270,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionExplicit/index.ts b/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionExplicit/index.ts index 38106ac24..6caa70071 100644 --- a/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionExplicit/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionExplicit/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -14,27 +15,49 @@ import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { ArrayRecursiveUnionExplicit } from "../../structures/ArrayRecursiveUnionExplicit"; -import type { IBucket } from "../../structures/IBucket"; +import type { IDirectory } from "../../structures/IDirectory"; +import type { IImageFile } from "../../structures/IImageFile"; +import type { IShortcut } from "../../structures/IShortcut"; +import type { ITextFile } from "../../structures/ITextFile"; +import type { IZipFile } from "../../structures/IZipFile"; /** * @controller ArrayRecursiveUnionExplicitController.index * @path GET /arrayRecursiveUnionExplicit * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: (IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: ArrayRecursiveUnionExplicit; - }>; + export type Output = IPropagation< + { + 200: (IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -44,14 +67,18 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/arrayRecursiveUnionExplicit"; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved< + Primitive<(IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]> + > => + typia.random< + Primitive<(IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]> + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -64,7 +91,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -76,19 +103,38 @@ export namespace index { export async function at( connection: IConnection, id: number, -): Promise { +): Promise< + IPropagation< + { + 200: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: IBucket; - }>; + export type Output = IPropagation< + { + 200: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 200 + >; export const METADATA = { method: "GET", @@ -98,14 +144,19 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/arrayRecursiveUnionExplicit/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive + > => + typia.random< + Primitive + >(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -135,7 +186,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -147,10 +198,17 @@ export namespace at { export async function store( connection: IConnection, body: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -167,10 +225,18 @@ export async function store( ); } export namespace store { - export type Input = IBucket; - export type Output = IPropagation<{ - 201: IBucket; - }>; + export type Input = + | IDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + export type Output = IPropagation< + { + 201: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 201 + >; export const METADATA = { method: "POST", @@ -183,13 +249,18 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arrayRecursiveUnionExplicit"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive + > => + typia.random< + Primitive + >(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -222,6 +293,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionImplicit/index.ts b/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionImplicit/index.ts index c3fa9b8ca..2189c0c00 100644 --- a/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionImplicit/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/arrayRecursiveUnionImplicit/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -14,27 +15,64 @@ import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { ArrayRecursiveUnionImplicit } from "../../structures/ArrayRecursiveUnionImplicit"; -import type { IBucket } from "../../structures/IBucket"; +import type { IDirectory } from "../../structures/IDirectory"; +import type { IImageFile } from "../../structures/IImageFile"; +import type { ISharedDirectory } from "../../structures/ISharedDirectory"; +import type { IShortcut } from "../../structures/IShortcut"; +import type { ITextFile } from "../../structures/ITextFile"; +import type { IZipFile } from "../../structures/IZipFile"; /** * @controller ArrayRecursiveUnionImplicitController.index * @path GET /arrayRecursiveUnionImplicit * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: ( + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + )[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: ArrayRecursiveUnionImplicit; - }>; + export type Output = IPropagation< + { + 200: ( + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + )[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -44,14 +82,36 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/arrayRecursiveUnionImplicit"; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved< + Primitive< + ( + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + )[] + > + > => + typia.random< + Primitive< + ( + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + )[] + > + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -64,7 +124,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -76,19 +136,50 @@ export namespace index { export async function at( connection: IConnection, id: number, -): Promise { +): Promise< + IPropagation< + { + 200: + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: IBucket.o1; - }>; + export type Output = IPropagation< + { + 200: + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + }, + 200 + >; export const METADATA = { method: "GET", @@ -98,14 +189,33 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/arrayRecursiveUnionImplicit/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive< + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + > + > => + typia.random< + Primitive< + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + > + >(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -135,7 +245,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -147,10 +257,23 @@ export namespace at { export async function store( connection: IConnection, body: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -167,10 +290,25 @@ export async function store( ); } export namespace store { - export type Input = IBucket.o1; - export type Output = IPropagation<{ - 201: IBucket.o1; - }>; + export type Input = + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + export type Output = IPropagation< + { + 201: + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut; + }, + 201 + >; export const METADATA = { method: "POST", @@ -183,13 +321,32 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arrayRecursiveUnionImplicit"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive< + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + > + > => + typia.random< + Primitive< + | IDirectory + | ISharedDirectory + | IImageFile + | ITextFile + | IZipFile + | IShortcut + > + >(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -222,6 +379,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/arraySimple/index.ts b/test/features/clone-and-propagate/src/api/functional/arraySimple/index.ts index 30698baf4..5f4fce395 100644 --- a/test/features/clone-and-propagate/src/api/functional/arraySimple/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/arraySimple/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -15,7 +16,6 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; import type { Format } from "typia/lib/tags/Format"; -import type { ArraySimple } from "../../structures/ArraySimple"; import type { IPerson } from "../../structures/IPerson"; /** @@ -23,19 +23,38 @@ import type { IPerson } from "../../structures/IPerson"; * @path GET /arraySimple * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: IPerson[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: ArraySimple; - }>; + export type Output = IPropagation< + { + 200: IPerson[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -45,13 +64,13 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/arraySimple"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -64,7 +83,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -76,19 +95,38 @@ export namespace index { export async function at( connection: IConnection, id: string & Format<"uuid">, -): Promise { +): Promise< + IPropagation< + { + 200: IPerson; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: IPerson; - }>; + export type Output = IPropagation< + { + 200: IPerson; + }, + 200 + >; export const METADATA = { method: "GET", @@ -98,14 +136,14 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => `/arraySimple/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = ( connection: IConnection, id: string & Format<"uuid">, @@ -138,7 +176,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -150,10 +188,17 @@ export namespace at { export async function store( connection: IConnection, body: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: IPerson; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -171,9 +216,12 @@ export async function store( } export namespace store { export type Input = IPerson; - export type Output = IPropagation<{ - 201: IPerson; - }>; + export type Output = IPropagation< + { + 201: IPerson; + }, + 201 + >; export const METADATA = { method: "POST", @@ -186,13 +234,13 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arraySimple"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -225,6 +273,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/bbs/articles/index.ts b/test/features/clone-and-propagate/src/api/functional/bbs/articles/index.ts index b14eed4b5..41e30cc04 100644 --- a/test/features/clone-and-propagate/src/api/functional/bbs/articles/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/bbs/articles/index.ts @@ -6,8 +6,9 @@ //================================================================ import type { IConnection, - Resolved, IPropagation, + Primitive, + Resolved, HttpError, } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; @@ -33,21 +34,39 @@ import type { IPageIBbsArticle } from "../../../structures/IPageIBbsArticle"; export async function index( connection: IConnection, section: string, - query: index.Query, -): Promise { + query: IPage.IRequest, +): Promise< + IPropagation< + { + 200: IPageIBbsArticle.ISummary; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection, section, query) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(section, query), + }, + ); } export namespace index { - export type Query = Resolved; - export type Output = IPropagation<{ - 200: IPageIBbsArticle.ISummary; - }>; + export type Output = IPropagation< + { + 200: IPageIBbsArticle.ISummary; + }, + 200 + >; export const METADATA = { method: "GET", @@ -57,29 +76,19 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; - export const path = (section: string, query: index.Query) => { - const variables: URLSearchParams = new URLSearchParams(); - for (const [key, value] of Object.entries(query as any)) - if (undefined === value) continue; - else if (Array.isArray(value)) - value.forEach((elem: any) => variables.append(key, String(elem))); - else variables.set(key, String(value)); - const location: string = `/bbs/articles/${encodeURIComponent(section ?? "null")}`; - return 0 === variables.size - ? location - : `${location}?${variables.toString()}`; - }; + export const path = (section: string, query: IPage.IRequest) => + `/bbs/articles/${encodeURIComponent(section ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, section: string, - query: index.Query, + query: IPage.IRequest, ): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -110,7 +119,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -129,10 +138,17 @@ export async function store( connection: IConnection, section: string, input: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: IBbsArticle; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, section, input) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -150,9 +166,12 @@ export async function store( } export namespace store { export type Input = IBbsArticle.IStore; - export type Output = IPropagation<{ - 201: IBbsArticle; - }>; + export type Output = IPropagation< + { + 201: IBbsArticle; + }, + 201 + >; export const METADATA = { method: "POST", @@ -165,14 +184,15 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => `/bbs/articles/${encodeURIComponent(section ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, section: string, @@ -207,7 +227,7 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -228,10 +248,17 @@ export async function update( section: string, id: string & Format<"uuid">, input: update.Input, -): Promise { +): Promise< + IPropagation< + { + 200: IBbsArticle; + }, + 200 + > +> { return !!connection.simulate ? update.simulate(connection, section, id, input) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -249,9 +276,12 @@ export async function update( } export namespace update { export type Input = IBbsArticle.IStore; - export type Output = IPropagation<{ - 200: IBbsArticle; - }>; + export type Output = IPropagation< + { + 200: IBbsArticle; + }, + 200 + >; export const METADATA = { method: "PUT", @@ -264,14 +294,15 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => `/bbs/articles/${encodeURIComponent(section ?? "null")}/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, section: string, @@ -308,6 +339,6 @@ export namespace update { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/health/index.ts b/test/features/clone-and-propagate/src/api/functional/health/index.ts index 747b90307..e83a79f4c 100644 --- a/test/features/clone-and-propagate/src/api/functional/health/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/health/index.ts @@ -13,19 +13,38 @@ import typia from "typia"; * @path GET /health * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise< + IPropagation< + { + 200: undefined; + }, + 200 + > +> { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.propagate(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { - export type Output = IPropagation<{ - 200: undefined; - }>; + export type Output = IPropagation< + { + 200: undefined; + }, + 200 + >; export const METADATA = { method: "GET", @@ -35,13 +54,12 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/health"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -54,6 +72,6 @@ export namespace get { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/multipart/index.ts b/test/features/clone-and-propagate/src/api/functional/multipart/index.ts index 8476f2318..283c32d5e 100644 --- a/test/features/clone-and-propagate/src/api/functional/multipart/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/multipart/index.ts @@ -24,10 +24,17 @@ import type { IMultipart } from "../../structures/IMultipart"; export async function post( connection: IConnection, body: post.Input, -): Promise { +): Promise< + IPropagation< + { + 201: undefined; + }, + 201 + > +> { return !!connection.simulate ? post.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( connection, { ...post.METADATA, @@ -39,9 +46,12 @@ export async function post( } export namespace post { export type Input = IMultipart; - export type Output = IPropagation<{ - 201: undefined; - }>; + export type Output = IPropagation< + { + 201: undefined; + }, + 201 + >; export const METADATA = { method: "POST", @@ -54,13 +64,12 @@ export namespace post { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/multipart"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = ( connection: IConnection, body: post.Input, @@ -93,6 +102,6 @@ export namespace post { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/objectLiteral/index.ts b/test/features/clone-and-propagate/src/api/functional/objectLiteral/index.ts index 8acb9c783..3393777de 100644 --- a/test/features/clone-and-propagate/src/api/functional/objectLiteral/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/objectLiteral/index.ts @@ -4,7 +4,12 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -17,19 +22,38 @@ export * as literal from "./literal"; * @path GET /objectLiteral * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: ObjectLietral[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: ObjectLietral[]; - }>; + export type Output = IPropagation< + { + 200: ObjectLietral[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -39,13 +63,14 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectLiteral"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -58,6 +83,6 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/objectLiteral/literal/index.ts b/test/features/clone-and-propagate/src/api/functional/objectLiteral/literal/index.ts index e3a71369a..9496268cd 100644 --- a/test/features/clone-and-propagate/src/api/functional/objectLiteral/literal/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/objectLiteral/literal/index.ts @@ -4,7 +4,12 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; import type { Format } from "typia/lib/tags/Format"; @@ -15,29 +20,54 @@ import type { Type } from "typia/lib/tags/Type"; * @path GET /objectLiteral/literal * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function literals( - connection: IConnection, -): Promise { +export async function literals(connection: IConnection): Promise< + IPropagation< + { + 200: { + id: string; + member: { + id: string & Format<"uuid">; + email: string & Format<"email">; + age: number & Type<"uint32">; + }; + created_at: string & Format<"date-time">; + }[]; + }, + 200 + > +> { return !!connection.simulate ? literals.simulate(connection) - : PlainFetcher.propagate(connection, { - ...literals.METADATA, - template: literals.METADATA.path, - path: literals.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...literals.METADATA, + template: literals.METADATA.path, + path: literals.path(), + }, + ); } export namespace literals { - export type Output = IPropagation<{ - 200: { - id: string; - member: { - id: string & Format<"uuid">; - email: string & Format<"email">; - age: number & Type<"uint32">; - }; - created_at: string & Format<"date-time">; - }[]; - }>; + export type Output = IPropagation< + { + 200: { + id: string; + member: { + id: string & Format<"uuid">; + email: string & Format<"email">; + age: number & Type<"uint32">; + }; + created_at: string & Format<"date-time">; + }[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -47,24 +77,14 @@ export namespace literals { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectLiteral/literal"; export const random = ( g?: Partial, ): Resolved< - { - id: string; - member: { - id: string & Format<"uuid">; - email: string & Format<"email">; - age: number & Type<"uint32">; - }; - created_at: string & Format<"date-time">; - }[] - > => - typia.random< + Primitive< { id: string; member: { @@ -74,6 +94,20 @@ export namespace literals { }; created_at: string & Format<"date-time">; }[] + > + > => + typia.random< + Primitive< + { + id: string; + member: { + id: string & Format<"uuid">; + email: string & Format<"email">; + age: number & Type<"uint32">; + }; + created_at: string & Format<"date-time">; + }[] + > >(g); export const simulate = (connection: IConnection): Output => { return { @@ -87,6 +121,6 @@ export namespace literals { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/objectSimple/index.ts b/test/features/clone-and-propagate/src/api/functional/objectSimple/index.ts index da7cb9931..c2764a57e 100644 --- a/test/features/clone-and-propagate/src/api/functional/objectSimple/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/objectSimple/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -21,19 +22,38 @@ import type { IBox3D } from "../../structures/IBox3D"; * @path GET /objectSimple * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: IBox3D[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: IBox3D[]; - }>; + export type Output = IPropagation< + { + 200: IBox3D[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -43,13 +63,13 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectSimple"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -62,7 +82,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -74,19 +94,38 @@ export namespace index { export async function at( connection: IConnection, id: number, -): Promise { +): Promise< + IPropagation< + { + 200: IBox3D; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: IBox3D; - }>; + export type Output = IPropagation< + { + 200: IBox3D; + }, + 200 + >; export const METADATA = { method: "GET", @@ -96,14 +135,14 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/objectSimple/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -133,7 +172,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -145,10 +184,17 @@ export namespace at { export async function store( connection: IConnection, body: store.Input, -): Promise { +): Promise< + IPropagation< + { + 201: IBox3D; + }, + 201 + > +> { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -166,9 +212,12 @@ export async function store( } export namespace store { export type Input = IBox3D; - export type Output = IPropagation<{ - 201: IBox3D; - }>; + export type Output = IPropagation< + { + 201: IBox3D; + }, + 201 + >; export const METADATA = { method: "POST", @@ -181,13 +230,13 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/objectSimple"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -220,6 +269,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/objectUnionExplicit/index.ts b/test/features/clone-and-propagate/src/api/functional/objectUnionExplicit/index.ts index 77b3e22a2..2f289ebf4 100644 --- a/test/features/clone-and-propagate/src/api/functional/objectUnionExplicit/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/objectUnionExplicit/index.ts @@ -4,30 +4,76 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { ObjectUnionExplicit } from "../../structures/ObjectUnionExplicit"; +import type { DiscriminatorcircleICircle } from "../../structures/DiscriminatorcircleICircle"; +import type { DiscriminatorlineILine } from "../../structures/DiscriminatorlineILine"; +import type { DiscriminatorpointIPoint } from "../../structures/DiscriminatorpointIPoint"; +import type { DiscriminatorpolygonIPolygon } from "../../structures/DiscriminatorpolygonIPolygon"; +import type { DiscriminatorpolylineIPolyline } from "../../structures/DiscriminatorpolylineIPolyline"; +import type { DiscriminatorrectangleIRectangle } from "../../structures/DiscriminatorrectangleIRectangle"; +import type { DiscriminatortriangleITriangle } from "../../structures/DiscriminatortriangleITriangle"; /** * @controller ObjectUnionExplicitController.get * @path GET /objectUnionExplicit * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise< + IPropagation< + { + 200: ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[]; + }, + 200 + > +> { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.propagate(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { - export type Output = IPropagation<{ - 200: ObjectUnionExplicit; - }>; + export type Output = IPropagation< + { + 200: ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -37,13 +83,38 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectUnionExplicit"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive< + ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[] + > + > => + typia.random< + Primitive< + ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[] + > + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -56,6 +127,6 @@ export namespace get { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/objectUnionImplicitControllere/index.ts b/test/features/clone-and-propagate/src/api/functional/objectUnionImplicitControllere/index.ts index d1d412c01..d358e027e 100644 --- a/test/features/clone-and-propagate/src/api/functional/objectUnionImplicitControllere/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/objectUnionImplicitControllere/index.ts @@ -4,30 +4,76 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { ObjectUnionImplicit } from "../../structures/ObjectUnionImplicit"; +import type { ICircle } from "../../structures/ICircle"; +import type { ILine } from "../../structures/ILine"; +import type { IPoint } from "../../structures/IPoint"; +import type { IPolygon } from "../../structures/IPolygon"; +import type { IPolyline } from "../../structures/IPolyline"; +import type { IRectangle } from "../../structures/IRectangle"; +import type { ITriangle } from "../../structures/ITriangle"; /** * @controller ObjectUnionImplicitController.get * @path GET /objectUnionImplicitControllere * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function get(connection: IConnection): Promise { +export async function get(connection: IConnection): Promise< + IPropagation< + { + 200: ( + | IPoint + | ILine + | ITriangle + | IRectangle + | IPolyline + | IPolygon + | ICircle + )[]; + }, + 200 + > +> { return !!connection.simulate ? get.simulate(connection) - : PlainFetcher.propagate(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...get.METADATA, + template: get.METADATA.path, + path: get.path(), + }, + ); } export namespace get { - export type Output = IPropagation<{ - 200: ObjectUnionImplicit; - }>; + export type Output = IPropagation< + { + 200: ( + | IPoint + | ILine + | ITriangle + | IRectangle + | IPolyline + | IPolygon + | ICircle + )[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -37,13 +83,38 @@ export namespace get { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectUnionImplicitControllere"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + Primitive< + ( + | IPoint + | ILine + | ITriangle + | IRectangle + | IPolyline + | IPolygon + | ICircle + )[] + > + > => + typia.random< + Primitive< + ( + | IPoint + | ILine + | ITriangle + | IRectangle + | IPolyline + | IPolygon + | ICircle + )[] + > + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -56,6 +127,6 @@ export namespace get { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/performance/index.ts b/test/features/clone-and-propagate/src/api/functional/performance/index.ts index c7e4b261a..1c54b6f54 100644 --- a/test/features/clone-and-propagate/src/api/functional/performance/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/performance/index.ts @@ -4,7 +4,12 @@ * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; +import type { + IConnection, + IPropagation, + Primitive, + Resolved, +} from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; @@ -15,19 +20,38 @@ import type { process } from "../../structures/process"; * @path GET /performance/cpu * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function cpu(connection: IConnection): Promise { +export async function cpu(connection: IConnection): Promise< + IPropagation< + { + 200: process.global.NodeJS.CpuUsage; + }, + 200 + > +> { return !!connection.simulate ? cpu.simulate(connection) - : PlainFetcher.propagate(connection, { - ...cpu.METADATA, - template: cpu.METADATA.path, - path: cpu.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...cpu.METADATA, + template: cpu.METADATA.path, + path: cpu.path(), + }, + ); } export namespace cpu { - export type Output = IPropagation<{ - 200: process.global.NodeJS.CpuUsage; - }>; + export type Output = IPropagation< + { + 200: process.global.NodeJS.CpuUsage; + }, + 200 + >; export const METADATA = { method: "GET", @@ -37,14 +61,14 @@ export namespace cpu { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/cpu"; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -57,7 +81,7 @@ export namespace cpu { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -66,19 +90,38 @@ export namespace cpu { * @path GET /performance/memory * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function memory(connection: IConnection): Promise { +export async function memory(connection: IConnection): Promise< + IPropagation< + { + 200: process.global.NodeJS.MemoryUsage; + }, + 200 + > +> { return !!connection.simulate ? memory.simulate(connection) - : PlainFetcher.propagate(connection, { - ...memory.METADATA, - template: memory.METADATA.path, - path: memory.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...memory.METADATA, + template: memory.METADATA.path, + path: memory.path(), + }, + ); } export namespace memory { - export type Output = IPropagation<{ - 200: process.global.NodeJS.MemoryUsage; - }>; + export type Output = IPropagation< + { + 200: process.global.NodeJS.MemoryUsage; + }, + 200 + >; export const METADATA = { method: "GET", @@ -88,14 +131,14 @@ export namespace memory { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/memory"; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -108,7 +151,7 @@ export namespace memory { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -117,21 +160,38 @@ export namespace memory { * @path GET /performance/resource * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function resource( - connection: IConnection, -): Promise { +export async function resource(connection: IConnection): Promise< + IPropagation< + { + 200: process.global.NodeJS.ResourceUsage; + }, + 200 + > +> { return !!connection.simulate ? resource.simulate(connection) - : PlainFetcher.propagate(connection, { - ...resource.METADATA, - template: resource.METADATA.path, - path: resource.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...resource.METADATA, + template: resource.METADATA.path, + path: resource.path(), + }, + ); } export namespace resource { - export type Output = IPropagation<{ - 200: process.global.NodeJS.ResourceUsage; - }>; + export type Output = IPropagation< + { + 200: process.global.NodeJS.ResourceUsage; + }, + 200 + >; export const METADATA = { method: "GET", @@ -141,14 +201,14 @@ export namespace resource { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/resource"; export const random = ( g?: Partial, - ): Resolved => - typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -161,6 +221,6 @@ export namespace resource { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/index.ts b/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/index.ts index d567e687b..7d8a258d3 100644 --- a/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -33,10 +34,17 @@ export * as password from "./password"; export async function join( connection: IConnection, input: join.Input, -): Promise { - const output: join.Output = !!connection.simulate +): Promise< + IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > +> { + const output: ISeller.IAuthorized = !!connection.simulate ? join.simulate(connection, input) - : await EncryptedFetcher.propagate( + : await EncryptedFetcher.propagate( { ...connection, headers: { @@ -59,9 +67,12 @@ export async function join( } export namespace join { export type Input = ISeller.IJoin; - export type Output = IPropagation<{ - 201: ISeller.IAuthorized; - }>; + export type Output = IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + >; export const METADATA = { method: "POST", @@ -74,13 +85,14 @@ export namespace join { type: "text/plain", encrypted: true, }, - status: null, + status: 201, } as const; export const path = () => "/sellers/authenticate/join"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, input: join.Input, @@ -113,7 +125,7 @@ export namespace join { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -131,10 +143,17 @@ export namespace join { export async function login( connection: IConnection, input: login.Input, -): Promise { - const output: login.Output = !!connection.simulate +): Promise< + IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > +> { + const output: ISeller.IAuthorized = !!connection.simulate ? login.simulate(connection, input) - : await EncryptedFetcher.propagate( + : await EncryptedFetcher.propagate( { ...connection, headers: { @@ -157,9 +176,12 @@ export async function login( } export namespace login { export type Input = ISeller.ILogin; - export type Output = IPropagation<{ - 201: ISeller.IAuthorized; - }>; + export type Output = IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + >; export const METADATA = { method: "POST", @@ -172,13 +194,14 @@ export namespace login { type: "text/plain", encrypted: true, }, - status: null, + status: 201, } as const; export const path = () => "/sellers/authenticate/login"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => + typia.random>(g); export const simulate = ( connection: IConnection, input: login.Input, @@ -211,7 +234,7 @@ export namespace login { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -221,19 +244,38 @@ export namespace login { * @path DELETE /sellers/authenticate/exit * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function exit(connection: IConnection): Promise { +export async function exit(connection: IConnection): Promise< + IPropagation< + { + 200: undefined; + }, + 200 + > +> { return !!connection.simulate ? exit.simulate(connection) - : PlainFetcher.propagate(connection, { - ...exit.METADATA, - template: exit.METADATA.path, - path: exit.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...exit.METADATA, + template: exit.METADATA.path, + path: exit.path(), + }, + ); } export namespace exit { - export type Output = IPropagation<{ - 200: undefined; - }>; + export type Output = IPropagation< + { + 200: undefined; + }, + 200 + >; export const METADATA = { method: "DELETE", @@ -243,13 +285,12 @@ export namespace exit { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/sellers/authenticate/exit"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -262,6 +303,6 @@ export namespace exit { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/password/index.ts b/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/password/index.ts index 3853ad94d..546bfe4e8 100644 --- a/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/password/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/sellers/authenticate/password/index.ts @@ -29,10 +29,17 @@ import type { ISeller } from "../../../../structures/ISeller"; export async function change( connection: IConnection, input: change.Input, -): Promise { +): Promise< + IPropagation< + { + 200: undefined; + }, + 200 + > +> { return !!connection.simulate ? change.simulate(connection, input) - : EncryptedFetcher.propagate( + : EncryptedFetcher.propagate( { ...connection, headers: { @@ -50,9 +57,12 @@ export async function change( } export namespace change { export type Input = ISeller.IChangePassword; - export type Output = IPropagation<{ - 200: undefined; - }>; + export type Output = IPropagation< + { + 200: undefined; + }, + 200 + >; export const METADATA = { method: "PATCH", @@ -65,13 +75,12 @@ export namespace change { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/sellers/authenticate/password/change"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); + export const random = (g?: Partial): Resolved => + typia.random(g); export const simulate = ( connection: IConnection, input: change.Input, @@ -104,6 +113,6 @@ export namespace change { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/template/index.ts b/test/features/clone-and-propagate/src/api/functional/template/index.ts index bb71ae639..0808ca645 100644 --- a/test/features/clone-and-propagate/src/api/functional/template/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/template/index.ts @@ -7,6 +7,7 @@ import type { IConnection, IPropagation, + Primitive, Resolved, HttpError, } from "@nestia/fetcher"; @@ -21,19 +22,38 @@ import type { Template } from "../../structures/Template"; * @path GET /template * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function index(connection: IConnection): Promise { +export async function index(connection: IConnection): Promise< + IPropagation< + { + 200: Template[]; + }, + 200 + > +> { return !!connection.simulate ? index.simulate(connection) - : PlainFetcher.propagate(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...index.METADATA, + template: index.METADATA.path, + path: index.path(), + }, + ); } export namespace index { - export type Output = IPropagation<{ - 200: Template[]; - }>; + export type Output = IPropagation< + { + 200: Template[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -43,13 +63,13 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/template"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved> => typia.random>(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -62,7 +82,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -74,19 +94,38 @@ export namespace index { export async function at( connection: IConnection, id: number, -): Promise { +): Promise< + IPropagation< + { + 200: Template; + }, + 200 + > +> { return !!connection.simulate ? at.simulate(connection, id) - : PlainFetcher.propagate(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(id), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...at.METADATA, + template: at.METADATA.path, + path: at.path(id), + }, + ); } export namespace at { - export type Output = IPropagation<{ - 200: Template; - }>; + export type Output = IPropagation< + { + 200: Template; + }, + 200 + >; export const METADATA = { method: "GET", @@ -96,14 +135,14 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/template/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved