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

Commit 3430c0d

Browse files
committed
feat(oas3): support header parameters
Closes #72
1 parent 61b36fa commit 3430c0d

File tree

8 files changed

+299
-17
lines changed

8 files changed

+299
-17
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
- Added primitive support for generating a JSON message body from a schema for
1111
JSON media types. Referencing is not supported for this feature.
1212

13+
- Added support for header parameters.
14+
1315
### Bug Fixes
1416

1517
- Prevents an exception being raised due to improper handling of invalid

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Key:
103103
|:--|:--|
104104
| path ||
105105
| query ||
106-
| header | |
106+
| header | |
107107
| cookie ||
108108

109109
## Request Body Object

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ function parseOperationObject(context, path, member) {
135135
transition.push(description);
136136
}
137137

138+
const transactions = createTransactions(namespace, member, operation);
139+
transition.content = transition.content.concat(transactions);
140+
138141
const parameters = operation.get('parameters');
139142
if (parameters) {
140143
const queryParameters = parameters.get('query');
@@ -143,10 +146,20 @@ function parseOperationObject(context, path, member) {
143146
}
144147

145148
transition.hrefVariables = hrefVariablesFromParameters(namespace, parameters);
146-
}
147149

148-
const transactions = createTransactions(namespace, member, operation);
149-
transition.content = transition.content.concat(transactions);
150+
const headerParameters = parameters.get('header');
151+
if (headerParameters) {
152+
transactions.map(transaction => transaction.request).forEach((request) => {
153+
const headers = R.or(request.headers, new namespace.elements.HttpHeaders());
154+
155+
headers.content = headers.content.concat(
156+
R.reject(member => !headers.include(member.key.toValue()).isEmpty, headerParameters.content)
157+
);
158+
159+
request.headers = headers;
160+
});
161+
}
162+
}
150163

151164
return transition;
152165
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const isValidInValue = R.anyPass([
2525
hasValue('query'), hasValue('header'), hasValue('path'), hasValue('cookie'),
2626
]);
2727
const isSupportedIn = R.anyPass([
28-
hasValue('path'), hasValue('query'),
28+
hasValue('path'), hasValue('query'), hasValue('header'),
2929
]);
3030

