<commit_before>"use strict";
var KeyCodes = {
  altKey: 18,
  backspace: 8,
  ctrlKey: 17,
  deleteKey: 46,
  enter: 13,
  esc: 27,
  f1: 112,
  f12: 123,
  left: 37,
  metaKey: 91,
  right: 39,
  shiftKey: 16,
  space: 32,
  tab: 9
},
KeyboardUtils = {
  init: function(onMac) {
    this.init = null;
    if (!onMac) { return; }
    this.onMac = true;
    this.correctionMap = {};
    this.correctionMap.__proto__ = null;
  },
  onMac: false,
  keyNames: {
    33: "pageup",
    34: "pagedown",
    35: "end",
    36: "home",
    37: "left",
    38: "up",
    39: "right",
    40: "down",
    112: "f1",
    113: "f2",
    114: "f3",
    115: "f4",
    116: "f5",
    117: "f6",
    118: "f7",
    119: "f8",
    120: "f9",
    121: "f10",
    122: "f11",
    123: "f12"
  },
  correctionMap: {
    "BA": ";:",
    "BB": "=+",
    "BC": ",<",
    "BD": "-_",
    "BE": ".>",
    "BF": "/?",
    "C0": "`~",
    "DB": "[{",
    "DC": "\|",
    "DD": "]}",
    "DE": "'\""
  },
  getKeyName: function(event) {
    var keyChar;
    if (keyChar = this.keyNames[event.keyCode]) {
      return event.shiftKey ? keyChar.toUpperCase() : keyChar;
    }
    return "";
  },
  getKeyChar: function(event) {
    var keyIdentifier = event.keyIdentifier;
    if (! keyIdentifier.startsWith("U+")) {
      return this.getKeyName(event);
    }
    if (keyIdentifier = this.correctionMap[keyIdentifier.substring(4)]) {
      return keyIdentifier[event.shiftKey ? 1 : 0];
    }
    keyIdentifier = String.fromCharCode("0x" + event.keyIdentifier.substring(2));
    // It is too hard to handle a space char at background.
    return (keyIdentifier === " ") ? (event.shiftKey ? "SPACE" : "space")
      : event.shiftKey ? keyIdentifier.toUpperCase() : keyIdentifier.toLowerCase();
  },
  isPlain: function(event) {
    return !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey);
  }
};
KeyCodes.__proto__ = null;
KeyboardUtils.__proto__ = null;
KeyboardUtils.keyNames.__proto__ = null;
KeyboardUtils.correctionMap.__proto__ = null;
<commit_msg>getKeyChar: explicitly pass an integer to String.fromCharCode<commit_after>"use strict";
var KeyCodes = {
  altKey: 18,
  backspace: 8,
  ctrlKey: 17,
  deleteKey: 46,
  down: 40,
  enter: 13,
  esc: 27,
  f1: 112,
  f12: 123,
  left: 37,
  metaKey: 91,
  right: 39,
  shiftKey: 16,
  space: 32,
  tab: 9,
  up: 38
},
KeyboardUtils = {
  init: function(onMac) {
    this.init = null;
    if (!onMac) { return; }
    this.onMac = true;
    this.correctionMap = {};
    this.correctionMap.__proto__ = null;
  },
  onMac: false,
  keyNames: {
    33: "pageup",
    34: "pagedown",
    35: "end",
    36: "home",
    37: "left",
    38: "up",
    39: "right",
    40: "down",
    112: "f1",
    113: "f2",
    114: "f3",
    115: "f4",
    116: "f5",
    117: "f6",
    118: "f7",
    119: "f8",
    120: "f9",
    121: "f10",
    122: "f11",
    123: "f12"
  },
  correctionMap: {
    "BA": ";:",
    "BB": "=+",
    "BC": ",<",
    "BD": "-_",
    "BE": ".>",
    "BF": "/?",
    "C0": "`~",
    "DB": "[{",
    "DC": "\|",
    "DD": "]}",
    "DE": "'\""
  },
  getKeyName: function(event) {
    var keyChar;
    if (keyChar = this.keyNames[event.keyCode]) {
      return event.shiftKey ? keyChar.toUpperCase() : keyChar;
    }
    return "";
  },
  getKeyChar: function(event) {
    var keyIdentifier = event.keyIdentifier;
    if (! keyIdentifier.startsWith("U+")) {
      return this.getKeyName(event);
    }
    if (keyIdentifier = this.correctionMap[keyIdentifier.substring(4)]) {
      return keyIdentifier[event.shiftKey ? 1 : 0];
    }
    // TODO: .keyIdentifier ? .keyCode
    keyIdentifier = String.fromCharCode(parseInt(event.keyIdentifier.substring(2), 16));
    // It is too hard to handle a space char at background.
    return (keyIdentifier === " ") ? (event.shiftKey ? "SPACE" : "space")
      : event.shiftKey ? keyIdentifier.toUpperCase() : keyIdentifier.toLowerCase();
  },
  isPlain: function(event) {
    return !(event.shiftKey || event.ctrlKey || event.altKey || event.metaKey);
  }
};
KeyCodes.__proto__ = null;
KeyboardUtils.__proto__ = null;
KeyboardUtils.keyNames.__proto__ = null;
KeyboardUtils.correctionMap.__proto__ = null;
<|endoftext|>


<commit_before>var _ = require('lodash');

var alias = {
    'PORT': 'CODEBOX_PORT'
}

var parseEnv = function (env, parent, prefix) {
    var k, v, envVar, parsedEnv;

    if (!parent) {
        parent = {};
    }

    for(k in parent) {
        v = parent[k];

        envVar = prefix? prefix + '_': '';
        envVar += k.toUpperCase();

        if (_.isObject(v) && !_.isArray(v) && !_.isFunction(v)) {
            parseEnv(env, v, envVar);
        }
        else {
            if (envVar in env) {
                if (_.isArray(v) && (v.length == 0 || _.isString(v[0]))) {
                    parent[k] = _.compact(env[envVar].split(","));
                } else if (_.isNumber(v)) {
            		parent[k] = parseInt(env[envVar]);
            	} else if (_.isBoolean(v)) {
            		parent[k] = (env[envVar] == "false"? false : true);
            	} else {
            		parent[k] = env[envVar]
            	}
            }
        }
    }

    return parent;
};

// Extend configuration with environment variables
module.exports = function(options) {
    var env = _.clone(process.env);
    _.each(alias, function(to, from) {
        if (env[from]) env[to] = env[from] || env[to];
    });

    return parseEnv(env, options, 'CODEBOX');
};<commit_msg>Use __ as dots in envs<commit_after>var _ = require('lodash');

var alias = {
    'PORT': 'CODEBOX_PORT'
}

var parseEnv = function (env, parent, prefix) {
    var k, v, envVar, parsedEnv;

    if (!parent) {
        parent = {};
    }

    for(k in parent) {
        v = parent[k];

        envVar = prefix? prefix + '_': '';
        envVar += k.toUpperCase();

        if (_.isObject(v) && !_.isArray(v) && !_.isFunction(v)) {
            parseEnv(env, v, envVar);
        }
        else {
            envVar = envVar.replace(/\./g, '__');
            if (envVar in env) {
                if (_.isArray(v) && (v.length == 0 || _.isString(v[0]))) {
                    parent[k] = _.compact(env[envVar].split(","));
                } else if (_.isNumber(v)) {
            		parent[k] = parseInt(env[envVar]);
            	} else if (_.isBoolean(v)) {
            		parent[k] = (env[envVar] == "false"? false : true);
            	} else {
            		parent[k] = env[envVar]
            	}
            }
        }
    }

    return parent;
};

// Extend configuration with environment variables
module.exports = function(options) {
    var env = _.clone(process.env);
    _.each(alias, function(to, from) {
        if (env[from]) env[to] = env[from] || env[to];
    });
    return parseEnv(env, options, 'CODEBOX');
};<|endoftext|>


