<commit_before>'use strict';

module.exports = function (grunt) {

    // Project configuration.
    grunt.config.init({
        buildEnv: grunt.option('buildEnv') || 'prod',
        buildNumber: grunt.option('buildNumber') || '0',
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readJSON('grunt.json'),
        bump: {
            options: {
                files: '<%= config.bump.options.files %>',
                commit: true,
                commitFiles: '<%= config.bump.options.commitFiles %>',
                commitMessage: 'Release v%VERSION%',
                createTag: false,
                tagName: 'v%VERSION%',
                tagMessage: 'Version %VERSION%',
                push: false
            }
        },
        changelog: {
            options: {
                dest: 'CHANGELOG.md',
                prepend: true
            }
        },
        clean: {
            build: ['dist/', 'tmp/'],
            tmp: ['tmp/']
        },
        concat: {
            app: {
                src: [
                    'tmp/js/templates.js',
                    'app/js/**/*.js'
                ],
                dest: 'tmp/js/scripts.js'
            },
            components: '<%= config.concat.components %>',
            css: '<%= config.concat.css %>'
        },
        copy: {
            tmp: {
                files: [
                    {
                        expand: true,
                        cwd: 'app/',
                        src: ['**'],
                        dest: 'tmp/'
                    }
                ]
            },
            build: {
                files: [
                    {
                        expand: true,
                        cwd: 'tmp/',
                        src: ['index.html', 'js/scripts.min.js', 'image/**', 'css/styles.min.css', 'routes.json'],
                        dest: 'dist/',
                        rename: function (desc, src) {
                            return desc + src.replace('min.', 'min.<%= buildNumber %>.')
                        }
                    },
                    {
                        expand: true,
                        cwd: 'tmp/',
                        src: ['htaccess.dist', 'config.json.dist'],
                        dest: 'dist/',
                        rename: function (dest, src) {
                            console.log(src);
                            if (src == 'htaccess.dist') {
                                return dest + '.htaccess';
                            } else {
                                return dest + 'config.json';
                            }
                        }
                    },
                    {
                        expand: true,
                        cwd: 'app/components/font-awesome/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/bootstrap/dist/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/ace/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/angular-tree-control/images/',
                        src: ['*'],
                        dest: 'dist/images'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/chosen/',
                        src: ['*.png'],
                        dest: 'dist/css'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/ace-builds/',
                        src: ['src-min-noconflict/worker-json.js'],
                        flatten: true,
                        dest: 'dist/'
                    }
                ]
            }
        },
        html2js: {
            partials: {
                src: ['app/partials/**/*.html'],
                dest: 'tmp/js/templates.js',
                module: 'templates',
                options: {
                    base: 'app/'
                }
            }
        },
        htmlrefs: {
            dist: {
                src: 'tmp/index.html',
                dest: 'tmp/'
            },
            options: {
                buildNumber: '<%= buildNumber %>'
            }
        },
        jshint: {
            files: ['app/js/**/*.js', '!app/components/**'],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        less: {
            prod: {
                options: {
                    compress: true,
                    yuicompress: true
                },
                files: {
                    'tmp/css/styles.css': 'tmp/less/build.less'
                }
            }
        },
        lesslint: {
            src: ['app/less/**.less']
        },
        manifest: {
            build: {
                options: {
                    basePath: 'dist/',
                    timestamp: true
                },
                src: ['**/**.**'],
                dest: 'dist/manifest.appcache'
            }
        },
        'regex-replace': {
            strict: {
                src: ['tmp/js/scripts.js'],
                actions: [
                    {
                        name: 'use strict',
                        search: '\\\'use strict\\\';',
                        replace: '',
                        flags: 'gmi'
                    }
                ]
            },
            manifest: {
                src: ['tmp/index.html'],
                actions: [
                    {
                        name: 'manifest',
                        search: '<html>',
                        replace: '<html manifest="manifest.appcache">'
                    }
                ]
            },
            templates: {
                src: ['tmp/js/scripts.js'],
                actions: [
                    {
                        name: 'templates',
                        search: /kylin\', \[/,
                        replace: 'kylin\', [\'templates\',',
                        flags: 'gmi'
                    }
                ]
            }
        },
        uglify: {
            app: {
                options: {
                    mangle: false
                },
                files: {
                    'tmp/js/scripts.js': 'tmp/js/scripts.js'
                }
            }
        },
        karma: {
            unit: {
                configFile: 'karma.unit.conf.js'
            },
            e2e: {
                configFile: 'karma.e2e.conf.js'
            }
        }
    });

    // Additional task plugins
    grunt.loadNpmTasks('grunt-contrib');
    grunt.loadNpmTasks('grunt-conventional-changelog');
    grunt.loadNpmTasks('grunt-bump');
    grunt.loadNpmTasks('grunt-html2js');
    grunt.loadNpmTasks('grunt-htmlrefs');
    grunt.loadNpmTasks('grunt-lesslint');
    grunt.loadNpmTasks('grunt-manifest');
    grunt.loadNpmTasks('grunt-regex-replace');
    grunt.loadNpmTasks('grunt-karma');

    grunt.registerTask('test', ['jshint', 'lesslint']);

    grunt.registerTask('prod', [
//    'test',
        'clean:build',
        'copy:tmp',
        'html2js',
        'concat:app',
        'regex-replace:strict',
        'regex-replace:templates',
        'uglify',
        'regex-replace:manifest',
        'concat:components',
        'less',
        'concat:css',
        'htmlrefs',
        'copy:build',
        'clean:tmp',
        'manifest',
        'changelog'
    ]);

    grunt.registerTask('dev', [
//    'test',
        'clean:build',
        'copy:tmp',
        'html2js',
        'concat:app',
        'regex-replace:strict',
        'regex-replace:templates',
//        'uglify',
        'regex-replace:manifest',
        'concat:components',
        'less',
        'concat:css',
        'htmlrefs',
        'copy:build',
        'clean:tmp',
        'manifest',
        'changelog'
    ]);
};
<commit_msg>fix config.json missing issue<commit_after>'use strict';

module.exports = function (grunt) {

    // Project configuration.
    grunt.config.init({
        buildEnv: grunt.option('buildEnv') || 'prod',
        buildNumber: grunt.option('buildNumber') || '0',
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readJSON('grunt.json'),
        bump: {
            options: {
                files: '<%= config.bump.options.files %>',
                commit: true,
                commitFiles: '<%= config.bump.options.commitFiles %>',
                commitMessage: 'Release v%VERSION%',
                createTag: false,
                tagName: 'v%VERSION%',
                tagMessage: 'Version %VERSION%',
                push: false
            }
        },
        changelog: {
            options: {
                dest: 'CHANGELOG.md',
                prepend: true
            }
        },
        clean: {
            build: ['dist/', 'tmp/'],
            tmp: ['tmp/']
        },
        concat: {
            app: {
                src: [
                    'tmp/js/templates.js',
                    'app/js/**/*.js'
                ],
                dest: 'tmp/js/scripts.js'
            },
            components: '<%= config.concat.components %>',
            css: '<%= config.concat.css %>'
        },
        copy: {
            tmp: {
                files: [
                    {
                        expand: true,
                        cwd: 'app/',
                        src: ['**'],
                        dest: 'tmp/'
                    }
                ]
            },
            build: {
                files: [
                    {
                        expand: true,
                        cwd: 'tmp/',
                        src: ['index.html', 'js/scripts.min.js', 'image/**', 'css/styles.min.css', 'routes.json'],
                        dest: 'dist/',
                        rename: function (desc, src) {
                            return desc + src.replace('min.', 'min.<%= buildNumber %>.')
                        }
                    },
                    {
                        expand: true,
                        cwd: 'tmp/',
                        src: ['htaccess.dist', 'config.json'],
                        dest: 'dist/',
                        rename: function (dest, src) {
                            console.log(src);
                            if (src == 'htaccess.dist') {
                                return dest + '.htaccess';
                            } else {
                                return dest + 'config.json';
                            }
                        }
                    },
                    {
                        expand: true,
                        cwd: 'app/components/font-awesome/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/bootstrap/dist/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/ace/',
                        src: ['fonts/*'],
                        dest: 'dist/'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/angular-tree-control/images/',
                        src: ['*'],
                        dest: 'dist/images'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/chosen/',
                        src: ['*.png'],
                        dest: 'dist/css'
                    },
                    {
                        expand: true,
                        cwd: 'app/components/ace-builds/',
                        src: ['src-min-noconflict/worker-json.js'],
                        flatten: true,
                        dest: 'dist/'
                    }
                ]
            }
        },
        html2js: {
            partials: {
                src: ['app/partials/**/*.html'],
                dest: 'tmp/js/templates.js',
                module: 'templates',
                options: {
                    base: 'app/'
                }
            }
        },
        htmlrefs: {
            dist: {
                src: 'tmp/index.html',
                dest: 'tmp/'
            },
            options: {
                buildNumber: '<%= buildNumber %>'
            }
        },
        jshint: {
            files: ['app/js/**/*.js', '!app/components/**'],
            options: {
                jshintrc: '.jshintrc'
            }
        },
        less: {
            prod: {
                options: {
                    compress: true,
                    yuicompress: true
                },
                files: {
                    'tmp/css/styles.css': 'tmp/less/build.less'
                }
            }
        },
        lesslint: {
            src: ['app/less/**.less']
        },
        manifest: {
            build: {
                options: {
                    basePath: 'dist/',
                    timestamp: true
                },
                src: ['**/**.**'],
                dest: 'dist/manifest.appcache'
            }
        },
        'regex-replace': {
            strict: {
                src: ['tmp/js/scripts.js'],
                actions: [
                    {
                        name: 'use strict',
                        search: '\\\'use strict\\\';',
                        replace: '',
                        flags: 'gmi'
                    }
                ]
            },
            manifest: {
                src: ['tmp/index.html'],
                actions: [
                    {
                        name: 'manifest',
                        search: '<html>',
                        replace: '<html manifest="manifest.appcache">'
                    }
                ]
            },
            templates: {
                src: ['tmp/js/scripts.js'],
                actions: [
                    {
                        name: 'templates',
                        search: /kylin\', \[/,
                        replace: 'kylin\', [\'templates\',',
                        flags: 'gmi'
                    }
                ]
            }
        },
        uglify: {
            app: {
                options: {
                    mangle: false
                },
                files: {
                    'tmp/js/scripts.js': 'tmp/js/scripts.js'
                }
            }
        },
        karma: {
            unit: {
                configFile: 'karma.unit.conf.js'
            },
            e2e: {
                configFile: 'karma.e2e.conf.js'
            }
        }
    });

    // Additional task plugins
    grunt.loadNpmTasks('grunt-contrib');
    grunt.loadNpmTasks('grunt-conventional-changelog');
    grunt.loadNpmTasks('grunt-bump');
    grunt.loadNpmTasks('grunt-html2js');
    grunt.loadNpmTasks('grunt-htmlrefs');
    grunt.loadNpmTasks('grunt-lesslint');
    grunt.loadNpmTasks('grunt-manifest');
    grunt.loadNpmTasks('grunt-regex-replace');
    grunt.loadNpmTasks('grunt-karma');

    grunt.registerTask('test', ['jshint', 'lesslint']);

    grunt.registerTask('prod', [
//    'test',
        'clean:build',
        'copy:tmp',
        'html2js',
        'concat:app',
        'regex-replace:strict',
        'regex-replace:templates',
        'uglify',
        'regex-replace:manifest',
        'concat:components',
        'less',
        'concat:css',
        'htmlrefs',
        'copy:build',
        'clean:tmp',
        'manifest',
        'changelog'
    ]);

    grunt.registerTask('dev', [
//    'test',
        'clean:build',
        'copy:tmp',
        'html2js',
        'concat:app',
        'regex-replace:strict',
        'regex-replace:templates',
//        'uglify',
        'regex-replace:manifest',
        'concat:components',
        'less',
        'concat:css',
        'htmlrefs',
        'copy:build',
        'clean:tmp',
        'manifest',
        'changelog'
    ]);
};
<|endoftext|>


<commit_before>import {encrypt} from '../../kms';

export default async (req, res, next) => {
  const plaintext = req.body.secret;
  const submittedKeys = (
    (req.body.keys instanceof Array) ?
      req.body.keys :
      [req.body.keys]
  ).filter((k) => !!k);

  if (!submittedKeys || submittedKeys.length === 0) {
    return next(new Error('KMS key(s) not selected'));
  }

  if (!plaintext) {
    return next(new Error('Plaintext not entered'));
  }

  // Deserialize form data
  const keys = submittedKeys.map((body) => {
    const invalidKeyError = new Error('Invalid key format');
    let parsedBody;

    try {
      parsedBody = JSON.parse(body);
    } catch (ex) {
      return invalidKeyError;
    }
    const {region, account, key} = parsedBody;

    console.log(parsedBody);

    if (!region || !account || !key) {
      return invalidKeyError;
    }

    return encrypt({
      KeyId: key,
      Plaintext: plaintext,
      region,
      account
    });
  });

  let data;

  try {
    data = await Promise.all(keys);
  } catch (err) {
    return next(err);
  }

  const resp = data.map((key) => {
    if (key instanceof Error) {
      return {
        status: 'ERROR',
        account: key.account || '',
        region: key.region || '',
        text: key.message
      };
    }

    return {
      status: 'SUCCESS',
      account: key.account,
      region: key.region,
      text: `$tokend:
  type: kms
  resource: /v1/kms/decrypt
  region: ${key.region}
  ciphertext: "${key.ciphertext}"
  datakey: "${key.datakey}"`
    };
  });

  return res.json(resp);
};
<commit_msg>Removed some debugging code.<commit_after>import {encrypt} from '../../kms';

export default async (req, res, next) => {
  const plaintext = req.body.secret;
  const submittedKeys = (
    (req.body.keys instanceof Array) ?
      req.body.keys :
      [req.body.keys]
  ).filter((k) => !!k);

  if (!submittedKeys || submittedKeys.length === 0) {
    return next(new Error('KMS key(s) not selected'));
  }

  if (!plaintext) {
    return next(new Error('Plaintext not entered'));
  }

  // Deserialize form data
  const keys = submittedKeys.map((body) => {
    const invalidKeyError = new Error('Invalid key format');
    let parsedBody;

    try {
      parsedBody = JSON.parse(body);
    } catch (ex) {
      return invalidKeyError;
    }
    const {region, account, key} = parsedBody;

    if (!region || !account || !key) {
      return invalidKeyError;
    }

    return encrypt({
      KeyId: key,
      Plaintext: plaintext,
      region,
      account
    });
  });

  let data;

  try {
    data = await Promise.all(keys);
  } catch (err) {
    return next(err);
  }

  const resp = data.map((key) => {
    if (key instanceof Error) {
      return {
        status: 'ERROR',
        account: key.account || '',
        region: key.region || '',
        text: key.message
      };
    }

    return {
      status: 'SUCCESS',
      account: key.account,
      region: key.region,
      text: `$tokend:
  type: kms
  resource: /v1/kms/decrypt
  region: ${key.region}
  ciphertext: "${key.ciphertext}"
  datakey: "${key.datakey}"`
    };
  });

  return res.json(resp);
};
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    util = require('util'),
    http = require('http'),
    io = require('socket.io-client'),
    sutil = require('./lib/client-utils'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort;

// base config for beagle
var config =  {
    dojoHost: 'http://ninjdojo.herokuapp.com',
    dojoPort:80,
    cloudHost: 'ninj.herokuapp.com',
    cloudPort: 80,
    devtty: "/dev/ttyO1",
    serialFile: "/etc/opt/ninja/serial.conf",
    tokenFile: "/etc/opt/ninja/token.conf",
    heartbeat_interval: 500
};
var nodedetails = {};
// commandline config overwrites
if (process.argv[2] == 'local') {
    config.dojoHost = process.argv[3];
    config.dojoPort = 3001;
    config.cloudHost = process.argv[3];
    config.cloudPort = 3000;
}
if (process.argv[4] == 'ftdi') {
    config.devtty = "/dev/tty.usbserial-AE01AAE3";
    config.serialFile = __dirname+"/serialnumber";
    config.tokenFile = __dirname+"/token";
}
console.log(config);
var tty = new SerialPort(config.devtty, {
    parser: serialport.parsers.readline("\n")
});

/*
*   Serial Port Stuff
*/
var activationRequiredState = function() {
    console.log("Entered activated requried state");
    changeLEDColor('purple');
    var cmdOptions = {
        host: config.cloudHost,
        port: config.cloudPort,
        path: '/a/block_activation/'+nodedetails.id,
        method: 'GET'
    };
    var longPollConn={};
    var killLongPoll = false;
    var longpoll = function(){
        if (longPollConn.end) longPollConn.end();
        longPollConn = http.get(cmdOptions, function (http_res) {
            http_res.on("data", function (data) {
                parseResponse(data);
            });
            http_res.on("end", function () {
                if (!killLongPoll) longpoll();
            });
            http_res.on("close", function () {
                if (!killLongPoll) longpoll();
            });
            http_res.on("error", function () {
                setTimeout(longpoll,5000);
            });

        }).on('error',function(err){
            console.log('Error in longpoll request: '+err);
            setTimeout(longpoll,5000)
        });
    };

    var parseResponse = function(data){
        // write it to the fs
        var data = sutil.getJSON(data) || false;
        if (data && data.token) {
            fs.writeFileSync(config.tokenFile, data.token, 'utf8');
            nodedetails["token"] = data.token;
            killLongPoll = true;
            longPollConn.end();
            activatedState();   // Set state to activated
        }
    }
    longpoll();
};

var changeLEDColor = function(color) {
    switch (color) {
        case 'purple':
            var hex = 'FF00FF';
        break;
        case 'green':
            var hex = '00FF00';
        break;
        case 'red':
            var hex = 'FF0000';
        break;
        case 'cyan':
            var hex = '00FFFF';
        break;
        default:
            var hex = '000000';
        break;
    }
    sutil.writeTTY(tty,'{"DEVICE":[{"G":"0","V":0,"D":1000,"DA":"'+hex+'"}]}');
}

var activatedState = function() {
    console.log("Entered activated state");

    var deviceMeta = {
        '0': {
            '5': {
                instant:true
            },
            '7': {
                instant:true
            },
            '10': {
                instant:true
            }
        }
    };

    var readings = {};

    tty.on('data',function(data){
        handleRawTtyData(data);
    });

    var handleRawTtyData = function(data) {
        var jsonTtyData = sutil.getJSON(data) || false;
        if (!jsonTtyData) return;
        var deviceDataPoints = jsonTtyData.DEVICE;
        if (!(deviceDataPoints instanceof Array)) return;
        // only keep latest reading per device between heartbeats
        for (var i=0; i<deviceDataPoints.length; i++) {
            var device = deviceDataPoints[i];
            // Log unknown device messages for JP
            if (device.D == 0) console.log("UNKNOWN DEVICE MSG: "+data);

            // Build the GUID
            device.GUID = buildDeviceGuid(device);

            // If we have meta data about the device, handle it.
            if (deviceHasMetaData(device)) {
                var meta = getDeviceMetaData(device);
                if (meta.instant) trySendInstantData(device);
            }

            // Add the devices data to the heartbeat container
            readings[deviceDataPoints[i].GUID] = deviceDataPoints[i];
        }
    };

    var buildDeviceGuid = function(device) {
        return nodedetails.id+'_'+device.G+'_'+device.V+'_'+device.D;
    };

    var deviceHasMetaData = function(device) {
        return (deviceMeta[device.V]&&deviceMeta[device.V][device.D]);
    };

    var getDeviceMetaData = function(device) {
        return deviceMeta[device.V][device.D];
    };

    var instantContainer = {};
    var trySendInstantData = function(deviceData) {
        if (instantContainer.hasOwnProperty(deviceData.GUID)) {
            // We've got stuff
            if (instantContainer[deviceData.GUID].DA!==deviceData.DA) {
                // It's different
                var newMsg = {
                    "NODE_ID":nodedetails.id,
                    "TOKEN": nodedetails.token,
                    "TIMESTAMP": new Date().getTime(),
                    "DEVICE":[deviceData]
                }
                socket.emit('data',JSON.stringify(newMsg));
            }
        }
        instantContainer[deviceData.GUID] = deviceData;
    };

    var getHeartbeat = function(){
        var hb = {
            "NODE_ID":nodedetails.id,
            "TOKEN": nodedetails.token,
            "TIMESTAMP": null,
            "DEVICE":[]
        };
        hb.TIMESTAMP = new Date().getTime();
        for (r in readings) {
            hb.DEVICE.unshift(readings[r]);
        }
        readings={};
        return JSON.stringify(hb);
    }
    var ioOpts = {
        'port':config.dojoPort,
        'transports':['xhr-polling'],
        'try multiple transports':false
    };
    var socket = io.connect(config.dojoHost,ioOpts);

    var sendIv;
    socket.on('connecting',function(transport){
        currentState="connecting";
        changeLEDColor('cyan');
    });
    socket.on('connect', function () {
        console.log("Connected");
        sendingData=true;
        setStateToOK();
        clearInterval(sendIv);
        sendIv = setInterval(function(){
            if (beatThrottle.isGoodToGo()) {
                socket.emit('heartbeat',getHeartbeat());
            }
        },config.heartbeat_interval);
    });

    socket.on('invalidToken',function() {
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    });

    socket.on('error',function() {
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
        sendingData=false;
        setStateToError();
    });

    socket.on('disconnect', function () {
        // socket disconnected
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
        sendingData=false;
        setStateToError();
    });
    /*
    socket.on('connect_failed', function () {

        // socket cannot reconnect. Keep trying
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
    });
    */
    var emptyBeats = 0;

    var beatThrottle = {
        isGoodToGo : function() {
            if (!sutil.isEmpty(readings) || beatThrottle.counter>beatThrottle.rate) {
                beatThrottle.counter = 0;
                return true;
            } else {
                beatThrottle.counter++;
                return false;
            }
        },
        rate : 10000/config.heartbeat_interval,
        counter: 0
    };
    /*
    *   Receiving Data
    */
    var cmdOptions = {
        host: config.cloudHost,
        port: config.cloudPort,
        path: '/commands/'+nodedetails.id,
        method: 'GET',
        headers: {
            'X-Ninja-Token':nodedetails["token"]
        }
    }
    var longPollConn={};
    var longpoll = function(){
        if (longPollConn.end) longPollConn.end();
        longPollConn = http.get(cmdOptions, function (http_res) {
            http_res.on("data", function (data) {
                receivingData=true;
                setStateToOK();
                executeCommand(data);
            });
            http_res.on("end", function () {
                longpoll();
            });
            http_res.on("close", function () {
                longpoll();
            });
            http_res.on("error", function () {
                setTimeout(longpoll,5000)
            });

        }).on('error',function(err){
            console.log('Error in longpoll request: '+err);
            setTimeout(longpoll,5000);
            receivingData=false;
            setStateToError();
        });
    }

    var executeCommand = function(data){
        var ds = sutil.getJSON(data).DEVICE;
        if (ds && ds.welcome) return;
        if (ds && ds.length>0) {
            for (d in ds) {
                delete ds[d].GUID;
                ds[d].G = ds[d].G.toString(); //TODO get JP to fix for 0
                sutil.writeTTY(tty,'{"DEVICE":['+JSON.stringify(ds[d])+']}');
            }
        } else {
            console.log(data.toString());
        }
    }
    longpoll();
};
// main() -- basically
var sendingData = false;
var receivingData = false;
var currentState;
var setStateToOK = function() {
    if (sendingData && receivingData && currentState!=='ok') {
        changeLEDColor('green');
        currentState="ok";
    }
};

var setStateToError = function() {
    currentState='error';
    changeLEDColor('red');
};
/*
var Inotify = require('inotify-plusplus'), // should be 'inotify++', but npm has issues with the ++
    inotify,
    directive,
    options;

inotify = Inotify.create(true); // stand-alone, persistent mode, runs until you hit ctrl+c
directive = (function() {
    // multiple events may fire at the same time
    return {
      create: function (ev) {
        // Send the device added message to the server here
        if(ev.name == 'v4l'){console.log(ev.name + " was added.");}
      },
      delete: function(ev) {
        // Send the device removed message to teh server here
        if(ev.name == 'v4l'){console.log(ev.name + " was removed.");}
      }
    };
}());
inotify.watch(directive, '/dev/');
*/
(function() {
    nodedetails["id"] = fs.readFileSync(config.serialFile).toString().replace(/\n/g,''); // TODO

    if (path.existsSync(config.tokenFile)) {
        // We're good to go
        nodedetails["token"] = fs.readFileSync(config.tokenFile).toString().replace(/\n/g,''); // TODO
        activatedState();
    }
    else {
        // Need to request a token
        activationRequiredState();
    }
})();<commit_msg>Better handling of state in LED<commit_after>var fs = require('fs'),
    path = require('path'),
    util = require('util'),
    http = require('http'),
    io = require('socket.io-client'),
    sutil = require('./lib/client-utils'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort;

// base config for beagle
var config =  {
    dojoHost: 'http://ninjdojo.herokuapp.com',
    dojoPort:80,
    cloudHost: 'ninj.herokuapp.com',
    cloudPort: 80,
    devtty: "/dev/ttyO1",
    serialFile: "/etc/opt/ninja/serial.conf",
    tokenFile: "/etc/opt/ninja/token.conf",
    heartbeat_interval: 500
};
var nodedetails = {};
// commandline config overwrites
if (process.argv[2] == 'local') {
    config.dojoHost = process.argv[3];
    config.dojoPort = 3001;
    config.cloudHost = process.argv[3];
    config.cloudPort = 3000;
}
if (process.argv[4] == 'ftdi') {
    config.devtty = "/dev/tty.usbserial-AE01AAE3";
    config.serialFile = __dirname+"/serialnumber";
    config.tokenFile = __dirname+"/token";
}
console.log(config);
var tty = new SerialPort(config.devtty, {
    parser: serialport.parsers.readline("\n")
});

/*
*   Serial Port Stuff
*/
var activationRequiredState = function() {
    console.log("Entered activated requried state");
    changeLEDColor('purple');
    var cmdOptions = {
        host: config.cloudHost,
        port: config.cloudPort,
        path: '/a/block_activation/'+nodedetails.id,
        method: 'GET'
    };
    var longPollConn={};
    var killLongPoll = false;
    var longpoll = function(){
        if (longPollConn.end) longPollConn.end();
        longPollConn = http.get(cmdOptions, function (http_res) {
            http_res.on("data", function (data) {
                parseResponse(data);
            });
            http_res.on("end", function () {
                if (!killLongPoll) longpoll();
            });
            http_res.on("close", function () {
                if (!killLongPoll) longpoll();
            });
            http_res.on("error", function () {
                setTimeout(longpoll,5000);
            });

        }).on('error',function(err){
            console.log('Error in longpoll request: '+err);
            setTimeout(longpoll,5000)
        });
    };

    var parseResponse = function(data){
        // write it to the fs
        var data = sutil.getJSON(data) || false;
        if (data && data.token) {
            fs.writeFileSync(config.tokenFile, data.token, 'utf8');
            nodedetails["token"] = data.token;
            killLongPoll = true;
            longPollConn.end();
            activatedState();   // Set state to activated
        }
    }
    longpoll();
};

var changeLEDColor = function(color) {
    switch (color) {
        case 'purple':
            var hex = 'FF00FF';
        break;
        case 'green':
            var hex = '00FF00';
        break;
        case 'red':
            var hex = 'FF0000';
        break;
        case 'cyan':
            var hex = '00FFFF';
        break;
        default:
            var hex = '000000';
        break;
    }
    sutil.writeTTY(tty,'{"DEVICE":[{"G":"0","V":0,"D":1000,"DA":"'+hex+'"}]}');
}

var activatedState = function() {
    console.log("Entered activated state");

    var deviceMeta = {
        '0': {
            '5': {
                instant:true
            },
            '7': {
                instant:true
            },
            '10': {
                instant:true
            }
        }
    };

    var readings = {};

    tty.on('data',function(data){
        handleRawTtyData(data);
    });

    var handleRawTtyData = function(data) {
        var jsonTtyData = sutil.getJSON(data) || false;
        if (!jsonTtyData) return;
        var deviceDataPoints = jsonTtyData.DEVICE;
        if (!(deviceDataPoints instanceof Array)) return;
        // only keep latest reading per device between heartbeats
        for (var i=0; i<deviceDataPoints.length; i++) {
            var device = deviceDataPoints[i];
            // Log unknown device messages for JP
            if (device.D == 0) console.log("UNKNOWN DEVICE MSG: "+data);

            // Build the GUID
            device.GUID = buildDeviceGuid(device);

            // If we have meta data about the device, handle it.
            if (deviceHasMetaData(device)) {
                var meta = getDeviceMetaData(device);
                if (meta.instant) trySendInstantData(device);
            }

            // Add the devices data to the heartbeat container
            readings[deviceDataPoints[i].GUID] = deviceDataPoints[i];
        }
    };

    var buildDeviceGuid = function(device) {
        return nodedetails.id+'_'+device.G+'_'+device.V+'_'+device.D;
    };

    var deviceHasMetaData = function(device) {
        return (deviceMeta[device.V]&&deviceMeta[device.V][device.D]);
    };

    var getDeviceMetaData = function(device) {
        return deviceMeta[device.V][device.D];
    };

    var instantContainer = {};
    var trySendInstantData = function(deviceData) {
        if (instantContainer.hasOwnProperty(deviceData.GUID)) {
            // We've got stuff
            if (instantContainer[deviceData.GUID].DA!==deviceData.DA) {
                // It's different
                var newMsg = {
                    "NODE_ID":nodedetails.id,
                    "TOKEN": nodedetails.token,
                    "TIMESTAMP": new Date().getTime(),
                    "DEVICE":[deviceData]
                }
                socket.emit('data',JSON.stringify(newMsg));
            }
        }
        instantContainer[deviceData.GUID] = deviceData;
    };

    var getHeartbeat = function(){
        var hb = {
            "NODE_ID":nodedetails.id,
            "TOKEN": nodedetails.token,
            "TIMESTAMP": null,
            "DEVICE":[]
        };
        hb.TIMESTAMP = new Date().getTime();
        for (r in readings) {
            hb.DEVICE.unshift(readings[r]);
        }
        readings={};
        return JSON.stringify(hb);
    }
    var ioOpts = {
        'port':config.dojoPort,
        'transports':['xhr-polling'],
        'try multiple transports':false
    };
    var socket = io.connect(config.dojoHost,ioOpts);

    var sendIv;
    socket.on('connecting',function(transport){
        currentState="connecting";
        sendingData=false;
        changeLEDColor('cyan');
    });
    socket.on('connect', function () {
        console.log("Connected");
        sendingData=true;
        setStateToOK();
        clearInterval(sendIv);
        sendIv = setInterval(function(){
            if (beatThrottle.isGoodToGo()) {
                socket.emit('heartbeat',getHeartbeat());
            }
        },config.heartbeat_interval);
    });

    socket.on('invalidToken',function() {
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    });

    socket.on('error',function() {
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
        sendingData=false;
        setStateToError();
    });

    socket.on('disconnect', function () {
        // socket disconnected
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
        sendingData=false;
        setStateToError();
    });
    socket.on('connect_failed', function () {
        sendingData=false;
        setStateToError();
        // socket cannot reconnect. Keep trying
        setTimeout(function () {
            socket = io.connect(config.dojoHost);
        }, 1000);
    });
    var emptyBeats = 0;

    var beatThrottle = {
        isGoodToGo : function() {
            if (!sutil.isEmpty(readings) || beatThrottle.counter>beatThrottle.rate) {
                beatThrottle.counter = 0;
                return true;
            } else {
                beatThrottle.counter++;
                return false;
            }
        },
        rate : 10000/config.heartbeat_interval,
        counter: 0
    };
    /*
    *   Receiving Data
    */
    var cmdOptions = {
        host: config.cloudHost,
        port: config.cloudPort,
        path: '/commands/'+nodedetails.id,
        method: 'GET',
        headers: {
            'X-Ninja-Token':nodedetails["token"]
        }
    }
    var longPollConn={};
    var longpoll = function(){
        if (longPollConn.end) longPollConn.end();
        longPollConn = http.get(cmdOptions, function (http_res) {
            http_res.on("data", function (data) {
                receivingData=true;
                setStateToOK();
                executeCommand(data);
            });
            http_res.on("end", function () {
                longpoll();
            });
            http_res.on("close", function () {
                longpoll();
            });
            http_res.on("error", function () {
                setTimeout(longpoll,5000)
            });

        }).on('error',function(err){
            console.log('Error in longpoll request: '+err);
            setTimeout(longpoll,5000);
            receivingData=false;
            setStateToError();
        });
    }

    var executeCommand = function(data){
        var data = sutil.getJSON(data);
        if (data && data.welcome) return;
        var ds = data.DEVICE;
        if (ds && ds.length>0) {
            for (d in ds) {
                delete ds[d].GUID;
                ds[d].G = ds[d].G.toString(); //TODO get JP to fix for 0
                sutil.writeTTY(tty,'{"DEVICE":['+JSON.stringify(ds[d])+']}');
            }
        } else {
            console.log(data.toString());
        }
    }
    longpoll();
};
// main() -- basically
var sendingData = false;
var receivingData = false;
var currentState;
var setStateToOK = function() {
    if (sendingData && receivingData && currentState!=='ok') {
        changeLEDColor('green');
        currentState="ok";
    }
};

var setStateToError = function() {
    currentState='error';
    changeLEDColor('red');
};
/*
var Inotify = require('inotify-plusplus'), // should be 'inotify++', but npm has issues with the ++
    inotify,
    directive,
    options;

inotify = Inotify.create(true); // stand-alone, persistent mode, runs until you hit ctrl+c
directive = (function() {
    // multiple events may fire at the same time
    return {
      create: function (ev) {
        // Send the device added message to the server here
        if(ev.name == 'v4l'){console.log(ev.name + " was added.");}
      },
      delete: function(ev) {
        // Send the device removed message to teh server here
        if(ev.name == 'v4l'){console.log(ev.name + " was removed.");}
      }
    };
}());
inotify.watch(directive, '/dev/');
*/
(function() {
    nodedetails["id"] = fs.readFileSync(config.serialFile).toString().replace(/\n/g,''); // TODO

    if (path.existsSync(config.tokenFile)) {
        // We're good to go
        nodedetails["token"] = fs.readFileSync(config.tokenFile).toString().replace(/\n/g,''); // TODO
        activatedState();
    }
    else {
        // Need to request a token
        activationRequiredState();
    }
})();<|endoftext|>


<commit_before>var fs      = require("fs");
var cheerio = require("cheerio");
var _       = require('underscore');
var moment  = require('moment');
var argv    = require('argv');
var net     = require('net');
var path    = require('path');
var repl    = require('repl');
var csv     = require('csv');
var wkhtmltopdf = require('wkhtmltopdf');

// Args
argv.option({
  name: 'input',
  short: 'i',
  type: 'string',
  description: 'Defines schema file to open',
  example: "'beardo.js --input=value' or 'beardo.js -i data/hours.json"
});

argv.option({
  name: 'format',
  short: 'f',
  type: 'csv,string',
  description: 'Defines output formats with html,cli',
  example: "'beardo.js --format=value' or 'beardo.js -f value1,value2'"
});

argv.option({
  name: 'output',
  short: 'o',
  type: 'string',
  description: 'Defines name to save output files as',
  example: "'beardo.js --save=value' or 'beardo.js -s January Invoice'"
});

argv.option({
  name: 'date',
  short: 'd',
  type: 'string',
  description: 'Returns only by current date (currently month only)',
  example: "'beardo.js --date=value' or 'beardo.js -d January or Jan or 01'"
});

argv.option({
  name: 'trim',
  short: 't',
  type: 'list,string',
  description: 'Trims output by a given string value declared in schema',
  example: "'beardo.js --trim=value' or 'beardo.js -t Client'"
});

var args = argv.run();

// File Manipulation
var SaveFile = require('./lib/save_file');

// Beardo
var Beardo = {};

Beardo.Date = {};

Beardo.Date.full = function(date) {

  var this_date = date.replace(/-/g,'');
  var filter_date = args.options.date.replace(/-/g, '');

  if (this_date >= filter_date) {
    //console.log('this date: 'this_date + ' is greater than filter date: ' + filter_date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.year_month = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by year_month ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.month_day = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by month_day ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.year = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by year ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.month = function(date) {

  var date_trim = args.options.date.toLowerCase();
  var date_full = moment(date).format('MMMM').toLowerCase();
  var date_abbr = moment(date).format('MMM').toLowerCase();
  var date_numb = moment(date).format('MM').toLowerCase();

  if (_.indexOf([date_full, date_abbr, date_numb], date_trim) > -1) {
    //console.log('date matches filter: ' + date_trim)
    return true;
  } else {
    return false;
  }
};

Beardo.Date.none = function(parts) {
  console.log('no date filtering performed');
};

Beardo.Trim = function(parts) {
  if (args.options.trim !== undefined) {
    var part = parts[3].trim();

    if (_.indexOf(args.options.trim, part) > -1) {
      //console.log('client: ' + part + ' matches: ' + args.options.trim);
      return true;
    } else {
      return false;
    }
  } else {
    return true;
  }
};

Beardo.summonUser = function(user_schema_file, callback){
  fs.exists(user_schema_file, function(exists) {
    if (exists) {
      var contents = fs.readFileSync(user_schema_file);
      var userSchema = JSON.parse(contents);
      var path = 'data/' + userSchema[0].resources[0].path;
      var schema = userSchema[0].resources[0].schema;
      var user = {};
      var rowCount = 0;

      fs.createReadStream(path)
        .pipe(csv.parse())
        .pipe(csv.transform(function(row) {
          // handle each row before the "end" or "error" stuff happens above
          if (rowCount >= 1){
            user = Beardo.murmurLineToSchema(row, schema);
          }
          rowCount++;
        })).on('finish', function() {
          return callback(user);
        }).on('error', function(error) {
          return callback(error);
        });

    } else {
      console.log('No user data found, continuing');
      return callback({'exists': exists})
    }

  });
};

Beardo.magickData = function(data, resource, outputs) {

  // Filter by Date / Type
  var date_filter = 'none';

  if (args.options.date !== undefined) {

    var is_date_full        = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
    var is_date_year_month  = /[0-9]{4}-[0-9]{2}/;
    var is_date_month_day   = /[0-9]{2}-[0-9]{2}/;
    var is_date_year        = /[0-9]{4}/;

    if (is_date_full.exec(args.options.date)) {
      date_filter = 'full';
    } else if (is_date_year_month.exec(args.options.date)) {
      date_filter = 'year_month';
    } else if (is_date_month_day.exec(args.options.date)) {
      date_filter = 'month_day';
    } else if (is_date_year.exec(args.options.date)) {
      date_filter = 'year';
    } else {
      date_filter = 'month';
    }

    console.log('filter date by: ' + date_filter);
  }

  // Make Totals
  var increment_output = function(item) {

    outputs.totals.hours += item.time;
    outputs.totals.money += (item.time * item.rate);

    // CLI format
    outputs.cli += item.date + '\t ' + item.time + ' \t' + item.client + ' \t' + item.description + '\n';

    // HTML format
    if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {
      outputs.html += '
\n';
      outputs.html += '   ' + item.date + '	\n';
      outputs.html += '   ' + item.time + '	\n';
      outputs.html += '    hrs	\n';
      outputs.html += '   ' + item.description + '	\n';
      outputs.html += '
\n';
    }
  };

  _.each(data, function(line, index){
    if (index !== 0){ // skip the first line
      var parts = line;
      // Filter Date & Trim
      var check_date = Beardo.Date[date_filter](parts[0]);
      var check_trim = Beardo.Trim(parts);

      if (_.indexOf([check_date, check_trim], false) === -1) {
        var item_output = Beardo.murmurLineToSchema(line, resource.schema);

        increment_output(item_output);
      }
    }
  });

  return outputs;
}

Beardo.murmurLineToSchema = function(parts, schema, fields) {
  // line is the line being processed,
  // schema is the schema provided in the resource,
  // fields is the fields required in the output, if blank it
  // defaults to everything in the schema.
  // var parts = line.split(',');
  var lineItem = {};

  _.each(schema.fields, function(field, index){
    if (fields === undefined || fields.indexOf(field.name) !== -1){
      var parsed;

      if (field.type === 'date') {
        parsed = moment(parts[index]).format('D MMM');
      }
      if (field.type === 'number'){
        parsed = parseFloat(parts[index]);
      }
      if (field.type === 'string'){
        parsed = parts[index].trim();
      }
      if (field.type === 'boolean'){
        parsed = parts[index];
        if (parsed === 'yes') parsed = true;
        else if (parsed === 'no') parse = false;
      }

      // For Output
      lineItem[field.name] = parsed;

    }
  });
  return lineItem;
};

Beardo.castToHTML = function(outputs, user){
  // Output HTML
  console.log(args.options.format);
  if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {

    // Get HTML Template
    var template_path = './templates/invoice.html';

    // Open
    fs.exists(template_path, function(exists) {
      if (exists) {

        console.log('Beardo loaded template');
        fs.stat(template_path, function(error, stats) {
          fs.open(template_path, "r", function(error, fd) {
            var buffer = new Buffer(stats.size);
            fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
              fs.close(fd);

              // Invoice Name
              var output_name = 'Beardo - ' + moment().format('D MMMM YYYY');
              if (args.options.output) {
                output_name = args.options.output;
              }

              var template_file = buffer.toString("utf8", 0, buffer.length);
              var template_html = _.template(template_file);

              var template_data = {
                generated_name: output_name,
                generated_date: moment().format('Do MMMM, YYYY'),
                hours_rows: outputs.html,
                hours_total: outputs.totals.hours,
                money_total: outputs.totals.money
              };

              if (user !== undefined){
                template_data.user = user;
              }

              var output_html = template_html(template_data);

              // Save HTML file
              if (_.indexOf(args.options.format, 'html') > -1) {
                var saveFile = new SaveFile(fs, 'output/' + output_name + '.html', output_html);
              }

              // Save PDF file
              Beardo.castHTMLToPDF(output_html, output_name);
            });
          });
        });
      }
    });
  }
}

Beardo.castHTMLToPDF = function(output_html, output_name){
  if (_.indexOf(args.options.format, 'pdf') > -1) {
    console.log('Saving as a PDF');
    wkhtmltopdf(output_html, {
      pageSize: 'letter',
      output: 'output/' + output_name + '.pdf'
    });
  }
}

// Load Data & Parse
Beardo.Twirl = function(path, resource) {

  var resource_file = path + '/' + resource.path;
  fs.exists(resource_file, function(exists) {
  	if (exists) {

  		console.log('Beardo is twirling some data');

      fs.stat(resource_file, function(error, stats) {
        fs.open(resource_file, "r", function(error, fd) {

          var buffer = new Buffer(stats.size);

          fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
            fs.close(fd);

            var data = buffer.toString("utf8", 0, buffer.length);
            // var lines = data.split("\n");
            // delete lines[0];

            // var hours = 0;

            // Totals (for tallying)
            // console.log(resource.schema.fields);
            var outputs = {
              totals: {
                hours: 0,
                money: 0.00,
              },
              cli: '',
              html: ''
            };

            // Loop Through Items

            csv.parse(data, function(err, data){
              if (err){
                console.log("Had a problem with the CSV File: ", err);
              }

              outputs = Beardo.magickData(data, resource, outputs);

              // FIXME: OUTPUT STUFF (Refactor out)
              console.log('-----------------------------------------------------------------------------');
              console.log('Output Formats: ' + args.options.format);
              console.log('-----------------------------------------------------------------------------');
              console.log(outputs.cli);
              console.log('-----------------------------------------------------------------------------');
              console.log('Total hours worked: ' + outputs.totals.hours);
              console.log('Total monies earned: $' + outputs.totals.money);

              // FIXME: This user data is not being used yet but will fix Issue #14 after some tiny work
              Beardo.summonUser('data/user.json', function(user_data) {
                if (user_data && user_data.error === undefined){
                  Beardo.castToHTML(outputs, user_data);
                } else {
                  Beardo.castToHTML(outputs, undefined);
                }
              })

              // Beardo.castToHTML(outputs);

            });
          });
        });
      });
  	}
  	else {
      console.log('awwww no data');
  	}
  });

};

