Skip to content

Commit

Permalink
Merge pull request #807 from postmanlabs/feature/readonly-writeonly-s…
Browse files Browse the repository at this point in the history
…upport-v2

Added support for readOnly and writeOnly keywords with v2 APIs.
  • Loading branch information
VShingala authored Aug 13, 2024
2 parents 34f299e + a0aa439 commit 083b837
Show file tree
Hide file tree
Showing 11 changed files with 781 additions and 73 deletions.
224 changes: 188 additions & 36 deletions libV2/schemaUtils.js

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions libV2/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const _ = require('lodash'),
jsonPointer = require('json-pointer'),
{ Item } = require('postman-collection/lib/collection/item'),
{ Response } = require('postman-collection/lib/collection/response'),

Expand Down Expand Up @@ -200,6 +201,48 @@ module.exports = {
return title;
},

/**
* Adds provided property array to the given JSON path
*
* @param {string} jsonPath - JSON path to which properties should be added
* @param {array} propArray - Array of properties to be added to JSON path
* @returns {string} - Combined JSON path
*/
addToJsonPath: function (jsonPath, propArray) {
const jsonPathArray = jsonPointer.parse(jsonPath),
escapedPropArray = _.map(propArray, (prop) => {
return jsonPointer.escape(prop);
});

return jsonPointer.compile(jsonPathArray.concat(escapedPropArray));
},

/**
* Merges two JSON paths. i.e. Parent JSON path and Child JSON path
*
* @param {string} parentJsonPath - Parent JSON path
* @param {string} childJsonPath - Child JSON path
* @returns {string} - Merged JSON path
*/
mergeJsonPath: function (parentJsonPath, childJsonPath) {
let jsonPathArray = jsonPointer.parse(parentJsonPath);

// Merges childJsonPath with parentJsonPath
jsonPathArray = jsonPathArray.concat(jsonPointer.parse(childJsonPath));

return jsonPointer.compile(jsonPathArray);
},

/**
* Gets JSON path in array from string JSON path
*
* @param {string} jsonPath - input JSON path
* @returns {array} - Parsed JSON path (each part is distributed in an array)
*/
getJsonPathArray: function (jsonPath) {
return jsonPointer.parse(jsonPointer.unescape(jsonPath));
},

