diff --git a/benchmark/package.json b/benchmark/package.json index 5cdf3830c..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.7.0" + "typia": "^6.8.0" }, "devDependencies": { "@types/autocannon": "^7.9.0", diff --git a/package.json b/package.json index 21f8ea747..64e5b3898 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.20240814", "description": "Nestia station", "scripts": { "build": "node build/index.js", diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json index 5aa5e2eb3..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.7.0", + "typia": "^6.8.0", "uuid": "^10.0.0" }, "dependencies": { 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/core/package.json b/packages/core/package.json index 11b5bda97..5604aa553 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.20240814", "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,10 +36,10 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^3.10.0", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240814.tgz", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", - "@samchon/openapi": "^0.4.3", + "@samchon/openapi": "^0.4.5", "detect-ts-node": "^1.0.5", "get-function-location": "^2.0.0", "glob": "^7.2.0", @@ -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", "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=3.10.0", + "@nestia/fetcher": ">=3.11.0-dev.20240814", "@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 <7.0.0" }, "devDependencies": { "@fastify/multipart": "^8.1.0", 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/TypedException.ts b/packages/core/src/decorators/TypedException.ts index 3bc87ad3b..9304c8965 100644 --- a/packages/core/src/decorators/TypedException.ts +++ b/packages/core/src/decorators/TypedException.ts @@ -1,3 +1,21 @@ +/** + * > 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 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` * @@ -14,6 +32,8 @@ * @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( @@ -21,6 +41,26 @@ export function TypedException( description?: string | undefined, ): never; +/** + * 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 props Properties for the exception + * @returns Method decorator + * + * @author Jeongho Nam - https://github.com/samchon + */ +export function TypedException( + props: TypedException.IProps, +): MethodDecorator; + /** * Exception decorator. * @@ -36,6 +76,8 @@ export function TypedException( * @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( @@ -46,24 +88,25 @@ export function TypedException( /** * @internal */ -export function TypedException( - status: number | "2XX" | "3XX" | "4XX" | "5XX", - description?: string | undefined, - type?: string | undefined, -): MethodDecorator { +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, 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 +114,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/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/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/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/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/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/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/e2e/package.json b/packages/e2e/package.json index bda2c1502..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.7.0" + "typia": "^6.8.0" }, "files": [ "lib", diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index 438a19d15..054af5d71 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.20240814", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -43,6 +43,6 @@ "src" ], "dependencies": { - "@samchon/openapi": "^0.4.3" + "@samchon/openapi": "^0.4.5" } } \ No newline at end of file diff --git a/packages/fetcher/src/IPropagation.ts b/packages/fetcher/src/IPropagation.ts index 8a2b17677..d6d6a88b4 100644 --- a/packages/fetcher/src/IPropagation.ts +++ b/packages/fetcher/src/IPropagation.ts @@ -1,5 +1,3 @@ -import { Primitive } from "./Primitive"; - /** * Propagation type. * @@ -73,7 +71,7 @@ export namespace IPropagation { : StatusValue extends number ? StatusValue : never; - data: Primitive; + data: BodyData; headers: Record; } diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 26d1a78a5..f2b409d53 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.5", "commander": "10.0.0", "inquirer": "8.2.5", "prettier": "^3.2.5", "tstl": "^3.0.0", "typescript": "^5.5.4", - "typia": "^6.7.0" + "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 9e656f5ed..532f024cb 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.20240814", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -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" }, @@ -32,9 +32,9 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "^3.10.0", - "@nestia/fetcher": "^3.10.0", - "@samchon/openapi": "^0.4.3", + "@nestia/core": "../core/nestia-core-3.11.0-dev.20240814.tgz", + "@nestia/fetcher": "../fetcher/nestia-fetcher-3.11.0-dev.20240814.tgz", + "@samchon/openapi": "^0.4.5", "@wrtnio/openai-function-schema": "^0.2.3", "cli": "^1.0.1", "get-function-location": "^2.0.0", @@ -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" }, "peerDependencies": { - "@nestia/core": ">=3.10.0", - "@nestia/fetcher": ">=3.10.0", + "@nestia/core": ">=3.11.0-dev.20240814", + "@nestia/fetcher": ">=3.11.0-dev.20240814", "@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 <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 2cfc3b884..0dbffd20e 100644 --- a/packages/sdk/src/NestiaSdkApplication.ts +++ b/packages/sdk/src/NestiaSdkApplication.ts @@ -1,29 +1,49 @@ -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 { 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 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) @@ -49,11 +69,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); + }, }); } @@ -71,7 +91,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 { @@ -91,7 +114,9 @@ export class NestiaSdkApplication { ); print_title("Nestia Swagger Generator"); - await this.generate(SwaggerGenerator.generate); + await this.generate({ + generate: SwaggerGenerator.generate, + }); } public async openai(): Promise { @@ -111,21 +136,19 @@ 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 //---- const unique: WeakSet = new WeakSet(); - const controllers: IReflectController[] = []; const project: INestiaProject = { config: this.config, input: await ConfigAnalyzer.input(this.config), @@ -135,28 +158,36 @@ 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 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}`); @@ -174,82 +205,66 @@ 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)), - ); - } - - // 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); - - // DO GENERATE - AccessorAnalyzer.analyze(routeList); - await archiver(project)(routeList); + console.log("Analyzing soure codes"); + + // METADATA COMPONENTS + const collection: IMetadataDictionary = + TypedHttpRouteAnalyzer.dictionary(controllers); + + // 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), + }), + ); + } + AccessorAnalyzer.analyze(routes); + + if (props.validate !== undefined) + props.validate({ + project, + collection, + routes, + }); + if (project.errors.length) + return report({ + type: "error", + errors: project.errors, + }); + await props.generate({ + project, + collection, + routes, + }); } } @@ -259,60 +274,48 @@ 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_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); - } - - 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(""); - } - } - }; - -// 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 {} +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 ${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); + } }; + +const wrapPaths = (paths: string[]): string[] => + paths.length === 0 ? [""] : paths; diff --git a/packages/sdk/src/analyses/ConfigAnalyzer.ts b/packages/sdk/src/analyses/ConfigAnalyzer.ts index 6efa2ace7..765d03d64 100644 --- a/packages/sdk/src/analyses/ConfigAnalyzer.ts +++ b/packages/sdk/src/analyses/ConfigAnalyzer.ts @@ -1,44 +1,43 @@ /// +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 => - 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."); + export const input = async ( + config: INestiaConfig, + ): 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, {}, true as any), + { + 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 +46,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 +87,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/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 2243f0d21..0fd6e399f 100644 --- a/packages/sdk/src/analyses/GenericAnalyzer.ts +++ b/packages/sdk/src/analyses/GenericAnalyzer.ts @@ -1,20 +1,18 @@ 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(); + ): WeakMap { + const dict: WeakMap = new WeakMap(); explore(checker, dict, classNode); return dict; } 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/ImportAnalyzer.ts b/packages/sdk/src/analyses/ImportAnalyzer.ts index c2a525f03..484dab72e 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 && getNameOfSymbol(type.symbol) === "Promise") { const generic: readonly ts.Type[] = checker.getTypeArguments( type as ts.TypeReference, ); @@ -59,98 +63,109 @@ 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 getNameOfSymbol = (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( + .map(getEscapedText) + .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 = getNameOfSymbol(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, ) : name; + + const getEscapedText = (type: IReflectType): string => + type.typeArguments + ? `${type.name}<${type.typeArguments.map(getEscapedText).join(", ")}>` + : type.name; } diff --git a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts index b202ce97a..e069da340 100644 --- a/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectControllerAnalyzer.ts @@ -5,140 +5,90 @@ import { } from "@nestjs/common/constants"; import { INestiaProject } from "../structures/INestiaProject"; +import { INestiaSdkInput } from "../structures/INestiaSdkInput"; 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"; 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, + 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; } 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", + props.controller.class.prototype, + key, + ); + if (metadata === undefined) continue; + else if (metadata.jsDocTags.some((tag) => tag.name === "ignore")) + 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..10c53b0df 100644 --- a/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectHttpOperationAnalyzer.ts @@ -1,300 +1,173 @@ -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 { METHOD_METADATA, PATH_METADATA } from "@nestjs/common/constants"; import { ranges } from "tstl"; -import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; import { IReflectController } from "../structures/IReflectController"; import { IReflectHttpOperation } from "../structures/IReflectHttpOperation"; -import { ParamCategory } from "../structures/ParamCategory"; +import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; +import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +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"; 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 []; - - 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); - })(); - - // 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.`, - }); + 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 errors: IReflectOperationError[] = []; + const method: string = + METHODS[Reflect.getMetadata(METHOD_METADATA, props.function)]; + if (method === undefined || method === "OPTIONS") return null; + + const parameters: IReflectHttpOperationParameter[] = + ReflectHttpOperationParameterAnalyzer.analyze({ + controller: props.controller, + metadata: props.metadata, + httpMethod: method, + function: props.function, + functionName: props.name, + errors, + }); + const success: IReflectHttpOperationSuccess | null = (() => { + const localErrors: IReflectOperationError[] = []; + const success = ReflectHttpOperationResponseAnalyzer.analyze({ + controller: props.controller, + function: props.function, + functionName: props.name, + httpMethod: method, + metadata: props.metadata, + errors, + }); + if (localErrors.length) { + errors.push(...localErrors); return null; } - - // DO CONSTRUCT - const example: SwaggerExample.IData | undefined = - Reflect.getMetadata("nestia/SwaggerExample/Response", props.function); - const meta: IReflectHttpOperation = { - protocol: "http", + return success; + })(); + if (errors.length) { + props.project.errors.push(...errors); + return null; + } else if (success === null) return null; + + // 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, + class: props.controller.class.name, + function: props.name, + from: "", + contents: ["@nestia/sdk does not compose wildcard method."], + }); + return false; + } + return true; + }), + versions: ReflectMetadataAnalyzer.versions(props.function), + parameters, + success, + security: ReflectMetadataAnalyzer.securities(props.function), + exceptions: ReflectHttpOperationExceptionAnalyzer.analyze({ + controller: props.controller, 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; - - // 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(); + functionName: props.name, + httpMethod: method, + metadata: props.metadata, + errors, + }), + 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, + ...Object.values(props.metadata.exceptions).map((e) => e.imports), + ].flat(), + ), + description: props.metadata.description, + jsDocTags: props.metadata.jsDocTags, + operationId: props.metadata.jsDocTags + .find(({ name }) => name === "operationId") + ?.text?.[0].text.split(" ")[0] + .trim(), + }; - // 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( + // 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; + + // LIST UP PARAMETERS + const binded: string[] | null = PathAnalyzer.parameters(location); + if (binded === null) { + props.project.errors.push({ + file: props.controller.file, + class: props.controller.class.name, + function: props.name, + from: "{parameters}", + contents: [`invalid path (${JSON.stringify(location)})`], + }); + continue; + } + const parameters: string[] = operation.parameters + .filter((param) => param.category === "param") + .map((param) => param.field!) + .sort(); + + // DO VALIDATE + if (ranges.equal(binded.sort(), parameters) === false) + errors.push({ + file: props.controller.file, + class: props.controller.class.name, + function: props.name, + from: "{parameters}", + contents: [ + `binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${binded.join( ", ", )}], parameters: [${parameters.join(", ")}]).`, - }); - } - - // RETURNS - if (errors.length) { - project.errors.push(...errors); - return null; + ], + }); } - return meta; - }; - - 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); + // RETURNS + if (errors.length) { + props.project.errors.push(...errors); + return null; + } + return operation; }; - -// 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 = [ 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 new file mode 100644 index 000000000..9ddefefe0 --- /dev/null +++ b/packages/sdk/src/analyses/ReflectHttpOperationParameterAnalyzer.ts @@ -0,0 +1,348 @@ +import { SwaggerExample } from "@nestia/core"; +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 { IReflectController } from "../structures/IReflectController"; +import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter"; +import { IReflectOperationError } from "../structures/IReflectOperationError"; +import { IOperationMetadata } from "../transformers/IOperationMetadata"; +import { TextPlainValidator } from "../transformers/TextPlainValidator"; +import { HttpHeadersValidator } from "../validators/HttpHeadersValidator"; +import { HttpQueryValidator } from "../validators/HttpQueryValidator"; + +export namespace ReflectHttpOperationParameterAnalyzer { + export interface IContext { + controller: IReflectController; + function: Function; + functionName: string; + httpMethod: string; + metadata: IOperationMetadata; + errors: IReflectOperationError[]; + } + export const analyze = (ctx: IContext): IReflectHttpOperationParameter[] => { + const preconfigured: IReflectHttpOperationParameter.IPreconfigured[] = + analyzePreconfigured(ctx); + 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.category === "body") + ) + contradict(`@Body() is not allowed in the ${ctx.httpMethod} method.`); + + // FIND DUPLICATED BODY + if ( + preconfigured.filter( + (x) => x.category === "body" && x.field === undefined, + ).length > 1 + ) + contradict(`Duplicated @Body() is not allowed.`); + if ( + 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.category === "headers" && x.field === undefined, + ).length > 1 + ) + contradict(`Duplicated @Headers() without field name is not allowed.`); + + // FIND DUPLICATED FIELDS + if ( + isUnique( + preconfigured + .filter((x) => x.category === "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.category === "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.category === "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) + 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.category === "body" && + (p.contentType === "application/json" || p.encrypted === true) + ? matched.primitive + : matched.resolved; + return result.success ? result.data : null; + })(); + if (p.category === "body" && p.field !== undefined) + pErrorContents.push(`@Body() must not have a field name.`); + else if (p.category === "param" && p.field === undefined) + pErrorContents.push(`@Param() must have a field name.`); + + if (pErrorContents.length) return report(); + else if ( + matched === undefined || + matched.type === null || + schema === null + ) + return null; // unreachable + + const example: SwaggerExample.IData | undefined = ( + Reflect.getMetadata( + "nestia/SwaggerExample/Parameters", + ctx.controller.class.prototype, + ctx.functionName, + ) ?? [] + ).find((x: SwaggerExample.IData) => x.index === matched.index); + + // COMPOSITION + if (p.category === "param") + return { + category: p.category, + index: p.index, + field: p.field!, + name: matched.name, + type: matched.type, + validate: HttpParameterProgrammer.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, + example: example?.example, + examples: example?.examples, + ...schema, + }; + else if (p.category === "query") + return { + category: p.category, + index: p.index, + field: p.field ?? null, + name: matched.name, + type: matched.type, + validate: p.field + ? HttpQueryValidator.validate + : HttpQueryProgrammer.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, + example: example?.example, + examples: example?.examples, + ...schema, + }; + else if (p.category === "headers") + return { + category: p.category, + index: p.index, + field: p.field ?? null, + name: matched.name, + type: matched.type, + validate: p.field + ? HttpHeadersValidator.validate + : HttpHeadersProgrammer.validate, + description: matched.description, + jsDocTags: matched.jsDocTags, + example: example?.example, + examples: example?.examples, + ...schema, + }; + else if (p.category === "body") + return { + category: p.category, + index: p.index, + encrypted: !!p.encrypted, + contentType: p.contentType, + name: matched.name, + type: matched.type, + 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, + description: matched.description, + jsDocTags: matched.jsDocTags, + example: example?.example, + examples: example?.examples, + ...schema, + }; + else { + pErrorContents.push(`Unknown kind of the parameter.`); + return report(); + } + }) + .filter((x): x is IReflectHttpOperationParameter => x !== null); + + if (errors.length) ctx.errors.push(...errors); + return parameters; + }; + + const analyzePreconfigured = ( + props: IContext, + ): IReflectHttpOperationParameter.IPreconfigured[] => { + const dict: NestParameters | undefined = Reflect.getMetadata( + ROUTE_ARGS_METADATA, + props.controller.class, + 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 category: + | IReflectHttpOperationParameter.IPreconfigured["category"] + | null = getNestParamType(Number(symbol[0]) as RouteParamtypes); + if (category === null) return null; + if (category === "body") + return { + category: "body", + index: param.index, + field: param.data, + contentType: "application/json", + }; + else + return { + category, + 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 { + category: "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 { + category: "headers", + index: param.index, + field: param.data, + }; + else if (param.factory.name === "TypedParam") + return { + category: "param", + index: param.index, + field: param.data, + }; + else if (param.factory.name === "TypedQuery") + return { + category: "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..c9b0cff6c --- /dev/null +++ b/packages/sdk/src/analyses/ReflectHttpOperationResponseAnalyzer.ts @@ -0,0 +1,125 @@ +import { SwaggerExample } from "@nestia/core"; +import { + HEADERS_METADATA, + HTTP_CODE_METADATA, + 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 { 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 { + controller: IReflectController; + function: Function; + functionName: string; + httpMethod: string; + metadata: IOperationMetadata; + 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, + }); + 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"); + + 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) + errors.push(`HEAD method must not have a content type.`); + if ( + typia.is(contentType) === + false + ) + errors.push( + `@nestia/sdk does not support ${JSON.stringify(contentType)} content type.`, + ); + + if (errors.length) return report(); + else if ( + ctx.metadata.success.type === null || + schema.success === false || + !typia.is(contentType) + ) + return null; + + const example: SwaggerExample.IData | undefined = Reflect.getMetadata( + "nestia/SwaggerExample/Response", + ctx.function, + ); + return { + contentType: contentType, + encrypted, + status: + getStatus(ctx.function) ?? (ctx.httpMethod === "POST" ? 201 : 200), + type: ctx.metadata.success.type, + ...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."] + : [], + example: example?.example, + examples: example?.examples, + }; + }; + + 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..e68577468 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -1,96 +1,169 @@ import { ranges } from "tstl"; -import { IErrorReport } from "../structures/IErrorReport"; import { INestiaProject } from "../structures/INestiaProject"; 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"; 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[] = ( + // @todo -> detailing is required + const errors: string[] = []; + 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.category === "acceptor") === undefined)) + errors.push("@WebSocketRoute.Acceptor() is essentially required"); + if (preconfigured.length !== ctx.function.length) + errors.push( + [ + "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 errors.push( + `Unable to find parameter type of the ${p.index} (th).`, + ); + else if (matched.type === null) + return errors.push( + `Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`, + ); + else if ( + p.category === "param" && + !(p as IReflectWebSocketOperationParameter.IParam).field?.length + ) + return errors.push(`@WebSocketRoute.Param() must have a field name.`); + else if ( + p.category === "acceptor" && + matched.type?.typeArguments?.length !== 3 + ) + return `@WebSocketRoute.Acceptor() must have three type arguments.`; + else if ( + p.category === "driver" && + matched.type?.typeArguments?.length !== 1 + ) + return errors.push( + `@WebSocketRoute.Driver() must have one type argument.`, + ); + + // COMPLETE COMPOSITION + 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" || + p.category === "query" + ) + return { + ...p, + name: matched.name, + type: matched.type, + }; + else if (p.category === "param") + return { + ...p, + category: "param", + field: p.field!, + name: matched.name, + type: matched.type, + imports: matched.imports, + description: matched.description, + jsDocTags: matched.jsDocTags, + } satisfies IReflectWebSocketOperationParameter.IParam; + + // UNKNOWN TYPE, MAYBE NEW FEATURE + return errors.push( + `@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[] = 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.category === "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) + errors.push(`invalid path (${JSON.stringify(location)})`); + else if (ranges.equal(binded.sort(), fields) === false) + errors.push( + `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({ + file: ctx.controller.file, + class: ctx.controller.class.name, + function: ctx.function.name, + from: ctx.name, + contents: 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/TypedHttpRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts new file mode 100644 index 000000000..d27d87745 --- /dev/null +++ b/packages/sdk/src/analyses/TypedHttpRouteAnalyzer.ts @@ -0,0 +1,181 @@ +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"; +import { PathUtil } from "../utils/PathUtil"; + +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.category === "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) { + props.errors.push(...errors); + return []; + } + return props.paths.map((path) => ({ + ...props.operation, + controller: props.controller, + path, + accessors: [...PathUtil.accessors(path), props.operation.name], + 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/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/analyses/TypedWebSocketRouteAnalyzer.ts b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts new file mode 100644 index 000000000..8b1432359 --- /dev/null +++ b/packages/sdk/src/analyses/TypedWebSocketRouteAnalyzer.ts @@ -0,0 +1,18 @@ +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: { + controller: IReflectController; + operation: IReflectWebSocketOperation; + paths: string[]; + }): ITypedWebSocketRoute[] => + props.paths.map((path) => ({ + ...props.operation, + controller: props.controller, + path, + accessors: [...PathUtil.accessors(path), props.operation.name], + })); +} diff --git a/packages/sdk/src/decorators/OperationMetadata.ts b/packages/sdk/src/decorators/OperationMetadata.ts new file mode 100644 index 000000000..77420b164 --- /dev/null +++ b/packages/sdk/src/decorators/OperationMetadata.ts @@ -0,0 +1,15 @@ +import { IOperationMetadata } from "../transformers/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..38afcb3af 100644 --- a/packages/sdk/src/executable/internal/NestiaConfigLoader.ts +++ b/packages/sdk/src/executable/internal/NestiaConfigLoader.ts @@ -32,15 +32,29 @@ 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.`); + 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: rawCompilerOptions, - require: rawCompilerOptions.baseUrl + compilerOptions: { + ...compilerOptions, + plugins, + }, + require: compilerOptions.baseUrl ? ["tsconfig-paths/register"] : undefined, }); diff --git a/packages/sdk/src/executable/internal/NestiaSdkCommand.ts b/packages/sdk/src/executable/internal/NestiaSdkCommand.ts index 8c9c896f0..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 @@ -34,10 +35,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/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 33a91f00c..b5241e51a 100644 --- a/packages/sdk/src/generates/CloneGenerator.ts +++ b/packages/sdk/src/generates/CloneGenerator.ts @@ -2,23 +2,25 @@ import fs from "fs"; import ts from "typescript"; import { INestiaProject } from "../structures/INestiaProject"; -import { ITypedHttpRoute } from "../structures/ITypedHttpRoute"; +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 = - (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 const write = async (app: ITypedApplication): Promise => { + const dict: Map = + SdkHttpCloneProgrammer.write(app); + if (dict.size === 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); + }; const writeDtoFile = (project: INestiaProject) => 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..a8b969415 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 { 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..772410fd8 100644 --- a/packages/sdk/src/generates/SwaggerGenerator.ts +++ b/packages/sdk/src/generates/SwaggerGenerator.ts @@ -1,312 +1,104 @@ +import { SwaggerCustomizer } from "@nestia/core"; 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 + console.log("Generating Swagger Document"); + 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); + fillPaths({ ...props, schema, document }); + + return document; + }; - /* --------------------------------------------------------- - INITIALIZERS - --------------------------------------------------------- */ export const initialize = async ( config: INestiaConfig.ISwaggerConfig, ): Promise => { @@ -378,126 +170,106 @@ 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, - ); - 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"); + const fillPaths = (props: { + config: INestiaConfig.ISwaggerConfig; + document: OpenApi.IDocument; + schema: (metadata: Metadata) => OpenApi.IJsonSchema | undefined; + routes: ITypedHttpRoute[]; + }): void => { + // SWAGGER CUSTOMIZER + const customizers: Array<() => void> = []; + const neighbor = { + at: new Singleton(() => { + const functor: Map = new Map(); + for (const r of props.routes) { + const method: OpenApi.Method = + r.method.toLowerCase() as OpenApi.Method; + const path: string = getPath(r); + const operation: OpenApi.IOperation | undefined = + props.document.paths?.[path]?.[method]; + if (operation === undefined) continue; + functor.set(r.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 props.document.paths?.[path]?.[method]; + }, + ), + }; - // 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", + // COMPOSE OPERATIONS + for (const r of props.routes) { + const operation: OpenApi.IOperation = SwaggerOperationComposer.compose({ + ...props, + route: r, }); - const deprecated = route.jsDocTags.find( - (tag) => tag.name === "deprecated", + const path: string = getPath(r); + props.document.paths ??= {}; + props.document.paths[path] ??= {}; + props.document.paths[path][r.method.toLowerCase() as "get"] = operation; + + const closure: Function | Function[] | undefined = Reflect.getMetadata( + "nestia/SwaggerCustomizer", + r.controller.class.prototype, + r.name, ); - - // 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(" "); + if (closure !== undefined) { + const array: Function[] = Array.isArray(closure) ? closure : [closure]; + customizers.push(() => { + for (const closure of array) + closure({ + swagger: props.document, + method: r.method, + path, + route: operation, + at: (func: Function) => neighbor.at.get().get(func), + get: (accessor: Accessor) => neighbor.get.get()(accessor), + } satisfies SwaggerCustomizer.IProps); + }); } + } - // 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); - } + // DO CUSTOMIZE + for (const fn of customizers) fn(); + }; - function emend_security( - input: OpenApi.ISecurityScheme, - ): OpenApi.ISecurityScheme { - if (input.type === "apiKey") - return { - ...input, - in: input.in ?? "header", - name: input.name ?? "Authorization", - }; - return input; - } + const getPath = (route: { + path: string; + parameters: ITypedHttpRouteParameter[]; + }): string => { + let str: string = route.path; + const filtered: ITypedHttpRouteParameter.IParam[] = route.parameters.filter( + (param) => param.category === "param", + ); + for (const param of filtered) + str = str.replace(`:${param.field}`, `{${param.field}}`); + return str; + }; } interface Accessor { diff --git a/packages/sdk/src/generates/internal/E2eFileProgrammer.ts b/packages/sdk/src/generates/internal/E2eFileProgrammer.ts index d5a35e91a..3fddf0183 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.category === "headers" && p.field === null, ); const connection = headers ? ts.factory.createObjectLiteralExpression( @@ -91,7 +90,13 @@ export namespace E2eFileProgrammer { SdkImportWizard.typia(importer), ), )("random"), - [getTypeName(project)(importer)(headers)], + [ + project.config.clone === true + ? SdkAliasCollection.from(project)(importer)( + headers.metadata, + ) + : SdkAliasCollection.name(headers), + ], undefined, ), ), @@ -117,7 +122,11 @@ export namespace E2eFileProgrammer { IdentifierFactory.access( ts.factory.createIdentifier(SdkImportWizard.typia(importer)), )("random"), - [getTypeName(project)(importer)(p)], + [ + project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p), + ], undefined, ), ), @@ -151,7 +160,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 +177,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/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/SdkAliasCollection.ts b/packages/sdk/src/generates/internal/SdkAliasCollection.ts index b08ba9e0a..64f93e55e 100644 --- a/packages/sdk/src/generates/internal/SdkAliasCollection.ts +++ b/packages/sdk/src/generates/internal/SdkAliasCollection.ts @@ -1,26 +1,38 @@ 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 { 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 = + export const name = ({ type }: { type: IReflectType }): ts.TypeNode => + ts.factory.createTypeReferenceNode( + type.name, + type.typeArguments + ? type.typeArguments.map((a) => name({ type: a })) + : undefined, + ); + + export const from = (project: INestiaProject) => (importer: ImportDictionary) => - (p: ITypedHttpRoute.IParameter | ITypedHttpRoute.IOutput): ts.TypeNode => - p.metadata - ? SdkTypeProgrammer.write(project)(importer)(p.metadata) - : ts.factory.createTypeReferenceNode(p.typeName); + (metadata: Metadata) => + SdkTypeProgrammer.write(project)(importer)(metadata); 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 => { + 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( importer.external({ @@ -35,8 +47,10 @@ 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 => { + 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( importer.external({ @@ -51,19 +65,21 @@ export namespace SdkAliasCollection { export const input = (project: INestiaProject) => (importer: ImportDictionary) => - (param: ITypedHttpRoute.IParameter): ts.TypeNode => { - const type: ts.TypeNode = name(project)(importer)(param); - if (project.config.clone === true || project.config.primitive === false) - return type; + (param: ITypedHttpRouteParameter): 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( importer.external({ 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], ); @@ -73,41 +89,37 @@ export namespace SdkAliasCollection { (project: INestiaProject) => (importer: ImportDictionary) => (route: ITypedHttpRoute): ts.TypeNode => { - 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; - - 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; - return ts.factory.createTypeReferenceNode( - importer.external({ - type: true, - library: "@nestia/fetcher", - instance: - route.output.contentType === "application/x-www-form-urlencoded" - ? "Resolved" - : "Primitive", - }), - [node], - ); - } + const schema = (p: { metadata: Metadata; type: IReflectType }) => + p.metadata.size() === 0 + ? TypeFactory.keyword("void") + : project.config.clone === true + ? from(project)(importer)(p.metadata) + : project.config.primitive !== false + ? ts.factory.createTypeReferenceNode( + importer.external({ + type: true, + library: "@nestia/fetcher", + instance: + route.success.contentType === "application/json" || + route.success.encrypted === true + ? "Primitive" + : "Resolved", + }), + [name(p)], + ) + : name(p); + if (project.config.propagate !== true) return schema(route.success); 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: schema(route.success), }, ...Object.entries(route.exceptions).map(([status, value]) => ({ status, - type: name(project)(importer)(value), + type: schema(value), })), ]; return ts.factory.createTypeReferenceNode( @@ -127,10 +139,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..3996d9801 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) => @@ -78,18 +75,18 @@ export namespace SdkFileProgrammer { `${outDir}/index.ts`, ); directory.routes.forEach((route, i) => { - if (project.config.clone !== true || route.protocol === "websocket") + if (!(project.config.clone === true && route.protocol === "http")) 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..cef246dac 100644 --- a/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpCloneProgrammer.ts @@ -1,15 +1,14 @@ 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 { 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"; @@ -23,69 +22,51 @@ export namespace SdkHttpCloneProgrammer { | ((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; - } - - 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; - }; + export const write = (app: ITypedApplication): Map => { + // console.log({ + // objects: Array.from(app.collection.objects.keys()).sort(), + // aliases: Array.from(app.collection.aliases.keys()).sort(), + // }); + // 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), + }); + 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) => @@ -117,13 +98,6 @@ export namespace SdkHttpCloneProgrammer { }; } -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 => { diff --git a/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts new file mode 100644 index 000000000..bc7239419 --- /dev/null +++ b/packages/sdk/src/generates/internal/SdkHttpCloneReferencer.ts @@ -0,0 +1,71 @@ +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"; + +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, + 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) => ({ + file: `${props.directory}/${str}`, + instances: [str], + })); + }; + + const visitType = (p: { + unique: Set; + metadata: Metadata; + type: IReflectType; + 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()); + }; +} + +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 0161a143e..de5d29298 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 { TypeFactory } from "typia/lib/factories/TypeFactory"; -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( @@ -48,7 +47,7 @@ export namespace SdkHttpFunctionProgrammer { [], undefined, p.name, - p.optional + p.metadata.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, project.config.primitive !== false && @@ -56,53 +55,52 @@ export namespace SdkHttpFunctionProgrammer { ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) - : getTypeName(project)(importer)(p), + : project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p), ), ), ], ts.factory.createTypeReferenceNode("Promise", [ - getReturnType(project.config)(route), + project.config.propagate === true || + route.success.metadata.size() !== 0 + ? ts.factory.createTypeReferenceNode(`${route.name}.Output`) + : ts.factory.createTypeReferenceNode("void"), ]), ts.factory.createBlock( - write_body(project.config)(importer)(route, props), + write_body(project)(importer)(route, props), true, ), ); const write_body = - (config: INestiaConfig) => + (project: INestiaProject) => (importer: ImportDictionary) => ( 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 || route.success.encrypted, + )(importer), ), - )(config.propagate ? "propagate" : "fetch"), - undefined, + )(project.config.propagate ? "propagate" : "fetch"), + project.config.propagate + ? route.method.toLowerCase() === "get" || + route.method.toLowerCase() === "head" + ? [TypeFactory.keyword("any")] + : [TypeFactory.keyword("any"), TypeFactory.keyword("any")] + : undefined, [ - contentType && contentType !== "multipart/form-data" + props.input?.contentType !== "multipart/form-data" ? ts.factory.createObjectLiteralExpression( [ ts.factory.createSpreadAssignment( @@ -119,7 +117,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, @@ -164,8 +164,8 @@ export namespace SdkHttpFunctionProgrammer { ...(props.input ? [ts.factory.createIdentifier(props.input.name)] : []), - ...(config.json && - typia.is(props.input) && + ...(project.config.json && + props.input !== undefined && (props.input.contentType === "application/json" || props.input.encrypted === true) ? [ts.factory.createIdentifier(`${route.name}.stringify`)] @@ -173,7 +173,7 @@ export namespace SdkHttpFunctionProgrammer { ], ); const output = (awaiter: boolean) => - config.simulate + project.config.simulate ? ts.factory.createConditionalExpression( ts.factory.createIdentifier("!!connection.simulate"), undefined, @@ -194,7 +194,7 @@ export namespace SdkHttpFunctionProgrammer { ? ts.factory.createAwaitExpression(caller()) : caller(); return [ - ...(config.assert + ...(project.config.assert ? route.parameters .filter((p) => p.category !== "headers") .map((p) => @@ -215,14 +215,15 @@ export namespace SdkHttpFunctionProgrammer { ), ) : []), - ...(route.setHeaders.length === 0 + ...(route.success.setHeaders.length === 0 ? [ts.factory.createReturnStatement(output(false))] - : write_set_headers(config)(route)(output(true))), + : write_set_headers(project)(importer)(route)(output(true))), ]; }; const write_set_headers = - (config: INestiaConfig) => + (project: INestiaProject) => + (importer: ImportDictionary) => (route: ITypedHttpRoute) => (condition: ts.Expression): ts.Statement[] => { const accessor = (x: string) => (y: string) => @@ -232,7 +233,9 @@ export namespace SdkHttpFunctionProgrammer { ...route.parameters.map((p) => p.name), ])("output"); const headers: string = accessor("connection")("headers"); - const data: string = config.propagate ? accessor(output)("data") : output; + const data: string = project.config.propagate + ? accessor(output)("data") + : output; const assigners: ts.ExpressionStatement[] = [ ts.factory.createBinaryExpression( @@ -240,7 +243,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,14 +270,14 @@ export namespace SdkHttpFunctionProgrammer { ts.factory.createVariableDeclaration( output, undefined, - getReturnType(config)(route), + SdkAliasCollection.output(project)(importer)(route), condition, ), ], ts.NodeFlags.Const, ), ), - ...(config.propagate + ...(project.config.propagate ? [ ts.factory.createIfStatement( ts.factory.createIdentifier(accessor(output)("success")), @@ -289,18 +292,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..0d8adfe00 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); @@ -35,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(project)(importer)(route, props), + write_path(project)(importer)(route, props.query), ...(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,19 +92,23 @@ export namespace SdkHttpNamespaceProgrammer { "Input", SdkAliasCollection.input(project)(importer)(props.input), ); - if (project.config.propagate === true || route.output.typeName !== "void") + if ( + project.config.propagate === true || + route.success.metadata.size() !== 0 + ) declare("Output", SdkAliasCollection.output(project)(importer)(route)); return array; }; const write_metadata = + (project: INestiaProject) => (importer: ImportDictionary) => ( 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 +127,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 +143,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( @@ -164,9 +164,11 @@ export namespace SdkHttpNamespaceProgrammer { `${SdkImportWizard.typia(importer)}.http.createAssertQuery`, ), [ - ts.factory.createTypeReferenceNode( - route.output.typeName, - ), + project.config.clone === true + ? SdkAliasCollection.from(project)(importer)( + route.success.metadata, + ) + : SdkAliasCollection.name(route.success), ], undefined, ), @@ -187,9 +189,7 @@ export namespace SdkHttpNamespaceProgrammer { (importer: ImportDictionary) => ( route: ITypedHttpRoute, - props: { - query: ITypedHttpRoute.IParameter | undefined; - }, + query: ITypedHttpRouteParameter.IQuery | undefined, ): ts.VariableStatement => { const g = { total: [ @@ -197,9 +197,9 @@ export namespace SdkHttpNamespaceProgrammer { (param) => param.category === "param" || param.category === "query", ), ], - query: route.parameters.filter( - (param) => param.category === "query" && param.field !== undefined, - ), + 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) => @@ -210,8 +210,8 @@ export namespace SdkHttpNamespaceProgrammer { g.total.map((p) => IdentifierFactory.parameter( p.name, - p === props.query - ? p.optional + p === query + ? p.metadata.isRequired() === false ? ts.factory.createUnionTypeNode([ ts.factory.createTypeReferenceNode( `${route.name}.Query`, @@ -219,7 +219,9 @@ export namespace SdkHttpNamespaceProgrammer { ts.factory.createTypeReferenceNode("undefined"), ]) : ts.factory.createTypeReferenceNode(`${route.name}.Query`) - : getType(project)(importer)(p), + : project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p), ), ), undefined, @@ -244,8 +246,17 @@ export namespace SdkHttpNamespaceProgrammer { undefined, [ ts.factory.createBinaryExpression( - ts.factory.createIdentifier( - g.path.find((p) => p.field === name)!.name, + ts.factory.createCallChain( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier( + g.path.find((p) => p.field === name)!.name, + ), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + "toString", + ), + undefined, + undefined, + [], ), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createStringLiteral("null"), @@ -259,14 +270,11 @@ 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 => - g.total - .filter((p) => p.category !== "headers") - .find((p) => p.name === str) !== undefined + g.total.find((p) => p.name === str) !== undefined ? computeName("_" + str) : str; const variables: string = computeName("variables"); @@ -415,26 +423,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.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), ), ] : []), @@ -513,10 +521,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..b778cfbec 100644 --- a/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts +++ b/packages/sdk/src/generates/internal/SdkHttpRouteProgrammer.ts @@ -14,12 +14,12 @@ 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, - ), + headers: route.parameters + .filter((p) => p.category === "headers") + .find((p) => p.field === null), + query: route.parameters + .filter((p) => p.category === "query") + .find((p) => p.field === null), input: route.parameters.find((p) => p.category === "body"), }; return [ @@ -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..2ed4837e4 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,14 @@ 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.metadata.size() !== 0; const caller = () => ts.factory.createCallExpression( ts.factory.createIdentifier("random"), @@ -105,7 +106,7 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createTypeReferenceNode( SdkImportWizard.IConnection(importer), route.parameters.some( - (p) => p.category === "headers" && p.field === undefined, + (p) => p.category === "headers" && p.field === null, ) ? [ ts.factory.createTypeReferenceNode( @@ -122,7 +123,7 @@ export namespace SdkHttpSimulationProgrammer { [], undefined, p.name, - p.optional + p.metadata.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, project.config.primitive !== false && @@ -130,7 +131,9 @@ export namespace SdkHttpSimulationProgrammer { ? ts.factory.createTypeReferenceNode( `${route.name}.${p === props.query ? "Query" : "Input"}`, ) - : getTypeName(project)(importer)(p), + : project.config.clone === true + ? SdkAliasCollection.from(project)(importer)(p.metadata) + : SdkAliasCollection.name(p), ), ), ], @@ -141,28 +144,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.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.output.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(), ), @@ -222,7 +228,7 @@ export namespace SdkHttpSimulationProgrammer { ts.factory.createPropertyAssignment( "contentType", ts.factory.createIdentifier( - JSON.stringify(route.output.contentType), + JSON.stringify(route.success.contentType), ), ), ], @@ -256,11 +262,7 @@ export namespace SdkHttpSimulationProgrammer { "assert", ), undefined, - [ - ts.factory.createIdentifier( - p.category === "headers" ? "connection.headers" : p.name, - ), - ], + [ts.factory.createIdentifier(p.name)], ), ), ], @@ -353,11 +355,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..e79d3576b 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,257 @@ 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.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", - ts.factory.createTypeReferenceNode( - ( - (route.parameters.find((x) => x.category === "header") as - | ITypedWebSocketRoute.IHeaderParameter - | undefined) ?? acceptor.header - ).typeName, - ), + SdkAliasCollection.name({ + type: (route.parameters.find((x) => x.category === "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({ + type: + driver?.type.typeArguments?.[0] ?? + acceptor.type.typeArguments?.[2]!, + }), ); declare( "Listener", - ts.factory.createTypeReferenceNode(acceptor.provider.typeName), + SdkAliasCollection.name({ + type: acceptor.type.typeArguments?.[1]!, + }), ); - if (query) - declare("Query", ts.factory.createTypeReferenceNode(query.typeName)); + if (query) declare("Query", SdkAliasCollection.name(query)); 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.category === "param", + ) as ITypedWebSocketRouteParameter.IParam[]; + const query: ITypedWebSocketRouteParameter.IQuery | undefined = + route.parameters.find((p) => p.category === "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, + 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.createCallChain( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier( + pathParams.find((p) => p.field === name)!.name, + ), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + "toString", ), - ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), - ts.factory.createStringLiteral("null"), + undefined, + undefined, + [], ), - ], - ), - (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( + 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.createForOfStatement( + 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 +357,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..cee6bad3d 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 => { @@ -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( @@ -81,7 +78,7 @@ export namespace SdkWebSocketRouteProgrammer { IdentifierFactory.parameter( p.name, p.category === "param" - ? getPathParameterType(project)(importer)(p) + ? SdkAliasCollection.name(p) : ts.factory.createTypeReferenceNode(`${route.name}.Query`), ), ), @@ -167,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`)], ), )( @@ -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..18a43ed58 --- /dev/null +++ b/packages/sdk/src/generates/internal/SwaggerOperationComposer.ts @@ -0,0 +1,115 @@ +import { OpenApi } from "@samchon/openapi"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; + +import { INestiaConfig } from "../../INestiaConfig"; +import { SecurityAnalyzer } from "../../analyses/SecurityAnalyzer"; +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.category === "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(" "); + } + } + + // SECURITY + const security: Record[] = SecurityAnalyzer.merge( + ...props.route.controller.security, + ...props.route.security, + ...props.route.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(), + ); + + // 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, + }), + security: security.length ? security : undefined, + }; + }; +} diff --git a/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts new file mode 100644 index 000000000..93a85b9e2 --- /dev/null +++ b/packages/sdk/src/generates/internal/SwaggerOperationParameterComposer.ts @@ -0,0 +1,176 @@ +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.category === "body" + ? [] + : props.parameter.category === "param" + ? [path({ ...props, parameter: props.parameter })] + : props.parameter.category === "query" + ? query({ ...props, parameter: props.parameter }) + : header({ ...props, parameter: props.parameter }); + + export const body = ( + props: Omit, "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", + }), + example: props.parameter.example, + examples: props.parameter.examples, + }); + + 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.category === "query" ? "query" : "header", + schema: props.schema, + ...SwaggerDescriptionComposer.compose({ + description: + props.parameter.description ?? + props.parameter.jsDocTags.find((tag) => tag.name === "description") + ?.text?.[0].text ?? + props.jsDocTags + .find( + (tag) => + tag.name === "param" && + tag.text?.[0].text === props.parameter.name, + ) + ?.text?.map((e) => e.text) + .join("") + .substring(props.parameter.name.length) ?? + null, + jsDocTags: props.jsDocTags, + kind: "title", + }), + required: props.parameter.metadata.isRequired(), + example: props.parameter.example, + examples: props.parameter.examples, + }; + if ( + props.config.decompose === false || + props.parameter.metadata.objects.length === 0 + ) + return [param]; + return props.parameter.metadata.objects[0].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.category === "query" ? "query" : "header", + schema: json.schemas[0], + required: p.value.isRequired(), + ...SwaggerDescriptionComposer.compose({ + description: p.description ?? null, + jsDocTags: p.jsDocTags, + kind: "title", + }), + }; + }); + }; +} + +const warning = new VariadicSingleton((described: boolean): string => { + const summary = "Request body must be encrypted."; + const component = + "[EncryptedBody](https://github.com/samchon/@nestia/core#encryptedbody)"; + const content: string[] = [ + "## Warning", + "", + summary, + "", + `The request body data would be encrypted as "AES-128(256) / CBC mode / PKCS#5 Padding / Base64 Encoding", through the ${component} component.`, + "", + `Therefore, just utilize this swagger editor only for referencing. If you need to call the real API, using [SDK](https://github.com/samchon/nestia#software-development-kit) would be much better.`, + ]; + if (described === true) content.push("", "----------------", "", ""); + return content.join("\n"); +}); 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/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 86a0da847..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 { INormalizedInput } from "./INormalizedInput"; +import { INestiaSdkInput } from "./INestiaSdkInput"; +import { IReflectOperationError } from "./IReflectOperationError"; export interface INestiaProject { config: INestiaConfig; - input: INormalizedInput; + input: INestiaSdkInput; checker: ts.TypeChecker; - errors: IErrorReport[]; - warnings: IErrorReport[]; + errors: IReflectOperationError[]; + warnings: IReflectOperationError[]; } 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/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/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..be6e5c664 100644 --- a/packages/sdk/src/structures/IReflectHttpOperation.ts +++ b/packages/sdk/src/structures/IReflectHttpOperation.ts @@ -1,6 +1,10 @@ import { VERSION_NEUTRAL } from "@nestjs/common/interfaces"; +import { IJsDocTagInfo } from "typia"; -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 +13,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"; + parameters: IReflectHttpOperationParameter[]; + success: IReflectHttpOperationSuccess; + exceptions: Record; security: Record[]; - exceptions: Record< - number | "2XX" | "3XX" | "4XX" | "5XX", - IReflectHttpOperation.IException - >; - 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; - } + 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 new file mode 100644 index 000000000..5ff3da68c --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationException.ts @@ -0,0 +1,19 @@ +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 IReflectHttpOperationException { + // BASIC PROPERTIES + status: number | "2XX" | "3XX" | "4XX" | "5XX"; + description: string | null; + example?: any; + examples?: Record; + + // REFLECTED PROPERTIES + type: IReflectType; + metadata: IMetadata; + components: IMetadataComponents; + validate: MetadataFactory.Validator; +} diff --git a/packages/sdk/src/structures/IReflectHttpOperationParameter.ts b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts new file mode 100644 index 000000000..e3e70bb56 --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationParameter.ts @@ -0,0 +1,81 @@ +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"; + +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 { + category: Category; + name: string; + index: number; + type: IReflectType; + metadata: IMetadata; + components: IMetadataComponents; + validate: MetadataFactory.Validator; + example?: any; + examples?: Record; + description: string | null; + jsDocTags: IJsDocTagInfo[]; + } + + /** + * @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 { + category: Category; + index: number; + } + } +} diff --git a/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts new file mode 100644 index 000000000..8cb0cee48 --- /dev/null +++ b/packages/sdk/src/structures/IReflectHttpOperationSuccess.ts @@ -0,0 +1,22 @@ +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 { + type: IReflectType; + status: number; + contentType: + | "application/json" + | "text/plain" + | "application/x-www-form-urlencoded" + | "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..16b447e92 --- /dev/null +++ b/packages/sdk/src/structures/IReflectOperationError.ts @@ -0,0 +1,26 @@ +import { IComparable } from "tstl"; + +import { IOperationMetadata } from "../transformers/IOperationMetadata"; + +export interface IReflectOperationError { + file: string; + class: string; + function: string | null; + 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/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..1d50fd749 --- /dev/null +++ b/packages/sdk/src/structures/IReflectWebSocketOperationParameter.ts @@ -0,0 +1,38 @@ +import { IJsDocTagInfo } from "typia"; + +import { IReflectType } from "./IReflectType"; +import { IReflectTypeImport } from "./IReflectTypeImport"; + +export type IReflectWebSocketOperationParameter = + | IReflectWebSocketOperationParameter.IAcceptor + | IReflectWebSocketOperationParameter.IDriver + | IReflectWebSocketOperationParameter.IHeader + | IReflectWebSocketOperationParameter.IParam + | IReflectWebSocketOperationParameter.IQuery; +export namespace IReflectWebSocketOperationParameter { + 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 { + category: Category; + name: string; + index: number; + type: IReflectType; + imports: IReflectTypeImport[]; + description: string | null; + jsDocTags: IJsDocTagInfo[]; + } + + /** + * @internal + */ + export interface IPreconfigured { + category: "acceptor" | "driver" | "header" | "param" | "query"; + index: number; + field?: string; + } +} 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/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/ITypedApplication.ts b/packages/sdk/src/structures/ITypedApplication.ts new file mode 100644 index 000000000..5a498b941 --- /dev/null +++ b/packages/sdk/src/structures/ITypedApplication.ts @@ -0,0 +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/structures/ITypedHttpRoute.ts b/packages/sdk/src/structures/ITypedHttpRoute.ts index d034d5772..c35c388dc 100644 --- a/packages/sdk/src/structures/ITypedHttpRoute.ts +++ b/packages/sdk/src/structures/ITypedHttpRoute.ts @@ -1,59 +1,29 @@ -import ts from "typescript"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { IJsDocTagInfo } from "typia"; import { IReflectController } from "./IReflectController"; -import { IReflectHttpOperation } from "./IReflectHttpOperation"; +import { IReflectTypeImport } from "./IReflectTypeImport"; +import { ITypedHttpRouteException } from "./ITypedHttpRouteException"; +import { ITypedHttpRouteParameter } from "./ITypedHttpRouteParameter"; +import { ITypedHttpRouteSuccess } from "./ITypedHttpRouteSuccess"; export interface ITypedHttpRoute { protocol: "http"; - controller: IReflectController; function: Function; + controller: IReflectController; 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[]; + parameters: ITypedHttpRouteParameter[]; + success: ITypedHttpRouteSuccess; exceptions: Record< number | "2XX" | "3XX" | "4XX" | "5XX", - ITypedHttpRoute.IOutput + ITypedHttpRouteException >; - 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"; - } + 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..018be8f29 --- /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 { + category: Category; + 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 index 5d9372604..bb9278783 100644 --- a/packages/sdk/src/structures/ITypedWebSocketRoute.ts +++ b/packages/sdk/src/structures/ITypedWebSocketRoute.ts @@ -1,68 +1,20 @@ +import { VERSION_NEUTRAL } from "@nestjs/common"; import ts from "typescript"; -import { Metadata } from "typia/lib/schemas/metadata/Metadata"; import { IReflectController } from "./IReflectController"; -import { IReflectWebSocketOperation } from "./IReflectWebSocketOperation"; -import { ITypeTuple } from "./ITypeTuple"; +import { IReflectTypeImport } from "./IReflectTypeImport"; +import { ITypedWebSocketRouteParameter } from "./ITypedWebSocketRouteParameter"; export interface ITypedWebSocketRoute { protocol: "websocket"; controller: IReflectController; name: string; path: string; - accessors: string[]; - parameters: ITypedWebSocketRoute.IParameter[]; - imports: [string, string[]][]; - - location: string; - description?: string; + function: Function; + versions: Array | undefined; + parameters: ITypedWebSocketRouteParameter[]; + imports: IReflectTypeImport[]; + description: string | null; 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/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/transform.ts b/packages/sdk/src/transform.ts new file mode 100644 index 000000000..cc1f3830a --- /dev/null +++ b/packages/sdk/src/transform.ts @@ -0,0 +1,9 @@ +import ts from "typescript"; + +import { SdkOperationTransformer } from "./transformers/SdkOperationTransformer"; + +export const transform = ( + program: ts.Program, +): ts.TransformerFactory => + SdkOperationTransformer.iterateFile(program.getTypeChecker()); +export default transform; diff --git a/packages/sdk/src/transformers/IOperationMetadata.ts b/packages/sdk/src/transformers/IOperationMetadata.ts new file mode 100644 index 000000000..b2000ccd0 --- /dev/null +++ b/packages/sdk/src/transformers/IOperationMetadata.ts @@ -0,0 +1,44 @@ +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 "../structures/IReflectType"; +import { IReflectTypeImport } from "../structures/IReflectTypeImport"; + +export interface IOperationMetadata { + parameters: IOperationMetadata.IParameter[]; + success: IOperationMetadata.IResponse; + exceptions: IOperationMetadata.IResponse[]; + description: string | null; + jsDocTags: IJsDocTagInfo[]; +} +export namespace IOperationMetadata { + export interface IParameter extends IResponse { + name: string; + index: number; + description: string | null; + jsDocTags: IJsDocTagInfo[]; + } + export interface IResponse { + type: IReflectType | null; + imports: IReflectTypeImport[]; + primitive: ValidationPipe; + resolved: ValidationPipe; + } + export interface IException { + type: IReflectType | null; + imports: IReflectTypeImport[]; + primitive: ValidationPipe; + } + + export interface ISchema { + components: IMetadataComponents; + metadata: IMetadata; + } + export interface IError { + name: string; + accessor: string | null; + messages: string[]; + } +} diff --git a/packages/sdk/src/transformers/ISdkOperationTransformerContext.ts b/packages/sdk/src/transformers/ISdkOperationTransformerContext.ts new file mode 100644 index 000000000..4d6d46de8 --- /dev/null +++ b/packages/sdk/src/transformers/ISdkOperationTransformerContext.ts @@ -0,0 +1,8 @@ +import ts from "typescript"; +import { MetadataCollection } from "typia/lib/factories/MetadataCollection"; + +export interface ISdkOperationTransformerContext { + checker: ts.TypeChecker; + api: ts.TransformationContext; + collection: MetadataCollection; +} diff --git a/packages/sdk/src/transformers/SdkOperationProgrammer.ts b/packages/sdk/src/transformers/SdkOperationProgrammer.ts new file mode 100644 index 000000000..f176d2156 --- /dev/null +++ b/packages/sdk/src/transformers/SdkOperationProgrammer.ts @@ -0,0 +1,212 @@ +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 { 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 { MetadataUtil } from "../utils/MetadataUtil"; +import { IOperationMetadata } from "./IOperationMetadata"; +import { ISdkOperationTransformerContext } from "./ISdkOperationTransformerContext"; + +export namespace SdkOperationProgrammer { + export interface IProps { + context: ISdkOperationTransformerContext; + generics: WeakMap; + node: ts.MethodDeclaration; + symbol: ts.Symbol | undefined; + exceptions: ts.TypeNode[]; + } + export const write = (p: IProps): IOperationMetadata => { + 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: p.context.checker.getSignatureFromDeclaration(p.node), + }), + }), + 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: ISdkOperationTransformerContext; + generics: WeakMap; + parameter: ts.ParameterDeclaration; + index: number; + }): IOperationMetadata.IParameter => { + const symbol: ts.Symbol | undefined = + props.context.checker.getSymbolAtLocation(props.parameter); + const common: IOperationMetadata.IResponse = writeType({ + context: props.context, + generics: props.generics, + type: + props.context.checker.getTypeFromTypeNode( + props.parameter.type ?? TypeFactory.keyword("any"), + ) ?? null, + required: props.parameter.questionToken === undefined, + }); + return { + ...common, + name: props.parameter.name.getText(), + index: props.index, + description: (symbol && CommentFactory.description(symbol)) ?? null, + jsDocTags: symbol?.getJsDocTags() ?? [], + }; + }; + + const writeResponse = (props: { + context: ISdkOperationTransformerContext; + generics: WeakMap; + type: ts.Type | null; + }): IOperationMetadata.IResponse => + writeType({ + ...props, + required: true, + }); + + const writeType = (p: { + context: ISdkOperationTransformerContext; + generics: WeakMap; + type: ts.Type | null; + required: boolean; + }): IOperationMetadata.IResponse => { + 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, + primitive: writeSchema({ + collection: p.context.collection, + result: primitive, + }), + resolved: writeSchema({ + collection: p.context.collection, + result: resolved, + }), + }; + }; + + const writeSchema = (p: { + collection: MetadataCollection; + 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 { + 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(), + }, + }; + }; + + const getReturnType = (p: { + checker: ts.TypeChecker; + signature: ts.Signature | undefined; + }): ts.Type | null => { + const type: ts.Type | null = + (p.signature && p.checker.getReturnTypeOfSignature(p.signature)) ?? 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 = (metadata: Metadata): Set => { + const names: Set = new Set(); + 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; +}; + +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/SdkOperationTransformer.ts b/packages/sdk/src/transformers/SdkOperationTransformer.ts new file mode 100644 index 000000000..31fd3b2c7 --- /dev/null +++ b/packages/sdk/src/transformers/SdkOperationTransformer.ts @@ -0,0 +1,240 @@ +import path from "path"; +import { HashSet, Singleton, 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 "./IOperationMetadata"; +import { ISdkOperationTransformerContext } from "./ISdkOperationTransformerContext"; +import { SdkOperationProgrammer } from "./SdkOperationProgrammer"; + +export namespace SdkOperationTransformer { + export const iterateFile = + (checker: ts.TypeChecker) => (api: ts.TransformationContext) => { + const context: ISdkOperationTransformerContext = { + checker, + api, + collection: collection.get(), + }; + 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) => + iterateNode({ + 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 iterateNode = (props: { + context: ISdkOperationTransformerContext; + visitor: IVisitor; + node: ts.Node; + }): ts.Node => + ts.visitEachChild( + transformNode(props), + (child) => + iterateNode({ + ...props, + node: child, + }), + props.context.api, + ); + + const transformNode = (props: { + context: ISdkOperationTransformerContext; + visitor: IVisitor; + node: ts.Node; + }): ts.Node => { + return ts.isClassDeclaration(props.node) + ? transformClass({ + ...props, + node: props.node, + }) + : props.node; + }; + + const transformClass = (props: { + context: ISdkOperationTransformerContext; + visitor: IVisitor; + node: ts.ClassDeclaration; + }): ts.ClassDeclaration => { + const generics: WeakMap = GenericAnalyzer.analyze( + 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, + 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, + symbol: symbolDict.get(m.name.getText()), + }) + : m, + ), + ); + }; + + const transformMethod = (props: { + context: ISdkOperationTransformerContext; + visitor: IVisitor; + 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) + : (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 = SdkOperationProgrammer.write({ + ...props, + exceptions: getExceptionTypes({ + checker: props.context.checker, + decorators, + }), + }); + 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, + ); + }; + + 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 { + 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); + } +} + +const TYPED_EXCEPTION_PATH = path.join( + "@nestia", + "core", + "lib", + "decorators", + `TypedException.d.ts`, +); + +const collection = new Singleton( + () => + new MetadataCollection({ + replace: MetadataCollection.replace, + }), +); 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 []; + }; +} 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 3fcc64c70..496a734b9 100644 --- a/packages/sdk/src/utils/StringUtil.ts +++ b/packages/sdk/src/utils/StringUtil.ts @@ -1,6 +1,17 @@ export namespace StringUtil { + export const capitalize = (text: string): string => + text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); + export const escapeDuplicate = (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/src/validators/HttpHeadersValidator.ts b/packages/sdk/src/validators/HttpHeadersValidator.ts new file mode 100644 index 000000000..a17f0075f --- /dev/null +++ b/packages/sdk/src/validators/HttpHeadersValidator.ts @@ -0,0 +1,34 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { MetadataArrayType } from "typia/lib/schemas/metadata/MetadataArrayType"; + +export namespace HttpHeadersValidator { + export const validate = ( + meta: Metadata, + explore: MetadataFactory.IExplore, + ): string[] => { + const errors: string[] = []; + const insert = (msg: string) => errors.push(msg); + + if (explore.top === true) { + const expected: number = + meta.atomics.length + + meta.templates.length + + meta.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0) + + meta.arrays.length; + if (meta.size() !== expected) + insert("Only atomic or array of atomic types are allowed."); + } else if ( + explore.nested !== null && + explore.nested instanceof MetadataArrayType + ) { + 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) + insert("Only atomic types are allowed in array."); + } + return errors; + }; +} diff --git a/packages/sdk/src/validators/HttpQueryValidator.ts b/packages/sdk/src/validators/HttpQueryValidator.ts new file mode 100644 index 000000000..ac20291ec --- /dev/null +++ b/packages/sdk/src/validators/HttpQueryValidator.ts @@ -0,0 +1,34 @@ +import { MetadataFactory } from "typia/lib/factories/MetadataFactory"; +import { Metadata } from "typia/lib/schemas/metadata/Metadata"; +import { MetadataArrayType } from "typia/lib/schemas/metadata/MetadataArrayType"; + +export namespace HttpQueryValidator { + export const validate = ( + meta: Metadata, + explore: MetadataFactory.IExplore, + ): string[] => { + const errors: string[] = []; + const insert = (msg: string) => errors.push(msg); + + if (explore.top === true) { + const expected: number = + meta.atomics.length + + meta.templates.length + + meta.constants.map((c) => c.values.length).reduce((a, b) => a + b, 0) + + meta.arrays.length; + if (meta.size() !== expected) + insert("Only atomic or array of atomic types are allowed."); + } else if ( + explore.nested !== null && + explore.nested instanceof MetadataArrayType + ) { + 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) + insert("Only atomic types are allowed in array."); + } + return errors; + }; +} 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/executable/test.js b/test/executable/test.js new file mode 100644 index 000000000..95e956d60 --- /dev/null +++ b/test/executable/test.js @@ -0,0 +1,15 @@ +const cp = require("child_process"); +const fs = require("fs"); + +const directory = fs.readdirSync(`${__dirname}/../features`); +for (const feature of directory) { + const location = `${__dirname}/../features/${feature}`; + if (feature.includes("error")) continue; + else if (fs.existsSync(`${location}/src/test`) === false) continue; + + console.log(`Testing ${feature}`); + cp.execSync(`npx ts-node src/test`, { + cwd: location, + stdio: "inherit", + }); +} 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 9bc5cc256..d56e25401 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, 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"; @@ -44,7 +44,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 +58,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..8cf10f2e5 100644 --- a/test/features/all/src/api/functional/performance/index.ts +++ b/test/features/all/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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/all/swagger.json b/test/features/all/swagger.json index d532be12b..46c122230 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.20240814", "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": { @@ -31,7 +33,7 @@ "required": true }, "responses": { - "201": { + "200": { "description": "Newly archived article", "content": { "application/json": { @@ -41,9 +43,7 @@ } } } - }, - "summary": "Store an article", - "description": "Store an article.\n\nCreate an article, and returns it." + } } }, "/health": { @@ -52,7 +52,9 @@ "parameters": [], "responses": { "200": { - "description": "" + "content": { + "application/json": {} + } } } } @@ -63,7 +65,6 @@ "parameters": [], "responses": { "200": { - "description": "", "content": { "application/json": { "schema": { @@ -78,9 +79,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 +106,8 @@ } }, "required": [ + "id", + "created_at", "title", "body", "files" @@ -139,17 +150,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 +169,6 @@ } }, "required": [ - "id", - "created_at", "title", "body", "files" @@ -309,9 +310,7 @@ }, "securitySchemes": { "bearer": { - "type": "apiKey", - "in": "header", - "name": "Authorization" + "type": "apiKey" } } }, diff --git a/test/features/app-globalPrefix-versionUri-routerModule/nestia.config.ts b/test/features/app-globalPrefix-versionUri-routerModule/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/nestia.config.ts +++ b/test/features/app-globalPrefix-versionUri-routerModule/nestia.config.ts @@ -8,6 +8,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-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..5d9525cf0 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..cd3c5368f 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -123,7 +141,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 +155,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -179,7 +197,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 +211,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..09776b384 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @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 type Output = Primitive; @@ -32,7 +41,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..a494f1543 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..05984ecf2 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -123,7 +141,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 +155,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -179,7 +197,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 +211,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..c4cf1a214 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @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 type Output = Primitive; @@ -32,7 +41,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..d69a7457e 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..bd65e3acb 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -123,7 +141,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 +155,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -179,7 +197,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 +211,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..0eca98782 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../../structures/IPerformance"; * @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 type Output = Primitive; @@ -32,7 +41,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..e594d15e4 100644 --- a/test/features/app-globalPrefix-versionUri-routerModule/swagger.json +++ b/test/features/app-globalPrefix-versionUri-routerModule/swagger.json @@ -1 +1,1672 @@ -{"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.20240814", + "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/nestia.config.ts b/test/features/app-globalPrefix-versionUri/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-globalPrefix-versionUri/nestia.config.ts +++ b/test/features/app-globalPrefix-versionUri/nestia.config.ts @@ -8,6 +8,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-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..8b9dd919d 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 @@ -20,11 +20,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -38,7 +47,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -88,7 +97,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 +111,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..920946613 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,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..1199f2b39 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../../../structures/IPerformance"; * @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 type Output = Primitive; @@ -32,7 +41,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..c75fc0530 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -91,7 +100,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 +114,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..5087b2ed8 100644 --- a/test/features/app-globalPrefix-versionUri/swagger.json +++ b/test/features/app-globalPrefix-versionUri/swagger.json @@ -1 +1,692 @@ -{"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.20240814", + "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/nestia.config.ts b/test/features/app-globalPrefix/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-globalPrefix/nestia.config.ts +++ b/test/features/app-globalPrefix/nestia.config.ts @@ -8,6 +8,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-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 6490e85c1..d70784634 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..c49b0a220 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 @@ -23,11 +23,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -41,7 +50,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -68,11 +77,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +103,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -125,7 +143,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 +157,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -181,7 +199,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 +213,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/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..9d795dcc4 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../structures/IPerformance"; * @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 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/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-globalPrefix/swagger.json b/test/features/app-globalPrefix/swagger.json index e219348f3..35d362b34 100644 --- a/test/features/app-globalPrefix/swagger.json +++ b/test/features/app-globalPrefix/swagger.json @@ -1 +1,846 @@ -{"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.20240814", + "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-routerModule/nestia.config.ts b/test/features/app-routerModule/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-routerModule/nestia.config.ts +++ b/test/features/app-routerModule/nestia.config.ts @@ -8,6 +8,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-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..d45592bc3 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..606975ac6 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -123,7 +141,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 +155,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -179,7 +197,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 +211,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..8cf10f2e5 100644 --- a/test/features/app-routerModule/src/api/functional/performance/index.ts +++ b/test/features/app-routerModule/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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..54b6253fd 100644 --- a/test/features/app-routerModule/swagger.json +++ b/test/features/app-routerModule/swagger.json @@ -1 +1,846 @@ -{"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.20240814", + "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/nestia.config.ts b/test/features/app-versionHeader/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-versionHeader/nestia.config.ts +++ b/test/features/app-versionHeader/nestia.config.ts @@ -8,6 +8,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-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..47655b6fa 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 @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -40,7 +49,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -72,11 +81,20 @@ export async function at( 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), - }); + 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 +107,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -128,7 +146,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 +160,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => @@ -178,7 +196,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 +210,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..2d372d275 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 @@ -23,11 +23,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -41,7 +50,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -68,11 +77,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +103,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -125,7 +143,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 +157,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -181,7 +199,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 +213,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..8cf10f2e5 100644 --- a/test/features/app-versionHeader/src/api/functional/performance/index.ts +++ b/test/features/app-versionHeader/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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..bb98df776 100644 --- a/test/features/app-versionHeader/swagger.json +++ b/test/features/app-versionHeader/swagger.json @@ -1 +1,846 @@ -{"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.20240814", + "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/nestia.config.ts b/test/features/app-versionUri/nestia.config.ts index 290c80821..44f098bea 100644 --- a/test/features/app-versionUri/nestia.config.ts +++ b/test/features/app-versionUri/nestia.config.ts @@ -8,6 +8,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-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..9254ef408 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 @@ -20,11 +20,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -38,7 +47,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -88,7 +97,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 +111,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..030d27d77 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +75,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +101,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..2bb959f1b 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../../structures/IPerformance"; * @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 type Output = Primitive; @@ -32,7 +41,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..58ccc70d1 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 @@ -21,11 +21,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +48,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -91,7 +100,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 +114,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..d8f94d8fc 100644 --- a/test/features/app-versionUri/swagger.json +++ b/test/features/app-versionUri/swagger.json @@ -1 +1,692 @@ -{"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.20240814", + "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/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/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/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/articles/comments/index.ts b/test/features/app/src/api/functional/bbs/articles/comments/index.ts similarity index 67% rename from test/features/app/src/api/functional/articles/comments/index.ts rename to test/features/app/src/api/functional/bbs/articles/comments/index.ts index 1fc0a9721..47655b6fa 100644 --- a/test/features/app/src/api/functional/articles/comments/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/comments/index.ts @@ -1,6 +1,6 @@ /** * @packageDocumentation - * @module api.functional.articles.comments + * @module api.functional.bbs.articles.comments * @nestia Generated by Nestia - https://github.com/samchon/nestia */ //================================================================ @@ -8,12 +8,12 @@ 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"; +import type { IBbsComment } from "../../../../structures/IBbsComment"; +import type { IPage } from "../../../../structures/IPage"; /** * @controller BbsCommentsController.index - * @path GET /:section/articles/:articleId/comments + * @path GET /bbs/:section/articles/:articleId/comments * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function index( @@ -22,11 +22,20 @@ export async function index( articleId: string & Format<"uuid">, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, articleId, query), - }); + 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; @@ -34,13 +43,13 @@ export namespace index { export const METADATA = { method: "GET", - path: "/:section/articles/:articleId/comments", + path: "/bbs/:section/articles/:articleId/comments", request: null, response: { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -54,7 +63,7 @@ export namespace index { 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`; + const location: string = `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; return 0 === variables.size ? location : `${location}?${variables.toString()}`; @@ -63,7 +72,7 @@ export namespace index { /** * @controller BbsCommentsController.at - * @path GET /:section/articles/:articleId/comments/:id + * @path GET /bbs/:section/articles/:articleId/comments/:id * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function at( @@ -72,24 +81,33 @@ export async function at( 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), - }); + 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: "/:section/articles/:articleId/comments/:id", + path: "/bbs/:section/articles/:articleId/comments/:id", request: null, response: { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -97,12 +115,12 @@ export namespace at { articleId: string & Format<"uuid">, id: string & Format<"uuid">, ) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; } /** * @controller BbsCommentsController.store - * @path POST /:section/articles/:articleId/comments + * @path POST /bbs/:section/articles/:articleId/comments * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function store( @@ -128,12 +146,12 @@ export async function store( ); } export namespace store { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { method: "POST", - path: "/:section/articles/:articleId/comments", + path: "/bbs/:section/articles/:articleId/comments", request: { type: "application/json", encrypted: false, @@ -142,16 +160,16 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string, articleId: string & Format<"uuid">) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; + `/bbs/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments`; } /** * @controller BbsCommentsController.update - * @path PUT /:section/articles/:articleId/comments/:id + * @path PUT /bbs/:section/articles/:articleId/comments/:id * @nestia Generated by Nestia - https://github.com/samchon/nestia */ export async function update( @@ -178,12 +196,12 @@ export async function update( ); } export namespace update { - export type Input = Primitive; + export type Input = Resolved; export type Output = Primitive; export const METADATA = { method: "PUT", - path: "/:section/articles/:articleId/comments/:id", + path: "/bbs/:section/articles/:articleId/comments/:id", request: { type: "application/json", encrypted: false, @@ -192,7 +210,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = ( @@ -200,5 +218,5 @@ export namespace update { articleId: string & Format<"uuid">, id: string & Format<"uuid">, ) => - `/${encodeURIComponent(section ?? "null")}/articles/${encodeURIComponent(articleId ?? "null")}/comments/${encodeURIComponent(id ?? "null")}`; + `/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 index 5efa486fc..2d372d275 100644 --- a/test/features/app/src/api/functional/bbs/articles/index.ts +++ b/test/features/app/src/api/functional/bbs/articles/index.ts @@ -11,6 +11,8 @@ 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 @@ -21,11 +23,20 @@ export async function index( section: string, query: index.Query, ): Promise { - return PlainFetcher.fetch(connection, { - ...index.METADATA, - template: index.METADATA.path, - path: index.path(section, query), - }); + 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; @@ -39,7 +50,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -66,11 +77,20 @@ export async function at( section: string, id: string & Format<"uuid">, ): Promise { - return PlainFetcher.fetch(connection, { - ...at.METADATA, - template: at.METADATA.path, - path: at.path(section, id), - }); + 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 +103,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -123,7 +143,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 +157,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -179,7 +199,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 +213,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/src/api/functional/health/index.ts b/test/features/app/src/api/functional/health/index.ts index d5a9f9925..0218f7f3a 100644 --- a/test/features/app/src/api/functional/health/index.ts +++ b/test/features/app/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/src/api/functional/index.ts b/test/features/app/src/api/functional/index.ts index a11a25ddb..acdfad8d4 100644 --- a/test/features/app/src/api/functional/index.ts +++ b/test/features/app/src/api/functional/index.ts @@ -7,4 +7,3 @@ 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 index 2e71dbb40..8cf10f2e5 100644 --- a/test/features/app/src/api/functional/performance/index.ts +++ b/test/features/app/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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/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/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts b/test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts similarity index 82% rename from test/features/app/src/test/features/api/automated/test_api_articles_comments_at.ts rename to test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_at.ts index c315c2a7d..4b173be66 100644 --- 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_bbs_articles_comments_at.ts @@ -5,11 +5,11 @@ 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 ( +export const test_api_bbs_articles_comments_at = async ( connection: api.IConnection, ) => { const output: Primitive = - await api.functional.articles.comments.at( + await api.functional.bbs.articles.comments.at( connection, typia.random(), typia.random>(), 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_bbs_articles_comments_index.ts similarity index 83% rename from test/features/app/src/test/features/api/automated/test_api_articles_comments_index.ts rename to test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_index.ts index f86166b32..c6fc4fa69 100644 --- 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_bbs_articles_comments_index.ts @@ -6,11 +6,11 @@ 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 ( +export const test_api_bbs_articles_comments_index = async ( connection: api.IConnection, ) => { const output: Primitive> = - await api.functional.articles.comments.index( + await api.functional.bbs.articles.comments.index( connection, typia.random(), typia.random>(), 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_bbs_articles_comments_store.ts similarity index 81% rename from test/features/app/src/test/features/api/automated/test_api_articles_comments_store.ts rename to test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_store.ts index f8e5d9242..54fcb409f 100644 --- 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_bbs_articles_comments_store.ts @@ -5,11 +5,11 @@ 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 ( +export const test_api_bbs_articles_comments_store = async ( connection: api.IConnection, ) => { const output: Primitive = - await api.functional.articles.comments.store( + await api.functional.bbs.articles.comments.store( connection, typia.random(), typia.random>(), 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_bbs_articles_comments_update.ts similarity index 82% rename from test/features/app/src/test/features/api/automated/test_api_articles_comments_update.ts rename to test/features/app/src/test/features/api/automated/test_api_bbs_articles_comments_update.ts index 3b8c24e13..3c23d742b 100644 --- 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_bbs_articles_comments_update.ts @@ -5,11 +5,11 @@ 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 ( +export const test_api_bbs_articles_comments_update = async ( connection: api.IConnection, ) => { const output: Primitive = - await api.functional.articles.comments.update( + await api.functional.bbs.articles.comments.update( connection, typia.random(), typia.random>(), diff --git a/test/features/app/swagger.json b/test/features/app/swagger.json index ac9c7b00c..bb98df776 100644 --- a/test/features/app/swagger.json +++ b/test/features/app/swagger.json @@ -1 +1,846 @@ -{"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.11.0-dev.20240814", + "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/tsconfig.json b/test/features/app/tsconfig.json index c33dfa28f..3c815da8b 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. */ 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..fb266a417 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, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -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..e430ab63c 100644 --- a/test/features/beautify-4/src/api/functional/performance/index.ts +++ b/test/features/beautify-4/src/api/functional/performance/index.ts @@ -24,11 +24,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 type Output = Primitive; @@ -41,7 +50,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..585af29e0 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.20240814", "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..fb266a417 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, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -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..e430ab63c 100644 --- a/test/features/beautify-false/src/api/functional/performance/index.ts +++ b/test/features/beautify-false/src/api/functional/performance/index.ts @@ -24,11 +24,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 type Output = Primitive; @@ -41,7 +50,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..2d17e740f 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.20240814","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..fb266a417 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, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -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..e430ab63c 100644 --- a/test/features/beautify/src/api/functional/performance/index.ts +++ b/test/features/beautify/src/api/functional/performance/index.ts @@ -24,11 +24,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 type Output = Primitive; @@ -41,7 +50,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..422fdc611 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.20240814", "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 index cbdd94d58..13165902c 100644 --- a/test/features/body-config-assert/src/api/functional/body/index.ts +++ b/test/features/body-config-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"; @@ -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-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/performance/index.ts b/test/features/body-config-assert/src/api/functional/performance/index.ts index 2e71dbb40..8cf10f2e5 100644 --- a/test/features/body-config-assert/src/api/functional/performance/index.ts +++ b/test/features/body-config-assert/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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-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/swagger.json b/test/features/body-config-assert/swagger.json index 3a0eaff86..6aab6e5d8 100644 --- a/test/features/body-config-assert/swagger.json +++ b/test/features/body-config-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.20240814","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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-assertClone/swagger.json b/test/features/body-config-assertClone/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-assertEquals/swagger.json b/test/features/body-config-assertEquals/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-assertPrune/swagger.json b/test/features/body-config-assertPrune/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-equals/swagger.json b/test/features/body-config-equals/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-is/swagger.json b/test/features/body-config-is/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-validate/swagger.json b/test/features/body-config-validate/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-validateClone/swagger.json b/test/features/body-config-validateClone/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-validateEquals/swagger.json b/test/features/body-config-validateEquals/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-config-validatePrune/swagger.json b/test/features/body-config-validatePrune/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 db930f490..c837d33d6 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"; @@ -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..b5d6d8deb 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"; @@ -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/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-generic-default/swagger.json b/test/features/body-generic-default/swagger.json index 2c9d0313e..2297d3821 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-assert/swagger.json b/test/features/body-manual-assert/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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-is/swagger.json b/test/features/body-manual-is/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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/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 cbdd94d58..13165902c 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"; @@ -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..8cf10f2e5 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 @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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/body-manual-validate/swagger.json b/test/features/body-manual-validate/swagger.json index 3a0eaff86..6aab6e5d8 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.20240814","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..fb266a417 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, Resolved, Primitive } from "@nestia/fetcher"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import type { Format } from "typia/lib/tags/Format"; @@ -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..e430ab63c 100644 --- a/test/features/body/src/api/functional/performance/index.ts +++ b/test/features/body/src/api/functional/performance/index.ts @@ -24,11 +24,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 type Output = Primitive; @@ -41,7 +50,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..2d17e740f 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.20240814","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/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 ab651405e..83233ca27 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"; @@ -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..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"; @@ -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,13 +48,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): 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 df538a4d6..7918627b1 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 @@ -27,11 +27,20 @@ import type { IPerformance } from "../../structures/IPerformance"; 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/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-project/swagger.json b/test/features/cli-config-project/swagger.json index ef022e1b5..4380322f5 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.20240814","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/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 ab651405e..83233ca27 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"; @@ -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..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"; @@ -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,13 +48,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): 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 df538a4d6..7918627b1 100644 --- a/test/features/cli-config/src/api/functional/performance/index.ts +++ b/test/features/cli-config/src/api/functional/performance/index.ts @@ -27,11 +27,20 @@ import type { IPerformance } from "../../structures/IPerformance"; 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/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-config/swagger.json b/test/features/cli-config/swagger.json index ef022e1b5..4380322f5 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.20240814","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/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/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..8cf10f2e5 100644 --- a/test/features/cli-project/src/api/functional/performance/index.ts +++ b/test/features/cli-project/src/api/functional/performance/index.ts @@ -15,11 +15,20 @@ import type { IPerformance } from "../../structures/IPerformance"; * @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 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/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/cli-project/swagger.json b/test/features/cli-project/swagger.json index 6dd2589df..0ac808da8 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.20240814","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/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/functional/health/index.ts b/test/features/clone-and-exact-optional-property/src/api/functional/health/index.ts index 747b90307..ec15b5983 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 @@ -16,16 +16,28 @@ import typia from "typia"; export async function get(connection: IConnection): Promise { 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: void; + }, + 200 + >; export const METADATA = { method: "GET", @@ -35,13 +47,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 +65,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..478b1a005 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 @@ -23,16 +23,28 @@ export async function original( ): Promise { 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,7 +54,7 @@ export namespace original { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/partial-dto-test/original"; @@ -61,6 +73,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..1d37b53d5 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 @@ -28,7 +28,7 @@ export async function partialInterface( ): Promise { return !!connection.simulate ? partialInterface.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -46,9 +46,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,7 +64,7 @@ export namespace partialInterface { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/partial-dto-test/partial-interface"; @@ -100,6 +103,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..d98081d7a 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 @@ -14,8 +14,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 @@ -28,7 +28,7 @@ export async function partialType( ): Promise { return !!connection.simulate ? partialType.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -46,10 +46,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,14 +65,14 @@ 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( + ): Resolved => + typia.random( g, ); export const simulate = ( @@ -104,6 +107,6 @@ export namespace partialType { ? connection.simulate : undefined, ), - }; + } as Output; }; } 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/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..d1d919465 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: void; + }, + 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..28b6a8c86 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.20240814","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..e3d3a4260 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 @@ -24,16 +24,28 @@ import type { ICategory } from "../../structures/ICategory"; export async function index(connection: IConnection): Promise { 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,7 +55,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/arrayRecursive"; @@ -62,7 +74,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -77,16 +89,28 @@ export async function at( ): Promise { 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,7 +120,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => @@ -133,7 +157,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -148,7 +172,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -166,9 +190,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,7 +208,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arrayRecursive"; @@ -220,6 +247,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..de0649561 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 @@ -14,8 +14,11 @@ 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 @@ -25,16 +28,28 @@ import type { IBucket } from "../../structures/IBucket"; export async function index(connection: IConnection): Promise { 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 +59,16 @@ 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<(IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]> => + typia.random< + (IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[] + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -64,7 +81,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -79,16 +96,28 @@ export async function at( ): Promise { 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 +127,15 @@ 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 => + typia.random(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -135,7 +165,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -150,7 +180,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -167,10 +197,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 +221,14 @@ 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 => + typia.random(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -222,6 +261,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..99c4b2c34 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 @@ -14,8 +14,12 @@ 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 @@ -25,16 +29,35 @@ import type { IBucket } from "../../structures/IBucket"; export async function index(connection: IConnection): Promise { 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.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -44,14 +67,32 @@ 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< + ( + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[] + > => + typia.random< + ( + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[] + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -64,7 +105,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -79,16 +120,34 @@ export async function at( ): Promise { 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.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; + }, + 200 + >; export const METADATA = { method: "GET", @@ -98,14 +157,29 @@ 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< + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + > => + typia.random< + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + >(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -135,7 +209,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -150,7 +224,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -167,10 +241,25 @@ export async function store( ); } export namespace store { - export type Input = IBucket.o1; - export type Output = IPropagation<{ - 201: IBucket.o1; - }>; + export type Input = + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; + export type Output = IPropagation< + { + 201: + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; + }, + 201 + >; export const METADATA = { method: "POST", @@ -183,13 +272,28 @@ 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< + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + > => + typia.random< + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + >(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -222,6 +326,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..3bb81318a 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 @@ -15,7 +15,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"; /** @@ -26,16 +25,28 @@ import type { IPerson } from "../../structures/IPerson"; export async function index(connection: IConnection): Promise { 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 +56,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 +75,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -79,16 +90,28 @@ export async function at( ): Promise { 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,7 +121,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: string & Format<"uuid">) => @@ -138,7 +161,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -153,7 +176,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -171,9 +194,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,7 +212,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/arraySimple"; @@ -225,6 +251,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..b2d89fc76 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,8 @@ //================================================================ import type { IConnection, - Resolved, IPropagation, + Resolved, HttpError, } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; @@ -37,17 +37,29 @@ export async function index( ): Promise { 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 Query = IPage.IRequest; + export type Output = IPropagation< + { + 200: IPageIBbsArticle.ISummary; + }, + 200 + >; export const METADATA = { method: "GET", @@ -57,7 +69,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, query: index.Query) => { @@ -110,7 +122,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -132,7 +144,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, section, input) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -150,9 +162,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,7 +180,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (section: string) => @@ -207,7 +222,7 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -231,7 +246,7 @@ export async function update( ): Promise { return !!connection.simulate ? update.simulate(connection, section, id, input) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -249,9 +264,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,7 +282,7 @@ export namespace update { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (section: string, id: string & Format<"uuid">) => @@ -308,6 +326,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..ec15b5983 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 @@ -16,16 +16,28 @@ import typia from "typia"; export async function get(connection: IConnection): Promise { 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: void; + }, + 200 + >; export const METADATA = { method: "GET", @@ -35,13 +47,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 +65,6 @@ export namespace get { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/index.ts b/test/features/clone-and-propagate/src/api/functional/index.ts index 2b701f155..c58844925 100644 --- a/test/features/clone-and-propagate/src/api/functional/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/index.ts @@ -19,5 +19,4 @@ export * as performance from "./performance"; export * as sellers from "./sellers"; export * as template from "./template"; export * as tupleHierarchicalController from "./tupleHierarchicalController"; -export * as tupleRestController from "./tupleRestController"; export * as users from "./users"; 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..dae3e6a0d 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 @@ -27,7 +27,7 @@ export async function post( ): Promise { return !!connection.simulate ? post.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( connection, { ...post.METADATA, @@ -39,9 +39,12 @@ export async function post( } export namespace post { export type Input = IMultipart; - export type Output = IPropagation<{ - 201: undefined; - }>; + export type Output = IPropagation< + { + 201: void; + }, + 201 + >; export const METADATA = { method: "POST", @@ -54,13 +57,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 +95,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..d58839260 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 @@ -20,16 +20,28 @@ export * as literal from "./literal"; export async function index(connection: IConnection): Promise { 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,7 +51,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectLiteral"; @@ -58,6 +70,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..1a6d31c63 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 @@ -20,24 +20,36 @@ export async function literals( ): Promise { 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,7 +59,7 @@ export namespace literals { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectLiteral/literal"; @@ -87,6 +99,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..84acad219 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 @@ -24,16 +24,28 @@ import type { IBox3D } from "../../structures/IBox3D"; export async function index(connection: IConnection): Promise { 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,7 +55,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/objectSimple"; @@ -62,7 +74,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -77,16 +89,28 @@ export async function at( ): Promise { 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,7 +120,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => @@ -133,7 +157,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -148,7 +172,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -166,9 +190,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,7 +208,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/objectSimple"; @@ -220,6 +247,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..2db32498b 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 @@ -8,7 +8,13 @@ import type { IConnection, IPropagation, 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 @@ -18,16 +24,36 @@ import type { ObjectUnionExplicit } from "../../structures/ObjectUnionExplicit"; export async function get(connection: IConnection): Promise { 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 +63,34 @@ 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< + ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[] + > => + typia.random< + ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[] + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -56,6 +103,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..6d9a16b6f 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 @@ -8,7 +8,13 @@ import type { IConnection, IPropagation, 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 @@ -18,16 +24,36 @@ import type { ObjectUnionImplicit } from "../../structures/ObjectUnionImplicit"; export async function get(connection: IConnection): Promise { 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.o1 + | ILine + | ITriangle + | IRectangle + | IPolyline.o1 + | IPolygon + | ICircle + )[]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -37,13 +63,34 @@ 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< + ( + | IPoint.o1 + | ILine + | ITriangle + | IRectangle + | IPolyline.o1 + | IPolygon + | ICircle + )[] + > => + typia.random< + ( + | IPoint.o1 + | ILine + | ITriangle + | IRectangle + | IPolyline.o1 + | IPolygon + | ICircle + )[] + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -56,6 +103,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..50c418e57 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 @@ -18,16 +18,28 @@ import type { process } from "../../structures/process"; export async function cpu(connection: IConnection): Promise { 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,7 +49,7 @@ export namespace cpu { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/cpu"; @@ -57,7 +69,7 @@ export namespace cpu { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -69,16 +81,28 @@ export namespace cpu { export async function memory(connection: IConnection): Promise { 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,7 +112,7 @@ export namespace memory { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/memory"; @@ -108,7 +132,7 @@ export namespace memory { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -122,16 +146,28 @@ export async function resource( ): Promise { 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,7 +177,7 @@ export namespace resource { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/performance/resource"; @@ -161,6 +197,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..1c58afafb 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 @@ -34,9 +34,14 @@ export async function join( connection: IConnection, input: join.Input, ): Promise { - const output: join.Output = !!connection.simulate + const output: IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > = !!connection.simulate ? join.simulate(connection, input) - : await EncryptedFetcher.propagate( + : await EncryptedFetcher.propagate( { ...connection, headers: { @@ -59,9 +64,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,7 +82,7 @@ export namespace join { type: "text/plain", encrypted: true, }, - status: null, + status: 201, } as const; export const path = () => "/sellers/authenticate/join"; @@ -113,7 +121,7 @@ export namespace join { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -132,9 +140,14 @@ export async function login( connection: IConnection, input: login.Input, ): Promise { - const output: login.Output = !!connection.simulate + const output: IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > = !!connection.simulate ? login.simulate(connection, input) - : await EncryptedFetcher.propagate( + : await EncryptedFetcher.propagate( { ...connection, headers: { @@ -157,9 +170,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,7 +188,7 @@ export namespace login { type: "text/plain", encrypted: true, }, - status: null, + status: 201, } as const; export const path = () => "/sellers/authenticate/login"; @@ -211,7 +227,7 @@ export namespace login { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -224,16 +240,28 @@ export namespace login { export async function exit(connection: IConnection): Promise { 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: void; + }, + 200 + >; export const METADATA = { method: "DELETE", @@ -243,13 +271,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 +289,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..dd7bd709a 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 @@ -32,7 +32,7 @@ export async function change( ): Promise { return !!connection.simulate ? change.simulate(connection, input) - : EncryptedFetcher.propagate( + : EncryptedFetcher.propagate( { ...connection, headers: { @@ -50,9 +50,12 @@ export async function change( } export namespace change { export type Input = ISeller.IChangePassword; - export type Output = IPropagation<{ - 200: undefined; - }>; + export type Output = IPropagation< + { + 200: void; + }, + 200 + >; export const METADATA = { method: "PATCH", @@ -65,13 +68,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 +106,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..f73a7c5db 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 @@ -24,16 +24,28 @@ import type { Template } from "../../structures/Template"; export async function index(connection: IConnection): Promise { 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,7 +55,7 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/template"; @@ -62,7 +74,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -77,16 +89,28 @@ export async function at( ): Promise { 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,7 +120,7 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => @@ -133,7 +157,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -148,7 +172,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -166,9 +190,12 @@ export async function store( } export namespace store { export type Input = Template; - export type Output = IPropagation<{ - 201: Template; - }>; + export type Output = IPropagation< + { + 201: Template; + }, + 201 + >; export const METADATA = { method: "POST", @@ -181,7 +208,7 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/template"; @@ -220,6 +247,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/tupleHierarchicalController/index.ts b/test/features/clone-and-propagate/src/api/functional/tupleHierarchicalController/index.ts index 6f30856b1..d340a4650 100644 --- a/test/features/clone-and-propagate/src/api/functional/tupleHierarchicalController/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/tupleHierarchicalController/index.ts @@ -14,8 +14,6 @@ import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; -import type { TupleHierarchical } from "../../structures/TupleHierarchical"; - /** * @controller TupleHierarchicalController.index * @path GET /tupleHierarchicalController @@ -24,16 +22,34 @@ import type { TupleHierarchical } from "../../structures/TupleHierarchical"; export async function index(connection: IConnection): Promise { 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: TupleHierarchical[]; - }>; + export type Output = IPropagation< + { + 200: [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ][]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -43,13 +59,30 @@ export namespace index { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = () => "/tupleHierarchicalController"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ][] + > => + typia.random< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ][] + >(g); export const simulate = (connection: IConnection): Output => { return { success: true, @@ -62,7 +95,7 @@ export namespace index { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -77,16 +110,34 @@ export async function at( ): Promise { 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: TupleHierarchical; - }>; + export type Output = IPropagation< + { + 200: [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ]; + }, + 200 + >; export const METADATA = { method: "GET", @@ -96,14 +147,31 @@ export namespace at { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (id: number) => `/tupleHierarchicalController/${encodeURIComponent(id ?? "null")}`; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ] + > => + typia.random< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ] + >(g); export const simulate = (connection: IConnection, id: number): Output => { const assert = NestiaSimulator.assert({ method: METADATA.method, @@ -133,7 +201,7 @@ export namespace at { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -148,7 +216,7 @@ export async function store( ): Promise { return !!connection.simulate ? store.simulate(connection, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -165,10 +233,25 @@ export async function store( ); } export namespace store { - export type Input = TupleHierarchical; - export type Output = IPropagation<{ - 201: TupleHierarchical; - }>; + export type Input = [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ]; + export type Output = IPropagation< + { + 201: [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ]; + }, + 201 + >; export const METADATA = { method: "POST", @@ -181,13 +264,30 @@ export namespace store { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = () => "/tupleHierarchicalController"; export const random = ( g?: Partial, - ): Resolved => typia.random(g); + ): Resolved< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ] + > => + typia.random< + [ + boolean, + null, + number, + [boolean, null, [number, [boolean, string]]], + [number, [string, boolean, [number, number, [boolean, string]][]][]], + ] + >(g); export const simulate = ( connection: IConnection, body: store.Input, @@ -220,6 +320,6 @@ export namespace store { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/tupleRestController/index.ts b/test/features/clone-and-propagate/src/api/functional/tupleRestController/index.ts deleted file mode 100644 index 5d22c0e71..000000000 --- a/test/features/clone-and-propagate/src/api/functional/tupleRestController/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @packageDocumentation - * @module api.functional.tupleRestController - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -//================================================================ -import type { IConnection, IPropagation, Resolved } from "@nestia/fetcher"; -import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; -import typia from "typia"; - -import type { TupleRest } from "../../structures/TupleRest"; - -/** - * @controller TupleRestController.get - * @path GET /tupleRestController - * @nestia Generated by Nestia - https://github.com/samchon/nestia - */ -export async function get(connection: IConnection): Promise { - return !!connection.simulate - ? get.simulate(connection) - : PlainFetcher.propagate(connection, { - ...get.METADATA, - template: get.METADATA.path, - path: get.path(), - }); -} -export namespace get { - export type Output = IPropagation<{ - 200: TupleRest; - }>; - - export const METADATA = { - method: "GET", - path: "/tupleRestController", - request: null, - response: { - type: "application/json", - encrypted: false, - }, - status: null, - } as const; - - export const path = () => "/tupleRestController"; - export const random = ( - g?: Partial, - ): Resolved => typia.random(g); - export const simulate = (connection: IConnection): Output => { - return { - success: true, - status: 200, - headers: { - "Content-Type": "application/json", - }, - data: random( - "object" === typeof connection.simulate && null !== connection.simulate - ? connection.simulate - : undefined, - ), - }; - }; -} diff --git a/test/features/clone-and-propagate/src/api/functional/users/oauth/index.ts b/test/features/clone-and-propagate/src/api/functional/users/oauth/index.ts index 5dff2a9e1..e9bce6aa0 100644 --- a/test/features/clone-and-propagate/src/api/functional/users/oauth/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/users/oauth/index.ts @@ -6,8 +6,8 @@ //================================================================ import type { IConnection, - Resolved, IPropagation, + Resolved, HttpError, } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; @@ -33,18 +33,30 @@ export async function getOauthProfile( ): Promise { return !!connection.simulate ? getOauthProfile.simulate(connection, user_id, query) - : PlainFetcher.propagate(connection, { - ...getOauthProfile.METADATA, - template: getOauthProfile.METADATA.path, - path: getOauthProfile.path(user_id, query), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...getOauthProfile.METADATA, + template: getOauthProfile.METADATA.path, + path: getOauthProfile.path(user_id, query), + }, + ); } export namespace getOauthProfile { - export type Query = Resolved; - export type Output = IPropagation<{ - 200: IAuthentication.IProfile; - 404: "404 Not Found"; - }>; + export type Query = IAuthentication; + export type Output = IPropagation< + { + 200: IAuthentication.IProfile; + 404: "404 Not Found"; + }, + 200 + >; export const METADATA = { method: "GET", @@ -54,7 +66,7 @@ export namespace getOauthProfile { type: "application/json", encrypted: false, }, - status: null, + status: 200, } as const; export const path = (user_id: string, query: getOauthProfile.Query) => { @@ -107,6 +119,6 @@ export namespace getOauthProfile { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/functional/users/user/index.ts b/test/features/clone-and-propagate/src/api/functional/users/user/index.ts index 92afc8d63..d95fa6567 100644 --- a/test/features/clone-and-propagate/src/api/functional/users/user/index.ts +++ b/test/features/clone-and-propagate/src/api/functional/users/user/index.ts @@ -6,8 +6,8 @@ //================================================================ import type { IConnection, - Resolved, IPropagation, + Resolved, HttpError, } from "@nestia/fetcher"; import { NestiaSimulator } from "@nestia/fetcher/lib/NestiaSimulator"; @@ -15,7 +15,7 @@ import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import typia from "typia"; import type { IUser } from "../../../structures/IUser"; -import type { PartialPickIUseremailnamenullable_attroptional_attr } from "../../../structures/PartialPickIUseremailnamenullable_attroptional_attr"; +import type { PartialPickIUsernameemailoptional_attrnullable_attr } from "../../../structures/PartialPickIUsernameemailoptional_attrnullable_attr"; /** * - When namespaced DTO type comes, `@nestia/sdk` had taken a mistake that writing only the deepest type even in the top or middle level namespaced types. @@ -37,14 +37,23 @@ export async function getUserProfile( ): Promise { return !!connection.simulate ? getUserProfile.simulate(connection, user_id, query) - : PlainFetcher.propagate(connection, { - ...getUserProfile.METADATA, - template: getUserProfile.METADATA.path, - path: getUserProfile.path(user_id, query), - }); + : PlainFetcher.propagate( + { + ...connection, + headers: { + ...connection.headers, + "Content-Type": "application/json", + }, + }, + { + ...getUserProfile.METADATA, + template: getUserProfile.METADATA.path, + path: getUserProfile.path(user_id, query), + }, + ); } export namespace getUserProfile { - export type Query = Resolved; + export type Query = IUser.ISearch; export type Output = IPropagation< { 202: IUser; @@ -113,7 +122,7 @@ export namespace getUserProfile { ? connection.simulate : undefined, ), - }; + } as Output; }; } @@ -130,7 +139,7 @@ export async function updateUserProfile( ): Promise { return !!connection.simulate ? updateUserProfile.simulate(connection, user_id, body) - : PlainFetcher.propagate( + : PlainFetcher.propagate( { ...connection, headers: { @@ -147,10 +156,13 @@ export async function updateUserProfile( ); } export namespace updateUserProfile { - export type Input = PartialPickIUseremailnamenullable_attroptional_attr; - export type Output = IPropagation<{ - 201: IUser; - }>; + export type Input = PartialPickIUsernameemailoptional_attrnullable_attr; + export type Output = IPropagation< + { + 201: IUser; + }, + 201 + >; export const METADATA = { method: "POST", @@ -163,7 +175,7 @@ export namespace updateUserProfile { type: "application/json", encrypted: false, }, - status: null, + status: 201, } as const; export const path = (user_id: string) => @@ -205,6 +217,6 @@ export namespace updateUserProfile { ? connection.simulate : undefined, ), - }; + } as Output; }; } diff --git a/test/features/clone-and-propagate/src/api/module.ts b/test/features/clone-and-propagate/src/api/module.ts index dbb6e9a51..2137b4473 100644 --- a/test/features/clone-and-propagate/src/api/module.ts +++ b/test/features/clone-and-propagate/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-propagate/src/api/structures/ArrayRecursiveUnionExplicit.ts b/test/features/clone-and-propagate/src/api/structures/ArrayRecursiveUnionExplicit.ts deleted file mode 100644 index d690a73c4..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ArrayRecursiveUnionExplicit.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { IBucket } from "./IBucket"; - -export type ArrayRecursiveUnionExplicit = IBucket[]; diff --git a/test/features/clone-and-propagate/src/api/structures/ArrayRecursiveUnionImplicit.ts b/test/features/clone-and-propagate/src/api/structures/ArrayRecursiveUnionImplicit.ts deleted file mode 100644 index ae77a4a2a..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ArrayRecursiveUnionImplicit.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { IBucket } from "./IBucket"; - -export type ArrayRecursiveUnionImplicit = IBucket.o1[]; diff --git a/test/features/clone-and-propagate/src/api/structures/ArraySimple.ts b/test/features/clone-and-propagate/src/api/structures/ArraySimple.ts deleted file mode 100644 index fb9f78259..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ArraySimple.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { IPerson } from "./IPerson"; - -export type ArraySimple = IPerson[]; diff --git a/test/features/clone-and-propagate/src/api/structures/IArrayIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IArrayIIdentified.ts deleted file mode 100644 index 50e3dc3a7..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IArrayIIdentified.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Type } from "typia/lib/tags/Type"; - -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IJsonSchema } from "./IJsonSchema"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; - -export type IArrayIIdentified = { - items: IJsonSchema; - minItems?: undefined | (number & Type<"uint32">); - maxItems?: undefined | (number & Type<"uint32">); - "x-typia-tuple"?: undefined | IJsonSchema.ITuple; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - type: "array"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IAuthentication.ts b/test/features/clone-and-propagate/src/api/structures/IAuthentication.ts index 67f4f658b..8053105f2 100644 --- a/test/features/clone-and-propagate/src/api/structures/IAuthentication.ts +++ b/test/features/clone-and-propagate/src/api/structures/IAuthentication.ts @@ -2,14 +2,13 @@ import type { Format } from "typia/lib/tags/Format"; export type IAuthentication = { user_id: string; - oauth_type: IAuthentication.OauthType; + oauth_type: "google" | "github" | "kakao"; }; export namespace IAuthentication { - export type OauthType = "google" | "github" | "kakao"; export type IProfile = { id: string; name: string; email: null | (string & Format<"email">); - oauth_type: IAuthentication.OauthType; + oauth_type: "google" | "github" | "kakao"; }; } diff --git a/test/features/clone-and-propagate/src/api/structures/IBooleanIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IBooleanIIdentified.ts deleted file mode 100644 index e442f282b..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IBooleanIIdentified.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; - -export type IBooleanIIdentified = { - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | boolean; - type: "boolean"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IBucket.ts b/test/features/clone-and-propagate/src/api/structures/IBucket.ts deleted file mode 100644 index 14e965ad1..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IBucket.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { IDirectory } from "./IDirectory"; -import type { IImageFile } from "./IImageFile"; -import type { ISharedDirectory } from "./ISharedDirectory"; -import type { IShortcut } from "./IShortcut"; -import type { ITextFile } from "./ITextFile"; -import type { IZipFile } from "./IZipFile"; - -export type IBucket = - | IDirectory - | IImageFile - | ITextFile - | IZipFile - | IShortcut; -export namespace IBucket { - export type o1 = - | IDirectory.o1 - | ISharedDirectory - | IImageFile.o1 - | ITextFile.o1 - | IZipFile.o1 - | IShortcut.o1; -} diff --git a/test/features/clone-and-propagate/src/api/structures/IDirectory.ts b/test/features/clone-and-propagate/src/api/structures/IDirectory.ts index 2905a211f..1499453f4 100644 --- a/test/features/clone-and-propagate/src/api/structures/IDirectory.ts +++ b/test/features/clone-and-propagate/src/api/structures/IDirectory.ts @@ -1,10 +1,14 @@ -import type { IBucket } from "./IBucket"; +import type { IImageFile } from "./IImageFile"; +import type { ISharedDirectory } from "./ISharedDirectory"; +import type { IShortcut } from "./IShortcut"; +import type { ITextFile } from "./ITextFile"; +import type { IZipFile } from "./IZipFile"; export type IDirectory = { id: number; name: string; path: string; - children: IBucket[]; + children: (IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]; type: "directory"; }; export namespace IDirectory { @@ -12,6 +16,13 @@ export namespace IDirectory { id: number; name: string; path: string; - children: IBucket.o1[]; + children: ( + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[]; }; } diff --git a/test/features/clone-and-propagate/src/api/structures/IEnumerationbooleanIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IEnumerationbooleanIIdentified.ts deleted file mode 100644 index d86623e68..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IEnumerationbooleanIIdentified.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type IEnumerationbooleanIIdentified = { - enum: boolean[]; - type: "boolean"; - default?: undefined | boolean; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IEnumerationnumberIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IEnumerationnumberIIdentified.ts deleted file mode 100644 index 132774d3f..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IEnumerationnumberIIdentified.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type IEnumerationnumberIIdentified = { - enum: number[]; - type: "number"; - default?: undefined | number; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IEnumerationstringIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IEnumerationstringIIdentified.ts deleted file mode 100644 index 854e68be7..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IEnumerationstringIIdentified.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type IEnumerationstringIIdentified = { - enum: string[]; - type: "string"; - default?: undefined | string; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IIntegerIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IIntegerIIdentified.ts deleted file mode 100644 index 2704cbb44..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IIntegerIIdentified.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Type } from "typia/lib/tags/Type"; - -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; - -export type IIntegerIIdentified = { - minimum?: undefined | (number & Type<"int32">); - maximum?: undefined | (number & Type<"int32">); - exclusiveMinimum?: undefined | boolean; - exclusiveMaximum?: undefined | boolean; - multipleOf?: undefined | (number & Type<"int32">); - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | number; - type: "integer"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IJsDocTagInfo.ts b/test/features/clone-and-propagate/src/api/structures/IJsDocTagInfo.ts deleted file mode 100644 index d53a9606d..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IJsDocTagInfo.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type IJsDocTagInfo = { - name: string; - text?: undefined | IJsDocTagInfo.IText[]; -}; -export namespace IJsDocTagInfo { - export type IText = { - text: string; - kind: string; - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/IJsonComponents.ts b/test/features/clone-and-propagate/src/api/structures/IJsonComponents.ts deleted file mode 100644 index c693093ce..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IJsonComponents.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { IArrayIIdentified } from "./IArrayIIdentified"; -import type { IBooleanIIdentified } from "./IBooleanIIdentified"; -import type { IEnumerationbooleanIIdentified } from "./IEnumerationbooleanIIdentified"; -import type { IEnumerationnumberIIdentified } from "./IEnumerationnumberIIdentified"; -import type { IEnumerationstringIIdentified } from "./IEnumerationstringIIdentified"; -import type { IIntegerIIdentified } from "./IIntegerIIdentified"; -import type { INullOnlyIIdentified } from "./INullOnlyIIdentified"; -import type { INumberIIdentified } from "./INumberIIdentified"; -import type { IObjectIIdentified } from "./IObjectIIdentified"; -import type { IOneOfIIdentified } from "./IOneOfIIdentified"; -import type { IReferenceIIdentified } from "./IReferenceIIdentified"; -import type { IStringIIdentified } from "./IStringIIdentified"; -import type { ITupleIIdentified } from "./ITupleIIdentified"; -import type { IUnknownIIdentified } from "./IUnknownIIdentified"; - -export namespace IJsonComponents { - export type IAlias = - | IEnumerationbooleanIIdentified - | IEnumerationnumberIIdentified - | IEnumerationstringIIdentified - | IBooleanIIdentified - | IIntegerIIdentified - | INumberIIdentified - | IStringIIdentified - | IArrayIIdentified - | ITupleIIdentified - | IObjectIIdentified - | IReferenceIIdentified - | INullOnlyIIdentified - | IOneOfIIdentified - | IUnknownIIdentified; -} diff --git a/test/features/clone-and-propagate/src/api/structures/IJsonSchema.ts b/test/features/clone-and-propagate/src/api/structures/IJsonSchema.ts deleted file mode 100644 index c0b4eb4b5..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IJsonSchema.ts +++ /dev/null @@ -1,275 +0,0 @@ -import type { Type } from "typia/lib/tags/Type"; - -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; -import type { RecordstringIJsonSchema } from "./RecordstringIJsonSchema"; - -export type IJsonSchema = - | IJsonSchema.IEnumerationboolean - | IJsonSchema.IEnumerationnumber - | IJsonSchema.IEnumerationstring - | IJsonSchema.IBoolean - | IJsonSchema.IInteger - | IJsonSchema.INumber - | IJsonSchema.IString - | IJsonSchema.IArray - | IJsonSchema.ITuple - | IJsonSchema.IObject - | IJsonSchema.IReference - | IJsonSchema.INullOnly - | IJsonSchema.IOneOf - | IJsonSchema.IUnknown; -export namespace IJsonSchema { - export type IEnumerationboolean = { - enum: boolean[]; - type: "boolean"; - default?: undefined | boolean; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IEnumerationnumber = { - enum: number[]; - type: "number"; - default?: undefined | number; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IEnumerationstring = { - enum: string[]; - type: "string"; - default?: undefined | string; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IBoolean = { - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | boolean; - type: "boolean"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IInteger = { - minimum?: undefined | (number & Type<"int32">); - maximum?: undefined | (number & Type<"int32">); - exclusiveMinimum?: undefined | boolean; - exclusiveMaximum?: undefined | boolean; - multipleOf?: undefined | (number & Type<"int32">); - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | number; - type: "integer"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type INumber = { - minimum?: undefined | number; - maximum?: undefined | number; - exclusiveMinimum?: undefined | boolean; - exclusiveMaximum?: undefined | boolean; - multipleOf?: undefined | number; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | number; - type: "number"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IString = { - minLength?: undefined | (number & Type<"uint32">); - maxLength?: undefined | (number & Type<"uint32">); - pattern?: undefined | string; - format?: undefined | string; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | string; - type: "string"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IArray = { - items: IJsonSchema; - minItems?: undefined | (number & Type<"uint32">); - maxItems?: undefined | (number & Type<"uint32">); - "x-typia-tuple"?: undefined | IJsonSchema.ITuple; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - type: "array"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type ITuple = { - items: IJsonSchema[]; - minItems: number & Type<"uint32">; - maxItems?: undefined | (number & Type<"uint32">); - type: "array"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IObject = { - properties: RecordstringIJsonSchema; - required?: undefined | string[]; - patternProperties?: undefined | RecordstringIJsonSchema; - additionalProperties?: - | undefined - | IJsonSchema.IEnumerationstring - | IJsonSchema.IEnumerationnumber - | IJsonSchema.IEnumerationboolean - | IJsonSchema.IBoolean - | IJsonSchema.INumber - | IJsonSchema.IInteger - | IJsonSchema.IString - | IJsonSchema.IArray - | IJsonSchema.ITuple - | IJsonSchema.IObject - | IJsonSchema.IReference - | IJsonSchema.INullOnly - | IJsonSchema.IOneOf - | IJsonSchema.IUnknown; - "x-typia-patternProperties"?: undefined | RecordstringIJsonSchema; - "x-typia-additionalProperties"?: - | undefined - | IJsonSchema.IEnumerationstring - | IJsonSchema.IEnumerationnumber - | IJsonSchema.IEnumerationboolean - | IJsonSchema.IBoolean - | IJsonSchema.INumber - | IJsonSchema.IInteger - | IJsonSchema.IString - | IJsonSchema.IArray - | IJsonSchema.ITuple - | IJsonSchema.IObject - | IJsonSchema.IReference - | IJsonSchema.INullOnly - | IJsonSchema.IOneOf - | IJsonSchema.IUnknown; - type: "object"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IReference = { - $ref: string; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type INullOnly = { - type: "null"; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IOneOf = { - oneOf: IJsonSchema[]; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; - export type IUnknown = { - type?: undefined; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/IMetadataTypeTag.ts b/test/features/clone-and-propagate/src/api/structures/IMetadataTypeTag.ts deleted file mode 100644 index e8d5c88f4..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IMetadataTypeTag.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type IMetadataTypeTag = { - target: "array" | "bigint" | "boolean" | "number" | "string"; - name: string; - kind: string; - exclusive: boolean | string[]; - value?: any | undefined; - validate?: undefined | string; - schema?: undefined | {}; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/INullOnlyIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/INullOnlyIIdentified.ts deleted file mode 100644 index a53b36d20..000000000 --- a/test/features/clone-and-propagate/src/api/structures/INullOnlyIIdentified.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type INullOnlyIIdentified = { - type: "null"; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/INumberIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/INumberIIdentified.ts deleted file mode 100644 index c2554e84b..000000000 --- a/test/features/clone-and-propagate/src/api/structures/INumberIIdentified.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; - -export type INumberIIdentified = { - minimum?: undefined | number; - maximum?: undefined | number; - exclusiveMinimum?: undefined | boolean; - exclusiveMaximum?: undefined | boolean; - multipleOf?: undefined | number; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | number; - type: "number"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IObjectIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IObjectIIdentified.ts deleted file mode 100644 index 533f6fc94..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IObjectIIdentified.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IJsonSchema } from "./IJsonSchema"; -import type { RecordstringIJsonSchema } from "./RecordstringIJsonSchema"; - -export type IObjectIIdentified = { - properties: RecordstringIJsonSchema; - required?: undefined | string[]; - patternProperties?: undefined | RecordstringIJsonSchema; - additionalProperties?: - | undefined - | IJsonSchema.IEnumerationstring - | IJsonSchema.IEnumerationnumber - | IJsonSchema.IEnumerationboolean - | IJsonSchema.IBoolean - | IJsonSchema.INumber - | IJsonSchema.IInteger - | IJsonSchema.IString - | IJsonSchema.IArray - | IJsonSchema.ITuple - | IJsonSchema.IObject - | IJsonSchema.IReference - | IJsonSchema.INullOnly - | IJsonSchema.IOneOf - | IJsonSchema.IUnknown; - "x-typia-patternProperties"?: undefined | RecordstringIJsonSchema; - "x-typia-additionalProperties"?: - | undefined - | IJsonSchema.IEnumerationstring - | IJsonSchema.IEnumerationnumber - | IJsonSchema.IEnumerationboolean - | IJsonSchema.IBoolean - | IJsonSchema.INumber - | IJsonSchema.IInteger - | IJsonSchema.IString - | IJsonSchema.IArray - | IJsonSchema.ITuple - | IJsonSchema.IObject - | IJsonSchema.IReference - | IJsonSchema.INullOnly - | IJsonSchema.IOneOf - | IJsonSchema.IUnknown; - type: "object"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IOneOfIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IOneOfIIdentified.ts deleted file mode 100644 index 3e66c26fd..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IOneOfIIdentified.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IJsonSchema } from "./IJsonSchema"; - -export type IOneOfIIdentified = { - oneOf: IJsonSchema[]; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IPage.ts b/test/features/clone-and-propagate/src/api/structures/IPage.ts index 3e2e99174..ba30cf634 100644 --- a/test/features/clone-and-propagate/src/api/structures/IPage.ts +++ b/test/features/clone-and-propagate/src/api/structures/IPage.ts @@ -1,17 +1,6 @@ import type { Type } from "typia/lib/tags/Type"; export namespace IPage { - /** - * Page request data - */ - export type IRequest = - /** - * Page request data - */ - { - page?: null | undefined | (number & Type<"uint32">); - limit?: null | undefined | (number & Type<"uint32">); - }; /** * Page information. */ @@ -25,4 +14,15 @@ export namespace IPage { records: number & Type<"uint32">; pages: number & Type<"uint32">; }; + /** + * Page request data + */ + export type IRequest = + /** + * Page request data + */ + { + page?: null | undefined | (number & Type<"uint32">); + limit?: null | undefined | (number & Type<"uint32">); + }; } diff --git a/test/features/clone-and-propagate/src/api/structures/IReferenceIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IReferenceIIdentified.ts deleted file mode 100644 index a249d4331..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IReferenceIIdentified.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type IReferenceIIdentified = { - $ref: string; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/ISeller.ts b/test/features/clone-and-propagate/src/api/structures/ISeller.ts index 18d557614..d1c4511cf 100644 --- a/test/features/clone-and-propagate/src/api/structures/ISeller.ts +++ b/test/features/clone-and-propagate/src/api/structures/ISeller.ts @@ -2,13 +2,6 @@ import type { Format } from "typia/lib/tags/Format"; import type { Type } from "typia/lib/tags/Type"; export namespace ISeller { - export type IJoin = { - email: string & Format<"email">; - password: string; - name: string; - mobile: string; - company: string; - }; export type IAuthorized = { authorization: { token: string; @@ -39,6 +32,13 @@ export namespace ISeller { */ created_at: string & Format<"date-time">; }; + export type IJoin = { + email: string & Format<"email">; + password: string; + name: string; + mobile: string; + company: string; + }; export type ILogin = { email: string & Format<"email">; password: string; diff --git a/test/features/clone-and-propagate/src/api/structures/ISharedDirectory.ts b/test/features/clone-and-propagate/src/api/structures/ISharedDirectory.ts index 0ebe68931..93891b03a 100644 --- a/test/features/clone-and-propagate/src/api/structures/ISharedDirectory.ts +++ b/test/features/clone-and-propagate/src/api/structures/ISharedDirectory.ts @@ -1,9 +1,20 @@ -import type { IBucket } from "./IBucket"; +import type { IDirectory } from "./IDirectory"; +import type { IImageFile } from "./IImageFile"; +import type { IShortcut } from "./IShortcut"; +import type { ITextFile } from "./ITextFile"; +import type { IZipFile } from "./IZipFile"; export type ISharedDirectory = { access: "read" | "write"; id: number; name: string; path: string; - children: IBucket.o1[]; + children: ( + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[]; }; diff --git a/test/features/clone-and-propagate/src/api/structures/IShortcut.ts b/test/features/clone-and-propagate/src/api/structures/IShortcut.ts index 00e016fb7..ca44aafec 100644 --- a/test/features/clone-and-propagate/src/api/structures/IShortcut.ts +++ b/test/features/clone-and-propagate/src/api/structures/IShortcut.ts @@ -1,10 +1,14 @@ -import type { IBucket } from "./IBucket"; +import type { IDirectory } from "./IDirectory"; +import type { IImageFile } from "./IImageFile"; +import type { ISharedDirectory } from "./ISharedDirectory"; +import type { ITextFile } from "./ITextFile"; +import type { IZipFile } from "./IZipFile"; export type IShortcut = { id: number; name: string; path: string; - target: IBucket; + target: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; type: "file"; extension: "lnk"; }; @@ -13,6 +17,12 @@ export namespace IShortcut { id: number; name: string; path: string; - target: IBucket.o1; + target: + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; }; } diff --git a/test/features/clone-and-propagate/src/api/structures/IStringIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IStringIIdentified.ts deleted file mode 100644 index 4a4d2a66b..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IStringIIdentified.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Type } from "typia/lib/tags/Type"; - -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IMetadataTypeTag } from "./IMetadataTypeTag"; - -export type IStringIIdentified = { - minLength?: undefined | (number & Type<"uint32">); - maxLength?: undefined | (number & Type<"uint32">); - pattern?: undefined | string; - format?: undefined | string; - "x-typia-typeTags"?: undefined | IMetadataTypeTag[]; - default?: undefined | string; - type: "string"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/ISwagger.ts b/test/features/clone-and-propagate/src/api/structures/ISwagger.ts deleted file mode 100644 index b071e498e..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ISwagger.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { Format } from "typia/lib/tags/Format"; - -import type { ISwaggerComponents } from "./ISwaggerComponents"; -import type { ISwaggerInfo } from "./ISwaggerInfo"; -import type { RecordstringRecordstringISwaggerRoute } from "./RecordstringRecordstringISwaggerRoute"; - -/** - * Swagger Document. - * - * `ISwagger` is a data structure representing content of `swagger.json` file - * generated by Nestia. Note that, this is not an universal structure, but a dedicated - * structure only for Nestia. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export type ISwagger = - /** - * Swagger Document. - * - * `ISwagger` is a data structure representing content of `swagger.json` file - * generated by Nestia. Note that, this is not an universal structure, but a dedicated - * structure only for Nestia. - * - * @author Jeongho Nam - https://github.com/samchon - */ - { - /** - * The version of the OpenAPI document. - * - * Nestia always generate OpenAPI 3.0.x document. - */ - openapi: `3.0.${number}`; - /** - * List of servers that provide the API. - */ - servers: ISwagger.IServer[]; - /** - * Information about the API. - */ - info: ISwaggerInfo; - /** - * The available paths and operations for the API. - * - * The 1st key is the path, and the 2nd key is the HTTP method. - */ - paths: RecordstringRecordstringISwaggerRoute; - /** - * An object to hold reusable data structures. - * - * It stores both DTO schemas and security schemes. - * - * For reference, `nestia` defines every object and alias types as reusable DTO - * schemas. The alias type means that defined by `type` keyword in TypeScript. - */ - components: ISwaggerComponents; - }; -export namespace ISwagger { - /** - * Remote server definition. - */ - export type IServer = - /** - * Remote server definition. - */ - { - /** - * A URL to the target host. - */ - url: string & Format<"uri">; - /** - * An optional string describing the target server. - */ - description?: undefined | string; - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/ISwaggerComponents.ts b/test/features/clone-and-propagate/src/api/structures/ISwaggerComponents.ts deleted file mode 100644 index 65e89310b..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ISwaggerComponents.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { RecordstringIJsonComponents } from "./RecordstringIJsonComponents"; -import type { RecordstringISwaggerSecurityScheme } from "./RecordstringISwaggerSecurityScheme"; - -/** - * Reusable components in Swagger. - * - * `ISwaggerComponents` is a data structure representing content of `components` object - * in `swagger.json` file generated by Nestia. Note that, this is not an universal - * structure, but a dedicated structure only for Nestia. - * - * @author Jeongho Nam - https://github.com/samchon - */ -export type ISwaggerComponents = - /** - * Reusable components in Swagger. - * - * `ISwaggerComponents` is a data structure representing content of `components` object - * in `swagger.json` file generated by Nestia. Note that, this is not an universal - * structure, but a dedicated structure only for Nestia. - * - * @author Jeongho Nam - https://github.com/samchon - */ - { - /** - * An object to hold reusable DTO schemas. - * - * For reference, `nestia` stores every object and alias types as reusable DTO - * schemas. The alias type means that defined by `type` keyword in TypeScript. - */ - schemas?: undefined | RecordstringIJsonComponents.IAlias; - /** - * An object to hold reusable security schemes. - * - * This property be configured by user in `nestia.config.ts` file. - */ - securitySchemes?: undefined | RecordstringISwaggerSecurityScheme; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/ISwaggerInfo.ts b/test/features/clone-and-propagate/src/api/structures/ISwaggerInfo.ts deleted file mode 100644 index ddfb461d0..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ISwaggerInfo.ts +++ /dev/null @@ -1,79 +0,0 @@ -import type { Format } from "typia/lib/tags/Format"; - -/** - * Information about the API. - * - * @author Samchon - */ -export type ISwaggerInfo = - /** - * Information about the API. - * - * @author Samchon - */ - { - /** - * The title of the API. - */ - title: string; - /** - * A short description of the API. - */ - description?: undefined | string; - /** - * A URL to the Terms of Service for the API. - */ - termsOfService?: undefined | (string & Format<"uri">); - /** - * The contact information for the exposed API. - */ - contact?: undefined | ISwaggerInfo.IContact; - /** - * The license information for the exposed API. - */ - license?: undefined | ISwaggerInfo.ILicense; - /** - * Version of the API. - */ - version: string; - }; -export namespace ISwaggerInfo { - /** - * Contact information for the exposed API. - */ - export type IContact = - /** - * Contact information for the exposed API. - */ - { - /** - * The identifying name of the contact person/organization. - */ - name?: undefined | string; - /** - * The URL pointing to the contact information. - */ - url?: undefined | (string & Format<"uri">); - /** - * The email address of the contact person/organization. - */ - email?: undefined | (string & Format<"email">); - }; - /** - * License information for the exposed API. - */ - export type ILicense = - /** - * License information for the exposed API. - */ - { - /** - * The license name used for the API. - */ - name: string; - /** - * A URL to the license used for the API. - */ - url?: undefined | (string & Format<"uri">); - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/ISwaggerRoute.ts b/test/features/clone-and-propagate/src/api/structures/ISwaggerRoute.ts deleted file mode 100644 index 4ffe04766..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ISwaggerRoute.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IJsonSchema } from "./IJsonSchema"; -import type { RecordstringArraystring } from "./RecordstringArraystring"; - -export type ISwaggerRoute = { - deprecated?: undefined | boolean; - security?: undefined | RecordstringArraystring[]; - operationId?: undefined | string; - tags: string[]; - parameters: ISwaggerRoute.IParameter[]; - requestBody?: undefined | ISwaggerRoute.IRequestBody; - responses: ISwaggerRoute.IResponseBody; - summary?: undefined | string; - description?: undefined | string; - "x-nestia-method"?: undefined | string; - "x-nestia-namespace"?: undefined | string; - "x-nestia-jsDocTags"?: undefined | IJsDocTagInfo[]; -}; -export namespace ISwaggerRoute { - export type IResponseBody = { - [key: string]: { - description: string; - content?: undefined | ISwaggerRoute.IContent; - "x-nestia-encrypted"?: undefined | boolean; - }; - }; - export type IParameter = { - name: string; - in: string; - schema: IJsonSchema; - required: boolean; - description?: undefined | string; - }; - export type IRequestBody = { - description?: undefined | string; - content: ISwaggerRoute.IContent; - required: true; - "x-nestia-encrypted"?: undefined | boolean; - }; - export type IContent = { - "application/x-www-form-urlencoded"?: - | undefined - | { - schema: IJsonSchema; - }; - "application/json"?: - | undefined - | { - schema: IJsonSchema; - }; - "text/plain"?: - | undefined - | { - schema: IJsonSchema; - }; - "multipart/form-data"?: - | undefined - | { - schema: IJsonSchema; - }; - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/ISwaggerSecurityScheme.ts b/test/features/clone-and-propagate/src/api/structures/ISwaggerSecurityScheme.ts deleted file mode 100644 index 255cfe840..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ISwaggerSecurityScheme.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { OmitISwaggerSecurityScheme } from "./OmitISwaggerSecurityScheme"; -import type { Recordstringstring } from "./Recordstringstring"; - -/** - * Security scheme of Swagger Documents. - * - * `ISwaggerSecurityScheme` is a data structure representing content of - * `securitySchemes` in `swagger.json` file. It is composed with 5 types of security - * schemes as an union type like below. - * - * @reference https://swagger.io/specification/#security-scheme-object - * @author Jeongho Nam - https://github.com/samchon - */ -export type ISwaggerSecurityScheme = - | ISwaggerSecurityScheme.IHttpBasic - | ISwaggerSecurityScheme.IHttpBearer - | ISwaggerSecurityScheme.IApiKey - | ISwaggerSecurityScheme.IOpenId - | ISwaggerSecurityScheme.IOAuth2; -export namespace ISwaggerSecurityScheme { - export type IHttpBasic = { - type: "http"; - scheme: "basic"; - }; - export type IHttpBearer = { - type: "http"; - scheme: "bearer"; - bearerFormat?: undefined | string; - }; - export type IApiKey = { - type: "apiKey"; - /** - * @default header - */ - in?: undefined | "cookie" | "header" | "query"; - /** - * @default Authorization - */ - name?: undefined | string; - }; - export type IOpenId = { - type: "openIdConnect"; - openIdConnectUrl: string; - }; - export type IOAuth2 = { - type: "oauth2"; - flows: ISwaggerSecurityScheme.IOAuth2.IFlowSet; - description?: undefined | string; - }; - export namespace IOAuth2 { - export type IFlowSet = { - authorizationCode?: undefined | ISwaggerSecurityScheme.IOAuth2.IFlow; - implicit?: undefined | OmitISwaggerSecurityScheme.IOAuth2.IFlowtokenUrl; - password?: - | undefined - | OmitISwaggerSecurityScheme.IOAuth2.IFlowauthorizationUrl; - clientCredentials?: - | undefined - | OmitISwaggerSecurityScheme.IOAuth2.IFlowauthorizationUrl; - }; - export type IFlow = { - authorizationUrl: string; - tokenUrl?: undefined | string; - refreshUrl?: undefined | string; - scopes?: undefined | Recordstringstring; - }; - } -} diff --git a/test/features/clone-and-propagate/src/api/structures/ITupleIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/ITupleIIdentified.ts deleted file mode 100644 index 5db841531..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ITupleIIdentified.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Type } from "typia/lib/tags/Type"; - -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; -import type { IJsonSchema } from "./IJsonSchema"; - -export type ITupleIIdentified = { - items: IJsonSchema[]; - minItems: number & Type<"uint32">; - maxItems?: undefined | (number & Type<"uint32">); - type: "array"; - /** - * Only when swagger mode. - */ - nullable?: undefined | boolean; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IUnknownIIdentified.ts b/test/features/clone-and-propagate/src/api/structures/IUnknownIIdentified.ts deleted file mode 100644 index f2538958f..000000000 --- a/test/features/clone-and-propagate/src/api/structures/IUnknownIIdentified.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IJsDocTagInfo } from "./IJsDocTagInfo"; - -export type IUnknownIIdentified = { - type?: undefined; - deprecated?: undefined | boolean; - title?: undefined | string; - description?: undefined | string; - "x-typia-jsDocTags"?: undefined | IJsDocTagInfo[]; - "x-typia-required"?: undefined | boolean; - "x-typia-optional"?: undefined | boolean; - "x-typia-rest"?: undefined | boolean; - $id?: undefined | string; - $recursiveAnchor?: undefined | boolean; -}; diff --git a/test/features/clone-and-propagate/src/api/structures/IUser.ts b/test/features/clone-and-propagate/src/api/structures/IUser.ts index 1382fdc5c..759b0f9f7 100644 --- a/test/features/clone-and-propagate/src/api/structures/IUser.ts +++ b/test/features/clone-and-propagate/src/api/structures/IUser.ts @@ -9,11 +9,10 @@ export type IUser = { both_optional_and_undefindable?: undefined | string; nullable_attr: null | string; optional_and_nullable_attr?: null | undefined | number; - user_type: IUser.Type; + user_type: "admin" | "default" | "seller"; }; export namespace IUser { - export type Type = "admin" | "default" | "seller"; export type ISearch = { - user_type?: undefined | IUser.Type; + user_type?: undefined | "admin" | "default" | "seller"; }; } diff --git a/test/features/clone-and-propagate/src/api/structures/ObjectUnionExplicit.ts b/test/features/clone-and-propagate/src/api/structures/ObjectUnionExplicit.ts deleted file mode 100644 index 8c52bd333..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ObjectUnionExplicit.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { DiscriminatorcircleICircle } from "./DiscriminatorcircleICircle"; -import type { DiscriminatorlineILine } from "./DiscriminatorlineILine"; -import type { DiscriminatorpointIPoint } from "./DiscriminatorpointIPoint"; -import type { DiscriminatorpolygonIPolygon } from "./DiscriminatorpolygonIPolygon"; -import type { DiscriminatorpolylineIPolyline } from "./DiscriminatorpolylineIPolyline"; -import type { DiscriminatorrectangleIRectangle } from "./DiscriminatorrectangleIRectangle"; -import type { DiscriminatortriangleITriangle } from "./DiscriminatortriangleITriangle"; - -export type ObjectUnionExplicit = ( - | DiscriminatorpointIPoint - | DiscriminatorlineILine - | DiscriminatortriangleITriangle - | DiscriminatorrectangleIRectangle - | DiscriminatorpolylineIPolyline - | DiscriminatorpolygonIPolygon - | DiscriminatorcircleICircle -)[]; diff --git a/test/features/clone-and-propagate/src/api/structures/ObjectUnionImplicit.ts b/test/features/clone-and-propagate/src/api/structures/ObjectUnionImplicit.ts deleted file mode 100644 index db863ff2e..000000000 --- a/test/features/clone-and-propagate/src/api/structures/ObjectUnionImplicit.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ICircle } from "./ICircle"; -import type { ILine } from "./ILine"; -import type { IPoint } from "./IPoint"; -import type { IPolygon } from "./IPolygon"; -import type { IPolyline } from "./IPolyline"; -import type { IRectangle } from "./IRectangle"; -import type { ITriangle } from "./ITriangle"; - -export type ObjectUnionImplicit = ( - | IPoint.o1 - | ILine - | ITriangle - | IRectangle - | IPolyline.o1 - | IPolygon - | ICircle -)[]; diff --git a/test/features/clone-and-propagate/src/api/structures/OmitISwaggerSecurityScheme.ts b/test/features/clone-and-propagate/src/api/structures/OmitISwaggerSecurityScheme.ts deleted file mode 100644 index 14af9af10..000000000 --- a/test/features/clone-and-propagate/src/api/structures/OmitISwaggerSecurityScheme.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Recordstringstring } from "./Recordstringstring"; - -export namespace OmitISwaggerSecurityScheme { - export namespace IOAuth2 { - /** - * Construct a type with the properties of T except for those in type K. - */ - export type IFlowtokenUrl = - /** - * Construct a type with the properties of T except for those in type K. - */ - { - authorizationUrl: string; - refreshUrl?: undefined | string; - scopes?: undefined | Recordstringstring; - }; - /** - * Construct a type with the properties of T except for those in type K. - */ - export type IFlowauthorizationUrl = - /** - * Construct a type with the properties of T except for those in type K. - */ - { - tokenUrl?: undefined | string; - refreshUrl?: undefined | string; - scopes?: undefined | Recordstringstring; - }; - } -} diff --git a/test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailnullable_attroptional_attr.ts b/test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailnullable_attroptional_attr.ts deleted file mode 100644 index bc8d0b35c..000000000 --- a/test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailnullable_attroptional_attr.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Format } from "typia/lib/tags/Format"; - -/** - * Make all properties in T optional - */ -export type PartialPickIUsernameemailnullable_attroptional_attr = - /** - * Make all properties in T optional - */ - { - name?: undefined | string; - email?: null | undefined | (string & Format<"email">); - nullable_attr?: null | undefined | string; - optional_attr?: undefined | string; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/PartialPickIUseremailnamenullable_attroptional_attr.ts b/test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailoptional_attrnullable_attr.ts similarity index 84% rename from test/features/clone-and-propagate/src/api/structures/PartialPickIUseremailnamenullable_attroptional_attr.ts rename to test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailoptional_attrnullable_attr.ts index 97c107014..4b0aa13df 100644 --- a/test/features/clone-and-propagate/src/api/structures/PartialPickIUseremailnamenullable_attroptional_attr.ts +++ b/test/features/clone-and-propagate/src/api/structures/PartialPickIUsernameemailoptional_attrnullable_attr.ts @@ -3,13 +3,13 @@ import type { Format } from "typia/lib/tags/Format"; /** * Make all properties in T optional */ -export type PartialPickIUseremailnamenullable_attroptional_attr = +export type PartialPickIUsernameemailoptional_attrnullable_attr = /** * Make all properties in T optional */ { - email?: null | undefined | (string & Format<"email">); name?: undefined | string; - nullable_attr?: null | undefined | string; + email?: null | undefined | (string & Format<"email">); optional_attr?: undefined | string; + nullable_attr?: null | undefined | string; }; diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringArraystring.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringArraystring.ts deleted file mode 100644 index e09c2ee05..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringArraystring.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Construct a type with a set of properties K of type T - */ -export type RecordstringArraystring = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: string[]; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonComponents.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonComponents.ts deleted file mode 100644 index 38adcd939..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonComponents.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { IJsonComponents } from "./IJsonComponents"; - -export namespace RecordstringIJsonComponents { - /** - * Construct a type with a set of properties K of type T - */ - export type IAlias = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: IJsonComponents.IAlias; - }; -} diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonSchema.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonSchema.ts deleted file mode 100644 index 7b391e05d..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringIJsonSchema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { IJsonSchema } from "./IJsonSchema"; - -/** - * Construct a type with a set of properties K of type T - */ -export type RecordstringIJsonSchema = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: IJsonSchema; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerRoute.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerRoute.ts deleted file mode 100644 index 4c420fcb5..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerRoute.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ISwaggerRoute } from "./ISwaggerRoute"; - -/** - * Construct a type with a set of properties K of type T - */ -export type RecordstringISwaggerRoute = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: ISwaggerRoute; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerSecurityScheme.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerSecurityScheme.ts deleted file mode 100644 index e95efd1df..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringISwaggerSecurityScheme.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ISwaggerSecurityScheme } from "./ISwaggerSecurityScheme"; - -/** - * Construct a type with a set of properties K of type T - */ -export type RecordstringISwaggerSecurityScheme = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: ISwaggerSecurityScheme; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/RecordstringRecordstringISwaggerRoute.ts b/test/features/clone-and-propagate/src/api/structures/RecordstringRecordstringISwaggerRoute.ts deleted file mode 100644 index 26b188b75..000000000 --- a/test/features/clone-and-propagate/src/api/structures/RecordstringRecordstringISwaggerRoute.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { RecordstringISwaggerRoute } from "./RecordstringISwaggerRoute"; - -/** - * Construct a type with a set of properties K of type T - */ -export type RecordstringRecordstringISwaggerRoute = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: RecordstringISwaggerRoute; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/Recordstringstring.ts b/test/features/clone-and-propagate/src/api/structures/Recordstringstring.ts deleted file mode 100644 index b19a79d49..000000000 --- a/test/features/clone-and-propagate/src/api/structures/Recordstringstring.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Construct a type with a set of properties K of type T - */ -export type Recordstringstring = - /** - * Construct a type with a set of properties K of type T - */ - { - [key: string]: string; - }; diff --git a/test/features/clone-and-propagate/src/api/structures/Template.ts b/test/features/clone-and-propagate/src/api/structures/Template.ts index b271cc231..8fa4f2a51 100644 --- a/test/features/clone-and-propagate/src/api/structures/Template.ts +++ b/test/features/clone-and-propagate/src/api/structures/Template.ts @@ -8,8 +8,8 @@ export type Template = { ipv4: `${number}.${number}.${number}.${number}`; email: `${string}@${string}.${string}`; combined: + | `the_1_value_with_label_A_${string}_${number}_4` | `the_2_value_with_label_A_${string}_${number}_4` - | `the_3_value_with_label_A_${string}_${number}_4` - | `the_1_value_with_label_A_${string}_${number}_4`; + | `the_3_value_with_label_A_${string}_${number}_4`; nosubstitution: "something"; }; diff --git a/test/features/clone-and-propagate/src/api/structures/TupleHierarchical.ts b/test/features/clone-and-propagate/src/api/structures/TupleHierarchical.ts deleted file mode 100644 index 0741862ae..000000000 --- a/test/features/clone-and-propagate/src/api/structures/TupleHierarchical.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type TupleHierarchical = [ - boolean, - null, - number, - [boolean, null, [number, [boolean, string]]], - [number, [string, boolean, [number, number, [boolean, string]][]][]], -]; diff --git a/test/features/clone-and-propagate/src/api/structures/TupleRest.ts b/test/features/clone-and-propagate/src/api/structures/TupleRest.ts deleted file mode 100644 index d4a5be1ee..000000000 --- a/test/features/clone-and-propagate/src/api/structures/TupleRest.ts +++ /dev/null @@ -1 +0,0 @@ -export type TupleRest = [boolean, number, ...string[]]; diff --git a/test/features/clone-and-propagate/src/controllers/TupleRestController.ts b/test/features/clone-and-propagate/src/controllers/TupleRestController.ts deleted file mode 100644 index 79d13e3a2..000000000 --- a/test/features/clone-and-propagate/src/controllers/TupleRestController.ts +++ /dev/null @@ -1,13 +0,0 @@ -import core from "@nestia/core"; -import * as nest from "@nestjs/common"; -import typia, { Primitive, Resolved } from "typia"; - -@nest.Controller("tupleRestController") -export class TupleRestController { - @core.TypedRoute.Get() - public get(): TupleRest { - return [false, 1, "two", "three"]; - } -} - -type TupleRest = [boolean, number, ...string[]]; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_at.ts index 622f4fd03..2be11938e 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_at.ts @@ -2,14 +2,21 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { IBucket } from "../../../../api/structures/IBucket"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionExplicit_at = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IBucket; - }> = await api.functional.arrayRecursiveUnionExplicit.at( + const output: IPropagation< + { + 200: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 200 + > = await api.functional.arrayRecursiveUnionExplicit.at( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_index.ts index 4b597dc3c..591bb3ee6 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_index.ts @@ -2,13 +2,20 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { ArrayRecursiveUnionExplicit } from "../../../../api/structures/ArrayRecursiveUnionExplicit"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionExplicit_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ArrayRecursiveUnionExplicit; - }> = await api.functional.arrayRecursiveUnionExplicit.index(connection); + const output: IPropagation< + { + 200: (IDirectory | IImageFile | ITextFile | IZipFile | IShortcut)[]; + }, + 200 + > = await api.functional.arrayRecursiveUnionExplicit.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_store.ts index 576c19122..e927ff2ab 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionExplicit_store.ts @@ -2,16 +2,23 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { IBucket } from "../../../../api/structures/IBucket"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionExplicit_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: IBucket; - }> = await api.functional.arrayRecursiveUnionExplicit.store( + const output: IPropagation< + { + 201: IDirectory | IImageFile | ITextFile | IZipFile | IShortcut; + }, + 201 + > = await api.functional.arrayRecursiveUnionExplicit.store( connection, - typia.random(), + typia.random(), ); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_at.ts index 0c0834b2c..f577f1fec 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_at.ts @@ -2,14 +2,28 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { IBucket } from "../../../../api/structures/IBucket"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { ISharedDirectory } from "../../../../api/structures/ISharedDirectory"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionImplicit_at = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IBucket.o1; - }> = await api.functional.arrayRecursiveUnionImplicit.at( + const output: IPropagation< + { + 200: + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; + }, + 200 + > = await api.functional.arrayRecursiveUnionImplicit.at( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_index.ts index fdbcc5afa..c48c6cccb 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_index.ts @@ -2,13 +2,28 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { ArrayRecursiveUnionImplicit } from "../../../../api/structures/ArrayRecursiveUnionImplicit"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { ISharedDirectory } from "../../../../api/structures/ISharedDirectory"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionImplicit_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ArrayRecursiveUnionImplicit; - }> = await api.functional.arrayRecursiveUnionImplicit.index(connection); + const output: IPropagation< + { + 200: ( + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + )[]; + }, + 200 + > = await api.functional.arrayRecursiveUnionImplicit.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_store.ts index afd7f4a49..30eceddda 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursiveUnionImplicit_store.ts @@ -2,16 +2,37 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { IBucket } from "../../../../api/structures/IBucket"; +import type { IDirectory } from "../../../../api/structures/IDirectory"; +import type { IImageFile } from "../../../../api/structures/IImageFile"; +import type { ISharedDirectory } from "../../../../api/structures/ISharedDirectory"; +import type { IShortcut } from "../../../../api/structures/IShortcut"; +import type { ITextFile } from "../../../../api/structures/ITextFile"; +import type { IZipFile } from "../../../../api/structures/IZipFile"; export const test_api_arrayRecursiveUnionImplicit_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: IBucket.o1; - }> = await api.functional.arrayRecursiveUnionImplicit.store( + const output: IPropagation< + { + 201: + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1; + }, + 201 + > = await api.functional.arrayRecursiveUnionImplicit.store( connection, - typia.random(), + typia.random< + | IDirectory.o1 + | ISharedDirectory + | IImageFile.o1 + | ITextFile.o1 + | IZipFile.o1 + | IShortcut.o1 + >(), ); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_at.ts index b8adeec52..ab78f9a9f 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_at.ts @@ -7,9 +7,12 @@ import type { ICategory } from "../../../../api/structures/ICategory"; export const test_api_arrayRecursive_at = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ICategory; - }> = await api.functional.arrayRecursive.at( + const output: IPropagation< + { + 200: ICategory; + }, + 200 + > = await api.functional.arrayRecursive.at( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_index.ts index 15a785d8c..f20a25fc6 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_index.ts @@ -7,8 +7,11 @@ import type { ICategory } from "../../../../api/structures/ICategory"; export const test_api_arrayRecursive_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ICategory[]; - }> = await api.functional.arrayRecursive.index(connection); + const output: IPropagation< + { + 200: ICategory[]; + }, + 200 + > = await api.functional.arrayRecursive.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_store.ts index 5fdedf690..07658538f 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arrayRecursive_store.ts @@ -7,9 +7,12 @@ import type { ICategory } from "../../../../api/structures/ICategory"; export const test_api_arrayRecursive_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: ICategory; - }> = await api.functional.arrayRecursive.store( + const output: IPropagation< + { + 201: ICategory; + }, + 201 + > = await api.functional.arrayRecursive.store( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_at.ts index 2c094f4ff..ecdf2e7fa 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_at.ts @@ -6,9 +6,12 @@ import api from "../../../../api"; import type { IPerson } from "../../../../api/structures/IPerson"; export const test_api_arraySimple_at = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: IPerson; - }> = await api.functional.arraySimple.at( + const output: IPropagation< + { + 200: IPerson; + }, + 200 + > = await api.functional.arraySimple.at( connection, typia.random>(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_index.ts index 52aa04500..30b42ec06 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_index.ts @@ -2,13 +2,16 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { ArraySimple } from "../../../../api/structures/ArraySimple"; +import type { IPerson } from "../../../../api/structures/IPerson"; export const test_api_arraySimple_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ArraySimple; - }> = await api.functional.arraySimple.index(connection); + const output: IPropagation< + { + 200: IPerson[]; + }, + 200 + > = await api.functional.arraySimple.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_store.ts index 1c3236c33..18ffda4eb 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_arraySimple_store.ts @@ -7,9 +7,12 @@ import type { IPerson } from "../../../../api/structures/IPerson"; export const test_api_arraySimple_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: IPerson; - }> = await api.functional.arraySimple.store( + const output: IPropagation< + { + 201: IPerson; + }, + 201 + > = await api.functional.arraySimple.store( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_index.ts index 568fd93e8..603e58423 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_index.ts @@ -8,9 +8,12 @@ import type { IPageIBbsArticle } from "../../../../api/structures/IPageIBbsArtic export const test_api_bbs_articles_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IPageIBbsArticle.ISummary; - }> = await api.functional.bbs.articles.index( + const output: IPropagation< + { + 200: IPageIBbsArticle.ISummary; + }, + 200 + > = await api.functional.bbs.articles.index( connection, typia.random(), typia.random(), diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_store.ts index 8cd45c939..d919841c4 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_store.ts @@ -7,9 +7,12 @@ import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; export const test_api_bbs_articles_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: IBbsArticle; - }> = await api.functional.bbs.articles.store( + const output: IPropagation< + { + 201: IBbsArticle; + }, + 201 + > = await api.functional.bbs.articles.store( connection, typia.random(), typia.random(), diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_update.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_update.ts index 2fcac5709..5f84d1579 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_update.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_bbs_articles_update.ts @@ -8,9 +8,12 @@ import type { IBbsArticle } from "../../../../api/structures/IBbsArticle"; export const test_api_bbs_articles_update = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IBbsArticle; - }> = await api.functional.bbs.articles.update( + const output: IPropagation< + { + 200: IBbsArticle; + }, + 200 + > = await api.functional.bbs.articles.update( connection, typia.random(), typia.random>(), diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_health_get.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_health_get.ts index 13ab6f844..d1d919465 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_health_get.ts +++ b/test/features/clone-and-propagate/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: void; + }, + 200 + > = await api.functional.health.get(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_multipart_post.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_multipart_post.ts index db965953a..66c850077 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_multipart_post.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_multipart_post.ts @@ -5,9 +5,12 @@ import api from "../../../../api"; import type { IMultipart } from "../../../../api/structures/IMultipart"; export const test_api_multipart_post = async (connection: api.IConnection) => { - const output: IPropagation<{ - 201: undefined; - }> = await api.functional.multipart.post( + const output: IPropagation< + { + 201: void; + }, + 201 + > = await api.functional.multipart.post( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_index.ts index 4e27161cb..1b43b6d3e 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_index.ts @@ -7,8 +7,11 @@ import type { ObjectLietral } from "../../../../api/structures/ObjectLietral"; export const test_api_objectLiteral_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ObjectLietral[]; - }> = await api.functional.objectLiteral.index(connection); + const output: IPropagation< + { + 200: ObjectLietral[]; + }, + 200 + > = await api.functional.objectLiteral.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_literal_literals.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_literal_literals.ts index e0933639c..b4d952c09 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_literal_literals.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectLiteral_literal_literals.ts @@ -8,16 +8,19 @@ import api from "../../../../api"; export const test_api_objectLiteral_literal_literals = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: { - id: string; - member: { - id: string & Format<"uuid">; - email: string & Format<"email">; - age: number & Type<"uint32">; - }; - created_at: string & Format<"date-time">; - }[]; - }> = await api.functional.objectLiteral.literal.literals(connection); + const 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 + > = await api.functional.objectLiteral.literal.literals(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_at.ts index 6764d6d6d..e205f3d12 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_at.ts @@ -5,8 +5,11 @@ import api from "../../../../api"; import type { IBox3D } from "../../../../api/structures/IBox3D"; export const test_api_objectSimple_at = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: IBox3D; - }> = await api.functional.objectSimple.at(connection, typia.random()); + const output: IPropagation< + { + 200: IBox3D; + }, + 200 + > = await api.functional.objectSimple.at(connection, typia.random()); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_index.ts index fe5482f84..a6607f478 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_index.ts @@ -7,8 +7,11 @@ import type { IBox3D } from "../../../../api/structures/IBox3D"; export const test_api_objectSimple_index = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: IBox3D[]; - }> = await api.functional.objectSimple.index(connection); + const output: IPropagation< + { + 200: IBox3D[]; + }, + 200 + > = await api.functional.objectSimple.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_store.ts index 524a7f2a0..0f55c1361 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectSimple_store.ts @@ -7,9 +7,12 @@ import type { IBox3D } from "../../../../api/structures/IBox3D"; export const test_api_objectSimple_store = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: IBox3D; - }> = await api.functional.objectSimple.store( + const output: IPropagation< + { + 201: IBox3D; + }, + 201 + > = await api.functional.objectSimple.store( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionExplicit_get.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionExplicit_get.ts index fbac2dcde..146c0d920 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionExplicit_get.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionExplicit_get.ts @@ -2,13 +2,30 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { ObjectUnionExplicit } from "../../../../api/structures/ObjectUnionExplicit"; +import type { DiscriminatorcircleICircle } from "../../../../api/structures/DiscriminatorcircleICircle"; +import type { DiscriminatorlineILine } from "../../../../api/structures/DiscriminatorlineILine"; +import type { DiscriminatorpointIPoint } from "../../../../api/structures/DiscriminatorpointIPoint"; +import type { DiscriminatorpolygonIPolygon } from "../../../../api/structures/DiscriminatorpolygonIPolygon"; +import type { DiscriminatorpolylineIPolyline } from "../../../../api/structures/DiscriminatorpolylineIPolyline"; +import type { DiscriminatorrectangleIRectangle } from "../../../../api/structures/DiscriminatorrectangleIRectangle"; +import type { DiscriminatortriangleITriangle } from "../../../../api/structures/DiscriminatortriangleITriangle"; export const test_api_objectUnionExplicit_get = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ObjectUnionExplicit; - }> = await api.functional.objectUnionExplicit.get(connection); + const output: IPropagation< + { + 200: ( + | DiscriminatorpointIPoint + | DiscriminatorlineILine + | DiscriminatortriangleITriangle + | DiscriminatorrectangleIRectangle + | DiscriminatorpolylineIPolyline + | DiscriminatorpolygonIPolygon + | DiscriminatorcircleICircle + )[]; + }, + 200 + > = await api.functional.objectUnionExplicit.get(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionImplicitControllere_get.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionImplicitControllere_get.ts index 9041cbf88..8f68313d6 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionImplicitControllere_get.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_objectUnionImplicitControllere_get.ts @@ -2,13 +2,30 @@ import type { IPropagation } from "@nestia/fetcher"; import typia from "typia"; import api from "../../../../api"; -import type { ObjectUnionImplicit } from "../../../../api/structures/ObjectUnionImplicit"; +import type { ICircle } from "../../../../api/structures/ICircle"; +import type { ILine } from "../../../../api/structures/ILine"; +import type { IPoint } from "../../../../api/structures/IPoint"; +import type { IPolygon } from "../../../../api/structures/IPolygon"; +import type { IPolyline } from "../../../../api/structures/IPolyline"; +import type { IRectangle } from "../../../../api/structures/IRectangle"; +import type { ITriangle } from "../../../../api/structures/ITriangle"; export const test_api_objectUnionImplicitControllere_get = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: ObjectUnionImplicit; - }> = await api.functional.objectUnionImplicitControllere.get(connection); + const output: IPropagation< + { + 200: ( + | IPoint.o1 + | ILine + | ITriangle + | IRectangle + | IPolyline.o1 + | IPolygon + | ICircle + )[]; + }, + 200 + > = await api.functional.objectUnionImplicitControllere.get(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_cpu.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_cpu.ts index bfb871c53..126ee9de4 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_cpu.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_cpu.ts @@ -5,8 +5,11 @@ import api from "../../../../api"; import type { process } from "../../../../api/structures/process"; export const test_api_performance_cpu = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: process.global.NodeJS.CpuUsage; - }> = await api.functional.performance.cpu(connection); + const output: IPropagation< + { + 200: process.global.NodeJS.CpuUsage; + }, + 200 + > = await api.functional.performance.cpu(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_memory.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_memory.ts index 09b029f66..1742cd780 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_memory.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_memory.ts @@ -7,8 +7,11 @@ import type { process } from "../../../../api/structures/process"; export const test_api_performance_memory = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: process.global.NodeJS.MemoryUsage; - }> = await api.functional.performance.memory(connection); + const output: IPropagation< + { + 200: process.global.NodeJS.MemoryUsage; + }, + 200 + > = await api.functional.performance.memory(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_resource.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_resource.ts index 54cbb5045..0171322d2 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_resource.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_performance_resource.ts @@ -7,8 +7,11 @@ import type { process } from "../../../../api/structures/process"; export const test_api_performance_resource = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: process.global.NodeJS.ResourceUsage; - }> = await api.functional.performance.resource(connection); + const output: IPropagation< + { + 200: process.global.NodeJS.ResourceUsage; + }, + 200 + > = await api.functional.performance.resource(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_exit.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_exit.ts index a3a271fe5..938f17bc9 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_exit.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_exit.ts @@ -6,8 +6,11 @@ import api from "../../../../api"; export const test_api_sellers_authenticate_exit = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: undefined; - }> = await api.functional.sellers.authenticate.exit(connection); + const output: IPropagation< + { + 200: void; + }, + 200 + > = await api.functional.sellers.authenticate.exit(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_join.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_join.ts index 4ac75a1b5..c9c13208c 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_join.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_join.ts @@ -7,9 +7,12 @@ import type { ISeller } from "../../../../api/structures/ISeller"; export const test_api_sellers_authenticate_join = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: ISeller.IAuthorized; - }> = await api.functional.sellers.authenticate.join( + const output: IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > = await api.functional.sellers.authenticate.join( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_login.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_login.ts index 449eaf51f..9402f7bc9 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_login.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_login.ts @@ -7,9 +7,12 @@ import type { ISeller } from "../../../../api/structures/ISeller"; export const test_api_sellers_authenticate_login = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 201: ISeller.IAuthorized; - }> = await api.functional.sellers.authenticate.login( + const output: IPropagation< + { + 201: ISeller.IAuthorized; + }, + 201 + > = await api.functional.sellers.authenticate.login( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_password_change.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_password_change.ts index 8c404ae3f..39f11cc4b 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_password_change.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_sellers_authenticate_password_change.ts @@ -7,9 +7,12 @@ import type { ISeller } from "../../../../api/structures/ISeller"; export const test_api_sellers_authenticate_password_change = async ( connection: api.IConnection, ) => { - const output: IPropagation<{ - 200: undefined; - }> = await api.functional.sellers.authenticate.password.change( + const output: IPropagation< + { + 200: void; + }, + 200 + > = await api.functional.sellers.authenticate.password.change( connection, typia.random(), ); diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_at.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_at.ts index 6f1c32c37..bc3350a0c 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_at.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_at.ts @@ -5,8 +5,11 @@ import api from "../../../../api"; import type { Template } from "../../../../api/structures/Template"; export const test_api_template_at = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: Template; - }> = await api.functional.template.at(connection, typia.random()); + const output: IPropagation< + { + 200: Template; + }, + 200 + > = await api.functional.template.at(connection, typia.random()); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_index.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_index.ts index 979d63796..f9c584e37 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_index.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_index.ts @@ -5,8 +5,11 @@ import api from "../../../../api"; import type { Template } from "../../../../api/structures/Template"; export const test_api_template_index = async (connection: api.IConnection) => { - const output: IPropagation<{ - 200: Template[]; - }> = await api.functional.template.index(connection); + const output: IPropagation< + { + 200: Template[]; + }, + 200 + > = await api.functional.template.index(connection); typia.assert(output); }; diff --git a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_store.ts b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_store.ts index f2192ba09..5f05134e7 100644 --- a/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_store.ts +++ b/test/features/clone-and-propagate/src/test/features/api/automated/test_api_template_store.ts @@ -5,11 +5,11 @@ import api from "../../../../api"; import type { Template } from "../../../../api/structures/Template"; export const test_api_template_store = async (connection: api.IConnection) => { - const output: IPropagation<{ - 201: Template; - }> = await api.functional.template.store( - connection, - typia.random