// Load Schema
Beardo.Grow = function(schema_file) {

  fs.exists(schema_file, function(exists) {
  	if (exists) {

  		console.log('Splendid schema exists open it up');

      var path = require('path').dirname(schema_file);

      fs.stat(schema_file, function(error, stats) {
        fs.open(schema_file, "r", function(error, fd) {

          var buffer = new Buffer(stats.size);

          fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
            fs.close(fd);

            var json = buffer.toString("utf8", 0, buffer.length);
            var schema = JSON.parse(json);

            console.log('Get item from schema: ' + schema[0].name);

            // If Schema Contains Multiple
            _.each(schema[0].resources, function(resource, key) {

              console.log('Twirl resource: ' + resource.path);

              // Open Data
              Beardo.Twirl(path, resource);

            });

          });

        });
      });

    }
    else {
      console.log('awwww no schema');
    }

  });
};

// Start It Up
if (args.options.input !== undefined) {
  Beardo.Grow(args.options.input);
} else {
  console.log('404 No beard found \nAre you sure you specified an --input -i value');
}

module.exports = Beardo;
<commit_msg>Ensure user data exists<commit_after>var fs      = require("fs");
var cheerio = require("cheerio");
var _       = require('underscore');
var moment  = require('moment');
var argv    = require('argv');
var net     = require('net');
var path    = require('path');
var repl    = require('repl');
var csv     = require('csv');
var wkhtmltopdf = require('wkhtmltopdf');