<commit_before>define(["sugar-web/env"], function (env) {

    'use strict';

    describe("environment", function () {

        it("should not return undefined", function () {

            env.getEnvironment(function (error, environment) {
                expect(environment.bundleId).not.toBeUndefined();
                expect(environment.activityId).not.toBeUndefined();
                expect(environment.activityName).not.toBeUndefined();
            });
        });

        it("should return objectId provided from the sugar's environment", function () {
            var environment = {
                objectId: "objectId"
            };
            spyOn(env, "getEnvironment").andCallFake(function (callback) {
                setTimeout(function () {
                    callback(null, environment);
                }, 5000);
            });
            var expected_objectId;

            runs(function () {
                env.getObjectId(function (objectId) {
                    expected_objectId = objectId;
                });
            });

            waitsFor(function () {
                return expected_objectId !== undefined;
            }, "should return objectId");
        });
    });

    describe("standalone mode", function () {

        it("should return true if in standalone mode", function () {
            var noWebActivityURLScheme = "http:";
            spyOn(env, 'getURLScheme').andReturn(noWebActivityURLScheme);

            var isStandaloneMode = env.isStandalone();
            expect(isStandaloneMode).toBe(true);
        });

        it("should return false if not in standalone mode", function () {
            var webActivityURLScheme = "activity:";
            spyOn(env, 'getURLScheme').andReturn(webActivityURLScheme);

            var isStandaloneMode = env.isStandalone();
            expect(isStandaloneMode).toBe(false);
        });
    });

    describe("getEnvironment", function () {
        var sugarOrig;

        beforeEach(function () {
            sugarOrig = JSON.parse(JSON.stringify(window.top.sugar));
        });

        afterEach(function () {
            window.top.sugar = sugarOrig;
        });

        describe("in sugar mode", function () {

            beforeEach(function () {
                window.top.sugar = {};
            });

            describe("when env was already set", function () {

                it("should run callback with null error and env", function () {
                    var environment = {};
                    window.top.sugar = {
                        environment: environment
                    };
                    var callback = jasmine.createSpy();

                    runs(function () {
                        env.getEnvironment(callback);
                    });

                    waitsFor(function () {
                        return callback.wasCalled;
                    }, "callback should be executed");

                    runs(function () {
                        expect(callback).toHaveBeenCalledWith(
                            null, environment);
                    });
                });
            });

            describe("when env was not set, yet", function () {
                var sugar;

                beforeEach(function () {
                    sugar = window.top.sugar;
                    sugar.environment = undefined;
                });

                it("should set onEnvironmentSet handler", function () {
                    expect(sugar.onEnvironmentSet).toBeUndefined();
                    env.getEnvironment(function () {});
                    expect(sugar.onEnvironmentSet).not.toBeUndefined();
                });

                it("should run callback on EnvironmentSet event", function () {
                    expect(sugar.onEnvironmentSet).toBeUndefined();
                    var callback = jasmine.createSpy();
                    var expectedEnv = "env";

                    env.getEnvironment(callback);
                    sugar.environment = expectedEnv;
                    sugar.onEnvironmentSet();

                    expect(callback).toHaveBeenCalledWith(null, expectedEnv);
                });
            });
        });

        describe("in standalone mode", function () {

            //FIXME: Why do I need to set sugar on standalone mode???
            it("should set global sugar", function () {
                window.top.sugar = undefined;
                env.getEnvironment();
                expect(window.top.sugar).not.toBeUndefined();
            });

        });
    });
});
<commit_msg>better semantics, test: "should run in standalone mode"<commit_after>define(["sugar-web/env"], function (env) {

    'use strict';

    describe("environment", function () {

        it("should not return undefined", function () {

            env.getEnvironment(function (error, environment) {
                expect(environment.bundleId).not.toBeUndefined();
                expect(environment.activityId).not.toBeUndefined();
                expect(environment.activityName).not.toBeUndefined();
            });
        });

        it("should return objectId provided from the sugar's environment", function () {
            var environment = {
                objectId: "objectId"
            };
            spyOn(env, "getEnvironment").andCallFake(function (callback) {
                setTimeout(function () {
                    callback(null, environment);
                }, 5000);
            });
            var expected_objectId;

            runs(function () {
                env.getObjectId(function (objectId) {
                    expected_objectId = objectId;
                });
            });

            waitsFor(function () {
                return expected_objectId !== undefined;
            }, "should return objectId");
        });
    });

    describe("standalone mode", function () {

        it("should return true if in standalone mode", function () {
            var noWebActivityURLScheme = "http:";
            spyOn(env, 'getURLScheme').andReturn(noWebActivityURLScheme);

            var isStandaloneMode = env.isStandalone();
            expect(isStandaloneMode).toBe(true);
        });

        it("should return false if not in standalone mode", function () {
            var webActivityURLScheme = "activity:";
            spyOn(env, 'getURLScheme').andReturn(webActivityURLScheme);

            var isStandaloneMode = env.isStandalone();
            expect(isStandaloneMode).toBe(false);
        });
    });

    describe("getEnvironment", function () {
        var sugarOrig;

        beforeEach(function () {
            sugarOrig = JSON.parse(JSON.stringify(window.top.sugar));
        });

        afterEach(function () {
            window.top.sugar = sugarOrig;
        });

        describe("in sugar mode", function () {

            beforeEach(function () {
                window.top.sugar = {};
            });

            describe("when env was already set", function () {

                it("should run callback with null error and env", function () {
                    var environment = {};
                    window.top.sugar = {
                        environment: environment
                    };
                    var callback = jasmine.createSpy();

                    runs(function () {
                        env.getEnvironment(callback);
                    });

                    waitsFor(function () {
                        return callback.wasCalled;
                    }, "callback should be executed");

                    runs(function () {
                        expect(callback).toHaveBeenCalledWith(
                            null, environment);
                    });
                });
            });

            describe("when env was not set, yet", function () {
                var sugar;

                beforeEach(function () {
                    sugar = window.top.sugar;
                    sugar.environment = undefined;
                });

                it("should set onEnvironmentSet handler", function () {
                    expect(sugar.onEnvironmentSet).toBeUndefined();
                    env.getEnvironment(function () {});
                    expect(sugar.onEnvironmentSet).not.toBeUndefined();
                });

                it("should run callback on EnvironmentSet event", function () {
                    expect(sugar.onEnvironmentSet).toBeUndefined();
                    var callback = jasmine.createSpy();
                    var expectedEnv = "env";

                    env.getEnvironment(callback);
                    sugar.environment = expectedEnv;
                    sugar.onEnvironmentSet();

                    expect(callback).toHaveBeenCalledWith(null, expectedEnv);
                });
            });
        });

        it("should run in standalone mode", function () {
            window.top.sugar = undefined;
            expect(env.getEnvironment).not.toThrow();
        });
    });
});
<|endoftext|>


<commit_before>var should = require('should')
  , time = require('../')

describe('exports', function () {

  it('should be a function', function () {
    time.should.be.a('function')
  })

  it('should return itself when invoked', function () {
    var dummy = function () {}
    time(dummy).should.equal(time)
  })

  it('should add node-time extensions to the passed in function\'s prototype'
  , function () {
    var dummy = function () {}
      , proto = dummy.prototype

    proto.should.not.have.property('setTimezone')
    proto.should.not.have.property('getTimezone')
    proto.should.not.have.property('getTimezoneAbbr')
    time(dummy)
    proto.should.have.property('setTimezone')
    proto.should.have.property('getTimezone')
    proto.should.have.property('getTimezoneAbbr')

  })

  it('should throw if in invalid object is passed into it', function () {
    should.throws(time)
  })

  it('should have a "currentTimezone" property', function () {
    time.should.have.property('currentTimezone')
    time.currentTimezone.should.not.be.empty
  })

  describe('Date', function () {

    it('should have a "Date" property', function () {
      time.should.have.property('Date')
    })

    it('should *not* be the global "Date" object', function () {
      time.Date.should.not.equal(Date)
    })

    it('should already have the node-time extensions', function () {
      should.exist(time.Date.prototype.setTimezone)
      should.exist(time.Date.prototype.getTimezone)
      should.exist(time.Date.prototype.getTimezoneAbbr)
    })

    it('should have all the regular Date properties', function () {
      time.Date.should.have.property('now')
      time.Date.should.have.property('parse')
      time.Date.should.have.property('UTC')
    })

  })

})
<commit_msg>Update throws test case for new should.js syntax.<commit_after>var should = require('should')
  , time = require('../')

describe('exports', function () {

  it('should be a function', function () {
    time.should.be.a('function')
  })

  it('should return itself when invoked', function () {
    var dummy = function () {}
    time(dummy).should.equal(time)
  })

  it('should add node-time extensions to the passed in function\'s prototype'
  , function () {
    var dummy = function () {}
      , proto = dummy.prototype

    proto.should.not.have.property('setTimezone')
    proto.should.not.have.property('getTimezone')
    proto.should.not.have.property('getTimezoneAbbr')
    time(dummy)
    proto.should.have.property('setTimezone')
    proto.should.have.property('getTimezone')
    proto.should.have.property('getTimezoneAbbr')

  })

  it('should throw if in invalid object is passed into it', function () {
    time.should.throw()
  })

  it('should have a "currentTimezone" property', function () {
    time.should.have.property('currentTimezone')
    time.currentTimezone.should.not.be.empty
  })

  describe('Date', function () {

    it('should have a "Date" property', function () {
      time.should.have.property('Date')
    })

    it('should *not* be the global "Date" object', function () {
      time.Date.should.not.equal(Date)
    })

    it('should already have the node-time extensions', function () {
      should.exist(time.Date.prototype.setTimezone)
      should.exist(time.Date.prototype.getTimezone)
      should.exist(time.Date.prototype.getTimezoneAbbr)
    })

    it('should have all the regular Date properties', function () {
      time.Date.should.have.property('now')
      time.Date.should.have.property('parse')
      time.Date.should.have.property('UTC')
    })

  })

})
<|endoftext|>


<commit_before>const fs = require('./fs.js');
const File = require('./file.js');
const Plugins = require('./plugins.js');
const utils = require('../utils/utils.js');
const path = require('path');
const deepEqual = require('deep-equal');
const constants = require('../constants');
const {canonicalHost, defaultHost} = require('../utils/host');

var Config = function(options) {
  const self = this;
  this.env = options.env;
  this.blockchainConfig = {};
  this.contractsConfig  = {};
  this.pipelineConfig   = {};
  this.webServerConfig  = options.webServerConfig;
  this.chainTracker     = {};
  this.assetFiles = {};
  this.contractsFiles = [];
  this.configDir = options.configDir || 'config/';
  this.chainsFile = options.chainsFile || './chains.json';
  this.plugins = options.plugins;
  this.logger = options.logger;
  this.events = options.events;
  this.embarkConfig = {};
  this.context = options.context || [constants.contexts.any];

  self.events.setCommandHandler("config:contractsConfig", (cb) => {
    cb(self.contractsConfig);
  });

  self.events.setCommandHandler("config:contractsFiles", (cb) => {
    cb(self.contractsFiles);
  });

  // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api
  self.events.setCommandHandler("config:contractsFiles:add", (filename) => {
    self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
      callback(fs.readFileSync(filename).toString());
    }}));
  });
};

Config.prototype.loadConfigFiles = function(options) {
  var interceptLogs = options.interceptLogs;
  if (options.interceptLogs === undefined) {
    interceptLogs = true;
  }

  if (!fs.existsSync(options.embarkConfig)){
    this.logger.error(__('Cannot find file %s Please ensure you are running this command inside the Dapp folder', options.embarkConfig));
    process.exit(1);
  }

  this.embarkConfig = fs.readJSONSync(options.embarkConfig);
  this.embarkConfig.plugins = this.embarkConfig.plugins || {};

  this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context, env: this.env});
  this.plugins.loadPlugins();

  this.loadEmbarkConfigFile();
  this.loadBlockchainConfigFile();
  this.loadStorageConfigFile();
  this.loadCommunicationConfigFile();
  this.loadNameSystemConfigFile();
  this.loadPipelineConfigFile();
  this.loadContractsConfigFile();
  this.loadExternalContractsFiles();
  this.loadWebServerConfigFile();
  this.loadChainTrackerFile();
  this.loadPluginContractFiles();

  this._updateBlockchainCors();
};

Config.prototype.reloadConfig = function() {
  this.loadEmbarkConfigFile();
  this.loadBlockchainConfigFile();
  this.loadStorageConfigFile();
  this.loadCommunicationConfigFile();
  this.loadNameSystemConfigFile();
  this.loadPipelineConfigFile();
  this.loadContractsConfigFile();
  this.loadExternalContractsFiles();
  this.loadChainTrackerFile();

  this._updateBlockchainCors();
};

