Skip to content

Commit

Permalink
feat: Make the behavior of anyOf equivalent to oneOf (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
Himenon authored Jan 30, 2022
1 parent 253154b commit aab686d
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 5 deletions.
1 change: 1 addition & 0 deletions scripts/testCodeGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const main = () => {
});

Writer.generateSplitCode("test/api.test.domain/index.yml", "test/code/split");
Writer.generateSplitCode("test/multi-type.test.domain/index.yml", "test/code/mulit-type-test.domain");

Writer.generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json");
Writer.generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json");
Expand Down
9 changes: 6 additions & 3 deletions src/internal/OpenApiTools/toTypeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import DotProp from "dot-prop";
import ts from "typescript";

import type { OpenApi } from "../../types";
import { UnsetTypeError } from "../Exception";
import { UnSupportError } from "../Exception";
import * as Logger from "../Logger";
import { Factory } from "../TsGenerator";
Expand Down Expand Up @@ -65,8 +64,12 @@ export const generateMultiTypeNode = (
typeNodes,
});
}
// TODO Feature Development: Calculate intersection types
return factory.TypeNode.create({ type: "never" });
/**
* If you see this comment and have an idea for an AnyOf type output, please submit an Issue.
*/
return factory.UnionTypeNode.create({
typeNodes,
});
};

const nullable = (factory: Factory.Type, typeNode: ts.TypeNode, nullable: boolean): ts.TypeNode => {
Expand Down
7 changes: 6 additions & 1 deletion src/internal/ResolveReference/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export { OpenApi };

export type ObjectLike = { [key: string]: any };

const escapeFromJsonCyclic = (obj: any) => JSON.parse(JSON.stringify(obj));
const escapeFromJsonCyclic = (obj: any) => {
if (!obj) {
return obj;
}
return JSON.parse(JSON.stringify(obj));
};

const isObject = (value: any): value is ObjectLike => {
return !!value && value !== null && !Array.isArray(value) && typeof value === "object";
Expand Down
101 changes: 101 additions & 0 deletions test/__tests__/__snapshots__/multi-type.test.domain.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Multi Type apiClient 1`] = `
"//
// Generated by @himenon/openapi-typescript-code-generator
//
// OpenApi : 3.0.1
//
// License : MIT
//
import { Schemas } from \\"./types\\";
export interface RequestBody$putAnyOf {
\\"application/json\\": Schemas.Cat | Schemas.Dog;
}
export interface RequestBody$patchOneOf {
\\"application/json\\": Schemas.Cat | Schemas.Dog;
}
export type RequestContentType$putAnyOf = keyof RequestBody$putAnyOf;
export interface Params$putAnyOf {
requestBody: RequestBody$putAnyOf[\\"application/json\\"];
}
export type RequestContentType$patchOneOf = keyof RequestBody$patchOneOf;
export interface Params$patchOneOf {
requestBody: RequestBody$patchOneOf[\\"application/json\\"];
}
export type HttpMethod = \\"GET\\" | \\"PUT\\" | \\"POST\\" | \\"DELETE\\" | \\"OPTIONS\\" | \\"HEAD\\" | \\"PATCH\\" | \\"TRACE\\";
export interface ObjectLike {
[key: string]: any;
}
export interface QueryParameter {
value: any;
style?: \\"form\\" | \\"spaceDelimited\\" | \\"pipeDelimited\\" | \\"deepObject\\";
explode: boolean;
}
export interface QueryParameters {
[key: string]: QueryParameter;
}
export type SuccessResponses = void;
export namespace ErrorResponse {
export type putAnyOf = void;
export type patchOneOf = void;
}
export interface ApiClient<RequestOption> {
request: <T = SuccessResponses>(httpMethod: HttpMethod, url: string, headers: ObjectLike | any, requestBody: ObjectLike | any, queryParameters: QueryParameters | undefined, options?: RequestOption) => Promise<T>;
}
export class Client<RequestOption> {
private baseUrl: string;
constructor(private apiClient: ApiClient<RequestOption>, baseUrl: string) { this.baseUrl = baseUrl.replace(/\\\\/$/, \\"\\"); }
/**
* operationId: putAnyOf
* Request URI: /pets
*/
public async putAnyOf(params: Params$putAnyOf, option?: RequestOption): Promise<void> {
const url = this.baseUrl + \`/pets\`;
const headers = {
\\"Content-Type\\": \\"application/json\\"
};
return this.apiClient.request(\\"PUT\\", url, headers, params.requestBody, undefined, option);
}
/**
* operationId: patchOneOf
* Request URI: /pets
*/
public async patchOneOf(params: Params$patchOneOf, option?: RequestOption): Promise<void> {
const url = this.baseUrl + \`/pets\`;
const headers = {
\\"Content-Type\\": \\"application/json\\"
};
return this.apiClient.request(\\"PATCH\\", url, headers, params.requestBody, undefined, option);
}
}
"
`;
exports[`Multi Type types 1`] = `
"//
// Generated by @himenon/openapi-typescript-code-generator
//
// OpenApi : 3.0.1
//
// License : MIT
//
export namespace Schemas {
export interface Pet {
pet_type: string;
}
export type Dog = Schemas.Pet & {
bark?: boolean;
breed?: \\"Dingo\\" | \\"Husky\\" | \\"Retriever\\" | \\"Shepherd\\";
};
export type Cat = Schemas.Pet & {
hunts?: boolean;
age?: number;
};
}
"
`;
18 changes: 18 additions & 0 deletions test/__tests__/multi-type.test.domain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as fs from "fs";
import * as path from "path";

import * as Utils from "../utils";

describe("Multi Type", () => {
test("types", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/types.ts"), { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});

test("apiClient", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/apiClient.ts"), { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
});
3 changes: 2 additions & 1 deletion test/api.test.domain/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ components:
- type: string
- type: number
- type: boolean

responses:
Continue:
description: |
Expand Down Expand Up @@ -413,7 +414,7 @@ paths:
200:
description: Search Book Result
content:
application/json:
application/json:
schema:
type: object
properties:
Expand Down
71 changes: 71 additions & 0 deletions test/multi-type.test.domain/index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
openapi: 3.0.1
info:
version: 1.0.0
title: multi-type.test.domain
description: Library test schema
license:
name: MIT

servers:
- url: "http://multi-type.test.domain/"
description: Development Environment

components:
schemas:
Pet:
type: object
required:
- pet_type
properties:
pet_type:
type: string
discriminator:
propertyName: pet_type
Dog:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
# all other properties specific to a `Dog`
properties:
bark:
type: boolean
breed:
type: string
enum: [Dingo, Husky, Retriever, Shepherd]
Cat:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
hunts:
type: boolean
age:
type: integer

paths:
/pets:
put:
operationId: putAnyOf
requestBody:
content:
application/json:
schema:
anyOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
responses:
"200":
description: Updated

patch:
operationId: patchOneOf
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
responses:
"200":
description: Updated

0 comments on commit aab686d

Please sign in to comment.