Skip to content

Commit

Permalink
Close #914: @TypedParam() to consider validate option.
Browse files Browse the repository at this point in the history
  • Loading branch information
samchon committed Dec 16, 2024
1 parent cdb1019 commit 9855458
Show file tree
Hide file tree
Showing 61 changed files with 1,931 additions and 183 deletions.
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-dev.20241216-3",
"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

0 comments on commit 9855458

Please sign in to comment.