// Args
argv.option({
  name: 'input',
  short: 'i',
  type: 'string',
  description: 'Defines schema file to open',
  example: "'beardo.js --input=value' or 'beardo.js -i data/hours.json"
});

argv.option({
  name: 'format',
  short: 'f',
  type: 'csv,string',
  description: 'Defines output formats with html,cli',
  example: "'beardo.js --format=value' or 'beardo.js -f value1,value2'"
});

argv.option({
  name: 'output',
  short: 'o',
  type: 'string',
  description: 'Defines name to save output files as',
  example: "'beardo.js --save=value' or 'beardo.js -s January Invoice'"
});

argv.option({
  name: 'date',
  short: 'd',
  type: 'string',
  description: 'Returns only by current date (currently month only)',
  example: "'beardo.js --date=value' or 'beardo.js -d January or Jan or 01'"
});

argv.option({
  name: 'trim',
  short: 't',
  type: 'list,string',
  description: 'Trims output by a given string value declared in schema',
  example: "'beardo.js --trim=value' or 'beardo.js -t Client'"
});

var args = argv.run();

// File Manipulation
var SaveFile = require('./lib/save_file');

// Beardo
var Beardo = {};

Beardo.Date = {};

Beardo.Date.full = function(date) {

  var this_date = date.replace(/-/g,'');
  var filter_date = args.options.date.replace(/-/g, '');

  if (this_date >= filter_date) {
    //console.log('this date: 'this_date + ' is greater than filter date: ' + filter_date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.year_month = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by year_month ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.month_day = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by month_day ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.year = function(date) {

  if (date.indexOf(args.options.date) > -1) {
    //console.log('date filter by year ' + date);
    return true;
  } else {
    return false;
  }
};

Beardo.Date.month = function(date) {

  var date_trim = args.options.date.toLowerCase();
  var date_full = moment(date).format('MMMM').toLowerCase();
  var date_abbr = moment(date).format('MMM').toLowerCase();
  var date_numb = moment(date).format('MM').toLowerCase();

  if (_.indexOf([date_full, date_abbr, date_numb], date_trim) > -1) {
    //console.log('date matches filter: ' + date_trim)
    return true;
  } else {
    return false;
  }
};

Beardo.Date.none = function(parts) {
  console.log('no date filtering performed');
};

Beardo.Trim = function(parts) {
  if (args.options.trim !== undefined) {
    var part = parts[3].trim();

    if (_.indexOf(args.options.trim, part) > -1) {
      //console.log('client: ' + part + ' matches: ' + args.options.trim);
      return true;
    } else {
      return false;
    }
  } else {
    return true;
  }
};

Beardo.summonUser = function(user_schema_file, callback){
  fs.exists(user_schema_file, function(exists) {
    if (exists) {
      var contents = fs.readFileSync(user_schema_file);
      var userSchema = JSON.parse(contents);
      var path = 'data/' + userSchema[0].resources[0].path;
      var schema = userSchema[0].resources[0].schema;
      var user = {};
      var rowCount = 0;

      fs.exists(path, function(exists) {
        if (exists) {
          fs.createReadStream(path)
            .pipe(csv.parse())
            .pipe(csv.transform(function(row) {
              // handle each row before the "end" or "error" stuff happens above
              if (rowCount >= 1){
                user = Beardo.murmurLineToSchema(row, schema);
              }
              rowCount++;
            })).on('error', function(error) {
              console.log('error', error);
              return callback(error);
            }).on('finish', function() {
              return callback(user);
            });
        } else {
          return callback({'exists': exists})
        }
      });
    } else {
      console.log('No user data found, continuing');
      return callback({'exists': exists})
    }

  });
};

Beardo.magickData = function(data, resource, outputs) {

  // Filter by Date / Type
  var date_filter = 'none';

  if (args.options.date !== undefined) {

    var is_date_full        = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
    var is_date_year_month  = /[0-9]{4}-[0-9]{2}/;
    var is_date_month_day   = /[0-9]{2}-[0-9]{2}/;
    var is_date_year        = /[0-9]{4}/;

    if (is_date_full.exec(args.options.date)) {
      date_filter = 'full';
    } else if (is_date_year_month.exec(args.options.date)) {
      date_filter = 'year_month';
    } else if (is_date_month_day.exec(args.options.date)) {
      date_filter = 'month_day';
    } else if (is_date_year.exec(args.options.date)) {
      date_filter = 'year';
    } else {
      date_filter = 'month';
    }

    console.log('filter date by: ' + date_filter);
  }

  // Make Totals
  var increment_output = function(item) {

    outputs.totals.hours += item.time;
    outputs.totals.money += (item.time * item.rate);

    // CLI format
    outputs.cli += item.date + '\t ' + item.time + ' \t' + item.client + ' \t' + item.description + '\n';

    // HTML format
    if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {
      outputs.html += '
\n';
      outputs.html += '   ' + item.date + '	\n';
      outputs.html += '   ' + item.time + '	\n';
      outputs.html += '    hrs	\n';
      outputs.html += '   ' + item.description + '	\n';
      outputs.html += '
\n';
    }
  };

  _.each(data, function(line, index){
    if (index !== 0){ // skip the first line
      var parts = line;
      // Filter Date & Trim
      var check_date = Beardo.Date[date_filter](parts[0]);
      var check_trim = Beardo.Trim(parts);

      if (_.indexOf([check_date, check_trim], false) === -1) {
        var item_output = Beardo.murmurLineToSchema(line, resource.schema);

        increment_output(item_output);
      }
    }
  });

  return outputs;
}

Beardo.murmurLineToSchema = function(parts, schema, fields) {
  // line is the line being processed,
  // schema is the schema provided in the resource,
  // fields is the fields required in the output, if blank it
  // defaults to everything in the schema.
  // var parts = line.split(',');
  var lineItem = {};

  _.each(schema.fields, function(field, index){
    if (fields === undefined || fields.indexOf(field.name) !== -1){
      var parsed;

      if (field.type === 'date') {
        parsed = moment(parts[index]).format('D MMM');
      }
      if (field.type === 'number'){
        parsed = parseFloat(parts[index]);
      }
      if (field.type === 'string'){
        parsed = parts[index].trim();
      }
      if (field.type === 'boolean'){
        parsed = parts[index];
        if (parsed === 'yes') parsed = true;
        else if (parsed === 'no') parse = false;
      }

      // For Output
      lineItem[field.name] = parsed;

    }
  });
  return lineItem;
};

Beardo.castToHTML = function(outputs, user){
  // Output HTML
  console.log(args.options.format);
  if (_.indexOf(args.options.format, 'html') > -1 || _.indexOf(args.options.format, 'pdf') > -1) {

    // Get HTML Template
    var template_path = './templates/invoice.html';

    // Open
    fs.exists(template_path, function(exists) {
      if (exists) {

        console.log('Beardo loaded template');
        fs.stat(template_path, function(error, stats) {
          fs.open(template_path, "r", function(error, fd) {
            var buffer = new Buffer(stats.size);
            fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
              fs.close(fd);

              // Invoice Name
              var output_name = 'Beardo - ' + moment().format('D MMMM YYYY');
              if (args.options.output) {
                output_name = args.options.output;
              }

              var template_file = buffer.toString("utf8", 0, buffer.length);
              var template_html = _.template(template_file);

              var template_data = {
                generated_name: output_name,
                generated_date: moment().format('Do MMMM, YYYY'),
                hours_rows: outputs.html,
                hours_total: outputs.totals.hours,
                money_total: outputs.totals.money
              };

              if (user !== undefined){
                template_data.user = user;
              }

              var output_html = template_html(template_data);

              // Save HTML file
              if (_.indexOf(args.options.format, 'html') > -1) {
                var saveFile = new SaveFile(fs, 'output/' + output_name + '.html', output_html);
              }

              // Save PDF file
              Beardo.castHTMLToPDF(output_html, output_name);
            });
          });
        });
      }
    });
  }
}

