Skip to content

Commit

Permalink
Fix decompose option's bug on swagger generator with comment tags.
Browse files Browse the repository at this point in the history
When `decompose` option being used in swagger generator, comment tags written on property had been ignored.

```typescript
export interface IHeaders {
    "x-category": "x" | "y" | "z";
    "x-memo"?: string;

    /**
     * @default Samchon
     */
    "x-name"?: string;
    "x-values": number[];
    "x-flags": boolean[];

    /**
     * @hidden
     */
    "X-descriptions": string[];
}
```
  • Loading branch information
samchon committed Sep 26, 2023
1 parent 92639e4 commit bedb0a4
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 55 deletions.
31 changes: 21 additions & 10 deletions packages/sdk/src/generates/SwaggerGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from "path";
import { Singleton } from "tstl/thread/Singleton";
import ts from "typescript";

import typia, { IJsonApplication } from "typia";
import typia, { IJsonApplication, IJsonComponents } from "typia";
import { MetadataCollection } from "typia/lib/factories/MetadataCollection";
import { JsonApplicationProgrammer } from "typia/lib/programmers/json/JsonApplicationProgrammer";

Expand All @@ -13,8 +13,9 @@ import { IRoute } from "../structures/IRoute";
import { ISwagger } from "../structures/ISwagger";
import { ISwaggerError } from "../structures/ISwaggerError";
import { ISwaggerInfo } from "../structures/ISwaggerInfo";
import { ISwaggerLazyProperty } from "../structures/ISwaggerLazyProperty";
import { ISwaggerLazySchema } from "../structures/ISwaggerLazySchema";
import { ISwaggerRoute } from "../structures/ISwaggerRoute";
import { ISwaggerSchemaTuple } from "../structures/ISwaggerSchemaTuple";
import { ISwaggerSecurityScheme } from "../structures/ISwaggerSecurityScheme";
import { FileRetriever } from "../utils/FileRetriever";
import { MapUtil } from "../utils/MapUtil";
Expand All @@ -25,7 +26,8 @@ export namespace SwaggerGenerator {
config: INestiaConfig.ISwaggerConfig;
checker: ts.TypeChecker;
collection: MetadataCollection;
tuples: Array<ISwaggerSchemaTuple>;
lazySchemas: Array<ISwaggerLazySchema>;
lazyProperties: Array<ISwaggerLazyProperty>;
errors: ISwaggerError[];
}

Expand Down Expand Up @@ -63,7 +65,8 @@ export namespace SwaggerGenerator {

// CONSTRUCT SWAGGER DOCUMENTS
const errors: ISwaggerError[] = [];
const tuples: Array<ISwaggerSchemaTuple> = [];
const lazySchemas: Array<ISwaggerLazySchema> = [];
const lazyProperties: Array<ISwaggerLazyProperty> = [];
const swagger: ISwagger = await initialize(config);
const pathDict: Map<
string,
Expand All @@ -83,27 +86,35 @@ export namespace SwaggerGenerator {
config,
checker,
collection,
tuples,
lazySchemas,
lazyProperties,
errors,
})(route);
}
swagger.paths = {};
for (const [path, routes] of pathDict) {
swagger.paths[path] = routes;
}
for (const [path, routes] of pathDict) swagger.paths[path] = routes;

