Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Gi60s/openapi-enforcer
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5f3718155e0e07f0f092374a5a6031353e03668f
Choose a base ref
...
head repository: Gi60s/openapi-enforcer
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: f6427c46f04e25eeb4986330c9fe98f20f656666
Choose a head ref
  • 2 commits
  • 10 files changed
  • 1 contributor

Commits on Mar 2, 2024

  1. Copy the full SHA
    c8d7201 View commit details
  2. 1.23.0

    Gi60s committed Mar 2, 2024
    Copy the full SHA
    f6427c4 View commit details
Showing with 177 additions and 13 deletions.
  1. +12 −0 CHANGELOG.md
  2. +2 −1 index.js
  3. +2 −2 package-lock.json
  4. +1 −1 package.json
  5. +6 −1 src/enforcers/Schema.js
  6. +7 −4 src/schema/deserialize.js
  7. +7 −4 src/schema/serialize.js
  8. +3 −0 src/schema/validate.js
  9. +12 −0 src/util.js
  10. +125 −0 test/issues.test.js
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 1.23.0

### Added

- **You Can Ignore Undefined Property Values**

The default implementation complains of objects where a property is defined but set to `undefined`.
This will cause Schema instances to fail validations, serialization, and deserialization.
Now you have the option to set the global `Enforcer.config.ignoreUndefinedPropertyValues` to `true` or `false` (default)
or when calling a Schema instance's `validate` function you can specify the `ignoreUndefinedPropertyValues` as an option property.
Serialization and deserialization will now ignore undefined values in all cases.

## 1.22.3