Beardo.castHTMLToPDF = function(output_html, output_name){
  if (_.indexOf(args.options.format, 'pdf') > -1) {
    console.log('Saving as a PDF');
    wkhtmltopdf(output_html, {
      pageSize: 'letter',
      output: 'output/' + output_name + '.pdf'
    });
  }
}

// Load Data & Parse
Beardo.Twirl = function(path, resource) {

  var resource_file = path + '/' + resource.path;
  fs.exists(resource_file, function(exists) {
  	if (exists) {

  		console.log('Beardo is twirling some data');

      fs.stat(resource_file, function(error, stats) {
        fs.open(resource_file, "r", function(error, fd) {

          var buffer = new Buffer(stats.size);

          fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
            fs.close(fd);

            var data = buffer.toString("utf8", 0, buffer.length);
            // var lines = data.split("\n");
            // delete lines[0];

            // var hours = 0;

            // Totals (for tallying)
            // console.log(resource.schema.fields);
            var outputs = {
              totals: {
                hours: 0,
                money: 0.00,
              },
              cli: '',
              html: ''
            };

            // Loop Through Items

            csv.parse(data, function(err, data){
              if (err){
                console.log("Had a problem with the CSV File: ", err);
              }

              outputs = Beardo.magickData(data, resource, outputs);

              // FIXME: OUTPUT STUFF (Refactor out)
              console.log('-----------------------------------------------------------------------------');
              console.log('Output Formats: ' + args.options.format);
              console.log('-----------------------------------------------------------------------------');
              console.log(outputs.cli);
              console.log('-----------------------------------------------------------------------------');
              console.log('Total hours worked: ' + outputs.totals.hours);
              console.log('Total monies earned: $' + outputs.totals.money);

              // FIXME: This user data is not being used yet but will fix Issue #14 after some tiny work
              Beardo.summonUser('data/user.json', function(user_data) {
                if (user_data && user_data.error === undefined){
                  Beardo.castToHTML(outputs, user_data);
                } else {
                  Beardo.castToHTML(outputs, undefined);
                }
              })

              // Beardo.castToHTML(outputs);

            });
          });
        });
      });
  	}
  	else {
      console.log('awwww no data');
  	}
  });

};

