From 48a440ac89fc220eded1da0e834d9446a1d4ebbc Mon Sep 17 00:00:00 2001 From: uzlopak Date: Fri, 26 Jan 2024 23:15:51 +0100 Subject: [PATCH] fix: determine type of combinedKeywords --- package.json | 1 + src/BaseSchema.test.js | 5 ++++- src/FluentSchema.integration.test.js | 4 +++- src/FluentSchema.test.js | 14 +++++++++++- src/ObjectSchema.js | 5 +++-- src/utils.js | 33 ++++++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0e50ce1..b63b1ac 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "scripts": { "lint": "standard | snazzy", + "lint:fix": "standard --fix", "test": "npm run test:unit && npm run test:typescript", "test:unit": "jest --coverage", "test:watch": "jest src/*.test.js --verbose --watch", diff --git a/src/BaseSchema.test.js b/src/BaseSchema.test.js index 5157d5e..56790c6 100644 --- a/src/BaseSchema.test.js +++ b/src/BaseSchema.test.js @@ -548,7 +548,10 @@ describe('BaseSchema', () => { ).toEqual({ $schema: 'http://json-schema.org/draft-07/schema#', properties: { - prop: { anyOf: [{ type: 'string' }, { type: 'null' }] } + prop: { + type: ['string', 'null'], + anyOf: [{ type: 'string' }, { type: 'null' }] + } }, type: 'object' }) diff --git a/src/FluentSchema.integration.test.js b/src/FluentSchema.integration.test.js index daf6a41..c034704 100644 --- a/src/FluentSchema.integration.test.js +++ b/src/FluentSchema.integration.test.js @@ -224,7 +224,9 @@ describe('S', () => { }) describe('compose keywords', () => { - const ajv = new Ajv() + const ajv = new Ajv({ + allowUnionTypes: true + }) const schema = S.object() .prop('foo', S.anyOf([S.string()])) .prop('bar', S.not(S.anyOf([S.integer()]))) diff --git a/src/FluentSchema.test.js b/src/FluentSchema.test.js index e20144e..2809e61 100644 --- a/src/FluentSchema.test.js +++ b/src/FluentSchema.test.js @@ -202,13 +202,24 @@ describe('S', () => { }) describe('composition', () => { + it('anyOf', () => { + const schema = S.object() + .prop('foo', S.string().anyOf([S.string()])) + .valueOf() + expect(schema).toEqual({ + $schema: 'http://json-schema.org/draft-07/schema#', + properties: { foo: { type: 'string', anyOf: [{ type: 'string' }] } }, + type: 'object' + }) + }) + it('anyOf', () => { const schema = S.object() .prop('foo', S.anyOf([S.string()])) .valueOf() expect(schema).toEqual({ $schema: 'http://json-schema.org/draft-07/schema#', - properties: { foo: { anyOf: [{ type: 'string' }] } }, + properties: { foo: { type: 'string', anyOf: [{ type: 'string' }] } }, type: 'object' }) }) @@ -225,6 +236,7 @@ describe('S', () => { $schema: 'http://json-schema.org/draft-07/schema#', properties: { multipleRestrictedTypesKey: { + type: ['string', 'number'], oneOf: [{ type: 'string' }, { minimum: 10, type: 'number' }] }, notTypeKey: { not: { oneOf: [{ pattern: 'js$', type: 'string' }] } } diff --git a/src/ObjectSchema.js b/src/ObjectSchema.js index 26bf978..4965b91 100644 --- a/src/ObjectSchema.js +++ b/src/ObjectSchema.js @@ -8,7 +8,8 @@ const { patchIdsWithParentId, appendRequired, FluentSchemaError, - combineDeepmerge + combineDeepmerge, + getCombinedType } = require('./utils') const initialState = { @@ -303,7 +304,7 @@ const ObjectSchema = ({ schema = initialState, ...options } = {}) => { } const type = hasCombiningKeywords(attributes) - ? undefined + ? getCombinedType(attributes) : attributes.type // strip undefined values or empty arrays or internals diff --git a/src/utils.js b/src/utils.js index 40446b5..f44e86b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -5,6 +5,38 @@ const isFluentSchema = obj => obj && obj.isFluentSchema const hasCombiningKeywords = attributes => attributes.allOf || attributes.anyOf || attributes.oneOf || attributes.not +const getCombinedType = (attributes) => { + const resultSet = new Set() + + if (attributes.type) { + resultSet.add(attributes.type) + } + + if (attributes.allOf) { + for (const item of attributes.allOf) { + resultSet.add(item.type) + } + } + + if (attributes.anyOf) { + for (const item of attributes.anyOf) { + resultSet.add(item.type) + } + } + + if (attributes.oneOf) { + for (const item of attributes.oneOf) { + resultSet.add(item.type) + } + } + + if (resultSet.size === 1) { + return [...resultSet][0] + } + + return [...resultSet] +} + class FluentSchemaError extends Error { constructor (message) { super(message) @@ -224,6 +256,7 @@ const setComposeType = ({ prop, schemas, schema, options }) => { module.exports = { isFluentSchema, + getCombinedType, hasCombiningKeywords, FluentSchemaError, last,