Skip to content

Commit c6b3852

Browse files
committed
Added better support for readOnly and writeOnly path via usage of json-pointer
1 parent 84f8011 commit c6b3852

10 files changed

+582
-51
lines changed

libV2/schemaUtils.js

+23-21
Original file line numberDiff line numberDiff line change
@@ -269,16 +269,16 @@ let QUERYPARAM = 'query',
269269
* @param {Object} context - Global context object
270270
* @param {Object} readOnlyPropCache - readOnly properties cache to be merged
271271
* @param {Object} writeOnlyPropCache - writeOnly properties cache to be merged
272-
* @param {Object} currentPath - Current path being resolved relative to original schema
272+
* @param {Object} currentPath - Current path (json-pointer) being resolved relative to original schema
273273
* @returns {void}
274274
*/
275275
mergeReadWritePropCache = (context, readOnlyPropCache, writeOnlyPropCache, currentPath = '') => {
276276
_.forOwn(readOnlyPropCache, (value, key) => {
277-
context.readOnlyPropCache[`${currentPath}${key}`] = true;
277+
context.readOnlyPropCache[utils.mergeJsonPath(currentPath, key)] = true;
278278
});
279279

280280
_.forOwn(writeOnlyPropCache, (value, key) => {
281-
context.writeOnlyPropCache[`${currentPath}${key}`] = true;
281+
context.writeOnlyPropCache[utils.mergeJsonPath(currentPath, key)] = true;
282282
});
283283
},
284284

@@ -487,7 +487,7 @@ let QUERYPARAM = 'query',
487487
* @param {Number} [stack] - Current recursion depth
488488
* @param {*} resolveFor - resolve refs for flow validation/conversion (value to be one of VALIDATION/CONVERSION)
489489
* @param {Object} seenRef - Map of all the references that have been resolved
490-
* @param {String} currentPath - Current path being resolved relative to original schema
490+
* @param {String} currentPath - Current path (json-pointer) being resolved relative to original schema
491491
*
492492
* @returns {Object} Resolved schema
493493
*/
@@ -524,7 +524,7 @@ let QUERYPARAM = 'query',
524524
* @param {Number} [stack] - Current recursion depth
525525
* @param {String} resolveFor - For which action this resolution is to be done
526526
* @param {Object} seenRef - Map of all the references that have been resolved
527-
* @param {String} currentPath - Current path being resolved relative to original schema
527+
* @param {String} currentPath - Current path (json-pointer) being resolved relative to original schema
528528
* @todo: Explore using a directed graph/tree for maintaining seen ref
529529
*
530530
* @returns {Object} Returns the object that satisfies the schema
@@ -569,9 +569,9 @@ let QUERYPARAM = 'query',
569569
return _resolveSchema(context, compositeSchema[0], stack, resolveFor, _.cloneDeep(seenRef), currentPath);
570570
}
571571

572-
return { [compositeKeyword]: _.map(compositeSchema, (schemaElement) => {
572+
return { [compositeKeyword]: _.map(compositeSchema, (schemaElement, index) => {
573573
return _resolveSchema(context, schemaElement, stack, resolveFor, _.cloneDeep(seenRef),
574-
`${currentPath}.${compositeKeyword}`);
574+
utils.addToJsonPath(currentPath, [compositeKeyword, index]));
575575
}) };
576576
}
577577

@@ -664,16 +664,7 @@ let QUERYPARAM = 'query',
664664
return;
665665
}
666666

667-
const currentPropPath = `${currentPath}.properties.${propertyName}`;
668-
669-
// Keep track of readOnly and writeOnly properties to resolve request and responses accordingly later.
670-
if (property.readOnly) {
671-
context.readOnlyPropCache[currentPropPath] = true;
672-
}
673-
674-
if (property.writeOnly) {
675-
context.writeOnlyPropCache[currentPropPath] = true;
676-
}
667+
const currentPropPath = utils.addToJsonPath(currentPath, ['properties', propertyName]);
677668

678669
resolvedSchemaProps[propertyName] = _resolveSchema(context, property, stack, resolveFor,
679670
_.cloneDeep(seenRef), currentPropPath);
@@ -685,7 +676,7 @@ let QUERYPARAM = 'query',
685676
// If schema is of type array
686677
else if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.array) && schema.items) {
687678
schema.items = _resolveSchema(context, schema.items, stack, resolveFor, _.cloneDeep(seenRef),
688-
`${currentPath}.items`);
679+
utils.addToJsonPath(currentPath, ['items']));
689680
}
690681
// Any properties to ignored should not be available in schema
691682
else if (_.every(SCHEMA_PROPERTIES_TO_EXCLUDE, (schemaKey) => { return !schema.hasOwnProperty(schemaKey); })) {
@@ -718,7 +709,7 @@ let QUERYPARAM = 'query',
718709
if (schema.hasOwnProperty('additionalProperties')) {
719710
schema.additionalProperties = _.isBoolean(schema.additionalProperties) ? schema.additionalProperties :
720711
_resolveSchema(context, schema.additionalProperties, stack, resolveFor, _.cloneDeep(seenRef),
721-
`${currentPath}.additionalProperties`);
712+
utils.addToJsonPath(currentPath, ['additionalProperties']));
722713
schema.type = schema.type || SCHEMA_TYPES.object;
723714
}
724715

