Skip to content

Commit

Permalink
Merge pull request #40 from nicolas-chaulet/fix/generics-flag
Browse files Browse the repository at this point in the history
fix(types): add explicit flag for generics
  • Loading branch information
mrlubos authored Mar 7, 2024
2 parents cbb633e + 9a5f2aa commit 2182aba
Show file tree
Hide file tree
Showing 27 changed files with 140 additions and 74 deletions.
2 changes: 2 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const params = program
.option('--exportSchemas <value>', 'Write schemas to disk', false)
.option('--indent <value>', 'Indentation options [4, 2, tab]', '4')
.option('--postfixServices <value>', 'Service name postfix', 'Service')
.option('--serviceResponse [value]', 'Define shape of returned value from service calls')
.option('--useDateType <value>', 'Output Date instead of string for the format "date-time" in the models', false)
.option('--useOperationId <value>', 'Use operation id to generate operation names', true)
.option('--postfixModels <value>', 'Model name postfix')
Expand Down Expand Up @@ -55,6 +56,7 @@ if (OpenAPI) {
postfixModels: params.postfixModels,
postfixServices: params.postfixServices,
request: params.request,
serviceResponse: params.serviceResponse,
useDateType: JSON.parse(params.useDateType) === true,
useOperationId: JSON.parse(params.useOperationId) === true,
useOptions: JSON.parse(params.useOptions) === true,
Expand Down
5 changes: 4 additions & 1 deletion src/client/interfaces/Options.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { HttpClient } from '../../HttpClient';
import { Indent } from '../interfaces/Indent';
import { Indent } from '../../Indent';

export type ServiceResponse = 'body' | 'generics' | 'response';

export interface Options {
autoformat?: boolean;
Expand All @@ -15,6 +17,7 @@ export interface Options {
postfixModels?: string;
postfixServices?: string;
request?: string;
serviceResponse?: ServiceResponse;
useDateType?: boolean;
useOperationId?: boolean;
useOptions?: boolean;
Expand Down
24 changes: 14 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,24 @@ export { Indent } from './Indent';
* Generate the OpenAPI client. This method will read the OpenAPI specification and based on the
* given language it will generate the client, including the typed models, validation schemas,
* service layer, etc.
* @param input The relative location of the OpenAPI spec
* @param output The relative location of the output directory
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
* @param clientName Custom client class name
* @param useOptions Use options or arguments functions
* @param useUnionTypes Use union types instead of enums
* @param autoformat Process generated files with autoformatter
* @param clientName Custom client class name
* @param exportCore Generate core client classes
* @param exportServices Generate services
* @param exportModels Generate models
* @param exportSchemas Generate schemas
* @param useDateType Output Date instead of string for the format "date-time" in the models
* @param useOperationId should the operationId be used when generating operation names
* @param exportServices Generate services
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
* @param indent Indentation options (4, 2 or tab)
* @param postfixServices Service name postfix
* @param input The relative location of the OpenAPI spec
* @param output The relative location of the output directory
* @param postfixModels Model name postfix
* @param postfixServices Service name postfix
* @param request Path to custom request file
* @param serviceResponse Define shape of returned value from service calls
* @param useDateType Output Date instead of string for the format "date-time" in the models
* @param useOperationId should the operationId be used when generating operation names
* @param useOptions Use options or arguments functions
* @param useUnionTypes Use union types instead of enums
* @param write Write the files to disk (true or false)
*/
export const generate = async (options: Options): Promise<void> => {
Expand All @@ -46,6 +47,7 @@ export const generate = async (options: Options): Promise<void> => {
indent = Indent.SPACE_4,
postfixModels = '',
postfixServices = 'Service',
serviceResponse = 'body',
useDateType = false,
useOptions = false,
useUnionTypes = false,
Expand All @@ -55,6 +57,7 @@ export const generate = async (options: Options): Promise<void> => {
const openApiVersion = getOpenApiVersion(openApi);
const templates = registerHandlebarTemplates({
httpClient,
serviceResponse,
useUnionTypes,
useOptions,
});
Expand Down Expand Up @@ -90,6 +93,7 @@ export const generate = async (options: Options): Promise<void> => {
indent,
postfixModels,
postfixServices,
serviceResponse,
useDateType,
useOptions,
useUnionTypes,
Expand Down
6 changes: 5 additions & 1 deletion src/templates/core/axios/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import type { OpenAPIConfig } from './OpenAPI';
* @param config The OpenAPI configuration object
* @param options The request options from the service
* @param axiosClient The axios client instance to use
* @returns CancelablePromise<T>
* @returns CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}>
* @throws ApiError
*/
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => {
Expand All @@ -90,7 +90,11 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, ax

catchErrorCodes(options, result);

{{#equals @root.serviceResponse 'generics'}}
resolve(config.RESULT === 'raw' ? result : result.body);
{{else}}
resolve({{#equals @root.serviceResponse 'body'}}result.body{{else}}result{{/equals}});
{{/equals}}
}
} catch (error) {
reject(error);
Expand Down
8 changes: 6 additions & 2 deletions src/templates/core/fetch/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ import type { OpenAPIConfig } from './OpenAPI';
* Request method
* @param config The OpenAPI configuration object
* @param options The request options from the service
* @returns CancelablePromise<T>
* @returns CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}>
* @throws ApiError
*/
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}> => {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(config, options);
Expand All @@ -82,7 +82,11 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C

catchErrorCodes(options, result);

{{#equals @root.serviceResponse 'generics'}}
resolve(config.RESULT === 'raw' ? result : result.body);
{{else}}
resolve({{#equals @root.serviceResponse 'body'}}result.body{{else}}result{{/equals}});
{{/equals}}
}
} catch (error) {
reject(error);
Expand Down
8 changes: 6 additions & 2 deletions src/templates/core/node/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ import type { OpenAPIConfig } from './OpenAPI';
* Request method
* @param config The OpenAPI configuration object
* @param options The request options from the service
* @returns CancelablePromise<T>
* @returns CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}>
* @throws ApiError
*/
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}> => {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(config, options);
Expand All @@ -87,7 +87,11 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C

catchErrorCodes(options, result);

{{#equals @root.serviceResponse 'generics'}}
resolve(config.RESULT === 'raw' ? result : result.body);
{{else}}
resolve({{#equals @root.serviceResponse 'body'}}result.body{{else}}result{{/equals}});
{{/equals}}
}
} catch (error) {
reject(error);
Expand Down
6 changes: 3 additions & 3 deletions src/templates/core/request.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{~#equals @root.httpClient 'fetch'}}{{>fetch/request}}{{/equals~}}
{{~#equals @root.httpClient 'xhr'}}{{>xhr/request}}{{/equals~}}
{{~#equals @root.httpClient 'axios'}}{{>axios/request}}{{/equals~}}
{{~#equals @root.httpClient 'angular'}}{{>angular/request}}{{/equals~}}
{{~#equals @root.httpClient 'axios'}}{{>axios/request}}{{/equals~}}
{{~#equals @root.httpClient 'fetch'}}{{>fetch/request}}{{/equals~}}
{{~#equals @root.httpClient 'node'}}{{>node/request}}{{/equals~}}
{{~#equals @root.httpClient 'xhr'}}{{>xhr/request}}{{/equals~}}
8 changes: 6 additions & 2 deletions src/templates/core/xhr/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ import type { OpenAPIConfig } from './OpenAPI';
* Request method
* @param config The OpenAPI configuration object
* @param options The request options from the service
* @returns CancelablePromise<T>
* @returns CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}>
* @throws ApiError
*/
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<{{#equals @root.serviceResponse 'response'}}ApiResult<T>{{else}}T{{/equals}}> => {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(config, options);
Expand All @@ -85,7 +85,11 @@ export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): C

catchErrorCodes(options, result);

{{#equals @root.serviceResponse 'generics'}}
resolve(config.RESULT === 'raw' ? result : result.body);
{{else}}
resolve({{#equals @root.serviceResponse 'body'}}result.body{{else}}result{{/equals}});
{{/equals}}
}
} catch (error) {
reject(error);
Expand Down
10 changes: 9 additions & 1 deletion src/templates/exportService.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import type { {{{this}}} } from '../models/{{{this}}}';
{{/each}}

{{/if}}
{{#equals @root.serviceResponse 'response'}}
import type { ApiResult } from '../core/ApiResult';
{{/equals}}
{{#notEquals @root.httpClient 'angular'}}
import type { CancelablePromise } from '../core/CancelablePromise';
{{/notEquals}}
Expand All @@ -28,19 +31,24 @@ import type { BaseHttpRequest } from '../core/BaseHttpRequest';
{{/equals}}
{{else}}
{{#if @root.useOptions}}
{{#equals @root.serviceResponse 'generics'}}
import { mergeOpenApiConfig, OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
import type { TApiResponse, TConfig, TResult } from '../core/types';
{{else}}
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
{{/equals}}
{{else}}
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
{{/if}}
{{/if}}

{{#if @root.useOptions}}
{{#each operations}}
{{#if parameters}}
type {{{nameOperationDataType name}}} = {
export type {{{nameOperationDataType name}}} = {
{{#each parameters}}
{{{name}}}{{>isRequired}}: {{>type}},
{{/each}}
Expand Down
14 changes: 12 additions & 2 deletions src/templates/partials/destructureData.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@ const {
} = data;
{{/if}}
{{/if}}
{{~else~}}
{{else~}}
{{#if @root.useOptions~}}
{{~#notEquals @root.serviceResponse 'generics'~}}
{{~#if parameters~}}
const {
{{#each parameters}}
{{{name}}}{{#if default}} = {{{default}}}{{/if}},
{{/each}}
} = data;
{{/if}}
{{else}}
const {
{{#each parameters}}
{{{name}}}{{#if default}} = {{{default}}}{{/if}},
{{/each}}
...overrides
} = data;
{{/notEquals}}
{{/if}}
{{/if}}
{{~/if~}}
4 changes: 4 additions & 0 deletions src/templates/partials/operationParameters.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
{{~#if @root.exportClient~}}
{{#if parameters}}data: {{{nameOperationDataType name}}}{{#ifOperationDataOptional parameters}} = {}{{/ifOperationDataOptional}}{{/if}}
{{~else~}}
{{~#notEquals @root.serviceResponse 'generics'~}}
{{#if parameters}}data: {{{nameOperationDataType name}}}{{#ifOperationDataOptional parameters}} = {}{{/ifOperationDataOptional}}{{/if}}
{{~else~}}
data: {{#if parameters}}{{{nameOperationDataType name}}} & {{/if}}TConfig<T>{{#ifOperationDataOptional parameters}} = {}{{/ifOperationDataOptional}}
{{~/notEquals~}}
{{~/if~}}
{{~else}}
{{#if parameters}}
Expand Down
19 changes: 14 additions & 5 deletions src/templates/partials/operationResult.hbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
{{~#if @root.useOptions~}}{{~#unless @root.exportClient~}}
TApiResponse<T, {{/unless}}{{/if~}}
{{~#if @root.useOptions~}}
{{~#notEquals @root.serviceResponse 'generics'~}}
{{~#equals @root.serviceResponse 'response'}}ApiResult<{{/equals~}}
{{~else~}}
{{~#unless @root.exportClient}}TApiResponse<T, {{/unless~}}
{{~/notEquals~}}
{{~/if~}}
{{~#if results~}}
{{#each results}}{{>type}}{{#unless @last}} | {{/unless}}{{/each}}
{{~else~}}
void
{{~/if~}}
{{~#if @root.useOptions~}}{{~#unless @root.exportClient~}}
>
{{~/unless~}}{{~/if~}}
{{~#if @root.useOptions~}}
{{~#notEquals @root.serviceResponse 'generics'~}}
{{~#equals @root.serviceResponse 'response'}}>{{/equals~}}
{{~else~}}
{{~#unless @root.exportClient}}>{{/unless~}}
{{~/notEquals~}}
{{~/if~}}
2 changes: 2 additions & 0 deletions src/templates/partials/operationTypes.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{{#if @root.useOptions~}}
{{#equals @root.serviceResponse 'generics'}}
{{~#unless @root.exportClient~}}
<T extends TResult>
{{~/unless~}}
{{/equals}}
{{~/if}}
4 changes: 4 additions & 0 deletions src/templates/partials/requestConfig.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{{~#if @root.useOptions~}}
{{~#equals @root.serviceResponse 'generics'~}}
mergeOpenApiConfig(OpenAPI, overrides)
{{~else~}}
OpenAPI
{{~/equals~}}
{{~else~}}
OpenAPI
{{~/if~}}
1 change: 1 addition & 0 deletions src/utils/registerHandlebarHelpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ describe('registerHandlebarHelpers', () => {
it('should register the helpers', () => {
registerHandlebarHelpers({
httpClient: HttpClient.FETCH,
serviceResponse: 'body',
useOptions: false,
useUnionTypes: false,
});
Expand Down
2 changes: 1 addition & 1 deletion src/utils/registerHandlebarHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const escapeEnumName = (name?: string) => {
};

export const registerHandlebarHelpers = (
root: Pick<Required<Options>, 'httpClient' | 'useOptions' | 'useUnionTypes'>
root: Pick<Required<Options>, 'httpClient' | 'serviceResponse' | 'useOptions' | 'useUnionTypes'>
): void => {
Handlebars.registerHelper('camelCase', function (value: string): string {
return camelCase(value);
Expand Down
1 change: 1 addition & 0 deletions src/utils/registerHandlebarTemplates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ describe('registerHandlebarTemplates', () => {
it('should return correct templates', () => {
const templates = registerHandlebarTemplates({
httpClient: HttpClient.FETCH,
serviceResponse: 'body',
useOptions: false,
useUnionTypes: false,
});
Expand Down
2 changes: 1 addition & 1 deletion src/utils/registerHandlebarTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export interface Templates {
* so we can easily access the templates in our generator/write functions.
*/
export const registerHandlebarTemplates = (
root: Pick<Required<Options>, 'httpClient' | 'useOptions' | 'useUnionTypes'>
root: Pick<Required<Options>, 'httpClient' | 'serviceResponse' | 'useOptions' | 'useUnionTypes'>
): Templates => {
registerHandlebarHelpers(root);

Expand Down
1 change: 1 addition & 0 deletions src/utils/writeClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('writeClient', () => {
httpClient: HttpClient.FETCH,
postfixModels: 'AppClient',
postfixServices: 'Service',
serviceResponse: 'body',
useDateType: false,
useOptions: false,
useUnionTypes: false,
Expand Down
12 changes: 3 additions & 9 deletions src/utils/writeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const writeClient = async (
| 'output'
| 'postfixModels'
| 'postfixServices'
| 'serviceResponse'
| 'useDateType'
| 'useOptions'
| 'useUnionTypes'
Expand All @@ -49,6 +50,7 @@ export const writeClient = async (
| 'output'
| 'postfixModels'
| 'postfixServices'
| 'serviceResponse'
| 'useDateType'
| 'useOptions'
| 'useUnionTypes'
Expand All @@ -74,15 +76,7 @@ export const writeClient = async (
const outputPathCore = Path.resolve(outputPath, 'core');
await rmdir(outputPathCore);
await mkdir(outputPathCore);
await writeClientCore(
client,
templates,
outputPathCore,
options.httpClient,
options.indent,
options.clientName,
options.request
);
await writeClientCore(client, templates, outputPathCore, options);
}

if (options.exportServices) {
Expand Down
Loading

0 comments on commit 2182aba

Please sign in to comment.