Skip to content

Commit

Permalink
Merge pull request #814 from samchon/features/swagger
Browse files Browse the repository at this point in the history
Enhance #810 - multiple customization + `at()` method
  • Loading branch information
samchon authored Feb 24, 2024
2 parents 20c82a1 + 57c9f55 commit a81c664
Show file tree
Hide file tree
Showing 46 changed files with 263 additions and 138 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": "2.5.10",
"version": "2.5.11",
"description": "Nestia station",
"main": "prettier.config.js",
"scripts": {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nestia/core",
"version": "2.5.10",
"version": "2.5.11",
"description": "Super-fast validation decorators of NestJS",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -36,7 +36,7 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/fetcher": "^2.5.10",
"@nestia/fetcher": "^2.5.11",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"detect-ts-node": "^1.0.5",
Expand All @@ -48,7 +48,7 @@
"typia": "^5.4.12"
},
"peerDependencies": {
"@nestia/fetcher": ">=2.5.10",
"@nestia/fetcher": ">=2.5.11",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
41 changes: 35 additions & 6 deletions packages/core/src/decorators/SwaggerCustomizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,23 @@ export function SwaggerCustomizer(
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<any>,
) {
Reflect.defineMetadata(
"nestia/SwaggerCustomizer",
closure,
target,
propertyKey,
);
const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = (() => {
if (Reflect.hasMetadata("nestia/SwaggerCustomizer", target, propertyKey))
return Reflect.getMetadata(
"nestia/SwaggerCustomizer",
target,
propertyKey,
);
const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = [];
Reflect.defineMetadata(
"nestia/SwaggerCustomizer",
array,
target,
propertyKey,
);
return array;
})();
array.push(closure);
return descriptor;
};
}
Expand Down Expand Up @@ -61,6 +72,14 @@ export namespace SwaggerCustomizer {
*/
route: ISwaggerRoute;

/**
* Get neighbor endpoint data through the controller method.
*
* @param func Controller method to find the neighbor endpoint
* @returns Neighbor endpoint data
*/
at(func: Function): ISwaggerEndpoint | undefined;

/**
* Get neighbor route data.
*
Expand All @@ -84,4 +103,14 @@ export namespace SwaggerCustomizer {
*/
method: string;
}

