Skip to content

Commit

Permalink
Merge pull request #548 from samchon/features/TypedException
Browse files Browse the repository at this point in the history
`@TypedException()` decorator for swagger documents
  • Loading branch information
samchon authored Aug 16, 2023
2 parents a11e2db + 1567107 commit be5066c
Show file tree
Hide file tree
Showing 56 changed files with 2,268 additions and 153 deletions.
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

0 comments on commit be5066c

Please sign in to comment.