Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@TypedException() decorator for swagger documents #548

Merged
merged 4 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/core": "^1.6.3",
"typia": "^4.2.1"
"@nestia/core": "^1.6.4",
"typia": "^4.2.3"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"inquirer": "^8.2.5"
},
"devDependencies": {
"@nestia/core": "^1.6.3",
"@nestia/sdk": "^1.6.3",
"@nestia/core": "^1.6.4",
"@nestia/sdk": "^1.6.4",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/inquirer": "^9.0.3",
"@types/node": "^18.11.16",
Expand Down
10 changes: 5 additions & 5 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "1.6.3",
"version": "1.6.4",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -34,7 +34,7 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/fetcher": "^1.6.3",
"@nestia/fetcher": "^1.6.4",
"@nestjs/common": ">= 7.0.1",
"@nestjs/core": ">= 7.0.1",
"@nestjs/platform-express": ">= 7.0.1",
Expand All @@ -44,10 +44,10 @@
"raw-body": ">= 2.0.0",
"reflect-metadata": ">= 0.1.12",
"rxjs": ">= 6.0.0",
"typia": "^4.2.1"
"typia": "^4.2.3"
},
"peerDependencies": {
"@nestia/fetcher": ">= 1.6.3",
"@nestia/fetcher": ">= 1.6.4",
"@nestjs/common": ">= 7.0.1",
"@nestjs/core": ">= 7.0.1",
"@nestjs/platform-express": ">= 7.0.1",
Expand All @@ -56,7 +56,7 @@
"reflect-metadata": ">= 0.1.12",
"rxjs": ">= 6.0.0",
"typescript": ">= 4.8.0",
"typia": ">= 4.2.1"
"typia": ">= 4.2.3"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
Expand Down
90 changes: 90 additions & 0 deletions packages/core/src/decorators/TypedException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import "reflect-metadata";

/**
* > You must configure the generic argument `T`
*
* Exception decorator.
*
* `TypedException` is a decorator function describing HTTP exception and its type
* which could be occured in the method.
*
* For reference, this decorator function does not affect to the method's behavior,
* but only affects to the swagger documents generation. Also, it does not affect to
* the SDK library generation yet, but will be used in the future.
*
* @param status Status number or pattern like "2XX", "3XX", "4XX", "5XX"
* @param description Description about the exception
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
* @deprecated
*/
export function TypedException(
status: number | "2XX" | "3XX" | "4XX" | "5XX",
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 status Status number or pattern like "2XX", "3XX", "4XX", "5XX"
* @param description Description about the exception
* @returns Method decorator
*
* @author Jeongho Nam - https://github.com/samchon
*/
export function TypedException<T extends object>(
status: number | "2XX" | "3XX" | "4XX" | "5XX",
description?: string | undefined,
): MethodDecorator;

/**
* @internal
*/
export function TypedException<T extends object>(
status: number | "2XX" | "3XX" | "4XX" | "5XX",
description?: string | undefined,
type?: string | undefined,
): MethodDecorator {
return function TypedException(
target: Object | T,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<any>,
) {
const array: IProps[] = (() => {
const oldbie: IProps[] | undefined = Reflect.getMetadata(
`swagger/TypedException`,
(target as any)[propertyKey],
);
if (oldbie !== undefined) return oldbie;

const newbie: IProps[] = [];
Reflect.defineMetadata(
`swagger/TypedException`,
newbie,
(target as any)[propertyKey],
);
return newbie;
})();
array.push({
status,
description,
type: type!,
});
return descriptor;
};
}

interface IProps {
status: number | "2XX" | "3XX" | "4XX" | "5XX";
description?: string | undefined;
type: string;
}
1 change: 1 addition & 0 deletions packages/core/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./decorators/EncryptedRoute";
export * from "./utils/ExceptionManager";
export * from "./decorators/PlainBody";
export * from "./decorators/TypedBody";
export * from "./decorators/TypedException";
export * from "./decorators/TypedHeaders";
export * from "./decorators/TypedParam";
export * from "./decorators/TypedRoute";
Expand Down
40 changes: 40 additions & 0 deletions packages/core/src/programmers/TypedExceptionProgrammer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import ts from "typescript";

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 new Error(NOT_SPECIFIED);

// GET TYPE INFO
const node: ts.TypeNode = expression.typeArguments[0];
const type: ts.Type = checker.getTypeFromTypeNode(node);

if (type.isTypeParameter()) throw new Error(NO_GENERIC_ARGUMENT);

// CHECK DUPLICATED TRNASFORMATION
if (expression.arguments.length === 3) return expression;

// DO TRANSFORM
const name: string = node.getFullText().trim();
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.";
20 changes: 9 additions & 11 deletions packages/core/src/transformers/MethodTransformer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ts from "typescript";

import { INestiaTransformProject } from "../options/INestiaTransformProject";
import { MethodDecoratorTransformer } from "./MethodDecoratorTransformer";
import { TypedExceptionTransformer } from "./TypedExceptionTransformer";
import { TypedRouteTransformer } from "./TypedRouteTransformer";

export namespace MethodTransformer {
export const transform =
Expand All @@ -23,15 +24,16 @@ export namespace MethodTransformer {

if (escaped === undefined) return method;

const operator = (deco: ts.Decorator): ts.Decorator => {
deco = TypedExceptionTransformer.transform(project)(deco);
deco = TypedRouteTransformer.transform(project)(escaped)(deco);
return deco;
};
if (ts.getDecorators !== undefined)
return ts.factory.updateMethodDeclaration(
method,
(method.modifiers || []).map((mod) =>
ts.isDecorator(mod)
? MethodDecoratorTransformer.transform(project)(
escaped,
)(mod)
: mod,
ts.isDecorator(mod) ? operator(mod) : mod,
),
method.asteriskToken,
method.name,
Expand All @@ -44,11 +46,7 @@ export namespace MethodTransformer {
// eslint-disable-next-line
return (ts.factory.updateMethodDeclaration as any)(
method,
decorators.map((deco) =>
MethodDecoratorTransformer.transform(project)(escaped)(
deco,
),
),
decorators.map(operator),
(method as any).modifiers,
method.asteriskToken,
method.name,
Expand Down
51 changes: 51 additions & 0 deletions packages/core/src/transformers/TypedExceptionTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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 && location !== SRC_PATH)
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(
"node_modules",
"@nestia",
"core",
"lib",
"decorators",
`TypedException.d.ts`,
);
const SRC_PATH = path.resolve(
path.join(__dirname, "..", "decorators", `TypedException.ts`),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ts from "typescript";
import { INestiaTransformProject } from "../options/INestiaTransformProject";
import { TypedRouteProgrammer } from "../programmers/TypedRouteProgrammer";

export namespace MethodDecoratorTransformer {
export namespace TypedRouteTransformer {
export const transform =
(project: INestiaTransformProject) =>
(type: ts.Type) =>
Expand Down
2 changes: 1 addition & 1 deletion packages/fetcher/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/fetcher",
"version": "1.6.3",
"version": "1.6.4",
"description": "Fetcher library of Nestia SDK",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
7 changes: 6 additions & 1 deletion packages/fetcher/src/HttpError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ export class HttpError extends Error {
* @returns JSON object of the `HttpError`.
*/
public toJSON<T>(): HttpError.IProps<T> {
if (this.body_ === NOT_YET) this.body_ = JSON.parse(this.message);
if (this.body_ === NOT_YET)
try {
this.body_ = JSON.parse(this.message);
} catch {
this.body_ = this.message;
}
return {
method: this.method,
path: this.path,
Expand Down
2 changes: 1 addition & 1 deletion packages/migrate/assets/input/fireblocks.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.0.0",
"info": {
"title": "Fireblocks API",
"version": "1.6.3",
"version": "1.0.0",
"contact": {
"email": "[email protected]"
}
Expand Down
8 changes: 4 additions & 4 deletions packages/migrate/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/migrate",
"version": "0.2.10",
"version": "0.2.11",
"description": "Migration program from swagger to NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -30,8 +30,8 @@
},
"homepage": "https://github.com/samchon/nestia#readme",
"devDependencies": {
"@nestia/core": "^1.6.3",
"@nestia/fetcher": "^1.6.3",
"@nestia/core": "^1.6.4",
"@nestia/fetcher": "^1.6.4",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/node": "^20.3.3",
"prettier": "^2.8.8",
Expand All @@ -45,7 +45,7 @@
"typescript-transform-paths": "^3.4.6"
},
"dependencies": {
"typia": "^4.2.1"
"typia": "^4.2.3"
},
"files": [
"lib",
Expand Down
Loading
Loading