mirror of https://gitee.com/bigwinds/arangodb
Add joi-to-json-schema dep
This commit is contained in:
parent
a00ac0d96f
commit
774a47a185
|
@ -163,6 +163,11 @@
|
|||
* GITHUB: https://github.com/hapijs/joi
|
||||
* License: [BSD-style 3-Clause License](https://github.com/hapijs/joi/blob/master/LICENSE)
|
||||
|
||||
### joi-to-json-schema
|
||||
|
||||
* GITHUB: https://github.com/lightsofapollo/joi-to-json-schema
|
||||
* License: [Apache 2 License](https://github.com/lightsofapollo/joi-to-json-schema/blob/master/README.md#license)
|
||||
|
||||
#### JS-YAML
|
||||
|
||||
* GITHUB: https://github.com/nodeca/js-yaml
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
- "iojs"
|
||||
- "iojs-v1.0.4"
|
|
@ -0,0 +1,2 @@
|
|||
## [**2.0.0**](https://github.com/lightsofapollo/joi-to-json-schema/commit/620f7f15d6cc28e1e647b2ec9a8355be69af423c)
|
||||
- [**#2**](https://github.com/lightsofapollo/joi-to-json-schema/issues/2) Added support for joi.forbidden() in object properties
|
|
@ -0,0 +1,119 @@
|
|||
# joi-to-json-schema
|
||||
|
||||
The goal is to provide best effort conversion from Joi objects to JSON
|
||||
Schema (draft-04) with the understanding that only some of Joi's schematics
|
||||
can be converted directly. Primarily this module exists to convert Joi schema
|
||||
objects for existing tools which happen to currently consume JSON Schema.
|
||||
|
||||
[](http://badge.fury.io/js/joi-to-json-schema)
|
||||
[](https://travis-ci.org/lightsofapollo/joi-to-json-schema)
|
||||
[](https://david-dm.org/lightsofapollo/joi-to-json-schema)
|
||||
[](https://david-dm.org/lightsofapollo/joi-to-json-schema#info=devDependencies)
|
||||
|
||||
[](https://nodei.co/npm/joi-to-json-schema/)
|
||||
[](https://nodei.co/npm-dl/joi-to-json-schema/)
|
||||
|
||||
|
||||
## Installation
|
||||
> npm install joi-to-json
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
var joi = require('joi'),
|
||||
convert = require('joi-to-json-schema'),
|
||||
joiSchema=joi.object({
|
||||
'name':joi.string().required().regex(/^\w+$/),
|
||||
'description':joi.string().optional().default('no description provided'),
|
||||
'a': joi.boolean().required().default(false),
|
||||
'b': joi.alternatives().when('a', {
|
||||
is: true,
|
||||
then: joi.string().default('a is true'),
|
||||
otherwise: joi.number().default(0)
|
||||
})
|
||||
});
|
||||
|
||||
convert(joiSchema);
|
||||
```
|
||||
|
||||
which will produce:
|
||||
|
||||
```js
|
||||
{ type: 'object',
|
||||
properties:
|
||||
{ name: { type: 'string', pattern: '^\\w+$' },
|
||||
description: { default: 'no description provided', type: 'string' },
|
||||
a: { type: 'boolean' },
|
||||
b: { oneOf: [ { default: 'a is true', type: 'string' }, { type: 'number' } ] } },
|
||||
additionalProperties: false,
|
||||
required: [ 'name', 'a' ] }
|
||||
```
|
||||
|
||||
## JSDOC
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Converts the supplied joi validation object into a JSON schema object,
|
||||
* optionally applying a transformation.
|
||||
*
|
||||
* @param {JoiValidation} joi
|
||||
* @param {TransformFunction} [transformer=null]
|
||||
* @returns {JSONSchema}
|
||||
*/
|
||||
export default function convert(joi,transformer=null) {
|
||||
// ...
|
||||
};
|
||||
|
||||
/**
|
||||
* Joi Validation Object
|
||||
* @typedef {object} JoiValidation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transformation Function - applied just before `convert()` returns and called as `function(object):object`
|
||||
* @typedef {function} TransformFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSON Schema Object
|
||||
* @typedef {object} JSONSchema
|
||||
*/
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
Joi's conditional form, i.e. `.when('name',{is:cond,then:joi,otherwise:joi})`, is evaluated at runtime
|
||||
and since, from the perspective of the schema, there is no way of knowing what the condition might resolve to, this
|
||||
module takes the position that it should provide _all possible resolutions_ in a JSON Schema `oneOf:[]` clause.
|
||||
|
||||
Also, while it does not affect consumers, it should be noted that `joi-to-json-schema` is written in ES6 and uses
|
||||
[6-to-5](https://www.npmjs.com/package/6to5) to convert to ES5 before the module is published to `npm`.
|
||||
|
||||
## Testing
|
||||
|
||||
All tests cases are first checked against expected results and then validated using
|
||||
Kris Zyp's excellent [json-schema](https://github.com/kriszyp/json-schema)
|
||||
|
||||
## References
|
||||
|
||||
- [JSON Schema - Draft 4 from json-schema.org](http://json-schema.org/documentation.html)
|
||||
- [IETF Draft: JSON Schema: core definitions and terminology - draft-zyp-json-schema-04](https://tools.ietf.org/html/draft-zyp-json-schema-04)
|
||||
- [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/UnderstandingJSONSchema.pdf)(pdf)
|
||||
|
||||
## LICENSE
|
||||
|
||||
Copyright 2014, Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,237 @@
|
|||
"use strict";
|
||||
|
||||
var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };
|
||||
|
||||
/**
|
||||
* Converts the supplied joi validation object into a JSON schema object,
|
||||
* optionally applying a transformation.
|
||||
*
|
||||
* @param {JoiValidation} joi
|
||||
* @param {TransformFunction} [transformer=null]
|
||||
* @returns {JSONSchema}
|
||||
*/
|
||||
module.exports = convert;
|
||||
var assert = _interopRequire(require("assert"));
|
||||
|
||||
// Converter helpers for Joi types.
|
||||
|
||||
var TYPES = {
|
||||
|
||||
alternatives: function (schema, joi) {
|
||||
var result = schema.oneOf = [];
|
||||
|
||||
joi._inner.matches.forEach(function (match) {
|
||||
if (match.schema) {
|
||||
return result.push(convert(match.schema));
|
||||
}
|
||||
|
||||
if (!match.is) {
|
||||
throw new Error("joi.when requires an \"is\"");
|
||||
}
|
||||
if (!(match.then || match.otherwise)) {
|
||||
throw new Error("joi.when requires one or both of \"then\" and \"otherwise\"");
|
||||
}
|
||||
|
||||
if (match.then) {
|
||||
result.push(convert(match.then));
|
||||
}
|
||||
|
||||
if (match.otherwise) {
|
||||
result.push(convert(match.otherwise));
|
||||
}
|
||||
});
|
||||
return schema;
|
||||
},
|
||||
|
||||
date: function (schema) {
|
||||
schema.type = "string";
|
||||
schema.format = "date-time";
|
||||
return schema;
|
||||
},
|
||||
|
||||
any: function (schema) {
|
||||
schema.type = ["array", "boolean", "number", "object", "string", "null"];
|
||||
return schema;
|
||||
},
|
||||
|
||||
array: function (schema, joi) {
|
||||
schema.type = "array";
|
||||
|
||||
joi._tests.forEach(function (test) {
|
||||
switch (test.name) {
|
||||
case "unique":
|
||||
schema.uniqueItems = true;
|
||||
break;
|
||||
case "length":
|
||||
schema.minItems = schema.maxItems = test.arg;
|
||||
break;
|
||||
case "min":
|
||||
schema.minItems = test.arg;
|
||||
break;
|
||||
case "max":
|
||||
schema.maxItems = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (joi._inner) {
|
||||
var list = undefined;
|
||||
if (joi._inner.inclusions.length) {
|
||||
list = joi._inner.inclusions;
|
||||
} else if (joi._inner.requireds.length) {
|
||||
list = joi._inner.requireds;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
schema.items = schema.items || [];
|
||||
list.forEach(function (i) {
|
||||
schema.items.push(convert(i));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
boolean: function (schema) {
|
||||
schema.type = "boolean";
|
||||
return schema;
|
||||
},
|
||||
|
||||
number: function (schema, joi) {
|
||||
schema.type = "number";
|
||||
joi._tests.forEach(function (test) {
|
||||
switch (test.name) {
|
||||
case "integer":
|
||||
schema.type = "integer";
|
||||
break;
|
||||
case "less":
|
||||
schema.exclusiveMaximum = true;
|
||||
schema.maximum = test.arg;
|
||||
break;
|
||||
case "greater":
|
||||
schema.exclusiveMinimum = true;
|
||||
schema.minimum = test.arg;
|
||||
break;
|
||||
case "min":
|
||||
schema.minimum = test.arg;
|
||||
break;
|
||||
case "max":
|
||||
schema.maximum = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
return schema;
|
||||
},
|
||||
|
||||
string: function (schema, joi) {
|
||||
schema.type = "string";
|
||||
|
||||
joi._tests.forEach(function (test) {
|
||||
switch (test.name) {
|
||||
case "email":
|
||||
schema.format = "email";
|
||||
break;
|
||||
case "regex":
|
||||
schema.pattern = String(test.arg).replace(/^\//, "").replace(/\/$/, "");
|
||||
break;
|
||||
case "min":
|
||||
schema.minLength = test.arg;
|
||||
break;
|
||||
case "max":
|
||||
schema.maxLength = test.arg;
|
||||
break;
|
||||
case "length":
|
||||
schema.minLength = schema.maxLength = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
object: function (schema, joi) {
|
||||
schema.type = "object";
|
||||
schema.properties = {};
|
||||
schema.additionalProperties = joi._flags.allowUnknown || false;
|
||||
|
||||
if (!joi._inner.children) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
joi._inner.children.forEach(function (property) {
|
||||
if (property.schema._flags.presence !== "forbidden") {
|
||||
schema.properties[property.key] = convert(property.schema);
|
||||
if (property.schema._flags.presence === "required") {
|
||||
schema.required = schema.required || [];
|
||||
schema.required.push(property.key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
};function convert(joi) {
|
||||
var transformer = arguments[1] === undefined ? null : arguments[1];
|
||||
|
||||
|
||||
assert("object" === typeof joi && true === joi.isJoi, "requires a joi schema object");
|
||||
|
||||
assert(joi._type, "joi schema object must have a type");
|
||||
|
||||
if (!TYPES[joi._type]) {
|
||||
throw new Error("sorry, do not know how to convert unknown joi type: \"" + joi._type + "\"");
|
||||
}
|
||||
|
||||
if (transformer) {
|
||||
assert("function" === typeof transformer, "transformer must be a function");
|
||||
}
|
||||
|
||||
// JSON Schema root for this type.
|
||||
var schema = {};
|
||||
|
||||
// Copy over the details that all schemas may have...
|
||||
if (joi._description) {
|
||||
schema.description = joi._description;
|
||||
}
|
||||
|
||||
if (joi._flags && joi._flags["default"]) {
|
||||
schema["default"] = joi._flags["default"];
|
||||
}
|
||||
|
||||
if (joi._valids && joi._valids._set && joi._valids._set.length) {
|
||||
if (Array.isArray(joi._inner.children)) {
|
||||
return {
|
||||
"------oneOf": [{
|
||||
type: joi._type,
|
||||
"enum": joi._valids._set
|
||||
}, TYPES[joi._type](schema, joi)]
|
||||
};
|
||||
}
|
||||
schema["enum"] = joi._valids._set;
|
||||
}
|
||||
|
||||
var result = TYPES[joi._type](schema, joi);
|
||||
|
||||
if (transformer) {
|
||||
result = transformer(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joi Validation Object
|
||||
* @typedef {object} JoiValidation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transformation Function - applied just before `convert()` returns and called as `function(object):object`
|
||||
* @typedef {function} TransformFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSON Schema Object
|
||||
* @typedef {object} JSONSchema
|
||||
*/
|
||||
//# sourceMappingURL=index.js.map
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"name": "joi-to-json-schema",
|
||||
"version": "2.0.5",
|
||||
"description": "Joi -> JSON Schema Converter",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"build": "6to5 -s -e src --out-dir build",
|
||||
"pretest": "npm run build",
|
||||
"test": "mocha",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lightsofapollo/joi-to-json-schema.git"
|
||||
},
|
||||
"keywords": [
|
||||
"joi",
|
||||
"json-schema",
|
||||
"json",
|
||||
"schema"
|
||||
],
|
||||
"author": {
|
||||
"name": "James Lal [:lightsofapollo]"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "lights-of-apollo",
|
||||
"email": "james@lightsofapollo.com"
|
||||
},
|
||||
{
|
||||
"name": "raisch",
|
||||
"email": "raisch@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "Apache2",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lightsofapollo/joi-to-json-schema/issues"
|
||||
},
|
||||
"homepage": "https://github.com/lightsofapollo/joi-to-json-schema",
|
||||
"devDependencies": {
|
||||
"6to5": "^3.6.5",
|
||||
"joi": "^6.6.0",
|
||||
"json-schema": "^0.2.2",
|
||||
"lodash": "^3.9.3",
|
||||
"mocha": "^2.2.5"
|
||||
},
|
||||
"gitHead": "d1cab007ada3ded19a7d4080a93dac8064d4d2d8",
|
||||
"_id": "joi-to-json-schema@2.0.5",
|
||||
"_shasum": "9a1203252092d8007792883c07bd652a24b53ced",
|
||||
"_from": "joi-to-json-schema@*",
|
||||
"_npmVersion": "2.14.7",
|
||||
"_nodeVersion": "4.2.0",
|
||||
"_npmUser": {
|
||||
"name": "lights-of-apollo",
|
||||
"email": "james@lightsofapollo.com"
|
||||
},
|
||||
"dist": {
|
||||
"shasum": "9a1203252092d8007792883c07bd652a24b53ced",
|
||||
"tarball": "http://registry.npmjs.org/joi-to-json-schema/-/joi-to-json-schema-2.0.5.tgz"
|
||||
},
|
||||
"directories": {},
|
||||
"_resolved": "https://registry.npmjs.org/joi-to-json-schema/-/joi-to-json-schema-2.0.5.tgz"
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
import assert from 'assert';
|
||||
|
||||
// Converter helpers for Joi types.
|
||||
|
||||
let TYPES = {
|
||||
|
||||
alternatives: (schema, joi) => {
|
||||
|
||||
var result = schema.oneOf = [];
|
||||
|
||||
joi._inner.matches.forEach(function (match) {
|
||||
|
||||
if (match.schema) {
|
||||
return result.push(convert(match.schema));
|
||||
}
|
||||
|
||||
if (!match.is) {
|
||||
throw new Error('joi.when requires an "is"');
|
||||
}
|
||||
if (!(match.then || match.otherwise)) {
|
||||
throw new Error('joi.when requires one or both of "then" and "otherwise"');
|
||||
}
|
||||
|
||||
if (match.then) {
|
||||
result.push(convert(match.then));
|
||||
}
|
||||
|
||||
if (match.otherwise) {
|
||||
result.push(convert(match.otherwise));
|
||||
}
|
||||
|
||||
});
|
||||
return schema;
|
||||
},
|
||||
|
||||
date: (schema) => {
|
||||
schema.type = 'string';
|
||||
schema.format = 'date-time';
|
||||
return schema;
|
||||
},
|
||||
|
||||
any: (schema) => {
|
||||
schema.type = [
|
||||
"array",
|
||||
"boolean",
|
||||
'number',
|
||||
"object",
|
||||
'string',
|
||||
"null"
|
||||
];
|
||||
return schema;
|
||||
},
|
||||
|
||||
array: (schema, joi) => {
|
||||
schema.type = 'array';
|
||||
|
||||
joi._tests.forEach((test) => {
|
||||
switch (test.name) {
|
||||
case 'unique':
|
||||
schema.uniqueItems = true;
|
||||
break;
|
||||
case 'length':
|
||||
schema.minItems = schema.maxItems = test.arg;
|
||||
break;
|
||||
case 'min':
|
||||
schema.minItems = test.arg;
|
||||
break;
|
||||
case 'max':
|
||||
schema.maxItems = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (joi._inner) {
|
||||
let list;
|
||||
if (joi._inner.inclusions.length) {
|
||||
list = joi._inner.inclusions;
|
||||
} else if (joi._inner.requireds.length) {
|
||||
list = joi._inner.requireds;
|
||||
}
|
||||
|
||||
if (list) {
|
||||
schema.items = schema.items || [];
|
||||
list.forEach((i) => {
|
||||
schema.items.push(convert(i));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
boolean: (schema) => {
|
||||
schema.type = 'boolean';
|
||||
return schema;
|
||||
},
|
||||
|
||||
number: (schema, joi) => {
|
||||
schema.type = 'number';
|
||||
joi._tests.forEach((test) => {
|
||||
switch (test.name) {
|
||||
case 'integer':
|
||||
schema.type = 'integer';
|
||||
break;
|
||||
case 'less':
|
||||
schema.exclusiveMaximum = true;
|
||||
schema.maximum = test.arg;
|
||||
break;
|
||||
case 'greater':
|
||||
schema.exclusiveMinimum = true;
|
||||
schema.minimum = test.arg;
|
||||
break;
|
||||
case 'min':
|
||||
schema.minimum = test.arg;
|
||||
break;
|
||||
case 'max':
|
||||
schema.maximum = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
return schema;
|
||||
},
|
||||
|
||||
string: (schema, joi) => {
|
||||
schema.type = 'string';
|
||||
|
||||
joi._tests.forEach((test) => {
|
||||
switch (test.name) {
|
||||
case 'email':
|
||||
schema.format = 'email';
|
||||
break;
|
||||
case 'regex':
|
||||
schema.pattern = String(test.arg).replace(/^\//,'').replace(/\/$/,'');
|
||||
break;
|
||||
case 'min':
|
||||
schema.minLength = test.arg;
|
||||
break;
|
||||
case 'max':
|
||||
schema.maxLength = test.arg;
|
||||
break;
|
||||
case 'length':
|
||||
schema.minLength = schema.maxLength = test.arg;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
},
|
||||
|
||||
object: (schema, joi) => {
|
||||
schema.type = 'object';
|
||||
schema.properties = {};
|
||||
schema.additionalProperties = joi._flags.allowUnknown || false;
|
||||
|
||||
if (!joi._inner.children) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
joi._inner.children.forEach((property) => {
|
||||
if(property.schema._flags.presence !== 'forbidden') {
|
||||
schema.properties[property.key] = convert(property.schema);
|
||||
if (property.schema._flags.presence === 'required') {
|
||||
schema.required = schema.required || [];
|
||||
schema.required.push(property.key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the supplied joi validation object into a JSON schema object,
|
||||
* optionally applying a transformation.
|
||||
*
|
||||
* @param {JoiValidation} joi
|
||||
* @param {TransformFunction} [transformer=null]
|
||||
* @returns {JSONSchema}
|
||||
*/
|
||||
export default function convert(joi,transformer=null) {
|
||||
|
||||
assert('object'===typeof joi && true === joi.isJoi, 'requires a joi schema object');
|
||||
|
||||
assert(joi._type, 'joi schema object must have a type');
|
||||
|
||||
if(!TYPES[joi._type]){
|
||||
throw new Error(`sorry, do not know how to convert unknown joi type: "${joi._type}"`);
|
||||
}
|
||||
|
||||
if(transformer){
|
||||
assert('function'===typeof transformer, 'transformer must be a function');
|
||||
}
|
||||
|
||||
// JSON Schema root for this type.
|
||||
let schema = {};
|
||||
|
||||
// Copy over the details that all schemas may have...
|
||||
if (joi._description) {
|
||||
schema.description = joi._description;
|
||||
}
|
||||
|
||||
if (joi._flags && joi._flags.default) {
|
||||
schema['default'] = joi._flags.default;
|
||||
}
|
||||
|
||||
if (joi._valids && joi._valids._set && joi._valids._set.length){
|
||||
if(Array.isArray(joi._inner.children)) {
|
||||
return {
|
||||
'------oneOf': [
|
||||
{
|
||||
'type': joi._type,
|
||||
'enum': joi._valids._set
|
||||
},
|
||||
TYPES[joi._type](schema, joi)
|
||||
]
|
||||
};
|
||||
}
|
||||
schema['enum']=joi._valids._set;
|
||||
}
|
||||
|
||||
let result = TYPES[joi._type](schema, joi);
|
||||
|
||||
if(transformer){
|
||||
result = transformer(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joi Validation Object
|
||||
* @typedef {object} JoiValidation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transformation Function - applied just before `convert()` returns and called as `function(object):object`
|
||||
* @typedef {function} TransformFunction
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSON Schema Object
|
||||
* @typedef {object} JSONSchema
|
||||
*/
|
|
@ -0,0 +1,414 @@
|
|||
//@formatter:off
|
||||
var Joi = require('joi'),
|
||||
convert = require('../src/index'),
|
||||
assert = require('assert'),
|
||||
jsonSchema = require('json-schema');
|
||||
//@formatter:on
|
||||
|
||||
/* jshint mocha:true */
|
||||
/* global suite, test */
|
||||
|
||||
/**
|
||||
* Throws if schema !== expected or if schema fails to jsonSchema.validate()
|
||||
* @param {object} schema
|
||||
* @param {object} expected
|
||||
*/
|
||||
assert.validate = function (schema, expected) {
|
||||
var result = jsonSchema.validate(schema);
|
||||
assert.deepEqual(schema, expected);
|
||||
if ('object' === typeof result && Array.isArray(result.errors) && 0 === result.errors.length) {
|
||||
return;
|
||||
}
|
||||
throw new Error('json-schema validation failed: %s', result.errors.join(','));
|
||||
};
|
||||
|
||||
suite('convert', function () {
|
||||
|
||||
test('object defaults', function () {
|
||||
var joi = Joi.object(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: false
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('object description', function () {
|
||||
var joi = Joi.object().description('woot'),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
description: 'woot'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('object allow unknown', function () {
|
||||
var joi = Joi.object().unknown(true),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: true
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('object', function () {
|
||||
let joi = Joi.object().keys({
|
||||
string: Joi.string(),
|
||||
'string default': Joi.string().default('bar').description('bar desc'),
|
||||
'number': Joi.number(),
|
||||
'boolean': Joi.boolean()
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'string': {
|
||||
type: 'string'
|
||||
},
|
||||
'string default': {
|
||||
type: 'string',
|
||||
'default': 'bar',
|
||||
description: 'bar desc'
|
||||
},
|
||||
'number': {
|
||||
type: 'number'
|
||||
},
|
||||
'boolean': {
|
||||
type: 'boolean'
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('object property required', function () {
|
||||
let joi = Joi.object().keys({
|
||||
string: Joi.string(),
|
||||
'string default': Joi.string().default('bar').description('bar desc'),
|
||||
'number': Joi.number(),
|
||||
'boolean required': Joi.boolean().required()
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
required: ['boolean required'],
|
||||
properties: {
|
||||
'string': {
|
||||
type: 'string'
|
||||
},
|
||||
'string default': {
|
||||
type: 'string',
|
||||
'default': 'bar',
|
||||
description: 'bar desc'
|
||||
},
|
||||
'number': {
|
||||
type: 'number'
|
||||
},
|
||||
'boolean required': {
|
||||
type: 'boolean'
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('object property forbidden', function(){
|
||||
let joi = Joi.object().keys({
|
||||
string: Joi.string(),
|
||||
'string default': Joi.string().default('bar').description('bar desc'),
|
||||
'number forbidden': Joi.number().forbidden(),
|
||||
'boolean required': Joi.boolean().required()
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
required: ['boolean required'],
|
||||
properties: {
|
||||
'string': {
|
||||
type: 'string'
|
||||
},
|
||||
'string default': {
|
||||
type: 'string',
|
||||
'default': 'bar',
|
||||
description: 'bar desc'
|
||||
},
|
||||
'boolean required': {
|
||||
type: 'boolean'
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('type: array', function () {
|
||||
var joi = Joi.array(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'array'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('enum', function () {
|
||||
var joi = Joi.string().valid(['a', 'b']),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
'type': 'string',
|
||||
'enum': ['a', 'b']
|
||||
};
|
||||
//console.log('.enum: %s', util.inspect({type: joi._type, schema: schema}, {depth: 10}));
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('alternatives -> oneOf', function () {
|
||||
|
||||
let joi = Joi.object().keys({
|
||||
value: Joi.alternatives().try(
|
||||
Joi.string().valid('a'),
|
||||
Joi.number().valid(100)
|
||||
)
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
value: {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string',
|
||||
'enum': ['a']
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
'enum': [100]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//console.log('alt -> oneOf: %s', util.inspect({type: joi._type, schema: schema}, {depth: 10}));
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('string min/max', function () {
|
||||
var joi = Joi.string().min(5).max(100),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
minLength: 5,
|
||||
maxLength: 100
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('string -> maxLength', function () {
|
||||
var joi = Joi.string().length(5),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
maxLength: 5,
|
||||
minLength: 5
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('string email', function () {
|
||||
var joi = Joi.string().email(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
format: 'email'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('date', function () {
|
||||
var joi = Joi.date(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('string regex -> pattern', function () {
|
||||
let joi = Joi.string().regex(/^[a-z]$/),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
pattern: '^[a-z]$'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('string allow', function () {
|
||||
let joi = Joi.string().allow(['a', 'b', '', null]),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'string',
|
||||
'enum': ['a', 'b', '', null]
|
||||
};
|
||||
//console.log('string allow: %s', util.inspect({type: joi._type, joi:joi, schema: schema}, {depth: 10}));
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('number min/max', function () {
|
||||
let joi = Joi.number().min(0).max(100),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 100
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('number greater/less', function () {
|
||||
let joi = Joi.number().greater(0).less(100),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
exclusiveMinimum: true,
|
||||
maximum: 100,
|
||||
exclusiveMaximum: true
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('integer', function () {
|
||||
var joi = Joi.number().integer(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'integer'
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('array min/max', function () {
|
||||
let joi = Joi.array().min(5).max(100),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'array',
|
||||
minItems: 5,
|
||||
maxItems: 100
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('array length', function () {
|
||||
let joi = Joi.array().length(100),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'array',
|
||||
minItems: 100,
|
||||
maxItems: 100
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('array unique', function () {
|
||||
let joi = Joi.array().unique(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'array',
|
||||
uniqueItems: true
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('array inclusions', function () {
|
||||
let joi = Joi.array().items(Joi.string()),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'array',
|
||||
items: [{type: 'string'}]
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('joi any', function () {
|
||||
let joi = Joi.any(),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: ['array', 'boolean', 'number', 'object', 'string', 'null']
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('big and complicated', function () {
|
||||
let joi = Joi.object({
|
||||
aFormattedString: Joi.string().regex(/^[ABC]_\w+$/),
|
||||
aFloat: Joi.number().default(0.8).min(0.0).max(1.0),
|
||||
anInt: Joi.number().required().precision(0).greater(0),
|
||||
aForbiddenString: Joi.string().forbidden(),
|
||||
anArrayOfFloats: Joi.array().items(Joi.number().default(0.8).min(0.0).max(1.0)),
|
||||
anArrayOfNumbersOrStrings: Joi.array().items(Joi.alternatives(Joi.number(), Joi.string()))
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = require('./fixtures/complicated.json');
|
||||
|
||||
assert.validate(schema, expected);
|
||||
|
||||
// now make it fail
|
||||
expected.properties.aForbiddenString={type:'string'};
|
||||
|
||||
try {
|
||||
assert.validate(schema,expected);
|
||||
}
|
||||
catch(e){
|
||||
//console.warn(e);
|
||||
if(e.name !== 'AssertionError' && e.operator !== 'deepEqual'){
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
test('joi.when', function () {
|
||||
let joi = Joi.object({
|
||||
'a': Joi.boolean().required().default(false),
|
||||
'b': Joi.alternatives().when('a', {
|
||||
is: true,
|
||||
then: Joi.string().default('a is true'),
|
||||
otherwise: Joi.number().default(0)
|
||||
})
|
||||
}),
|
||||
schema = convert(joi),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
a: {type: 'boolean'},
|
||||
b: {
|
||||
oneOf: [
|
||||
{
|
||||
'default': 'a is true',
|
||||
type: 'string'
|
||||
}, {
|
||||
type: 'number'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: ['a']
|
||||
};
|
||||
//console.log('when: %s', util.inspect({type:joi._type,schema:schema}, {depth: 10}));
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
50
js/node/node_modules/joi-to-json-schema/test/fixtures/complicated.json
generated
vendored
Normal file
50
js/node/node_modules/joi-to-json-schema/test/fixtures/complicated.json
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aFormattedString": {
|
||||
"type": "string",
|
||||
"pattern": "^[ABC]_\\w+$"
|
||||
},
|
||||
"aFloat": {
|
||||
"default": 0.8,
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"anInt": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": true,
|
||||
"minimum": 0
|
||||
},
|
||||
"anArrayOfFloats": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"default": 0.8,
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"anArrayOfNumbersOrStrings": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"anInt"
|
||||
]
|
||||
}
|
84
js/node/node_modules/joi-to-json-schema/test/fixtures/transform.json
generated
vendored
Normal file
84
js/node/node_modules/joi-to-json-schema/test/fixtures/transform.json
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"oneOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"enum": [
|
||||
"",
|
||||
"foo"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"enum": [
|
||||
"2x4"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"size",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"enum": [
|
||||
"foo"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"enum": [
|
||||
"4x8"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "number",
|
||||
"minimum": 11,
|
||||
"maximum": 20
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"size",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+$"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[A-Z]+$"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"options"
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
--require test/setup.js --ui tdd
|
|
@ -0,0 +1 @@
|
|||
require('6to5/register');
|
|
@ -0,0 +1,115 @@
|
|||
'use strict';
|
||||
|
||||
/*
|
||||
* Created by raisch on 3/17/15.
|
||||
*/
|
||||
|
||||
/*jshint mocha:true, node:true, bitwise:true, camelcase:false, curly:true, undef:false, unused:false, eqeqeq:true, shadow:true */
|
||||
/* global suite, test */
|
||||
|
||||
//@formatter:off
|
||||
var Joi = require('joi'),
|
||||
convert = require('../src/index'),
|
||||
assert = require('assert'),
|
||||
jsonSchema = require('json-schema'),
|
||||
_ = require('lodash');
|
||||
//@formatter:on
|
||||
|
||||
/**
|
||||
* Throws if schema !== expected or if schema fails to jsonSchema.validate()
|
||||
* @param {object} schema
|
||||
* @param {object} expected
|
||||
*/
|
||||
assert.validate = function (schema, expected) {
|
||||
var result = jsonSchema.validate(schema);
|
||||
assert.deepEqual(schema, expected);
|
||||
if ('object' === typeof result && Array.isArray(result.errors) && 0 === result.errors.length) {
|
||||
return;
|
||||
}
|
||||
throw new Error('json-schema validation failed: %s', result.errors.join(','));
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes null values from all arrays.
|
||||
* @param {object} obj - transformable object
|
||||
* @returns {*}
|
||||
*/
|
||||
var removeNullsFromArrays = function (obj) {
|
||||
var result;
|
||||
if (_.isArray(obj)) {
|
||||
result = [];
|
||||
for (var i = 0, len = obj.length; i < len; i++) {
|
||||
var val = obj[i];
|
||||
if (null !== val) {
|
||||
result.push(removeNullsFromArrays(val));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (_.isObject(obj)) {
|
||||
result = {};
|
||||
_.keys(obj).forEach(function (key) {
|
||||
result[key] = removeNullsFromArrays(obj[key]);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
suite('transform', function () {
|
||||
|
||||
test('object defaults', function () {
|
||||
var joi = Joi.object(),
|
||||
transformer = function (obj) {
|
||||
obj.additionalProperties = true;
|
||||
return obj;
|
||||
},
|
||||
schema = convert(joi, transformer),
|
||||
expected = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: true // false
|
||||
};
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
test('complicated', function () {
|
||||
|
||||
var joi = Joi.object({
|
||||
name: Joi.string().required(),
|
||||
options: Joi.alternatives()
|
||||
.when('name', {
|
||||
is: 'foo',
|
||||
then: Joi.alternatives().try([
|
||||
Joi.object({
|
||||
name: Joi.string().allow([null, '', 'foo']).required(),
|
||||
size: Joi.string().valid('2x4').required(),
|
||||
value: Joi.string().required()
|
||||
}),
|
||||
Joi.object({
|
||||
name: Joi.string().valid('foo').required(),
|
||||
size: Joi.string().valid('4x8').required(),
|
||||
value: Joi.number().min(11).max(20).required()
|
||||
})
|
||||
])
|
||||
})
|
||||
.when('name', {
|
||||
is: 'bar',
|
||||
then: Joi.string().regex(/^[a-z]+$/)
|
||||
})
|
||||
.when('name', {
|
||||
is: 'baz',
|
||||
then: Joi.string().regex(/^[A-Z]+$/)
|
||||
})
|
||||
.required()
|
||||
}),
|
||||
schema = convert(joi, removeNullsFromArrays),
|
||||
expected = require('./fixtures/transform.json');
|
||||
|
||||
|
||||
assert.validate(schema, expected);
|
||||
});
|
||||
|
||||
});
|
|
@ -13,6 +13,7 @@
|
|||
"http-errors": "1.3.1",
|
||||
"i": "0.3.3",
|
||||
"joi": "6.10.1",
|
||||
"joi-to-json-schema": "2.0.5",
|
||||
"js-yaml": "3.4.6",
|
||||
"jshint": "2.8.0",
|
||||
"lodash": "3.10.1",
|
||||
|
|
Loading…
Reference in New Issue