generatePmResponseObject,
generateRequestItemObject
};
55 changes: 39 additions & 16 deletions libV2/validationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,10 @@ function safeSchemaFaker (context, oldSchema, resolveFor, parameterSourceOption,
* i.e. For array it'll add maxItems = 2. This should be avoided as we'll again be needing non-mutated schema
* in further VALIDATION use cases as needed.
*/
resolvedSchema = resolveSchema(context, _.cloneDeep(oldSchema), 0, _.toLower(PROCESSING_TYPE.CONVERSION));
resolvedSchema = resolveSchema(context, _.cloneDeep(oldSchema), {
resolveFor: _.toLower(PROCESSING_TYPE.CONVERSION),
isResponseSchema: parameterSourceOption === PARAMETER_SOURCE.RESPONSE
});

resolvedSchema = concreteUtils.fixExamplesByVersion(resolvedSchema);
key = JSON.stringify(resolvedSchema);
Expand Down Expand Up @@ -404,8 +407,10 @@ function getParameterDescription (parameter) {
*/
function getParamSerialisationInfo (param, parameterSource, components, options) {
var paramName = _.get(param, 'name'),
paramSchema = resolveSchema(getDefaultContext(options, components), _.cloneDeep(param.schema),
0, PROCESSING_TYPE.VALIDATION),
paramSchema = resolveSchema(getDefaultContext(options, components), _.cloneDeep(param.schema), {
resolveFor: PROCESSING_TYPE.VALIDATION,
isResponseSchema: parameterSource === PARAMETER_SOURCE.RESPONSE
}),
style, // style property defined/inferred from schema
explode, // explode property defined/inferred from schema
propSeparator, // separates two properties or values
Expand Down Expand Up @@ -494,8 +499,10 @@ function getParamSerialisationInfo (param, parameterSource, components, options)
*/
function deserialiseParamValue (param, paramValue, parameterSource, components, options) {
var constructedValue,
paramSchema = resolveSchema(getDefaultContext(options, components), _.cloneDeep(param.schema),
0, PROCESSING_TYPE.VALIDATION),
paramSchema = resolveSchema(getDefaultContext(options, components), _.cloneDeep(param.schema), {
resolveFor: PROCESSING_TYPE.VALIDATION,
isResponseSchema: parameterSource === PARAMETER_SOURCE.RESPONSE
}),
isEvenNumber = (num) => {
return (num % 2 === 0);
},
Expand Down Expand Up @@ -1335,7 +1342,10 @@ function checkValueAgainstSchema (context, property, jsonPathPrefix, txnParamNam
invalidJson = false,
valueToUse = value,

schema = resolveSchema(context, openApiSchemaObj, 0, PROCESSING_TYPE.VALIDATION),
schema = resolveSchema(context, openApiSchemaObj, {
resolveFor: PROCESSING_TYPE.VALIDATION,
isResponseSchema: parameterSourceOption === PARAMETER_SOURCE.RESPONSE
}),
compositeSchema = schema.oneOf || schema.anyOf,
compareTypes = _.get(context, 'concreteUtils.compareTypes') || concreteUtils.compareTypes;

Expand Down Expand Up @@ -1711,7 +1721,9 @@ function checkPathVariables (context, matchedPathData, transactionPathPrefix, sc
};

if (options.suggestAvailableFixes) {
const resolvedSchema = resolveSchema(context, pathVar.schema, 0, PROCESSING_TYPE.VALIDATION);
const resolvedSchema = resolveSchema(context, pathVar.schema, {
resolveFor: PROCESSING_TYPE.VALIDATION
});

mismatchObj.suggestedFix = {
key: pathVar.name,
Expand Down Expand Up @@ -1758,8 +1770,9 @@ function checkQueryParams (context, queryParams, transactionPathPrefix, schemaPa
// below will make sure for exploded params actual schema of property present in collection is present
_.forEach(schemaParams, (param) => {
let pathPrefix = param.pathPrefix,
paramSchema = resolveSchema(context, _.cloneDeep(param.schema),
0, PROCESSING_TYPE.VALIDATION),
paramSchema = resolveSchema(context, _.cloneDeep(param.schema), {
resolveFor: PROCESSING_TYPE.VALIDATION
}),
{ style, explode } = getParamSerialisationInfo(param, PARAMETER_SOURCE.REQUEST, components, options),
encodingObj = { [param.name]: { style, explode } },
metaInfo = {
Expand Down Expand Up @@ -1862,7 +1875,9 @@ function checkQueryParams (context, queryParams, transactionPathPrefix, schemaPa
};

if (options.suggestAvailableFixes) {
const resolvedSchema = resolveSchema(context, qp.schema, 0, PROCESSING_TYPE.VALIDATION);
const resolvedSchema = resolveSchema(context, qp.schema, {
resolveFor: PROCESSING_TYPE.VALIDATION
});

mismatchObj.suggestedFix = {
key: qp.name,
Expand Down Expand Up @@ -2000,7 +2015,9 @@ function checkRequestHeaders (context, headers, transactionPathPrefix, schemaPat
};

if (options.suggestAvailableFixes) {
const resolvedSchema = resolveSchema(context, header.schema, 0, PROCESSING_TYPE.VALIDATION);
const resolvedSchema = resolveSchema(context, header.schema, {
resolveFor: PROCESSING_TYPE.VALIDATION
});

mismatchObj.suggestedFix = {
key: header.name,
Expand Down Expand Up @@ -2131,15 +2148,18 @@ function checkResponseHeaders (context, schemaResponse, headers, transactionPath
};

if (options.suggestAvailableFixes) {
const resolvedSchema = resolveSchema(context, header.schema, 0, PROCESSING_TYPE.VALIDATION);
const resolvedSchema = resolveSchema(context, header.schema, {
resolveFor: PROCESSING_TYPE.VALIDATION,
isResponseSchema: true
});

mismatchObj.suggestedFix = {
key: header.name,
actualValue: null,
suggestedValue: {
key: header.name,
value: safeSchemaFaker(context, resolvedSchema || {}, PROCESSING_TYPE.VALIDATION,
PARAMETER_SOURCE.REQUEST, components, SCHEMA_FORMATS.DEFAULT, schemaCache),
PARAMETER_SOURCE.RESPONSE, components, SCHEMA_FORMATS.DEFAULT, schemaCache),
description: getParameterDescription(header)
}
};
Expand Down Expand Up @@ -2204,8 +2224,9 @@ function checkRequestBody (context, requestBody, transactionPathPrefix, schemaPa
return param.value !== OAS_NOT_SUPPORTED;
});

urlencodedBodySchema = resolveSchema(context, urlencodedBodySchema,
0, PROCESSING_TYPE.VALIDATION);
urlencodedBodySchema = resolveSchema(context, urlencodedBodySchema, {
resolveFor: PROCESSING_TYPE.VALIDATION
});

resolvedSchemaParams = resolveFormParamSchema(urlencodedBodySchema, '', encodingObj,
filteredUrlEncodedBody, {}, components, options);
Expand Down Expand Up @@ -2313,7 +2334,9 @@ function checkRequestBody (context, requestBody, transactionPathPrefix, schemaPa
};

if (options.suggestAvailableFixes) {
const resolvedSchema = resolveSchema(context, uParam.schema, 0, PROCESSING_TYPE.VALIDATION);
const resolvedSchema = resolveSchema(context, uParam.schema, {
resolveFor: PROCESSING_TYPE.VALIDATION
});

mismatchObj.suggestedFix = {
key: uParam.name,
Expand Down
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"async": "3.2.4",
"commander": "2.20.3",
"js-yaml": "4.1.0",
"json-pointer": "0.6.2",
"json-schema-merge-allof": "0.8.1",
"lodash": "4.17.21",
"neotraverse": "0.6.15",
Expand Down
35 changes: 14 additions & 21 deletions test/data/valid_openapi/readOnly.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,20 @@
"type": "array",
"items": {
"type": "object",
"$ref": "#/components/schemas/Pet"
"properties": {
"id": {
"type": "integer",
"format": "int64",
"readOnly": true
},
"name": {
"type": "string"
},
"tag": {
"type": "string",
"writeOnly": true
}
}
}
}
}
Expand Down Expand Up @@ -59,25 +72,5 @@
}
}
}
},
"components": {
"schemas": {
"Pet": {
"properties": {
"id": {
"type": "integer",
"format": "int64",
"readOnly": true
},
"name": {
"type": "string"
},
"tag": {
"type": "string",
"writeOnly": true
}
}
}
}
}
}
Loading

0 comments on commit 083b837

Please sign in to comment.