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

Close #914: @TypedParam() to consider validate option. #1157

Merged
merged 2 commits into from
Dec 16, 2024
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@nestia/station",
"version": "4.4.0-dev.20241216",
"version": "4.4.0",
"description": "Nestia station",
"scripts": {
"build": "node deploy build",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "4.4.0-dev.20241216",
"version": "4.4.0-dev.20241216-3",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -52,7 +52,7 @@
"ws": "^7.5.3"
},
"peerDependencies": {
"@nestia/fetcher": ">=4.4.0-dev.20241216",
"@nestia/fetcher": ">=4.4.0-dev.20241216-3",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
21 changes: 16 additions & 5 deletions packages/core/src/decorators/TypedParam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "@nestjs/common";
import type express from "express";
import type { FastifyRequest } from "fastify";
import typia, { TypeGuardError } from "typia";
import typia, { IValidation, TypeGuardError } from "typia";

import { NoTransformConfigurationError } from "./NoTransformConfigurationError";

Expand Down Expand Up @@ -35,6 +35,7 @@ import { NoTransformConfigurationError } from "./NoTransformConfigurationError";
export function TypedParam<T extends boolean | bigint | number | string | null>(
name: string,
assert?: (value: string) => T,
validate?: boolean,
): ParameterDecorator {
if (assert === undefined) {
NoTransformConfigurationError("TypedParam");
Expand All @@ -52,14 +53,24 @@ export function TypedParam<T extends boolean | bigint | number | string | null>(
try {
return assert(str);
} catch (exp) {
if (typia.is<TypeGuardError>(exp))
throw new BadRequestException({
path: exp.path,
reason: exp.message,
if (typia.is<TypeGuardError>(exp)) {
const trace: IValidation.IError = {
path: exp.path ?? "$input",
expected: exp.expected,
value: exp.value,
};
throw new BadRequestException({
message: `Invalid URL parameter value on "${name}".`,
...(validate === true
? {
errors: [trace],
}
: {
...trace,
reason: exp.message,
}),
});
}
throw exp;
}
})(name);
Expand Down
17 changes: 11 additions & 6 deletions packages/core/src/options/INestiaTransformOptions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export interface INestiaTransformOptions {
validate?: // NORMAL
| "assert"
validate?: INestiaTransformOptions.Validate;
stringify?: INestiaTransformOptions.Stringify | null;
throws?: boolean;
}
export namespace INestiaTransformOptions {
export type Validate =
// NORMAL
| "assert"
| "is"
| "validate"
// STRICT
Expand All @@ -13,12 +19,11 @@ export interface INestiaTransformOptions {
// PRUNE
| "assertPrune"
| "validatePrune";
stringify?:

export type Stringify =
| "stringify"
| "assert"
| "is"
| "validate"
| "validate.log"
| null;
throws?: boolean;
| "validate.log";
}
3 changes: 3 additions & 0 deletions packages/core/src/programmers/TypedParamProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export namespace TypedParamProgrammer {
type: props.type,
name: undefined,
}),
...(props.context.options?.validate?.startsWith("validate")
? [ts.factory.createTrue()]
: []),
];
};
}
2 changes: 1 addition & 1 deletion packages/editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/editor",
"version": "4.4.0-dev.20241216",
"version": "4.4.0-dev.20241216-3",
"typings": "lib/index.d.ts",
"main": "lib/index.js",
"module": "lib/index.mjs",
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": "4.4.0-dev.20241216",
"version": "4.4.0-dev.20241216-3",
"description": "Fetcher library of Nestia SDK",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/migrate/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/migrate",
"version": "4.4.0-dev.20241216",
"version": "4.4.0-dev.20241216-3",
"description": "Migration program from swagger to NestJS",
"typings": "lib/index.d.ts",
"main": "lib/index.js",
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/sdk",
"version": "4.4.0-dev.20241216",
"version": "4.4.0-dev.20241216-3",
"description": "Nestia SDK and Swagger generator",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -47,8 +47,8 @@
"typia": "^7.4.0"
},
"peerDependencies": {
"@nestia/core": ">=4.4.0-dev.20241216",
"@nestia/fetcher": ">=4.4.0-dev.20241216",
"@nestia/core": ">=4.4.0-dev.20241216-3",
"@nestia/fetcher": ">=4.4.0-dev.20241216-3",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
16 changes: 16 additions & 0 deletions test/features/param-validate/nestia.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { INestiaConfig } from "@nestia/sdk";

export const NESTIA_CONFIG: INestiaConfig = {
input: ["src/controllers"],
output: "src/api",
swagger: {
beautify: true,
output: "swagger.json",
security: {
bearer: {
type: "apiKey",
},
},
},
};
export default NESTIA_CONFIG;
28 changes: 28 additions & 0 deletions test/features/param-validate/src/Backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import core from "@nestia/core";
import { INestApplication } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";

export class Backend {
private application_?: INestApplication;

public async open(): Promise<void> {
this.application_ = await NestFactory.create(
await core.EncryptedModule.dynamic(__dirname + "/controllers", {
key: "A".repeat(32),
iv: "B".repeat(16),
}),
{ logger: false },
);
await core.WebSocketAdaptor.upgrade(this.application_);
await this.application_.listen(37_000);
}

public async close(): Promise<void> {
if (this.application_ === undefined) return;

const app = this.application_;
await app.close();

delete this.application_;
}
}
1 change: 1 addition & 0 deletions test/features/param-validate/src/api/HttpError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { HttpError } from "@nestia/fetcher";
1 change: 1 addition & 0 deletions test/features/param-validate/src/api/IConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { IConnection } from "@nestia/fetcher";
1 change: 1 addition & 0 deletions test/features/param-validate/src/api/Primitive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { Primitive } from "typia";
1 change: 1 addition & 0 deletions test/features/param-validate/src/api/Resolved.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { Resolved } from "typia";
51 changes: 51 additions & 0 deletions test/features/param-validate/src/api/functional/calculate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @packageDocumentation
* @module api.functional.calculate
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
//================================================================
import type { IConnection } from "@nestia/fetcher";
import { WebSocketConnector } from "tgrid";
import type { Driver } from "tgrid";
import type { Format } from "typia/lib/tags/Format";

import type { ICalculator } from "../../structures/ICalculator";
import type { IListener } from "../../structures/IListener";
import type { IPrecision } from "../../structures/IPrecision";

/**
* @controller CalculateController.connect
* @path /calculate/:id
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export async function connect(
connection: IConnection<connect.Header>,
id: string & Format<"uuid">,
provider: connect.Provider,
): Promise<connect.Output> {
const connector: WebSocketConnector<
connect.Header,
connect.Provider,
connect.Listener
> = new WebSocketConnector(connection.headers ?? ({} as any), provider);
await connector.connect(
`${connection.host.endsWith("/") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${connect.path(id)}`,
);
const driver: Driver<connect.Listener> = connector.getDriver();
return {
connector,
driver,
};
}
export namespace connect {
export type Output = {
connector: WebSocketConnector<Header, Provider, Listener>;
driver: Driver<Listener>;
};
export type Header = IPrecision;
export type Provider = IListener;
export type Listener = ICalculator;

export const path = (id: string & Format<"uuid">) =>
`/calculate/${encodeURIComponent(id?.toString() ?? "null")}`;
}
35 changes: 35 additions & 0 deletions test/features/param-validate/src/api/functional/health/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @packageDocumentation
* @module api.functional.health
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
//================================================================
import type { IConnection } from "@nestia/fetcher";
import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher";

/**
* @controller HealthController.get
* @path GET /health
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export async function get(connection: IConnection): Promise<void> {
return PlainFetcher.fetch(connection, {
...get.METADATA,
template: get.METADATA.path,
path: get.path(),
});
}
export namespace get {
export const METADATA = {
method: "GET",
path: "/health",
request: null,
response: {
type: "application/json",
encrypted: false,
},
status: 200,
} as const;

export const path = () => "/health";
}
10 changes: 10 additions & 0 deletions test/features/param-validate/src/api/functional/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @packageDocumentation
* @module api.functional
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
//================================================================
export * as calculate from "./calculate";
export * as health from "./health";
export * as performance from "./performance";
export * as param from "./param";
Loading
Loading