From a36194c50a587ae9c853a3cc636156b713f12d8d Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 4 Dec 2024 00:53:07 +0900 Subject: [PATCH] Fix `@WebSocketRoute.Header()` blocks SDK library generation. --- benchmark/package.json | 2 +- package.json | 2 +- packages/benchmark/package.json | 4 +- packages/core/README.md | 2 +- packages/core/package.json | 8 +- packages/fetcher/README.md | 2 +- packages/fetcher/package.json | 2 +- packages/migrate/package.json | 2 +- packages/sdk/README.md | 2 +- packages/sdk/package.json | 12 +- .../ReflectWebSocketOperationAnalyzer.ts | 11 +- .../src/api/functional/calculate/index.ts | 186 ++++++++++++++++-- .../src/api/interfaces/ICalcConfig.ts | 3 + .../src/api/interfaces/ICalcEvent.ts | 5 + .../src/api/interfaces/ICalcEventListener.ts | 5 + .../api/interfaces/ICompositeCalculator.ts | 8 + .../api/interfaces/IScientificCalculator.ts | 5 + .../src/api/interfaces/ISimpleCalculator.ts | 6 + .../api/interfaces/IStatisticsCalculator.ts | 4 + .../src/api/structures/ICalculator.ts | 6 - .../websocket/src/api/structures/IListener.ts | 11 -- .../src/api/structures/IPrecision.ts | 3 - .../controllers/CalculateControllerBase.ts | 135 ++++++++++--- .../websocket/src/providers/CalculatorBase.ts | 18 ++ .../src/providers/CompositeCalculator.ts | 25 +++ .../src/providers/ScientificCalculator.ts | 17 ++ .../src/providers/SimpleCalculator.ts | 20 ++ .../src/providers/StatisticsCalculator.ts | 17 ++ .../test/features/api/test_api_calculate.ts | 51 ----- .../features/api/test_api_calculate_simple.ts | 56 ++++++ test/package.json | 8 +- 31 files changed, 489 insertions(+), 149 deletions(-) create mode 100644 test/features/websocket/src/api/interfaces/ICalcConfig.ts create mode 100644 test/features/websocket/src/api/interfaces/ICalcEvent.ts create mode 100644 test/features/websocket/src/api/interfaces/ICalcEventListener.ts create mode 100644 test/features/websocket/src/api/interfaces/ICompositeCalculator.ts create mode 100644 test/features/websocket/src/api/interfaces/IScientificCalculator.ts create mode 100644 test/features/websocket/src/api/interfaces/ISimpleCalculator.ts create mode 100644 test/features/websocket/src/api/interfaces/IStatisticsCalculator.ts delete mode 100644 test/features/websocket/src/api/structures/ICalculator.ts delete mode 100644 test/features/websocket/src/api/structures/IListener.ts delete mode 100644 test/features/websocket/src/api/structures/IPrecision.ts create mode 100644 test/features/websocket/src/providers/CalculatorBase.ts create mode 100644 test/features/websocket/src/providers/CompositeCalculator.ts create mode 100644 test/features/websocket/src/providers/ScientificCalculator.ts create mode 100644 test/features/websocket/src/providers/SimpleCalculator.ts create mode 100644 test/features/websocket/src/providers/StatisticsCalculator.ts delete mode 100644 test/features/websocket/src/test/features/api/test_api_calculate.ts create mode 100644 test/features/websocket/src/test/features/api/test_api_calculate_simple.ts diff --git a/benchmark/package.json b/benchmark/package.json index bf98e52ab..1d2d54738 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -40,7 +40,7 @@ "physical-cpu-count": "^2.0.0", "randexp": "^0.5.3", "reflect-metadata": "^0.2.2", - "tgrid": "^1.0.3", + "tgrid": "^1.1.0", "tstl": "^3.0.0", "typia": "^7.0.1" }, diff --git a/package.json b/package.json index 72a32409a..4a4911cc9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@nestia/station", - "version": "4.0.1", + "version": "4.0.2", "description": "Nestia station", "scripts": { "build": "node build/index.js", diff --git a/packages/benchmark/package.json b/packages/benchmark/package.json index 9bfa46fb9..107351372 100644 --- a/packages/benchmark/package.json +++ b/packages/benchmark/package.json @@ -38,8 +38,8 @@ "uuid": "^10.0.0" }, "dependencies": { - "@nestia/fetcher": "^4.0.0", - "tgrid": "^1.0.3", + "@nestia/fetcher": "^4.0.1", + "tgrid": "^1.1.0", "tstl": "^3.0.0" }, "files": [ diff --git a/packages/core/README.md b/packages/core/README.md index 796a338f1..8e40440ee 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -6,7 +6,7 @@ [![Downloads](https://img.shields.io/npm/dm/@nestia/fetcher.svg)](https://www.npmjs.com/package/@nestia/fetcher) [![Build Status](https://github.com/samchon/nestia/workflows/build/badge.svg)](https://github.com/samchon/nestia/actions?query=workflow%3Abuild) [![Guide Documents](https://img.shields.io/badge/guide-documents-forestgreen)](https://nestia.io/docs/) -[![Discord Badge](https://img.shields.io/badge/discord-NestJS/Nestia-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.com/channels/520622812742811698/1197293125434093701)](https://discord.com/channels/520622812742811698/1181877086797967420) +[![Discord Badge](https://img.shields.io/badge/discord-samchon-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.gg/E94XhzrUCZ)](https://discord.gg/E94XhzrUCZ) Nestia is a set of helper libraries for NestJS, supporting below features: diff --git a/packages/core/package.json b/packages/core/package.json index 5ba8225e3..6b79d1206 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/core", - "version": "4.0.1", + "version": "4.0.2", "description": "Super-fast validation decorators of NestJS", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -36,7 +36,7 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/fetcher": "^4.0.1", + "@nestia/fetcher": "^4.0.2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "@samchon/openapi": "^2.0.0", @@ -47,12 +47,12 @@ "raw-body": "^2.0.0", "reflect-metadata": ">=0.1.12", "rxjs": ">=6.0.3", - "tgrid": "^1.0.0", + "tgrid": "^1.1.0", "typia": "^7.0.1", "ws": "^7.5.3" }, "peerDependencies": { - "@nestia/fetcher": ">=4.0.1", + "@nestia/fetcher": ">=4.0.2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", diff --git a/packages/fetcher/README.md b/packages/fetcher/README.md index 796a338f1..8e40440ee 100644 --- a/packages/fetcher/README.md +++ b/packages/fetcher/README.md @@ -6,7 +6,7 @@ [![Downloads](https://img.shields.io/npm/dm/@nestia/fetcher.svg)](https://www.npmjs.com/package/@nestia/fetcher) [![Build Status](https://github.com/samchon/nestia/workflows/build/badge.svg)](https://github.com/samchon/nestia/actions?query=workflow%3Abuild) [![Guide Documents](https://img.shields.io/badge/guide-documents-forestgreen)](https://nestia.io/docs/) -[![Discord Badge](https://img.shields.io/badge/discord-NestJS/Nestia-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.com/channels/520622812742811698/1197293125434093701)](https://discord.com/channels/520622812742811698/1181877086797967420) +[![Discord Badge](https://img.shields.io/badge/discord-samchon-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.gg/E94XhzrUCZ)](https://discord.gg/E94XhzrUCZ) Nestia is a set of helper libraries for NestJS, supporting below features: diff --git a/packages/fetcher/package.json b/packages/fetcher/package.json index dcbaa6f9f..a9bd50799 100644 --- a/packages/fetcher/package.json +++ b/packages/fetcher/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/fetcher", - "version": "4.0.1", + "version": "4.0.2", "description": "Fetcher library of Nestia SDK", "main": "lib/index.js", "typings": "lib/index.d.ts", diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 187128939..0694feb02 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -65,7 +65,7 @@ "serialize-error": "^4.1.0", "source-map-support": "^0.5.21", "swagger-ui-express": "^5.0.0", - "tgrid": "^1.0.3", + "tgrid": "^1.1.0", "ts-node": "^10.9.1", "ts-patch": "^3.2.1", "typescript-transform-paths": "^3.5.2" diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 796a338f1..8e40440ee 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -6,7 +6,7 @@ [![Downloads](https://img.shields.io/npm/dm/@nestia/fetcher.svg)](https://www.npmjs.com/package/@nestia/fetcher) [![Build Status](https://github.com/samchon/nestia/workflows/build/badge.svg)](https://github.com/samchon/nestia/actions?query=workflow%3Abuild) [![Guide Documents](https://img.shields.io/badge/guide-documents-forestgreen)](https://nestia.io/docs/) -[![Discord Badge](https://img.shields.io/badge/discord-NestJS/Nestia-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.com/channels/520622812742811698/1197293125434093701)](https://discord.com/channels/520622812742811698/1181877086797967420) +[![Discord Badge](https://img.shields.io/badge/discord-samchon-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.gg/E94XhzrUCZ)](https://discord.gg/E94XhzrUCZ) Nestia is a set of helper libraries for NestJS, supporting below features: diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 3e728407a..21c3b5fe2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@nestia/sdk", - "version": "4.0.1", + "version": "4.0.2", "description": "Nestia SDK and Swagger generator", "main": "lib/index.js", "typings": "lib/index.d.ts", @@ -32,8 +32,8 @@ }, "homepage": "https://nestia.io", "dependencies": { - "@nestia/core": "^4.0.1", - "@nestia/fetcher": "^4.0.1", + "@nestia/core": "^4.0.2", + "@nestia/fetcher": "^4.0.2", "@samchon/openapi": "^2.0.0", "cli": "^1.0.1", "get-function-location": "^2.0.0", @@ -47,8 +47,8 @@ "typia": "^7.0.1" }, "peerDependencies": { - "@nestia/core": ">=4.0.1", - "@nestia/fetcher": ">=4.0.1", + "@nestia/core": ">=4.0.2", + "@nestia/fetcher": ">=4.0.2", "@nestjs/common": ">=7.0.1", "@nestjs/core": ">=7.0.1", "reflect-metadata": ">=0.1.12", @@ -68,7 +68,7 @@ "eslint": "^8.29.0", "eslint-plugin-deprecation": "^1.4.1", "rimraf": "^3.0.2", - "tgrid": "^1.0.3", + "tgrid": "^1.1.0", "ts-patch": "^3.2.1", "typescript": "~5.6.3", "typescript-transform-paths": "^3.4.4", diff --git a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts index 8920503cd..c3a8cedb1 100644 --- a/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts +++ b/packages/sdk/src/analyses/ReflectWebSocketOperationAnalyzer.ts @@ -116,11 +116,14 @@ export namespace ReflectWebSocketOperationAnalyzer { description: matched.description, jsDocTags: matched.jsDocTags, } satisfies IReflectWebSocketOperationParameter.IParam; - // UNKNOWN TYPE, MAYBE NEW FEATURE - return errors.push( - `@WebSocketRoute.${StringUtil.capitalize(p.category)}() has not been supported yet. How about upgrading the nestia packages?`, - ); + else { + if (p.category !== "header") + errors.push( + `@WebSocketRoute.${StringUtil.capitalize(p.category)}() has not been supported yet. How about upgrading the nestia packages?`, + ); + return null; + } }) .filter((p): p is IReflectWebSocketOperationParameter => !!p); diff --git a/test/features/websocket/src/api/functional/calculate/index.ts b/test/features/websocket/src/api/functional/calculate/index.ts index 62e29dbe2..0305ce92a 100644 --- a/test/features/websocket/src/api/functional/calculate/index.ts +++ b/test/features/websocket/src/api/functional/calculate/index.ts @@ -5,44 +5,192 @@ */ //================================================================ import type { IConnection } from "@nestia/fetcher"; +import { PlainFetcher } from "@nestia/fetcher/lib/PlainFetcher"; import { WebSocketConnector } from "tgrid"; import type { Driver } from "tgrid"; +import type { Primitive } from "typia"; -import type { ICalculator } from "../../structures/ICalculator"; -import type { IListener } from "../../structures/IListener"; -import type { IPrecision } from "../../structures/IPrecision"; +import type { ICalcConfig } from "../../interfaces/ICalcConfig"; +import type { ICalcEventListener } from "../../interfaces/ICalcEventListener"; +import type { ICompositeCalculator } from "../../interfaces/ICompositeCalculator"; +import type { IScientificCalculator } from "../../interfaces/IScientificCalculator"; +import type { ISimpleCalculator } from "../../interfaces/ISimpleCalculator"; +import type { IStatisticsCalculator } from "../../interfaces/IStatisticsCalculator"; /** - * @controller CalculateController.connect - * @path /calculate + * Health check API (HTTP GET). + * @controller CalculateController.health + * @path GET /calculate/health * @nestia Generated by Nestia - https://github.com/samchon/nestia */ -export async function connect( - connection: IConnection, - provider: connect.Provider, -): Promise { +export async function health(connection: IConnection): Promise { + return PlainFetcher.fetch(connection, { + ...health.METADATA, + template: health.METADATA.path, + path: health.path(), + }); +} +export namespace health { + export type Output = Primitive; + + export const METADATA = { + method: "GET", + path: "/calculate/health", + request: null, + response: { + type: "application/json", + encrypted: false, + }, + status: 200, + } as const; + + export const path = () => "/calculate/health"; +} + +/** + * Prepare a composite calculator. + * + * @controller CalculateController.composite + * @path /calculate/composite + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function composite( + connection: IConnection, + provider: composite.Provider, +): Promise { + const connector: WebSocketConnector< + composite.Header, + composite.Provider, + composite.Listener + > = new WebSocketConnector(connection.headers ?? ({} as any), provider); + await connector.connect( + `${connection.host.endsWith("/") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${composite.path()}`, + ); + const driver: Driver = connector.getDriver(); + return { + connector, + driver, + }; +} +export namespace composite { + export type Output = { + connector: WebSocketConnector; + driver: Driver; + }; + export type Header = ICalcConfig; + export type Provider = ICalcEventListener; + export type Listener = ICompositeCalculator; + + export const path = () => "/calculate/composite"; +} + +/** + * Prepare a simple calculator. + * + * @controller CalculateController.simple + * @path /calculate/simple + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function simple( + connection: IConnection, + provider: simple.Provider, +): Promise { + const connector: WebSocketConnector< + simple.Header, + simple.Provider, + simple.Listener + > = new WebSocketConnector(connection.headers ?? ({} as any), provider); + await connector.connect( + `${connection.host.endsWith("/") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${simple.path()}`, + ); + const driver: Driver = connector.getDriver(); + return { + connector, + driver, + }; +} +export namespace simple { + export type Output = { + connector: WebSocketConnector; + driver: Driver; + }; + export type Header = ICalcConfig; + export type Provider = ICalcEventListener; + export type Listener = ISimpleCalculator; + + export const path = () => "/calculate/simple"; +} + +/** + * Prepare a scientific calculator. + * + * @controller CalculateController.scientific + * @path /calculate/scientific + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function scientific( + connection: IConnection, + provider: scientific.Provider, +): Promise { + const connector: WebSocketConnector< + scientific.Header, + scientific.Provider, + scientific.Listener + > = new WebSocketConnector(connection.headers ?? ({} as any), provider); + await connector.connect( + `${connection.host.endsWith("/") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${scientific.path()}`, + ); + const driver: Driver = connector.getDriver(); + return { + connector, + driver, + }; +} +export namespace scientific { + export type Output = { + connector: WebSocketConnector; + driver: Driver; + }; + export type Header = ICalcConfig; + export type Provider = ICalcEventListener; + export type Listener = IScientificCalculator; + + export const path = () => "/calculate/scientific"; +} + +/** + * Prepare a statistics calculator. + * + * @controller CalculateController.statistics + * @path /calculate/statistics + * @nestia Generated by Nestia - https://github.com/samchon/nestia + */ +export async function statistics( + connection: IConnection, + provider: statistics.Provider, +): Promise { const connector: WebSocketConnector< - connect.Header, - connect.Provider, - connect.Listener + statistics.Header, + statistics.Provider, + statistics.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()}`, + `${connection.host.endsWith("/") ? connection.host.substring(0, connection.host.length - 1) : connection.host}${statistics.path()}`, ); - const driver: Driver = connector.getDriver(); + const driver: Driver = connector.getDriver(); return { connector, driver, }; } -export namespace connect { +export namespace statistics { export type Output = { connector: WebSocketConnector; driver: Driver; }; - export type Header = IPrecision; - export type Provider = IListener; - export type Listener = ICalculator; + export type Header = ICalcConfig; + export type Provider = ICalcEventListener; + export type Listener = IStatisticsCalculator; - export const path = () => "/calculate"; + export const path = () => "/calculate/statistics"; } diff --git a/test/features/websocket/src/api/interfaces/ICalcConfig.ts b/test/features/websocket/src/api/interfaces/ICalcConfig.ts new file mode 100644 index 000000000..1f4a038b3 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/ICalcConfig.ts @@ -0,0 +1,3 @@ +export interface ICalcConfig { + precision: number; +} diff --git a/test/features/websocket/src/api/interfaces/ICalcEvent.ts b/test/features/websocket/src/api/interfaces/ICalcEvent.ts new file mode 100644 index 000000000..eb544d761 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/ICalcEvent.ts @@ -0,0 +1,5 @@ +export interface ICalcEvent { + type: string; + input: number[]; + output: number; +} diff --git a/test/features/websocket/src/api/interfaces/ICalcEventListener.ts b/test/features/websocket/src/api/interfaces/ICalcEventListener.ts new file mode 100644 index 000000000..099884c16 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/ICalcEventListener.ts @@ -0,0 +1,5 @@ +import { ICalcEvent } from "./ICalcEvent"; + +export interface ICalcEventListener { + on(event: ICalcEvent): void; +} diff --git a/test/features/websocket/src/api/interfaces/ICompositeCalculator.ts b/test/features/websocket/src/api/interfaces/ICompositeCalculator.ts new file mode 100644 index 000000000..1a14e9ba0 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/ICompositeCalculator.ts @@ -0,0 +1,8 @@ +import { IScientificCalculator } from "./IScientificCalculator"; +import { ISimpleCalculator } from "./ISimpleCalculator"; +import { IStatisticsCalculator } from "./IStatisticsCalculator"; + +export interface ICompositeCalculator extends ISimpleCalculator { + scientific: IScientificCalculator; + statistics: IStatisticsCalculator; +} diff --git a/test/features/websocket/src/api/interfaces/IScientificCalculator.ts b/test/features/websocket/src/api/interfaces/IScientificCalculator.ts new file mode 100644 index 000000000..36429b886 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/IScientificCalculator.ts @@ -0,0 +1,5 @@ +export interface IScientificCalculator { + pow(x: number, y: number): number; + sqrt(x: number): number; + log(x: number, base: number): number; +} diff --git a/test/features/websocket/src/api/interfaces/ISimpleCalculator.ts b/test/features/websocket/src/api/interfaces/ISimpleCalculator.ts new file mode 100644 index 000000000..0f1a17c4c --- /dev/null +++ b/test/features/websocket/src/api/interfaces/ISimpleCalculator.ts @@ -0,0 +1,6 @@ +export interface ISimpleCalculator { + plus(x: number, y: number): number; + minus(x: number, y: number): number; + multiplies(x: number, y: number): number; + divides(x: number, y: number): number; +} diff --git a/test/features/websocket/src/api/interfaces/IStatisticsCalculator.ts b/test/features/websocket/src/api/interfaces/IStatisticsCalculator.ts new file mode 100644 index 000000000..9a111ff42 --- /dev/null +++ b/test/features/websocket/src/api/interfaces/IStatisticsCalculator.ts @@ -0,0 +1,4 @@ +export interface IStatisticsCalculator { + mean(...values: number[]): number; + stdev(...values: number[]): number; +} diff --git a/test/features/websocket/src/api/structures/ICalculator.ts b/test/features/websocket/src/api/structures/ICalculator.ts deleted file mode 100644 index b949326c5..000000000 --- a/test/features/websocket/src/api/structures/ICalculator.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ICalculator { - plus(x: number, y: number): number; - minus(x: number, y: number): number; - multiply(x: number, y: number): number; - divide(x: number, y: number): number; -} diff --git a/test/features/websocket/src/api/structures/IListener.ts b/test/features/websocket/src/api/structures/IListener.ts deleted file mode 100644 index 27bf00791..000000000 --- a/test/features/websocket/src/api/structures/IListener.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface IListener { - on(event: IListener.IEvent): void; -} -export namespace IListener { - export interface IEvent { - operator: "plus" | "minus" | "multiply" | "divide"; - x: number; - y: number; - z: number; - } -} diff --git a/test/features/websocket/src/api/structures/IPrecision.ts b/test/features/websocket/src/api/structures/IPrecision.ts deleted file mode 100644 index 5fbe8947d..000000000 --- a/test/features/websocket/src/api/structures/IPrecision.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IPrecision { - value: number; -} diff --git a/test/features/websocket/src/controllers/CalculateControllerBase.ts b/test/features/websocket/src/controllers/CalculateControllerBase.ts index 317cee32b..c019c2e13 100644 --- a/test/features/websocket/src/controllers/CalculateControllerBase.ts +++ b/test/features/websocket/src/controllers/CalculateControllerBase.ts @@ -1,43 +1,114 @@ -import core from "@nestia/core"; +import core, { TypedRoute, WebSocketRoute } from "@nestia/core"; import { Controller } from "@nestjs/common"; import { Driver, WebSocketAcceptor } from "tgrid"; -import { ICalculator } from "@api/lib/structures/ICalculator"; -import { IListener } from "@api/lib/structures/IListener"; -import { IPrecision } from "@api/lib/structures/IPrecision"; +import { ICalcConfig } from "@api/lib/interfaces/ICalcConfig"; +import { ICalcEventListener } from "@api/lib/interfaces/ICalcEventListener"; +import { ICompositeCalculator } from "@api/lib/interfaces/ICompositeCalculator"; +import { IScientificCalculator } from "@api/lib/interfaces/IScientificCalculator"; +import { ISimpleCalculator } from "@api/lib/interfaces/ISimpleCalculator"; +import { IStatisticsCalculator } from "@api/lib/interfaces/IStatisticsCalculator"; + +import { CompositeCalculator } from "../providers/CompositeCalculator"; +import { ScientificCalculator } from "../providers/ScientificCalculator"; +import { SimpleCalculator } from "../providers/SimpleCalculator"; +import { StatisticsCalculator } from "../providers/StatisticsCalculator"; export function CalculateControllerBase(path: string) { @Controller(path) abstract class CalculateControllerBase { - @core.WebSocketRoute() - public async connect( - @core.WebSocketRoute.Acceptor() - acceptor: WebSocketAcceptor, - @core.WebSocketRoute.Driver() - driver: Driver, + /** + * Health check API (HTTP GET). + */ + @TypedRoute.Get("health") + public health(): string { + return "Health check OK"; + } + + /** + * Prepare a composite calculator. + */ + @WebSocketRoute("composite") + public async composite( + @WebSocketRoute.Acceptor() + acceptor: WebSocketAcceptor< + ICalcConfig, + ICompositeCalculator, + ICalcEventListener + >, + @WebSocketRoute.Header() header: ICalcConfig, + @WebSocketRoute.Driver() listener: Driver, + ): Promise { + const provider: CompositeCalculator = new CompositeCalculator( + header, + listener, + ); + await acceptor.accept(provider); + acceptor.ping(15_000); + } + + /** + * Prepare a simple calculator. + */ + @WebSocketRoute("simple") + public async simple( + @WebSocketRoute.Acceptor() + acceptor: WebSocketAcceptor< + ICalcConfig, // header + ISimpleCalculator, // provider for remote client + ICalcEventListener // provider from remote client + >, + ): Promise { + const header: ICalcConfig = acceptor.header; + const listener: Driver = acceptor.getDriver(); + const provider: SimpleCalculator = new SimpleCalculator(header, listener); + + await acceptor.accept(provider); + acceptor.ping(15_000); + } + + /** + * Prepare a scientific calculator. + */ + @WebSocketRoute("scientific") + public async scientific( + @WebSocketRoute.Acceptor() + acceptor: WebSocketAcceptor< + ICalcConfig, + IScientificCalculator, + ICalcEventListener + >, + ): Promise { + const header: ICalcConfig = acceptor.header; + const listener: Driver = acceptor.getDriver(); + const provider: ScientificCalculator = new ScientificCalculator( + header, + listener, + ); + await acceptor.accept(provider); + acceptor.ping(15_000); + } + + /** + * Prepare a statistics calculator. + */ + @WebSocketRoute("statistics") + public async statistics( + @WebSocketRoute.Acceptor() + acceptor: WebSocketAcceptor< + ICalcConfig, + IStatisticsCalculator, + ICalcEventListener + >, ): Promise { - await acceptor.accept({ - plus: (x, y) => { - const z: number = x + y; - driver.on({ operator: "plus", x, y, z }).catch(() => {}); - return z; - }, - minus: (x, y) => { - const z: number = x - y; - driver.on({ operator: "minus", x, y, z }).catch(() => {}); - return z; - }, - multiply: (x, y) => { - const z: number = x * y; - driver.on({ operator: "multiply", x, y, z }).catch(() => {}); - return z; - }, - divide: (x, y) => { - const z: number = x / y; - driver.on({ operator: "divide", x, y, z }).catch(() => {}); - return z; - }, - }); + const header: ICalcConfig = acceptor.header; + const listener: Driver = acceptor.getDriver(); + const provider: IStatisticsCalculator = new StatisticsCalculator( + header, + listener, + ); + await acceptor.accept(provider); + acceptor.ping(15_000); } } return CalculateControllerBase; diff --git a/test/features/websocket/src/providers/CalculatorBase.ts b/test/features/websocket/src/providers/CalculatorBase.ts new file mode 100644 index 000000000..83b8b7299 --- /dev/null +++ b/test/features/websocket/src/providers/CalculatorBase.ts @@ -0,0 +1,18 @@ +import { Driver } from "tgrid"; + +import { ICalcConfig } from "../api/interfaces/ICalcConfig"; +import { ICalcEventListener } from "../api/interfaces/ICalcEventListener"; + +export abstract class CalculatorBase { + public constructor( + private readonly config: ICalcConfig, + private readonly listener: Driver, + ) {} + + protected compute(type: string, input: number[], output: number): number { + const pow: number = Math.pow(10, this.config.precision); + output = Math.round(output * pow) / pow; + this.listener.on({ type, input, output }).catch(() => {}); + return output; + } +} diff --git a/test/features/websocket/src/providers/CompositeCalculator.ts b/test/features/websocket/src/providers/CompositeCalculator.ts new file mode 100644 index 000000000..fb04bca6d --- /dev/null +++ b/test/features/websocket/src/providers/CompositeCalculator.ts @@ -0,0 +1,25 @@ +import { Driver } from "tgrid"; + +import { ICalcConfig } from "../api/interfaces/ICalcConfig"; +import { ICalcEventListener } from "../api/interfaces/ICalcEventListener"; +import { ICompositeCalculator } from "../api/interfaces/ICompositeCalculator"; +import { ScientificCalculator } from "./ScientificCalculator"; +import { SimpleCalculator } from "./SimpleCalculator"; +import { StatisticsCalculator } from "./StatisticsCalculator"; + +export class CompositeCalculator + extends SimpleCalculator + implements ICompositeCalculator +{ + public readonly scientific: ScientificCalculator; + public readonly statistics: StatisticsCalculator; + + public constructor( + config: ICalcConfig, + listener: Driver, + ) { + super(config, listener); + this.scientific = new ScientificCalculator(config, listener); + this.statistics = new StatisticsCalculator(config, listener); + } +} diff --git a/test/features/websocket/src/providers/ScientificCalculator.ts b/test/features/websocket/src/providers/ScientificCalculator.ts new file mode 100644 index 000000000..16eeb6391 --- /dev/null +++ b/test/features/websocket/src/providers/ScientificCalculator.ts @@ -0,0 +1,17 @@ +import { IScientificCalculator } from "../api/interfaces/IScientificCalculator"; +import { CalculatorBase } from "./CalculatorBase"; + +export class ScientificCalculator + extends CalculatorBase + implements IScientificCalculator +{ + public pow(x: number, y: number): number { + return this.compute("pow", [x, y], Math.pow(x, y)); + } + public sqrt(x: number): number { + return this.compute("sqrt", [x], Math.sqrt(x)); + } + public log(x: number, base: number): number { + return this.compute("log", [x, base], Math.log(x) / Math.log(base)); + } +} diff --git a/test/features/websocket/src/providers/SimpleCalculator.ts b/test/features/websocket/src/providers/SimpleCalculator.ts new file mode 100644 index 000000000..fdb2c69d8 --- /dev/null +++ b/test/features/websocket/src/providers/SimpleCalculator.ts @@ -0,0 +1,20 @@ +import { ISimpleCalculator } from "../api/interfaces/ISimpleCalculator"; +import { CalculatorBase } from "./CalculatorBase"; + +export class SimpleCalculator + extends CalculatorBase + implements ISimpleCalculator +{ + public plus(x: number, y: number): number { + return this.compute("plus", [x, y], x + y); + } + public minus(x: number, y: number): number { + return this.compute("minus", [x, y], x - y); + } + public multiplies(x: number, y: number): number { + return this.compute("multiplies", [x, y], x * y); + } + public divides(x: number, y: number): number { + return this.compute("divides", [x, y], x / y); + } +} diff --git a/test/features/websocket/src/providers/StatisticsCalculator.ts b/test/features/websocket/src/providers/StatisticsCalculator.ts new file mode 100644 index 000000000..351d5ad60 --- /dev/null +++ b/test/features/websocket/src/providers/StatisticsCalculator.ts @@ -0,0 +1,17 @@ +import { IStatisticsCalculator } from "../api/interfaces/IStatisticsCalculator"; +import { CalculatorBase } from "./CalculatorBase"; + +export class StatisticsCalculator + extends CalculatorBase + implements IStatisticsCalculator +{ + public mean(...values: number[]): number { + const sum: number = values.reduce((x, y) => x + y); + return this.compute("mean", values, sum / values.length); + } + public stdev(...values: number[]): number { + const mean: number = values.reduce((x, y) => x + y) / values.length; + const sum: number = values.reduce((x, y) => x + Math.pow(y - mean, 2)); + return this.compute("stdev", values, Math.sqrt(sum / values.length)); + } +} diff --git a/test/features/websocket/src/test/features/api/test_api_calculate.ts b/test/features/websocket/src/test/features/api/test_api_calculate.ts deleted file mode 100644 index 218336673..000000000 --- a/test/features/websocket/src/test/features/api/test_api_calculate.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { TestValidator } from "@nestia/e2e"; -import { sleep_for } from "tstl"; -import typia from "typia"; - -import api from "@api"; -import { IListener } from "@api/lib/structures/IListener"; - -export const test_api_calculate = async ( - connection: api.IConnection, -): Promise => { - const events: IListener.IEvent[] = []; - const listener: IListener = { - on: (e) => events.push(e), - }; - const { connector, driver } = await api.functional.calculate.connect( - connection, - listener, - ); - const expected: IListener.IEvent[] = new Array(100).fill(0).map(() => { - const operator = typia.random(); - const x: number = 10; - const y: number = 5; - return { - operator, - x, - y, - z: - operator === "plus" - ? x + y - : operator === "minus" - ? x - y - : operator === "divide" - ? x / y - : operator === "multiply" - ? x * y - : 0, - }; - }); - try { - for (const e of expected) { - const z: number = await driver[e.operator](e.x, e.y); - TestValidator.equals("result")(z)(e.z); - } - await sleep_for(100); - TestValidator.equals("events")(events)(expected); - } catch (exp) { - throw exp; - } finally { - await connector.close(); - } -}; diff --git a/test/features/websocket/src/test/features/api/test_api_calculate_simple.ts b/test/features/websocket/src/test/features/api/test_api_calculate_simple.ts new file mode 100644 index 000000000..197037a6d --- /dev/null +++ b/test/features/websocket/src/test/features/api/test_api_calculate_simple.ts @@ -0,0 +1,56 @@ +import { TestValidator } from "@nestia/e2e"; +import { sleep_for } from "tstl"; +import typia from "typia"; + +import api from "@api"; +import { ICalcEvent } from "@api/lib/interfaces/ICalcEvent"; +import { ICalcEventListener } from "@api/lib/interfaces/ICalcEventListener"; + +export const test_api_calculate_simple = async ( + connection: api.IConnection, +): Promise => { + const events: ICalcEvent[] = []; + const listener: ICalcEventListener = { + on: (e) => events.push(e), + }; + const { connector, driver } = await api.functional.calculate.simple( + { + ...connection, + headers: { + precision: 2, + }, + }, + listener, + ); + const expected = new Array(100).fill(0).map(() => { + const type = typia.random<"plus" | "minus" | "multiplies" | "divides">(); + const x: number = 10; + const y: number = 5; + return { + type, + input: [x, y], + output: + type === "plus" + ? x + y + : type === "minus" + ? x - y + : type === "divides" + ? x / y + : type === "multiplies" + ? x * y + : 0, + }; + }); + try { + for (const e of expected) { + const z: number = await driver[e.type](e.input[0], e.input[1]); + TestValidator.equals("result")(z)(e.output); + } + await sleep_for(100); + TestValidator.equals("events")(events)(expected); + } catch (exp) { + throw exp; + } finally { + await connector.close(); + } +}; diff --git a/test/package.json b/test/package.json index ee1cee3f3..141269605 100644 --- a/test/package.json +++ b/test/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@samchon/nestia-test", - "version": "4.0.1", + "version": "4.0.2", "description": "Test program of Nestia", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ }, "homepage": "https://nestia.io", "devDependencies": { - "@nestia/sdk": "^4.0.1", + "@nestia/sdk": "^4.0.2", "@nestjs/swagger": "^8.0.7", "@types/express": "^4.17.17", "@types/multer": "^1.4.12", @@ -40,9 +40,9 @@ }, "dependencies": { "@fastify/multipart": "^8.1.0", - "@nestia/core": "^4.0.1", + "@nestia/core": "^4.0.2", "@nestia/e2e": "^0.7.0", - "@nestia/fetcher": "^4.0.1", + "@nestia/fetcher": "^4.0.2", "@nestjs/common": "^10.4.12", "@nestjs/core": "^10.4.12", "@nestjs/platform-express": "^10.4.12",