// Load Schema
Beardo.Grow = function(schema_file) {

  fs.exists(schema_file, function(exists) {
  	if (exists) {

  		console.log('Splendid schema exists open it up');

      var path = require('path').dirname(schema_file);

      fs.stat(schema_file, function(error, stats) {
        fs.open(schema_file, "r", function(error, fd) {

          var buffer = new Buffer(stats.size);

          fs.read(fd, buffer, 0, buffer.length, null, function(error, bytesRead, buffer) {
            fs.close(fd);

            var json = buffer.toString("utf8", 0, buffer.length);
            var schema = JSON.parse(json);

            console.log('Get item from schema: ' + schema[0].name);

            // If Schema Contains Multiple
            _.each(schema[0].resources, function(resource, key) {

              console.log('Twirl resource: ' + resource.path);

              // Open Data
              Beardo.Twirl(path, resource);

            });

          });

        });
      });

    }
    else {
      console.log('awwww no schema');
    }

  });
};

// Start It Up
if (args.options.input !== undefined) {
  Beardo.Grow(args.options.input);
} else {
  console.log('404 No beard found \nAre you sure you specified an --input -i value');
}

module.exports = Beardo;
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const program = require('commander');
const pkg = require('../package.json');
const minimist = require('minimist');
const cowsay = require('cowsay');
const Config = require('../src/config');

