Skip to content

Commit

Permalink
feat: add support for oneOf in requestBody schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
spokli committed Aug 23, 2023
1 parent 3eafce4 commit c6ed409
Show file tree
Hide file tree
Showing 7 changed files with 440 additions and 224 deletions.
16 changes: 16 additions & 0 deletions examples/unified-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ paths:
application/json:
schema: {$ref: '#/components/schemas/PlaygroundResponse'}

post:
operationId: postPlayground
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/ContactInfoRequest'
- $ref: '#/components/schemas/OffsetInfoRequest'
required: true
responses:
200:
description: OK
content:
application/json:
schema: {$ref: '#/components/schemas/PlaygroundResponse'}

components:
schemas:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "st-open-api",
"version": "1.6.3",
"version": "1.6.4",
"publishConfig": {
"access": "public"
},
Expand Down Expand Up @@ -45,6 +45,7 @@
"commander": "^5.1.0",
"mustache": "^4.0.1",
"node-fetch": "^2.6.0",
"st-cp": "^2.0.0-beta.2",
"st-rm-rf": "^2.0.0-beta.1",
"typescript": "^3.8.3",
"valid-url": "^1.0.9",
Expand Down
292 changes: 161 additions & 131 deletions src/classes/object-property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,176 +119,206 @@ export class ObjectProperty implements IPropertyClass {
}),
headerParameterClassName: fun.headerParameters?.className,

isRequestBody: !!fun.requestBodyClass,
isRequestBodyJson: !!fun.requestBodyClass && fun.isRequestBodyJson,
requestBodyClass: fun.requestBodyClass,

forceInterceptor: fun.forceInterceptor,
isResponse: !!fun.responseClass,
isJsonResponse: !!fun.responseClass && fun.isJsonResponse,
isPlaintextResponse: fun.isPlaintextResponse,
isDownloadResponse: fun.isDownloadResponse,
responseClass: fun.responseClass
isRequestBody: !!fun.requestBody.requestBodyClass,
isRequestBodyJson: !!fun.requestBody.requestBodyClass && fun.requestBody.isRequestBodyJson,
requestBodyClass:
fun.requestBody.requestBodyCardinality === "oneOf"
? fun.requestBody.requestBodyClass.join(" | ")
: fun.requestBody.requestBodyClass,


forceInterceptor: fun.forceInterceptor,
isResponse: !!fun.response.responseClass,
isJsonResponse: !!fun.response.responseClass && fun.response.isJsonResponse,
isPlaintextResponse: fun.response.isPlaintextResponse,
isDownloadResponse: fun.response.isDownloadResponse,
responseClass: fun.response.responseClass
}
this.functions.push({data: data, imports: fun.imports, name: fun.functionName});
}
this.functions.push({data: data, imports: fun.imports, name: fun.functionName});
}

addProperty(prop: IProperty) {
const data: IMustacheProperty = {
isDescription: !!prop.description,
description: splitByLineBreak(prop.description),
required: prop.required,
value: prop.value,
propertyName: prop.propertyName,
isArray: prop.isArray,

addProperty(prop: IProperty) {
const data: IMustacheProperty = {
isDescription: !!prop.description,
description: splitByLineBreak(prop.description),
required: prop.required,
value: prop.value,
propertyName: prop.propertyName,
isArray: prop.isArray,
}
this.properties.push({data: data, import: prop.import, name: data.propertyName});
}
this.properties.push({data: data, import: prop.import, name: data.propertyName});
}

render(): IRenderResult {
const renderedFunctions = this.functions.map(fun => {

render(): IRenderResult {
const renderedFunctions = this.functions.map(fun => {
return {
imports: fun.imports || [],
name: fun.name,
render: renderMustache('function-class.mustache', fun.data)
}
}).sort((a, b) => a.name.localeCompare(b.name));
const renderProperties = this.properties.map((prop) => {
return {import: prop.import, name: prop.name, render: renderMustache('property-class.mustache', prop.data)}
}).sort((a, b) => a.name.localeCompare(b.name));

const isFunction = renderedFunctions.length > 0;

renderedFunctions.forEach(fun => this.imports.push(...fun.imports));
renderProperties.forEach(property => this.imports.push(property.import));

const viewData: IMustacheClass = {
className: this.className,
isInterface: Object.values(this.functions).length == 0,
isImport: this.imports.get().length > 0,
imports: this.imports.get().sort(),

isDescription: (this.description || '').length > 0,
description: this.description,

isFunction: isFunction,
function: renderedFunctions.map(rf => splitByLineBreak(rf.render)),

isProperties: renderProperties.length > 0,
properties: renderProperties.map(rf => splitByLineBreak(rf.render))
}
return {
imports: fun.imports || [],
name: fun.name,
render: renderMustache('function-class.mustache', fun.data)
classEnumName: this.className,
fileName: this.fileName,
render: renderMustache('service-class.mustache', viewData)
}
}).sort((a, b) => a.name.localeCompare(b.name));
const renderProperties = this.properties.map((prop) => {
return {import: prop.import, name: prop.name, render: renderMustache('property-class.mustache', prop.data)}
}).sort((a, b) => a.name.localeCompare(b.name));
}
}

interface IMustacheClass {
className: string;

const isFunction = renderedFunctions.length > 0;
isInterface: boolean;

renderedFunctions.forEach(fun => this.imports.push(...fun.imports));
renderProperties.forEach(property => this.imports.push(property.import));
isImport: boolean;
imports?: Array<string>;

const viewData: IMustacheClass = {
className: this.className,
isInterface: Object.values(this.functions).length == 0,
isImport: this.imports.get().length > 0,
imports: this.imports.get().sort(),
isDescription: boolean;
description?: Array<string>;

isDescription: (this.description || '').length > 0,
description: this.description,
isFunction: boolean;
function?: Array<Array<string>>;

isFunction: isFunction,
function: renderedFunctions.map(rf => splitByLineBreak(rf.render)),
isProperties: boolean;
properties: Array<Array<string>>;

isProperties: renderProperties.length > 0,
properties: renderProperties.map(rf => splitByLineBreak(rf.render))
}
return {
classEnumName: this.className,
fileName: this.fileName,
render: renderMustache('service-class.mustache', viewData)
}
}
}

