Skip to content

Commit 73fc3b7

Browse files
committed
Adds option keepFullResponseMediaType
1 parent 75a957b commit 73fc3b7

6 files changed

+204
-8
lines changed

Diff for: lib/operation.ts

+39-6
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,13 @@ export class Operation {
201201
if (content && content.length > 0) {
202202
for (const type of content) {
203203
if (type && type.mediaType) {
204-
map.set(this.variantMethodPart(type), type);
204+
const part = this.variantMethodPart(type);
205+
206+
if (map.has(part)) {
207+
this.logger.warn(`Overwriting variant method part '${part}' for media type '${map.get(part)?.mediaType}' by media type '${type.mediaType}'.`);
208+
}
209+
210+
map.set(part, type);
205211
}
206212
}
207213
}
@@ -234,19 +240,46 @@ export class Operation {
234240
*/
235241
private variantMethodPart(content: Content | null): string {
236242
if (content) {
237-
let type = content.mediaType.replace(/\/\*/, '');
243+
const keep = this.keepFullResponseMediaType(content.mediaType);
244+
let type = content.mediaType;
245+
type = content.mediaType.replace(/\/\*/, '');
238246
if (type === '*' || type === 'application/octet-stream') {
239247
return '$Any';
240248
}
241-
type = last(type.split('/')) as string;
242-
const plus = type.lastIndexOf('+');
243-
if (plus >= 0) {
244-
type = type.substring(plus + 1);
249+
250+
if (keep !== 'full') {
251+
type = last(type.split('/')) as string;
252+
253+
if (keep !== 'tail') {
254+
const plus = type.lastIndexOf('+');
255+
if (plus >= 0) {
256+
type = type.substring(plus + 1);
257+
}
258+
}
245259
}
260+
246261
return this.options.skipJsonSuffix && type === 'json' ? '' : `$${typeName(type)}`;
247262
} else {
248263
return '';
249264
}
250265
}
251266

267+
/**
268+
* Returns hint, how the expected response type in the request method names should be abbreviated.
269+
*/
270+
private keepFullResponseMediaType(mediaType: string) {
271+
if (this.options.keepFullResponseMediaType === true) {
272+
return 'full';
273+
}
274+
275+
if (Array.isArray(this.options.keepFullResponseMediaType)) {
276+
for (const check of this.options.keepFullResponseMediaType) {
277+
if (check.mediaType === undefined || new RegExp(check.mediaType).test(mediaType)) {
278+
return check.use ?? 'short';
279+
}
280+
}
281+
}
282+
283+
return 'short';
284+
}
252285
}

Diff for: lib/options.ts

+11
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,15 @@ export interface Options {
120120

121121
/** When true, no verbose output will be displayed */
122122
silent?: boolean;
123+
124+
/**
125+
* When true, the expected response type in the request method names are not abbreviated and all response variants are kept.
126+
* When array is given, `mediaType` is expected to be a RegExp string matching the response media type. The first match in the array
127+
* will decide whether or how to shorten the media type. If no mediaType is given, it will always match.
128+
*
129+
* 'short': application/x-spring-data-compact+json -> getEntities$Json
130+
* 'tail': application/x-spring-data-compact+json -> getEntities$XSpringDataCompactJson
131+
* 'full': application/x-spring-data-compact+json -> getEntities$ApplicationXSpringDataCompactJson
132+
*/
133+
keepFullResponseMediaType?: boolean | Array<{ mediaType?: string; use: 'full' | 'tail' | 'short' }>;
123134
}

Diff for: ng-openapi-gen-schema.json

+30
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,36 @@
217217
"description": "When set to true, no verbose output will be displayed.",
218218
"default": "false",
219219
"type": "boolean"
220+
},
221+
"keepFullResponseMediaType": {
222+
"description": "When true, the expected response type in the request method names are not abbreviated and all response variants are kept.\\nWhen array is given, `mediaType` is expected to be a RegExp string matching the response media type. The first match in the array\\nwill decide whether or how to shorten the media type. If no mediaType is given, it will always match.\\n'short': application/x-spring-data-compact+json -> getEntities$Json\\n'tail': application/x-spring-data-compact+json -> getEntities$XSpringDataCompactJson\\n'full': application/x-spring-data-compact+json -> getEntities$ApplicationXSpringDataCompactJson",
223+
"default": "false",
224+
"anyOf": [
225+
{
226+
"type": "boolean"
227+
},
228+
{
229+
"type": "array",
230+
"items": {
231+
"type": "object",
232+
"required": [
233+
"use"
234+
],
235+
"properties": {
236+
"mediaType": {
237+
"type": "string"
238+
},
239+
"use": {
240+
"enum": [
241+
"full",
242+
"tail",
243+
"short"
244+
]
245+
}
246+
}
247+
}
248+
}
249+
]
220250
}
221251
}
222252
}

