all files / lib/ converter.js

89.13% Statements 41/46
88.46% Branches 23/26
100% Functions 7/7
88.64% Lines 39/44
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118                      1536×     1344× 1344×                       241×     241× 192× 192× 192× 192× 192× 192× 192× 192×               241×     241×     241×     237×                             159× 54× 54× 54×     159× 159×   157×                               82× 25× 25× 25×     82× 82×   80×          
'use strict';
 
var json2Csv = require('./json-2-csv'), // Require our json-2-csv code
    csv2Json = require('./csv-2-json'), // Require our csv-2-json code
    constants = require('./constants.json'), // Require in constants
    docPath = require('doc-path'),
    _ = require('underscore'); // Require underscore
 
/**
 * Default options
 */
var defaultOptions = constants.DefaultOptions;
 
var isDefined = function (val) {
    return !_.isUndefined(val);
};
 
var copyOption = function (options, lowercasePath, uppercasePath) {
    var lowerCaseValue = docPath.evaluatePath(options, lowercasePath);
    Iif (isDefined(lowerCaseValue)) {
        docPath.setPath(options, uppercasePath, lowerCaseValue);
    }
};
 
/**
 * Build the options to be passed to the appropriate function
 * If a user does not provide custom options, then we use our default
 * If options are provided, then we set each valid key that was passed
 */
var buildOptions = function (opts, cb) {
    // PREVIOUS VERSION SUPPORT (so that future versions are backwards compatible)
    // Issue #26: opts.EOL should be opts.DELIMITER.EOL -- this will move the option & provide backwards compatibility
    if (docPath.evaluatePath(opts, 'EOL')) { docPath.setPath(opts, 'DELIMITER.EOL', opts.EOL); }
    
    // #62: Allow for lower case option names
    if (opts) {
        copyOption(opts, 'prependHeader', 'PREPEND_HEADER');
        copyOption(opts, 'trimHeaderFields', 'TRIM_HEADER_FIELDS');
        copyOption(opts, 'sortHeader', 'SORT_HEADER');
        copyOption(opts, 'parseCsvNumbers', 'PARSE_CSV_NUMBERS');
        copyOption(opts, 'keys', 'KEYS');
        copyOption(opts, 'checkSchemaDifferences', 'CHECK_SCHEMA_DIFFERENCES');
        copyOption(opts, 'emptyFieldValue', 'EMPTY_FIELD_VALUE');
        Iif (isDefined(opts.delimiter)) { 
            copyOption(opts, 'delimiter.field', 'DELIMITER.FIELD');
            copyOption(opts, 'delimiter.array', 'DELIMITER.ARRAY');
            copyOption(opts, 'delimiter.wrap', 'DELIMITER.WRAP');
            copyOption(opts, 'delimiter.eol', 'DELIMITER.EOL');
        }
    }
 
    opts = _.defaults(opts || {}, defaultOptions);
 
    // Note: _.defaults does a shallow default, we need to deep copy the DELIMITER object
    opts.DELIMITER = _.defaults(opts.DELIMITER || {}, defaultOptions.DELIMITER);
 
    // If the delimiter fields are the same, report an error to the caller
    if (opts.DELIMITER.FIELD === opts.DELIMITER.ARRAY) { return cb(new Error(constants.Errors.delimitersMustDiffer)); }
    
    // Otherwise, send the options back
    return cb(null, opts);
};
 
// Export the following functions that will be client accessible
module.exports = {
 
    /**
     * Client accessible json2csv function
     * Takes an array of JSON documents to be converted, a callback that will be called with (err, csv)
     * after processing is complete, and optional options
     * @param array Object[] data to be converted
     * @param callback Function callback
     * @param opts Object options object
     */
    json2csv: function (array, callback, opts) {
        // If this was promisified (callback and opts are swapped) then fix the argument order.
        if (_.isObject(callback) && !_.isFunction(callback)) {
            var func = opts;
            opts = callback;
            callback = func;
        }
 
        buildOptions(opts, function (err, options) { // Build the options
            if (err) {
                return callback(err);
            } else {
                json2Csv.json2csv(options, array, callback); // Call our internal json2csv function
            }
        });
    },
 
    
    /**
     * Client accessible csv2json function
     * Takes a string of CSV to be converted to a JSON document array, a callback that will be called
     * with (err, json) after processing is complete, and optional options
     * @param csv
     * @param callback
     * @param opts
     */
    csv2json: function (csv, callback, opts) {
        // If this was promisified (callback and opts are swapped) then fix the argument order.
        if (_.isObject(callback) && !_.isFunction(callback)) {
            var func = opts;
            opts = callback;
            callback = func;
        }
 
        buildOptions(opts, function (err, options) { // Build the options
            if (err) {
                return callback(err);
            } else {
                csv2Json.csv2json(options, csv, callback); // Call our internal csv2json function
            }
        });
    }
};