/**
* Endpoint info of the route.
*/
export interface ISwaggerEndpoint extends IAccessor {
/**
* Route data.
*/
route: ISwaggerRoute;
}
}
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": "2.5.10",
"version": "2.5.11",
"description": "Fetcher library of Nestia SDK",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
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": "2.5.10",
"version": "2.5.11",
"description": "Nestia SDK and Swagger generator",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
Expand Down Expand Up @@ -32,7 +32,7 @@
},
"homepage": "https://nestia.io",
"dependencies": {
"@nestia/fetcher": "^2.5.10",
"@nestia/fetcher": "^2.5.11",
"cli": "^1.0.1",
"get-function-location": "^2.0.0",
"glob": "^7.2.0",
Expand All @@ -45,7 +45,7 @@
"typia": "^5.4.12"
},
"peerDependencies": {
"@nestia/fetcher": ">=2.5.10",
"@nestia/fetcher": ">=2.5.11",
"@nestjs/common": ">=7.0.1",
"@nestjs/core": ">=7.0.1",
"reflect-metadata": ">=0.1.12",
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/src/NestiaSdkApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export class NestiaSdkApplication {
implicit
.map(
(it) =>
` - ${it.symbol.class}.${it.symbol.function} at "${it.location}"`,
` - ${it.target.class.name}.${it.target.function.name} at "${it.location}"`,
)
.join("\n"),
);
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk/src/analyses/ControllerAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ export namespace ControllerAnalyzer {
},
imports,
status: func.status,
symbol: {
class: controller.name,
function: func.name,
target: {
class: controller.target,
function: func.target,
},
location: (() => {
const file = declaration.getSourceFile();
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/analyses/ReflectAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ export namespace ReflectAnalyzer {

// DO CONSTRUCT
const meta: IController.IFunction = {
target: proto,
name,
method: method === "ALL" ? "POST" : method,
paths: _Get_paths(proto).filter((str) => {
Expand Down
92 changes: 57 additions & 35 deletions packages/sdk/src/generates/SwaggerGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ export namespace SwaggerGenerator {
for (const e of errors)
console.error(
`${path.relative(e.route.location, process.cwd())}:${
e.route.symbol.class
}.${e.route.symbol.function}:${
e.route.target.class.name
}.${e.route.target.function.name}:${
e.from
} - error TS(@nestia/sdk): invalid type detected.\n\n` +
e.messages.map((m) => ` - ${m}`).join("\n"),
Expand All @@ -125,51 +125,63 @@ export namespace SwaggerGenerator {
}

// SWAGGER CUSTOMIZER
const routeGetter = new Singleton(
() =>
(accessor: {
method: string;
path: string;
}): ISwaggerRoute | undefined => {
const method: string = accessor.method.toLowerCase();
const path: string =
"/" +
accessor.path
.split("/")
.filter((str) => !!str.length)
.map((str) =>
str.startsWith(":") ? `{${str.substring(1)}}` : str,
)
.join("/");
console.log({ path, method });
return swagger.paths[path]?.[method];
},
);
const customizer = {
at: new Singleton(() => {
const functor: Map<Function, Endpoint> = new Map();
for (const route of routeList) {
const method = route.method.toLowerCase();
const path = get_path(route.path, route.parameters);
functor.set(route.target.function, {
method,
path,
route: swagger.paths[path][method],
});
}
return functor;
}),
get: new Singleton(() => (key: Accessor): ISwaggerRoute | undefined => {
const method: string = key.method.toLowerCase();
const path: string =
"/" +
key.path
.split("/")
.filter((str) => !!str.length)
.map((str) =>
str.startsWith(":") ? `{${str.substring(1)}}` : str,
)
.join("/");
return swagger.paths[path]?.[method];
}),
};
for (const route of routeList) {
if (
false ===
Reflect.hasMetadata(
"nestia/SwaggerCustomizer",
route.controller.prototype,
route.symbol.function,
route.target.function.name,
)
)
continue;

const path: string = get_path(route.path, route.parameters);
const method: string = route.method.toLowerCase();
const target: ISwaggerRoute = swagger.paths[path][method];
const closure: Function = Reflect.getMetadata(
const closure: Function | Function[] = Reflect.getMetadata(
"nestia/SwaggerCustomizer",
route.controller.prototype,
route.symbol.function,
route.target.function.name,
);
closure({
route: target,
method,
path,
swagger,
get: routeGetter.get(),
});
const array: Function[] = Array.isArray(closure) ? closure : [closure];
for (const fn of array)
fn({
route: target,
method,
path,
swagger,
at: (func: Function) => customizer.at.get().get(func),
get: (accessor: Accessor) => customizer.get.get()(accessor),
});
}

// DO GENERATE
Expand Down Expand Up @@ -231,7 +243,7 @@ export namespace SwaggerGenerator {
for (const [key, scopes] of Object.entries(record))
validate((str) =>
violations.push(
` - ${str} (${route.symbol} at "${route.location}")`,
` - ${str} (${route.target.class.name}.${route.target.function.name}() at "${route.location}")`,
),
)(key, scopes);

Expand Down Expand Up @@ -370,8 +382,8 @@ export namespace SwaggerGenerator {
operationId:
route.operationId ??
props.config.operationId?.({
class: route.symbol.class,
function: route.symbol.function,
class: route.target.class.name,
function: route.target.function.name,
method: route.method as "GET",
path: route.path,
}),
Expand Down Expand Up @@ -422,3 +434,13 @@ export namespace SwaggerGenerator {
return input;
}
}

interface Accessor {
method: string;
path: string;
}
interface Endpoint {
method: string;
path: string;
route: ISwaggerRoute;
}
2 changes: 1 addition & 1 deletion packages/sdk/src/generates/internal/SdkRouteProgrammer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export namespace SdkRouteProgrammer {
// POSTFIX
if (!!comments.length) comments.push("");
comments.push(
`@controller ${route.symbol.class}.${route.symbol.function}`,
`@controller ${route.target.class.name}.${route.target.function.name}`,
`@path ${route.method} ${route.path}`,
`@nestia Generated by Nestia - https://github.com/samchon/nestia`,
);
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/structures/IController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface IController {

export namespace IController {
export interface IFunction {
target: Function;
name: string;
method: string;
paths: string[];
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk/src/structures/IRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export interface IRoute {
output: IRoute.IOutput;

location: string;
symbol: {
class: string;
function: string;
target: {
class: Function;
function: Function;
};
description?: string;
operationId?: string;
Expand Down
2 changes: 1 addition & 1 deletion test/features/all/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
],
"info": {
"version": "2.5.10",
"version": "2.5.11-dev.20240224",
"title": "@nestia/test",
"description": "Test program of Nestia",
"license": {
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/app-globalPrefix-versionUri/swagger.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/app-globalPrefix/swagger.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/features/app-routerModule/src/api/IConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { IConnection } from "@nestia/fetcher";
2 changes: 1 addition & 1 deletion test/features/app-routerModule/swagger.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/app-versionHeader/swagger.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/app-versionUri/swagger.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/app/swagger.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/features/beautify-4/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
],
"info": {
"version": "2.5.8",
"version": "2.5.11-dev.20240224",
"title": "@nestia/test",
"description": "Test program of Nestia",
"license": {
Expand Down
2 changes: 1 addition & 1 deletion test/features/beautify-false/swagger.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"openapi":"3.0.1","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"2.5.8","title":"@nestia/test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"description":"","required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":""}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"nullable":false,"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"nullable":false,"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"nullable":false,"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"nullable":false,"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"type":"string","maxLength":255,"nullable":true},"extension":{"type":"string","maxLength":8,"minLength":1,"nullable":true},"url":{"type":"string","format":"uri"}},"nullable":false,"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"required":["id","created_at","title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}}}
{"openapi":"3.0.1","servers":[{"url":"https://github.com/samchon/nestia","description":"insert your server url"}],"info":{"version":"2.5.11-dev.20240224","title":"@nestia/test","description":"Test program of Nestia","license":{"name":"MIT"}},"paths":{"/health":{"get":{"tags":["system","health"],"parameters":[],"responses":{"200":{"description":""}},"summary":"Health check API","description":"Health check API.\n\nJust for health checking API liveness."}},"/performance":{"get":{"tags":["system","performance"],"parameters":[],"responses":{"200":{"description":"Performance info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IPerformance"}}}}},"summary":"Get server performance info","description":"Get server performance info."}},"/body":{"post":{"tags":[],"parameters":[],"requestBody":{"description":"Content to store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle.IStore"}}},"required":true},"responses":{"201":{"description":"Newly archived article","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IBbsArticle"}}}}},"summary":"Store an article","description":"Store an article."}},"/body/{id}":{"put":{"tags":[],"parameters":[{"name":"id","in":"path","schema":{"type":"string","format":"uuid"},"description":"","required":true}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartialIBbsArticle.IStore"}}},"required":true},"responses":{"200":{"description":""}}}}},"components":{"schemas":{"IPerformance":{"type":"object","properties":{"cpu":{"$ref":"#/components/schemas/process.global.NodeJS.CpuUsage"},"memory":{"$ref":"#/components/schemas/process.global.NodeJS.MemoryUsage"},"resource":{"$ref":"#/components/schemas/process.global.NodeJS.ResourceUsage"}},"nullable":false,"required":["cpu","memory","resource"],"description":"Performance info."},"process.global.NodeJS.CpuUsage":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}},"nullable":false,"required":["user","system"]},"process.global.NodeJS.MemoryUsage":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}},"nullable":false,"required":["rss","heapTotal","heapUsed","external","arrayBuffers"]},"process.global.NodeJS.ResourceUsage":{"type":"object","properties":{"fsRead":{"type":"number"},"fsWrite":{"type":"number"},"involuntaryContextSwitches":{"type":"number"},"ipcReceived":{"type":"number"},"ipcSent":{"type":"number"},"majorPageFault":{"type":"number"},"maxRSS":{"type":"number"},"minorPageFault":{"type":"number"},"sharedMemorySize":{"type":"number"},"signalsCount":{"type":"number"},"swappedOut":{"type":"number"},"systemCPUTime":{"type":"number"},"unsharedDataSize":{"type":"number"},"unsharedStackSize":{"type":"number"},"userCPUTime":{"type":"number"},"voluntaryContextSwitches":{"type":"number"}},"nullable":false,"required":["fsRead","fsWrite","involuntaryContextSwitches","ipcReceived","ipcSent","majorPageFault","maxRSS","minorPageFault","sharedMemorySize","signalsCount","swappedOut","systemCPUTime","unsharedDataSize","unsharedStackSize","userCPUTime","voluntaryContextSwitches"]},"IBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"required":["title","body","files"]},"IAttachmentFile":{"type":"object","properties":{"name":{"type":"string","maxLength":255,"nullable":true},"extension":{"type":"string","maxLength":8,"minLength":1,"nullable":true},"url":{"type":"string","format":"uri"}},"nullable":false,"required":["name","extension","url"]},"IBbsArticle":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"created_at":{"type":"string","format":"date-time"},"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"required":["id","created_at","title","body","files"]},"PartialIBbsArticle.IStore":{"type":"object","properties":{"title":{"type":"string","maxLength":50,"minLength":3},"body":{"type":"string"},"files":{"type":"array","items":{"$ref":"#/components/schemas/IAttachmentFile"}}},"nullable":false,"description":"Make all properties in T optional"}},"securitySchemes":{"bearer":{"type":"apiKey","in":"header","name":"Authorization"}}}}
2 changes: 1 addition & 1 deletion test/features/beautify/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
],
"info": {
"version": "2.5.8",
"version": "2.5.11-dev.20240224",
"title": "@nestia/test",
"description": "Test program of Nestia",
"license": {
Expand Down
Loading

0 comments on commit a81c664

Please sign in to comment.