Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 5baa347

Browse files
committed
feat(oas3): support explode query parameters
1 parent ea93b2a commit 5baa347

File tree

9 files changed

+129
-17
lines changed

9 files changed

+129
-17
lines changed

packages/fury-adapter-oas3-parser/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
## Master
44

5+
### Enhancements
6+
57
- Support for `servers` in `Path Item Object` and `Operation Object`
68

9+
- 'Parameter Object' 'explode' style is partially supported, it can be used
10+
with query parameters.
11+
712
## 0.11.0 (2020-04-20)
813

914
### Enhancements

packages/fury-adapter-oas3-parser/STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Key:
122122
| Field Name | Support |
123123
|:--|:--|
124124
| style ||
125-
| explode | |
125+
| explode | ~ |
126126
| allowReserved ||
127127
| schema ||
128128
| example ||

packages/fury-adapter-oas3-parser/lib/parser/oas/parseOperationObject.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,14 @@ function hrefVariablesFromParameters(namespace, parameters) {
7575

7676
function hrefFromParameters(path, queryParameters) {
7777
const href = path.clone();
78-
const queryString = queryParameters.keys().join(',');
78+
const keys = queryParameters.map((value, key, member) => {
79+
if (member.explode) {
80+
return `${key.toValue()}*`;
81+
}
82+
83+
return key.toValue();
84+
});
85+
const queryString = keys.join(',');
7986
href.content += `{?${queryString}}`;
8087
return href;
8188
}

packages/fury-adapter-oas3-parser/lib/parser/oas/parseParameterObject.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const parseBoolean = require('../parseBoolean');
1616
const name = 'Parameter Object';
1717
const requiredKeys = ['name', 'in'];
1818
const unsupportedKeys = [
19-
'deprecated', 'allowEmptyValue', 'style', 'explode', 'allowReserved',
19+
'deprecated', 'allowEmptyValue', 'style', 'allowReserved',
2020
'schema', 'examples', 'content',
2121
];
2222
const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys));
@@ -60,6 +60,11 @@ function validateRequiredForPathParameter(context, object, parameter) {
6060
return parseResult;
6161
}
6262

