Skip to content

Commit

Permalink
Merge pull request #351 from samchon/features/date
Browse files Browse the repository at this point in the history
Add "date" type in `@TypedParam`
  • Loading branch information
samchon authored May 23, 2023
2 parents 5746ff2 + fb0e39b commit a21ade0
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "1.2.2",
"version": "1.2.3",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/decorators/TypedParam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { send_bad_request } from "./internal/send_bad_request";
*/
export function TypedParam(
name: string,
type?: "boolean" | "number" | "string" | "uuid",
type?: "boolean" | "number" | "string" | "uuid" | "date",
nullable?: false | true,
): ParameterDecorator {
function TypedParam({}: any, context: ExecutionContext) {
Expand Down Expand Up @@ -73,7 +73,16 @@ export function TypedParam(
),
);
return str;
} else return str;
} else if (type === "date") {
if (DATE_PATTERN.test(str) === false)
return send_bad_request(context)(
new BadRequestException(
`Value of the URL parameter "${name}" is not a valid date.`,
),
);
return str;
}
else return str;
}
(TypedParam as any).nullable = !!nullable;
(TypedParam as any).type = type;
Expand All @@ -82,3 +91,4 @@ export function TypedParam(

const UUID_PATTERN =
/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
3 changes: 2 additions & 1 deletion packages/core/src/programmers/TypedParamProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ const error = (message: string) =>

const equals = (atomic: string, p: Metadata) => {
const name: string = p.getName();
if (atomic === "string") return name === `"string"` || name === `"uuid"`;
if (atomic === "string")
return name === `"string"` || name === `"uuid"` || name === `"date"`;
return `"${atomic}"` === name;
};
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/sdk",
"version": "1.2.7",
"version": "1.2.8",
"description": "Nestia SDK and Swagger generator",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down
12 changes: 11 additions & 1 deletion packages/sdk/src/generates/SwaggerGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ export namespace SwaggerGenerator {

// PREPARE ASSETS
const parsed: NodePath.ParsedPath = NodePath.parse(config.output);
const directory: string = NodePath.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}`,
);

const location: string = !!parsed.ext
? NodePath.resolve(config.output)
: NodePath.join(
Expand Down Expand Up @@ -283,7 +293,7 @@ export namespace SwaggerGenerator {
);
if (schema === null)
throw new Error(
`Error on NestiaApplication.sdk(): invalid parameter type on ${route.symbol}#${parameter.name}`,
`Error on NestiaApplication.swagger(): invalid parameter type on ${route.symbol}#${parameter.name}`,
);

