Skip to content

Commit

Permalink
fix(schema): fix OASv3 schema generation fails when model used in @OneOf
Browse files Browse the repository at this point in the history
 has kind property

Closes: #2413
  • Loading branch information
Romakita committed Sep 3, 2023
1 parent 238bac8 commit 1628958
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 19 deletions.
4 changes: 2 additions & 2 deletions packages/specs/schema/src/components/anyMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export function anyMapper(input: any, options: JsonSchemaOptions = {}): any {
return toRef(enumSchema, enumSchema.toJSON(options), options);
}

if (input.kind) {
const kind = oneOfMapper(input.kind, "map");
if (input.$kind && input.$isJsonDocument) {
const kind = oneOfMapper(input.$kind, "map");
const schema = execMapper(kind, input, mapGenericsOptions(options));

return input.canRef ? toRef(input, schema, options) : schema;
Expand Down
3 changes: 2 additions & 1 deletion packages/specs/schema/src/domain/JsonMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {execMapper} from "../registries/JsonSchemaMapperContainer";
export class JsonMap<T> extends Map<string, any> {
[key: string]: any;

kind: string = "map";
$kind: string = "map";
readonly $isJsonDocument = true;

constructor(obj: Partial<T> = {}) {
super();
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/domain/JsonMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {JsonMap} from "./JsonMap";
import {JsonSchema} from "./JsonSchema";

export class JsonMedia extends JsonMap<OS3MediaType<JsonSchema>> {
kind: string = "operationMedia";
$kind: string = "operationMedia";

groups: string[] = [];
groupsName: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/domain/JsonOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface JsonOperationOptions extends OS3Operation<JsonSchema, JsonParam
}

export class JsonOperation extends JsonMap<JsonOperationOptions> {
kind: string = "operation";
$kind: string = "operation";

readonly operationPaths: Map<string, JsonMethodPath> = new Map();
#status: number;
Expand Down
3 changes: 2 additions & 1 deletion packages/specs/schema/src/domain/JsonOperationPathsMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {OperationMethods} from "../constants/httpMethods";
import {JsonMethodPath} from "./JsonOperation";

export class JsonOperationPathsMap extends Map<string, JsonMethodPath> {
kind: string = "operationPaths";
$kind: string = "operationPaths";
readonly $isJsonDocument = true;

setOperationPath(operationPath: JsonMethodPath) {
if (operationPath.method !== OperationMethods.CUSTOM) {
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/domain/JsonParameter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {formatParameterType} from "./JsonParameterTypes";
import {JsonSchema} from "./JsonSchema";

export class JsonParameter extends JsonMap<OS3Parameter<JsonSchema>> implements NestedGenerics {
kind = "operationInParameter";
$kind = "operationInParameter";

nestedGenerics: Type<any>[][] = [];
groups: string[];
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/domain/JsonRequestBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {JsonSchema} from "./JsonSchema";
export type JsonRequestBodyOptions = OS3RequestBody<JsonSchema>;

export class JsonRequestBody extends JsonMap<JsonRequestBodyOptions> {
kind = "operationRequestBody";
$kind = "operationRequestBody";

constructor(obj: Partial<JsonRequestBodyOptions> = {}) {
super(obj);
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/domain/JsonResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {JsonSchema} from "./JsonSchema";
export type JsonResponseOptions = OS3Response<JsonSchema, string | JsonHeader>;

export class JsonResponse extends JsonMap<JsonResponseOptions> {
kind: string = "operationResponse";
$kind: string = "operationResponse";

status: number;

Expand Down
7 changes: 3 additions & 4 deletions packages/specs/schema/src/domain/JsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function mapToJsonSchema(item: any): any {
return (item as any[]).map(mapToJsonSchema);
}

if (item.isStore || item.isJsonSchema || item.isLazyRef) {
if (item.isStore || item.$isJsonDocument || item.isLazyRef) {
return item;
}

Expand All @@ -75,9 +75,8 @@ function mapToJsonSchema(item: any): any {
}

export class JsonSchema extends Map<string, any> implements NestedGenerics {
kind: string = "schema";

readonly isJsonSchema = true;
readonly $kind: string = "schema";
readonly $isJsonDocument = true;
readonly $hooks = new Hooks();
readonly $required: Set<string> = new Set();
readonly $allow: any[] = [];
Expand Down
6 changes: 3 additions & 3 deletions packages/specs/schema/src/utils/inlineEnums.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("inlineEnums()", () => {
const result = inlineEnums(
{
enum: {
isJsonSchema: true,
$isJsonDocument: true,
toJSON() {
return {enum: ["type"]};
}
Expand All @@ -26,7 +26,7 @@ describe("inlineEnums()", () => {
{
type: "object",
enum: {
isJsonSchema: true,
$isJsonDocument: true,
toJSON() {
return {enum: ["type"]};
}
Expand All @@ -46,7 +46,7 @@ describe("inlineEnums()", () => {
{
type: "string",
enum: {
isJsonSchema: true,
$isJsonDocument: true,
toJSON() {
return {enum: ["type"]};
}
Expand Down
2 changes: 1 addition & 1 deletion packages/specs/schema/src/utils/inlineEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {JsonSchema} from "../domain/JsonSchema";
import {JsonSchemaOptions} from "../interfaces/JsonSchemaOptions";

export function inlineEnums(obj: any, schema: JsonSchema, options: JsonSchemaOptions) {
if (options.inlineEnums && obj.enum?.isJsonSchema) {
if (options.inlineEnums && obj.enum?.$isJsonDocument) {
obj.enum = obj.enum.toJSON().enum;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Discriminator with kind property should generate the spec 1`] = `
Object {
"components": Object {
"schemas": Object {
"FirstImpl": Object {
"properties": Object {
"kind": Object {
"enum": Array [
"json",
],
"type": "string",
},
"type": Object {
"enum": Array [
"one",
"two",
],
"example": "one",
"type": "string",
},
},
"type": "object",
},
"ParentModel": Object {
"properties": Object {
"test": Object {
"discriminator": Object {
"propertyName": "type",
},
"oneOf": Array [
Object {
"$ref": "#/components/schemas/FirstImpl",
},
Object {
"$ref": "#/components/schemas/SecondImpl",
},
],
"required": Array [
"type",
],
},
},
"type": "object",
},
"SecondImpl": Object {
"properties": Object {
"prop": Object {
"type": "string",
},
"type": Object {
"enum": Array [
"one",
"two",
],
"example": "two",
"type": "string",
},
},
"type": "object",
},
},
},
"paths": Object {
"/test": Object {
"get": Object {
"operationId": "testGet",
"parameters": Array [],
"responses": Object {
"200": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"items": Object {
"$ref": "#/components/schemas/ParentModel",
},
"type": "array",
},
},
},
"description": "Success",
},
},
"tags": Array [
"Test",
],
},
},
},
"tags": Array [
Object {
"name": "Test",
},
],
}
`;
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import {Controller} from "@tsed/di";
import {BodyParams, PathParams} from "@tsed/platform-params";
import * as Path from "path";
import {
DiscriminatorKey,
DiscriminatorValue,
Enum,
Get,
getJsonSchema,
getSpec,
JsonEntityStore,
Name,
OneOf,
Partial,
Patch,
Expand Down Expand Up @@ -1287,4 +1290,51 @@ describe("Discriminator", () => {
expect(JsonEntityStore.from(Event).isDiscriminatorChild).toEqual(false);
});
});
describe("with kind property", () => {
it("should generate the spec", () => {
enum Discriminator {
ONE = "one",
TWO = "two"
}

abstract class BaseModel {
@Enum(Discriminator.ONE, Discriminator.TWO)
@DiscriminatorKey()
public type!: Discriminator.ONE | Discriminator.TWO;
}

@DiscriminatorValue(Discriminator.ONE)
class FirstImpl extends BaseModel {
public declare type: Discriminator.ONE;

@Enum("json")
public kind!: "json";
}

@DiscriminatorValue(Discriminator.TWO)
class SecondImpl extends BaseModel {
public declare type: Discriminator.TWO;

@Property()
public prop!: string;
}

class ParentModel {
@OneOf(FirstImpl, SecondImpl)
public test?: FirstImpl | SecondImpl;
}

@Controller("/test")
@Name("Test")
class TestController {
@Get()
@Returns(200, Array).Of(ParentModel)
public get(): Promise<ParentModel> {
return null as any;
}
}

expect(getSpec(TestController)).toMatchSnapshot();
});
});
});
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"resolveJsonModule": true,
"esModuleInterop": true,
"allowJs": true,
"moduleResolution": "node"
"moduleResolution": "node",
"skipLibCheck": true
},
"references": [
{
Expand Down Expand Up @@ -172,5 +173,5 @@
"path": "./packages/security/oidc-provider-plugin-wildcard-redirect-uri"
}
],
"exclude": ["node_modules", "docs", "docs-references"]
"exclude": ["**/node_modules/**", "docs", "docs-references"]
}

0 comments on commit 1628958

Please sign in to comment.