63+
const hasExplodeWithoutQueryIn = R.allPass([
64+
object => object.getValue('explode') === true,
65+
object => object.getValue('in') !== 'query',
66+
]);
67+
6368
/**
6469
* Parse Parameter Object
6570
*
@@ -95,6 +100,7 @@ function parseParameterObject(context, object) {
95100
[hasKey('in'), parseIn],
96101
[hasKey('description'), parseString(context, name, false)],
97102
[hasKey('required'), parseBoolean(context, name, false)],
103+
[hasKey('explode'), parseBoolean(context, name, false)],
98104
[hasKey('example'), e => e.clone()],
99105

100106
[isUnsupportedKey, createUnsupportedMemberWarning(namespace, name)],
@@ -137,12 +143,25 @@ function parseParameterObject(context, object) {
137143
R.when(nameContainsReservedCharacter, createUnsupportedNameError),
138144
R.when(nameContainsReservedHeaderName, createReservedHeaderNamesWarning));
139145

146+
const createUnsupportedExplodeWarning = (object) => {
147+
const member = object.getMember('explode');
148+
const inValue = object.getValue('in');
149+
const message = `'${name}' '${member.key.toValue()}' is unsupported in ${inValue}`;
150+
return createWarning(namespace, message, member.key);
151+
};
152+
153+
const attachWarning = R.curry((createWarning, value) => {
154+
const warning = createWarning(value);
155+
return new namespace.elements.ParseResult([value, warning]);
156+
});
157+
140158
const parseParameter = pipeParseResult(namespace,
141159
parseObject(context, name, parseMember, requiredKeys),
142160
R.when(hasLocation('path'), R.curry(validateRequiredForPathParameter)(context, object)),
143161
R.when(hasLocation('path'), validatePathName),
144162
R.when(hasLocation('query'), sanitizeQueryName),
145163
R.when(hasLocation('header'), validateHeaderName),
164+
R.when(hasExplodeWithoutQueryIn, attachWarning(createUnsupportedExplodeWarning)),
146165
(parameter) => {
147166
const example = parameter.get('example');
148167
const member = new namespace.elements.Member(parameter.get('name'), example);
@@ -159,6 +178,7 @@ function parseParameterObject(context, object) {
159178
}
160179

161180
member.in = parameter.getValue('in');
181+
member.explode = parameter.getValue('explode');
162182

163183
return member;
164184
});

packages/fury-adapter-oas3-parser/lib/parser/oas/parseParameterObjects.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ function parseParameterObjects(context, name, array) {
2929

3030
// Convert an array of parameters into the correct types
3131
const convertParameters = R.cond([
32-
[isPathOrQuery, member => new namespace.elements.HrefVariables(member.value.clone().content)],
32+
[isPathOrQuery, member => new namespace.elements.HrefVariables(member.value.content.map((element) => {
33+
const member = element.clone();
34+
member.explode = element.explode;
35+
return member;
36+
}))],
3337
// FIXME when headers and cookies are supported these should be converted
3438
[R.T, member => member.clone()],
3539
]);

packages/fury-adapter-oas3-parser/lib/parser/oas/parsePathItemObject.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,15 @@ function hrefFromParameters(path, parameters) {
108108
const href = path.clone();
109109

110110
if (parameters && parameters.get('query')) {
111-
const queryString = parameters.get('query').keys().join(',');
111+
const queryString = parameters.get('query')
112+
.map((value, key, member) => {
113+
if (member.explode) {
114+
return `${key.toValue()}*`;
115+
}
116+
117+
return key.toValue();
118+
})
119+
.join(',');
112120
href.content += `{?${queryString}}`;
113121
}
114122

packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseOperationObject-test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,27 @@ describe('Operation Object', () => {
322322
expect(transition.href.toValue()).to.equal('/{?categories}');
323323
});
324324

325+
it('exposes query parameter in href with explode', () => {
326+
const operation = new namespace.elements.Member('get', {
327+
parameters: [
328+
{
329+
name: 'categories',
330+
in: 'query',
331+
explode: true,
332+
},
333+
],
334+
responses: {},
335+
});
336+
337+
const parseResult = parse(context, path, operation);
338+
339+
expect(parseResult.length).to.equal(1);
340+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Transition);
341+
342+
const transition = parseResult.get(0);
343+
expect(transition.href.toValue()).to.equal('/{?categories*}');
344+
});
345+
325346
it('exposes multiple query parameter in href', () => {
326347
const operation = new namespace.elements.Member('get', {
327348
parameters: [

packages/fury-adapter-oas3-parser/test/unit/parser/oas/parseParameterObject-test.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,45 @@ describe('Parameter Object', () => {
317317
});
318318
});
319319

320+
describe('#explode', () => {
321+
it('provides a warning when explode is not a boolean', () => {
322+
const parameter = new namespace.elements.Object({
323+
name: 'example',
324+
in: 'query',
325+
explode: 1,
326+
});
327+
328+
const parseResult = parse(context, parameter);
329+
expect(parseResult.length).to.equal(2);
330+
expect(parseResult).to.contain.warning("'Parameter Object' 'explode' is not a boolean");
331+
});
332+
333+
it('provides an unsupported warning when explode is used in header parameter', () => {
334+
const parameter = new namespace.elements.Object({
335+
name: 'example',
336+
in: 'header',
337+
explode: true,
338+
});
339+
340+
const parseResult = parse(context, parameter);
341+
expect(parseResult.length).to.equal(2);
342+
expect(parseResult).to.contain.warning("'Parameter Object' 'explode' is unsupported in header");
343+
});
344+
345+
it('provides an unsupported warning when explode is used in path parameter', () => {
346+
const parameter = new namespace.elements.Object({
347+
name: 'example',
348+
in: 'path',
349+
required: true,
350+
explode: true,
351+
});
352+
353+
const parseResult = parse(context, parameter);
354+
expect(parseResult.length).to.equal(2);
355+
expect(parseResult).to.contain.warning("'Parameter Object' 'explode' is unsupported in path");
356+
});
357+
});
358+
320359
describe('#required', () => {
321360
it('create typeAttribute required', () => {
322361
const parameter = new namespace.elements.Object({
@@ -467,18 +506,6 @@ describe('Parameter Object', () => {
467506
expect(parseResult).to.contain.warning("'Parameter Object' contains unsupported key 'style'");
468507
});
469508

470-
it('provides warning for unsupported explode property', () => {
471-
const parameter = new namespace.elements.Object({
472-
name: 'example',
473-
in: 'query',
474-
explode: true,
475-
});
476-
477-
const parseResult = parse(context, parameter);
478-
479-
expect(parseResult).to.contain.warning("'Parameter Object' contains unsupported key 'explode'");
480-
});
481-
482509
it('provides warning for unsupported allowReserved property', () => {
483510
const parameter = new namespace.elements.Object({
484511
name: 'example',

packages/fury-adapter-oas3-parser/test/unit/parser/oas/parsePathItemObject-test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,26 @@ describe('Path Item Object', () => {
226226
expect(resource.href.toValue()).to.equal('/{?categories}');
227227
});
228228

229+
it('exposes query parameter in href with explode', () => {
230+
const path = new namespace.elements.Member('/', {
231+
parameters: [
232+
{
233+
name: 'categories',
234+
in: 'query',
235+
explode: true,
236+
},
237+
],
238+
});
239+
240+
const parseResult = parse(context, path);
241+
242+
expect(parseResult.length).to.equal(1);
243+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Resource);
244+
245+
const resource = parseResult.get(0);
246+
expect(resource.href.toValue()).to.equal('/{?categories*}');
247+
});
248+
229249
it('exposes multiple query parameter in href', () => {
230250
const path = new namespace.elements.Member('/', {
231251
parameters: [

0 commit comments

Comments
 (0)