const Dumpinator = require('../src/dumpinator');

function msg() {
  console.log.apply(null, arguments); // eslint-disable-line no-console,prefer-rest-params
}

function handleResult(text, code) {
  code = code || 0;

  if (text) {
    msg(text);
  }

  process.exit(code);
}

function generalErrorHandler(err) {
  console.log(cowsay.think({ // eslint-disable-line no-console
    text: 'Shit, something went wrong!',
    e: 'oO',
    T: '',
    f: 'head-in'
  }));

  console.log(''); // eslint-disable-line no-console
  console.log(program.verbose ? err.message : err.stack || err.message); // eslint-disable-line no-console
  console.log(''); // eslint-disable-line no-console
}

process.on('uncaughtException', (err) => {
  generalErrorHandler(err);
  process.exit(1);
});

program.version(pkg.version);
program.option('-v, --verbose', 'be more verbose');

program
  .command('diff [left] [right]')
  .description('Compare the given routes')
  .option('-c, --config [config]', 'Set a custom config')
  .option('-H, --header [header]', 'Add a HTTP header to both sides')
  .option('-L, --header-left [headerLeft]', 'Add a HTTP header to left side')
  .option('-r, --rate [rateLimit]', 'Rate limit for concurrent requests')
  .option('-R, --header-right [headerRight]', 'Add a HTTP header to right side')
  .option('-t, --tag [tag]', 'Only include routes with this tag')
  .action((left, right, options) => {
    options = options || {};
    options.args = minimist(process.argv);
    const config = new Config();

    if (options.config) {
      if (left) {
        return handleResult('No arguments allowed when using "-c"!', 1);
      }
      config.parseOptions(options);
      config.load(options.config);
    } else if (!left) {
      config.parseOptions(options);
      config.load();
    } else {
      config.parseArguments(left, right, options);
    }

    const notify = Dumpinator.run(config);
    Dumpinator.report(notify);
    notify.on('finish', (allPassed) => {
      console.log(cowsay.think({ // eslint-disable-line no-console
        text: allPassed ? 'Fuck yeah, I\'m awesome!' : 'Dude, you did a fucking mistake!',
        e: 'oO',
        T: 'U'
      }));

      console.log(''); // eslint-disable-line no-console
    });

    notify.on('error', (err) => {
      generalErrorHandler(err);
    });
  });