// FILL JSON-SCHEMAS
const application: IJsonApplication =
JsonApplicationProgrammer.write({
purpose: "swagger",
})(tuples.map(({ metadata }) => metadata));
})(lazySchemas.map(({ metadata }) => metadata));
swagger.components = {
...(swagger.components ?? {}),
...(application.components ?? {}),
};
tuples.forEach(({ schema }, index) => {
lazySchemas.forEach(({ schema }, index) => {
Object.assign(schema, application.schemas[index]!);
});
for (const p of lazyProperties)
Object.assign(
p.schema,
(
application.components.schemas?.[
p.object
] as IJsonComponents.IObject
)?.properties[p.property],
);

// CONFIGURE SECURITY
if (config.security) fill_security(config.security, swagger);
Expand Down
52 changes: 32 additions & 20 deletions packages/sdk/src/generates/internal/SwaggerSchemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import { ValidationPipe } from "typia/lib/typings/ValidationPipe";
import { INestiaConfig } from "../../INestiaConfig";
import { IRoute } from "../../structures/IRoute";
import { ISwaggerError } from "../../structures/ISwaggerError";
import { ISwaggerLazyProperty } from "../../structures/ISwaggerLazyProperty";
import { ISwaggerLazySchema } from "../../structures/ISwaggerLazySchema";
import { ISwaggerRoute } from "../../structures/ISwaggerRoute";
import { ISwaggerSchemaTuple } from "../../structures/ISwaggerSchemaTuple";
import { SwaggerSchemaValidator } from "./SwaggerSchemaValidator";

export namespace SwaggerSchemaGenerator {
export interface IProps {
config: INestiaConfig.ISwaggerConfig;
checker: ts.TypeChecker;
collection: MetadataCollection;
tuples: Array<ISwaggerSchemaTuple>;
lazySchemas: Array<ISwaggerLazySchema>;
lazyProperties: Array<ISwaggerLazyProperty>;
errors: ISwaggerError[];
}

Expand Down Expand Up @@ -286,6 +288,10 @@ export namespace SwaggerSchemaGenerator {
(
result: ValidationPipe<Metadata, MetadataFactory.IError>,
): ISwaggerRoute.IParameter[] => {
const decoded: ISwaggerRoute.IParameter = lazy(props)(route)(
param,
result,
);
if (result.success === false) {
props.errors.push(
...result.errors.map((e) => ({
Expand All @@ -294,29 +300,35 @@ export namespace SwaggerSchemaGenerator {
from: param.name,
})),
);
return [lazy(props)(route)(param, result)];
return [decoded];
} else if (
props.config.decompose !== true ||
result.data.objects.length === 0
)
return [lazy(props)(route)(param, result)];
return [decoded];

return result.data.objects[0].properties.map((p) => {
const schema = coalesce(props)({
success: true,
data: p.value,
return result.data.objects[0].properties
.filter((p) =>
p.jsDocTags.every((tag) => tag.name !== "hidden"),
)
.map((p) => {
const schema: IJsonSchema = {};
props.lazyProperties.push({
schema,
object: result.data.objects[0].name,
property: p.key.constants[0].values[0] as string,
});
return {
name: p.key.constants[0].values[0] as string,
in:
param.category === "headers"
? "header"
: param.category,
schema,
description: p.description ?? undefined,
required: p.value.isRequired(),
};
});
return {
name: p.key.constants[0].values[0] as string,
in:
param.category === "headers"
? "header"
: param.category,
schema,
description: p.description ?? undefined,
required: p.value.isRequired(),
};
});
};

const lazy =
Expand Down Expand Up @@ -347,7 +359,7 @@ export namespace SwaggerSchemaGenerator {
result: ValidationPipe<Metadata, MetadataFactory.IError>,
): IJsonSchema => {
const schema: IJsonSchema = {} as any;
props.tuples.push({
props.lazySchemas.push({
metadata: result.success ? result.data : any.get(),
schema,
});
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk/src/structures/ISwaggerLazyProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IJsonSchema } from "typia";

export interface ISwaggerLazyProperty {
schema: IJsonSchema;
object: string;
property: string;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IJsonSchema } from "typia";
import { Metadata } from "typia/lib/schemas/metadata/Metadata";

export interface ISwaggerSchemaTuple {
export interface ISwaggerLazySchema {
metadata: Metadata;
schema: IJsonSchema;
}
78 changes: 54 additions & 24 deletions test/features/headers-decompose/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"name": "x-category",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "string",
"enum": [
"x",
Expand All @@ -36,6 +38,8 @@
"name": "x-memo",
"in": "header",
"schema": {
"x-typia-required": false,
"x-typia-optional": true,
"type": "string"
},
"required": false
Expand All @@ -44,16 +48,34 @@
"name": "x-name",
"in": "header",
"schema": {
"type": "string"
"x-typia-jsDocTags": [
{
"name": "default",
"text": [
{
"text": "Samchon",
"kind": "text"
}
]
}
],
"x-typia-required": false,
"x-typia-optional": true,
"type": "string",
"default": "Samchon"
},
"required": false
},
{
"name": "x-values",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "array",
"items": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "number"
}
},
Expand All @@ -63,24 +85,17 @@
"name": "x-flags",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "array",
"items": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "boolean"
}
},
"required": true
},
{
"name": "X-descriptions",
"in": "header",
"schema": {
"type": "array",
"items": {
"type": "string"
}
},
"required": true
},
{
"name": "section",
"in": "path",
Expand Down Expand Up @@ -115,6 +130,8 @@
"name": "x-category",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "string",
"enum": [
"x",
Expand All @@ -128,6 +145,8 @@
"name": "x-memo",
"in": "header",
"schema": {
"x-typia-required": false,
"x-typia-optional": true,
"type": "string"
},
"required": false
Expand All @@ -136,16 +155,34 @@
"name": "x-name",
"in": "header",
"schema": {
"type": "string"
"x-typia-jsDocTags": [
{
"name": "default",
"text": [
{
"text": "Samchon",
"kind": "text"
}
]
}
],
"x-typia-required": false,
"x-typia-optional": true,
"type": "string",
"default": "Samchon"
},
"required": false
},
{
"name": "x-values",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "array",
"items": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "number"
}
},
Expand All @@ -155,24 +192,17 @@
"name": "x-flags",
"in": "header",
"schema": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "array",
"items": {
"x-typia-required": true,
"x-typia-optional": false,
"type": "boolean"
}
},
"required": true
},
{
"name": "X-descriptions",
"in": "header",
"schema": {
"type": "array",
"items": {
"type": "string"
}
},
"required": true
},
{
"name": "section",
"in": "path",
Expand Down
Loading

0 comments on commit bedb0a4

Please sign in to comment.