Skip to content

Commit ae1bf3e

Browse files
authored
Merge pull request #787 from postmanlabs/feature/fix-allof-additional-props
Fixed an issue where schemas under allOf keyword having additionalProperties set to false were not generating bodies correctly.
2 parents 0b6503b + e8046bc commit ae1bf3e

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-0
lines changed

lib/deref.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ module.exports = {
131131
{ stack, seenRef: _.cloneDeep(seenRef), resolveFor, resolveTo, stackLimit, isAllOf: true, analytics });
132132
})
133133
}), {
134+
// below option is required to make sure schemas with additionalProperties set to false are resolved correctly
135+
ignoreAdditionalProperties: true,
134136
resolvers: {
135137
// for keywords in OpenAPI schema that are not standard defined JSON schema keywords, use default resolver
136138
defaultResolver: (compacted) => { return compacted[0]; },

libV2/schemaUtils.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ let QUERYPARAM = 'query',
456456
return resolveSchema(context, schema, stack, resolveFor, _.cloneDeep(seenRef));
457457
})
458458
}), {
459+
// below option is required to make sure schemas with additionalProperties set to false are resolved correctly
460+
ignoreAdditionalProperties: true,
459461
resolvers: {
460462
// for keywords in OpenAPI schema that are not standard defined JSON schema keywords, use default resolver
461463
defaultResolver: (compacted) => { return compacted[0]; },
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
{
2+
"x-generator": "NSwag v14.0.3.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))",
3+
"openapi": "3.0.0",
4+
"info": {
5+
"title": "Join API",
6+
"version": "1.0.0"
7+
},
8+
"paths": {
9+
"/api/Membership": {
10+
"post": {
11+
"tags": [
12+
"Membership"
13+
],
14+
"operationId": "PostMember",
15+
"requestBody": {
16+
"x-name": "query",
17+
"content": {
18+
"application/json": {
19+
"schema": {
20+
"$ref": "#/components/schemas/StandardJoinCommand"
21+
}
22+
}
23+
},
24+
"required": true,
25+
"x-position": 1
26+
},
27+
"responses": {
28+
"201": {
29+
"description": "",
30+
"content": {
31+
"application/json": {
32+
"schema": {
33+
"$ref": "#/components/schemas/StandardJoinDto"
34+
}
35+
}
36+
}
37+
}
38+
}
39+
}
40+
}
41+
},
42+
"components": {
43+
"schemas": {
44+
"GetMemberDto": {
45+
"type": "object",
46+
"additionalProperties": false,
47+
"properties": {
48+
"memberId": {
49+
"type": "integer",
50+
"format": "int32"
51+
},
52+
"username": {
53+
"type": "string",
54+
"nullable": true
55+
},
56+
"comment": {
57+
"type": "string",
58+
"nullable": true
59+
},
60+
"email": {
61+
"type": "string",
62+
"nullable": true
63+
}
64+
}
65+
},
66+
"StandardJoinDto": {
67+
"allOf": [
68+
{
69+
"$ref": "#/components/schemas/BaseJoinDto"
70+
},
71+
{
72+
"type": "object",
73+
"additionalProperties": false
74+
}
75+
]
76+
},
77+
"BaseJoinDto": {
78+
"type": "object",
79+
"additionalProperties": false,
80+
"properties": {
81+
"memberId": {
82+
"type": "integer",
83+
"format": "int32"
84+
},
85+
"firstName": {
86+
"type": "string",
87+
"nullable": true
88+
},
89+
"lastName": {
90+
"type": "string",
91+
"nullable": true
92+
}
93+
}
94+
},
95+
"StandardJoinCommand": {
96+
"allOf": [
97+
{
98+
"$ref": "#/components/schemas/BaseJoinCommandOfStandardJoinDto"
99+
},
100+
{
101+
"type": "object",
102+
"additionalProperties": false,
103+
"properties": {
104+
"email": {
105+
"type": "string",
106+
"nullable": true
107+
},
108+
"firstName": {
109+
"type": "string",
110+
"nullable": true
111+
},
112+
"lastName": {
113+
"type": "string",
114+
"nullable": true
115+
},
116+
"address1": {
117+
"type": "string",
118+
"nullable": true
119+
},
120+
"city": {
121+
"type": "string",
122+
"nullable": true
123+
},
124+
"state": {
125+
"type": "string"
126+
},
127+
"countryCode": {
128+
"type": "string",
129+
"nullable": true
130+
},
131+
"zipCode": {
132+
"type": "string",
133+
"nullable": true
134+
},
135+
"phoneNumber": {
136+
"type": "string",
137+
"nullable": true
138+
}
139+
}
140+
}
141+
]
142+
},
143+
"BaseJoinCommandOfStandardJoinDto": {
144+
"type": "object",
145+
"x-abstract": true,
146+
"additionalProperties": false,
147+
"properties": {
148+
"campaignId": {
149+
"type": "integer",
150+
"format": "int32"
151+
}
152+
}
153+
}
154+
}
155+
}
156+
}
157+

test/unit/convertV2.test.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,6 +2091,28 @@ describe('The convert v2 Function', function() {
20912091
});
20922092
});
20932093

