├── .gitignore ├── .travis.yml ├── LICENSE ├── bin └── generate-schema ├── package.json ├── readme.md ├── src ├── index.js ├── schemas │ ├── bigquery.js │ ├── clickhouse.js │ ├── generic.js │ ├── json.js │ ├── mongoose.js │ └── mysql.js └── utils.js └── test ├── bigquery.js ├── clickhouse.js ├── fixtures ├── advanced.js ├── review.js └── simple.js ├── generic.js ├── json.js ├── mocha.opts ├── mongoose.js └── mysql.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Nijiko Yonskai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bin/generate-schema: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Packages 4 | var GenerateSchema = require('../src/index') 5 | var cli = require('commander') 6 | var pkg = require('../package.json') 7 | var fs = require('fs') 8 | 9 | // Outputs 10 | var content = '' 11 | var mode = 'json' 12 | var quiet = false 13 | var file = null 14 | 15 | // Setup CLI 16 | cli.version(pkg.version) 17 | cli.usage('[options ...] [file]') 18 | 19 | cli.option('-q, --quiet', 'Skip help message in program output', function () { 20 | quiet = true 21 | }) 22 | 23 | cli.option('-g, --generic', 'Generic JSON Primitives schema output', function () { 24 | mode = 'generic' 25 | }) 26 | 27 | cli.option('-j, --json-schema', 'JSON Schema output', function () { 28 | mode = 'json' 29 | }) 30 | 31 | cli.option('-m, --mongoose', 'Mongoose Schema output', function () { 32 | mode = 'mongoose' 33 | }) 34 | 35 | cli.option('-s, --mysql', 'MySQL Table Schema output', function () { 36 | mode = 'mysql' 37 | }) 38 | 39 | cli.option('-b, --big-query', 'Google BigQuery Schema output', function () { 40 | mode = 'bigquery' 41 | }) 42 | 43 | cli.option('-c, --clickhouse', 'ClickHouse Table Schema output', function () { 44 | mode = 'clickhouse' 45 | }) 46 | 47 | cli.action(function (filename) { 48 | file = filename 49 | }) 50 | 51 | cli.parse(process.argv) 52 | 53 | // Program 54 | 55 | function evaluate (data) { 56 | var obj 57 | 58 | try { 59 | obj = JSON.parse(data) 60 | } catch (e) { 61 | try { 62 | obj = eval('(' + data + ')') 63 | } catch (e) { 64 | console.log('Invalid Object or JSON') 65 | } 66 | } 67 | 68 | return obj 69 | } 70 | 71 | function prompt (motd) { 72 | if (motd && !quiet) { 73 | console.log('generate-schema', 'v' + pkg.version, '(' + mode + ')') 74 | console.log('Type', '"exit"', 'to quit.') 75 | console.log('Type', '{a:"b"}', 'to see an example.') 76 | } 77 | 78 | if (process.stdin.isTTY) { 79 | process.stdout.write('> ') 80 | } 81 | } 82 | 83 | function callback (data) { 84 | if (data.indexOf('exit') === 0) { 85 | return process.exit() 86 | } 87 | 88 | var obj 89 | obj = evaluate(data) 90 | obj = GenerateSchema[mode](obj) 91 | 92 | if (obj && typeof obj === 'object') { 93 | obj = JSON.stringify(obj, null, 2) 94 | } 95 | 96 | if (obj) { 97 | console.log(obj) 98 | } 99 | 100 | prompt() 101 | } 102 | 103 | var stream = process.stdin 104 | 105 | if (file) { 106 | stream = fs.createReadStream(file) 107 | stream.on('close', function () { 108 | process.exit(1) 109 | }) 110 | } 111 | 112 | stream.setEncoding('utf8') 113 | 114 | if (!file) { 115 | prompt(true) 116 | 117 | stream.on('data', function (buf) { 118 | callback(buf.toString().trim()) 119 | }) 120 | } else { 121 | stream.on('data', function (buf) { 122 | content += buf.toString() 123 | }) 124 | 125 | stream.on('end', function () { 126 | console.log(JSON.stringify(GenerateSchema[mode](evaluate(content)), null, 2)) 127 | }) 128 | } 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generate-schema", 3 | "version": "2.6.0", 4 | "author": "Nijiko Yonskai ", 5 | "description": "Effortlessly convert your JSON Object to JSON Schema, Mongoose Schema, or a Generic template for quick documentation / upstart.", 6 | "license": "MIT", 7 | "repository": "Nijikokun/generate-schema", 8 | "homepage": "https://github.com/Nijikokun/generate-schema", 9 | "main": "src/index.js", 10 | "scripts": { 11 | "test": "node_modules/mocha/bin/mocha" 12 | }, 13 | "bin": { 14 | "generate-schema": "./bin/generate-schema" 15 | }, 16 | "keywords": [ 17 | "json", 18 | "schema", 19 | "object", 20 | "convert", 21 | "manipulate", 22 | "traverse", 23 | "types", 24 | "type", 25 | "is", 26 | "generate", 27 | "produce", 28 | "mongoose", 29 | "generic", 30 | "validation", 31 | "bigquery", 32 | "google", 33 | "data", 34 | "datatypes" 35 | ], 36 | "dependencies": { 37 | "commander": "^2.9.0", 38 | "type-of-is": "^3.4.0" 39 | }, 40 | "devDependencies": { 41 | "mocha": "^3.0.0", 42 | "should": "^10.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Generate Schemas 2 | 3 | Convert JSON Objects to MySQL Table Schema, JSON Schema, Mongoose Schema, ClickHouse Schema, Google BigQuery, or a Generic template for documentation, code generation, and more. 4 | 5 | [![Build Status][travis-image]][travis-url] 6 | [![version][npm-version]][npm-url] 7 | [![License][npm-license]][license-url] 8 | [![Downloads][npm-downloads]][npm-url] 9 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnijikokun%2Fgenerate-schema.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnijikokun%2Fgenerate-schema?ref=badge_shield) 10 | 11 | ## Table of Contents 12 | 13 | - [Installation](#installation) 14 | - [CLI](#cli) 15 | * [Options](#options) 16 | * [REPL Mode](#repl-interactive-mode) 17 | + [Example](#example) 18 | - [Usage](#usage) 19 | + [Example](#example-1) 20 | + [Methods](#methods) 21 | - [`g.generic(Object object)`](#ggenericobject-object) 22 | - [`g.mysql([String tableName,] Mixed object)`](#gmysqlstring-tablename-mixed-object) 23 | - [`g.json([String title,] Mixed object)`](#gjsonstring-title-mixed-object) 24 | - [`g.mongoose(Object object)`](#gmongooseobject-object) 25 | - [`g.bigquery(Object object)`](#gbigqueryobject-object) 26 | - [`g.clickhouse([String tableName,] Mixed object, String dateField)`](#gclickhousestring-tablename-mixed-object) 27 | - [License](#license) 28 | 29 | # Installation 30 | 31 | Install with [npm](https://www.npmjs.com/): 32 | 33 | ```bash 34 | $ npm i --save generate-schema 35 | ``` 36 | 37 | Optionally, add `-g` to the above if you want the `generate-schema` [command line](#cli) executable. 38 | 39 | # CLI 40 | 41 | ``` 42 | Usage: generate-schema [options ...] [file] 43 | 44 | Common Options: 45 | 46 | -h, --help output usage information 47 | -V, --version output the version number 48 | -q, --quiet Skip help message in program output 49 | 50 | Mode Options: 51 | -g, --generic Generic JSON Primitives schema output 52 | -j, --json-schema JSON Schema output 53 | -s, --mysql MySQL Table Schema output 54 | -m, --mongoose Mongoose Schema output 55 | -b, --big-query Google BigQuery Schema output 56 | -c, --clickhouse Clickhouse Table Schema output 57 | ``` 58 | 59 | ## REPL Mode 60 | 61 | When no file is specified, `generate-schema` enters a REPL mode. 62 | 63 | ### Example 64 | 65 | ``` 66 | $ generate-schema -b 67 | generate-schema v2.5.1 (bigquery) 68 | Type "exit" to quit. 69 | Type {a:"b"} to see an example. 70 | > {a:"b"} 71 | [ 72 | { 73 | "name": "a", 74 | "type": "STRING", 75 | "mode": "NULLABLE" 76 | } 77 | ] 78 | ``` 79 | 80 | # Usage 81 | 82 | ```js 83 | var GenerateSchema = require('generate-schema') 84 | ``` 85 | 86 | ## Example 87 | 88 | ``` 89 | // Capture Schema Output 90 | var schema = GenerateSchema.json('Product', [ 91 | { 92 | "id": 2, 93 | "name": "An ice sculpture", 94 | "price": 12.50, 95 | "tags": ["cold", "ice"], 96 | "dimensions": { 97 | "length": 7.0, 98 | "width": 12.0, 99 | "height": 9.5 100 | }, 101 | "warehouseLocation": { 102 | "latitude": -78.75, 103 | "longitude": 20.4 104 | } 105 | }, 106 | { 107 | "id": 3, 108 | "name": "A blue mouse", 109 | "price": 25.50, 110 | "dimensions": { 111 | "length": 3.1, 112 | "width": 1.0, 113 | "height": 1.0 114 | }, 115 | "warehouseLocation": { 116 | "latitude": 54.4, 117 | "longitude": -32.7 118 | } 119 | } 120 | ]) 121 | ``` 122 | 123 | Outputs: 124 | 125 | ```json 126 | { 127 | "$schema": "http://json-schema.org/draft-04/schema#", 128 | "title": "Product Set", 129 | "type": "array", 130 | "items": { 131 | "type": "object", 132 | "properties": { 133 | "id": { 134 | "type": "number" 135 | }, 136 | "name": { 137 | "type": "string" 138 | }, 139 | "price": { 140 | "type": "number" 141 | }, 142 | "tags": { 143 | "type": "array", 144 | "items": { 145 | "type": "string" 146 | } 147 | }, 148 | "dimensions": { 149 | "type": "object", 150 | "properties": { 151 | "length": { 152 | "type": "number" 153 | }, 154 | "width": { 155 | "type": "number" 156 | }, 157 | "height": { 158 | "type": "number" 159 | } 160 | } 161 | }, 162 | "warehouseLocation": { 163 | "type": "object", 164 | "properties": { 165 | "latitude": { 166 | "type": "number" 167 | }, 168 | "longitude": { 169 | "type": "number" 170 | } 171 | } 172 | } 173 | }, 174 | "required": [ 175 | "id", 176 | "name", 177 | "price", 178 | "dimensions", 179 | "warehouseLocation" 180 | ], 181 | "title": "Product" 182 | } 183 | } 184 | ``` 185 | 186 | 187 | ## Methods 188 | 189 | #### `g.generic(Object object)` 190 | 191 | Generates a generic schema from `object`. Property types are described using primitives. 192 | 193 | #### `g.mysql([String tableName,] Mixed object)` 194 | 195 | Generates MySQL Table Schema from `object`. 196 | 197 | - `tableName` is optional, defaults to `generic` 198 | - `object` must be of type `Object` or `Array` 199 | 200 | #### `g.json([String title,] Mixed object)` 201 | 202 | Generates JSON Schema from `object`. 203 | 204 | - `title` is optional 205 | - `object` must be of type `Object` or `Array` 206 | 207 | #### `g.mongoose(Object object)` 208 | 209 | Generates a [Mongoose Schema][mongoose-schema] from `object`. 210 | 211 | #### `g.bigquery(Object object)` 212 | 213 | Generates a [Google BigQuery][bigquery-schema] schema from `object`. 214 | 215 | #### `g.clickhouse([String tableName,] Mixed object, String dateField)` 216 | 217 | Generates [ClickHouse Table Schema][clickhouse-schema] from `object`. 218 | 219 | - `tableName` is optional, defaults to `generic` 220 | - `object` must be of type `Object` or `Array` 221 | - `dateField` Date field for ENGINE, must be of type `Date` 222 | 223 | # License 224 | 225 | [MIT][license-url] 226 | 227 | 228 | 229 | 230 | [license-url]: https://github.com/Nijikokun/generate-schema/blob/master/LICENSE 231 | [travis-url]: https://travis-ci.org/nijikokun/generate-schema 232 | [travis-image]: https://travis-ci.org/nijikokun/generate-schema.svg?branch=master 233 | [npm-url]: https://www.npmjs.com/package/generate-schema 234 | [npm-license]: https://img.shields.io/npm/l/generate-schema.svg?style=flat 235 | [npm-version]: https://badge.fury.io/js/generate-schema.svg 236 | [npm-downloads]: https://img.shields.io/npm/dm/generate-schema.svg?style=flat 237 | 238 | [json-schema]: http://json-schema.org 239 | [mongoose-schema]: http://mongoosejs.com 240 | [bigquery-schema]: https://cloud.google.com/bigquery/ 241 | [clickhouse-schema]: https://clickhouse.yandex/ 242 | 243 | 244 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnijikokun%2Fgenerate-schema.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnijikokun%2Fgenerate-schema?ref=badge_large) -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | exports.generic = require('./schemas/generic') 2 | exports.mongoose = require('./schemas/mongoose') 3 | exports.bigquery = require('./schemas/bigquery') 4 | exports.mysql = require('./schemas/mysql') 5 | exports.json = require('./schemas/json') 6 | exports.clickhouse = require('./schemas/clickhouse') 7 | -------------------------------------------------------------------------------- /src/schemas/bigquery.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils') 2 | 3 | function getPropertyMode (value) { 4 | return Array.isArray(value) ? 'REPEATED' : 'NULLABLE' 5 | } 6 | 7 | function getPropertyType (value) { 8 | if (Array.isArray(value)) { 9 | return getPropertyType(value[0]) 10 | } 11 | 12 | if (value instanceof Date) return 'TIMESTAMP' 13 | if (typeof value === 'object') return 'RECORD' 14 | if (typeof value === 'boolean') return 'BOOLEAN' 15 | if (typeof value === 'string') { 16 | if (utils.isDateString(value)) return 'DATE' 17 | if (utils.isTimestamp(value)) return 'TIMESTAMP' 18 | } 19 | 20 | if (!isNaN(value)) { 21 | return Number.isInteger(parseFloat(value)) ? 'INTEGER' : 'FLOAT' 22 | } 23 | 24 | return 'STRING' 25 | } 26 | 27 | function processFields (data) { 28 | return Object.keys(data).map(function (key) { 29 | var value = data[key] 30 | var entry = { 31 | name: key, 32 | type: getPropertyType(data[key]), 33 | mode: getPropertyMode(data[key]) 34 | } 35 | 36 | if (entry.type === 'RECORD') { 37 | entry.fields = processFields((entry.mode === 'REPEATED') ? value[0] : value) 38 | } 39 | 40 | return entry 41 | }) 42 | } 43 | 44 | module.exports = function Process (data) { 45 | return processFields(data) 46 | } 47 | -------------------------------------------------------------------------------- /src/schemas/clickhouse.js: -------------------------------------------------------------------------------- 1 | // Modules 2 | var Type = require('type-of-is') 3 | var Utils = require('../utils') 4 | 5 | // Type Mapping for Clickhouse 6 | var types = { 7 | boolean: 'String', 8 | string: 'String', 9 | number: 'Int32', 10 | date: 'Date', 11 | timestamp: 'DateTime', 12 | object: 'Nested', 13 | 'regexp': 'String', 14 | 'undefined': 'String', 15 | 'null': 'String' 16 | } 17 | 18 | var lang = { 19 | create: function (name) { 20 | return ['CREATE TABLE ', name, ' ('].join('') 21 | }, 22 | 23 | close: function (id, dateField) { 24 | if (!dateField) return [') ENGINE = Memory;'].join('') 25 | else return [') ENGINE = MergeTree(', dateField, ', (', id, ', ', dateField, '), 8192);'].join('') 26 | }, 27 | 28 | id: function (name, value) { 29 | return [' ', name, '_id ', value, ','].join('') 30 | }, 31 | 32 | property: function (name, value) { 33 | return [' ', name, ' ', value, ','].join('') 34 | }, 35 | 36 | } 37 | 38 | function processObject (obj, options, dateField) { 39 | var name = options.tableName 40 | var parent = options.parentTableName 41 | var parentId = options.parentTableId 42 | var parentIdType = options.parentTableIdType 43 | 44 | // In-memory storage 45 | var keys = Object.keys(obj) 46 | var output = [] 47 | var tables = [] 48 | 49 | // Table variables 50 | var id = null 51 | var idType = 'string' 52 | 53 | // Initialize Table 54 | output.push(lang.create(name)) 55 | 56 | if (parent) { 57 | output.push(lang.property(parent + '_' + parentId, types[parentIdType])) 58 | } 59 | 60 | // Obtain ID 61 | var nkey 62 | for (var i = 0; i < keys.length; i++) { 63 | if (keys[i].toLowerCase() === 'id' || keys[i].toLowerCase().indexOf('_id') > -1) { 64 | nkey = keys[i] 65 | obj[nkey] = obj[keys[i]] 66 | keys[i] = nkey 67 | id = keys[i] 68 | idType = typeof obj[keys[i]] 69 | } 70 | } 71 | 72 | if (!id) { 73 | id = 'id' 74 | idType = parentIdType || idType 75 | output.push(lang.property(id, types[idType])) 76 | } 77 | 78 | // Create table properties 79 | var key, value, type 80 | for (var i = 0; i < keys.length; i++) { 81 | key = keys[i] 82 | value = obj[key] 83 | type = value instanceof Date 84 | ? 'date' 85 | : Type.string(value).toLowerCase() 86 | 87 | if (type == 'date' && !dateField) { 88 | var dateField = key; 89 | } 90 | 91 | if (type !== 'undefined') { 92 | type = Utils.isTimestamp(value) ? 'timestamp' : type 93 | } 94 | 95 | if (type === 'function') { 96 | continue 97 | } 98 | 99 | // pojo 100 | if (type === 'object' && !value.length) { 101 | tables.push('') 102 | tables.push(processObject(value, { 103 | parentTableName: name, 104 | parentTableId: id, 105 | parentTableIdType: idType, 106 | tableName: name + '_' + key 107 | }).join('')) 108 | continue 109 | } 110 | 111 | // array 112 | if (type === 'object' || type === 'array') { 113 | if (typeof value[0] === 'object') { 114 | tables.push('') 115 | tables.push(processObject(value[0], { 116 | parentTableName: name, 117 | parentTableId: id, 118 | parentTableIdType: idType, 119 | tableName: name + '_' + key 120 | }).join('')) 121 | continue 122 | } 123 | 124 | tables.push('') 125 | tables.push(processObject({ 126 | value: typeof value[0] 127 | }, { 128 | parentTableName: name, 129 | parentTableId: id, 130 | parentTableIdType: idType, 131 | tableName: name + '_' + key 132 | }).join('')) 133 | 134 | continue 135 | } 136 | 137 | output.push(lang.property(key, types[type])) 138 | } 139 | 140 | output[output.length - 1] = Utils.arrayLastItem(output) 141 | .substr(0, Utils.arrayLastItem(output).length - 1) 142 | 143 | output.push(lang.close(id, dateField)) 144 | 145 | return output.concat(tables) 146 | } 147 | 148 | module.exports = function Process (tableName, object, dateField) { 149 | if (typeof tableName !== 'string') { 150 | object = tableName 151 | tableName = 'generic' 152 | } 153 | if (!dateField) 154 | dateField = null 155 | 156 | return processObject(object, { 157 | tableName: tableName 158 | }, dateField).join('') 159 | } 160 | -------------------------------------------------------------------------------- /src/schemas/generic.js: -------------------------------------------------------------------------------- 1 | // Modules 2 | var Type = require('type-of-is') 3 | var Utils = require('../utils') 4 | 5 | module.exports = function Process (object, output) { 6 | output = output || {} 7 | 8 | for (var key in object) { 9 | var value = object[key] 10 | var type = Type.string(value).toLowerCase() 11 | 12 | if (type === 'undefined') { 13 | type = 'null' 14 | } 15 | 16 | if (type === 'string' && Utils.isDate(value)) { 17 | type = 'date' 18 | } 19 | 20 | if (type !== 'object') { 21 | output[key] = { 22 | type: type 23 | } 24 | } else { 25 | output[key] = Process(object[key]) 26 | output[key].type = type 27 | } 28 | } 29 | 30 | return output 31 | } -------------------------------------------------------------------------------- /src/schemas/json.js: -------------------------------------------------------------------------------- 1 | // Modules 2 | var Type = require('type-of-is') 3 | 4 | // Constants 5 | var DRAFT = 'http://json-schema.org/draft-04/schema#' 6 | 7 | function getPropertyFormat(value) { 8 | var type = Type.string(value).toLowerCase() 9 | 10 | if (type === 'date') return 'date-time' 11 | 12 | return null 13 | } 14 | 15 | function getPropertyType(value) { 16 | var type = Type.string(value).toLowerCase() 17 | 18 | if (type === 'number') return Number.isInteger(value) ? 'integer' : type 19 | if (type === 'date') return 'string' 20 | if (type === 'regexp') return 'string' 21 | if (type === 'function') return 'string' 22 | 23 | return type 24 | } 25 | 26 | function getUniqueKeys(a, b, c) { 27 | a = Object.keys(a) 28 | b = Object.keys(b) 29 | c = c || [] 30 | 31 | var value 32 | var cIndex 33 | var aIndex 34 | 35 | for (var keyIndex = 0, keyLength = b.length; keyIndex < keyLength; keyIndex++) { 36 | value = b[keyIndex] 37 | aIndex = a.indexOf(value) 38 | cIndex = c.indexOf(value) 39 | 40 | if (aIndex === -1) { 41 | if (cIndex !== -1) { 42 | // Value is optional, it doesn't exist in A but exists in B(n) 43 | c.splice(cIndex, 1) 44 | } 45 | } else if (cIndex === -1) { 46 | // Value is required, it exists in both B and A, and is not yet present in C 47 | c.push(value) 48 | } 49 | } 50 | 51 | return c 52 | } 53 | 54 | function processArray(array, output, nested) { 55 | var format 56 | var oneOf 57 | var type 58 | 59 | if (nested && output) { 60 | output = { items: output } 61 | } else { 62 | output = output || {} 63 | output.type = getPropertyType(array) 64 | output.items = output.items || {} 65 | type = output.items.type || null 66 | } 67 | 68 | // Determine whether each item is different 69 | for (var arrIndex = 0, arrLength = array.length; arrIndex < arrLength; arrIndex++) { 70 | var elementType = getPropertyType(array[arrIndex]) 71 | var elementFormat = getPropertyFormat(array[arrIndex]) 72 | 73 | if (type && elementType !== type) { 74 | output.items.oneOf = [] 75 | oneOf = true 76 | break 77 | } else { 78 | type = elementType 79 | format = elementFormat 80 | } 81 | } 82 | 83 | // Setup type otherwise 84 | if (!oneOf && type) { 85 | output.items.type = type 86 | if (format) { 87 | output.items.format = format 88 | } 89 | } else if (oneOf && type !== 'object') { 90 | output.items = { 91 | oneOf: [{ type: type }], 92 | required: output.items.required 93 | } 94 | } 95 | 96 | // Process each item depending 97 | if (typeof output.items.oneOf !== 'undefined' || type === 'object' || type === 'array') { 98 | for (var itemIndex = 0, itemLength = array.length; itemIndex < itemLength; itemIndex++) { 99 | var value = array[itemIndex] 100 | var itemType = getPropertyType(value) 101 | var itemFormat = getPropertyFormat(value) 102 | var arrayItem 103 | if (itemType === 'object') { 104 | if (output.items.properties) { 105 | output.items.required = getUniqueKeys(output.items.properties, value, output.items.required) 106 | } 107 | arrayItem = processObject(value, oneOf ? {} : output.items.properties, true) 108 | } else if (itemType === 'array') { 109 | arrayItem = processArray(value, oneOf ? {} : output.items.properties, true) 110 | } else { 111 | arrayItem = {} 112 | arrayItem.type = itemType 113 | if (itemFormat) { 114 | arrayItem.format = itemFormat 115 | } 116 | } 117 | if (oneOf) { 118 | var childType = Type.string(value).toLowerCase() 119 | var tempObj = {} 120 | if (!arrayItem.type && childType === 'object') { 121 | tempObj.properties = arrayItem 122 | tempObj.type = 'object' 123 | arrayItem = tempObj 124 | } 125 | output.items.oneOf.push(arrayItem) 126 | } else if (output.items.type === 'object') { 127 | output.items.properties = arrayItem 128 | } else if(output.items.type === 'array') { 129 | output.items.items = arrayItem 130 | } 131 | } 132 | } 133 | return nested ? output.items : output 134 | } 135 | 136 | function processObject(object, output, nested) { 137 | if (nested && output) { 138 | output = { properties: output } 139 | } else { 140 | output = output || {} 141 | output.type = getPropertyType(object) 142 | output.properties = output.properties || {} 143 | output.required = Object.keys(object) 144 | } 145 | 146 | for (var key in object) { 147 | var value = object[key] 148 | var type = getPropertyType(value) 149 | var format = getPropertyFormat(value) 150 | 151 | type = type === 'undefined' ? 'null' : type 152 | 153 | if (type === 'object') { 154 | output.properties[key] = processObject(value, output.properties[key]) 155 | continue 156 | } 157 | 158 | if (type === 'array') { 159 | output.properties[key] = processArray(value, output.properties[key]) 160 | continue 161 | } 162 | 163 | if (output.properties[key]) { 164 | var entry = output.properties[key] 165 | var hasTypeArray = Array.isArray(entry.type) 166 | 167 | // When an array already exists, we check the existing 168 | // type array to see if it contains our current property 169 | // type, if not, we add it to the array and continue 170 | if (hasTypeArray && entry.type.indexOf(type) < 0) { 171 | entry.type.push(type) 172 | } 173 | 174 | // When multiple fields of differing types occur, 175 | // json schema states that the field must specify the 176 | // primitive types the field allows in array format. 177 | if (!hasTypeArray && entry.type !== type) { 178 | entry.type = [entry.type, type] 179 | } 180 | 181 | continue 182 | } 183 | 184 | output.properties[key] = {} 185 | output.properties[key].type = type 186 | 187 | if (format) { 188 | output.properties[key].format = format 189 | } 190 | } 191 | 192 | return nested ? output.properties : output 193 | } 194 | 195 | module.exports = function Process (title, object) { 196 | var processOutput 197 | var output = { 198 | $schema: DRAFT 199 | } 200 | 201 | // Determine title exists 202 | if (typeof title !== 'string') { 203 | object = title 204 | title = undefined 205 | } else { 206 | output.title = title 207 | } 208 | 209 | // Set initial object type 210 | output.type = Type.string(object).toLowerCase() 211 | 212 | // Process object 213 | if (output.type === 'object') { 214 | processOutput = processObject(object) 215 | output.type = processOutput.type 216 | output.properties = processOutput.properties 217 | output.required = Object.keys(object).filter(function (key) { 218 | return !key.startsWith('$') 219 | }) 220 | } 221 | 222 | if (output.type === 'array') { 223 | processOutput = processArray(object) 224 | output.type = processOutput.type 225 | output.items = processOutput.items 226 | 227 | if (output.title) { 228 | output.items.title = output.title 229 | output.title += ' Set' 230 | } 231 | } 232 | 233 | // Output 234 | return output 235 | } 236 | -------------------------------------------------------------------------------- /src/schemas/mongoose.js: -------------------------------------------------------------------------------- 1 | // Modules 2 | var Type = require('type-of-is') 3 | var Utils = require('../utils') 4 | 5 | function getNativeType (string) { 6 | switch (string) { 7 | case "array": 8 | return 'Array' 9 | 10 | case "buffer": 11 | return 'Buffer' 12 | 13 | case "boolean": 14 | return 'Boolean' 15 | 16 | case "date": 17 | return 'Date' 18 | 19 | case "number": 20 | return 'Number' 21 | 22 | case "string": 23 | return 'String' 24 | 25 | case "objectid": 26 | return 'ObjectId' 27 | 28 | case "null": 29 | case "undefined": 30 | case "regexp": 31 | default: 32 | return 'Mixed' 33 | } 34 | } 35 | 36 | module.exports = function Process (object, output) { 37 | var output = output || {} 38 | 39 | for (var key in object) { 40 | var value = object[key] 41 | var originalType = null 42 | var elementType = null 43 | var type = null 44 | 45 | if (value instanceof Buffer) { 46 | type = 'buffer' 47 | } 48 | 49 | if (value != null && typeof value.toString !== 'undefined' && value.toString().match(/^[0-9a-fA-F]{24}$/)) { 50 | type = 'objectid' 51 | } 52 | 53 | if (!type) { 54 | type = Type.string(value).toLowerCase() 55 | } 56 | 57 | if (type === 'string' && !/^[0-9]+$/.test(value) && Utils.isDate(value)) { 58 | type = 'date' 59 | } 60 | 61 | if (type === 'object') { 62 | output[key] = Process(object[key]) 63 | } else { 64 | if (type === 'undefined') { 65 | type = 'null' 66 | } 67 | 68 | if (type === 'array' && value.length) { 69 | originalType = type 70 | type = undefined 71 | 72 | for (var index = 0, length = value.length; index < length; index++) { 73 | elementType = Type.string(value[index]).toLowerCase() 74 | 75 | if (type && elementType !== type) { 76 | type = 'mixed' 77 | break 78 | } else { 79 | type = elementType 80 | } 81 | } 82 | } 83 | 84 | if (originalType && originalType === 'array') { 85 | output[key] = { type: [getNativeType(type)] } 86 | } else { 87 | output[key] = { type: getNativeType(type) } 88 | } 89 | } 90 | } 91 | 92 | return output 93 | } 94 | -------------------------------------------------------------------------------- /src/schemas/mysql.js: -------------------------------------------------------------------------------- 1 | // Modules 2 | var Type = require('type-of-is') 3 | var Utils = require('../utils') 4 | 5 | // Type Mapping 6 | var types = { 7 | boolean: 'BOOLEAN', 8 | string: 'TEXT', 9 | number: 'INT', 10 | date: 'DATE', 11 | timestamp: 'TIMESTAMP', 12 | 'regexp': 'TEXT', 13 | 'undefined': 'TEXT', 14 | 'null': 'TEXT' 15 | } 16 | 17 | var lang = { 18 | create: function (name) { 19 | return ['CREATE TABLE ', name, ' ('].join('') 20 | }, 21 | 22 | close: function () { 23 | return ');' 24 | }, 25 | 26 | id: function (name, value) { 27 | return [' ', name, '_id ', value, ','].join('') 28 | }, 29 | 30 | property: function (name, value) { 31 | return [' ', name, ' ', value, ','].join('') 32 | }, 33 | 34 | primary: function (id) { 35 | return [' PRIMARY KEY (', id, '),'].join('') 36 | }, 37 | 38 | foreign: function (key1, table, key2) { 39 | return [' FOREIGN KEY (', key1, ') REFERENCES ', table, '(', key2, '),'].join('') 40 | }, 41 | } 42 | 43 | function processObject (obj, options) { 44 | var name = options.tableName 45 | var parent = options.parentTableName 46 | var parentId = options.parentTableId 47 | var parentIdType = options.parentTableIdType 48 | 49 | var tableId = options.tableId 50 | 51 | // In-memory storage 52 | var keys = Object.keys(obj) 53 | var output = [] 54 | var tables = [] 55 | 56 | // Table variables 57 | var id = null 58 | var idType = 'string' 59 | 60 | // Initialize Table 61 | output.push(lang.create(name)) 62 | 63 | if (parent) { 64 | output.push(lang.property(parent + '_' + parentId, types[parentIdType])) 65 | } 66 | 67 | 68 | if (tableId !== 'undefined' && tableId !== 'null' && obj[tableId]) { 69 | id = tableId 70 | idType = typeof obj[tableId] 71 | } else { 72 | 73 | // Obtain ID 74 | var nkey 75 | for (var i = 0; i < keys.length; i++) { 76 | if (keys[i].toLowerCase() === 'id' || keys[i].toLowerCase().indexOf('_id') > -1) { 77 | nkey = keys[i] 78 | obj[nkey] = obj[keys[i]] 79 | keys[i] = nkey 80 | id = keys[i] 81 | idType = typeof obj[keys[i]] 82 | } 83 | } 84 | 85 | if (!id) { 86 | id = 'id' 87 | idType = parentIdType || idType 88 | output.push(lang.property(id, types[idType])) 89 | } 90 | } 91 | 92 | // Create table properties 93 | var key, value, type 94 | for (var i = 0; i < keys.length; i++) { 95 | key = keys[i] 96 | value = obj[key] 97 | type = value instanceof Date 98 | ? 'date' 99 | : Type.string(value).toLowerCase() 100 | 101 | if (type !== 'undefined' && type !== 'null') { 102 | type = Utils.isTimestamp(value) ? 'timestamp' : type 103 | } 104 | 105 | if (type === 'function') { 106 | continue 107 | } 108 | 109 | // pojo 110 | if (type === 'object' && !value.length) { 111 | tables.push('') 112 | tables.push(processObject(value, { 113 | parentTableName: name, 114 | parentTableId: id, 115 | parentTableIdType: idType, 116 | tableName: name + '_' + key 117 | }).join('\n')) 118 | continue 119 | } 120 | 121 | // array 122 | if (type === 'object' || type === 'array') { 123 | if (typeof value[0] === 'object') { 124 | tables.push('') 125 | tables.push(processObject(value[0], { 126 | parentTableName: name, 127 | parentTableId: id, 128 | parentTableIdType: idType, 129 | tableName: name + '_' + key 130 | }).join('\n')) 131 | continue 132 | } 133 | 134 | tables.push('') 135 | tables.push(processObject({ 136 | value: typeof value[0] 137 | }, { 138 | parentTableName: name, 139 | parentTableId: id, 140 | parentTableIdType: idType, 141 | tableName: name + '_' + key 142 | }).join('\n')) 143 | 144 | continue 145 | } 146 | 147 | output.push(lang.property(key, types[type])) 148 | } 149 | 150 | // Handle table keys 151 | output.push(lang.primary(id)) 152 | 153 | if (parent) { 154 | output.push(lang.foreign(parent + '_id', parent, parentId)) 155 | } 156 | 157 | output[output.length - 1] = Utils.arrayLastItem(output) 158 | .substr(0, Utils.arrayLastItem(output).length - 1) 159 | 160 | output.push(lang.close()) 161 | 162 | return output.concat(tables) 163 | } 164 | 165 | module.exports = function Process (tableName, object, options) { 166 | if (typeof tableName !== 'string') { 167 | object = tableName 168 | tableName = 'generic' 169 | } 170 | 171 | return processObject(object, { 172 | tableName: tableName, 173 | ...options 174 | }).join('\n') 175 | } 176 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | var DATE_REGEXP = /\d{4}-\d{2}-\d{2}/ 2 | 3 | exports.isNumber = function (value) { 4 | return (typeof value === 'number' || Object.prototype.toString.call(value) === '[object Number]') 5 | } 6 | 7 | exports.isDate = function (date) { 8 | return ((new Date(date).toString() !== 'Invalid Date' && !isNaN(new Date(date)))) 9 | } 10 | 11 | exports.isTimestamp = function (string) { 12 | return string.length > 18 && !isNaN((new Date(string)).getTime()) 13 | } 14 | 15 | exports.isDateString = function (string) { 16 | return string.match(DATE_REGEXP) 17 | } 18 | 19 | exports.arrayLastItem = function (arr) { 20 | return arr[arr.length - 1] 21 | } 22 | -------------------------------------------------------------------------------- /test/bigquery.js: -------------------------------------------------------------------------------- 1 | var GenerateSchema = require('../src/index') 2 | var simple = require('./fixtures/simple') 3 | 4 | describe('BigQuery', function () { 5 | describe('Type Checks', function () { 6 | var schema 7 | 8 | beforeEach(function () { 9 | schema = GenerateSchema.bigquery(simple) 10 | }) 11 | 12 | it('id should be of type [INTEGER]', function () { 13 | var item = schema[0] 14 | 15 | item.name.should.equal('id') 16 | item.type.should.equal('INTEGER') 17 | item.mode.should.equal('NULLABLE') 18 | }) 19 | 20 | it('slug should be of type [STRING]', function () { 21 | var item = schema[1] 22 | 23 | item.name.should.equal('slug') 24 | item.type.should.equal('STRING') 25 | }) 26 | 27 | it('admin should be of type [BOOLEAN]', function () { 28 | var item = schema[2] 29 | 30 | item.name.should.equal('admin') 31 | item.type.should.equal('BOOLEAN') 32 | item.mode.should.equal('NULLABLE') 33 | }) 34 | 35 | it('avatar should be of type [STRING]', function () { 36 | var item = schema[3] 37 | 38 | item.name.should.equal('avatar') 39 | item.type.should.equal('STRING') 40 | item.mode.should.equal('NULLABLE') 41 | }) 42 | 43 | it('date should be of type [TIMESTAMP]', function () { 44 | var item = schema[4] 45 | 46 | item.name.should.equal('date') 47 | item.type.should.equal('TIMESTAMP') 48 | item.mode.should.equal('NULLABLE') 49 | }) 50 | 51 | it('article should be of type [RECORD] with [NULLABLE] mode and should contain [FIELDS]', function () { 52 | var item = schema[5] 53 | 54 | item.name.should.equal('article') 55 | item.type.should.equal('RECORD') 56 | item.mode.should.equal('NULLABLE') 57 | item.should.have.property('fields') 58 | }) 59 | 60 | it('article.title should be of type [STRING]', function () { 61 | var item = schema[5].fields[0] 62 | 63 | item.name.should.equal('title') 64 | item.type.should.equal('STRING') 65 | item.mode.should.equal('NULLABLE') 66 | }) 67 | 68 | it('article.description should be of type [STRING]', function () { 69 | var item = schema[5].fields[1] 70 | 71 | item.name.should.equal('description') 72 | item.type.should.equal('STRING') 73 | item.mode.should.equal('NULLABLE') 74 | }) 75 | 76 | it('article.body should be of type [STRING]', function () { 77 | var item = schema[5].fields[2] 78 | 79 | item.name.should.equal('body') 80 | item.type.should.equal('STRING') 81 | item.mode.should.equal('NULLABLE') 82 | }) 83 | 84 | it('comments should be of type [RECORD] with [REPEATED] mode and should contain [FIELDS]', function () { 85 | var item = schema[6] 86 | 87 | item.name.should.equal('comments') 88 | item.type.should.equal('RECORD') 89 | item.mode.should.equal('REPEATED') 90 | item.should.have.property('fields') 91 | }) 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /test/clickhouse.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var GenerateSchema = require('../src/index') 3 | var fixtureObj = require('./fixtures/simple') 4 | var fixtureName = 'simple' 5 | 6 | describe('CLICKHOUSE', function () { 7 | describe('Checks', function () { 8 | var schema = GenerateSchema.clickhouse(fixtureName, fixtureObj, 'date') 9 | 10 | it('should create table with the name ' + fixtureName, function () { 11 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName) > -1) 12 | }) 13 | 14 | it('should create table with the name ' + fixtureName + '_article', function () { 15 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_article') > -1) 16 | }) 17 | 18 | it('should create table with the name ' + fixtureName + '_comments', function () { 19 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_comments') > -1) 20 | }) 21 | 22 | it('should create table with the name ' + fixtureName + '_comments_tags', function () { 23 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_comments_tags') > -1) 24 | }) 25 | 26 | it(fixtureName + '_id should be of type [Int32]', function () { 27 | assert.ok(schema.indexOf(fixtureName + '_id Int32') > -1) 28 | }) 29 | 30 | it('slug should be of type [String]', function () { 31 | assert.ok(schema.indexOf('slug String') > -1) 32 | }) 33 | 34 | it('admin should be of type [String]', function () { 35 | assert.ok(schema.indexOf('admin String') > -1) 36 | }) 37 | 38 | it('avatar should be of type [String]', function () { 39 | assert.ok(schema.indexOf('avatar String') > -1) 40 | }) 41 | 42 | it('date should be of type [Date]', function () { 43 | assert.ok(schema.indexOf('date Date') > -1) 44 | }) 45 | 46 | it('should have ENGINE date, id', function () { 47 | assert.ok(schema.indexOf('ENGINE = MergeTree(date') > -1) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/fixtures/advanced.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "id": 2, 4 | "name": "An ice sculpture", 5 | "price": 12.50, 6 | "tags": ["cold", "ice"], 7 | "filters": [ 8 | [{ 9 | "operator": "EQ", 10 | "value": "Ice Cold" 11 | }] 12 | ], 13 | "dimensions": { 14 | "length": 7.0, 15 | "width": 12.0, 16 | "height": 9.5 17 | }, 18 | "warehouseLocation": { 19 | "latitude": -78.75, 20 | "longitude": 20.4 21 | } 22 | }, 23 | { 24 | "id": 3, 25 | "name": "A blue mouse", 26 | "price": 25.50, 27 | "dimensions": { 28 | "length": 3.1, 29 | "width": 1.0, 30 | "height": 1.0 31 | }, 32 | "warehouseLocation": { 33 | "latitude": 54.4, 34 | "longitude": -32.7 35 | } 36 | } 37 | ] -------------------------------------------------------------------------------- /test/fixtures/review.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "_id": "5883cb02b768a81f86fb467e", 3 | "votes": { 4 | "funny": 0, 5 | "useful": 0, 6 | "cool": 0 7 | }, 8 | "user_id": "KZuaJtFindQM9x2ZoMBxcQ", 9 | "review_id": "WExNE-f93SL4D1q8s9QWKg", 10 | "stars": 1, 11 | "date": "2013-07-14", 12 | "text": "This place is absolute garbage... Half of the tees are not available, including all the grass tees. It is cash only, and they sell the last bucket at 8, despite having lights. And if you finish even a minute after 8, don't plan on getting a drink. The vending machines are sold out (of course) and they sell drinks inside, but close the drawers at 8 on the dot. There are weeds grown all over the place. I noticed some sort of batting cage, but it looks like those are out of order as well. Someone should buy this place and turn it into what it should be.", 13 | "type": "review", 14 | "business_id": "cE27W9VPgO88Qxe4ol6y_g", 15 | "approved_by": null 16 | } -------------------------------------------------------------------------------- /test/fixtures/simple.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | id: 42, 3 | slug: 'name', 4 | admin: true, 5 | avatar: undefined, 6 | date: new Date(), 7 | article: { 8 | title: 'Name', 9 | description: 'Something longer goes here', 10 | body: 'There once was someone who viewed tests, and they were amazing.' 11 | }, 12 | comments: [{ 13 | body: 'Nothing is safe inside of an array', 14 | tags: ['hello'] 15 | }, { 16 | body: '', 17 | tags: [null] 18 | }, { 19 | body: null, 20 | tags: [null] 21 | }] 22 | } -------------------------------------------------------------------------------- /test/generic.js: -------------------------------------------------------------------------------- 1 | var GenerateSchema = require('../src/index') 2 | var simple = require('./fixtures/simple') 3 | 4 | describe('Generic', function () { 5 | describe('Type Checks', function () { 6 | var schema 7 | 8 | beforeEach(function () { 9 | schema = GenerateSchema.generic(simple) 10 | }) 11 | 12 | it('.id should be of type [number]', function () { 13 | schema.id.type.should.equal('number') 14 | }) 15 | 16 | it('.slug should be of type [string]', function () { 17 | schema.slug.type.should.equal('string') 18 | }) 19 | 20 | it('.admin should be of type [boolean]', function () { 21 | schema.admin.type.should.equal('boolean') 22 | }) 23 | 24 | it('.avatar should be of type [null]', function () { 25 | schema.avatar.type.should.equal('null') 26 | }) 27 | 28 | it('.date should be of type [date]', function () { 29 | schema.date.type.should.equal('date') 30 | }) 31 | 32 | it('.article should be of type [object]', function () { 33 | schema.article.type.should.equal('object') 34 | }) 35 | 36 | it('.article.title should be of type [string]', function () { 37 | schema.article.title.type.should.equal('string') 38 | }) 39 | 40 | it('.article.description should be of type [string]', function () { 41 | schema.article.description.type.should.equal('string') 42 | }) 43 | 44 | it('.article.body should be of type [string]', function () { 45 | schema.article.body.type.should.equal('string') 46 | }) 47 | 48 | it('.comments should be of type [array]', function () { 49 | schema.comments.type.should.equal('array') 50 | }) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /test/json.js: -------------------------------------------------------------------------------- 1 | var GenerateSchema = require('../src/index') 2 | var simple = require('./fixtures/simple') 3 | var advanced = require('./fixtures/advanced') 4 | 5 | describe('JSON', function () { 6 | describe('Schema Checks', function () { 7 | var schema 8 | 9 | beforeEach(function () { 10 | schema = GenerateSchema.json(simple) 11 | }) 12 | 13 | it('.$schema should exist', function () { 14 | schema.should.have.property('$schema') 15 | }) 16 | 17 | it('.type should exist', function () { 18 | schema.should.have.property('type') 19 | }) 20 | }) 21 | 22 | describe('Item Checks', function () { 23 | var schema 24 | 25 | beforeEach(function () { 26 | schema = GenerateSchema.json(advanced) 27 | }) 28 | 29 | it('.items should be an object', function () { 30 | schema.items.should.be.type('object') 31 | }) 32 | 33 | it('.items.required should be an array', function () { 34 | schema.items.required.should.be.an.Array 35 | schema.items.required.should.eql([ 'id', 'name', 'price', 'dimensions', 'warehouseLocation' ]) 36 | }) 37 | 38 | it('.items.properties.tags should be an object', function () { 39 | schema.items.properties.tags.should.be.type('object') 40 | }) 41 | 42 | it('.items.properties.filters.items should be an array of array', function () { 43 | schema.items.properties.filters.type.should.eql('array') 44 | schema.items.properties.filters.items.type.should.eql('array') 45 | schema.items.properties.filters.items.items.type.should.eql('object') 46 | }) 47 | 48 | it('.items.properties.id should be of type [integer]', function () { 49 | schema.items.properties.id.type.should.equal('integer') 50 | }) 51 | 52 | it('.items.properties.price should be of type [number]', function () { 53 | schema.items.properties.price.type.should.equal('number') 54 | }) 55 | 56 | it('.items.properties.dimensions.properties.length should be of type [integer, number]', function () { 57 | schema.items.properties.dimensions.properties.length.type.should.eql(['integer', 'number']) 58 | }) 59 | }) 60 | 61 | describe('Property Checks', function () { 62 | var schema 63 | 64 | beforeEach(function () { 65 | schema = GenerateSchema.json(simple) 66 | }) 67 | 68 | it('.properties should exist', function () { 69 | schema.should.have.property('properties') 70 | }) 71 | 72 | it('.properties should be an object', function () { 73 | schema.properties.should.be.type('object') 74 | }) 75 | 76 | it('.properties.id should be of type [integer]', function () { 77 | schema.properties.id.type.should.equal('integer') 78 | }) 79 | 80 | it('.properties.slug should be of type [string]', function () { 81 | schema.properties.slug.type.should.equal('string') 82 | }) 83 | 84 | it('.properties.admin should be of type [boolean]', function () { 85 | schema.properties.admin.type.should.equal('boolean') 86 | }) 87 | 88 | it('.properties.avatar should be of type [null]', function () { 89 | schema.properties.avatar.type.should.equal('null') 90 | }) 91 | 92 | it('.properties.date should be of type [string]', function () { 93 | schema.properties.date.type.should.equal('string') 94 | schema.properties.date.format.should.equal('date-time') 95 | }) 96 | 97 | it('.properties.article should be of type [object]', function () { 98 | schema.properties.article.type.should.equal('object') 99 | }) 100 | 101 | it('.properties.article.properties should be of type [object]', function () { 102 | schema.properties.article.properties.should.be.type('object') 103 | }) 104 | 105 | it('.properties.article.properties.title should be of type [string]', function () { 106 | schema.properties.article.properties.title.type.should.equal('string') 107 | }) 108 | 109 | it('.properties.article.properties.description should be of type [string]', function () { 110 | schema.properties.article.properties.description.type.should.equal('string') 111 | }) 112 | 113 | it('.properties.article.properties.body should be of type [string]', function () { 114 | schema.properties.article.properties.body.type.should.equal('string') 115 | }) 116 | 117 | it('.properties.comments should be of type [array]', function () { 118 | schema.properties.comments.type.should.equal('array') 119 | }) 120 | 121 | it('.properties.comments.items should be of type [object]', function () { 122 | schema.properties.comments.items.should.be.type('object') 123 | }) 124 | 125 | it('.properties.comments.items.properties.body should be of type [string, null]', function () { 126 | schema.properties.comments.items.properties.body.type[0].should.equal('string') 127 | schema.properties.comments.items.properties.body.type[1].should.equal('null') 128 | }) 129 | }) 130 | }) -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require should 2 | --reporter dot 3 | --ui bdd 4 | --timeout 200 -------------------------------------------------------------------------------- /test/mongoose.js: -------------------------------------------------------------------------------- 1 | var GenerateSchema = require('../src/index') 2 | var review = require('./fixtures/review') 3 | 4 | describe('Mongoose', function () { 5 | describe('Type Checks', function () { 6 | var schema 7 | 8 | beforeEach(function () { 9 | schema = GenerateSchema.mongoose(review) 10 | }) 11 | 12 | it('._id should be of type [ObjectId]', function () { 13 | schema._id.type.should.equal('ObjectId') 14 | }) 15 | 16 | it('.approved_by should be of type [Mixed]', function () { 17 | schema.approved_by.type.should.equal('Mixed') 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/mysql.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var GenerateSchema = require('../src/index') 3 | var fixtureObj = require('./fixtures/simple') 4 | var fixtureName = 'simple' 5 | 6 | describe('MYSQL', function () { 7 | describe('Checks', function () { 8 | var schema = GenerateSchema.mysql(fixtureName, fixtureObj) 9 | 10 | it('should create table with the name ' + fixtureName, function () { 11 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName) > -1) 12 | }) 13 | 14 | it('should create table with the name ' + fixtureName + '_article', function () { 15 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_article') > -1) 16 | }) 17 | 18 | it('should create table with the name ' + fixtureName + '_comments', function () { 19 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_comments') > -1) 20 | }) 21 | 22 | it('should create table with the name ' + fixtureName + '_comments_tags', function () { 23 | assert.ok(schema.indexOf('CREATE TABLE ' + fixtureName + '_comments_tags') > -1) 24 | }) 25 | 26 | it(fixtureName + '_id should be of type [INT]', function () { 27 | assert.ok(schema.indexOf(fixtureName + '_id INT') > -1) 28 | }) 29 | 30 | it('slug should be of type [TEXT]', function () { 31 | assert.ok(schema.indexOf('slug TEXT') > -1) 32 | }) 33 | 34 | it('admin should be of type [BOOLEAN]', function () { 35 | assert.ok(schema.indexOf('admin BOOLEAN') > -1) 36 | }) 37 | 38 | it('avatar should be of type [TEXT]', function () { 39 | assert.ok(schema.indexOf('avatar TEXT') > -1) 40 | }) 41 | 42 | it('date should be of type [DATE]', function () { 43 | assert.ok(schema.indexOf('date DATE') > -1) 44 | }) 45 | 46 | it('should have primary key id', function () { 47 | assert.ok(schema.indexOf('PRIMARY KEY (id)') > -1) 48 | }) 49 | 50 | it('should have table with foreign key ' + fixtureName + '_comments_id', function () { 51 | assert.ok(schema.indexOf('FOREIGN KEY (' + fixtureName + '_comments_id)') > -1) 52 | }) 53 | }) 54 | }) 55 | --------------------------------------------------------------------------------