UNPKG

joi-to-json

Version:

joi to JSON / OpenAPI Schema Converter

242 lines (209 loc) 5.65 kB
## Logical Relation There are some logical relation operators in Joi: * `and` * `nand` * `or` * `xor` * `oxor` (After Joi v14) * `with` * `without` For different operator, I have managed to describe them in JSON schema as below for different cases. Some named as `xxxGeneral` means it should be supported since JSON Draft 4. Some named as `xxxDraft7` means it is using some features until JSON Draft 7. Hence, if you are converting the Joi to Draft 4 or OpenAPI, the `xor` will be ignored. ### Usage By default, this feature is enabled. It can be disabled completely by passing option `{ logicalOpParser: false }` to `parse` API: `parse(joiObj, 'json', {}, { logicalOpParser: false })` It's also possible to disable one particular operator or use your own convertor if you come up with a more suitable JSON schema representation by passing options like `{ logicalOpParser: { xor: convertorFun, oxor: null } }`. The signature of the `convertorFun` is `function (schema, dependency)`. For example, the built-in `or` convertor function is: ```javascript function (schema, dependency) { schema.anyOf = _.map(dependency.peers, (peer) => { return { required: [peer] } }) } ``` ### OR ```javascript // At least one of a or b or c exists. // Failed on { d: 1 } const orJoi = joi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).or('a', 'b', 'c'); const orSchemaGeneral = { type: 'object', anyOf: [ { required: ['a'] }, { required: ['b'] }, { required: ['c'] } ], properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false } ``` ### AND ```javascript // Either a, b, and c All NOT exists, or all of them exists // Failed on { a: 'hi', b: 1 } const andJoi = joi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).and('a', 'b', 'c'); const andSchemaGeneral = { type: 'object', oneOf: [ { allOf: [ { not: { required: ['a'] } }, { not: { required: ['b'] } }, { not: { required: ['c'] } } ] }, { required: ['a', 'b', 'c'] } ], properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ``` ### NAND ```javascript // a, b, and c cannot all exist at the same time // Failed on { a: 'hi', b: 1, c: true } const nandJoi = joi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).nand('a', 'b', 'c'); const nandSchemaGeneral = { type: 'object', not: { required: ['a', 'b', 'c'] }, properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ``` ### XOR ```javascript // Only one of a, b and c can and must exist // Failed on { d: 1 } or { a: 'hi', b: 1 } const xorJoi = joi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).xor('a', 'b', 'c'); const xorSchemaDraft7 = { type: 'object', if: { propertyNames: { enum: ['a', 'b', 'c'] }, minProperties: 2 }, then: false, else: { oneOf: [ { required: ['a'] }, { required: ['b'] }, { required: ['c'] } ] }, properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ``` ### OXOR ```javascript // Only one of a, b and c can exist but none is required // Failed on { a: 'hi', b: 1 } const oxorJoi = joi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).oxor('a', 'b', 'c'); const oxorSchemaGeneral = { type: 'object', oneOf: [ { required: ['a'] }, { required: ['b'] }, { required: ['c'] }, { not: { oneOf: [ { required: ['a'] }, { required: ['b'] }, { required: ['c'] }, { required: ['a', 'b'] }, { required: ['a', 'c'] }, { required: ['b', 'c'] } // Combination up to 2 elements ] } } ], properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ``` ### WITH ```javascript // With d exists, both a and b must exist // Failed on { d: 1, a: '' } const withJoi = jjoi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).with('c', ['a']).with('d', ['a', 'b']); const withSchemaBeforeBefore2019 = { type: 'object', dependencies: { c: ['a'], d: ['a', 'b'] }, properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; const withSchemaBeforeDraft2019 = { type: 'object', dependentRequired: { c: ['a'], d: ['a', 'b'] }, properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ``` ### WITHOUT ```javascript // With a exists, either b or c must not exist // Failed on { a: '', b: 1 } const withoutJoi = jjoi.object({ a: joi.string(), b: joi.number(), c: joi.boolean(), d: joi.number() }).without('a', ['b', 'c']); const withoutSchemaDraft7 = { type: 'object', if: { required: ['a'] }, then: { not: { anyOf: [{ required: ['b'] }, { required: ['c'] }] } }, properties: { a: { type: 'string' }, b: { type: 'number' }, c: { type: 'boolean' }, d: { type: 'number' } }, additionalProperties: false }; ```