### Security
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -89,7 +89,8 @@ async function Enforcer(definition, options) {
Enforcer.config = {
examplesWarnAdditionalProperty: true,
useCaseSensitivePaths: true,
useNewRefParser: false
useNewRefParser: false,
ignoreUndefinedPropertyValues: false
};

Enforcer.bundle = async function (definition) {
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openapi-enforcer",
"version": "1.22.3",
"version": "1.23.0",
"description": "Library for validating, parsing, and formatting data against open api schemas.",
"main": "index.js",
"directories": {
7 changes: 6 additions & 1 deletion src/enforcers/Schema.js
Original file line number Diff line number Diff line change
@@ -206,7 +206,11 @@ const prototype = {
* Check to see if the value is valid for this schema.
* @param {*} value
* @param {object} [options]
* @param {string} [options.readWriteMode] Can be undefined, "read", or "write"
* @param {boolean} [options.enum] Set to false to skip enum validation.
* @param {boolean} [options.isExample] If the passed in value is an example then set this to true.
* @param {boolean} [options.maxMin] Set to false to skip max min validation.
* @param {'read', 'write} [options.readWriteMode] Set to 'read' if in read only mode or to 'write' if write only mode.
* @param {boolean} [options.ignoreUndefinedPropertyValues] Whether to ignore undefined property values during validation.
* @returns {EnforcerException|undefined}
*/
validate: function(value, options) {
@@ -923,6 +927,7 @@ function buildInjector(rxGenerator) {
* @param {boolean} [options.isExample] If the passed in value is an example then set this to true.
* @param {boolean} [options.maxMin] Set to false to skip max min validation.
* @param {'read', 'write} [options.readWriteMode] Set to 'read' if in read only mode or to 'write' if write only mode.
* @param {boolean} [options.ignoreUndefinedPropertyValues] Whether to ignore undefined property values during validation.
* @returns {*}
*/
function deserializeAndValidate(schema, exception, value, options) {
11 changes: 7 additions & 4 deletions src/schema/deserialize.js
Original file line number Diff line number Diff line change
@@ -95,10 +95,13 @@ function runDeserialize(exception, map, schema, originalValue, options) {
const additionalProperties = schema.additionalProperties;
const properties = schema.properties || {};
Object.keys(value).forEach(key => {
if (properties.hasOwnProperty(key)) {
value[key] = runDeserialize(exception.at(key), map, properties[key], Value.inherit(value[key], { serialize }), options);
} else if (additionalProperties) {
value[key] = runDeserialize(exception.at(key), map, additionalProperties, Value.inherit(value[key], { serialize }), options);
const nestedValue = value[key]
if (nestedValue !== undefined) {
if (properties.hasOwnProperty(key)) {
value[key] = runDeserialize(exception.at(key), map, properties[key], Value.inherit(value[key], {serialize}), options);
} else if (additionalProperties) {
value[key] = runDeserialize(exception.at(key), map, additionalProperties, Value.inherit(value[key], {serialize}), options);
}
}
});

11 changes: 7 additions & 4 deletions src/schema/serialize.js
Original file line number Diff line number Diff line change
@@ -84,10 +84,13 @@ function runSerialize(exception, map, schema, originalValue) {
const additionalProperties = schema.additionalProperties;
const properties = schema.properties || {};
Object.keys(value).forEach(key => {
if (properties.hasOwnProperty(key)) {
value[key] = runSerialize(exception.at(key), map, properties[key], value[key]);
} else if (additionalProperties) {
value[key] = runSerialize(exception.at(key), map, additionalProperties, value[key]);
const nestedValue = value[key]
if (nestedValue !== undefined) {
if (properties.hasOwnProperty(key)) {
value[key] = runSerialize(exception.at(key), map, properties[key], nestedValue);
} else if (additionalProperties) {
value[key] = runSerialize(exception.at(key), map, additionalProperties, nestedValue);
}
}
});

3 changes: 3 additions & 0 deletions src/schema/validate.js
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ module.exports = runValidate;
* @param {boolean} [options.isExample] If the passed in value is an example then set this to true.
* @param {boolean} [options.maxMin] Set to false to skip max min validation.
* @param {'read', 'write} [options.readWriteMode] Set to 'read' if in read only mode or to 'write' if write only mode.
* @param {boolean} [options.ignoreUndefinedPropertyValues] Whether to ignore undefined property values during validation.
* @returns {*}
*/
function runValidate(exception, map, schema, originalValue, options) {
@@ -180,11 +181,13 @@ function runValidate(exception, map, schema, originalValue, options) {
})
: [];
const keys = Object.keys(value);
const ignoreUndefinedProperties = util.ignoreUndefinedProperties(options);

// validate each property in the value
keys.forEach(key => {
// remove item for required remaining array
const index = required.indexOf(key);
if (value[key] === undefined && ignoreUndefinedProperties) return;
if (index !== -1) required.splice(index, 1);

if (properties.hasOwnProperty(key)) {
12 changes: 12 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ const queryString = require('querystring');
const rx = require('./rx');

const Exception = require('./exception');
const Enforcer = require("../index");
const rxMediaType = /^([\s\S]+?)\/(?:([\s\S]+?)\+)?([\s\S]+?)$/;
const punctuation = ',,,,,,,,,,.................................:;!?';
const punctuationCount = punctuation.length;
@@ -38,6 +39,7 @@ module.exports = {
getDateFromValidDateString,
getDefinitionType,
greatestCommonDenominator,
ignoreUndefinedProperties,
isDate,
isNumber,
isInteger,
@@ -319,6 +321,16 @@ function greatestCommonDenominator(x, y) {
return x;
}

/**
* @param {object} options
* @param {boolean} [options.ignoreUndefinedPropertyValues] Whether to ignore undefined property values during validation.
* @returns boolean
*/
function ignoreUndefinedProperties (options) {
if (options.ignoreUndefinedPropertyValues !== undefined) return options.ignoreUndefinedPropertyValues
return Enforcer.config.ignoreUndefinedPropertyValues
}

function isDate (value) {
return value && !isNaN(value) && value instanceof Date;
}
125 changes: 125 additions & 0 deletions test/issues.test.js
Original file line number Diff line number Diff line change
@@ -469,4 +469,129 @@ describe('documented issues fixes', () => {
})
})

describe('issue-161 - allow undefined values to be skipped during validation', () => {
let schema

before(() => {
schema = Enforcer.v3_0.Schema({
type: 'object',
required: ['bool'],
properties: {
str: { type: 'string' },
obj: {
type: 'object',
properties: {
num: { type: 'string' }
}
},
bool: { type: 'boolean' }
}
}).value
})

describe('global config allowUndefinedValuesInObjectValidation set to false', () => {
let previousConfigValue

before(() => {
previousConfigValue = Enforcer.config.ignoreUndefinedPropertyValues
Enforcer.config.ignoreUndefinedPropertyValues = false
})

after(() => {
Enforcer.config.ignoreUndefinedPropertyValues = previousConfigValue
})

it('validate will not allow undefined values', () => {
const err = schema.validate({
str: 'hello',
obj: undefined,
bool: true
})
expect(err.toString().replace(/(\r)?\n/g, ' ')).to.match(/at: obj[\s\S]+?Received: undefined/)
})

it('validate will not allow undefined values for required properties', () => {
const err = schema.validate({
a: 'hello',
bool: undefined
})
expect(err.toString().replace(/(\r)?\n/g, ' ')).to.match(/at: bool[\s\S]+?Received: undefined/)
})

it('serialize will allow undefined values', () => {
const { error } = schema.serialize({
a: 'hello',
obj: undefined,
bool: true
})
expect(error).to.equal(undefined)
})

it('validate will ignore undefined values when specified via the option', () => {
const err = schema.validate({
a: 'hello',
obj: undefined,
bool: true
}, { ignoreUndefinedPropertyValues: true })
expect(err).to.equal(undefined)
})

it('validate will not allow undefined values for required properties even when ignored', () => {
const err = schema.validate({
a: 'hello',
bool: undefined
}, { ignoreUndefinedPropertyValues: true })
expect(err.toString()).to.contain('One or more required properties missing: bool')
})
})

describe('global config allowUndefinedValuesInObjectValidation set to true', () => {
let previousConfigValue

before(() => {
previousConfigValue = Enforcer.config.ignoreUndefinedPropertyValues
Enforcer.config.ignoreUndefinedPropertyValues = true
})

after(() => {
Enforcer.config.ignoreUndefinedPropertyValues = previousConfigValue
})

it('validate will allow undefined values', () => {
const err = schema.validate({
str: 'hello',
obj: undefined,
bool: true
})
expect(err).to.equal(undefined)
})

it('validate will not allow undefined values for required properties', () => {
const err = schema.validate({
a: 'hello',
bool: undefined
})
expect(err.toString()).to.contain('One or more required properties missing: bool')
})

it('serialize will allow undefined values', () => {
const { error } = schema.serialize({
a: 'hello',
obj: undefined,
bool: true
})
expect(error).to.equal(undefined)
})

it('validate will not ignore undefined values when specified via the option', () => {
const err = schema.validate({
a: 'hello',
obj: undefined,
bool: true
}, { ignoreUndefinedPropertyValues: false })
expect(err.toString().replace(/(\r)?\n/g, ' ')).to.match(/at: obj[\s\S]+?Received: undefined/)
})
})
})

});