Skip to content

Commit 58e06a2

Browse files
committed
Merge remote-tracking branch 'tiholic/feature/omit-read-only'
2 parents 1d38e5c + b8ffc88 commit 58e06a2

15 files changed

+258
-14
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* The contents of this file are inspired from https://github.com/ts-essentials/ts-essentials#ReadonlyKeys
3+
*
4+
* The MIT License
5+
*
6+
* Copyright (c) 2018-2019 Chris Kaczor (github.com/krzkaczor)
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
* */
26+
27+
type Writable<T> = { -readonly [P in keyof T]: T[P] };
28+
29+
type IsEqualConsideringWritability<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
30+
? true
31+
: false;
32+
33+
type IsFullyWritable<T extends object> = IsEqualConsideringWritability<
34+
{ [Q in keyof T]: T[Q] },
35+
Writable<{ [Q in keyof T]: T[Q] }>
36+
>;
37+
38+
/** Gets keys of an object which are readonly */
39+
type ReadonlyKeys<T extends object> = {
40+
[P in keyof T]-?: IsFullyWritable<Pick<T, P>> extends true ? never : P;
41+
}[keyof T];
42+
43+
/**
44+
* Exclude keys of an object which are readonly
45+
* In case of union types containing types not extending object, type is inferred as-is
46+
* */
47+
export declare type OmitReadonly<T> = T extends object ? Omit<T, ReadonlyKeys<T>> : T;

src/templates/exportService.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { BaseHttpRequest } from '../core/BaseHttpRequest';
3030
import { OpenAPI } from '../core/OpenAPI';
3131
import { request as __request } from '../core/request';
3232
{{/if}}
33+
import type { OmitReadonly } from '../core/utils/OmitReadonly';
3334

