Comparing version 8.0.0 to 9.0.0
### 9.0.0 | ||
* #100 Allow extension constructors | ||
* #101 Keep string enum type | ||
* #104 Allow allOf, anyOf, oneOf to be combined with base schema | ||
* #107 Cycles in schema refs (fixed #106) | ||
* #108 Handle `not` properly | ||
* #109 Defaults options | ||
### 8.0.0 | ||
@@ -3,0 +12,0 @@ |
@@ -7,9 +7,11 @@ const Joi = require('joi'); | ||
const schemaSchema = Joi.alternatives(Joi.object().unknown(true), Joi.string()).required(); | ||
const extensionSchema = Joi.alternatives().try(Joi.object().unknown(true), Joi.function()) | ||
const optionsSchema = Joi.object({ | ||
subSchemas: Joi.object().unknown(true).allow(null), | ||
extensions: Joi.array().items(Joi.object().unknown(true)).allow(null), | ||
extensions: Joi.array().items(extensionSchema).allow(null), | ||
refineType: Joi.func().allow(null), | ||
refineSchema: Joi.func().allow(null), | ||
strictMode: Joi.boolean().default(false), | ||
useDefaults: Joi.boolean(). default(false) | ||
}); | ||
@@ -16,0 +18,0 @@ |
@@ -0,1 +1,2 @@ | ||
/* global WeakMap */ | ||
const Joi = require('joi'); | ||
@@ -6,4 +7,8 @@ const Util = require('util'); | ||
function randomString(length) { | ||
return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1); | ||
} | ||
class SchemaResolver { | ||
constructor(root, { subSchemas, refineType, refineSchema, strictMode, extensions = [] }) { | ||
constructor(root, { subSchemas, refineType, refineSchema, strictMode, useDefaults, extensions = [] }) { | ||
this.root = root; | ||
@@ -14,2 +19,4 @@ this.subSchemas = subSchemas; | ||
this.strictMode = strictMode; | ||
this.walkedSchemas = new WeakMap(); // map of schemas iterated thus far to the generated id they were given | ||
this.useDefaults = useDefaults; | ||
@@ -54,28 +61,58 @@ this.joi = Joi.extend( | ||
let resolvedSchema; | ||
if (schema.type) { | ||
resolvedSchema =this.resolveType(schema); | ||
} else if (schema.anyOf) { | ||
resolvedSchema = this.resolveAnyOf(schema); | ||
} else if (schema.allOf) { | ||
resolvedSchema = this.resolveAllOf(schema); | ||
} else if (schema.oneOf) { | ||
resolvedSchema = this.resolveOneOf(schema); | ||
} else if (schema.not) { | ||
resolvedSchema = this.resolveNot(schema); | ||
let generatedId = this.walkedSchemas.get(schema); | ||
if (generatedId) { | ||
// resolve cyclic schema by using joi reference via generated unique ids | ||
return this.resolveLink(schema) | ||
} else if (typeof schema === 'object') { | ||
generatedId = randomString(10) | ||
this.walkedSchemas.set(schema, generatedId) | ||
} | ||
if (typeof schema === 'string') { | ||
// If schema is itself a string, interpret it as a type | ||
resolvedSchema = this.resolveType({ type: schema }); | ||
} else if (schema.$ref) { | ||
resolvedSchema = this.resolve(this.resolveReference(schema.$ref)); | ||
} else if (schema.enum) { | ||
// If no type is specified, just enum | ||
resolvedSchema = this.joi.any().valid(...schema.enum); | ||
} else if (typeof schema === 'string') { | ||
// If schema is itself a string, interpret it as a type | ||
resolvedSchema = this.resolveType({ type: schema }); | ||
} else { | ||
//Fall through to whatever. | ||
//eslint-disable-next-line no-console | ||
console.warn('WARNING: schema missing a \'type\' or \'$ref\' or \'enum\': \n%s', JSON.stringify(schema, null, 2)); | ||
//TODO: Handle better | ||
resolvedSchema = this.joi.any(); | ||
const partialSchemas = []; | ||
if (schema.type) { | ||
partialSchemas.push(this.resolveType(schema)); | ||
} else if (schema.properties) { | ||
// if no type is specified, just properties | ||
partialSchemas.push(this.object(schema)) | ||
} else if (schema.format) { | ||
// if no type is specified, just format | ||
partialSchemas.push(this.string(schema)) | ||
} else if (schema.enum) { | ||
// If no type is specified, just enum | ||
partialSchemas.push(this.joi.any().valid(...schema.enum)); | ||
} | ||
if (schema.anyOf) { | ||
partialSchemas.push(this.resolveAnyOf(schema)); | ||
} | ||
if (schema.allOf) { | ||
partialSchemas.push(this.resolveAllOf(schema)); | ||
} | ||
if (schema.oneOf) { | ||
partialSchemas.push(this.resolveOneOf(schema)); | ||
} | ||
if (schema.not) { | ||
partialSchemas.push(this.resolveNot(schema)); | ||
} | ||
if (partialSchemas.length === 0) { | ||
//Fall through to whatever. | ||
//eslint-disable-next-line no-console | ||
console.warn('WARNING: schema missing a \'type\' or \'$ref\' or \'enum\': \n%s', JSON.stringify(schema, null, 2)); | ||
//TODO: Handle better | ||
partialSchemas.push(this.joi.any()); | ||
} | ||
resolvedSchema = partialSchemas.length === 1 ? partialSchemas[0] : this.joi.alternatives(partialSchemas).match('all'); | ||
} | ||
if (generatedId) { | ||
// we have finished resolving the schema, now attach the id generated earlier | ||
resolvedSchema = resolvedSchema.id(this.walkedSchemas.get(schema)) | ||
} | ||
if (this.refineSchema) { | ||
@@ -85,3 +122,7 @@ resolvedSchema = this.refineSchema(resolvedSchema, schema); | ||
return(resolvedSchema); | ||
if (this.useDefaults && schema.default) { | ||
resolvedSchema = resolvedSchema.default(schema.default) | ||
} | ||
return resolvedSchema; | ||
} | ||
@@ -184,3 +225,3 @@ | ||
return this.joi.alternatives(schema.oneOf.map(schema => this.resolve(schema))).match('any'); | ||
return this.joi.alternatives(schema.oneOf.map(schema => this.resolve(schema))).match('one'); | ||
} | ||
@@ -197,13 +238,22 @@ | ||
return schema.allOf.map(schema => this.resolve(schema)).reduce((a, v) => a.concat(v)); | ||
return this.joi.alternatives(schema.allOf.map(schema => this.resolve(schema))).match('all'); | ||
} | ||
resolveNot(schema) { | ||
Hoek.assert(Util.isArray(schema.not), 'Expected Not to be an array.'); | ||
Hoek.assert(Util.isObject(schema.not), 'Expected Not to be an object.'); | ||
return this.joi.alternatives().conditional(Joi.alternatives().try(...schema.not.map((schema) => { | ||
return this.resolve(schema); | ||
})), { then: this.joi.any().forbidden(), otherwise: this.joi.any() }); | ||
return this.joi.alternatives().conditional( | ||
'.', | ||
{ | ||
not: this.resolve(schema.not), | ||
then: this.joi.any(), | ||
otherwise: this.joi.any().forbidden() | ||
} | ||
); | ||
} | ||
resolveLink(schema) { | ||
return this.joi.link().ref(`#${this.walkedSchemas.get(schema)}`) | ||
} | ||
object(schema) { | ||
@@ -235,8 +285,6 @@ | ||
if (schema.additionalProperties === true) { | ||
joischema = joischema.unknown(true); | ||
} | ||
if (Util.isObject(schema.additionalProperties)) { | ||
joischema = joischema.pattern(/^/, this.resolve(schema.additionalProperties)); | ||
} else { | ||
joischema = joischema.unknown(schema.additionalProperties !== false); | ||
} | ||
@@ -311,3 +359,3 @@ | ||
if (schema.enum) { | ||
return this.joi.any().valid(...schema.enum); | ||
return this.joi.string().valid(...schema.enum); | ||
} | ||
@@ -314,0 +362,0 @@ |
{ | ||
"name": "enjoi", | ||
"version": "8.0.0", | ||
"version": "9.0.0", | ||
"license": "Apache 2.0", | ||
@@ -49,3 +49,4 @@ "description": "Converts json-schema to Joi schema.", | ||
"rmothilal", | ||
"sprootshift" | ||
"sprootshift", | ||
"nlundquist" | ||
], | ||
@@ -52,0 +53,0 @@ "bugs": { |
78248
386