@@ -733,6 +724,15 @@ let QUERYPARAM = 'query',
733724
});
734725
}
735726

727+
// Keep track of readOnly and writeOnly properties to resolve request and responses accordingly later.
728+
if (schema.readOnly) {
729+
context.readOnlyPropCache[currentPath] = true;
730+
}
731+
732+
if (schema.writeOnly) {
733+
context.writeOnlyPropCache[currentPath] = true;
734+
}
735+
736736
return schema;
737737
},
738738

@@ -769,12 +769,14 @@ let QUERYPARAM = 'query',
769769

770770
if (isResponseSchema) {
771771
_.forOwn(context.writeOnlyPropCache, (value, key) => {
772-
_.unset(resolvedSchema, key.substring(1));
772+
// We need to make sure to remove empty strings via _.compact that are added while forming json-pointer
773+
_.unset(resolvedSchema, utils.getJsonPathArray(key));
773774
});
774775
}
775776
else {
776777
_.forOwn(context.readOnlyPropCache, (value, key) => {
777-
_.unset(resolvedSchema, key.substring(1));
778+
// We need to make sure to remove empty strings via _.compact that are added while forming json-pointer
779+
_.unset(resolvedSchema, utils.getJsonPathArray(key));
778780
});
779781
}
780782

libV2/utils.js

+43
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const _ = require('lodash'),
2+
jsonPointer = require('json-pointer'),
23
{ Item } = require('postman-collection/lib/collection/item'),
34
{ Response } = require('postman-collection/lib/collection/response'),
45

@@ -200,6 +201,48 @@ module.exports = {
200201
return title;
201202
},
202203

204+
/**
205+
* Adds provided property array to the given JSON path
206+
*
207+
* @param {string} jsonPath - JSON path to which properties should be added
208+
* @param {array} propArray - Array of properties to be added to JSON path
209+
* @returns {string} - Combined JSON path
210+
*/
211+
addToJsonPath: function (jsonPath, propArray) {
212+
const jsonPathArray = jsonPointer.parse(jsonPath),
213+
escapedPropArray = _.map(propArray, (prop) => {
214+
return jsonPointer.escape(prop);
215+
});
216+
217+
return jsonPointer.compile(jsonPathArray.concat(escapedPropArray));
218+
},
219+
220+
/**
221+
* Merges two JSON paths. i.e. Parent JSON path and Child JSON path
222+
*
223+
* @param {string} parentJsonPath - Parent JSON path
224+
* @param {string} childJsonPath - Child JSON path
225+
* @returns {string} - Merged JSON path
226+
*/
227+
mergeJsonPath: function (parentJsonPath, childJsonPath) {
228+
let jsonPathArray = jsonPointer.parse(parentJsonPath);
229+
230+
// Merges childJsonPath with parentJsonPath
231+
jsonPathArray = jsonPathArray.concat(jsonPointer.parse(childJsonPath));
232+
233+
return jsonPointer.compile(jsonPathArray);
234+
},
235+
236+
/**
237+
* Gets JSON path in array from string JSON path
238+
*
239+
* @param {string} jsonPath - input JSON path
240+
* @returns {array} - Parsed JSON path (each part is distributed in an array)
241+
*/
242+
getJsonPathArray: function (jsonPath) {
243+
return jsonPointer.parse(jsonPointer.unescape(jsonPath));
244+
},
245+
203246
generatePmResponseObject,
204247
generateRequestItemObject
205248
};

package-lock.json

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"async": "3.2.4",
123123
"commander": "2.20.3",
124124
"js-yaml": "4.1.0",
125+
"json-pointer": "0.6.2",
125126
"json-schema-merge-allof": "0.8.1",
126127
"lodash": "4.17.21",
127128
"oas-resolver-browser": "2.5.6",

test/data/valid_openapi/readOnly.json

+29-22
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,21 @@
2020
"schema": {
2121
"type": "array",
2222
"items": {
23-
"$ref": "#/components/schemas/Pet"
23+
"type": "object",
24+
"properties": {
25+
"id": {
26+
"type": "integer",
27+
"format": "int64",
28+
"readOnly": true
29+
},
30+
"name": {
31+
"type": "string"
32+
},
33+
"tag": {
34+
"type": "string",
35+
"writeOnly": true
36+
}
37+
}
2438
}
2539
}
2640
}
@@ -33,7 +47,20 @@
3347
"content": {
3448
"application/json": {
3549
"schema": {
36-
"$ref": "#/components/schemas/Pet"
50+
"properties": {
51+
"id": {
52+
"type": "integer",
53+
"format": "int64",
54+
"readOnly": true
55+
},
56+
"name": {
57+
"type": "string"
58+
},
59+
"tag": {
60+
"type": "string",
61+
"writeOnly": true
62+
}
63+
}
3764
}
3865
}
3966
}
@@ -45,25 +72,5 @@
4572
}
4673
}
4774
}
48-
},
49-
"components": {
50-
"schemas": {
51-
"Pet": {
52-
"properties": {
53-
"id": {
54-
"type": "integer",
55-
"format": "int64",
56-
"readOnly": true
57-
},
58-
"name": {
59-
"type": "string"
60-
},
61-
"tag": {
62-
"type": "string",
63-
"writeOnly": true
64-
}
65-
}
66-
}
67-
}
6875
}
6976
}

0 commit comments

Comments
 (0)