fakeit
Version:
Command-line utility that generates fake data which can be output as JSON, YAML, CSON, or CSV formats based on models defined in YAML.
652 lines (504 loc) • 21.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.validate = exports.output_types = undefined;
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
exports.isServer = isServer;
exports.isString = isString;
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _lodash = require('lodash');
var _toJs = require('to-js');
var _toJs2 = _interopRequireDefault(_toJs);
var _base = require('../base');
var _base2 = _interopRequireDefault(_base);
var _utils = require('../utils');
var _defaultOptions = require('./default-options');
var _defaultOptions2 = _interopRequireDefault(_defaultOptions);
var _perfy = require('perfy');
var _perfy2 = _interopRequireDefault(_perfy);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var output_types = exports.output_types = ['return', 'console', 'couchbase', 'sync-gateway'];
/// @name Output
/// @description
/// This is used to output data into different environments
////
/// @page api
////
var Output = function (_Base) {
(0, _inherits3.default)(Output, _Base);
///# @name constructor
///# @arg {object} options [{}] - The options that apply to Base
///# @arg {object} output_options - The options for how you want save data
///# {
///#
///# }
///# @todo update the output_options to have the final options and descriptions of each
function Output() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var output_options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
(0, _classCallCheck3.default)(this, Output);
var _this = (0, _possibleConstructorReturn3.default)(this, (Output.__proto__ || (0, _getPrototypeOf2.default)(Output)).call(this, options));
_this.output_options = (0, _lodash.extend)({}, _defaultOptions2.default, output_options);
_this.validateOutputOptions();
_this.prepared = false;
return _this;
}
///# @name prepare
///# @description
///# This is used to prepare the saving functionality that is determined by the
///# options that were passed to the constructor.
///# It sets a variable of `this.preparing` that ultimately calls `this.setup` that returns a promise.
///# This way when you go to save data it, that function will know if the setup is complete or not and
///# wait for it to be done before it starts saving data.
///# @returns {promise} - The setup function that was called
///# @async
(0, _createClass3.default)(Output, [{
key: 'prepare',
value: function prepare() {
this.preparing = true;
this.preparing = this.setup();
return this.preparing;
}
///# @name setup
///# @description
///# This is used to setup the saving function that will be used.
}, {
key: 'setup',
value: function () {
var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() {
var _this2 = this;
var _output_options, output, archive, Outputter;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (!(this.preparing == null)) {
_context.next = 2;
break;
}
return _context.abrupt('return', this.prepare());
case 2:
_output_options = this.output_options, output = _output_options.output, archive = _output_options.archive;
if (!(output_types.indexOf(output) !== -1)) {
output = !!archive ? 'zip' : 'folder';
}
// get the outputter to use
if (!(output !== 'return')) {
_context.next = 12;
break;
}
Outputter = require('./' + output).default;
// creates a new instance of it so that we can use it to output the data in
// what ever way that the user wants to output it in.
this.outputter = new Outputter(this.options, this.output_options);
// if the outputter has a prepare function call it and await for it to be done
if (!(typeof this.outputter.prepare === 'function')) {
_context.next = 10;
break;
}
_context.next = 10;
return this.outputter.prepare();
case 10:
this.prepared = true;
return _context.abrupt('return');
case 12:
process.nextTick(function () {
_this2.prepared = true;
});
case 13:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
function setup() {
return _ref.apply(this, arguments);
}
return setup;
}()
///# @name output
///# @description
///# This is used to save data to any place that was passed in the constructor
///# @arg {array, object} documents - The data that you want to be saved
///# @returns {array, object, string} - This is determined by the output type that's passed and the format that's used.
///# @async
}, {
key: 'output',
value: function () {
var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(documents) {
var _this3 = this;
var count, _output_options2, format, spacing, limit, output, name, spinner, times, update, parser, parsed, result;
return _regenerator2.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
count = 0;
if (documents) {
_context3.next = 3;
break;
}
return _context3.abrupt('return', this.log('error', 'You must pass in documents to the output'));
case 3:
documents = _toJs2.default.array(documents);
if (!(this.prepared !== true)) {
_context3.next = 8;
break;
}
if (this.preparing == null) {
this.prepare();
}
_context3.next = 8;
return this.preparing;
case 8:
_output_options2 = this.output_options, format = _output_options2.format, spacing = _output_options2.spacing, limit = _output_options2.limit, output = _output_options2.output;
name = documents[0].__name; // eslint-disable-line
spinner = this.spinner('Outputting ' + name);
times = [];
update = function update() {
spinner.text = 'Outputting ' + name + ' to ' + output + ' (' + count + '/' + documents.length + ')';
if (times.length) {
spinner.text += ' (' + (0, _lodash.mean)(times).toFixed(2) + 'ms on average)';
}
};
parser = this.getParser(output, format);
// if the output type is `return` or `console` then this will return the complete
// data set instead of running them individually
if (!(['return', 'console'].indexOf(output) !== -1)) {
_context3.next = 28;
break;
}
_context3.next = 17;
return parser(documents, spacing);
case 17:
parsed = _context3.sent;
if (!(output === 'console')) {
_context3.next = 27;
break;
}
spinner.start();
_context3.next = 22;
return this.outputter.output(null, parsed);
case 22:
result = _context3.sent;
spinner.text = 'Outputting ' + name + ' to ' + output;
spinner.stop();
console.log(result);
return _context3.abrupt('return', result);
case 27:
return _context3.abrupt('return', parsed);
case 28:
// if the output isn't `return` or `console` and the `format` is `csv`
// then it needs to be updated
if (format === 'csv') {
documents = [documents];
}
// reformat the data into the output type
spinner.start();
return _context3.abrupt('return', (0, _utils.pool)(documents, function () {
var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(document, i) {
var label, key, result;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
label = (0, _lodash.uniqueId)('document ' + name + ' ' + i);
_perfy2.default.start(label);
key = document.__key || document.__name || (document[0] || {}).__name || ''; // eslint-disable-line no-underscore-dangle
// use the outputter's output function to output the data
_context2.t0 = _this3.outputter;
_context2.t1 = key;
_context2.next = 7;
return parser(document, spacing);
case 7:
_context2.t2 = _context2.sent;
_context2.next = 10;
return _context2.t0.output.call(_context2.t0, _context2.t1, _context2.t2);
case 10:
result = _context2.sent;
update(count++);
times.push(_perfy2.default.end(label).milliseconds);
return _context2.abrupt('return', result);
case 14:
case 'end':
return _context2.stop();
}
}
}, _callee2, _this3);
}));
return function (_x4, _x5) {
return _ref3.apply(this, arguments);
};
}(), limit).then(function (result) {
spinner.stop();
return result;
}).catch(function (err) {
spinner.fail(err);
}));
case 31:
case 'end':
return _context3.stop();
}
}
}, _callee3, this);
}));
function output(_x3) {
return _ref2.apply(this, arguments);
}
return output;
}()
///# @name finalize
///# @description
///# This is used to clean up anything that needs to be cleaned up
///# like a connection to a data base, any event listeners, or finish outputting a zip file.
///# @async
}, {
key: 'finalize',
value: function () {
var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4() {
return _regenerator2.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
if (!(this.outputter && _toJs.is.function(this.outputter.finalize))) {
_context4.next = 3;
break;
}
_context4.next = 3;
return this.outputter.finalize();
case 3:
case 'end':
return _context4.stop();
}
}
}, _callee4, this);
}));
function finalize() {
return _ref4.apply(this, arguments);
}
return finalize;
}()
///# @name validateOutputOptions
///# @description This is used to validate the output options
///# @throws {error} - If an option that was passed is invalid.
}, {
key: 'validateOutputOptions',
value: function validateOutputOptions() {
var _this4 = this;
_toJs2.default.each(this.output_options, function (_ref5) {
var key = _ref5.key,
value = _ref5.value;
try {
validate[key](value, _this4.output_options);
} catch (e) {
_this4.log('error', e);
}
});
}
///# @name getParser
///# @description Returns a parser function for the given output type and format
///# @arg {string} output - Output type
///# @arg {string} format - Output format
///# @returns {function} - Function to format a document
}, {
key: 'getParser',
value: function getParser(output, format) {
if (output === 'couchbase' && format === 'json') {
return function (obj) {
return obj;
};
}
return _utils.parsers[format].stringify;
}
}]);
return Output;
}(_base2.default);
/// @name Output validate
/// @description This holds the different options that can be passed to the Output constructor
/// @type {object}
exports.default = Output;
var validate = exports.validate = {
///# @name format
///# @description Used to validate the format option
///# @arg {string} option - The option to validate against
///# @throws {error} - If the format option that was pass was invalid
format: function format(option) {
var formats = ['json', 'csv', 'yaml', 'yml', 'cson'];
if (isString(option, 'format') && formats.indexOf(option) !== -1) {
return;
}
throw new Error('You must use one of the following formats ' + formats + '. You passed ' + option);
},
///# @name spacing
///# @description Used to validate the spacing option
///# @arg {number} option - The option to validate against
///# @throws {error} - If the spacing option that was pass was invalid
spacing: function spacing(option) {
if (option === '0' || option === 'null') {
option = 0;
}
if (!_toJs.is.number(option)) {
throw new Error('The spacing option must be a number');
}
},
///# @name output
///# @description Used to validate the output option
///# @arg {string} option - The option to validate against
///# @throws {error} - If the output option that was pass was invalid
output: function output(option) {
if (!isString(option, 'output')) return;
if (output_types.indexOf(option) !== -1 || !_path2.default.extname(option)) {
return;
}
throw new Error('The output option must be ' + output_types.join(', ') + ', or a folder path');
},
///# @name highlight
///# @description Used to validate the highlight option
///# @arg {boolean} option - The option to validate against
///# @throws {error} - If the highlight option that was pass was invalid
highlight: function highlight(option) {
if (!_toJs.is.boolean(option)) {
throw new Error('The highlight option must be a boolean');
}
},
///# @name limit
///# @description Used to validate the limit option
///# @arg {number} option - The option to validate against
///# @throws {error} - If the limit option that was pass was invalid
limit: function limit(option) {
if (!_toJs.is.number(option)) {
throw new Error('The limit option must be a number');
}
},
///# @name archive
///# @description Used to validate the archive option
///# @arg {boolean} option - The option to validate against
///# @throws {error} - If the archive option that was pass was invalid
archive: function archive(option, _ref6) {
var output = _ref6.output;
if (!_toJs.is.string(option)) {
throw new Error('The archive option must be a string');
}
// there's no archive file specified
if (!option.length) return;
if (option && ['return', 'console'].indexOf(output) !== -1) {
throw new Error('You can\'t have an archive file when you have the output option set to ' + output);
} else if (_path2.default.extname(option) !== '.zip') {
throw new Error('The archive file must have a file extention of `.zip`');
}
},
///# @name server
///# @description Used to validate the server option
///# @arg {string} option - The option to validate against
///# @arg {object} options - The other options for that are being validated
///# @throws {error} - If the server option that was pass was invalid
server: function server(option, _ref7) {
var output = _ref7.output,
archive = _ref7.archive;
// ignore this validation if the output isn't one of these
if (!isServer(output)) return;
if (archive === true) {
throw new Error('The archive option can\'t be used with ' + option);
}
isString(option, 'server');
},
///# @name bucket
///# @description Used to validate the bucket option
///# @arg {string} option - The option to validate against
///# @arg {object} options - The other options for that are being validated
///# @throws {error} - If the bucket option that was pass was invalid
bucket: function bucket(option, _ref8) {
var output = _ref8.output;
// ignore this validation if the output isn't one of these
if (!isServer(output)) return;
isString(option, 'bucket');
},
///# @name username
///# @description Used to validate the username option
///# @arg {string} option - The option to validate against
///# @arg {object} options - The other options for that are being validated
///# @throws {error} - If the username option that was pass was invalid
username: function username(option) {
if (!_toJs.is.string(option)) {
throw new Error('The username option must be a string');
}
},
///# @name password
///# @description Used to validate the password option
///# @arg {string} option - The option to validate against
///# @arg {object} options - The other options for that are being validated
///# @throws {error} - If the password option that was pass was invalid
password: function password(option) {
if (!_toJs.is.string(option)) {
throw new Error('The password option must be a string');
}
},
///# @name limit
///# @description Used to validate the limit option
///# @arg {number} option - The option to validate against
///# @throws {error} - If the limit option that was pass was invalid
timeout: function timeout(option) {
if (!_toJs.is.number(option)) {
throw new Error('The timeout option must be a number');
}
},
///# @name useStreams
///# @description Used to validate the useStreams option
///# @arg {boolean} option - The option to validate against
///# @throws {error} - If the useStreams option that was pass was invalid
useStreams: function useStreams(option) {
if (!_toJs.is.boolean(option)) {
throw new Error('The useStreams option must be a boolean');
}
},
///# @name highWaterMark
///# @description Used to validate the highWaterMark option
///# @arg {number} option - The option to validate against
///# @throws {error} - If the highWaterMark option that was pass was invalid
highWaterMark: function highWaterMark(option) {
if (!_toJs.is.number(option)) {
throw new Error('The highWaterMark option must be a number');
}
}
};
/// @name isServer
/// @description This is used to check if the output is `sync-gateway` or `couchbase`
/// @arg {string} output - The output that's being used
/// @returns {boolean} - If true the output option is a server
function isServer(output) {
return ['sync-gateway', 'couchbase'].indexOf(output) !== -1;
}
/// @name isString
/// @description This is used to check if an option is a string and that it has a length
/// @arg {*} option - The option that's passed
/// @arg {string} name - The name of the validate function that this is called in
/// @returns {boolean} - If true the option that was passed is a string and has a length.
/// @throws {error} - If the option isn't a string
/// @throws {error} - If the option is a string and doesn't have a length
function isString(option) {
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
if (!!name) {
name = ' ' + name + ' ';
}
if (!_toJs.is.string(option)) {
throw new Error('The' + name + 'option must be a string');
} else if (_toJs.is.string(option) && !option.length) {
throw new Error('The' + name + 'option must have a length');
}
return true;
}