UNPKG

joi-to-json

Version:

joi to JSON / OpenAPI Schema Converter

331 lines (295 loc) 7.97 kB
const _ = require('lodash') class JoiSpecConvertor { constructor() {} static getVersion(joiObj) { return joiObj._currentJoi.version } static getSupportVersion() { return '12' } _convertObject(joiObj) { _.each(joiObj.rules, (rule, _idx) => { const name = rule.name if (['min', 'max', 'length'].includes(name)) { rule.args = { limit: rule.arg } } delete rule.arg }) if (joiObj.children) { joiObj.keys = joiObj.children delete joiObj.children } _.each(joiObj.keys, (value, _key) => { this.toBaseSpec(value) }) _.each(joiObj.patterns, (pattern, _idx) => { this.toBaseSpec(pattern.rule) }) this._convertDependencies(joiObj) } _convertDependencies(joiObj) { _.each(joiObj.dependencies, (dependency) => { if (dependency.key === null) { delete dependency.key } if (dependency.type) { dependency.rel = dependency.type delete dependency.type } }) } _convertDate(joiObj) { if (joiObj.flags.timestamp) { joiObj.flags.format = joiObj.flags.timestamp delete joiObj.flags.timestamp delete joiObj.flags.multiplier } else if (joiObj.flags.format = {}) { joiObj.flags.format = 'iso' } } _convertAlternatives(joiObj) { // backup alternatives setting const alternatives = joiObj.alternatives let fieldName = 'matches' if (joiObj.base) { // When case is based on one schema type fieldName = 'whens' const baseType = joiObj.base delete joiObj.base delete joiObj.alternatives if (joiObj.flags && joiObj.flags.presence) { delete joiObj.flags.presence } this.toBaseSpec(baseType) _.merge(joiObj, baseType) } // Convert alternatives joiObj[fieldName] = _.map(alternatives, (alternative) => { if (alternative.peek || alternative.ref) { if (alternative.peek) { alternative.is = alternative.peek delete alternative.peek } return _.mapValues(alternative, (value, condition) => { switch (condition) { case 'is': this.toBaseSpec(value) if (alternative.ref) { // alternative field reference case value.type = 'any' value.allow.splice(0, 0, { 'override': true }) } break case 'otherwise': this.toBaseSpec(value) break case 'then': this.toBaseSpec(value) break case 'ref': if (value.indexOf('ref:') === 0) { value = { path: value.replace('ref:', '').split('.') } } break } return value }) } else { return { schema: this.toBaseSpec(alternative) } } }) // FIXME: Joi seems merging the `is` to `then` schema _.forEach(joiObj[fieldName], (alternative) => { if (alternative.is && alternative.then) { if (alternative.is.type === 'object') { const isKeys = Object.keys(alternative.is.keys) _.forEach(isKeys, (key) => { delete alternative.then.keys[key] }) } } }) delete joiObj.alternatives } _convertBinary(joiObj) { _.each(joiObj.rules, (rule, _idx) => { const name = rule.name if (['min', 'max', 'length'].includes(name)) { rule.args = { limit: rule.arg } } delete rule.arg }) } _convertString(joiObj) { delete joiObj.options _.each(joiObj.rules, (rule, _idx) => { const name = rule.name if (['min', 'max'].includes(name)) { rule.args = { limit: rule.arg } } else if (['lowercase', 'uppercase'].includes(name)) { rule.name = 'case' rule.args = { direction: name.replace('case', '') } delete joiObj.flags.case } else if (rule.arg) { rule.args = { options: rule.arg } } if (name === 'regex') { rule.name = 'pattern' rule.args.regex = rule.arg.pattern.toString() delete rule.args.options.pattern } delete rule.arg }) } _convertNumber(joiObj) { _.each(joiObj.rules, (rule, _idx) => { const name = rule.name if (['positive', 'negative'].includes(name)) { rule.args = { sign: name } rule.name = 'sign' } if (['greater', 'less', 'min', 'max', 'precision'].includes(name)) { rule.args = { limit: rule.arg } } if (name === 'multiple') { rule.args = { base: rule.arg } } delete rule.arg }) if (joiObj.flags.precision) { delete joiObj.flags.precision } } _convertBoolean(joiObj) { if (_.isArray(joiObj.truthy)) { _.remove(joiObj.truthy, (i) => i === true) if (joiObj.truthy.length === 0) { delete joiObj.truthy } } if (_.isArray(joiObj.falsy)) { _.remove(joiObj.falsy, (i) => i === false) if (joiObj.falsy.length === 0) { delete joiObj.falsy } } if (joiObj.flags.insensitive === false) { joiObj.flags.sensitive = true } delete joiObj.flags.insensitive } _convertArray(joiObj) { if (joiObj.flags.sparse === false) { delete joiObj.flags.sparse } _.each(joiObj.rules, (rule, _idx) => { const name = rule.name if (['min', 'max', 'length'].includes(name)) { rule.args = { limit: rule.arg } } if (name === 'has') { this.toBaseSpec(rule.arg) rule.args = { schema: rule.arg } } delete rule.arg }) _.each(joiObj.items, (item, _idx) => { this.toBaseSpec(item) }) } _convertExamples(_joiObj) { } toBaseSpec(joiObj) { joiObj.flags = joiObj.flags || {} if (joiObj.flags.allowOnly) { joiObj.flags.only = joiObj.flags.allowOnly } delete joiObj.flags.allowOnly if (joiObj.flags.allowUnknown) { joiObj.flags.unknown = joiObj.flags.allowUnknown } delete joiObj.flags.allowUnknown if (joiObj.description) { joiObj.flags.description = joiObj.description delete joiObj.description } if (joiObj.options) { joiObj.preferences = joiObj.options delete joiObj.options } if (joiObj.valids) { joiObj.allow = joiObj.valids delete joiObj.valids } if (joiObj.meta) { joiObj.metas = joiObj.meta delete joiObj.meta } if (joiObj.invalids) { // FIXME Looks like before v16, joi spec default add [''] for string and [Infinity,-Infinity] for number if (!(joiObj.invalids.length === 1 && joiObj.invalids[0] === '') && !(joiObj.invalids.length === 2 && joiObj.invalids[0] === Infinity && joiObj.invalids[1] === -Infinity)) { joiObj.invalid = joiObj.invalids } } delete joiObj.invalids this._convertExamples(joiObj) switch (joiObj.type) { case 'object': this._convertObject(joiObj) break case 'date': this._convertDate(joiObj) break case 'alternatives': this._convertAlternatives(joiObj) break case 'binary': this._convertBinary(joiObj) break case 'string': this._convertString(joiObj) break case 'number': this._convertNumber(joiObj) break case 'boolean': this._convertBoolean(joiObj) break case 'array': this._convertArray(joiObj) break default: break } if (_.isEmpty(joiObj.flags)) { delete joiObj.flags } return joiObj } } module.exports = JoiSpecConvertor