3435
{{#equals @root.httpClient 'angular'}}
3536
@Injectable({

src/templates/partials/parameters.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
{{/if}}
1717
*/
1818
{{/ifdef}}
19-
{{{name}}}{{>isRequired}}: {{>type}},
19+
{{{name}}}{{>isRequired}}: {{>typeWithOmitReadOnly httpMethod=../method}},
2020
{{/each}}
2121
}
2222
{{~else}}
2323

2424
{{#each parameters}}
25-
{{{name}}}{{>isRequired}}: {{>type}}{{#if default}} = {{{default}}}{{/if}},
25+
{{{name}}}{{>isRequired}}: {{>typeWithOmitReadOnly httpMethod=../method}}{{#if default}} = {{{default}}}{{/if}},
2626
{{/each}}
2727
{{/if}}
2828
{{/if}}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{{#equals export "reference"}}
2+
{{#equals httpMethod "POST"~}}
3+
OmitReadonly<{{>type}}>
4+
{{~else equals httpMethod "PUT"~}}
5+
OmitReadonly<{{>type}}>
6+
{{~else equals httpMethod "PATCH"~}}
7+
OmitReadonly<{{>type}}>
8+
{{else}}
9+
{{~>type}}
10+
{{~/equals}}
11+
{{~else}}
12+
{{~>type}}
13+
{{~/equals}}

src/utils/registerHandlebarTemplates.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import nodeRequest from '../templates/core/node/request.hbs';
4646
import nodeSendRequest from '../templates/core/node/sendRequest.hbs';
4747
import templateCoreSettings from '../templates/core/OpenAPI.hbs';
4848
import templateCoreRequest from '../templates/core/request.hbs';
49+
import omitReadonly from '../templates/core/utils/OmitReadonly.hbs';
4950
import xhrGetHeaders from '../templates/core/xhr/getHeaders.hbs';
5051
import xhrGetRequestBody from '../templates/core/xhr/getRequestBody.hbs';
5152
import xhrGetResponseBody from '../templates/core/xhr/getResponseBody.hbs';
@@ -83,6 +84,7 @@ import partialTypeInterface from '../templates/partials/typeInterface.hbs';
8384
import partialTypeIntersection from '../templates/partials/typeIntersection.hbs';
8485
import partialTypeReference from '../templates/partials/typeReference.hbs';
8586
import partialTypeUnion from '../templates/partials/typeUnion.hbs';
87+
import typeWithOmitReadOnly from '../templates/partials/typeWithOmitReadOnly.hbs';
8688
import { registerHandlebarHelpers } from './registerHandlebarHelpers';
8789

8890
export interface Templates {
@@ -102,6 +104,9 @@ export interface Templates {
102104
request: Handlebars.TemplateDelegate;
103105
baseHttpRequest: Handlebars.TemplateDelegate;
104106
httpRequest: Handlebars.TemplateDelegate;
107+
utils: {
108+
omitReadonly: Handlebars.TemplateDelegate;
109+
};
105110
};
106111
}
107112

@@ -134,6 +139,9 @@ export const registerHandlebarTemplates = (root: {
134139
request: Handlebars.template(templateCoreRequest),
135140
baseHttpRequest: Handlebars.template(templateCoreBaseHttpRequest),
136141
httpRequest: Handlebars.template(templateCoreHttpRequest),
142+
utils: {
143+
omitReadonly: Handlebars.template(omitReadonly),
144+
},
137145
},
138146
};
139147

@@ -163,6 +171,7 @@ export const registerHandlebarTemplates = (root: {
163171
Handlebars.registerPartial('typeInterface', Handlebars.template(partialTypeInterface));
164172
Handlebars.registerPartial('typeReference', Handlebars.template(partialTypeReference));
165173
Handlebars.registerPartial('typeUnion', Handlebars.template(partialTypeUnion));
174+
Handlebars.registerPartial('typeWithOmitReadOnly', Handlebars.template(typeWithOmitReadOnly));
166175
Handlebars.registerPartial('typeIntersection', Handlebars.template(partialTypeIntersection));
167176
Handlebars.registerPartial('base', Handlebars.template(partialBase));
168177

src/utils/writeClient.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ describe('writeClient', () => {
3333
request: () => 'request',
3434
baseHttpRequest: () => 'baseHttpRequest',
3535
httpRequest: () => 'httpRequest',
36+
utils: {
37+
omitReadonly: () => 'omitReadonly',
38+
},
3639
},
3740
};
3841

src/utils/writeClient.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const writeClient = async (
5252
): Promise<void> => {
5353
const outputPath = resolve(process.cwd(), output);
5454
const outputPathCore = resolve(outputPath, 'core');
55+
const outputPathCoreUtils = resolve(outputPath, 'core', 'utils');
5556
const outputPathModels = resolve(outputPath, 'models');
5657
const outputPathSchemas = resolve(outputPath, 'schemas');
5758
const outputPathServices = resolve(outputPath, 'services');
@@ -62,8 +63,17 @@ export const writeClient = async (
6263

6364
if (exportCore) {
6465
await rmdir(outputPathCore);
65-
await mkdir(outputPathCore);
66-
await writeClientCore(client, templates, outputPathCore, httpClient, indent, clientName, request);
66+
await mkdir(outputPathCoreUtils);
67+
await writeClientCore(
68+
client,
69+
templates,
70+
outputPathCore,
71+
outputPathCoreUtils,
72+
httpClient,
73+
indent,
74+
clientName,
75+
request
76+
);
6777
}
6878

6979
if (exportServices) {

src/utils/writeClientClass.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ describe('writeClientClass', () => {
3333
request: () => 'request',
3434
baseHttpRequest: () => 'baseHttpRequest',
3535
httpRequest: () => 'httpRequest',
36+
utils: {
37+
omitReadonly: () => 'omitReadonly',
38+
},
3639
},
3740
};
3841

src/utils/writeClientCore.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,20 @@ describe('writeClientCore', () => {
3535
request: () => 'request',
3636
baseHttpRequest: () => 'baseHttpRequest',
3737
httpRequest: () => 'httpRequest',
38+
utils: {
39+
omitReadonly: () => 'omitReadonly',
40+
},
3841
},
3942
};
4043

41-
await writeClientCore(client, templates, '/', HttpClient.FETCH, Indent.SPACE_4);
44+
await writeClientCore(client, templates, '/', '/utils', HttpClient.FETCH, Indent.SPACE_4);
4245

4346
expect(writeFile).toBeCalledWith('/OpenAPI.ts', `settings${EOL}`);
4447
expect(writeFile).toBeCalledWith('/ApiError.ts', `apiError${EOL}`);
4548
expect(writeFile).toBeCalledWith('/ApiRequestOptions.ts', `apiRequestOptions${EOL}`);
4649
expect(writeFile).toBeCalledWith('/ApiResult.ts', `apiResult${EOL}`);
4750
expect(writeFile).toBeCalledWith('/CancelablePromise.ts', `cancelablePromise${EOL}`);
4851
expect(writeFile).toBeCalledWith('/request.ts', `request${EOL}`);
52+
expect(writeFile).toBeCalledWith('/utils/OmitReadonly.ts', `omitReadonly${EOL}`);
4953
});
5054
});

src/utils/writeClientCore.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type { Templates } from './registerHandlebarTemplates';
1414
* @param client Client object, containing, models, schemas and services
1515
* @param templates The loaded handlebar templates
1616
* @param outputPath Directory to write the generated files to
17+
* @param outputUtilsPath Directory to write the generated util files to
1718
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
1819
* @param indent Indentation options (4, 2 or tab)
1920
* @param clientName Custom client class name
@@ -23,6 +24,7 @@ export const writeClientCore = async (
2324
client: Client,
2425
templates: Templates,
2526
outputPath: string,
27+
outputUtilsPath: string,
2628
httpClient: HttpClient,
2729
indent: Indent,
2830
clientName?: string,
@@ -43,6 +45,7 @@ export const writeClientCore = async (
4345
await writeFile(resolve(outputPath, 'ApiResult.ts'), i(templates.core.apiResult(context), indent));
4446
await writeFile(resolve(outputPath, 'CancelablePromise.ts'), i(templates.core.cancelablePromise(context), indent));
4547
await writeFile(resolve(outputPath, 'request.ts'), i(templates.core.request(context), indent));
48+
await writeFile(resolve(outputUtilsPath, 'OmitReadonly.ts'), i(templates.core.utils.omitReadonly(context), indent));
4649

4750
if (isDefined(clientName)) {
4851
await writeFile(resolve(outputPath, 'BaseHttpRequest.ts'), i(templates.core.baseHttpRequest(context), indent));

0 commit comments

Comments
 (0)