Config.prototype._updateBlockchainCors = function(){
  let blockchainConfig = this.blockchainConfig;
  let storageConfig = this.storageConfig;
  let webServerConfig = this.webServerConfig;
  let corsParts = [];

  if(webServerConfig && webServerConfig.enabled) {
    if(webServerConfig.host) corsParts.push(utils.buildUrlFromConfig(webServerConfig));
  }
  if(storageConfig && storageConfig.enabled) {
    // if getUrl is specified in the config, that needs to be included in cors
    // instead of the concatenated protocol://host:port
    if(storageConfig.upload.getUrl) {
      // remove /ipfs or /bzz: from getUrl if it's there
      let getUrlParts = storageConfig.upload.getUrl.split('/');
      getUrlParts = getUrlParts.slice(0, 3);
      let host = canonicalHost(getUrlParts[2].split(':')[0]);
      let port = getUrlParts[2].split(':')[1];
      getUrlParts[2] = port ? [host, port].join(':') : host;
      corsParts.push(getUrlParts.join('/'));
    }
    // use our modified getUrl or in case it wasn't specified, use a built url
    else{
      corsParts.push(utils.buildUrlFromConfig(storageConfig.upload));
    }
  }
  // add whisper cors
  if(this.communicationConfig && this.communicationConfig.enabled && this.communicationConfig.provider === 'whisper'){
    corsParts.push('embark');
  }

  let cors = corsParts.join(',');
  if(blockchainConfig.rpcCorsDomain === 'auto'){
    if(cors.length) blockchainConfig.rpcCorsDomain = cors;
    else blockchainConfig.rpcCorsDomain = '';
  }
  if(blockchainConfig.wsOrigins === 'auto'){
    if(cors.length) blockchainConfig.wsOrigins = cors;
    else blockchainConfig.wsOrigins = '';
  }
};

Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, enabledByDefault) {
  if (!configFilePath) {
    let configToReturn = defaultConfig['default'] || {};
    configToReturn.enabled = enabledByDefault || false;
    return configToReturn;
  }

  // due to embark.json; TODO: refactor this
  configFilePath = configFilePath.replace('.json','').replace('.js', '');
  if (!fs.existsSync(configFilePath + '.js') && !fs.existsSync(configFilePath + '.json')) {
    // TODO: remove this if
    if (this.logger) {
      this.logger.warn(__("no config file found at %s using default config", configFilePath));
    }
    return defaultConfig['default'] || {};
  }

  let config;
  if (fs.existsSync(configFilePath + '.js')) {
    delete require.cache[fs.dappPath(configFilePath + '.js')];
    config = require(fs.dappPath(configFilePath + '.js'));
  } else {
    config = fs.readJSONSync(configFilePath + '.json');
  }
  let configObject = utils.recursiveMerge(defaultConfig, config);

  if (env) {
    return utils.recursiveMerge(configObject['default'] || {}, configObject[env]);
  }
  return configObject;
};

Config.prototype._getFileOrOject = function(object, filePath, property) {
  if (typeof (this.configDir) === 'object') {
    return this.configDir[property];
  }
  return utils.joinPath(this.configDir, filePath);
};

Config.prototype.loadBlockchainConfigFile = function() {
  var configObject = {
    "default": {
      "enabled": true,
      "rpcCorsDomain": "auto",
      "wsOrigins": "auto"
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'blockchain', 'blockchain');

  this.blockchainConfig = this._mergeConfig(configFilePath, configObject, this.env, true);
  if (!configFilePath) {
    this.blockchainConfig.default = true;
  }
  if(!this.blockchainConfig.account && !this.blockchainConfig.isDev) {
    this.logger.warn(
      __('Account settings are needed for this chain.' +
         ' Please put a valid address and possibly a password in your blockchain config or use a dev chain.')
    );
  }
};

Config.prototype.loadContractsConfigFile = function() {
  var defaultVersions = {
    "web3": "1.0.0-beta",
    "solc": "0.4.17"
  };
  var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {});

  var configObject = {
    "default": {
      "versions": versions,
      "deployment": {
        "host": "localhost", "port": 8545, "type": "rpc"
      },
      "dappConnection": [
        "$WEB3",
        "localhost:8545"
      ],
      "gas": "auto",
      "contracts": {
      }
    }
  };

  var contractsConfigs = this.plugins.getPluginsProperty('contractsConfig', 'contractsConfigs');
  contractsConfigs.forEach(function(pluginConfig) {
    configObject = utils.recursiveMerge(configObject, pluginConfig);
  });

  let configFilePath = this._getFileOrOject(this.configDir, 'contracts', 'contracts');

  const newContractsConfig = this._mergeConfig(configFilePath, configObject, this.env);

  if (!deepEqual(newContractsConfig, this.contractsConfig)) {
    this.contractsConfig = newContractsConfig;
  }
};

Config.prototype.loadExternalContractsFiles = function() {
  let contracts = this.contractsConfig.contracts;
  for (let contractName in contracts) {
    let contract = contracts[contractName];
    if (!contract.file) {
      continue;
    }
    if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
      const fileObj = utils.getExternalContractUrl(contract.file);
      if (!fileObj) {
        return this.logger.error(__("HTTP contract file not found") + ": " + contract.file);
      }
      const localFile = fileObj.filePath;
      this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
    } else if (fs.existsSync(contract.file)) {
      this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file}));
    } else if (fs.existsSync(path.join('./node_modules/', contract.file))) {
      this.contractsFiles.push(new File({filename: path.join('./node_modules/', contract.file), type: File.types.dapp_file, basedir: '', path: path.join('./node_modules/', contract.file)}));
    } else {
      this.logger.error(__("contract file not found") + ": " + contract.file);
    }
  }
};

Config.prototype.loadStorageConfigFile = function() {
  var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {});

  var configObject = {
    "default": {
      "versions": versions,
      "enabled": true,
      "available_providers": ["ipfs", "swarm"],
      "ipfs_bin": "ipfs",
      "upload": {
        "provider": "ipfs",
        "protocol": "http",
        "host" : defaultHost,
        "port": 5001,
        "getUrl": "http://localhost:8080/ipfs/"
      },
      "dappConnection": [{"provider": "ipfs", "host": "localhost", "port": 5001, "getUrl": "http://localhost:8080/ipfs/"}]
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'storage', 'storage');

  this.storageConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadNameSystemConfigFile = function() {
  // todo: spec out names for registration in the file itself for a dev chain
  var configObject = {
    "default": {
      "available_providers": ["ens"],
      "provider": "ens",
      "enabled": true
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'namesystem', 'namesystem');

  this.namesystemConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadCommunicationConfigFile = function() {
  var configObject = {
    "default": {
      "enabled": true,
      "provider": "whisper",
      "available_providers": ["whisper"],
      "connection": {
        "host": defaultHost,
        "port": 8546,
        "type": "ws"
      }
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'communication', 'communication');

  this.communicationConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadWebServerConfigFile = function() {
  var configObject = {
    "enabled": true,
    "host": defaultHost,
    "port": 8000
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'webserver', 'webserver');

  let webServerConfig = this._mergeConfig(configFilePath, configObject, false);

  if (this.webServerConfig) {
    // cli falgs to `embark run` should override configFile and defaults (configObject)
    this.webServerConfig = utils.recursiveMerge(webServerConfig, this.webServerConfig);
  } else {
    this.webServerConfig = webServerConfig;
  }
};

Config.prototype.loadEmbarkConfigFile = function() {
  var configObject = {
    options: {
      solc: {
        "optimize": true,
        "optimize-runs": 200
      }
    }
  };

  this.embarkConfig = utils.recursiveMerge(configObject, this.embarkConfig);

  const contracts = this.embarkConfig.contracts;
  const newContractsFiles = this.loadFiles(contracts);
  if (!this.contractFiles || newContractsFiles.length !== this.contractFiles.length || !deepEqual(newContractsFiles, this.contractFiles)) {
    this.contractsFiles = this.contractsFiles.concat(newContractsFiles).filter((file, index, arr) => {
      return !arr.some((file2, index2) => {
        return file.filename === file2.filename && index < index2;
      });
    });
  }
  // determine contract 'root' directories
  this.contractDirectories = contracts.map((dir) => {
    return dir.split("**")[0];
  }).map((dir) => {
    return dir.split("*.")[0];
  });
  this.contractDirectories.push(constants.httpContractsDirectory);

  this.buildDir  = this.embarkConfig.buildDir;
  this.configDir = this.embarkConfig.config;
};

Config.prototype.loadPipelineConfigFile = function() {
  var assets = this.embarkConfig.app;
  for(var targetFile in assets) {
    this.assetFiles[targetFile] = this.loadFiles(assets[targetFile]);
  }
};

Config.prototype.loadChainTrackerFile = function() {
  if (!fs.existsSync(this.chainsFile)) {
    this.logger.info(this.chainsFile + ' ' + __('file not found, creating it...'));
    fs.writeJSONSync(this.chainsFile, {});
  }

  this.chainTracker = fs.readJSONSync(this.chainsFile);
};

function findMatchingExpression(filename, filesExpressions) {
  for (let fileExpression of filesExpressions) {
    var matchingFiles = utils.filesMatchingPattern(fileExpression);
    for (let matchFile of matchingFiles) {
      if (matchFile === filename) {
        return path.dirname(fileExpression).replace(/\*/g, '');
      }
    }
  }
  return path.dirname(filename);
}

Config.prototype.loadFiles = function(files) {
  var self = this;
  var originalFiles = utils.filesMatchingPattern(files);
  var readFiles = [];

  originalFiles.filter(function(file) {
    return (file[0] === '$' || file.indexOf('.') >= 0);
  }).filter(function(file) {
    let basedir = findMatchingExpression(file, files);
    readFiles.push(new File({filename: file, type: File.types.dapp_file, basedir: basedir, path: file}));
  });

  var filesFromPlugins = [];
  var filePlugins = self.plugins.getPluginsFor('pipelineFiles');
  filePlugins.forEach(function(plugin) {
    try {
      var fileObjects = plugin.runFilePipeline();
      for (var i=0; i < fileObjects.length; i++) {
        var fileObject = fileObjects[i];
        filesFromPlugins.push(fileObject);
      }
    }
    catch(err) {
      self.logger.error(err.message);
    }
  });
  filesFromPlugins.filter(function(file) {
    if ((file.intendedPath && utils.fileMatchesPattern(files, file.intendedPath)) || utils.fileMatchesPattern(files, file.file)) {
      readFiles.push(file);
    }
  });

  return readFiles;
};

// NOTE: this doesn't work for internal modules
Config.prototype.loadPluginContractFiles = function() {
  var self = this;

  var contractsPlugins = this.plugins.getPluginsFor('contractFiles');
  contractsPlugins.forEach(function(plugin) {
    plugin.contractsFiles.forEach(function(file) {
      var filename = file.replace('./','');
      self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
        callback(plugin.loadPluginFile(file));
      }}));
    });
  });
};

module.exports = Config;
<commit_msg>no warning in development<commit_after>const fs = require('./fs.js');
const File = require('./file.js');
const Plugins = require('./plugins.js');
const utils = require('../utils/utils.js');
const path = require('path');
const deepEqual = require('deep-equal');
const constants = require('../constants');
const {canonicalHost, defaultHost} = require('../utils/host');

var Config = function(options) {
  const self = this;
  this.env = options.env;
  this.blockchainConfig = {};
  this.contractsConfig  = {};
  this.pipelineConfig   = {};
  this.webServerConfig  = options.webServerConfig;
  this.chainTracker     = {};
  this.assetFiles = {};
  this.contractsFiles = [];
  this.configDir = options.configDir || 'config/';
  this.chainsFile = options.chainsFile || './chains.json';
  this.plugins = options.plugins;
  this.logger = options.logger;
  this.events = options.events;
  this.embarkConfig = {};
  this.context = options.context || [constants.contexts.any];

  self.events.setCommandHandler("config:contractsConfig", (cb) => {
    cb(self.contractsConfig);
  });

  self.events.setCommandHandler("config:contractsFiles", (cb) => {
    cb(self.contractsFiles);
  });

  // TODO: refactor this so reading the file can be done with a normal resolver or something that takes advantage of the plugin api
  self.events.setCommandHandler("config:contractsFiles:add", (filename) => {
    self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
      callback(fs.readFileSync(filename).toString());
    }}));
  });
};