program.parse(process.argv);

if (!process.argv.slice(2).length) {
  program.help();
}
<commit_msg>Wording :)<commit_after>#!/usr/bin/env node

'use strict';

const program = require('commander');
const pkg = require('../package.json');
const minimist = require('minimist');
const cowsay = require('cowsay');
const Config = require('../src/config');

const Dumpinator = require('../src/dumpinator');

function msg() {
  console.log.apply(null, arguments); // eslint-disable-line no-console,prefer-rest-params
}

function handleResult(text, code) {
  code = code || 0;

  if (text) {
    msg(text);
  }

  process.exit(code);
}

function generalErrorHandler(err) {
  console.log(cowsay.think({ // eslint-disable-line no-console
    text: 'Shit, something went wrong!',
    e: 'oO',
    T: '',
    f: 'head-in'
  }));

  console.log(''); // eslint-disable-line no-console
  console.log(program.verbose ? err.message : err.stack || err.message); // eslint-disable-line no-console
  console.log(''); // eslint-disable-line no-console
}

process.on('uncaughtException', (err) => {
  generalErrorHandler(err);
  process.exit(1);
});

program.version(pkg.version);
program.option('-v, --verbose', 'be more verbose');

program
  .command('diff [left] [right]')
  .description('Compare the given routes')
  .option('-c, --config [config]', 'Set a custom config')
  .option('-H, --header [header]', 'Add a HTTP header to both sides')
  .option('-L, --header-left [headerLeft]', 'Add a HTTP header to left side')
  .option('-r, --rate [rateLimit]', 'Rate limit for concurrent requests')
  .option('-R, --header-right [headerRight]', 'Add a HTTP header to right side')
  .option('-t, --tag [tag]', 'Only include routes with this tag')
  .action((left, right, options) => {
    options = options || {};
    options.args = minimist(process.argv);
    const config = new Config();

    if (options.config) {
      if (left) {
        return handleResult('No arguments allowed when using "-c"!', 1);
      }
      config.parseOptions(options);
      config.load(options.config);
    } else if (!left) {
      config.parseOptions(options);
      config.load();
    } else {
      config.parseArguments(left, right, options);
    }

    const notify = Dumpinator.run(config);
    Dumpinator.report(notify);
    notify.on('finish', (allPassed) => {
      console.log(cowsay.think({ // eslint-disable-line no-console
        text: allPassed ? 'Hell yeah, I\'m awesome!' : 'Geez, I fucked it up!',
        e: 'oO',
        T: 'U'
      }));

      console.log(''); // eslint-disable-line no-console
    });

    notify.on('error', (err) => {
      generalErrorHandler(err);
    });
  });

program.parse(process.argv);

if (!process.argv.slice(2).length) {
  program.help();
}
<|endoftext|>


<commit_before>let openapiUtil = require('../util/openapi');
let request = require('request');
let Action = require('./action');
let Response = require('./response');
let rssParser = require('rss-parser');
let zlib = require('zlib');
const ZLIB_OPTIONS = {
  flush: zlib.Z_SYNC_FLUSH,
  finishFlush: zlib.Z_SYNC_FLUSH
}

const BODY_METHODS = ['put', 'patch', 'post'];

const getActionFromOperation = module.exports = function(method, path, openapi, integration, modifyReq) {
  let op = openapi.paths[path][method];
  let params = op.parameters || [];
  let hasRequiredParam = !!params.filter(p => p.required).length;
  let inputSchema = {
    type: hasRequiredParam ? 'object' : ['object', 'null'],
    properties: {},
    additionalProperties: false,
    definitions: openapi.definitions,
  };
  let names = openapiUtil.getUniqueNames(params);
  params.forEach((param, idx) => {
    let name = names[idx];
    inputSchema.properties[name] = getSchemaFromParam(param);
    if (param.required) {
      inputSchema.required = inputSchema.required || [];
      inputSchema.required.push(name);
    }
  });
  let response = getDefaultResponse(op);
  let outputSchema = Object.assign({definitions: openapi.definitions}, response.schema);
  let actionSecurity = {};
  if (!op.security || op.security.length) {
    actionSecurity = integration.security;
  } else {
    actionSecurity[integration.id] = false;
  }
  return new Action({
    title: op.operationId || (method.toUpperCase() + ' ' + path),
    description: op.description || op.summary,
    inputSchema: params.length ? inputSchema : {},
    outputSchema: outputSchema,
    security: actionSecurity,
    ajv: integration.ajv,
    handler: function(input, ctx) {
      input = input || {};
      let account = ctx.accounts[integration.id];
      let scheme = openapiUtil.getBestScheme(openapi.schemes);
      if (!openapi.host && (!account || !account.host)) {
        throw new Error("The 'host' field must be specified in the " + integration.id + " account");
      }
      let url = (account && account.host) || (scheme + '://' + openapi.host);
      let reqOpts = {
        method,
        url,
        qs: {},
        qsStringifyOptions: {},
        headers: {},
        form: {},
        body: null,
        encoding: null,
      }
      if (openapi.basePath && openapi.basePath !== '/') reqOpts.url += openapi.basePath;
      reqOpts.url += path;

      let addParam = (loc, name, val) => {
        if (val === undefined) return;
        if (loc === 'query') reqOpts.qs[name] = val;
        else if (loc === 'header') reqOpts.headers[name] = val;
        else if (loc === 'path') reqOpts.url = reqOpts.url.replace('{' + name + '}', val);
        else if (loc === 'formData') reqOpts.form[name] = val;
        else if (loc === 'body') reqOpts.body = JSON.stringify(val);
      }
      let names = openapiUtil.getUniqueNames(params);
      params.forEach((param, idx) => {
        let val = input[names[idx]];
        if (param.in === 'query' && Array.isArray(val)) {
          if (param.collectionFormat === 'multi') {
            reqOpts.qsStringifyOptions.arrayFormat = 'repeat';
          } else {
            reqOpts.qsStringifyOptions.sep = openapiUtil.getCollectionFormatSeparator(param.collectionFormat);
          }
        }
        addParam(param.in, param.name, val);
      });

      let hasRefreshToken = false;
      let oauthDef = null;
      if (account) {
        for (let key in openapi.securityDefinitions || {}) {
          let def = openapi.securityDefinitions[key];
          if (def.type === 'basic' && account.username && account.password) {
            let details = account.username + ':' + account.password;
            addParam('header', 'Authorization', "Basic " + new Buffer(details, 'utf8').toString('base64'));
          } else if (def.type === 'apiKey' && account[key]) {
            addParam(def.in, def.name, account[key]);
          } else if (def.type === 'oauth2' && account.access_token) {
            hasRefreshToken = !!account.refresh_token;
            if (!oauthDef || oauthDef.flow === 'implicit') {
              oauthDef = def;
            }
            if (def.in) {
              addParam(def.in, def.name, account.access_token);
            } else {
              addParam('header', 'Authorization', "Bearer " + account.access_token);
            }
          }
        }
      }

      if (Object.keys(reqOpts.form).length === 0) delete reqOpts.form;

      let refreshOAuthToken = (callback) => {
        let form = {
          client_id: account.client_id,
          client_secret: account.client_secret,
          refresh_token: account.refresh_token,
          grant_type: 'refresh_token'
        };
        request.post({
          url: account.refresh_url || oauthDef.tokenUrl,
          headers: account.refresh_headers || {},
          json: true,
          form,
        }, (err, resp, body) => {
          if (err) return callback(err);
          if (resp.statusCode >= 300) return callback(new Error(resp.statusCode));
          account.access_token = body.access_token;
          account.refresh_token = body.refresh_token || account.refresh_token;
          Action.callOAuthRefreshCallbacks(account);
          addParam('header', 'Authorization', "Bearer " + body.access_token);
          callback();
        })
      }
      if (BODY_METHODS.indexOf(method) !== -1) {
        let consumes = op.consumes || ['application/json'];
        let cType = consumes.indexOf('application/json') === -1 ? consumes[0] : 'application/json';
        addParam('header', 'Content-Type', cType);
      }

      addParam('header', 'User-Agent', 'DataFire');

      let sendRequest = (resolve, reject, isRetry) => {
        request(reqOpts, (err, resp, body) => {
          if (err) {
            return reject(err);
          }
          if (body) {
            let encoding = resp.headers['content-encoding'];
            if (encoding === 'gzip') {
              body = zlib.gunzipSync(body, ZLIB_OPTIONS).toString('utf8');
            } else if (encoding === 'deflate') {
              body = zlib.inflateSync(body, ZLIB_OPTIONS).toString('utf8');
            } else {
              body = body.toString('utf8');
            }
          }
          if (!isRetry && resp.statusCode === 401 && hasRefreshToken) {
            refreshOAuthToken(err => {
              if (err) reject(new Response({statusCode: 401}));
              else sendRequest(resolve, reject, true);
            })
            return;
          } else if (resp.statusCode >= 300) {
            return reject(new Response({statusCode: resp.statusCode, body}));
          }
          let ctype = resp.headers['content-type'] || '';
          if (ctype.indexOf('application/json') !== -1) {
            body = JSON.parse(body);
            resolve(body);
          } else if (openapi.info['x-datafire'] && openapi.info['x-datafire'].type === 'rss') {
            rssParser.parseString(body, (err, feed) => {
              if (err) reject(err);
              else resolve(feed);
            })
          } else {
            resolve(body);
          }
        })
      }
      if (modifyReq) modifyReq(reqOpts, ctx);
      return new Promise(sendRequest);
    }
  });
}