3131
const unreservedCharacterRegex = /^[A-z0-9\\.\\_\\~\\-]+$/;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ function parseParameters(context, path, member) {
7575
const parseParameter = R.cond([
7676
[hasKey('path'), R.compose(validateHrefVariablesInPath(namespace, path), getValue)],
7777
[hasKey('query'), member => member],
78+
[hasKey('header'), member => member],
7879
]);
7980

8081
const parseParameters = pipeParseResult(namespace,
@@ -164,6 +165,23 @@ function parsePathItemObject(context, member) {
164165
.map(getValue);
165166
resource.content = resource.content.concat(methods);
166167

168+
if (parameters && parameters.get('header')) {
169+
const headerParameters = parameters.get('header');
170+
171+
const transactions = R.chain(method => method.transactions.elements, methods);
172+
const requests = R.map(transaction => transaction.request, transactions);
173+
174+
requests.forEach((request) => {
175+
const headers = R.or(request.headers, new namespace.elements.HttpHeaders());
176+
177+
headers.content = headers.content.concat(
178+
R.reject(member => !headers.include(member.key.toValue()).isEmpty, headerParameters.content)
179+
);
180+
181+
request.headers = headers;
182+
});
183+
}
184+
167185
return resource;
168186
});
169187

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

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,133 @@ describe('Operation Object', () => {
378378
expect(transition.hrefVariables.getMember('resource')).to.be.instanceof(namespace.elements.Member);
379379
});
380380
});
381+
382+
describe('header parameters', () => {
383+
it('exposes header parameter in request headers', () => {
384+
const operation = new namespace.elements.Member('get', {
385+
parameters: [
386+
{
387+
name: 'Accept',
388+
in: 'header',
389+
example: 'application/json',
390+
},
391+
],
392+
responses: {
393+
200: {
394+
description: 'dummy',
395+
},
396+
},
397+
});
398+
399+
const parseResult = parse(context, path, operation);
400+
401+
expect(parseResult.length).to.equal(1);
402+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Transition);
403+
404+
const transition = parseResult.get(0);
405+
expect(transition.transactions.length).to.equal(1);
406+
407+
const transaction = transition.transactions.get(0);
408+
const { request } = transaction;
409+
410+
expect(request.headers).to.be.instanceof(namespace.elements.HttpHeaders);
411+
expect(request.headers.toValue()).to.deep.equal([
412+
{
413+
key: 'Accept',
414+
value: 'application/json',
415+
},
416+
]);
417+
});
418+
419+
it('does not override request body content type header', () => {
420+
const operation = new namespace.elements.Member('post', {
421+
parameters: [
422+
{
423+
name: 'Content-Type',
424+
in: 'header',
425+
example: 'application/json',
426+
},
427+
],
428+
requestBody: {
429+
content: {
430+
'application/xml': {},
431+
},
432+
},
433+
responses: {
434+
204: {
435+
description: 'empty response',
436+
},
437+
},
438+
});
439+
440+
const parseResult = parse(context, path, operation);
441+
442+
expect(parseResult.length).to.equal(1);
443+
444+
const transition = parseResult.get(0);
445+
expect(transition).to.be.instanceof(namespace.elements.Transition);
446+
447+
const transaction = transition.get(0);
448+
expect(transaction).to.be.instanceof(namespace.elements.HttpTransaction);
449+
450+
expect(transaction.request).to.be.instanceof(namespace.elements.HttpRequest);
451+
expect(transaction.request.headers.toValue()).to.deep.equal([
452+
{
453+
key: 'Content-Type',
454+
value: 'application/xml',
455+
},
456+
]);
457+
});
458+
459+
it('merges headers with operation headers', () => {
460+
const operation = new namespace.elements.Member('post', {
461+
parameters: [
462+
{
463+
name: 'Content-Type',
464+
in: 'header',
465+
example: 'application/json',
466+
},
467+
{
468+
name: 'Link',
469+
in: 'header',
470+
example: '<https://api.github.com/user/repos?page=3&per_page=100>; rel="next"',
471+
},
472+
],
473+
requestBody: {
474+
content: {
475+
'application/xml': {},
476+
},
477+
},
478+
responses: {
479+
204: {
480+
description: 'empty response',
481+
},
482+
},
483+
});
484+
485+
const parseResult = parse(context, path, operation);
486+
487+
expect(parseResult.length).to.equal(1);
488+
489+
const transition = parseResult.get(0);
490+
expect(transition).to.be.instanceof(namespace.elements.Transition);
491+
492+
const transaction = transition.get(0);
493+
expect(transaction).to.be.instanceof(namespace.elements.HttpTransaction);
494+
495+
expect(transaction.request).to.be.instanceof(namespace.elements.HttpRequest);
496+
expect(transaction.request.headers.toValue()).to.deep.equal([
497+
{
498+
key: 'Content-Type',
499+
value: 'application/xml',
500+
},
501+
{
502+
key: 'Link',
503+
value: '<https://api.github.com/user/repos?page=3&per_page=100>; rel="next"',
504+
},
505+
]);
506+
});
507+
});
381508
});
382509