Config.prototype.loadConfigFiles = function(options) {
  var interceptLogs = options.interceptLogs;
  if (options.interceptLogs === undefined) {
    interceptLogs = true;
  }

  if (!fs.existsSync(options.embarkConfig)){
    this.logger.error(__('Cannot find file %s Please ensure you are running this command inside the Dapp folder', options.embarkConfig));
    process.exit(1);
  }

  this.embarkConfig = fs.readJSONSync(options.embarkConfig);
  this.embarkConfig.plugins = this.embarkConfig.plugins || {};

  this.plugins = new Plugins({plugins: this.embarkConfig.plugins, logger: this.logger, interceptLogs: interceptLogs, events: this.events, config: this, context: this.context, env: this.env});
  this.plugins.loadPlugins();

  this.loadEmbarkConfigFile();
  this.loadBlockchainConfigFile();
  this.loadStorageConfigFile();
  this.loadCommunicationConfigFile();
  this.loadNameSystemConfigFile();
  this.loadPipelineConfigFile();
  this.loadContractsConfigFile();
  this.loadExternalContractsFiles();
  this.loadWebServerConfigFile();
  this.loadChainTrackerFile();
  this.loadPluginContractFiles();

  this._updateBlockchainCors();
};

Config.prototype.reloadConfig = function() {
  this.loadEmbarkConfigFile();
  this.loadBlockchainConfigFile();
  this.loadStorageConfigFile();
  this.loadCommunicationConfigFile();
  this.loadNameSystemConfigFile();
  this.loadPipelineConfigFile();
  this.loadContractsConfigFile();
  this.loadExternalContractsFiles();
  this.loadChainTrackerFile();

  this._updateBlockchainCors();
};

Config.prototype._updateBlockchainCors = function(){
  let blockchainConfig = this.blockchainConfig;
  let storageConfig = this.storageConfig;
  let webServerConfig = this.webServerConfig;
  let corsParts = [];

  if(webServerConfig && webServerConfig.enabled) {
    if(webServerConfig.host) corsParts.push(utils.buildUrlFromConfig(webServerConfig));
  }
  if(storageConfig && storageConfig.enabled) {
    // if getUrl is specified in the config, that needs to be included in cors
    // instead of the concatenated protocol://host:port
    if(storageConfig.upload.getUrl) {
      // remove /ipfs or /bzz: from getUrl if it's there
      let getUrlParts = storageConfig.upload.getUrl.split('/');
      getUrlParts = getUrlParts.slice(0, 3);
      let host = canonicalHost(getUrlParts[2].split(':')[0]);
      let port = getUrlParts[2].split(':')[1];
      getUrlParts[2] = port ? [host, port].join(':') : host;
      corsParts.push(getUrlParts.join('/'));
    }
    // use our modified getUrl or in case it wasn't specified, use a built url
    else{
      corsParts.push(utils.buildUrlFromConfig(storageConfig.upload));
    }
  }
  // add whisper cors
  if(this.communicationConfig && this.communicationConfig.enabled && this.communicationConfig.provider === 'whisper'){
    corsParts.push('embark');
  }

  let cors = corsParts.join(',');
  if(blockchainConfig.rpcCorsDomain === 'auto'){
    if(cors.length) blockchainConfig.rpcCorsDomain = cors;
    else blockchainConfig.rpcCorsDomain = '';
  }
  if(blockchainConfig.wsOrigins === 'auto'){
    if(cors.length) blockchainConfig.wsOrigins = cors;
    else blockchainConfig.wsOrigins = '';
  }
};

Config.prototype._mergeConfig = function(configFilePath, defaultConfig, env, enabledByDefault) {
  if (!configFilePath) {
    let configToReturn = defaultConfig['default'] || {};
    configToReturn.enabled = enabledByDefault || false;
    return configToReturn;
  }

  // due to embark.json; TODO: refactor this
  configFilePath = configFilePath.replace('.json','').replace('.js', '');
  if (!fs.existsSync(configFilePath + '.js') && !fs.existsSync(configFilePath + '.json')) {
    // TODO: remove this if
    if (this.logger) {
      this.logger.warn(__("no config file found at %s using default config", configFilePath));
    }
    return defaultConfig['default'] || {};
  }

  let config;
  if (fs.existsSync(configFilePath + '.js')) {
    delete require.cache[fs.dappPath(configFilePath + '.js')];
    config = require(fs.dappPath(configFilePath + '.js'));
  } else {
    config = fs.readJSONSync(configFilePath + '.json');
  }
  let configObject = utils.recursiveMerge(defaultConfig, config);

  if (env) {
    return utils.recursiveMerge(configObject['default'] || {}, configObject[env]);
  }
  return configObject;
};

Config.prototype._getFileOrOject = function(object, filePath, property) {
  if (typeof (this.configDir) === 'object') {
    return this.configDir[property];
  }
  return utils.joinPath(this.configDir, filePath);
};