Diff for: test/all-operations.config.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,17 @@
33
"input": "all-operations.json",
44
"output": "out/all-operations",
55
"defaultTag": "noTag",
6-
"excludeParameters": ["X-Exclude"]
6+
"excludeParameters": [
7+
"X-Exclude"
8+
],
9+
"keepFullResponseMediaType": [
10+
{
11+
"mediaType": "spring",
12+
"use": "full"
13+
},
14+
{
15+
"mediaType": "hal\\+json",
16+
"use": "tail"
17+
}
18+
]
719
}

Diff for: test/all-operations.json

+60
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,66 @@
408408
}
409409
}
410410
},
411+
"/path8": {
412+
"get": {
413+
"tags": [
414+
"tag.tag2.tag3.tag4.tag5"
415+
],
416+
"responses": {
417+
"200": {
418+
"content": {
419+
"application/json": {
420+
"schema": {
421+
"type": "string"
422+
}
423+
},
424+
"application/hal+json": {
425+
"schema": {
426+
"type": "string"
427+
}
428+
},
429+
"application/x-spring-data-compact+json": {
430+
"schema": {
431+
"type": "string"
432+
}
433+
},
434+
"text/uri-list": {
435+
"schema": {
436+
"type": "string"
437+
}
438+
}
439+
},
440+
"description": "OK"
441+
}
442+
}
443+
},
444+
"post": {
445+
"tags": [
446+
"tag.tag2.tag3.tag4.tag5"
447+
],
448+
"requestBody": {
449+
"content": {
450+
"application/json": {
451+
"schema": {
452+
"type": "string"
453+
}
454+
}
455+
},
456+
"required": true
457+
},
458+
"responses": {
459+
"201": {
460+
"content": {
461+
"application/hal+json": {
462+
"schema": {
463+
"type": "string"
464+
}
465+
}
466+
}
467+
}
468+
}
469+
}
470+
},
411471
"/duplicated1": {
412472
"get": {
413473
"operationId": "duplicated",

Diff for: test/all-operations.spec.ts

+51-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('Generation tests using all-operations.json', () => {
2020
let gen: NgOpenApiGen;
2121

2222
beforeEach(() => {
23-
gen = new NgOpenApiGen(allOperationsSpec as OpenAPIObject, options);
23+
gen = new NgOpenApiGen(allOperationsSpec as OpenAPIObject, options as any);
2424
gen.generate();
2525
});
2626

@@ -533,4 +533,54 @@ describe('Generation tests using all-operations.json', () => {
533533
const success = operation.successResponse;
534534
expect(success?.statusCode).toEqual('204');
535535
});
536+
537+
538+
it('GET /path8', () => {
539+
const optionsWithCustomizedResponseType = { ...options } as Options;
540+
gen = new NgOpenApiGen(allOperationsSpec as OpenAPIObject, optionsWithCustomizedResponseType);
541+
gen.generate();
542+
const operation = gen.operations.get('path8Get');
543+
expect(operation).toBeDefined();
544+
545+
if (!operation) return;
546+
547+
// Assert each variant
548+
const vars = operation.variants;
549+
expect(vars.length).toBe(4);
550+
551+
const jsonPlain = vars[0];
552+
expect(jsonPlain.responseType).toBe('json');
553+
expect(jsonPlain.methodName).toBe('path8Get$Json');
554+
555+
const halJsonPlain = vars[1];
556+
expect(halJsonPlain.responseType).toBe('json');
557+
expect(halJsonPlain.methodName).toBe('path8Get$HalJson');
558+
559+
const compactJsonPlain = vars[2];
560+
expect(compactJsonPlain.responseType).toBe('json');
561+
expect(compactJsonPlain.methodName).toBe('path8Get$ApplicationXSpringDataCompactJson');
562+
563+
const text = vars[3];
564+
expect(text.responseType).toBe('text');
565+
expect(text.methodName).toBe('path8Get$UriList');
566+
});
567+
568+
569+
it('POST /path8', () => {
570+
const optionsWithCustomizedResponseType = { ...options } as Options;
571+
gen = new NgOpenApiGen(allOperationsSpec as OpenAPIObject, optionsWithCustomizedResponseType);
572+
gen.generate();
573+
const operation = gen.operations.get('path8Post');
574+
expect(operation).toBeDefined();
575+
expect(operation?.variants[0].responseType).toBe('json');
576+
577+
if (!operation) return;
578+
579+
// Assert each variant
580+
const vars = operation.variants;
581+
expect(vars.length).toBe(1);
582+
583+
const jsonPlain = vars[0];
584+
expect(jsonPlain.methodName).toBe('path8Post');
585+
});
536586
});

0 commit comments

Comments
 (0)