Skip to content

Commit 2cf4c3c

Browse files
committed
fix: path parameters are not correctly override, fixes #481
1 parent 6d1a9e5 commit 2cf4c3c

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

src/services/OpenAPIParser.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ export class OpenAPIParser {
4646

4747
constructor(
4848
spec: OpenAPISpec,
49-
specUrl: string | undefined,
50-
private options: RedocNormalizedOptions,
49+
specUrl?: string,
50+
private options: RedocNormalizedOptions = new RedocNormalizedOptions({}),
5151
) {
5252
this.validate(spec);
5353
this.preprocess(spec);
@@ -166,6 +166,13 @@ export class OpenAPIParser {
166166
return obj;
167167
}
168168

169+
shalowDeref<T extends object>(obj: OpenAPIRef | T): T {
170+
if (this.isRef(obj)) {
171+
return this.byRef<T>(obj.$ref)!;
172+
}
173+
return obj;
174+
}
175+
169176
/**
170177
* Merge allOf contsraints.
171178
* @param schema schema with allOF

src/services/models/Operation.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getOperationSummary,
1313
isAbsolutePath,
1414
JsonPointer,
15+
mergeParams,
1516
sortByRequired,
1617
stripTrailingSlash,
1718
} from '../../utils';
@@ -65,7 +66,9 @@ export class OperationModel implements IMenuItem {
6566
this.id =
6667
operationSpec.operationId !== undefined
6768
? 'operation/' + operationSpec.operationId
68-
: this.parent !== undefined ? this.parent.id + operationSpec._$ref : operationSpec._$ref;
69+
: this.parent !== undefined
70+
? this.parent.id + operationSpec._$ref
71+
: operationSpec._$ref;
6972

7073
this.name = getOperationSummary(operationSpec);
7174
this.description = operationSpec.description;
@@ -83,9 +86,11 @@ export class OperationModel implements IMenuItem {
8386
this.codeSamples = operationSpec['x-code-samples'] || [];
8487
this.path = JsonPointer.baseName(this._$ref, 2);
8588

86-
this.parameters = operationSpec.pathParameters
87-
.concat(operationSpec.parameters || [])
88-
.map(paramOrRef => new FieldModel(parser, paramOrRef, this._$ref, options));
89+
this.parameters = mergeParams(
90+
parser,
91+
operationSpec.pathParameters,
92+
operationSpec.parameters,
93+
).map(paramOrRef => new FieldModel(parser, paramOrRef, this._$ref, options));
8994

9095
if (options.requiredPropsFirst) {
9196
sortByRequired(this.parameters);

src/utils/__tests__/openapi.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import {
44
getStatusCodeType,
55
isOperationName,
66
isPrimitiveType,
7+
mergeParams,
78
} from '../';
89

10+
import { OpenAPIParser } from '../../services';
11+
import { OpenAPIParameter } from '../../types';
12+
913
describe('Utils', () => {
1014
describe('openapi getStatusCode', () => {
1115
it('Should return info for status codes within 100 and 200', () => {
@@ -183,4 +187,39 @@ describe('Utils', () => {
183187
expect(isPrimitiveType(schema)).toEqual(false);
184188
});
185189
});
190+
191+
describe('openapi mergeParams', () => {
192+
it('Should deduplicate params with same "name" and "in"', () => {
193+
const pathParams: OpenAPIParameter[] = [
194+
{
195+
name: 'param1',
196+
in: 'path',
197+
description: 'path',
198+
},
199+
{
200+
name: 'param2',
201+
in: 'path',
202+
},
203+
];
204+
const operationParams: OpenAPIParameter[] = [
205+
{
206+
name: 'param1',
207+
in: 'path',
208+
description: 'oper',
209+
},
210+
{
211+
name: 'param2',
212+
in: 'query',
213+
},
214+
];
215+
216+
const parser = new OpenAPIParser({ openapi: '3.0' } as any);
217+
218+
const res = mergeParams(parser, pathParams, operationParams) as OpenAPIParameter[];
219+
expect(res).toHaveLength(3);
220+
expect(res[0]).toEqual(pathParams[1]);
221+
expect(res[1]).toEqual(operationParams[0]);
222+
expect(res[2]).toEqual(operationParams[1]);
223+
});
224+
});
186225
});

src/utils/openapi.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { OpenAPIOperation, OpenAPISchema } from '../types';
1+
import { OpenAPIParser } from '../services/OpenAPIParser';
2+
import { OpenAPIOperation, OpenAPIParameter, OpenAPISchema, Referenced } from '../types';
23

34
export function getStatusCodeType(statusCode: string | number, defaultAsError = false): string {
45
if (statusCode === 'default') {
@@ -178,4 +179,24 @@ export function sortByRequired(
178179
});
179180
}
180181

182+
export function mergeParams(
183+
parser: OpenAPIParser,
184+
pathParams: Array<Referenced<OpenAPIParameter>> = [],
185+
operationParams: Array<Referenced<OpenAPIParameter>> = [],
186+
): Array<Referenced<OpenAPIParameter>> {
187+
const operationParamNames = {};
188+
operationParams.forEach(param => {
189+
param = parser.shalowDeref(param);
190+
operationParamNames[param.name + '_' + param.in] = true;
191+
});
192+
193+
// filter out path params overriden by operation ones with the same name
194+
pathParams = pathParams.filter(param => {
195+
param = parser.shalowDeref(param);
196+
return !operationParamNames[param.name + '_' + param.in];
197+
});
198+
199+
return pathParams.concat(operationParams);
200+
}
201+
181202
export const SECURITY_SCHEMES_SECTION = 'section/Authentication/';

0 commit comments

Comments
 (0)