const getSchemaFromParam = function(param) {
  if (param.in === 'body' && param.schema) return param.schema;
  let schema = {};
  schema.type = param.type === 'file' ? 'string' : param.type; // FIXME: handle file inputs
  openapiUtil.PARAM_SCHEMA_FIELDS.forEach(f => {
    if (param[f] !== undefined) schema[f] = param[f];
  })
  return schema;
}

const getDefaultResponse = function(op) {
  let keys = Object.keys(op.responses).sort();
  return op.responses[keys[0]];
}

<commit_msg>better handling for file inputs<commit_after>let openapiUtil = require('../util/openapi');
let request = require('request');
let Action = require('./action');
let Response = require('./response');
let rssParser = require('rss-parser');
let zlib = require('zlib');
const ZLIB_OPTIONS = {
  flush: zlib.Z_SYNC_FLUSH,
  finishFlush: zlib.Z_SYNC_FLUSH
}

const BODY_METHODS = ['put', 'patch', 'post'];

const getActionFromOperation = module.exports = function(method, path, openapi, integration, modifyReq) {
  let op = openapi.paths[path][method];
  let params = op.parameters || [];
  let hasRequiredParam = !!params.filter(p => p.required).length;
  let inputSchema = {
    type: hasRequiredParam ? 'object' : ['object', 'null'],
    properties: {},
    additionalProperties: false,
    definitions: openapi.definitions,
  };
  let names = openapiUtil.getUniqueNames(params);
  params.forEach((param, idx) => {
    let name = names[idx];
    inputSchema.properties[name] = getSchemaFromParam(param);
    if (param.required) {
      inputSchema.required = inputSchema.required || [];
      inputSchema.required.push(name);
    }
  });
  let response = getDefaultResponse(op);
  let outputSchema = Object.assign({definitions: openapi.definitions}, response.schema);
  let actionSecurity = {};
  if (!op.security || op.security.length) {
    actionSecurity = integration.security;
  } else {
    actionSecurity[integration.id] = false;
  }
  return new Action({
    title: op.operationId || (method.toUpperCase() + ' ' + path),
    description: op.description || op.summary,
    inputSchema: params.length ? inputSchema : {},
    outputSchema: outputSchema,
    security: actionSecurity,
    ajv: integration.ajv,
    handler: function(input, ctx) {
      input = input || {};
      let account = ctx.accounts[integration.id];
      let scheme = openapiUtil.getBestScheme(openapi.schemes);
      if (!openapi.host && (!account || !account.host)) {
        throw new Error("The 'host' field must be specified in the " + integration.id + " account");
      }
      let url = (account && account.host) || (scheme + '://' + openapi.host);
      let reqOpts = {
        method,
        url,
        qs: {},
        qsStringifyOptions: {},
        headers: {},
        formData: {},
        body: null,
        encoding: null,
      }
      if (openapi.basePath && openapi.basePath !== '/') reqOpts.url += openapi.basePath;
      reqOpts.url += path;
      let addParam = (loc, name, val, isFile) => {
        if (val === undefined) return;
        if (loc === 'query') {
          reqOpts.qs[name] = val;
        } else if (loc === 'header') {
          reqOpts.headers[name] = val;
        } else if (loc === 'path') {
          reqOpts.url = reqOpts.url.replace('{' + name + '}', val);
        } else if (loc === 'body') {
          reqOpts.body = JSON.stringify(val);
        } else if (loc === 'formData') {
          if (isFile) {
            reqOpts.formData[name] = {
              value: val,
              options: {
                filename: name,
              }
            }
          } else {
            reqOpts.formData[name] = val;
          }
        }
      }
      let names = openapiUtil.getUniqueNames(params);
      params.forEach((param, idx) => {
        let val = input[names[idx]];
        if (param.in === 'query' && Array.isArray(val)) {
          if (param.collectionFormat === 'multi') {
            reqOpts.qsStringifyOptions.arrayFormat = 'repeat';
          } else {
            reqOpts.qsStringifyOptions.sep = openapiUtil.getCollectionFormatSeparator(param.collectionFormat);
          }
        }
        addParam(param.in, param.name, val, param.type === 'file');
      });

      let hasRefreshToken = false;
      let oauthDef = null;
      if (account) {
        for (let key in openapi.securityDefinitions || {}) {
          let def = openapi.securityDefinitions[key];
          if (def.type === 'basic' && account.username && account.password) {
            let details = account.username + ':' + account.password;
            addParam('header', 'Authorization', "Basic " + new Buffer(details, 'utf8').toString('base64'));
          } else if (def.type === 'apiKey' && account[key]) {
            addParam(def.in, def.name, account[key]);
          } else if (def.type === 'oauth2' && account.access_token) {
            hasRefreshToken = !!account.refresh_token;
            if (!oauthDef || oauthDef.flow === 'implicit') {
              oauthDef = def;
            }
            if (def.in) {
              addParam(def.in, def.name, account.access_token);
            } else {
              addParam('header', 'Authorization', "Bearer " + account.access_token);
            }
          }
        }
      }

      if (Object.keys(reqOpts.formData).length === 0) delete reqOpts.formData;

      let refreshOAuthToken = (callback) => {
        let form = {
          client_id: account.client_id,
          client_secret: account.client_secret,
          refresh_token: account.refresh_token,
          grant_type: 'refresh_token'
        };
        request.post({
          url: account.refresh_url || oauthDef.tokenUrl,
          headers: account.refresh_headers || {},
          json: true,
          form,
        }, (err, resp, body) => {
          if (err) return callback(err);
          if (resp.statusCode >= 300) return callback(new Error(resp.statusCode));
          account.access_token = body.access_token;
          account.refresh_token = body.refresh_token || account.refresh_token;
          Action.callOAuthRefreshCallbacks(account);
          addParam('header', 'Authorization', "Bearer " + body.access_token);
          callback();
        })
      }
      if (BODY_METHODS.indexOf(method) !== -1) {
        let consumes = op.consumes || ['application/json'];
        let cType = consumes.indexOf('application/json') === -1 ? consumes[0] : 'application/json';
        addParam('header', 'Content-Type', cType);
      }

      addParam('header', 'User-Agent', 'DataFire');

      let sendRequest = (resolve, reject, isRetry) => {
        request(reqOpts, (err, resp, body) => {
          if (err) {
            return reject(err);
          }
          if (body) {
            let encoding = resp.headers['content-encoding'];
            if (encoding === 'gzip') {
              body = zlib.gunzipSync(body, ZLIB_OPTIONS).toString('utf8');
            } else if (encoding === 'deflate') {
              body = zlib.inflateSync(body, ZLIB_OPTIONS).toString('utf8');
            } else {
              body = body.toString('utf8');
            }
          }
          if (!isRetry && resp.statusCode === 401 && hasRefreshToken) {
            refreshOAuthToken(err => {
              if (err) reject(new Response({statusCode: 401}));
              else sendRequest(resolve, reject, true);
            })
            return;
          } else if (resp.statusCode >= 300) {
            return reject(new Response({statusCode: resp.statusCode, body}));
          }
          let ctype = resp.headers['content-type'] || '';
          if (ctype.indexOf('application/json') !== -1) {
            body = JSON.parse(body);
            resolve(body);
          } else if (openapi.info['x-datafire'] && openapi.info['x-datafire'].type === 'rss') {
            rssParser.parseString(body, (err, feed) => {
              if (err) reject(err);
              else resolve(feed);
            })
          } else {
            resolve(body);
          }
        })
      }
      if (modifyReq) modifyReq(reqOpts, ctx);
      return new Promise(sendRequest);
    }
  });
}

const getSchemaFromParam = function(param) {
  if (param.in === 'body' && param.schema) return param.schema;
  let schema = {};
  schema.type = param.type === 'file' ? 'string' : param.type;
  openapiUtil.PARAM_SCHEMA_FIELDS.forEach(f => {
    if (param[f] !== undefined) schema[f] = param[f];
  })
  return schema;
}

const getDefaultResponse = function(op) {
  let keys = Object.keys(op.responses).sort();
  return op.responses[keys[0]];
}

<|endoftext|>