2094+
it('[GITHUB #417] - should convert file with allOf schemas containing additionalProperties as false ', function() {
2095+
const fileSource = path.join(__dirname, VALID_OPENAPI_PATH, 'allOfAdditionalProperties.json'),
2096+
fileData = fs.readFileSync(fileSource, 'utf8'),
2097+
input = {
2098+
type: 'string',
2099+
data: fileData
2100+
};
2101+
2102+
Converter.convertV2(input, {
2103+
optimizeConversion: false
2104+
}, (err, result) => {
2105+
const expectedRequestBody = JSON.parse(result.output[0].data.item[0].item[0].item[0].request.body.raw);
2106+
2107+
expect(err).to.be.null;
2108+
expect(result.result).to.be.true;
2109+
2110+
expect(expectedRequestBody).to.be.an('object');
2111+
expect(expectedRequestBody).to.have.keys(['phoneNumber', 'zipCode', 'countryCode', 'state', 'city',
2112+
'address1', 'lastName', 'firstName', 'email', 'campaignId']);
2113+
});
2114+
});
2115+
20942116
it('Should convert a swagger document with XML example correctly', function(done) {
20952117
const fileData = fs.readFileSync(path.join(__dirname, SWAGGER_20_FOLDER_YAML, 'xml_example.yaml'), 'utf8'),
20962118
input = {

test/unit/deref.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,76 @@ describe('DEREF FUNCTION TESTS ', function() {
516516
});
517517
done();
518518
});
519+
520+
it('should resolve schemas under allOf keyword with additionalProperties set to false correctly', function (done) {
521+
var schema = {
522+
'allOf': [
523+
{
524+
'type': 'object',
525+
'additionalProperties': false,
526+
'properties': {
527+
'source': {
528+
'type': 'string',
529+
'format': 'uuid'
530+
},
531+
'status': {
532+
'type': 'string',
533+
'enum': ['incomplete', 'completed', 'refunded']
534+
},
535+
'actionId': { 'type': 'integer', 'minimum': 5 },
536+
'result': { 'type': 'object' }
537+
},
538+
'required': ['source', 'actionId', 'result']
539+
},
540+
{
541+
'additionalProperties': false,
542+
'properties': {
543+
'result': {
544+
'type': 'object',
545+
'properties': {
546+
'err': { 'type': 'string' },
547+
'data': { 'type': 'object' }
548+
}
549+
},
550+
'status': {
551+
'type': 'string',
552+
'enum': ['no_market', 'too_small', 'too_large']
553+
}
554+
}
555+
}
556+
]
557+
};
558+
559+
expect(deref.resolveAllOf(
560+
schema,
561+
'REQUEST',
562+
{ concreteUtils: schemaUtils30X },
563+
{ resolveTo: 'example' }
564+
)).to.deep.equal({
565+
type: 'object',
566+
additionalProperties: false,
567+
required: ['source', 'actionId', 'result'],
568+
properties: {
569+
source: {
570+
type: 'string',
571+
format: 'uuid'
572+
},
573+
status: {
574+
type: 'string',
575+
enum: ['incomplete', 'completed', 'refunded', 'no_market', 'too_small', 'too_large']
576+
},
577+
actionId: { 'type': 'integer', 'minimum': 5 },
578+
result: {
579+
type: 'object',
580+
properties: {
581+
err: { 'type': 'string' },
582+
data: { 'type': 'object' }
583+
}
584+
}
585+
}
586+
});
587+
done();
588+
});
519589
});
520590

521591
describe('_getEscaped should', function() {

0 commit comments

Comments
 (0)