interface IMustacheClass {
className: string;
interface IMustacheFunction {
functionName: string;
httpMethod: string;
originalPath: string;

isInterface: boolean;
isPathParameters: boolean;
pathParameterClassName?: string;
pathParameters?: Array<string>;

isImport: boolean;
imports?: Array<string>;
isJsonResponse: boolean;
isPlaintextResponse: boolean;
isDownloadResponse: boolean;
isRequestBodyJson: boolean;

isDescription: boolean;
description?: Array<string>;
isDescription: boolean;
description?: Array<string>;

isFunction: boolean;
function?: Array<Array<string>>;
isQueryParameters: boolean;
queryParameterClassName?: string;
queryParameters?: Array<string>;

isProperties: boolean;
properties: Array<Array<string>>;
isHeaderParameters: boolean;
headerParameterClassName?: string;
headerParameters?: Array<{ name: string, nameOriginal: string, required: boolean }>;

isRequestBody: boolean;
requestBodyClass?: string;

forceInterceptor: boolean;
isResponse: boolean;
responseClass?: string;
}

interface IMustacheFunction {
functionName: string;
httpMethod: string;
originalPath: string;
export interface IFunction {
functionName: string;
imports: Array<string>;

isPathParameters: boolean;
pathParameterClassName?: string;
pathParameters?: Array<string>;
httpMethod: string;
originalPath: string;

isJsonResponse: boolean;
isPlaintextResponse: boolean;
isDownloadResponse: boolean;
isRequestBodyJson: boolean;
pathParameters?: {
className: string;
params: Array<string>
};
headerParameters?: {
className: string;
params: { [parameterName: string]: IHeaderParameter }
};
queryParameters?: {
className: string;
params: Array<string>
};

isDescription: boolean;
description?: Array<string>;
requestBody: IFunctionRequestBody;
response: IFunctionResponse;

isQueryParameters: boolean;
queryParameterClassName?: string;
queryParameters?: Array<string>;
description: string;
forceInterceptor: boolean;
}

isHeaderParameters: boolean;
headerParameterClassName?: string;
headerParameters?: Array<{ name: string, nameOriginal: string, required: boolean }>;
export interface IFunctionResponse {
isJsonResponse: boolean;
isPlaintextResponse: boolean;
isDownloadResponse: boolean;
responseClass?: string;
}

isRequestBody: boolean;
requestBodyClass?: string;
export type RequestBodyCardinality = "oneOf" | "single";

forceInterceptor: boolean;
isResponse: boolean;
responseClass?: string;
interface IFunctionRequestBodyBase {
isRequestBodyJson: boolean;
requestBodyCardinality?: RequestBodyCardinality;
requestBodyClass?: string | string[];
}

export interface IFunction extends IFunctionResponse, IFunctionRequestBody {
functionName: string;
imports: Array<string>;

httpMethod: string;
originalPath: string;

pathParameters?: {
className: string;
params: Array<string>
};
headerParameters?: {
className: string;
params: { [parameterName: string]: IHeaderParameter }
};
queryParameters?: {
className: string;
params: Array<string>
};

description: string;
forceInterceptor: boolean;
export interface IFunctionRequestBodySingle extends IFunctionRequestBodyBase {
requestBodyCardinality: "single";
requestBodyClass?: string;
}

export interface IFunctionResponse {
isJsonResponse: boolean;
isPlaintextResponse: boolean;
isDownloadResponse: boolean;
responseClass?: string;
export interface IFunctionRequestBodyOneOf extends IFunctionRequestBodyBase {
requestBodyCardinality: "oneOf";
requestBodyClass?: string[];
}

export interface IFunctionRequestBody {
isRequestBodyJson: boolean;
requestBodyClass?: string;
export interface IFunctionRequestBodyNonJSON extends IFunctionRequestBodyBase {
isRequestBodyJson: false;
requestBodyCardinality: undefined;
requestBodyClass: undefined;
}

export type IFunctionRequestBody =
| IFunctionRequestBodySingle
| IFunctionRequestBodyOneOf;

interface IMustacheProperty {
propertyName: string;
propertyName: string;

isDescription: boolean;
description?: Array<string>;
isDescription: boolean;
description?: Array<string>;

required: boolean;
isArray: boolean;
value: string;
required: boolean;
isArray: boolean;
value: string;
}

export interface IProperty {
propertyName: string;
import: string;
description?: string;
required: boolean;
isArray: boolean;
value: string;
propertyName: string;
import: string;
description?: string;
required: boolean;
isArray: boolean;
value: string;
}
Loading

0 comments on commit c6ed409

Please sign in to comment.