Skip to content

Commit 707e42e

Browse files
Sahil ChoudharySahil Choudhary
authored andcommitted
Merge branch 'release/4.2.0'
2 parents 9bd5034 + 45acf3b commit 707e42e

13 files changed

+972
-15
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# OpenAPI-Postman Changelog
22

3+
#### v4.2.0 (August 10, 2022)
4+
* Improved the way to detect a circular reference by adding a new condition
5+
* A schema that comes from an allOf parent then we now return the same schema instead of defaulting to a schema with type as object, and no other properties
6+
* The method resolveAllOf is executed when the current node is an allOf element.
7+
* Avoiding to set type as object when property's schema is an empty object.
8+
* Added OAuth2 flows and configuration support.
9+
* OAuth2 values now default to variables instead of hardcoded strings.
10+
311
#### v4.1.1 (July 29, 2022)
412
* Replaced Object.hasOwnProperty usages with loadsh _.has for safe access.
513

lib/deref.js

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,33 @@ const _ = require('lodash'),
2727
'float',
2828
'double'
2929
],
30-
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
30+
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X'),
31+
traverseUtility = require('traverse');
32+
33+
/**
34+
* @param {*} currentNode - the object from which you're trying to find references
35+
* @param {*} seenRef References that are repeated. Used to identify circular references.
36+
* @returns {boolean} - Whether the object has circular references
37+
*/
38+
function hasReference(currentNode, seenRef) {
39+
let hasRef = false;
40+
41+
traverseUtility(currentNode).forEach(function (property) {
42+
if (property) {
43+
let hasReferenceTypeKey;
44+
hasReferenceTypeKey = Object.keys(property)
45+
.find(
46+
(key) => {
47+
return key === '$ref';
48+
}
49+
);
50+
if (hasReferenceTypeKey && seenRef[property.$ref]) {
51+
hasRef = true;
52+
}
53+
}
54+
});
55+
return hasRef;
56+
}
3157

