Skip to content

Commit df9d0a3

Browse files
authored
feat: requestContentType empowerment & attachContentTypeForEmptyPayload option (#1303)
* add failing tests for `requestContentType` * fix: swagger2 `requestContentType` priority * don't expect a body or Content-Type when we can't resolve... to a requestBody definition * only allow valid media types to be used as empty-payload Content-Type * attachContentTypeForEmptyPayload for Swagger 2 * attachContentTypeForEmptyPayload for OpenAPI 3 * fix swagger 2 FormData test parameter name logical error * test for requestContentType with payload-free formData * add test for proxying attachContentTypeForEmptyPayload through execute
1 parent 3cd93d9 commit df9d0a3

File tree

5 files changed

+492
-12
lines changed

5 files changed

+492
-12
lines changed

src/execute/oas3/build-request.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export default function (options, req) {
99
operation,
1010
requestBody,
1111
securities,
12-
spec
12+
spec,
13+
attachContentTypeForEmptyPayload
1314
} = options
1415

1516
let {
@@ -20,12 +21,12 @@ export default function (options, req) {
2021

2122
const requestBodyDef = operation.requestBody || {}
2223
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})
24+
const isExplicitContentTypeValid = requestContentType
25+
&& requestBodyMediaTypes.indexOf(requestContentType) > -1
2326

2427
// for OAS3: set the Content-Type
25-
if (requestBody) {
28+
if (requestBody || attachContentTypeForEmptyPayload) {
2629
// does the passed requestContentType appear in the requestBody definition?
27-
const isExplicitContentTypeValid = requestContentType
28-
&& requestBodyMediaTypes.indexOf(requestContentType) > -1
2930

3031
if (requestContentType && isExplicitContentTypeValid) {
3132
req.headers['Content-Type'] = requestContentType
@@ -38,6 +39,9 @@ export default function (options, req) {
3839
}
3940
}
4041
}
42+
else if (requestContentType && isExplicitContentTypeValid) {
43+
req.headers['Content-Type'] = requestContentType
44+
}
4145

4246
// for OAS3: add requestBody to request
4347
if (requestBody) {

src/execute/swagger2/build-request.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ export default function (options, req) {
1111
spec,
1212
operation,
1313
securities,
14-
requestContentType
14+
requestContentType,
15+
attachContentTypeForEmptyPayload
1516
} = options
1617
// Add securities, which are applicable
1718
req = applySecurities({request: req, securities, operation, spec})
1819

19-
if (req.body || req.form) {
20+
if (req.body || req.form || attachContentTypeForEmptyPayload) {
2021
// all following conditionals are Swagger2 only
2122
if (requestContentType) {
2223
req.headers['Content-Type'] = requestContentType
@@ -34,6 +35,13 @@ export default function (options, req) {
3435
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
3536
}
3637
}
38+
else if (requestContentType) {
39+
const isBodyParamPresent = operation.parameters && operation.parameters.filter(p => p.in === 'body').length > 0
40+
const isFormDataParamPresent = operation.parameters && operation.parameters.filter(p => p.in === 'formData').length > 0
41+
if (isBodyParamPresent || isFormDataParamPresent) {
42+
req.headers['Content-Type'] = requestContentType
43+
}
44+
}
3745

3846
return req
3947
}

src/execute/swagger2/parameter-builders.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ function bodyBuilder({req, value}) {
1818

1919
// Add a form data object.
2020
function formDataBuilder({req, value, parameter}) {
21-
req.form = req.form || {}
2221
if (value || parameter.allowEmptyValue) {
22+
req.form = req.form || {}
2323
req.form[parameter.name] = {
2424
value,
2525
allowEmptyValue: parameter.allowEmptyValue,

test/execute/main.js

Lines changed: 252 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,88 @@ describe('execute', () => {
570570
})
571571
})
572572

573-
it('should not add Content-Type with no form-data or body param', function () {
573+
it('should add Content-Type if a body param definition is present but there is no payload', function () {
574+
// Given
575+
const spec = {
576+
host: 'swagger.io',
577+
paths: {
578+
'/one': {
579+
get: {
580+
operationId: 'getMe',
581+
parameters: [
582+
{
583+
name: 'body',
584+
in: 'body',
585+
schema: {
586+
type: 'string'
587+
}
588+
}
589+
]
590+
}
591+
}
592+
}
593+
}
594+
595+
// When
596+
const req = buildRequest({
597+
spec,
598+
operationId: 'getMe',
599+
requestContentType: 'application/josh'
600+
})
601+
602+
// Then
603+
expect(req).toEqual({
604+
url: 'http://swagger.io/one',
605+
body: undefined,
606+
headers: {
607+
'Content-Type': 'application/josh'
608+
},
609+
credentials: 'same-origin',
610+
method: 'GET'
611+
})
612+
})
613+
614+
it('should add Content-Type if a formData param definition is present but there is no payload', function () {
615+
// Given
616+
const spec = {
617+
host: 'swagger.io',
618+
paths: {
619+
'/one': {
620+
get: {
621+
operationId: 'getMe',
622+
parameters: [
623+
{
624+
name: 'data',
625+
in: 'formData',
626+
schema: {
627+
type: 'string'
628+
}
629+
}
630+
]
631+
}
632+
}
633+
}
634+
}
635+
636+
// When
637+
const req = buildRequest({
638+
spec,
639+
operationId: 'getMe',
640+
requestContentType: 'application/x-www-form-encoded'
641+
})
642+
643+
// Then
644+
expect(req).toEqual({
645+
url: 'http://swagger.io/one',
646+
headers: {
647+
'Content-Type': 'application/x-www-form-encoded'
648+
},
649+
credentials: 'same-origin',
650+
method: 'GET'
651+
})
652+
})
653+
654+
it('should not add Content-Type if no form-data or body param definition is present', function () {
574655
// Given
575656
const spec = {
576657
host: 'swagger.io',
@@ -608,7 +689,7 @@ describe('execute', () => {
608689
const req = buildRequest({
609690
spec,
610691
operationId: 'postMe',
611-
parameters: {file: 'test'}})
692+
parameters: {foo: 'test'}})
612693

613694
// Then
614695
expect(req.headers).toEqual({
@@ -816,6 +897,171 @@ describe('execute', () => {
816897
one: 1
817898
})
818899
})
900+
901+
describe('attachContentTypeForEmptyPayload', () => {
902+
it('should attach a Content-Type to a Swagger 2 operation with a body parameter defined but no body provided', () => {
903+
const spec = {
904+
swagger: '2.0',
905+
host: 'swagger.io',
906+
consumes: ['application/json'],
907+
paths: {
908+
'/one': {
909+
post: {
910+
operationId: 'myOp',
911+
parameters: [
912+
{
913+
name: 'body',
914+
in: 'body'
915+
}
916+
]
917+
}
918+
}
919+
}
920+
}
921+
922+
const req = buildRequest({
923+
spec,
924+
operationId: 'myOp',
925+
attachContentTypeForEmptyPayload: true
926+
})
927+
928+
expect(req).toEqual({
929+
url: 'http://swagger.io/one',
930+
headers: {
931+
'Content-Type': 'application/json'
932+
},
933+
credentials: 'same-origin',
934+
method: 'POST',
935+
body: undefined
936+
})
937+
})
938+
it('should attach a Content-Type to a Swagger 2 operation with a formData parameter defined but no body provided', () => {
939+
const spec = {
940+
swagger: '2.0',
941+
host: 'swagger.io',
942+
paths: {
943+
'/one': {
944+
post: {
945+
operationId: 'myOp',
946+
parameters: [
947+
{
948+
name: 'data',
949+
in: 'formData',
950+
type: 'string'
951+
}
952+
]
953+
}
954+
}
955+
}
956+
}
957+
958+
const req = buildRequest({
959+
spec,
960+
operationId: 'myOp',
961+
attachContentTypeForEmptyPayload: true
962+
})
963+
964+
expect(req).toEqual({
965+
url: 'http://swagger.io/one',
966+
headers: {
967+
'Content-Type': 'application/x-www-form-urlencoded'
968+
},
969+
credentials: 'same-origin',
970+
method: 'POST'
971+
})
972+
})
973+
it('should not attach a Content-Type to a Swagger 2 operation with no body or formData parameter definition present', () => {
974+
const spec = {
975+
swagger: '2.0',
976+
host: 'swagger.io',
977+
paths: {
978+
'/one': {
979+
post: {
980+
operationId: 'myOp'
981+
}
982+
}
983+
}
984+
}
985+
986+
const req = buildRequest({
987+
spec,
988+
operationId: 'myOp',
989+
attachContentTypeForEmptyPayload: true
990+
})
991+
992+
expect(req).toEqual({
993+
url: 'http://swagger.io/one',
994+
headers: {},
995+
credentials: 'same-origin',
996+
method: 'POST'
997+
})
998+
})
999+
it('should not attach a Content-Type to a Swagger 2 operation with a body parameter defined but no body provided if the option is not enabled', () => {
1000+
const spec = {
1001+
swagger: '2.0',
1002+
host: 'swagger.io',
1003+
consumes: ['application/json'],
1004+
paths: {
1005+
'/one': {
1006+
post: {
1007+
operationId: 'myOp',
1008+
parameters: [
1009+
{
1010+
name: 'body',
1011+
in: 'body'
1012+
}
1013+
]
1014+
}
1015+
}
1016+
}
1017+
}
1018+
1019+
const req = buildRequest({
1020+
spec,
1021+
operationId: 'myOp',
1022+
})
1023+
1024+
expect(req).toEqual({
1025+
url: 'http://swagger.io/one',
1026+
headers: {},
1027+
credentials: 'same-origin',
1028+
method: 'POST',
1029+
body: undefined
1030+
})
1031+
})
1032+
it('should not attach a Content-Type to a Swagger 2 operation with a formData parameter defined but no body provided if the option is not enabled', () => {
1033+
const spec = {
1034+
swagger: '2.0',
1035+
host: 'swagger.io',
1036+
paths: {
1037+
'/one': {
1038+
post: {
1039+
operationId: 'myOp',
1040+
parameters: [
1041+
{
1042+
name: 'data',
1043+
in: 'formData',
1044+
type: 'string'
1045+
}
1046+
]
1047+
}
1048+
}
1049+
}
1050+
}
1051+
1052+
const req = buildRequest({
1053+
spec,
1054+
operationId: 'myOp',
1055+
})
1056+
1057+
expect(req).toEqual({
1058+
url: 'http://swagger.io/one',
1059+
headers: {},
1060+
credentials: 'same-origin',
1061+
method: 'POST'
1062+
})
1063+
})
1064+
})
8191065
})
8201066

8211067

@@ -834,12 +1080,14 @@ describe('execute', () => {
8341080
fetch: createSpy().andReturn({then() { }}),
8351081
spec,
8361082
operationId: 'getMe',
837-
josh: 1
1083+
josh: 1,
1084+
attachContentTypeForEmptyPayload: true
8381085
})
8391086

8401087
expect(buildRequestSpy.calls.length).toEqual(1)
8411088
expect(buildRequestSpy.calls[0].arguments[0]).toInclude({
842-
josh: 1
1089+
josh: 1,
1090+
attachContentTypeForEmptyPayload: true
8431091
})
8441092
})
8451093

0 commit comments

Comments
 (0)