return {
Expand Down
25 changes: 18 additions & 7 deletions packages/sdk/src/generates/internal/E2eFileProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ export namespace E2eFileProgrammer {
for (const instance of tuple[1])
importDict.emplace(tuple[0], false, instance);

const uuid: boolean = route.parameters.some(
(p) => p.category === "param" && p.meta?.type === "uuid",
);
const additional: string[] = [];
for (const param of route.parameters)
if (param.category === "param")
if (param.meta?.type === "uuid") additional.push(UUID);
else if (param.meta?.type === "date") additional.push(DATE);
const content: string = [
...(route.parameters.length || route.output.name !== "void"
? [
Expand All @@ -36,7 +38,7 @@ export namespace E2eFileProgrammer {
: [importDict.toScript(props.current)]),
"",
arrow(config)(route),
...(uuid ? ["", UUID] : []),
...(additional.length ? ["", ...additional] : []),
].join("\n");

await fs.promises.writeFile(
Expand Down Expand Up @@ -78,10 +80,11 @@ export namespace E2eFileProgrammer {
(tab: number) =>
(param: IRoute.IParameter): string => {
const middle: string =
param.category === "param" && param.meta?.type === "uuid"
param.category === "param" &&
(param.meta?.type === "uuid" || param.meta?.type === "date")
? param.meta.nullable
? `Math.random() < .2 ? null : uuid()`
: `uuid()`
? `Math.random() < .2 ? null : ${param.meta.type}()`
: `${param.meta.type}()`
: `typia.random<${primitive(config)(param.type.name)}>()`;
return `${" ".repeat(4 * tab)}${middle},`;
};
Expand Down Expand Up @@ -122,3 +125,11 @@ const UUID = `const uuid = (): string =>
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});`;
const DATE = `const date = (): string => {
const date: Date = new Date(Math.floor(Math.random() * Date.now() * 2));
return [
date.getFullYear(),
(date.getMonth() + 1).toString().padStart(2, "0"),
date.getDate().toString().padStart(2, "0"),
].join("-");
}`;
144 changes: 144 additions & 0 deletions test/features/param/src/api/functional/param/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,148 @@ export namespace literal
{
return `/param/${encodeURIComponent(value ?? "null")}/literal`;
}
}

/**
* @controller TypedParamController.uuid()
* @path GET /param/:value/uuid
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function uuid
(
connection: IConnection,
value: string
): Promise<uuid.Output>
{
return Fetcher.fetch
(
connection,
uuid.ENCRYPTED,
uuid.METHOD,
uuid.path(value)
);
}
export namespace uuid
{
export type Output = Primitive<string>;

export const METHOD = "GET" as const;
export const PATH: string = "/param/:value/uuid";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};

export function path(value: string): string
{
return `/param/${encodeURIComponent(value ?? "null")}/uuid`;
}
}

/**
* @controller TypedParamController.date()
* @path GET /param/:value/date
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function date
(
connection: IConnection,
value: string
): Promise<date.Output>
{
return Fetcher.fetch
(
connection,
date.ENCRYPTED,
date.METHOD,
date.path(value)
);
}
export namespace date
{
export type Output = Primitive<string>;

export const METHOD = "GET" as const;
export const PATH: string = "/param/:value/date";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};

export function path(value: string): string
{
return `/param/${encodeURIComponent(value ?? "null")}/date`;
}
}

/**
* @controller TypedParamController.uuid_nullable()
* @path GET /param/:value/uuid_nullable
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function uuid_nullable
(
connection: IConnection,
value: null | string
): Promise<uuid_nullable.Output>
{
return Fetcher.fetch
(
connection,
uuid_nullable.ENCRYPTED,
uuid_nullable.METHOD,
uuid_nullable.path(value)
);
}
export namespace uuid_nullable
{
export type Output = Primitive<null | string>;

export const METHOD = "GET" as const;
export const PATH: string = "/param/:value/uuid_nullable";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};

export function path(value: null | string): string
{
return `/param/${encodeURIComponent(value ?? "null")}/uuid_nullable`;
}
}

/**
* @controller TypedParamController.date_nullable()
* @path GET /param/:value/date_nullable
* @nestia Generated by Nestia - https://github.com/samchon/nestia
*/
export function date_nullable
(
connection: IConnection,
value: null | string
): Promise<date_nullable.Output>
{
return Fetcher.fetch
(
connection,
date_nullable.ENCRYPTED,
date_nullable.METHOD,
date_nullable.path(value)
);
}
export namespace date_nullable
{
export type Output = Primitive<null | string>;

export const METHOD = "GET" as const;
export const PATH: string = "/param/:value/date_nullable";
export const ENCRYPTED: Fetcher.IEncrypted = {
request: false,
response: false,
};

export function path(value: null | string): string
{
return `/param/${encodeURIComponent(value ?? "null")}/date_nullable`;
}
}
24 changes: 24 additions & 0 deletions test/features/param/src/controllers/TypedParamController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,28 @@ export class TypedParamController {
): "A" | "B" | "C" {
return value;
}

@core.TypedRoute.Get(":value/uuid")
public uuid(@core.TypedParam("value", "uuid") value: string): string {
return value;
}

@core.TypedRoute.Get(":value/date")
public date(@core.TypedParam("value", "date") value: string): string {
return value;
}

@core.TypedRoute.Get(":value/uuid_nullable")
public uuid_nullable(
@core.TypedParam("value", "uuid") value: string | null,
): string | null {
return value;
}

@core.TypedRoute.Get(":value/date_nullable")
public date_nullable(
@core.TypedParam("value", "date") value: string | null,
): string | null {
return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import typia, { Primitive } from "typia";

import api from "./../../../../api";

export const test_api_param_date = async (
connection: api.IConnection
): Promise<void> => {
const output: Primitive<string> =
await api.functional.param.date(
connection,
date(),
);
typia.assert(output);
};

const date = (): string => {
const date: Date = new Date(Math.floor(Math.random() * Date.now() * 2));
return [
date.getFullYear(),
(date.getMonth() + 1).toString().padStart(2, "0"),
date.getDate().toString().padStart(2, "0"),
].join("-");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import typia, { Primitive } from "typia";

import api from "./../../../../api";

export const test_api_param_date_nullable = async (
connection: api.IConnection
): Promise<void> => {
const output: Primitive<null | string> =
await api.functional.param.date_nullable(
connection,
Math.random() < .2 ? null : date(),
);
typia.assert(output);
};

const date = (): string => {
const date: Date = new Date(Math.floor(Math.random() * Date.now() * 2));
return [
date.getFullYear(),
(date.getMonth() + 1).toString().padStart(2, "0"),
date.getDate().toString().padStart(2, "0"),
].join("-");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import typia, { Primitive } from "typia";

import api from "./../../../../api";

export const test_api_param_uuid = async (
connection: api.IConnection
): Promise<void> => {
const output: Primitive<string> =
await api.functional.param.uuid(
connection,
uuid(),
);
typia.assert(output);
};

const uuid = (): string =>
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
Loading

0 comments on commit a21ade0

Please sign in to comment.