Skip to content

Commit fab9d04

Browse files
author
Luis Fernando Planella Gonzalez
committed
Merge branch 'baflo-master'
2 parents 00b427b + da076ac commit fab9d04

6 files changed

+205
-8
lines changed

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
}

lib/options.ts

+12
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,16 @@ export interface Options {
126126

127127
/** List of paths to early exclude from the processing */
128128
excludePaths?: string[];
129+
130+
/**
131+
* When true, the expected response type in the request method names are not abbreviated and all response variants are kept.
132+
* Default is false.
133+
* When array is given, `mediaType` is expected to be a RegExp string matching the response media type. The first match in the array
134+
* will decide whether or how to shorten the media type. If no mediaType is given, it will always match.
135+
*
136+
* 'short': application/x-spring-data-compact+json -> getEntities$Json
137+
* 'tail': application/x-spring-data-compact+json -> getEntities$XSpringDataCompactJson
138+
* 'full': application/x-spring-data-compact+json -> getEntities$ApplicationXSpringDataCompactJson
139+
*/
140+
keepFullResponseMediaType?: boolean | Array<{ mediaType?: string; use: 'full' | 'tail' | 'short' }>;
129141
}

ng-openapi-gen-schema.json

+30
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,36 @@
222222
"description": "When true (default) models names will be camelized, besides having the first letter capitalized. Setting to false will prevent camelizing.",
223223
"default": "true",
224224
"type": "boolean"
225+
},
226+
"keepFullResponseMediaType": {
227+
"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",
228+
"default": "false",
229+
"anyOf": [
230+
{
231+
"type": "boolean"
232+
},
233+
{
234+
"type": "array",
235+
"items": {
236+
"type": "object",
237+
"required": [
238+
"use"
239+
],
240+
"properties": {
241+
"mediaType": {
242+
"type": "string"
243+
},
244+
"use": {
245+
"enum": [
246+
"full",
247+
"tail",
248+
"short"
249+
]
250+
}
251+
}
252+
}
253+
}
254+
]
225255
}
226256
}
227257
}

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
}

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",

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)