3258
module.exports = {
3359
/**
@@ -86,7 +112,7 @@ module.exports = {
86112
return mergeAllOf({
87113
allOf: schemaArr.map((schema) => {
88114
return this.resolveRefs(schema, parameterSourceOption, components, schemaResolutionCache, resolveFor,
89-
resolveTo, stack, seenRef, stackLimit);
115+
resolveTo, stack, seenRef, stackLimit, true);
90116
})
91117
}, {
92118
resolvers: {
@@ -122,7 +148,7 @@ module.exports = {
122148
*/
123149

124150
resolveRefs: function (schema, parameterSourceOption, components, schemaResolutionCache,
125-
resolveFor = 'CONVERSION', resolveTo = 'schema', stack = 0, seenRef = {}, stackLimit = 10) {
151+
resolveFor = 'CONVERSION', resolveTo = 'schema', stack = 0, seenRef = {}, stackLimit = 10, isAllOf = false) {
126152
var resolvedSchema, prop, splitRef,
127153
ERR_TOO_MANY_LEVELS = '<Error: Too many levels of nesting to fake this schema>';
128154
let concreteUtils = components && components.hasOwnProperty('concreteUtils') ?
@@ -167,13 +193,6 @@ module.exports = {
167193
let refKey = schema.$ref,
168194
outerProperties = concreteUtils.getOuterPropsIfIsSupported(schema);
169195

170-
// if this reference is seen before, ignore and move on.
171-
if (seenRef[refKey]) {
172-
return { value: '<Circular reference to ' + refKey + ' detected>' };
173-
}
174-
// add to seen array if not encountered before.
175-
seenRef[refKey] = stack;
176-
177196
// points to an existing location
178197
// .split will return [#, components, schemas, schemaName]
179198
splitRef = refKey.split('/');
@@ -199,6 +218,12 @@ module.exports = {
199218
});
200219

201220
resolvedSchema = this._getEscaped(components, splitRef);
221+
// if this reference is seen before, ignore and move on.
222+
if (seenRef[refKey] && hasReference(resolvedSchema, seenRef)) {
223+
return { value: '<Circular reference to ' + refKey + ' detected>' };
224+
}
225+
// add to seen array if not encountered before.
226+
seenRef[refKey] = stack;
202227
if (outerProperties) {
203228
resolvedSchema = concreteUtils.addOuterPropsToRefSchemaIfIsSupported(resolvedSchema, outerProperties);
204229
}
@@ -256,8 +281,10 @@ module.exports = {
256281
continue;
257282
}
258283
/* eslint-enable */
259-
tempSchema.properties[prop] = this.resolveRefs(property, parameterSourceOption, components,
260-
schemaResolutionCache, resolveFor, resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
284+
tempSchema.properties[prop] = _.isEmpty(property) ?
285+
{} :
286+
this.resolveRefs(property, parameterSourceOption, components,
287+
schemaResolutionCache, resolveFor, resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
261288
}
262289
}
263290
return tempSchema;
@@ -325,6 +352,9 @@ module.exports = {
325352
value: schema.enum[0]
326353
};
327354
}
355+
else if (isAllOf) {
356+
return schema;
357+
}
328358
else {
329359
return {
330360
type: SCHEMA_TYPES.object

lib/schemaUtils.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc
7575
VALIDATION: 'VALIDATION',
7676
CONVERSION: 'CONVERSION'
7777
},
78+
FLOW_TYPE = {
79+
authorizationCode: 'authorization_code',
80+
implicit: 'implicit',
81+
password: 'password_credentials',
82+
clientCredentials: 'client_credentials'
83+
},
7884

7985
// These are the methods supported in the PathItem schema
8086
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathItemObject
@@ -1109,9 +1115,81 @@ module.exports = {
11091115
}
11101116
}
11111117
else if (securityDef.type === 'oauth2') {
1118+
let flowObj, currentFlowType;
1119+
11121120
helper = {
1113-
type: 'oauth2'
1121+
type: 'oauth2',
1122+
oauth2: []
11141123
};
1124+
1125+
if (_.isObject(securityDef.flows) && FLOW_TYPE[Object.keys(securityDef.flows)[0]]) {
1126+
/*
1127+
1128+
//===================[]========================\\
1129+
|| OAuth2 Flow Name || Key name in collection ||
1130+
|]===================[]========================[|
1131+
|| clientCredentials || client_credentials ||
1132+
|| password || password_credentials ||
1133+
|| implicit || implicit ||
1134+
|| authorizationCode || authorization_code ||
1135+
\\===================[]========================//
1136+
Ref : https://swagger.io/docs/specification/authentication/oauth2/
1137+
1138+
In case of multiple flow types, the first one will be preferred
1139+
and passed on to the collection.
1140+
1141+
Other flow types in collection which are not explicitly present in OA 3
1142+
• "authorization_code_with_pkce"
1143+
1144+
*/
1145+
currentFlowType = FLOW_TYPE[Object.keys(securityDef.flows)[0]];
1146+
flowObj = _.get(securityDef, `flows.${Object.keys(securityDef.flows)[0]}`);
1147+
}
1148+
1149+
if (currentFlowType) { // Means the flow is of supported type
1150+
1151+
// Fields supported by all flows -> refreshUrl, scopes
1152+
if (!_.isEmpty(flowObj.scopes)) {
1153+
helper.oauth2.push({
1154+
key: 'scope',
1155+
value: Object.keys(flowObj.scopes).join(' ')
1156+
});
1157+
}
1158+
1159+
/* refreshURL is indicated by key 'redirect_uri' in collection
1160+
Ref : https://stackoverflow.com/a/42131366/19078409 */
1161+
if (!_.isEmpty(flowObj.refreshUrl)) {
1162+
helper.oauth2.push({
1163+
key: 'redirect_uri',
1164+
value: _.isString(flowObj.refreshUrl) ? flowObj.refreshUrl : '{{OAuth2_CallbackURL}}'
1165+
});
1166+
}
1167+
1168+
// Fields supported by all flows except implicit -> tokenUrl
1169+
if (currentFlowType !== FLOW_TYPE.implicit) {
1170+
if (!_.isEmpty(flowObj.tokenUrl)) {
1171+
helper.oauth2.push({
1172+
key: 'accessTokenUrl',
1173+
value: _.isString(flowObj.tokenUrl) ? flowObj.tokenUrl : '{{OAuth2_AccessTokenURL}}'
1174+
});
1175+
}
1176+
}
1177+
1178+
// Fields supported by all flows all except password, clientCredentials -> authorizationUrl
1179+
if (currentFlowType !== FLOW_TYPE.password && currentFlowType !== FLOW_TYPE.clientCredentials) {
1180+
if (!_.isEmpty(flowObj.authorizationUrl)) {
1181+
helper.oauth2.push({
1182+
key: 'authUrl',
1183+
value: _.isString(flowObj.authorizationUrl) ? flowObj.authorizationUrl : '{{OAuth2_AuthURL}}'
1184+
});
1185+
}
1186+
}
1187+
1188+
helper.oauth2.push({
1189+
key: 'grant_type',
1190+
value: currentFlowType
1191+
});
1192+
}
11151193
}
11161194
else if (securityDef.type === 'apiKey') {
11171195
helper = {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openapi-to-postmanv2",
3-
"version": "4.1.1",
3+
"version": "4.2.0",
44
"description": "Convert a given OpenAPI specification to Postman Collection v2.0",
55
"homepage": "https://github.com/postmanlabs/openapi-to-postman",
66
"bugs": "https://github.com/postmanlabs/openapi-to-postman/issues",
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"info": {
3+
"title": "Inheritance test API",
4+
"version": "v1.0"
5+
},
6+
"openapi": "3.0.1",
7+
"paths": {
8+
"/api/inheritancetest": {
9+
"get": {
10+
"responses": {
11+
"200": {
12+
"content": {
13+
"application/json": {
14+
"schema": {
15+
"$ref": "#/components/schemas/Page"
16+
}
17+
}
18+
},
19+
"description": "The page data including metadata and content"
20+
}
21+
}
22+
}
23+
}
24+
},
25+
"components": {
26+
"schemas": {
27+
"SpecificType": {
28+
"allOf": [
29+
{
30+
"$ref": "#/components/schemas/ParentType"
31+
},
32+
{
33+
"properties": {
34+
"specificTypeData": {
35+
"type": "string"
36+
}
37+
},
38+
"required": ["specificTypeData"]
39+
}
40+
]
41+
},
42+
"ParentType": {
43+
"allOf": [
44+
{
45+
"$ref": "#/components/schemas/GrandParentType"
46+
},
47+
{
48+
"properties": {
49+
"parentTypeData": {
50+
"type": "string"
51+
}
52+
},
53+
"required": ["parentTypeData"]
54+
}
55+
]
56+
},
57+
"GrandParentType": {
58+
"properties": {
59+
"grandParentTypeData": {
60+
"type": "string"
61+
}
62+
},
63+
"required": ["grandParentTypeData"]
64+
},
65+
"Page": {
66+
"allOf": [
67+
{
68+
"$ref": "#/components/schemas/GrandParentType"
69+
},
70+
{
71+
"properties": {
72+
"specificType": {
73+
"$ref": "#/components/schemas/SpecificType"
74+
}
75+
},
76+
"required": ["specificType"]
77+
}
78+
]
79+
}
80+
},
81+
"securitySchemes": {
82+
"basic": {
83+
"description": "Basic HTTP Authentication",
84+
"scheme": "basic",
85+
"type": "http"
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)