joi-to-swagger
Advanced tools
Comparing version 3.3.0 to 4.0.0
@@ -0,1 +1,16 @@ | ||
4.0.0 / 2020-02-07 | ||
================== | ||
* BREAKING CHANGE: Move joi to @hapi/joi package with version > 16.0 which was a major rewrite | ||
* BREAKING CHANGE: Instead of use joi.object().unknown(false) to disallow additional properties | ||
this is the default now. To allow additional properties use joi.object().unknown() | ||
* BREAKING CHANGE: removed support for "swaggerIndex", because it is no longer needed due to added support of "oneOf, anyOf, allOf" keywords | ||
* add support for OAS3 "oneOf, anyOf, allOf and not" keywords | ||
- this functionality is reflected in the processing of `joi.when()` conditions, `joi.alternatives()` and `joi.array().items()` | ||
* add support for "switch" condition - `joi.when('x', { is: true, switch: { ... } })` | ||
* add support for "invalid" method - `joi.string().invalid('A','B','C')` | ||
* add add support for "uuid" format for string type | ||
* add support for ES6 default import syntax | ||
* fix string token pattern | ||
3.3.0 / 2019-10-18 | ||
@@ -2,0 +17,0 @@ ================== |
@@ -1,2 +0,2 @@ | ||
import { Schema } from 'joi'; | ||
import { Schema } from '@hapi/joi'; | ||
@@ -3,0 +3,0 @@ interface SwaggerSchema { |
487
index.js
'use strict'; | ||
var joi = require('joi'); | ||
var { find, get, set, merge } = require('lodash'); | ||
const joi = require('@hapi/joi'); | ||
const { find, get, isEqual, isNumber, isPlainObject, isString, merge, set, uniqWith } = require('lodash'); | ||
var patterns = { | ||
const patterns = { | ||
alphanum: '^[a-zA-Z0-9]*$', | ||
alphanumLower: '^[a-z0-9]*$', | ||
alphanumUpper: '^[A-Z0-9]*$', | ||
token: '^[a-zA-Z0-9_]*$', | ||
}; | ||
var isJoi = function (joiObj) { | ||
return !!((joiObj && joiObj.isJoi)); | ||
}; | ||
function meta (schema, key) { | ||
const flattened = Object.assign.apply(null, [ {} ].concat(schema.$_terms.metas)); | ||
var hasJoiMeta = function (joiObj) { | ||
return !!((isJoi(joiObj) && Array.isArray(joiObj._meta))); | ||
}; | ||
return get(flattened, key); | ||
} | ||
var getJoiMetaProperty = function (joiObj, propertyName) { | ||
function refDef (type, name) { | ||
return { $ref: '#/components/' + type + '/' + name }; | ||
} | ||
// get headers added using meta function | ||
if (isJoi(joiObj) && hasJoiMeta(joiObj)) { | ||
function getMinMax (schema, suffix = 'Length') { | ||
const swagger = {}; | ||
for (let i = 0; i < schema._rules.length; i++) { | ||
const test = schema._rules[i]; | ||
if (test.name === 'min') { | ||
swagger[`min${suffix}`] = test.args.limit; | ||
} | ||
var joiMeta = joiObj._meta; | ||
let i = joiMeta.length; | ||
while (i--) { | ||
if (joiMeta[i][propertyName]) { | ||
return joiMeta[i][propertyName]; | ||
} | ||
if (test.name === 'max') { | ||
swagger[`max${suffix}`] = test.args.limit; | ||
} | ||
} | ||
return undefined; | ||
}; | ||
module.exports = exports = function parse (schema, existingComponents) { | ||
// inspect(schema); | ||
if (!schema) throw new Error('No schema was passed.'); | ||
if (typeof schema === 'object' && !schema.isJoi) { | ||
schema = joi.object().keys(schema); | ||
if (test.name === 'length') { | ||
swagger[`min${suffix}`] = test.args.limit; | ||
swagger[`max${suffix}`] = test.args.limit; | ||
} | ||
} | ||
return swagger; | ||
} | ||
if (!schema.isJoi) throw new TypeError('Passed schema does not appear to be a joi schema.'); | ||
var override = meta(schema, 'swagger'); | ||
if (override && meta(schema, 'swaggerOverride')) { | ||
return { swagger: override, components: {} }; | ||
function getCaseSuffix (schema) { | ||
const caseRule = find(schema._rules, { name: 'case' }); | ||
if (caseRule && caseRule.args.direction === 'lower') { | ||
return 'Lower'; | ||
} else if (caseRule && caseRule.args.direction === 'upper') { | ||
return 'Upper'; | ||
} | ||
return ''; | ||
} | ||
var metaDefName = meta(schema, 'className'); | ||
var metaDefType = meta(schema, 'classTarget') || 'schemas'; | ||
function parseWhens (schema, existingComponents, newComponentsByRef) { | ||
const whens = get(schema, '$_terms.whens'); | ||
const mode = whens.length > 1 ? 'anyOf' : 'oneOf'; | ||
// if the schema has a definition class name, and that | ||
// definition is already defined, just use that definition | ||
if (metaDefName && get(existingComponents, [ metaDefType, metaDefName ])) { | ||
return { swagger: refDef(metaDefType, metaDefName) }; | ||
const alternatives = []; | ||
for (const w of whens) { | ||
if (w.then) alternatives.push(w.then); | ||
if (w.otherwise) alternatives.push(w.otherwise); | ||
if (w.switch) { | ||
for (const s of w.switch) { | ||
if (s.then) alternatives.push(s.then); | ||
if (s.otherwise) alternatives.push(s.otherwise); | ||
} | ||
} | ||
} | ||
if (get(schema, '_flags.presence') === 'forbidden') { | ||
return false; | ||
} | ||
return schemaForAlternatives(alternatives, existingComponents, newComponentsByRef, mode); | ||
} | ||
var swagger; | ||
var components = {}; | ||
function schemaForAlternatives (alternatives, existingComponents, newComponentsByRef, mode) { | ||
let swaggers = []; | ||
for (const joiSchema of alternatives) { | ||
const { swagger, components } = parse(joiSchema, merge({}, existingComponents || {}, newComponentsByRef || {})); | ||
if (!swagger) continue; // swagger is falsy if joi.forbidden() | ||
if (get(joiSchema, '_flags.presence') === 'required') { | ||
swagger['x-required'] = true; | ||
} | ||
merge(newComponentsByRef, components || {}); | ||
const type = meta(schema, 'baseType') || schema._type; | ||
if (parseAsType[type]) { | ||
swagger = parseAsType[type](schema, existingComponents, components); | ||
} else { | ||
throw new TypeError(`${type} is not a recognized Joi type.`); | ||
swaggers.push(swagger); | ||
} | ||
swaggers = uniqWith(swaggers, isEqual); | ||
if (!swagger) return { swagger, components }; | ||
return swaggers.length > 0 ? { [mode]: swaggers } : {}; | ||
} | ||
if (schema._valids && schema._valids.has(null)) { | ||
swagger.nullable = true; | ||
function parseValidsAndInvalids (schema, filterFunc) { | ||
const swagger = {}; | ||
if (schema._valids) { | ||
const valids = schema._valids.values().filter(filterFunc); | ||
if (get(schema, '_flags.only') && valids.length) { | ||
swagger.enum = valids; | ||
} | ||
} | ||
if (schema._description) { | ||
swagger.description = schema._description; | ||
} | ||
if (schema._examples.length) { | ||
if (schema._examples.length === 1) { | ||
swagger.example = extractExampleValue(schema._examples[0]); | ||
} else { | ||
swagger.examples = schema._examples.map(extractExampleValue); | ||
if (schema._invalids) { | ||
const invalids = schema._invalids.values().filter(filterFunc); | ||
if (invalids.length) { | ||
swagger.not = { enum: invalids }; | ||
} | ||
} | ||
var label = get(schema, '_flags.label'); | ||
if (label) { | ||
swagger.title = label; | ||
} | ||
return swagger; | ||
} | ||
var defaultValue = get(schema, '_flags.default'); | ||
if (defaultValue && typeof defaultValue !== 'function') { | ||
swagger.default = defaultValue; | ||
} | ||
if (metaDefName) { | ||
set(components, [ metaDefType, metaDefName ], swagger); | ||
return { swagger: refDef(metaDefType, metaDefName), components }; | ||
} | ||
if (override) { | ||
Object.assign(swagger, override); | ||
} | ||
return { swagger, components }; | ||
}; | ||
var parseAsType = { | ||
const parseAsType = { | ||
number: (schema) => { | ||
var swagger = {}; | ||
const swagger = {}; | ||
if (find(schema._tests, { name: 'integer' })) { | ||
if (find(schema._rules, { name: 'integer' })) { | ||
swagger.type = 'integer'; | ||
} else { | ||
swagger.type = 'number'; | ||
if (find(schema._tests, { name: 'precision' })) { | ||
if (find(schema._rules, { name: 'precision' })) { | ||
swagger.format = 'double'; | ||
@@ -131,24 +123,22 @@ } else { | ||
if (find(schema._tests, { name: 'positive' })) { | ||
swagger.minimum = 1; | ||
const sign = find(schema._rules, { name: 'sign' }); | ||
if (sign) { | ||
if (sign.args.sign === 'positive') { | ||
swagger.minimum = 1; | ||
} else if (sign.args.sign === 'negative') { | ||
swagger.maximum = -1; | ||
} | ||
} | ||
if (find(schema._tests, { name: 'negative' })) { | ||
swagger.maximum = -1; | ||
} | ||
var min = find(schema._tests, { name: 'min' }); | ||
const min = find(schema._rules, { name: 'min' }); | ||
if (min) { | ||
swagger.minimum = min.arg; | ||
swagger.minimum = min.args.limit; | ||
} | ||
var max = find(schema._tests, { name: 'max' }); | ||
const max = find(schema._rules, { name: 'max' }); | ||
if (max) { | ||
swagger.maximum = max.arg; | ||
swagger.maximum = max.args.limit; | ||
} | ||
var valids = schema._valids.values().filter((s) => typeof s === 'number'); | ||
if (get(schema, '_flags.allowOnly') && valids.length) { | ||
swagger.enum = valids; | ||
} | ||
Object.assign(swagger, parseValidsAndInvalids(schema, (s) => isNumber(s))); | ||
@@ -158,26 +148,14 @@ return swagger; | ||
string: (schema) => { | ||
var swagger = { type: 'string' }; | ||
var strict = get(schema, '_settings.convert') === false; | ||
const swagger = { type: 'string' }; | ||
if (find(schema._tests, { name: 'alphanum' })) { | ||
if (strict && find(schema._tests, { name: 'lowercase' })) { | ||
swagger.pattern = patterns.alphanumLower; | ||
} else if (strict && find(schema._tests, { name: 'uppercase' })) { | ||
swagger.pattern = patterns.alphanumUpper; | ||
} else { | ||
swagger.pattern = patterns.alphanum; | ||
} | ||
if (find(schema._rules, { name: 'alphanum' })) { | ||
const strict = get(schema, '_preferences.convert') === false; | ||
swagger.pattern = patterns[`alphanum${strict ? getCaseSuffix(schema) : ''}`]; | ||
} | ||
if (find(schema._tests, { name: 'token' })) { | ||
if (find(schema._tests, { name: 'lowercase' })) { | ||
swagger.pattern = patterns.alphanumLower; | ||
} else if (find(schema._tests, { name: 'uppercase' })) { | ||
swagger.pattern = patterns.alphanumUpper; | ||
} else { | ||
swagger.pattern = patterns.alphanum; | ||
} | ||
if (find(schema._rules, { name: 'token' })) { | ||
swagger.pattern = patterns.token; | ||
} | ||
if (find(schema._tests, { name: 'email' })) { | ||
if (find(schema._rules, { name: 'email' })) { | ||
swagger.format = 'email'; | ||
@@ -187,3 +165,3 @@ if (swagger.pattern) delete swagger.pattern; | ||
if (find(schema._tests, { name: 'isoDate' })) { | ||
if (find(schema._rules, { name: 'isoDate' })) { | ||
swagger.format = 'date-time'; | ||
@@ -193,27 +171,14 @@ if (swagger.pattern) delete swagger.pattern; | ||
var pattern = find(schema._tests, { name: 'regex' }); | ||
if (pattern) { | ||
swagger.pattern = pattern.arg.pattern.toString().slice(1, -1); | ||
if (find(schema._rules, { name: 'guid' })) { | ||
swagger.format = 'uuid'; | ||
if (swagger.pattern) delete swagger.pattern; | ||
} | ||
for (let i = 0; i < schema._tests.length; i++) { | ||
const test = schema._tests[i]; | ||
if (test.name === 'min') { | ||
swagger.minLength = test.arg; | ||
} | ||
if (test.name === 'max') { | ||
swagger.maxLength = test.arg; | ||
} | ||
if (test.name === 'length') { | ||
swagger.minLength = test.arg; | ||
swagger.maxLength = test.arg; | ||
} | ||
const pattern = find(schema._rules, { name: 'pattern' }); | ||
if (pattern) { | ||
swagger.pattern = pattern.args.regex.toString().slice(1, -1); | ||
} | ||
var valids = schema._valids.values().filter((s) => typeof s === 'string'); | ||
if (get(schema, '_flags.allowOnly') && valids.length) { | ||
swagger.enum = valids; | ||
} | ||
Object.assign(swagger, getMinMax(schema)); | ||
Object.assign(swagger, parseValidsAndInvalids(schema, (s) => isString(s))); | ||
@@ -223,3 +188,3 @@ return swagger; | ||
binary: (schema) => { | ||
var swagger = { type: 'string', format: 'binary' }; | ||
const swagger = { type: 'string', format: 'binary' }; | ||
@@ -230,18 +195,4 @@ if (get(schema, '_flags.encoding') === 'base64') { | ||
for (let i = 0; i < schema._tests.length; i++) { | ||
const test = schema._tests[i]; | ||
if (test.name === 'min') { | ||
swagger.minLength = test.arg; | ||
} | ||
Object.assign(swagger, getMinMax(schema)); | ||
if (test.name === 'max') { | ||
swagger.maxLength = test.arg; | ||
} | ||
if (test.name === 'length') { | ||
swagger.minLength = test.arg; | ||
swagger.maxLength = test.arg; | ||
} | ||
} | ||
return swagger; | ||
@@ -252,97 +203,92 @@ }, | ||
alternatives: (schema, existingComponents, newComponentsByRef) => { | ||
var index = meta(schema, 'swaggerIndex') || 0; | ||
const matches = get(schema, '$_terms.matches'); | ||
const mode = `${get(schema, '_flags.match') || 'any'}Of`; | ||
var matches = get(schema, [ '_inner', 'matches' ]); | ||
var firstItem = get(matches, [ 0 ]); | ||
var itemsSchema; | ||
if (firstItem.ref) { | ||
if (schema._baseType && !firstItem.otherwise) { | ||
itemsSchema = index ? firstItem.then : schema._baseType; | ||
const alternatives = []; | ||
for (const m of matches) { | ||
if (m.ref) { | ||
if (m.then) alternatives.push(m.then); | ||
if (m.otherwise) alternatives.push(m.otherwise); | ||
if (m.switch) { | ||
for (const s of m.switch) { | ||
if (s.then) alternatives.push(s.then); | ||
if (s.otherwise) alternatives.push(s.otherwise); | ||
} | ||
} | ||
} else { | ||
itemsSchema = index ? firstItem.otherwise : firstItem.then; | ||
alternatives.push(m.schema); | ||
} | ||
} else if (index) { | ||
itemsSchema = get(matches, [ index, 'schema' ]); | ||
} else { | ||
itemsSchema = firstItem.schema; | ||
} | ||
var items = exports(itemsSchema, merge({}, existingComponents || {}, newComponentsByRef || {})); | ||
if (get(itemsSchema, '_flags.presence') === 'required') { | ||
items.swagger.__required = true; | ||
} | ||
merge(newComponentsByRef, items.components || {}); | ||
return items.swagger; | ||
return schemaForAlternatives(alternatives, existingComponents, newComponentsByRef, mode); | ||
}, | ||
array: (schema, existingComponents, newComponentsByRef) => { | ||
var index = meta(schema, 'swaggerIndex') || 0; | ||
var itemsSchema = get(schema, [ '_inner', 'items', index ]); | ||
const items = get(schema, '$_terms.items'); | ||
const mode = 'oneOf'; | ||
if (!itemsSchema) throw Error('Array schema does not define an items schema at index ' + index); | ||
const alternatives = items; | ||
var items = exports(itemsSchema, merge({}, existingComponents || {}, newComponentsByRef || {})); | ||
let swaggers = []; | ||
for (const joiSchema of alternatives) { | ||
// eslint-disable-next-line max-len | ||
const { swagger, components } = parse(joiSchema, merge({}, existingComponents || {}, newComponentsByRef || {})); | ||
if (!swagger) continue; // swagger is falsy if joi.forbidden() | ||
merge(newComponentsByRef, items.components || {}); | ||
merge(newComponentsByRef, components || {}); | ||
var swagger = { type: 'array' }; | ||
swaggers.push(swagger); | ||
} | ||
swaggers = uniqWith(swaggers, isEqual); | ||
for (let i = 0; i < schema._tests.length; i++) { | ||
const test = schema._tests[i]; | ||
if (test.name === 'min') { | ||
swagger.minItems = test.arg; | ||
} | ||
const openapi = { | ||
type: 'array', | ||
items: { [mode]: swaggers }, | ||
}; | ||
if (swaggers.length <= 1) { | ||
openapi.items = get(swaggers, [ 0 ]) || {}; | ||
} | ||
if (test.name === 'max') { | ||
swagger.maxItems = test.arg; | ||
} | ||
Object.assign(openapi, getMinMax(schema, 'Items')); | ||
if (test.name === 'length') { | ||
swagger.minItems = test.arg; | ||
swagger.maxItems = test.arg; | ||
} | ||
if (find(schema._rules, { name: 'unique' })) { | ||
openapi.uniqueItems = true; | ||
} | ||
if (find(schema._tests, { name: 'unique' })) { | ||
swagger.uniqueItems = true; | ||
} | ||
swagger.items = items.swagger; | ||
return swagger; | ||
return openapi; | ||
}, | ||
object: (schema, existingComponents, newComponentsByRef) => { | ||
var requireds = []; | ||
var properties = {}; | ||
const requireds = []; | ||
const properties = {}; | ||
var combinedComponents = merge({}, existingComponents || {}, newComponentsByRef || {}); | ||
const combinedComponents = merge({}, existingComponents || {}, newComponentsByRef || {}); | ||
var children = get(schema, '_inner.children') || []; | ||
const children = get(schema, '$_terms.keys') || []; | ||
children.forEach((child) => { | ||
var key = child.key; | ||
var prop = exports(child.schema, combinedComponents); | ||
if (!prop.swagger) { // swagger is falsy if joi.forbidden() | ||
const key = child.key; | ||
const { swagger, components } = parse(child.schema, combinedComponents); | ||
if (!swagger) { // swagger is falsy if joi.forbidden() | ||
return; | ||
} | ||
merge(newComponentsByRef, prop.components || {}); | ||
merge(combinedComponents, prop.components || {}); | ||
merge(newComponentsByRef, components || {}); | ||
merge(combinedComponents, components || {}); | ||
properties[key] = prop.swagger; | ||
properties[key] = swagger; | ||
if (get(child, 'schema._flags.presence') === 'required' || prop.swagger.__required) { | ||
if (get(child, 'schema._flags.presence') === 'required') { | ||
requireds.push(key); | ||
delete prop.swagger.__required; | ||
} | ||
}); | ||
var swagger = { type: 'object' }; | ||
const swagger = { | ||
type: 'object', | ||
properties, | ||
}; | ||
if (requireds.length) { | ||
swagger.required = requireds; | ||
} | ||
swagger.properties = properties; | ||
if (get(schema, '_flags.allowUnknown') === false) { | ||
if (get(schema, '_flags.unknown') !== true) { | ||
swagger.additionalProperties = false; | ||
@@ -354,11 +300,11 @@ } | ||
any: (schema) => { | ||
var swagger = {}; | ||
const swagger = {}; | ||
// convert property to file upload, if indicated by meta property | ||
if (getJoiMetaProperty(schema, 'swaggerType') === 'file') { | ||
if (meta(schema, 'swaggerType') === 'file') { | ||
swagger.type = 'file'; | ||
swagger.in = 'formData'; | ||
} | ||
if (schema._description) { | ||
swagger.description = schema._description; | ||
} | ||
Object.assign(swagger, parseValidsAndInvalids(schema, (s) => isString(s) || isNumber(s))); | ||
return swagger; | ||
@@ -368,19 +314,92 @@ }, | ||
function meta (schema, key) { | ||
var flattened = Object.assign.apply(null, [ {} ].concat(schema._meta)); | ||
function parse (schema, existingComponents) { | ||
// inspect(schema); | ||
return get(flattened, key); | ||
} | ||
if (!schema) throw new Error('No schema was passed.'); | ||
function refDef (type, name) { | ||
return { $ref: '#/components/' + type + '/' + name }; | ||
} | ||
if (isPlainObject(schema)) { | ||
schema = joi.object().keys(schema); | ||
} | ||
function extractExampleValue (example) { | ||
return joi.version < '14' ? example : example.value; | ||
if (!joi.isSchema(schema)) throw new TypeError('Passed schema does not appear to be a joi schema.'); | ||
const flattenMeta = Object.assign.apply(null, [ {} ].concat(schema.$_terms.metas)); | ||
const override = flattenMeta.swagger; | ||
if (override && flattenMeta.swaggerOverride) { | ||
return { swagger: override, components: {} }; | ||
} | ||
const metaDefName = flattenMeta.className; | ||
const metaDefType = flattenMeta.classTarget || 'schemas'; | ||
// if the schema has a definition class name, and that | ||
// definition is already defined, just use that definition | ||
if (metaDefName && get(existingComponents, [ metaDefType, metaDefName ])) { | ||
return { swagger: refDef(metaDefType, metaDefName) }; | ||
} | ||
if (get(schema, '_flags.presence') === 'forbidden') { | ||
return false; | ||
} | ||
const type = meta(schema, 'baseType') || schema.type; | ||
if (!parseAsType[type]) { | ||
throw new TypeError(`${type} is not a recognized Joi type.`); | ||
} | ||
const components = {}; | ||
const swagger = parseAsType[type](schema, existingComponents, components); | ||
if (get(schema, '$_terms.whens')) { | ||
Object.assign(swagger, parseWhens(schema, existingComponents, components)); | ||
} | ||
if (!swagger) return { swagger, components }; | ||
if (schema._valids && schema._valids.has(null)) { | ||
swagger.nullable = true; | ||
} | ||
const description = get(schema, '_flags.description'); | ||
if (description) { | ||
swagger.description = description; | ||
} | ||
if (schema.$_terms.examples) { | ||
if (schema.$_terms.examples.length === 1) { | ||
swagger.example = schema.$_terms.examples[0]; | ||
} else { | ||
swagger.examples = schema.$_terms.examples; | ||
} | ||
} | ||
const label = get(schema, '_flags.label'); | ||
if (label) { | ||
swagger.title = label; | ||
} | ||
const defaultValue = get(schema, '_flags.default'); | ||
if (defaultValue && typeof defaultValue !== 'function') { | ||
swagger.default = defaultValue; | ||
} | ||
if (metaDefName) { | ||
set(components, [ metaDefType, metaDefName ], swagger); | ||
return { swagger: refDef(metaDefType, metaDefName), components }; | ||
} | ||
if (override) { | ||
Object.assign(swagger, override); | ||
} | ||
return { swagger, components }; | ||
} | ||
// var inspectU = require('util').inspect; | ||
module.exports = exports = parse; | ||
exports.default = parse; | ||
// const inspectU = require('util').inspect; | ||
// function inspect (value) { | ||
// console.error(inspectU(value, { colors: true, depth: 10 })); | ||
// console.error(inspectU(value, { colors: true, depth: 10 })); | ||
// } |
{ | ||
"name": "joi-to-swagger", | ||
"version": "3.3.0", | ||
"version": "4.0.0", | ||
"description": "", | ||
@@ -9,2 +9,3 @@ "main": "index.js", | ||
"test": "tap tests.js", | ||
"test:debug": "tap --node-arg=--inspect --no-timeout tests.js", | ||
"test:travis": "tap --reporter=tap tests.js", | ||
@@ -35,3 +36,3 @@ "lint": "eslint ./" | ||
"eslint-plugin-promise": "^4.2.1", | ||
"joi": "^14.3.1", | ||
"@hapi/joi": "^17.1.0", | ||
"tap": "^14.7.2", | ||
@@ -41,3 +42,3 @@ "tapsuite": "^2.0.1" | ||
"peerDependencies": { | ||
"joi": ">= 13" | ||
"@hapi/joi": ">=16" | ||
}, | ||
@@ -44,0 +45,0 @@ "files": [ |
@@ -5,2 +5,3 @@ joi-to-swagger | ||
[![npm](https://img.shields.io/npm/v/joi-to-swagger.svg?logo=npm)](https://www.npmjs.com/package/joi-to-swagger) | ||
[![travis](https://img.shields.io/travis/Twipped/joi-to-swagger/master.svg?label=tests&logo=travis-ci)](https://travis-ci.org/Twipped/joi-to-swagger) | ||
[![Dependency Status](https://img.shields.io/david/Twipped/joi-to-swagger.svg?style=flat-square)](https://david-dm.org/Twipped/joi-to-swagger) | ||
@@ -47,3 +48,4 @@ [![Download Status](https://img.shields.io/npm/dm/joi-to-swagger.svg?style=flat-square)](https://www.npmjs.com/package/joi-to-swagger) | ||
} | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
@@ -55,7 +57,14 @@ ``` | ||
```js | ||
var j2s = require('joi-to-swagger'); | ||
const j2s = require('joi-to-swagger'); | ||
var {swagger, components} = j2s(mySchema, existingComponents); | ||
const { swagger, components } = j2s(mySchema, existingComponents); | ||
``` | ||
_- in case of ES6 module syntax:_ | ||
```js | ||
import j2s from 'joi-to-swagger'; | ||
const { swagger, components } = j2s(mySchema, existingComponents); | ||
``` | ||
J2S takes two arguments, the first being the Joi object you wish to convert. The second optional argument is a collection of existing components to reference against for the meta `className` identifiers (see below). | ||
@@ -71,3 +80,3 @@ | ||
- `joi.array().items()` defines the structure using the first schema provided on `.items()` (see below for how to override) | ||
- `joi.array().items()` - in case of multiple provided schemas using `items()` method, the "oneOf" (OAS3) keyword is used | ||
- `.min(4)` -> `"minItems": 4` | ||
@@ -86,2 +95,4 @@ - `.max(10)` -> `"maxItems": 10` | ||
- `.negative()` -> `"maximum": -1` | ||
- `.valid(1, 2)` -> `"enum": [1, 2]` | ||
- `.invalid(1, 2)` -> `"not": { "enum": [1, 2] }` | ||
@@ -102,2 +113,5 @@ - `joi.string()` produces `"type": "string"` with no formatting | ||
- `.max(10)` -> `"maxLength": 10` | ||
- `.uuid()` -> `"format": "uuid"` | ||
- `.valid('A', 'B')` -> `"enum": ['A', 'B']` | ||
- `.invalid('A', 'B')` -> `"not": { "enum": ['A', 'B'] }` | ||
@@ -113,4 +127,10 @@ - `joi.binary()` produces `"type": "string"` with a format of `"binary"`. | ||
- `joi.alternatives()` defines the structure using the first schema provided on `.items()` (see below for how to override) | ||
- `joi.alternatives()` - structure of alternative schemas is defined by "anyOf", "oneOf" or "allOf (OAS3) keywords | ||
- `.mode('one')` -> produces `"oneOf": [ { ... } ]` | ||
- in case of `joi.required()` alternative schema, the custom property option "x-required" is added to subschema -> `"x-required": true` | ||
- `joi.when()` conditions are transformed to `"oneOf": [ { ... }, { ... } ]` keyword | ||
- if multiple `joi.when().when()` conditions are provided, they are transformed to `"anyOf": [ { ... }, { ... } ]` keyword | ||
- in case of `joi.required()` condition, the custom property option "x-required" is added to subschema -> `"x-required": true` | ||
- `any.default()` sets the `"default"` detail. | ||
@@ -120,6 +140,8 @@ | ||
- `.example('hi')` -> `"example": "hi"` | ||
- joi < v14: `.example('hi').example('hey')` -> `"examples": ["hi", "hey"]` | ||
- joi v14: `.example('hi', 'hey')` -> `"examples": ["hi", "hey"]` | ||
- `.example('hi', 'hey')` -> `"examples": ["hi", "hey"]` | ||
- `joi.any().meta({ swaggerType: 'file' }).description('simpleFile')` add a file to the swagger structure | ||
- `joi.any()` | ||
- `.meta({ swaggerType: 'file' }).description('simpleFile')` add a file to the swagger structure | ||
- `.valid(1, 'A')` -> `"enum": [1, 'A']` | ||
- `.invalid(1, 'A')` -> `"not": { "enum": [1, 'A'] }` | ||
@@ -134,4 +156,2 @@ ## Meta Overrides | ||
**swaggerIndex**: Swagger's deterministic design disallows for supporting multiple type components. Because of this, only a single schema from `.alternatives()` and `.array().items()` may be converted to swagger. By default J2S will use the first component. Defining a different zero based index for this meta tag will override that behavior. | ||
**swagger**: To explicitly define your own swagger component for a joi schema object, place that swagger object in the `swagger` meta tag. It will be mixed in to the schema that J2S produces. | ||
@@ -149,3 +169,3 @@ | ||
const customJoi = joi.extend({ | ||
name: 'customStringType', | ||
type: 'customStringType', | ||
base: joi.string().meta({ baseType: 'string' }), | ||
@@ -152,0 +172,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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
22471
327
165
1