383510
describe('#responses', () => {

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,6 @@ describe('Parameter Object', () => {
8989
expect(parseResult).to.contain.warning("'Parameter Object' 'in' must be either 'query', 'header', 'path' or 'cookie'");
9090
});
9191

92-
it('provides an unsupported error for header parameters', () => {
93-
const parameter = new namespace.elements.Object({
94-
name: 'example',
95-
in: 'header',
96-
});
97-
98-
const parseResult = parse(context, parameter);
99-
100-
expect(parseResult.length).to.equal(1);
101-
expect(parseResult).to.contain.warning("'Parameter Object' 'in' 'header' is unsupported");
102-
});
103-
10492
it('provides an unsupported error for cookie parameters', () => {
10593
const parameter = new namespace.elements.Object({
10694
name: 'example',

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

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,5 +284,139 @@ describe('#parsePathItemObject', () => {
284284
expect(resource.hrefVariables.getMember('resource')).to.be.instanceof(namespace.elements.Member);
285285
});
286286
});
287+
288+
describe('header parameters', () => {
289+
it('exposes header parameter in operation requests', () => {
290+
const path = new namespace.elements.Member('/', {
291+
parameters: [
292+
{
293+
name: 'Accept',
294+
in: 'header',
295+
example: 'application/json',
296+
},
297+
],
298+
get: {
299+
responses: {
300+
200: {
301+
description: 'dummy',
302+
},
303+
},
304+
},
305+
});
306+
307+
const parseResult = parse(context, path);
308+
309+
expect(parseResult.length).to.equal(1);
310+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Resource);
311+
312+
const resource = parseResult.get(0);
313+
314+
const transition = resource.transitions.get(0);
315+
const transaction = transition.transactions.get(0);
316+
const { request } = transaction;
317+
318+
expect(request.headers).to.be.instanceof(namespace.elements.HttpHeaders);
319+
expect(request.headers.toValue()).to.deep.equal([
320+
{
321+
key: 'Accept',
322+
value: 'application/json',
323+
},
324+
]);
325+
});
326+
327+
it('prefers headers defined by operation', () => {
328+
const path = new namespace.elements.Member('/', {
329+
parameters: [
330+
{
331+
name: 'Accept',
332+
in: 'header',
333+
example: 'application/json',
334+
},
335+
],
336+
get: {
337+
parameters: [
338+
{
339+
name: 'Accept',
340+
in: 'header',
341+
example: 'application/problem+json',
342+
},
343+
],
344+
responses: {
345+
404: {
346+
description: 'dummy',
347+
},
348+
},
349+
},
350+
});
351+
352+
const parseResult = parse(context, path);
353+
354+
expect(parseResult.length).to.equal(1);
355+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Resource);
356+
357+
const resource = parseResult.get(0);
358+
359+
const transition = resource.transitions.get(0);
360+
const transaction = transition.transactions.get(0);
361+
const { request } = transaction;
362+
363+
expect(request.headers).to.be.instanceof(namespace.elements.HttpHeaders);
364+
expect(request.headers.toValue()).to.deep.equal([
365+
{
366+
key: 'Accept',
367+
value: 'application/problem+json',
368+
},
369+
]);
370+
});
371+
372+
it('merges headers with operation headers', () => {
373+
const path = new namespace.elements.Member('/', {
374+
parameters: [
375+
{
376+
name: 'Accept',
377+
in: 'header',
378+
example: 'application/json',
379+
},
380+
],
381+
get: {
382+
parameters: [
383+
{
384+
name: 'Link',
385+
in: 'header',
386+
example: '<https://api.github.com/user/repos?page=3&per_page=100>; rel="next"',
387+
},
388+
],
389+
responses: {
390+
404: {
391+
description: 'dummy',
392+
},
393+
},
394+
},
395+
});
396+
397+
const parseResult = parse(context, path);
398+
399+
expect(parseResult.length).to.equal(1);
400+
expect(parseResult.get(0)).to.be.instanceof(namespace.elements.Resource);
401+
402+
const resource = parseResult.get(0);
403+
404+
const transition = resource.transitions.get(0);
405+
const transaction = transition.transactions.get(0);
406+
const { request } = transaction;
407+
408+
expect(request.headers).to.be.instanceof(namespace.elements.HttpHeaders);
409+
expect(request.headers.toValue()).to.deep.equal([
410+
{
411+
key: 'Link',
412+
value: '<https://api.github.com/user/repos?page=3&per_page=100>; rel="next"',
413+
},
414+
{
415+
key: 'Accept',
416+
value: 'application/json',
417+
},
418+
]);
419+
});
420+
});
287421
});
288422
});

0 commit comments

Comments
 (0)