@ibm-cloud/openapi-ruleset-utilities
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -0,1 +1,13 @@ | ||
# @ibm-cloud/openapi-ruleset-utilities [1.2.0](https://github.com/IBM/openapi-validator/compare/@ibm-cloud/openapi-ruleset-utilities@1.1.0...@ibm-cloud/openapi-ruleset-utilities@1.2.0) (2023-08-11) | ||
### Bug Fixes | ||
* support type list in existing rules ([dd4506f](https://github.com/IBM/openapi-validator/commit/dd4506f381824a89b65109a0d01e73159ea6e490)) | ||
### Features | ||
* **ibm-pattern-properties:** add new spectral-style rule ([26cdf89](https://github.com/IBM/openapi-validator/commit/26cdf89e867b32deb7a719013e6f62dd85201fa0)) | ||
# @ibm-cloud/openapi-ruleset-utilities [1.1.0](https://github.com/IBM/openapi-validator/compare/@ibm-cloud/openapi-ruleset-utilities@1.0.2...@ibm-cloud/openapi-ruleset-utilities@1.1.0) (2023-05-24) | ||
@@ -2,0 +14,0 @@ |
{ | ||
"name": "@ibm-cloud/openapi-ruleset-utilities", | ||
"description": "Programmatic utility functions for creating Spectral-formatted OpenAPI Rulesets", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"license": "Apache-2.0", | ||
@@ -11,2 +11,3 @@ "private": false, | ||
"clean": "rm -rf node_modules", | ||
"jest": "jest", | ||
"test": "jest test", | ||
@@ -13,0 +14,0 @@ "test-travis": "jest --silent --runInBand --no-colors --testNamePattern='^((?!@skip-ci).)*$' test", |
@@ -89,2 +89,19 @@ /** | ||
/** | ||
* Returns true iff 'schema' is of type 'type'. | ||
* This means that one of the following must be true: | ||
* 1. schema's 'type' field is a string and is equal to the specified type. | ||
* 2. schema's 'type' field is a list and contains the specified type. | ||
* @param {*} schema the schema to be checked | ||
* @param {*} type the desired type value to check for | ||
* @returns boolean true if 'schema' is of the specified type, false otherwise. | ||
*/ | ||
const schemaIsOfType = (schema, type) => { | ||
return ( | ||
'type' in schema && | ||
((typeof schema.type === 'string' && schema.type === type) || | ||
(Array.isArray(schema.type) && schema.type.includes(type))) | ||
); | ||
}; | ||
/* | ||
@@ -102,6 +119,6 @@ * The following functions employ heuristics to determine the logical type of a schema. Schema types | ||
* These functions do not attempt to account for contradictions (for example, a schema which | ||
* composes an `object`- and `integer`-type schemas with `allOf`), or use OAS 3.1 `type` arrays. | ||
* composes an `object`- and `integer`-type schemas with `allOf`). | ||
* They also do not account for `type` and `format` values defined separately across a | ||
* composite schema. (I.e., a `format` value is only considered meaningful in the context of a | ||
* `type`, value for which it is valid, in the same simple (non-composite) schema. | ||
* `type` value for which it is valid, in the same simple (non-composite) schema. | ||
*/ | ||
@@ -112,3 +129,3 @@ | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -118,7 +135,3 @@ */ | ||
return schemaHasConstraint(schema, s => { | ||
if ('type' in s) { | ||
return s.type === 'array'; // ignores the possibility of type arrays in OAS 3.1 | ||
} else { | ||
return isObject(s.items); | ||
} | ||
return schemaIsOfType(s, 'array') || (isObject(s.items) && !('type' in s)); | ||
}); | ||
@@ -130,3 +143,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -137,3 +150,3 @@ */ | ||
schema, | ||
s => s.type === 'string' && s.format === 'binary' | ||
s => schemaIsOfType(s, 'string') && s.format === 'binary' | ||
); | ||
@@ -145,7 +158,7 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
*/ | ||
const isBooleanSchema = schema => { | ||
return schemaHasConstraint(schema, s => s.type === 'boolean'); | ||
return schemaHasConstraint(schema, s => schemaIsOfType(s, 'boolean')); | ||
}; | ||
@@ -156,3 +169,3 @@ | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -163,3 +176,3 @@ */ | ||
schema, | ||
s => s.type === 'string' && s.format === 'byte' | ||
s => schemaIsOfType(s, 'string') && s.format === 'byte' | ||
); | ||
@@ -171,3 +184,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -178,3 +191,3 @@ */ | ||
schema, | ||
s => s.type === 'string' && s.format === 'date' | ||
s => schemaIsOfType(s, 'string') && s.format === 'date' | ||
); | ||
@@ -186,3 +199,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -193,3 +206,3 @@ */ | ||
schema, | ||
s => s.type === 'string' && s.format === 'date-time' | ||
s => schemaIsOfType(s, 'string') && s.format === 'date-time' | ||
); | ||
@@ -201,3 +214,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -209,3 +222,3 @@ */ | ||
schema, | ||
s => s.type === 'number' && s.format === 'double' | ||
s => schemaIsOfType(s, 'number') && s.format === 'double' | ||
); | ||
@@ -217,3 +230,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -224,3 +237,3 @@ */ | ||
schema, | ||
s => s.type === 'string' && Array.isArray(s.enum) | ||
s => schemaIsOfType(s, 'string') && Array.isArray(s.enum) | ||
); | ||
@@ -232,3 +245,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -240,3 +253,3 @@ */ | ||
schema, | ||
s => s.type === 'number' && s.format === 'float' | ||
s => schemaIsOfType(s, 'number') && s.format === 'float' | ||
); | ||
@@ -248,3 +261,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -255,3 +268,3 @@ */ | ||
schema, | ||
s => s.type === 'integer' && s.format === 'int32' | ||
s => schemaIsOfType(s, 'integer') && s.format === 'int32' | ||
); | ||
@@ -263,3 +276,3 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -270,3 +283,3 @@ */ | ||
schema, | ||
s => s.type === 'integer' && s.format === 'int64' | ||
s => schemaIsOfType(s, 'integer') && s.format === 'int64' | ||
); | ||
@@ -278,7 +291,7 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
*/ | ||
const isIntegerSchema = schema => { | ||
return schemaHasConstraint(schema, s => s.type === 'integer'); | ||
return schemaHasConstraint(schema, s => schemaIsOfType(s, 'integer')); | ||
}; | ||
@@ -289,7 +302,7 @@ | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
*/ | ||
const isNumberSchema = schema => { | ||
return schemaHasConstraint(schema, s => s.type === 'number'); | ||
return schemaHasConstraint(schema, s => schemaIsOfType(s, 'number')); | ||
}; | ||
@@ -300,3 +313,3 @@ | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -306,11 +319,10 @@ */ | ||
return schemaHasConstraint(schema, s => { | ||
if ('type' in s) { | ||
return s.type === 'object'; // ignores the possibility of type arrays in OAS 3.1 | ||
} else { | ||
return ( | ||
isObject(s.properties) || | ||
s.additionalProperties === true || | ||
isObject(s.additionalProperties) | ||
); | ||
} | ||
return ( | ||
schemaIsOfType(s, 'object') || | ||
(!('type' in s) && | ||
(isObject(s.properties) || | ||
s.additionalProperties === true || | ||
isObject(s.additionalProperties) || | ||
isObject(s.patternProperties))) | ||
); | ||
}); | ||
@@ -322,7 +334,7 @@ }; | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
*/ | ||
const isStringSchema = schema => { | ||
return schemaHasConstraint(schema, s => s.type === 'string'); | ||
return schemaHasConstraint(schema, s => schemaIsOfType(s, 'string')); | ||
}; | ||
@@ -333,3 +345,3 @@ | ||
* | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 schema object. | ||
* @param {object} schema - Simple or composite OpenAPI 3.0 or 3.1 schema object. | ||
* @returns {boolean} | ||
@@ -340,5 +352,6 @@ */ | ||
return ( | ||
!isObjectSchema(schema) && | ||
!isArraySchema(schema) && | ||
getSchemaType(schema) !== SchemaType.UNKNOWN | ||
isStringSchema(schema) || | ||
isNumberSchema(schema) || | ||
isIntegerSchema(schema) || | ||
isBooleanSchema(schema) | ||
); | ||
@@ -366,2 +379,3 @@ }; | ||
isStringSchema, | ||
schemaIsOfType, | ||
}; |
@@ -6,2 +6,4 @@ /** | ||
const isObject = require('./is-object'); | ||
/* | ||
@@ -33,2 +35,9 @@ * Performs validation on a schema and all of its nested schemas. | ||
) => { | ||
// Make sure 'schema' is an object. | ||
if (!isObject(schema)) { | ||
throw new Error( | ||
`the entity at location ${path.join('.')} must be a schema object` | ||
); | ||
} | ||
// If "schema" is a $ref, that means it didn't get resolved | ||
@@ -115,2 +124,19 @@ // properly (perhaps due to a circular ref), so just ignore it. | ||
if ( | ||
schema.patternProperties && | ||
typeof schema.patternProperties === 'object' | ||
) { | ||
for (const entry of Object.entries(schema.patternProperties)) { | ||
errors.push( | ||
...validateNestedSchemas( | ||
entry[1], | ||
[...path, 'patternProperties', entry[0]], | ||
validate, | ||
true, | ||
includeNot | ||
) | ||
); | ||
} | ||
} | ||
return errors; | ||
@@ -117,0 +143,0 @@ }; |
@@ -13,67 +13,126 @@ /** | ||
describe('Utility function: getSchemaType()', () => { | ||
it('should return `SchemaType.ARRAY` for `type` of `array`', async () => { | ||
expect(getSchemaType({ type: 'array' })).toBe(SchemaType.ARRAY); | ||
}); | ||
describe('type is a string', () => { | ||
it('type=array (ARRAY)', async () => { | ||
expect(getSchemaType({ type: 'array' })).toBe(SchemaType.ARRAY); | ||
}); | ||
it('type=boolean (BOOLEAN)', async () => { | ||
expect(getSchemaType({ type: 'boolean' })).toBe(SchemaType.BOOLEAN); | ||
}); | ||
it('type=string, format=date (DATE)', async () => { | ||
expect(getSchemaType({ type: 'string', format: 'date' })).toBe( | ||
SchemaType.DATE | ||
); | ||
}); | ||
it('type=string, format=date-time (DATE_TIME)', async () => { | ||
expect(getSchemaType({ type: 'string', format: 'date-time' })).toBe( | ||
SchemaType.DATE_TIME | ||
); | ||
}); | ||
it('type=number, format=double (DOUBLE)', async () => { | ||
expect(getSchemaType({ type: 'number', format: 'double' })).toBe( | ||
SchemaType.DOUBLE | ||
); | ||
}); | ||
it('type=string, enum present (ENUMERATION)', async () => { | ||
expect(getSchemaType({ type: 'string', enum: ['one', 'two'] })).toBe( | ||
SchemaType.ENUMERATION | ||
); | ||
}); | ||
it('type=number, format=float (FLOAT)', async () => { | ||
expect(getSchemaType({ type: 'number', format: 'float' })).toBe( | ||
SchemaType.FLOAT | ||
); | ||
}); | ||
it('type=integer, format=int32 (INT32)', async () => { | ||
expect(getSchemaType({ type: 'integer', format: 'int32' })).toBe( | ||
SchemaType.INT32 | ||
); | ||
}); | ||
it('type=integer, format=int64 (INT64)', async () => { | ||
expect(getSchemaType({ type: 'integer', format: 'int64' })).toBe( | ||
SchemaType.INT64 | ||
); | ||
}); | ||
it('type=integer (INTEGER)', async () => { | ||
expect(getSchemaType({ type: 'integer' })).toBe(SchemaType.INTEGER); | ||
}); | ||
it('should return `SchemaType.BOOLEAN` for `type` of `boolean`', async () => { | ||
expect(getSchemaType({ type: 'boolean' })).toBe(SchemaType.BOOLEAN); | ||
it('type=number (NUMBER)', async () => { | ||
expect(getSchemaType({ type: 'number' })).toBe(SchemaType.NUMBER); | ||
}); | ||
it('type=object (OBJECT)', async () => { | ||
expect(getSchemaType({ type: 'object' })).toBe(SchemaType.OBJECT); | ||
}); | ||
it('type=string (STRING)', async () => { | ||
expect(getSchemaType({ type: 'string' })).toBe(SchemaType.STRING); | ||
}); | ||
}); | ||
it('should return `SchemaType.DATE` for `type` of `string` and `format` of `date`', async () => { | ||
expect(getSchemaType({ type: 'string', format: 'date' })).toBe( | ||
SchemaType.DATE | ||
); | ||
}); | ||
describe('type is a list', () => { | ||
it('type contains array (ARRAY)', async () => { | ||
expect(getSchemaType({ type: ['array'] })).toBe(SchemaType.ARRAY); | ||
}); | ||
it('should return `SchemaType.DATE_TIME` for `type` of `string` and `format` of `date-time`', async () => { | ||
expect(getSchemaType({ type: 'string', format: 'date-time' })).toBe( | ||
SchemaType.DATE_TIME | ||
); | ||
}); | ||
it('type contains boolean (BOOLEAN)', async () => { | ||
expect(getSchemaType({ type: ['boolean'] })).toBe(SchemaType.BOOLEAN); | ||
}); | ||
it('should return `SchemaType.DOUBLE` for `type` of `number` and `format` of `double`', async () => { | ||
expect(getSchemaType({ type: 'number', format: 'double' })).toBe( | ||
SchemaType.DOUBLE | ||
); | ||
}); | ||
it('type contains string, format=date (DATE)', async () => { | ||
expect(getSchemaType({ type: ['string'], format: 'date' })).toBe( | ||
SchemaType.DATE | ||
); | ||
}); | ||
it('should return `SchemaType.ENUMERATION` for `type` of `string` and an `enum` array of strings', async () => { | ||
expect(getSchemaType({ type: 'string', enum: ['one', 'two'] })).toBe( | ||
SchemaType.ENUMERATION | ||
); | ||
}); | ||
it('type contains string, format=date-time (DATE_TIME)', async () => { | ||
expect(getSchemaType({ type: ['string'], format: 'date-time' })).toBe( | ||
SchemaType.DATE_TIME | ||
); | ||
}); | ||
it('should return `SchemaType.FLOAT` for `type` of `number` and `format` of `float`', async () => { | ||
expect(getSchemaType({ type: 'number', format: 'float' })).toBe( | ||
SchemaType.FLOAT | ||
); | ||
}); | ||
it('type contains number, format=double (DOUBLE)', async () => { | ||
expect(getSchemaType({ type: ['number'], format: 'double' })).toBe( | ||
SchemaType.DOUBLE | ||
); | ||
}); | ||
it('should return `SchemaType.INT32` for `type` of `integer` and `format` of `int32`', async () => { | ||
expect(getSchemaType({ type: 'integer', format: 'int32' })).toBe( | ||
SchemaType.INT32 | ||
); | ||
}); | ||
it('type contains string, enum is present (ENUMERATION)', async () => { | ||
expect(getSchemaType({ type: ['string'], enum: ['one', 'two'] })).toBe( | ||
SchemaType.ENUMERATION | ||
); | ||
}); | ||
it('should return `SchemaType.INT64` for `type` of `integer` and `format` of `int64`', async () => { | ||
expect(getSchemaType({ type: 'integer', format: 'int64' })).toBe( | ||
SchemaType.INT64 | ||
); | ||
}); | ||
it('type contains number, format=float (FLOAT)', async () => { | ||
expect(getSchemaType({ type: ['number'], format: 'float' })).toBe( | ||
SchemaType.FLOAT | ||
); | ||
}); | ||
it('should return `SchemaType.INTEGER` for `type` of `integer`', async () => { | ||
expect(getSchemaType({ type: 'integer' })).toBe(SchemaType.INTEGER); | ||
}); | ||
it('type contains integer, format=int32 (INT32)', async () => { | ||
expect(getSchemaType({ type: ['integer'], format: 'int32' })).toBe( | ||
SchemaType.INT32 | ||
); | ||
}); | ||
it('should return `SchemaType.NUMBER` for `type` of `number`', async () => { | ||
expect(getSchemaType({ type: 'number' })).toBe(SchemaType.NUMBER); | ||
}); | ||
it('type contains integer, format=int64 (INT64)', async () => { | ||
expect(getSchemaType({ type: ['integer'], format: 'int64' })).toBe( | ||
SchemaType.INT64 | ||
); | ||
}); | ||
it('should return `SchemaType.OBJECT` for `type` of `object`', async () => { | ||
expect(getSchemaType({ type: 'object' })).toBe(SchemaType.OBJECT); | ||
}); | ||
it('type contains integer (INTEGER)', async () => { | ||
expect(getSchemaType({ type: ['integer'] })).toBe(SchemaType.INTEGER); | ||
}); | ||
it('should return `SchemaType.STRING` for `type` of `string`', async () => { | ||
expect(getSchemaType({ type: 'string' })).toBe(SchemaType.STRING); | ||
it('type contains number (NUMBER)', async () => { | ||
expect(getSchemaType({ type: ['number'] })).toBe(SchemaType.NUMBER); | ||
}); | ||
it('type contains object (OBJECT)', async () => { | ||
expect(getSchemaType({ type: ['object'] })).toBe(SchemaType.OBJECT); | ||
}); | ||
it('type contains string (STRING)', async () => { | ||
expect(getSchemaType({ type: ['string'] })).toBe(SchemaType.STRING); | ||
}); | ||
}); | ||
}); |
@@ -29,4 +29,3 @@ /** | ||
// Skipped: debatable whether this test ought to pass, but maybe for OAS 3.1 support | ||
it.skip('should return `true` for an object with `type` of ["array"]', async () => { | ||
it('should return `true` if `type` contains "array"', async () => { | ||
expect(isArraySchema({ type: ['array'] })).toBe(true); | ||
@@ -58,3 +57,13 @@ }); | ||
).toBe(true); | ||
expect( | ||
isArraySchema({ | ||
oneOf: [ | ||
{ | ||
allOf: [{ anyOf: [{ items: {} }, { type: ['array'] }] }, {}], | ||
}, | ||
{ type: ['array'] }, | ||
], | ||
}) | ||
).toBe(true); | ||
}); | ||
}); |
@@ -28,5 +28,3 @@ /** | ||
}); | ||
// Skipped: debatable whether this test ought to pass, but maybe for OAS 3.1 support | ||
it.skip('should return `true` for an object with `type` of ["object"]', async () => { | ||
it('should return `true` for an object if `type` contains "object"', async () => { | ||
expect(isObjectSchema({ type: ['object'] })).toBe(true); | ||
@@ -47,6 +45,28 @@ }); | ||
it('should return `false` for an object with non-"object" `type` and an `properties` object', async () => { | ||
it('should return `false` for an object with no `type` and an `additionalProperties` of `false`', async () => { | ||
expect(isObjectSchema({ additionalProperties: false })).toBe(false); | ||
}); | ||
it('should return `true` for an object with no `type` and a `patternProperties` object', async () => { | ||
expect( | ||
isObjectSchema({ patternProperties: { '.*': { type: 'string' } } }) | ||
).toBe(true); | ||
}); | ||
it('should return `false` for an object with no `type` and a `patternProperties` not an object', async () => { | ||
expect( | ||
isObjectSchema({ | ||
patternProperties: 'bad patternProperties object!', | ||
}) | ||
).toBe(false); | ||
}); | ||
it('should return `false` for an object with non-"object" `type` and a `properties` object', async () => { | ||
expect(isObjectSchema({ type: 'array', properties: {} })).toBe(false); | ||
}); | ||
it('should return `false` for a non-object value for "schema"', async () => { | ||
expect(isObjectSchema('this is not a schema object')).toBe(false); | ||
}); | ||
it('should return `false` for an object with no `type` and a non-object `properties`', async () => { | ||
@@ -67,2 +87,12 @@ expect(isObjectSchema({ properties: null })).toBe(false); | ||
).toBe(true); | ||
expect( | ||
isObjectSchema({ | ||
oneOf: [ | ||
{ | ||
allOf: [{ properties: {} }, {}], | ||
}, | ||
{ type: ['object'] }, | ||
], | ||
}) | ||
).toBe(true); | ||
}); | ||
@@ -69,0 +99,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
114852
44
3323