Config.prototype.loadBlockchainConfigFile = function() {
  var configObject = {
    "default": {
      "enabled": true,
      "rpcCorsDomain": "auto",
      "wsOrigins": "auto"
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'blockchain', 'blockchain');

  this.blockchainConfig = this._mergeConfig(configFilePath, configObject, this.env, true);
  if (!configFilePath) {
    this.blockchainConfig.default = true;
  }
  if(!this.blockchainConfig.account && !this.blockchainConfig.isDev && this.env !== 'development') {
    this.logger.warn(
      __('Account settings are needed for this chain.' +
         ' Please put a valid address and possibly a password in your blockchain config or use a dev chain.')
    );
  }
};

Config.prototype.loadContractsConfigFile = function() {
  var defaultVersions = {
    "web3": "1.0.0-beta",
    "solc": "0.4.17"
  };
  var versions = utils.recursiveMerge(defaultVersions, this.embarkConfig.versions || {});

  var configObject = {
    "default": {
      "versions": versions,
      "deployment": {
        "host": "localhost", "port": 8545, "type": "rpc"
      },
      "dappConnection": [
        "$WEB3",
        "localhost:8545"
      ],
      "gas": "auto",
      "contracts": {
      }
    }
  };

  var contractsConfigs = this.plugins.getPluginsProperty('contractsConfig', 'contractsConfigs');
  contractsConfigs.forEach(function(pluginConfig) {
    configObject = utils.recursiveMerge(configObject, pluginConfig);
  });

  let configFilePath = this._getFileOrOject(this.configDir, 'contracts', 'contracts');

  const newContractsConfig = this._mergeConfig(configFilePath, configObject, this.env);

  if (!deepEqual(newContractsConfig, this.contractsConfig)) {
    this.contractsConfig = newContractsConfig;
  }
};

Config.prototype.loadExternalContractsFiles = function() {
  let contracts = this.contractsConfig.contracts;
  for (let contractName in contracts) {
    let contract = contracts[contractName];
    if (!contract.file) {
      continue;
    }
    if (contract.file.startsWith('http') || contract.file.startsWith('git')) {
      const fileObj = utils.getExternalContractUrl(contract.file);
      if (!fileObj) {
        return this.logger.error(__("HTTP contract file not found") + ": " + contract.file);
      }
      const localFile = fileObj.filePath;
      this.contractsFiles.push(new File({filename: localFile, type: File.types.http, basedir: '', path: fileObj.url}));
    } else if (fs.existsSync(contract.file)) {
      this.contractsFiles.push(new File({filename: contract.file, type: File.types.dapp_file, basedir: '', path: contract.file}));
    } else if (fs.existsSync(path.join('./node_modules/', contract.file))) {
      this.contractsFiles.push(new File({filename: path.join('./node_modules/', contract.file), type: File.types.dapp_file, basedir: '', path: path.join('./node_modules/', contract.file)}));
    } else {
      this.logger.error(__("contract file not found") + ": " + contract.file);
    }
  }
};

Config.prototype.loadStorageConfigFile = function() {
  var versions = utils.recursiveMerge({"ipfs-api": "17.2.4"}, this.embarkConfig.versions || {});

  var configObject = {
    "default": {
      "versions": versions,
      "enabled": true,
      "available_providers": ["ipfs", "swarm"],
      "ipfs_bin": "ipfs",
      "upload": {
        "provider": "ipfs",
        "protocol": "http",
        "host" : defaultHost,
        "port": 5001,
        "getUrl": "http://localhost:8080/ipfs/"
      },
      "dappConnection": [{"provider": "ipfs", "host": "localhost", "port": 5001, "getUrl": "http://localhost:8080/ipfs/"}]
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'storage', 'storage');

  this.storageConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadNameSystemConfigFile = function() {
  // todo: spec out names for registration in the file itself for a dev chain
  var configObject = {
    "default": {
      "available_providers": ["ens"],
      "provider": "ens",
      "enabled": true
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'namesystem', 'namesystem');

  this.namesystemConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadCommunicationConfigFile = function() {
  var configObject = {
    "default": {
      "enabled": true,
      "provider": "whisper",
      "available_providers": ["whisper"],
      "connection": {
        "host": defaultHost,
        "port": 8546,
        "type": "ws"
      }
    }
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'communication', 'communication');

  this.communicationConfig = this._mergeConfig(configFilePath, configObject, this.env);
};

Config.prototype.loadWebServerConfigFile = function() {
  var configObject = {
    "enabled": true,
    "host": defaultHost,
    "port": 8000
  };

  let configFilePath = this._getFileOrOject(this.configDir, 'webserver', 'webserver');

  let webServerConfig = this._mergeConfig(configFilePath, configObject, false);

  if (this.webServerConfig) {
    // cli falgs to `embark run` should override configFile and defaults (configObject)
    this.webServerConfig = utils.recursiveMerge(webServerConfig, this.webServerConfig);
  } else {
    this.webServerConfig = webServerConfig;
  }
};

Config.prototype.loadEmbarkConfigFile = function() {
  var configObject = {
    options: {
      solc: {
        "optimize": true,
        "optimize-runs": 200
      }
    }
  };

  this.embarkConfig = utils.recursiveMerge(configObject, this.embarkConfig);

  const contracts = this.embarkConfig.contracts;
  const newContractsFiles = this.loadFiles(contracts);
  if (!this.contractFiles || newContractsFiles.length !== this.contractFiles.length || !deepEqual(newContractsFiles, this.contractFiles)) {
    this.contractsFiles = this.contractsFiles.concat(newContractsFiles).filter((file, index, arr) => {
      return !arr.some((file2, index2) => {
        return file.filename === file2.filename && index < index2;
      });
    });
  }
  // determine contract 'root' directories
  this.contractDirectories = contracts.map((dir) => {
    return dir.split("**")[0];
  }).map((dir) => {
    return dir.split("*.")[0];
  });
  this.contractDirectories.push(constants.httpContractsDirectory);

  this.buildDir  = this.embarkConfig.buildDir;
  this.configDir = this.embarkConfig.config;
};

Config.prototype.loadPipelineConfigFile = function() {
  var assets = this.embarkConfig.app;
  for(var targetFile in assets) {
    this.assetFiles[targetFile] = this.loadFiles(assets[targetFile]);
  }
};

Config.prototype.loadChainTrackerFile = function() {
  if (!fs.existsSync(this.chainsFile)) {
    this.logger.info(this.chainsFile + ' ' + __('file not found, creating it...'));
    fs.writeJSONSync(this.chainsFile, {});
  }

  this.chainTracker = fs.readJSONSync(this.chainsFile);
};

function findMatchingExpression(filename, filesExpressions) {
  for (let fileExpression of filesExpressions) {
    var matchingFiles = utils.filesMatchingPattern(fileExpression);
    for (let matchFile of matchingFiles) {
      if (matchFile === filename) {
        return path.dirname(fileExpression).replace(/\*/g, '');
      }
    }
  }
  return path.dirname(filename);
}

Config.prototype.loadFiles = function(files) {
  var self = this;
  var originalFiles = utils.filesMatchingPattern(files);
  var readFiles = [];

  originalFiles.filter(function(file) {
    return (file[0] === '$' || file.indexOf('.') >= 0);
  }).filter(function(file) {
    let basedir = findMatchingExpression(file, files);
    readFiles.push(new File({filename: file, type: File.types.dapp_file, basedir: basedir, path: file}));
  });

  var filesFromPlugins = [];
  var filePlugins = self.plugins.getPluginsFor('pipelineFiles');
  filePlugins.forEach(function(plugin) {
    try {
      var fileObjects = plugin.runFilePipeline();
      for (var i=0; i < fileObjects.length; i++) {
        var fileObject = fileObjects[i];
        filesFromPlugins.push(fileObject);
      }
    }
    catch(err) {
      self.logger.error(err.message);
    }
  });
  filesFromPlugins.filter(function(file) {
    if ((file.intendedPath && utils.fileMatchesPattern(files, file.intendedPath)) || utils.fileMatchesPattern(files, file.file)) {
      readFiles.push(file);
    }
  });

  return readFiles;
};

// NOTE: this doesn't work for internal modules
Config.prototype.loadPluginContractFiles = function() {
  var self = this;

  var contractsPlugins = this.plugins.getPluginsFor('contractFiles');
  contractsPlugins.forEach(function(plugin) {
    plugin.contractsFiles.forEach(function(file) {
      var filename = file.replace('./','');
      self.contractsFiles.push(new File({filename: filename, type: File.types.custom, path: filename, resolver: function(callback) {
        callback(plugin.loadPluginFile(file));
      }}));
    });
  });
};

module.exports = Config;
<|endoftext|>


<commit_before>const fs = require('./fs.js');
const utils = require('../utils/utils.js');
const constants = require('../constants');

// TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) {
  this.name = options.name;
  this.isInternal = options.isInternal;
  this.pluginModule = options.pluginModule;
  this.pluginPath = options.pluginPath;
  this.pluginConfig = options.pluginConfig;
  this.shouldInterceptLogs = options.interceptLogs;
  this.clientWeb3Providers = [];
  this.beforeDeploy = [];
  this.contractsGenerators = [];
  this.pipeline = [];
  this.pipelineFiles = [];
  this.console = [];
  this.contractsConfigs = [];
  this.contractsFiles = [];
  this.compilers = [];
  this.serviceChecks = [];
  this.pluginTypes = [];
  this.uploadCmds = [];
  this.imports = [];
  this.embarkjs_code = [];
  this.embarkjs_init_code = {};
  this.afterContractsDeployActions = [];
  this.onDeployActions = [];
  this.eventActions = {};
  this.logger = options.logger;
  this.events = options.events;
  this.config = options.config;
  this.env = options.env;
  this.loaded = false;
  this.currentContext = options.context;
  this.acceptedContext = options.pluginConfig.context || [constants.contexts.any];

  if (!Array.isArray(this.currentContext)) {
    this.currentContext = [this.currentContext];
  }
  if (!Array.isArray(this.acceptedContext)) {
    this.acceptedContext = [this.acceptedContext];
  }
};

Plugin.prototype.isContextValid = function() {
  if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) {
    return true;
  }
  return this.acceptedContext.some(context => {
    return this.currentContext.includes(context);
  });
};

Plugin.prototype.hasContext = function(context) {
  return this.currentContext.includes(context);
};

Plugin.prototype.loadPlugin = function() {
  if (!this.isContextValid())  {
    console.log(this.acceptedContext);
    this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contextes}}"', {name: this.name, contextes: this.acceptedContext.join(', ')}));
    return false;
  }
  this.loaded = true;
  if (this.shouldInterceptLogs) {
    this.interceptLogs(this.pluginModule);
  }
  (this.pluginModule.call(this, this));
};

Plugin.prototype.loadInternalPlugin = function() {
  new this.pluginModule(this, this.pluginConfig); /*eslint no-new: "off"*/
};

Plugin.prototype.loadPluginFile = function(filename) {
  return fs.readFileSync(this.pathToFile(filename)).toString();
};

Plugin.prototype.pathToFile = function(filename) {
  if (!this.pluginPath) {
    throw new Error('pluginPath not defined for plugin: ' + this.name);
  }
  return utils.joinPath(this.pluginPath, filename);
};

Plugin.prototype.interceptLogs = function(context) {
  var self = this;
  // TODO: this is a bit nasty, figure out a better way
  context.console = context.console || console;

  //context.console.error  = function(txt) {
  //  // TODO: logger should support an array instead of a single argument
  //  //self.logger.error.apply(self.logger, arguments);
  //  self.logger.error(self.name + " > " + txt);
  //};
  context.console.log  = function(txt) {
    self.logger.info(self.name + " > " + txt);
  };
  context.console.warn  = function(txt) {
    self.logger.warn(self.name + " > " + txt);
  };
  context.console.info  = function(txt) {
    self.logger.info(self.name + " > " + txt);
  };
  context.console.debug  = function(txt) {
    // TODO: ue JSON.stringify
    self.logger.debug(self.name + " > " + txt);
  };
  context.console.trace  = function(txt) {
    self.logger.trace(self.name + " > " + txt);
  };
  context.console.dir  = function(txt) {
    self.logger.dir(txt);
  };
};

// TODO: add deploy provider
Plugin.prototype.registerClientWeb3Provider = function(cb) {
  this.clientWeb3Providers.push(cb);
  this.pluginTypes.push('clientWeb3Provider');
};

Plugin.prototype.registerBeforeDeploy = function(cb) {
  this.beforeDeploy.push(cb);
  this.pluginTypes.push('beforeDeploy');
};

Plugin.prototype.registerContractsGeneration = function(cb) {
  this.contractsGenerators.push(cb);
  this.pluginTypes.push('contractGeneration');
};

Plugin.prototype.registerPipeline = function(matcthingFiles, cb) {
  // TODO: generate error for more than one pipeline per plugin
  this.pipeline.push({matcthingFiles: matcthingFiles, cb: cb});
  this.pluginTypes.push('pipeline');
};

Plugin.prototype.addFileToPipeline = function(file, intendedPath, options) {
  this.pipelineFiles.push({file: file, intendedPath: intendedPath, options: options});
  this.pluginTypes.push('pipelineFiles');
};

Plugin.prototype.addContractFile = function(file) {
  this.contractsFiles.push(file);
  this.pluginTypes.push('contractFiles');
};

Plugin.prototype.registerConsoleCommand = function(cb) {
  this.console.push(cb);
  this.pluginTypes.push('console');
};

// TODO: this only works for services done on startup
Plugin.prototype.registerServiceCheck = function(checkName, checkFn, time) {
  this.serviceChecks.push({checkName: checkName, checkFn: checkFn, time: time});
  this.pluginTypes.push('serviceChecks');
};

Plugin.prototype.has = function(pluginType) {
  return this.pluginTypes.indexOf(pluginType) >= 0;
};

Plugin.prototype.generateProvider = function(args) {
  return this.clientWeb3Providers.map(function(cb) {
    return cb.call(this, args);
  }).join("\n");
};

Plugin.prototype.generateContracts = function(args) {
  return this.contractsGenerators.map(function(cb) {
    return cb.call(this, args);
  }).join("\n");
};

Plugin.prototype.registerContractConfiguration = function(config) {
  this.contractsConfigs.push(config);
  this.pluginTypes.push('contractsConfig');
};

Plugin.prototype.registerCompiler = function(extension, cb) {
  this.compilers.push({extension: extension, cb: cb});
  this.pluginTypes.push('compilers');
};

Plugin.prototype.registerUploadCommand = function(cmd, cb) {
  this.uploadCmds.push({cmd: cmd, cb: cb});
  this.pluginTypes.push('uploadCmds');
};

Plugin.prototype.addCodeToEmbarkJS = function(code) {
  this.embarkjs_code.push(code);
  this.pluginTypes.push('embarkjsCode');
};

Plugin.prototype.addProviderInit = function(providerType, code, initCondition) {
  this.embarkjs_init_code[providerType] = this.embarkjs_init_code[providerType] || [];
  this.embarkjs_init_code[providerType].push([code, initCondition]);
  this.pluginTypes.push('initCode');
};

Plugin.prototype.registerImportFile = function(importName, importLocation) {
  this.imports.push([importName, importLocation]);
  this.pluginTypes.push('imports');
};

Plugin.prototype.registerActionForEvent = function(eventName, cb) {
  if (!this.eventActions[eventName]) {
    this.eventActions[eventName] = [];
  }
  this.eventActions[eventName].push(cb);
  this.pluginTypes.push('eventActions');
}

Plugin.prototype.runFilePipeline = function() {
  var self = this;

  return this.pipelineFiles.map(function(file) {
      var obj = {};
      obj.filename = file.file.replace('./','');
      obj.content = self.loadPluginFile(file.file).toString();
      obj.intendedPath = file.intendedPath;
      obj.options = file.options;
      obj.path = self.pathToFile(obj.filename);

      return obj;
  });
};

Plugin.prototype.runPipeline = function(args) {
  // TODO: should iterate the pipelines
  var pipeline = this.pipeline[0];
  var shouldRunPipeline = utils.fileMatchesPattern(pipeline.matcthingFiles, args.targetFile);
  if (shouldRunPipeline) {
    return pipeline.cb.call(this, args);
  } else {
    return args.source;
  }
};

module.exports = Plugin;
<commit_msg>certify pluginTypes are uniques, to avoid issue with a plugin call being called twice or more<commit_after>const fs = require('./fs.js');
const utils = require('../utils/utils.js');
const constants = require('../constants');
const _ = require('underscore')

// TODO: pass other params like blockchainConfig, contract files, etc..
var Plugin = function(options) {
  this.name = options.name;
  this.isInternal = options.isInternal;
  this.pluginModule = options.pluginModule;
  this.pluginPath = options.pluginPath;
  this.pluginConfig = options.pluginConfig;
  this.shouldInterceptLogs = options.interceptLogs;
  this.clientWeb3Providers = [];
  this.beforeDeploy = [];
  this.contractsGenerators = [];
  this.pipeline = [];
  this.pipelineFiles = [];
  this.console = [];
  this.contractsConfigs = [];
  this.contractsFiles = [];
  this.compilers = [];
  this.serviceChecks = [];
  this.pluginTypes = [];
  this.uploadCmds = [];
  this.imports = [];
  this.embarkjs_code = [];
  this.embarkjs_init_code = {};
  this.afterContractsDeployActions = [];
  this.onDeployActions = [];
  this.eventActions = {};
  this.logger = options.logger;
  this.events = options.events;
  this.config = options.config;
  this.env = options.env;
  this.loaded = false;
  this.currentContext = options.context;
  this.acceptedContext = options.pluginConfig.context || [constants.contexts.any];

  if (!Array.isArray(this.currentContext)) {
    this.currentContext = [this.currentContext];
  }
  if (!Array.isArray(this.acceptedContext)) {
    this.acceptedContext = [this.acceptedContext];
  }
};

Plugin.prototype.isContextValid = function() {
  if (this.currentContext.includes(constants.contexts.any) || this.acceptedContext.includes(constants.contexts.any)) {
    return true;
  }
  return this.acceptedContext.some(context => {
    return this.currentContext.includes(context);
  });
};

Plugin.prototype.hasContext = function(context) {
  return this.currentContext.includes(context);
};

Plugin.prototype.loadPlugin = function() {
  if (!this.isContextValid())  {
    console.log(this.acceptedContext);
    this.logger.warn(__('Plugin {{name}} can only be loaded in the context of "{{contextes}}"', {name: this.name, contextes: this.acceptedContext.join(', ')}));
    return false;
  }
  this.loaded = true;
  if (this.shouldInterceptLogs) {
    this.interceptLogs(this.pluginModule);
  }
  (this.pluginModule.call(this, this));
};

Plugin.prototype.loadInternalPlugin = function() {
  new this.pluginModule(this, this.pluginConfig); /*eslint no-new: "off"*/
};

Plugin.prototype.loadPluginFile = function(filename) {
  return fs.readFileSync(this.pathToFile(filename)).toString();
};

Plugin.prototype.pathToFile = function(filename) {
  if (!this.pluginPath) {
    throw new Error('pluginPath not defined for plugin: ' + this.name);
  }
  return utils.joinPath(this.pluginPath, filename);
};

Plugin.prototype.interceptLogs = function(context) {
  var self = this;
  // TODO: this is a bit nasty, figure out a better way
  context.console = context.console || console;

  //context.console.error  = function(txt) {
  //  // TODO: logger should support an array instead of a single argument
  //  //self.logger.error.apply(self.logger, arguments);
  //  self.logger.error(self.name + " > " + txt);
  //};
  context.console.log  = function(txt) {
    self.logger.info(self.name + " > " + txt);
  };
  context.console.warn  = function(txt) {
    self.logger.warn(self.name + " > " + txt);
  };
  context.console.info  = function(txt) {
    self.logger.info(self.name + " > " + txt);
  };
  context.console.debug  = function(txt) {
    // TODO: ue JSON.stringify
    self.logger.debug(self.name + " > " + txt);
  };
  context.console.trace  = function(txt) {
    self.logger.trace(self.name + " > " + txt);
  };
  context.console.dir  = function(txt) {
    self.logger.dir(txt);
  };
};

// TODO: add deploy provider
Plugin.prototype.registerClientWeb3Provider = function(cb) {
  this.clientWeb3Providers.push(cb);
  this.pluginTypes.push('clientWeb3Provider');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerContractsGeneration = function(cb) {
  this.contractsGenerators.push(cb);
  this.pluginTypes.push('contractGeneration');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerPipeline = function(matcthingFiles, cb) {
  // TODO: generate error for more than one pipeline per plugin
  this.pipeline.push({matcthingFiles: matcthingFiles, cb: cb});
  this.pluginTypes.push('pipeline');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.addFileToPipeline = function(file, intendedPath, options) {
  this.pipelineFiles.push({file: file, intendedPath: intendedPath, options: options});
  this.pluginTypes.push('pipelineFiles');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.addContractFile = function(file) {
  this.contractsFiles.push(file);
  this.pluginTypes.push('contractFiles');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerConsoleCommand = function(cb) {
  this.console.push(cb);
  this.pluginTypes.push('console');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

// TODO: this only works for services done on startup
Plugin.prototype.registerServiceCheck = function(checkName, checkFn, time) {
  this.serviceChecks.push({checkName: checkName, checkFn: checkFn, time: time});
  this.pluginTypes.push('serviceChecks');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.has = function(pluginType) {
  return this.pluginTypes.indexOf(pluginType) >= 0;
};

Plugin.prototype.generateProvider = function(args) {
  return this.clientWeb3Providers.map(function(cb) {
    return cb.call(this, args);
  }).join("\n");
};

Plugin.prototype.generateContracts = function(args) {
  return this.contractsGenerators.map(function(cb) {
    return cb.call(this, args);
  }).join("\n");
};

Plugin.prototype.registerContractConfiguration = function(config) {
  this.contractsConfigs.push(config);
  this.pluginTypes.push('contractsConfig');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerCompiler = function(extension, cb) {
  this.compilers.push({extension: extension, cb: cb});
  this.pluginTypes.push('compilers');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerUploadCommand = function(cmd, cb) {
  this.uploadCmds.push({cmd: cmd, cb: cb});
  this.pluginTypes.push('uploadCmds');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.addCodeToEmbarkJS = function(code) {
  this.embarkjs_code.push(code);
  this.pluginTypes.push('embarkjsCode');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.addProviderInit = function(providerType, code, initCondition) {
  this.embarkjs_init_code[providerType] = this.embarkjs_init_code[providerType] || [];
  this.embarkjs_init_code[providerType].push([code, initCondition]);
  this.pluginTypes.push('initCode');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerImportFile = function(importName, importLocation) {
  this.imports.push([importName, importLocation]);
  this.pluginTypes.push('imports');
  this.pluginTypes = _.uniq(this.pluginTypes);
};

Plugin.prototype.registerActionForEvent = function(eventName, cb) {
  if (!this.eventActions[eventName]) {
    this.eventActions[eventName] = [];
  }
  this.eventActions[eventName].push(cb);
  this.pluginTypes.push('eventActions');
  this.pluginTypes = _.uniq(this.pluginTypes);
}

Plugin.prototype.runFilePipeline = function() {
  var self = this;

  return this.pipelineFiles.map(function(file) {
      var obj = {};
      obj.filename = file.file.replace('./','');
      obj.content = self.loadPluginFile(file.file).toString();
      obj.intendedPath = file.intendedPath;
      obj.options = file.options;
      obj.path = self.pathToFile(obj.filename);

      return obj;
  });
};

Plugin.prototype.runPipeline = function(args) {
  // TODO: should iterate the pipelines
  var pipeline = this.pipeline[0];
  var shouldRunPipeline = utils.fileMatchesPattern(pipeline.matcthingFiles, args.targetFile);
  if (shouldRunPipeline) {
    return pipeline.cb.call(this, args);
  } else {
    return args.source;
  }
};

module.exports = Plugin;
<|endoftext|>


<commit_before>/**
 * Wraps search requests to algolia index
 * @author adrien joly, whyd
 **/

var snip = require('../snip.js');
var mongodb = require('./mongodb.js');
var Algolia = require('algoliasearch');

var ENGINE = new Algolia(process.env.ALGOLIA_APP_ID.substr(), process.env.ALGOLIA_API_KEY.substr());

var INDEX_NAME_BY_TYPE = {
	user: 'users',
	track: 'tracks',
	post: 'posts',
	playlist: 'playlists',
};

// lazy init and caching of indexes
var getIndex = (function() {
	var INDEX = {}; // cache of indexes
	var INDEX_FIELDS = {
		users: [ 'email', 'handle' ],
		posts: [ 'text', 'uId' ],
		playlists: [ ],
	};
	return function(indexName) {
		var index = INDEX[indexName];
		if (!index) {
			index = INDEX[indexName] = ENGINE.initIndex(indexName);
			// init field indexing settings
			var fields = INDEX_FIELDS[indexName] || [];
			index.setSettings({
				attributesForFaceting: [ 'name' ].concat(fields.map(function(field) {
					return 'filterOnly(' + field + ')';
				}))
			});
		}
		return index;
	}
})();

function Search() {}

Search.prototype.search = function(index, query, options, cb) {
  var facetAttrs = [];
  var facetFilters = [];
  var i, q;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  if (typeof query === 'string') {
    q = query;
  } else {
    q = query.q;
    delete query.q;
    for (i in query) {
      facetAttrs.push(i);
      facetFilters.push(i + ':' + query[i]);
    }
    options = options || {};
    options.facets = facetAttrs.join(',');
    options.facetFilters = facetFilters;
  }
  if (cb) {
    if (this.queries) {
      options.index = index;
      options.query = q;
      this.queries.push(options);
      ENGINE.multipleQueries(this.queries, 'index', cb);
    } else {
      getIndex(index).search(q, options, cb);
    }
  } else {
    options.index = index;
    options.query = q;
    this.queries = this.queries || [];
    this.queries.push(options);
  }
  return this;
};

function makeCallbackTranslator(type, cb){
	function translateResult(r){
		r._type = type;
		r._id = r.objectID.replace(":", "_");
		delete r.objectID;
		return r;
	}
	return function(err, res){
		if (err)
			cb({error: err});
		else {
			res.hits = res.hits.map(translateResult);
			cb(res);
		}
	};
}

var FIELD_MAPPING = {
	limit: "hitsPerPage",
};

function extractOptions(q){
	return snip.filterFields(q, FIELD_MAPPING);
}

var searchIndex = new Search();

var searchByType = {
	"user": function(q, cb){
		console.log("search users", q);
		var options = extractOptions(q);
		return searchIndex.search("users", q.q, options, makeCallbackTranslator("user", cb));
		// tested http://localhost:8080/search?q=adrien&context=mention
		//     => { q: 'adrien', limit: 6 }
		// tested http://localhost:8080/search?q=pouet&context=header
		//     => { q: 'pouet' }
	},
	"track": function(q, cb){
		console.log("search tracks", q);
		var options = extractOptions(q);
		return searchIndex.search("tracks", q.q, options, makeCallbackTranslator("track", function(res){
			(res.hits || []).map(function(h){
				h.eId = h._id;
				h._id = mongodb.ObjectId(h.post);
				delete h.post;
			});
			cb(res);
		}));
	},
	"post": function(q, cb){
		console.log("search posts", q);
		var options = extractOptions(q);
		if (q.uId) {
			q = {
				q: q.q,
				uId: q.uId,
			};
			return searchIndex.search("posts", q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=adrien&context=quick
			//     => { q: 'adrien', uId: '4d94501d1f78ac091dbc9b4d', limit: 10 }
			// tested http://localhost:8080/search?q=hey&uid=4d94501d1f78ac091dbc9b4d (profile feed filter)
			//     => { q: 'hey', uId: '4d94501d1f78ac091dbc9b4d' }
		}
		else if (q.excludeUid) {
			q = {
				q: q.q,
				uId: "-" + q.excludeUid,
			};
			return searchIndex.search("posts", q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=adrien&context=quick
			//     => { q: 'adrien', excludeUid: '4d94501d1f78ac091dbc9b4d', limit: 10 }
			// tested http://localhost:8080/search?q=pouet&context=addTrack
			//     => { q: 'pouet', excludeUid: '4d94501d1f78ac091dbc9b4d', limit: 10 }
		}
		else {
			return searchIndex.search("posts", q.q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=pouet&context=header
			//     => { q: 'pouet' }
		}
	},
	"playlist": function(q, cb){
		console.log("search playlists", q)
		var options = extractOptions(q);
		return searchIndex.search("playlists", q.q, options, makeCallbackTranslator("playlist", cb));
		// tested http://localhost:8080/search?q=pouet&context=header
		//     => { q: 'pouet' }
	},
};

exports.query = function(q, cb) {
	var hits = [];
	var q = q || {};
	var queue;
	if (q._type)
		queue = [q._type];
	else if (q.uId)
		queue = ["post"];
	else
		queue = ["user", "track", "post", "playlist"]; //Object.keys(searchByType);
	delete q._type;
	(function next(){
		var type = queue.pop();
		if (!type)
			cb({q: q.q, hits: hits});
		else
			searchByType[type](q, function(res){
				if (res.hits) {
					console.log("=>", res.hits.length, "hits")
					hits = hits.concat(res.hits);
				} else {
					console.error("algolia error for " + JSON.stringify(q, null, 2)
					  + " => " + JSON.stringify(res, null, 2));
				}
				next();
			});
	})();
}

exports.indexTyped = function(type, item, handler) {
	//console.log("models.search.index(): ", item, "...");
	if (!type || !INDEX_NAME_BY_TYPE[type])
		handler || logToConsole({error: "indexTyped: unknown type"});
	else if (!item || !item._id || !item.name)
		handler || logToConsole({error: "indexTyped: missing parameters"});
	else {
		var doc = Object.assign({ objectID: item._id }, item);
		delete doc._id;
		getIndex(INDEX_NAME_BY_TYPE[type]).addObjects([ doc ], function(err, content) {
			if (err) {
				console.error("algolia error when indexing " + type + " " + JSON.stringify(item, null, 2)
					+ " => " + JSON.stringify(err, null, 2))
			} else {
				console.log("algolia indexTyped " + type + " =>", content);
			}
			handler && handler(); // TODO: check if parameters are required or not
		});
	}
}

exports.countDocs = function(type, callback) {
	ENGINE.listIndexes(function(err, content) {
		console.log('countDocs', type, err || content);
		try {
			callback(content.items.find(function(index) {
				return index.name === INDEX_NAME_BY_TYPE[type];
			}).entries);
		} catch(e) {
			console.error(err || e);
			callback(null);
		}
	});
}

// INIT

exports.init = function(){
	console.log("models.search: using Algolia Search index");
}
<commit_msg>add deleteAllDocs() implementation in algolia search (for index admin page)<commit_after>/**
 * Wraps search requests to algolia index
 * @author adrien joly, whyd
 **/

var snip = require('../snip.js');
var mongodb = require('./mongodb.js');
var Algolia = require('algoliasearch');

var ENGINE = new Algolia(process.env.ALGOLIA_APP_ID.substr(), process.env.ALGOLIA_API_KEY.substr());

var INDEX_NAME_BY_TYPE = {
	user: 'users',
	track: 'tracks',
	post: 'posts',
	playlist: 'playlists',
};

// lazy init and caching of indexes
var getIndex = (function() {
	var INDEX = {}; // cache of indexes
	var INDEX_FIELDS = {
		users: [ 'email', 'handle' ],
		posts: [ 'text', 'uId' ],
		playlists: [ ],
	};
	return function(indexName) {
		var index = INDEX[indexName];
		if (!index) {
			index = INDEX[indexName] = ENGINE.initIndex(indexName);
			// init field indexing settings
			var fields = INDEX_FIELDS[indexName] || [];
			index.setSettings({
				attributesForFaceting: [ 'name' ].concat(fields.map(function(field) {
					return 'filterOnly(' + field + ')';
				}))
			});
		}
		return index;
	}
})();

function Search() {}

Search.prototype.search = function(index, query, options, cb) {
  var facetAttrs = [];
  var facetFilters = [];
  var i, q;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  if (typeof query === 'string') {
    q = query;
  } else {
    q = query.q;
    delete query.q;
    for (i in query) {
      facetAttrs.push(i);
      facetFilters.push(i + ':' + query[i]);
    }
    options = options || {};
    options.facets = facetAttrs.join(',');
    options.facetFilters = facetFilters;
  }
  if (cb) {
    if (this.queries) {
      options.index = index;
      options.query = q;
      this.queries.push(options);
      ENGINE.multipleQueries(this.queries, 'index', cb);
    } else {
      getIndex(index).search(q, options, cb);
    }
  } else {
    options.index = index;
    options.query = q;
    this.queries = this.queries || [];
    this.queries.push(options);
  }
  return this;
};

function makeCallbackTranslator(type, cb){
	function translateResult(r){
		r._type = type;
		r._id = r.objectID.replace(":", "_");
		delete r.objectID;
		return r;
	}
	return function(err, res){
		if (err)
			cb({error: err});
		else {
			res.hits = res.hits.map(translateResult);
			cb(res);
		}
	};
}

var FIELD_MAPPING = {
	limit: "hitsPerPage",
};

function extractOptions(q){
	return snip.filterFields(q, FIELD_MAPPING);
}

var searchIndex = new Search();

var searchByType = {
	"user": function(q, cb){
		console.log("search users", q);
		var options = extractOptions(q);
		return searchIndex.search("users", q.q, options, makeCallbackTranslator("user", cb));
		// tested http://localhost:8080/search?q=adrien&context=mention
		//     => { q: 'adrien', limit: 6 }
		// tested http://localhost:8080/search?q=pouet&context=header
		//     => { q: 'pouet' }
	},
	"track": function(q, cb){
		console.log("search tracks", q);
		var options = extractOptions(q);
		return searchIndex.search("tracks", q.q, options, makeCallbackTranslator("track", function(res){
			(res.hits || []).map(function(h){
				h.eId = h._id;
				h._id = mongodb.ObjectId(h.post);
				delete h.post;
			});
			cb(res);
		}));
	},
	"post": function(q, cb){
		console.log("search posts", q);
		var options = extractOptions(q);
		if (q.uId) {
			q = {
				q: q.q,
				uId: q.uId,
			};
			return searchIndex.search("posts", q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=adrien&context=quick
			//     => { q: 'adrien', uId: '4d94501d1f78ac091dbc9b4d', limit: 10 }
			// tested http://localhost:8080/search?q=hey&uid=4d94501d1f78ac091dbc9b4d (profile feed filter)
			//     => { q: 'hey', uId: '4d94501d1f78ac091dbc9b4d' }
		}
		else if (q.excludeUid) {
			q = {
				q: q.q,
				uId: "-" + q.excludeUid,
			};
			return searchIndex.search("posts", q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=adrien&context=quick
			//     => { q: 'adrien', excludeUid: '4d94501d1f78ac091dbc9b4d', limit: 10 }
			// tested http://localhost:8080/search?q=pouet&context=addTrack
			//     => { q: 'pouet', excludeUid: '4d94501d1f78ac091dbc9b4d', limit: 10 }
		}
		else {
			return searchIndex.search("posts", q.q, options, makeCallbackTranslator("post", cb));
			// tested http://localhost:8080/search?q=pouet&context=header
			//     => { q: 'pouet' }
		}
	},
	"playlist": function(q, cb){
		console.log("search playlists", q)
		var options = extractOptions(q);
		return searchIndex.search("playlists", q.q, options, makeCallbackTranslator("playlist", cb));
		// tested http://localhost:8080/search?q=pouet&context=header
		//     => { q: 'pouet' }
	},
};

exports.query = function(q, cb) {
	var hits = [];
	var q = q || {};
	var queue;
	if (q._type)
		queue = [q._type];
	else if (q.uId)
		queue = ["post"];
	else
		queue = ["user", "track", "post", "playlist"]; //Object.keys(searchByType);
	delete q._type;
	(function next(){
		var type = queue.pop();
		if (!type)
			cb({q: q.q, hits: hits});
		else
			searchByType[type](q, function(res){
				if (res.hits) {
					console.log("=>", res.hits.length, "hits")
					hits = hits.concat(res.hits);
				} else {
					console.error("algolia error for " + JSON.stringify(q, null, 2)
					  + " => " + JSON.stringify(res, null, 2));
				}
				next();
			});
	})();
}

exports.indexTyped = function(type, item, handler) {
	//console.log("models.search.index(): ", item, "...");
	if (!type || !INDEX_NAME_BY_TYPE[type])
		handler || logToConsole({error: "indexTyped: unknown type"});
	else if (!item || !item._id || !item.name)
		handler || logToConsole({error: "indexTyped: missing parameters"});
	else {
		var doc = Object.assign({ objectID: item._id }, item);
		delete doc._id;
		getIndex(INDEX_NAME_BY_TYPE[type]).addObjects([ doc ], function(err, content) {
			if (err) {
				console.error("algolia error when indexing " + type + " " + JSON.stringify(item, null, 2)
					+ " => " + JSON.stringify(err, null, 2))
			} else {
				console.log("algolia indexTyped " + type + " =>", content);
			}
			handler && handler(); // TODO: check if parameters are required or not
		});
	}
}

exports.countDocs = function(type, callback) {
	ENGINE.listIndexes(function(err, content) {
		try {
			callback(content.items.find(function(index) {
				return index.name === INDEX_NAME_BY_TYPE[type];
			}).entries);
		} catch(e) {
			console.error(err || e);
			callback(null);
		}
	});
}

exports.deleteAllDocs = function(type, callback) {
	getIndex(INDEX_NAME_BY_TYPE[type]).clearIndex(function(err, content) {
		console.log('algolia deleteAllDocs =>', err || content);
		callback && callback(); // TODO: check if parameters are required or not
	});
}

// INIT

exports.init = function(){
	console.log("models.search: using Algolia Search index");
}
<|endoftext|>


<commit_before>var _ = require('underscore'),
  async = require('async'),
  bag = require('bagofholding'),
  cron = require('cron'),
  Db = require('./db'),
  fs = require('fs'),
  fsx = require('fs.extra'),
  p = require('path');

/**
 * class Couchpenter
 *
 * @param {String} url: CouchDB URL in format http(s)://user:pass@host:port, fallback to COUCHDB_URL environment variable, default to http://localhost:5984
 * @param {String} opts: optional
 * - setupFile: Couchpenter setup file
 * - dir: documents directory
 * - prefix: prefix for database names
 * - dbSetup: Couchpenter database setup object
 */
function Couchpenter(url, opts) {
  opts = opts || {};

  this.url = url || process.env.COUCHDB_URL || 'http://localhost:5984';
  this.opts = {
    setupFile: opts.setupFile || 'couchpenter.json',
    dir: opts.dir || process.cwd(),
    prefix: opts.prefix,
    setup: opts.dbSetup
  };
}

/**
 * Create a sample couchpenter.json setup file in current working directory.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.init = function (cb) {
  console.log('Creating sample setup file: couchpenter.json');
  fsx.copy(p.join(__dirname, '../examples/couchpenter.json'), 'couchpenter.json', cb);
};

// NOTE: pardon this method to tasks mapping,
// needed to preserve backward compatibility w/ v0.1.x

/**
 * Create databases and documents, overwrite if documents exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUp = function (cb) {
  this._task(['createDatabases', 'saveDocuments'], cb);
};

/**
 * Create databases only.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDatabases = function (cb) {
  this._task(['createDatabases'], cb);
};

/**
 * Create documents only, does not overwrite if exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDocuments = function (cb) {
  this._task(['createDocuments'], cb);
};

/**
 * Create documents only, overwrite if exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDocumentsOverwrite = function (cb) {
  this._task(['saveDocuments'], cb);
};

/**
 * Alias for tearDownDatabases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDown = function (cb) {
  this.tearDownDatabases(cb);
};

/**
 * Delete databases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDownDatabases = function (cb) {
  this._task(['removeDatabases'], cb);
};

/**
 * Delete documents.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDownDocuments = function (cb) {
  this._task(['removeDocuments'], cb);
};

/**
 * Alias for resetDocuments.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.reset = function (cb) {
  this.resetDocuments(cb);
};

/**
 * Delete then recreate databases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.resetDatabases = function (cb) {
  this._task(['removeDatabases', 'createDatabases'], cb);
};

/**
 * Delete then recreate documents only.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.resetDocuments = function (cb) {
  this._task(['removeDatabases', 'createDatabases', 'createDocuments'], cb);
};

/**
 * Alias for cleanDatabases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.clean = function (cb) {
  this.cleanDatabases(cb);
};

/**
 * Delete unknown databases (not configured in setup file).
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.cleanDatabases = function (cb) {
  this._task(['cleanDatabases'], cb);
};

/**
 * Warm up views specified in design documents.
 * If schedule is specified, then views warm up will be scheduled using cron.
 * Otherwise, it's just a once off.
 *
 * @param {String} schedule: cron scheduling definition in standard * * * * * format
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.warmViews = function(schedule, cb) {
  var self = this;
  if (cb) {
    new cron.CronJob(schedule, function () {
      self._task(['warmViews'], cb);
    });
  } else {
    cb = schedule;
    this._task(['warmViews'], cb);
  }
};

/**
 * Execute database tasks in series order.
 *
 * @param {Array} tasks: the tasks to execute
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype._task = function (tasks, cb) {
  var setup = this.opts.setup || JSON.parse(bag.cli.lookupFile(this.opts.setupFile)),
    db = new Db(this.url),
    asyncTasks = [],
    self = this;

  // prefix database names if optional prefix is specified
  if (this.opts.prefix) {
    _.keys(setup).forEach(function (dbName) {
      setup[self.opts.prefix + dbName] = setup[dbName];
      delete setup[dbName];
    });
  }

  tasks.forEach(function (taskName) {
    asyncTasks.push(function (cb) {
      var data = (taskName.match(/Databases$/)) ?  _.keys(setup) : self._docs(setup, self.opts.dir);
      db[taskName](data, cb);
    });
  });

  async.series(asyncTasks, function (err, results) {
    var combined = [];
    results.forEach(function (result) {
      combined = combined.concat(result);
    });
    cb(err, combined);
  });
};

/**
 * Process documents in Couchpenter setup:
 * - if it's an object, leave as-is
 * - if it's a json file location (ending with .json) then assign the content of the file as the document
 * - otherwise assume it's a module, and require it
 * Location of file and module is relative to this.opts.dir .
 *
 * @param {Object} setup: Couchpenter setup
 * @param {String} dir: base directory relative to file location
 * @return {Object} setup with documents processed
 */
Couchpenter.prototype._docs = function (setup, dir) {
  _.keys(setup).forEach(function (dbName) {
    for (var i = 0, ln = setup[dbName].length; i < ln; i += 1) {
      var item = setup[dbName][i];
      if (_.isString(item)) {
        if (item.match(/\.json$/)) {
          setup[dbName][i] = JSON.parse(fs.readFileSync(p.join(dir, item)));
        } else {
          setup[dbName][i] = require(p.join(dir, item));
        }
      } else if (!Array.isArray(item) && !_.isObject(item)) {
        throw new Error('Invalid document ' + item + ' in db ' + dbName + ', only object and string allowed');
      }
    }
  });
  return setup;
};

module.exports = Couchpenter;
<commit_msg>Added auto-start for cronjob when scheduling warmViews.<commit_after>var _ = require('underscore'),
  async = require('async'),
  bag = require('bagofholding'),
  cron = require('cron'),
  Db = require('./db'),
  fs = require('fs'),
  fsx = require('fs.extra'),
  p = require('path');

/**
 * class Couchpenter
 *
 * @param {String} url: CouchDB URL in format http(s)://user:pass@host:port, fallback to COUCHDB_URL environment variable, default to http://localhost:5984
 * @param {String} opts: optional
 * - setupFile: Couchpenter setup file
 * - dir: documents directory
 * - prefix: prefix for database names
 * - dbSetup: Couchpenter database setup object
 */
function Couchpenter(url, opts) {
  opts = opts || {};

  this.url = url || process.env.COUCHDB_URL || 'http://localhost:5984';
  this.opts = {
    setupFile: opts.setupFile || 'couchpenter.json',
    dir: opts.dir || process.cwd(),
    prefix: opts.prefix,
    setup: opts.dbSetup
  };
}

/**
 * Create a sample couchpenter.json setup file in current working directory.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.init = function (cb) {
  console.log('Creating sample setup file: couchpenter.json');
  fsx.copy(p.join(__dirname, '../examples/couchpenter.json'), 'couchpenter.json', cb);
};

// NOTE: pardon this method to tasks mapping,
// needed to preserve backward compatibility w/ v0.1.x

/**
 * Create databases and documents, overwrite if documents exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUp = function (cb) {
  this._task(['createDatabases', 'saveDocuments'], cb);
};

/**
 * Create databases only.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDatabases = function (cb) {
  this._task(['createDatabases'], cb);
};

/**
 * Create documents only, does not overwrite if exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDocuments = function (cb) {
  this._task(['createDocuments'], cb);
};

/**
 * Create documents only, overwrite if exist.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.setUpDocumentsOverwrite = function (cb) {
  this._task(['saveDocuments'], cb);
};

/**
 * Alias for tearDownDatabases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDown = function (cb) {
  this.tearDownDatabases(cb);
};

/**
 * Delete databases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDownDatabases = function (cb) {
  this._task(['removeDatabases'], cb);
};

/**
 * Delete documents.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.tearDownDocuments = function (cb) {
  this._task(['removeDocuments'], cb);
};

/**
 * Alias for resetDocuments.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.reset = function (cb) {
  this.resetDocuments(cb);
};

/**
 * Delete then recreate databases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.resetDatabases = function (cb) {
  this._task(['removeDatabases', 'createDatabases'], cb);
};

/**
 * Delete then recreate documents only.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.resetDocuments = function (cb) {
  this._task(['removeDatabases', 'createDatabases', 'createDocuments'], cb);
};

/**
 * Alias for cleanDatabases.
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.clean = function (cb) {
  this.cleanDatabases(cb);
};

/**
 * Delete unknown databases (not configured in setup file).
 *
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.cleanDatabases = function (cb) {
  this._task(['cleanDatabases'], cb);
};

/**
 * Warm up views specified in design documents.
 * If schedule is specified, then views warm up will be scheduled using cron.
 * Otherwise, it's just a once off.
 *
 * @param {String} schedule: cron scheduling definition in standard * * * * * format
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype.warmViews = function(schedule, cb) {
  var self = this;
  if (cb) {
    new cron.CronJob(schedule, function () {
      self._task(['warmViews'], cb);
    }, null, true);
  } else {
    cb = schedule;
    this._task(['warmViews'], cb);
  }
};

/**
 * Execute database tasks in series order.
 *
 * @param {Array} tasks: the tasks to execute
 * @param {Function} cb: standard cb(err, result) callback
 */
Couchpenter.prototype._task = function (tasks, cb) {
  var setup = this.opts.setup || JSON.parse(bag.cli.lookupFile(this.opts.setupFile)),
    db = new Db(this.url),
    asyncTasks = [],
    self = this;

  // prefix database names if optional prefix is specified
  if (this.opts.prefix) {
    _.keys(setup).forEach(function (dbName) {
      setup[self.opts.prefix + dbName] = setup[dbName];
      delete setup[dbName];
    });
  }

  tasks.forEach(function (taskName) {
    asyncTasks.push(function (cb) {
      var data = (taskName.match(/Databases$/)) ?  _.keys(setup) : self._docs(setup, self.opts.dir);
      db[taskName](data, cb);
    });
  });

  async.series(asyncTasks, function (err, results) {
    var combined = [];
    results.forEach(function (result) {
      combined = combined.concat(result);
    });
    cb(err, combined);
  });
};

/**
 * Process documents in Couchpenter setup:
 * - if it's an object, leave as-is
 * - if it's a json file location (ending with .json) then assign the content of the file as the document
 * - otherwise assume it's a module, and require it
 * Location of file and module is relative to this.opts.dir .
 *
 * @param {Object} setup: Couchpenter setup
 * @param {String} dir: base directory relative to file location
 * @return {Object} setup with documents processed
 */
Couchpenter.prototype._docs = function (setup, dir) {
  _.keys(setup).forEach(function (dbName) {
    for (var i = 0, ln = setup[dbName].length; i < ln; i += 1) {
      var item = setup[dbName][i];
      if (_.isString(item)) {
        if (item.match(/\.json$/)) {
          setup[dbName][i] = JSON.parse(fs.readFileSync(p.join(dir, item)));
        } else {
          setup[dbName][i] = require(p.join(dir, item));
        }
      } else if (!Array.isArray(item) && !_.isObject(item)) {
        throw new Error('Invalid document ' + item + ' in db ' + dbName + ', only object and string allowed');
      }
    }
  });
  return setup;
};

module.exports = Couchpenter;
<|endoftext|>