<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 *
 * TODO :
 *     - files extensions matching optimisation
 *     - add optimised mode that do not return deep chain handle for any HTTP verb (to be used when stores are used from within a chain)
 *     - check range object usage in chain
 *
 *
 *
 * - CLONE STORES : reset and initialised = false
 */

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require", "../utils", "../errors", "../promise", "../compiler"], function(require, utils, errors, prom) {

    var argToArr = Array.prototype.slice;
    var stores = {};
    //deep.extensions = [];
    /**
     * start chain setted with a certain store
     * @example
     *
     * Store("json").get("/campaign/").log();

     *  ...
     *  Store("campaign").get("?").log()
     *
     *
     * @class Store
     * @constructor
     */
    stores.store = function(store) {
        //console.log("Store(name) : ", name)
        return deep(store || {})
        .done(function(){
            if(!this._storeDef)
                this.store(store);
        })
        .done(function(s) {
            //console.log("store init trough chain : ", handler.provider);
            this._nodes = [deep.nodes.root(s)];
        });
    };

    stores.store.prepare = function(store)
    {
        if(typeof store === 'string')
            store = deep.protocol(store);
        if(!store)
            return errors.Store("no store found with : ",store);
        //console.log("PREPARE CONTEXT --------> ", deep.context.modes);
        if(store._deep_ocm_)
            return store.flatten()
            .done(function(store){
            	//console.log("Store prepare OCM flattened : ", deep.context.modes, store());
                store = store();
                if(store.init)
                    return store.init() || store;
            });
        if(store.init)
            return store.init() || store;
        return store;
    }

    /**
     * Empty class : Just there to get instanceof working (be warning with iframe issue in that cases).
     * @class Store
     * @constructor
     */
    var Store = stores.Store = function(protocol) {
        //console.log("Store : protocol : ", protocol);
        if (protocol && typeof protocol === 'object')
            utils.up(protocol, this);
        else
            this.protocol = protocol || this.protocol;

        if (this.protocol)
            deep.protocol(this.protocol, this);
        this._deep_store_ = true;
    };

    Store.prototype = {
        _deep_restrictable_:["get","range","post","put","patch","del","rpc","bulk"]
    };

    stores.Store.forbidden = function(message) {
        return function(any, options) {
            return errors.Forbidden(message);
        };
    };
    stores.store.Restrictions = function() {
        var restrictions = {};
        for (var i in arguments)
            restrictions[arguments[i]] = Store.forbidden();
        return restrictions;
    };
    stores.store.AllowOnly = function() {
        return {
            inner:{},
            allowable:argToArr.call(arguments),
            _deep_compiler_ : true,
            _deep_allow_only_ : true,
            up : function() {                 // apply arguments (up) on inner-layer : so merge
                // console.log("allow only up : ", arguments);
                var res = this.inner;
                for (var i = 0, len = arguments.length; i < len; ++i)
                {
                    var argi = arguments[i];
                    if(argi._deep_allow_only_)
                    {
                        res = utils.up(argi.inner, res);
                        this.allowable = utils.up(argi.allowable, this.allowable);
                    }
                    else
                        res = utils.up(argi, res);
                }
                this.inner = res;
                return this;
            },
            bottom : function() {             // apply arguments (bottom) on this : so apply restrictions

                // console.log("allow only bottom : ", arguments);
                var res = this.inner, toKeep = null;
                for (var len = arguments.length-1; len >= 0; --len)
                {
                    var argi = arguments[len];
                    if(argi._deep_allow_only_)
                    {
                        res = utils.bottom(argi.inner, res);
                        this.allowable = utils.up(argi.allowable, this.allowable);
                    }
                    else
                        res = utils.bottom(argi, res);
                }
                if(res._deep_restrictable_)
                {
                    var toRemove = deep.utils.removeInside(res._deep_restrictable_.slice(), this.allowable);
                    for(var j = 0, lenj = toRemove.length; j < lenj; ++j)
                        res[toRemove[j]] = Store.forbidden();
                }
                else
                {
                    if(!toKeep)
                    {
                        toKeep = {};
                        for(var l = 0, lenl = this.allowable.length; l < lenl; ++l)
                            toKeep[this.allowable[l]] = true;
                    }
                    for(var k in res)
                        if(!toKeep[k])
                            res[k] = Store.forbidden();
                }
                // console.log("allow only bottom : res :  ", res);
                return res;
            }
        };
    };

    stores.store.filterPrivate = function(method) {
        return function(result) {
            //console.log("private check : ", this, result);
            if (!this.schema)
                return result;
            var schema = this.schema;
            if (schema._deep_ocm_)
                schema = schema(method);
            var res = utils.remove(result, ".//!?_schema.private=true", this.schema);
            return result;
        };
    };

    return stores;
});

<commit_msg>return correct store in store.prepare<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 *
 *
 * TODO :
 *     - files extensions matching optimisation
 *     - add optimised mode that do not return deep chain handle for any HTTP verb (to be used when stores are used from within a chain)
 *     - check range object usage in chain
 *
 *
 *
 * - CLONE STORES : reset and initialised = false
 */

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require", "../utils", "../errors", "../promise", "../compiler"], function(require, utils, errors, prom) {

    var argToArr = Array.prototype.slice;
    var stores = {};
    //deep.extensions = [];
    /**
     * start chain setted with a certain store
     * @example
     *
     * Store("json").get("/campaign/").log();

     *  ...
     *  Store("campaign").get("?").log()
     *
     *
     * @class Store
     * @constructor
     */
    stores.store = function(store) {
        //console.log("Store(name) : ", name)
        return deep(store || {})
        .done(function(){
            if(!this._storeDef)
                this.store(store);
        })
        .done(function(s) {
            //console.log("store init trough chain : ", handler.provider);
            this._nodes = [deep.nodes.root(s)];
        });
    };

    stores.store.prepare = function(store)
    {
        if(typeof store === 'string')
            store = deep.protocol(store);
        if(!store)
            return errors.Store("no store found with : ",store);
        //console.log("PREPARE CONTEXT --------> ", deep.context.modes, store._deep_ocm_);
        if(store._deep_ocm_)
            return store.flatten()
            .done(function(store){
                store = store();
                //console.log("Store prepare OCM flattened : ", deep.context.modes, store);
                if(store.init)
                    return store.init() || store;
                return store;
            });
        if(store.init)
            return store.init() || store;
        return store;
    }

    /**
     * Empty class : Just there to get instanceof working (be warning with iframe issue in that cases).
     * @class Store
     * @constructor
     */
    var Store = stores.Store = function(protocol) {
        //console.log("Store : protocol : ", protocol);
        if (protocol && typeof protocol === 'object')
            utils.up(protocol, this);
        else
            this.protocol = protocol || this.protocol;

        if (this.protocol)
            deep.protocol(this.protocol, this);
        this._deep_store_ = true;
    };

    Store.prototype = {
        _deep_restrictable_:["get","range","post","put","patch","del","rpc","bulk"]
    };

    stores.Store.forbidden = function(message) {
        return function(any, options) {
            return errors.Forbidden(message);
        };
    };
    stores.store.Restrictions = function() {
        var restrictions = {};
        for (var i in arguments)
            restrictions[arguments[i]] = Store.forbidden();
        return restrictions;
    };
    stores.store.AllowOnly = function() {
        return {
            inner:{},
            allowable:argToArr.call(arguments),
            _deep_compiler_ : true,
            _deep_allow_only_ : true,
            up : function() {                 // apply arguments (up) on inner-layer : so merge
                // console.log("allow only up : ", arguments);
                var res = this.inner;
                for (var i = 0, len = arguments.length; i < len; ++i)
                {
                    var argi = arguments[i];
                    if(argi._deep_allow_only_)
                    {
                        res = utils.up(argi.inner, res);
                        this.allowable = utils.up(argi.allowable, this.allowable);
                    }
                    else
                        res = utils.up(argi, res);
                }
                this.inner = res;
                return this;
            },
            bottom : function() {             // apply arguments (bottom) on this : so apply restrictions

                // console.log("allow only bottom : ", arguments);
                var res = this.inner, toKeep = null;
                for (var len = arguments.length-1; len >= 0; --len)
                {
                    var argi = arguments[len];
                    if(argi._deep_allow_only_)
                    {
                        res = utils.bottom(argi.inner, res);
                        this.allowable = utils.up(argi.allowable, this.allowable);
                    }
                    else
                        res = utils.bottom(argi, res);
                }
                if(res._deep_restrictable_)
                {
                    var toRemove = deep.utils.removeInside(res._deep_restrictable_.slice(), this.allowable);
                    for(var j = 0, lenj = toRemove.length; j < lenj; ++j)
                        res[toRemove[j]] = Store.forbidden();
                }
                else
                {
                    if(!toKeep)
                    {
                        toKeep = {};
                        for(var l = 0, lenl = this.allowable.length; l < lenl; ++l)
                            toKeep[this.allowable[l]] = true;
                    }
                    for(var k in res)
                        if(!toKeep[k])
                            res[k] = Store.forbidden();
                }
                // console.log("allow only bottom : res :  ", res);
                return res;
            }
        };
    };

    stores.store.filterPrivate = function(method) {
        return function(result) {
            //console.log("private check : ", this, result);
            if (!this.schema)
                return result;
            var schema = this.schema;
            if (schema._deep_ocm_)
                schema = schema(method);
            var res = utils.remove(result, ".//!?_schema.private=true", this.schema);
            return result;
        };
    };

    return stores;
});

<|endoftext|>


<commit_before>import React, { Component } from 'react';

export default class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = { term: ''};

    this.onInputChange = this.onInputChange.bind(this);
    this.onFormSubmit = this.onFormSubmit.bind(this);
  }

  onInputChange(event) {
    this.setState({ term: event.target.value });
  }

  onFormSubmit(event) {
    // fetch weather data
    event.preventDefault();
  }

  render() {
    return (
      <form className="input-group" onSubmit={this.onFormSubmit}>
        <input
          placeholder="Get a five-day forecast in your favorite cities"
          className="form-control"
          value={this.state.term}
          onChange={this.onInputChange}
        />

          <button type="submit" className="btn btn-secondary">Submit</button>

      </form>
    );
  }
}
<commit_msg>connected fetchWeather action to SearchBar container<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchWeather } from '../actions/index';

class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = { term: ''};

    this.onInputChange = this.onInputChange.bind(this);
    this.onFormSubmit = this.onFormSubmit.bind(this);
  }

  onInputChange(event) {
    this.setState({ term: event.target.value });
  }

  onFormSubmit(event) {
    event.preventDefault();
    // fetch weather data
    this.props.fetchWeather(this.state.term);
    this.setState({ term: '' });
  }

  render() {
    return (
      <form className="input-group" onSubmit={this.onFormSubmit}>
        <input
          placeholder="Get a five-day forecast in your favorite cities"
          className="form-control"
          value={this.state.term}
          onChange={this.onInputChange}
        />

          <button type="submit" className="btn btn-secondary">Submit</button>

      </form>
    );
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ fetchWeather }, dispatch);
}

export default connect(null, mapDispatchToProps)(SearchBar);
<|endoftext|>


<commit_before>
var
  sys    = require('sys'),
  fs     = require('fs')
  events = require('events');

exports.levels = {debug: 0, info:1, warn:2, fatal:3};
exports.revLevels  = {}
for (var lName in exports.levels) {
  var lVal = exports.levels[lName];
  exports.revLevels[lVal]= lName;
}

var StreamLogger = exports.StreamLogger = function() {
  this.filePaths = new Array();
  for (var i = arguments.length; i != 0; i--)
    this.filePaths.push(arguments[i - 1]);
  this.fstreams   = [];

  var level = this.level  = exports.levels.info;
  //Build a reverse mapping of level values to keys, for fast lookup later
  //Events support
  this.emitter = new events.EventEmitter();

  //Setup a method for each log level
  for (var logLevel in exports.levels) {
    this[logLevel] = (function(logLevel) {
      return function (message,callback) {
        this.logAtLevel(message, exports.levels[logLevel], callback);
      }
    })(logLevel);
  }

  this.open();
};

//Create write streams for all the files, emit 'streamsOpen', if/when
//all streams open. Will fire callback after as well
StreamLogger.prototype.open = function(callback) {
  var emitter = this.emitter;
  for (var i = this.filePaths.length; i != 0; i--) {
    var filePath = this.filePaths[i - 1];
    var unopenedFilePathCount = this.filePaths.length;
    var stream = fs.createWriteStream(filePath,
        {flags: 'a', mode: 0644, encoding: 'utf8'}
      )
      .addListener('open', function(fd) {
        unopenedFilePathCount--;
        if (unopenedFilePathCount == 0) {
          emitter.emit("open");
          if (callback)
            callback();
        }
      })
      .addListener('error', function(err) {
        emitter.emit(err,filePath)
      });
    this.fstreams.push(stream);
  }
};

//Close all write streams, fire the callback after all streams are closed
//Also emits 'close' after all streams are closed
StreamLogger.prototype.close = function(callback) {
  var openStreamsCount = this.fstreams.length,
      emitter  = this.emitter,
      slSelf = this;
      this.emittedClose = false; //Ensures we only emit 'close' once
  for (var i = openStreamsCount; i !=0; i--) {
    this.fstreams[i - 1].close(function () {
      openStreamsCount--;
      if (openStreamsCount == 0 && ! this.emittedClose) {
        //We're done closing, so emit the callbacks, then remove the fstreams
        slSelf.fstreams = [];

        emitter.emit("close");
        if (callback)
          callback();
      }
    });
  }
};

StreamLogger.prototype.reopen = function(callback) {
  var slSelf = this;
  this.close(function() {
    slSelf.open(function() {
      if (callback)
        callback();
    });
  });
};

StreamLogger.prototype.logAtLevel = function(message,level,callback) {
  var levelName = exports.revLevels[level];
  this.emitter.emit('message', message, levelName)

  if (level < this.level)
    return false

  this.emitter.emit('loggedMessage', message, levelName)

  var outMessage = (new Date).toUTCString() + ' - ' +
                     levelName + ': ' + message;

  for (var i = this.fstreams.length; i != 0; i--) {
    var fstream = this.fstreams[i - 1];
    //Ideally we could trap the errors #write creates, I'm not sure
    //if thats possible though
    if (fstream.writeable) {
      fstream.write(outMessage + "\n");
      if (callback)
        callback();
    }
    else
      this.emitter.emit('error',"Stream not writable", fstream.path);
  }
};
<commit_msg>Small syntax cleanup<commit_after>
var
  sys    = require('sys'),
  fs     = require('fs')
  events = require('events');

exports.levels = {debug: 0, info:1, warn:2, fatal:3};
//Build a reverse mapping of level values to keys, for fast lookup later
exports.revLevels  = {}
for (var lName in exports.levels) {
  var lVal = exports.levels[lName];
  exports.revLevels[lVal]= lName;
}

var StreamLogger = exports.StreamLogger = function() {
  this.filePaths = new Array();
  for (var i = arguments.length; i != 0; i--)
    this.filePaths.push(arguments[i - 1]);
  this.fstreams   = [];
  this.emitter = new events.EventEmitter();
  this.level = exports.levels.info;

  //Setup a method for each log level
  for (var logLevel in exports.levels) {
    this[logLevel] = (function(logLevel) {
      return function (message,callback) {
        this.logAtLevel(message, exports.levels[logLevel], callback);
      }
    })(logLevel);
  }

  this.open();
};

//Create write streams for all the files, emit 'streamsOpen', if/when
//all streams open. Will fire callback after as well
StreamLogger.prototype.open = function(callback) {
  var emitter = this.emitter;
  for (var i = this.filePaths.length; i != 0; i--) {
    var filePath = this.filePaths[i - 1];
    var unopenedFilePathCount = this.filePaths.length;
    var stream = fs.createWriteStream(filePath,
        {flags: 'a', mode: 0644, encoding: 'utf8'}
      )
      .addListener('open', function(fd) {
        unopenedFilePathCount--;
        if (unopenedFilePathCount == 0) {
          emitter.emit("open");
          if (callback)
            callback();
        }
      })
      .addListener('error', function(err) {
        emitter.emit(err,filePath)
      });
    this.fstreams.push(stream);
  }
};

//Close all write streams, fire the callback after all streams are closed
//Also emits 'close' after all streams are closed
StreamLogger.prototype.close = function(callback) {
  var openStreamsCount = this.fstreams.length,
      emitter  = this.emitter,
      slSelf = this;
      this.emittedClose = false; //Ensures we only emit 'close' once
  for (var i = openStreamsCount; i !=0; i--) {
    this.fstreams[i - 1].close(function () {
      openStreamsCount--;
      if (openStreamsCount == 0 && ! this.emittedClose) {
        //We're done closing, so emit the callbacks, then remove the fstreams
        slSelf.fstreams = [];

        emitter.emit("close");
        if (callback)
          callback();
      }
    });
  }
};

StreamLogger.prototype.reopen = function(callback) {
  var slSelf = this;
  this.close(function() {
    slSelf.open(function() {
      if (callback)
        callback();
    });
  });
};

StreamLogger.prototype.logAtLevel = function(message,level,callback) {
  var levelName = exports.revLevels[level];
  this.emitter.emit('message', message, levelName)

  if (level < this.level)
    return false

  this.emitter.emit('loggedMessage', message, levelName)

  var outMessage = (new Date).toUTCString() + ' - ' +
                     levelName + ': ' + message;

  for (var i = this.fstreams.length; i != 0; i--) {
    var fstream = this.fstreams[i - 1];
    //Ideally we could trap the errors #write creates, I'm not sure
    //if thats possible though
    if (fstream.writeable) {
      fstream.write(outMessage + "\n");
      if (callback)
        callback();
    }
    else
      this.emitter.emit('error',"Stream not writable", fstream.path);
  }
};
<|endoftext|>


<commit_before>// closure
var serviceHandler = function(service) {
  /*
   * Below are the supported options for the serviceHandler:
   *
   * - data (object): request data payload
   * - params (object): use to replace the tokens in the url
   * - query (object): use to build the query string appended to the url
   * - callback (function): callback to use, with a default which emits 'success' or 'error'
   *   event depending on res.ok value
   * - edit (function): callback to use, to tweak req if needed.
   * - timeout (number): milli seconds before a timeout error is thrown
   */
  return function(options) {
    options = options || {};
    var data = options.data || {};
    var params = options.params || {};
    var query = options.query || {};
    var callback = options.callback || null;
    var edit = options.edit || null;
    var timeout = options.timeout || 20000;

    var self = this;
    var req = this.request(service, data, params, query);

    // edit request if function defined
    if (edit && "function" === typeof edit) {
      edit(req);
    }

    var resolver = {};

    var p = new Promise(function (resolve, reject) {
      resolver = {
        resolve: resolve,
        reject: reject
      };
    });

    req.end(callback ? callback : function(res) {
      resolver[!res.error ? "resolve" : "reject"](res);
    });

    if (timeout) {
      req.xhr.timeout = timeout;
      req.xhr.ontimeout = function () {
        req.aborted = true;
        var response = new self.agent.Response(req);
        response.timeout = true;
        response.error = true;
        req.emit('abort', response);
      }
    }

    req.on('abort', function (res) {
      resolver.reject(res);
    });

    return p;
  };
};

function Api(config) {
  this.config = config;

  // create a hash-liked object where all the services handlers are registered
  this.api = Object.create(null);

  for (var name in config.services) {
    if (!Object.prototype.hasOwnProperty(this, name)) {
      // syntatic sugar: install a service handler available on
      // the api instance with service name
      this.api[name] = serviceHandler(name).bind(this);
    }
  }
}

Api.prototype = {
  paramsPattern: /:(\w+)/g,

  service: function(id) {
    return this.config.services[id];
  },

  url: function(id) {
    var url = this.config.baseUrl;
    var resource = this.service(id);

    // from time being it"s a simple map
    if (resource) {
      var path;

      if (typeof resource === "string") {
        path = resource;
      }

      if (typeof resource === "object" && resource.path !== undefined) {
        path = resource.path;
      }

      if (!path) {
        throw new Error("path is not defined for route $" + id);
      }

      if (path.match(/^https?:\/\//)) {
        url = path;
      }
      else {
        url += path[0] === "/" ? path : "/" + path;
      }
    }

    return url;
  },

  replaceUrl: function(url, params) {
    var tokens = url.match(this.paramsPattern);

    if (!tokens || !tokens.length) {
      return url;
    }

    for (var i = 0, len = tokens.length; i < len; i += 1) {
      var token = tokens[i];
      var name = token.substring(1);
      if (params[name]) {
        url = url.replace(token, params[name]);
      }
    }
    return url;
  },

  buildUrlQuery: function(query) {
    if (!query) {
      return "";
    }

    var queryString;

    if (typeof query === "string") {
      queryString = query;
    } else {
      var queryArgs = [];
      for (var queryArg in query) {
        queryArgs.push(queryArg + "=" + query[queryArg]);
      }
      queryString = queryArgs.join("&");
    }
    return queryString ? "?" + queryString : "";
  },

  buildUrl: function(id, params, query) {
    var url = this.url(id);

    if (params) {
      url = this.replaceUrl(url, params);
    }

    if (query) {
      url += this.buildUrlQuery(query);
    }

    return url;
  },

  request: function(id, data, params, query) {
    var service = this.service(id);
    var method = (typeof service === "string" ? "get" : service.method ||
      "get").toLowerCase();

    var options = {
      options: service.options,
      headers: service.headers
    }

    return this.sendRequest(method, this.buildUrl(id, params, query), data, options);
  },

  sendRequest: function (method, url, data, opts) {
    opts = opts || {};

    if (!this.agent) {
      throw new Error("missing superagent or any api compatible agent.");
    }

    // fix for delete being a reserved word
    if (method === "delete") {
      method = "del";
    }

    // reset data for delete operation
    // kind of hack as request.del signature is different from others being `function(url, fn)`
    // instead of `function(url, data, fn)`
    if (method === "del") {
      data = undefined;
    }

    var request = this.agent[method];
    if (!request) {
      throw new Error("Unsupported method [" + method + "]");
    }

    var _req = request(url, data);

    // add global headers
    this._setHeaders(_req, this.config.headers);

    // add global options to request headers
    this._setOptions(_req, this.config.options);

    // add service options to request headers
    this._setOptions(_req, opts.options);

    // add service headers
    this._setHeaders(_req, opts.headers);

    // add runtime headers
    this._setHeaders(_req, this.headers);

    // set credentials
    if (this.config.withCredentials) {
      _req.withCredentials();
    }

    return _req;
  },

  addHeader: function(name, value) {
    this.headers = this.headers || {};
    this.headers[name] = value;
  },

  removeHeader: function(name) {
    if (this.headers && this.headers[name]) {
      delete this.headers[name];
    }
  },

  _setHeaders: function(req, headers) {
    for (var header in headers) {
      req.set(header, headers[header]);
    }
  },

  _setOptions: function(req, options) {
    for (var option in options) {
      req[option](options[option]);
    }
  }
};

export default Api;
<commit_msg>feat(api): support no configuration at init<commit_after>// closure
var serviceHandler = function(service) {
  /*
   * Below are the supported options for the serviceHandler:
   *
   * - data (object): request data payload
   * - params (object): use to replace the tokens in the url
   * - query (object): use to build the query string appended to the url
   * - callback (function): callback to use, with a default which emits 'success' or 'error'
   *   event depending on res.ok value
   * - edit (function): callback to use, to tweak req if needed.
   * - timeout (number): milli seconds before a timeout error is thrown
   */
  return function(options) {
    options = options || {};
    var data = options.data || {};
    var params = options.params || {};
    var query = options.query || {};
    var callback = options.callback || null;
    var edit = options.edit || null;
    var timeout = options.timeout || 20000;

    var self = this;
    var req = this.request(service, data, params, query);

    // edit request if function defined
    if (edit && "function" === typeof edit) {
      edit(req);
    }

    var resolver = {};

    var p = new Promise(function (resolve, reject) {
      resolver = {
        resolve: resolve,
        reject: reject
      };
    });

    req.end(callback ? callback : function(res) {
      resolver[!res.error ? "resolve" : "reject"](res);
    });

    if (timeout) {
      req.xhr.timeout = timeout;
      req.xhr.ontimeout = function () {
        req.aborted = true;
        var response = new self.agent.Response(req);
        response.timeout = true;
        response.error = true;
        req.emit('abort', response);
      }
    }

    req.on('abort', function (res) {
      resolver.reject(res);
    });

    return p;
  };
};

function Api(config) {
  this.config = config || {};

  // create a hash-liked object where all the services handlers are registered
  this.api = Object.create(null);

  // support undefined configuration in constructor
  if (!config) {
    return;
  }

  for (var name in config.services) {
    if (!Object.prototype.hasOwnProperty(this, name)) {
      // syntatic sugar: install a service handler available on
      // the api instance with service name
      this.api[name] = serviceHandler(name).bind(this);
    }
  }
}

Api.prototype = {
  paramsPattern: /:(\w+)/g,

  service: function(id) {
    return this.config.services[id];
  },

  url: function(id) {
    var url = this.config.baseUrl;
    var resource = this.service(id);

    // from time being it"s a simple map
    if (resource) {
      var path;

      if (typeof resource === "string") {
        path = resource;
      }

      if (typeof resource === "object" && resource.path !== undefined) {
        path = resource.path;
      }

      if (!path) {
        throw new Error("path is not defined for route $" + id);
      }

      if (path.match(/^https?:\/\//)) {
        url = path;
      }
      else {
        url += path[0] === "/" ? path : "/" + path;
      }
    }

    return url;
  },

  replaceUrl: function(url, params) {
    var tokens = url.match(this.paramsPattern);

    if (!tokens || !tokens.length) {
      return url;
    }

    for (var i = 0, len = tokens.length; i < len; i += 1) {
      var token = tokens[i];
      var name = token.substring(1);
      if (params[name]) {
        url = url.replace(token, params[name]);
      }
    }
    return url;
  },

  buildUrlQuery: function(query) {
    if (!query) {
      return "";
    }

    var queryString;

    if (typeof query === "string") {
      queryString = query;
    } else {
      var queryArgs = [];
      for (var queryArg in query) {
        queryArgs.push(queryArg + "=" + query[queryArg]);
      }
      queryString = queryArgs.join("&");
    }
    return queryString ? "?" + queryString : "";
  },

  buildUrl: function(id, params, query) {
    var url = this.url(id);

    if (params) {
      url = this.replaceUrl(url, params);
    }

    if (query) {
      url += this.buildUrlQuery(query);
    }

    return url;
  },

  request: function(id, data, params, query) {
    var service = this.service(id);
    var method = (typeof service === "string" ? "get" : service.method ||
      "get").toLowerCase();

    var options = {
      options: service.options,
      headers: service.headers
    }

    return this.sendRequest(method, this.buildUrl(id, params, query), data, options);
  },

  sendRequest: function (method, url, data, opts) {
    opts = opts || {};

    if (!this.agent) {
      throw new Error("missing superagent or any api compatible agent.");
    }

    // fix for delete being a reserved word
    if (method === "delete") {
      method = "del";
    }

    // reset data for delete operation
    // kind of hack as request.del signature is different from others being `function(url, fn)`
    // instead of `function(url, data, fn)`
    if (method === "del") {
      data = undefined;
    }

    var request = this.agent[method];
    if (!request) {
      throw new Error("Unsupported method [" + method + "]");
    }

    var _req = request(url, data);

    // add global headers
    this._setHeaders(_req, this.config.headers);

    // add global options to request headers
    this._setOptions(_req, this.config.options);

    // add service options to request headers
    this._setOptions(_req, opts.options);

    // add service headers
    this._setHeaders(_req, opts.headers);

    // add runtime headers
    this._setHeaders(_req, this.headers);

    // set credentials
    if (this.config.withCredentials) {
      _req.withCredentials();
    }

    return _req;
  },

  addHeader: function(name, value) {
    this.headers = this.headers || {};
    this.headers[name] = value;
  },

  removeHeader: function(name) {
    if (this.headers && this.headers[name]) {
      delete this.headers[name];
    }
  },

  _setHeaders: function(req, headers) {
    for (var header in headers) {
      req.set(header, headers[header]);
    }
  },

  _setOptions: function(req, options) {
    for (var option in options) {
      req[option](options[option]);
    }
  }
};

export default Api;
<|endoftext|>


<commit_before>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

/**
 * @fileOverview Manages synchronization of filter subscriptions.
 */

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

let {TimeLine} = require("timeline");
let {Downloader, Downloadable, MILLIS_IN_SECOND, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
let {Filter, CommentFilter} = require("filterClasses");
let {FilterStorage} = require("filterStorage");
let {FilterNotifier} = require("filterNotifier");
let {Prefs} = require("prefs");
let {Subscription, DownloadableSubscription} = require("subscriptionClasses");
let {Utils} = require("utils");

let INITIAL_DELAY = 6 * MILLIS_IN_MINUTE;
let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
let DEFAULT_EXPIRATION_INTERVAL = 5 * MILLIS_IN_DAY;

/**
 * The object providing actual downloading functionality.
 * @type Downloader
 */
let downloader = null;

/**
 * This object is responsible for downloading filter subscriptions whenever
 * necessary.
 * @class
 */
let Synchronizer = exports.Synchronizer =
{
  /**
   * Called on module startup.
   */
  init: function()
  {
    TimeLine.enter("Entered Synchronizer.init()");
    downloader = new Downloader(this.getDownloadables.bind(this), INITIAL_DELAY, CHECK_INTERVAL);

    onShutdown.add(function()
    {
      downloader.cancel();
    });

    downloader.onExpirationChange = this._onExpirationChange.bind(this);
    downloader.onDownloadStarted = this._onDownloadStarted.bind(this);
    downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this);
    downloader.onDownloadError = this._onDownloadError.bind(this);

    TimeLine.leave("Synchronizer.init() done");
  },

  /**
   * Checks whether a subscription is currently being downloaded.
   * @param {String} url  URL of the subscription
   * @return {Boolean}
   */
  isExecuting: function(url)
  {
    return downloader.isDownloading(url);
  },

  /**
   * Starts the download of a subscription.
   * @param {DownloadableSubscription} subscription  Subscription to be downloaded
   * @param {Boolean} manual  true for a manually started download (should not trigger fallback requests)
   */
  execute: function(subscription, manual)
  {
    downloader.download(this.getDownloadable(subscription, manual));
  },

  /**
   * Yields Downloadable instances for all subscriptions that can be downloaded.
   */
  getDownloadables: function()
  {
    if (!Prefs.subscriptions_autoupdate)
      return;

    for (let subscription of FilterStorage.subscriptions)
    {
      if (subscription instanceof DownloadableSubscription)
        yield this.getDownloadable(subscription, false);
    }
  },

  /**
   * Creates a Downloadable instance for a subscription.
   */
  getDownloadable: function(/**Subscription*/ subscription, /**Boolean*/ manual) /**Downloadable*/
  {
    let result = new Downloadable(subscription.url);
    if (subscription.lastDownload != subscription.lastSuccess)
      result.lastError = subscription.lastDownload * MILLIS_IN_SECOND;
    result.lastCheck = subscription.lastCheck * MILLIS_IN_SECOND;
    result.lastVersion = subscription.version;
    result.softExpiration = subscription.softExpiration * MILLIS_IN_SECOND;
    result.hardExpiration = subscription.expires * MILLIS_IN_SECOND;
    result.manual = manual;
    return result;
  },

  _onExpirationChange: function(downloadable)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    subscription.lastCheck = Math.round(downloadable.lastCheck / MILLIS_IN_SECOND);
    subscription.softExpiration = Math.round(downloadable.softExpiration / MILLIS_IN_SECOND);
    subscription.expires = Math.round(downloadable.hardExpiration / MILLIS_IN_SECOND);
  },

  _onDownloadStarted: function(downloadable)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    FilterNotifier.triggerListeners("subscription.downloadStatus", subscription);
  },

  _onDownloadSuccess: function(downloadable, responseText, errorCallback, redirectCallback)
  {
    let lines = responseText.split(/[\r\n]+/);
    let match = /\[Adblock(?:\s*Plus\s*([\d\.]+)?)?\]/i.exec(lines[0]);
    if (!match)
      return errorCallback("synchronize_invalid_data");
    let minVersion = match[1];

    // Don't remove parameter comments immediately but add them to a list first,
    // they need to be considered in the checksum calculation.
    let remove = [];
    let params = {
      redirect: null,
      homepage: null,
      title: null,
      version: null,
      expires: null
    };
    for (let i = 0; i < lines.length; i++)

    {
      let match = /^\s*!\s*(\w+)\s*:\s*(.*)/.exec(lines[i]);
      if (match)
      {
        let keyword = match[1].toLowerCase();
        let value = match[2];
        if (keyword in params)
        {
          params[keyword] = value;
          remove.push(i);
        }
        else if (keyword == "checksum")
        {
          lines.splice(i--, 1);
          let checksum = Utils.generateChecksum(lines);
          if (checksum && checksum != value.replace(/=+$/, ""))
            return errorCallback("synchronize_checksum_mismatch");
        }
      }
    }

    if (params.redirect)
      return redirectCallback(params.redirect);
    // Handle redirects
    let subscription = Subscription.fromURL(downloadable.redirectURL || downloadable.url);
    if (downloadable.redirectURL && downloadable.redirectURL != downloadable.url)

    {
      let oldSubscription = Subscription.fromURL(downloadable.url);
      subscription.title = oldSubscription.title;
      subscription.disabled = oldSubscription.disabled;
      subscription.lastCheck = oldSubscription.lastCheck;

      let listed = (oldSubscription.url in FilterStorage.knownSubscriptions);
      if (listed)
        FilterStorage.removeSubscription(oldSubscription);

      delete Subscription.knownSubscriptions[oldSubscription.url];

      if (listed)
        FilterStorage.addSubscription(subscription);
    }
    // The download actually succeeded
    subscription.lastSuccess = subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND);
    subscription.downloadStatus = "synchronize_ok";
    subscription.errors = 0;

    // Remove lines containing parameters
    for (let i = remove.length - 1; i >= 0; i--)
      lines.splice(remove[i], 1);

    // Process parameters
    if (params.homepage)

    {
      let uri = Utils.makeURI(params.homepage);
      if (uri && (uri.scheme == "http" || uri.scheme == "https"))
        subscription.homepage = uri.spec;
    }

    if (params.title)
    {
      subscription.title = params.title;
      subscription.fixedTitle = true;
    }
    else
      subscription.fixedTitle = false;

    subscription.version = (params.version ? parseInt(params.version, 10) : 0);

    let expirationInterval = DEFAULT_EXPIRATION_INTERVAL;
    if (params.expires)
    {
      let match = /^(\d+)\s*(h)?/.exec(params.expires);
      if (match)
      {
        let interval = parseInt(match[1], 10);
        if (match[2])
          expirationInterval = interval * MILLIS_IN_HOUR;
        else
          expirationInterval = interval * MILLIS_IN_DAY;
      }
    }

    let [softExpiration, hardExpiration] = downloader.processExpirationInterval(expirationInterval);
    subscription.softExpiration = Math.round(softExpiration / MILLIS_IN_SECOND);
    subscription.expires = Math.round(hardExpiration / MILLIS_IN_SECOND);
    delete subscription.requiredVersion;
    delete subscription.upgradeRequired;
    if (minVersion)
    {
      let {addonVersion} = require("info");
      subscription.requiredVersion = minVersion;
      if (Services.vc.compare(minVersion, addonVersion) > 0)
        subscription.upgradeRequired = true;
    }

    // Process filters
    lines.shift();
    let filters = [];
    for (let line of lines)
    {
      line = Filter.normalize(line);
      if (line)
        filters.push(Filter.fromText(line));
    }

    FilterStorage.updateSubscriptionFilters(subscription, filters);

    return undefined;
  },

  _onDownloadError: function(downloadable, downloadURL, error, channelStatus, responseStatus, redirectCallback)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND);
    subscription.downloadStatus = error;

    // Request fallback URL if necessary - for automatic updates only
    if (!downloadable.manual)
    {
      subscription.errors++;

      if (redirectCallback && subscription.errors >= Prefs.subscriptions_fallbackerrors && /^https?:\/\//i.test(subscription.url))
      {
        subscription.errors = 0;

        let fallbackURL = Prefs.subscriptions_fallbackurl;
        let {addonVersion} = require("info");
        fallbackURL = fallbackURL.replace(/%VERSION%/g, encodeURIComponent(addonVersion));
        fallbackURL = fallbackURL.replace(/%SUBSCRIPTION%/g, encodeURIComponent(subscription.url));
        fallbackURL = fallbackURL.replace(/%URL%/g, encodeURIComponent(downloadURL));
        fallbackURL = fallbackURL.replace(/%ERROR%/g, encodeURIComponent(error));
        fallbackURL = fallbackURL.replace(/%CHANNELSTATUS%/g, encodeURIComponent(channelStatus));
        fallbackURL = fallbackURL.replace(/%RESPONSESTATUS%/g, encodeURIComponent(responseStatus));

        let request = new XMLHttpRequest();
        request.mozBackgroundRequest = true;
        request.open("GET", fallbackURL);
        request.overrideMimeType("text/plain");
        request.channel.loadFlags = request.channel.loadFlags |
                                    request.channel.INHIBIT_CACHING |
                                    request.channel.VALIDATE_ALWAYS;
        request.addEventListener("load", function(ev)
        {
          if (onShutdown.done)
            return;

          if (!(subscription.url in FilterStorage.knownSubscriptions))
            return;

          let match = /^(\d+)(?:\s+(\S+))?$/.exec(request.responseText);
          if (match && match[1] == "301" && match[2] && /^https?:\/\//i.test(match[2])) // Moved permanently
            redirectCallback(match[2]);
          else if (match && match[1] == "410")        // Gone
          {
            let data = "[Adblock]\n" + subscription.filters.map((f) => f.text).join("\n");
            redirectCallback("data:text/plain," + encodeURIComponent(data));
          }
        }, false);
        request.send(null);
      }

    }

  },
};
Synchronizer.init();<commit_msg>Removing fallbackurl function<commit_after>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

/**
 * @fileOverview Manages synchronization of filter subscriptions.
 */

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

let {TimeLine} = require("timeline");
let {Downloader, Downloadable, MILLIS_IN_SECOND, MILLIS_IN_MINUTE, MILLIS_IN_HOUR, MILLIS_IN_DAY} = require("downloader");
let {Filter, CommentFilter} = require("filterClasses");
let {FilterStorage} = require("filterStorage");
let {FilterNotifier} = require("filterNotifier");
let {Prefs} = require("prefs");
let {Subscription, DownloadableSubscription} = require("subscriptionClasses");
let {Utils} = require("utils");

let INITIAL_DELAY = 6 * MILLIS_IN_MINUTE;
let CHECK_INTERVAL = 1 * MILLIS_IN_HOUR;
let DEFAULT_EXPIRATION_INTERVAL = 5 * MILLIS_IN_DAY;

/**
 * The object providing actual downloading functionality.
 * @type Downloader
 */
let downloader = null;

/**
 * This object is responsible for downloading filter subscriptions whenever
 * necessary.
 * @class
 */
let Synchronizer = exports.Synchronizer =
{
  /**
   * Called on module startup.
   */
  init: function()
  {
    TimeLine.enter("Entered Synchronizer.init()");
    downloader = new Downloader(this.getDownloadables.bind(this), INITIAL_DELAY, CHECK_INTERVAL);

    onShutdown.add(function()
    {
      downloader.cancel();
    });

    downloader.onExpirationChange = this._onExpirationChange.bind(this);
    downloader.onDownloadStarted = this._onDownloadStarted.bind(this);
    downloader.onDownloadSuccess = this._onDownloadSuccess.bind(this);
    downloader.onDownloadError = this._onDownloadError.bind(this);

    TimeLine.leave("Synchronizer.init() done");
  },

  /**
   * Checks whether a subscription is currently being downloaded.
   * @param {String} url  URL of the subscription
   * @return {Boolean}
   */
  isExecuting: function(url)
  {
    return downloader.isDownloading(url);
  },

  /**
   * Starts the download of a subscription.
   * @param {DownloadableSubscription} subscription  Subscription to be downloaded
   * @param {Boolean} manual  true for a manually started download (should not trigger fallback requests)
   */
  execute: function(subscription, manual)
  {
    downloader.download(this.getDownloadable(subscription, manual));
  },

  /**
   * Yields Downloadable instances for all subscriptions that can be downloaded.
   */
  getDownloadables: function()
  {
    if (!Prefs.subscriptions_autoupdate)
      return;

    for (let subscription of FilterStorage.subscriptions)
    {
      if (subscription instanceof DownloadableSubscription)
        yield this.getDownloadable(subscription, false);
    }
  },

  /**
   * Creates a Downloadable instance for a subscription.
   */
  getDownloadable: function(/**Subscription*/ subscription, /**Boolean*/ manual) /**Downloadable*/
  {
    let result = new Downloadable(subscription.url);
    if (subscription.lastDownload != subscription.lastSuccess)
      result.lastError = subscription.lastDownload * MILLIS_IN_SECOND;
    result.lastCheck = subscription.lastCheck * MILLIS_IN_SECOND;
    result.lastVersion = subscription.version;
    result.softExpiration = subscription.softExpiration * MILLIS_IN_SECOND;
    result.hardExpiration = subscription.expires * MILLIS_IN_SECOND;
    result.manual = manual;
    return result;
  },

  _onExpirationChange: function(downloadable)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    subscription.lastCheck = Math.round(downloadable.lastCheck / MILLIS_IN_SECOND);
    subscription.softExpiration = Math.round(downloadable.softExpiration / MILLIS_IN_SECOND);
    subscription.expires = Math.round(downloadable.hardExpiration / MILLIS_IN_SECOND);
  },

  _onDownloadStarted: function(downloadable)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    FilterNotifier.triggerListeners("subscription.downloadStatus", subscription);
  },

  _onDownloadSuccess: function(downloadable, responseText, errorCallback, redirectCallback)
  {
    let lines = responseText.split(/[\r\n]+/);
    let match = /\[Adblock(?:\s*Plus\s*([\d\.]+)?)?\]/i.exec(lines[0]);
    if (!match)
      return errorCallback("synchronize_invalid_data");
    let minVersion = match[1];

    // Don't remove parameter comments immediately but add them to a list first,
    // they need to be considered in the checksum calculation.
    let remove = [];
    let params = {
      redirect: null,
      homepage: null,
      title: null,
      version: null,
      expires: null
    };
    for (let i = 0; i < lines.length; i++)

    {
      let match = /^\s*!\s*(\w+)\s*:\s*(.*)/.exec(lines[i]);
      if (match)
      {
        let keyword = match[1].toLowerCase();
        let value = match[2];
        if (keyword in params)
        {
          params[keyword] = value;
          remove.push(i);
        }
        else if (keyword == "checksum")
        {
          lines.splice(i--, 1);
          let checksum = Utils.generateChecksum(lines);
          if (checksum && checksum != value.replace(/=+$/, ""))
            return errorCallback("synchronize_checksum_mismatch");
        }
      }
    }

    if (params.redirect)
      return redirectCallback(params.redirect);
    // Handle redirects
    let subscription = Subscription.fromURL(downloadable.redirectURL || downloadable.url);
    if (downloadable.redirectURL && downloadable.redirectURL != downloadable.url)

    {
      let oldSubscription = Subscription.fromURL(downloadable.url);
      subscription.title = oldSubscription.title;
      subscription.disabled = oldSubscription.disabled;
      subscription.lastCheck = oldSubscription.lastCheck;

      let listed = (oldSubscription.url in FilterStorage.knownSubscriptions);
      if (listed)
        FilterStorage.removeSubscription(oldSubscription);

      delete Subscription.knownSubscriptions[oldSubscription.url];

      if (listed)
        FilterStorage.addSubscription(subscription);
    }
    // The download actually succeeded
    subscription.lastSuccess = subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND);
    subscription.downloadStatus = "synchronize_ok";
    subscription.errors = 0;

    // Remove lines containing parameters
    for (let i = remove.length - 1; i >= 0; i--)
      lines.splice(remove[i], 1);

    // Process parameters
    if (params.homepage)

    {
      let uri = Utils.makeURI(params.homepage);
      if (uri && (uri.scheme == "http" || uri.scheme == "https"))
        subscription.homepage = uri.spec;
    }

    if (params.title)
    {
      subscription.title = params.title;
      subscription.fixedTitle = true;
    }
    else
      subscription.fixedTitle = false;

    subscription.version = (params.version ? parseInt(params.version, 10) : 0);

    let expirationInterval = DEFAULT_EXPIRATION_INTERVAL;
    if (params.expires)
    {
      let match = /^(\d+)\s*(h)?/.exec(params.expires);
      if (match)
      {
        let interval = parseInt(match[1], 10);
        if (match[2])
          expirationInterval = interval * MILLIS_IN_HOUR;
        else
          expirationInterval = interval * MILLIS_IN_DAY;
      }
    }

    let [softExpiration, hardExpiration] = downloader.processExpirationInterval(expirationInterval);
    subscription.softExpiration = Math.round(softExpiration / MILLIS_IN_SECOND);
    subscription.expires = Math.round(hardExpiration / MILLIS_IN_SECOND);
    delete subscription.requiredVersion;
    delete subscription.upgradeRequired;
    if (minVersion)
    {
      let {addonVersion} = require("info");
      subscription.requiredVersion = minVersion;
      if (Services.vc.compare(minVersion, addonVersion) > 0)
        subscription.upgradeRequired = true;
    }

    // Process filters
    lines.shift();
    let filters = [];
    for (let line of lines)
    {
      line = Filter.normalize(line);
      if (line)
        filters.push(Filter.fromText(line));
    }

    FilterStorage.updateSubscriptionFilters(subscription, filters);

    return undefined;
  },

  _onDownloadError: function(downloadable, downloadURL, error, channelStatus, responseStatus, redirectCallback)
  {
    let subscription = Subscription.fromURL(downloadable.url);
    subscription.lastDownload = Math.round(Date.now() / MILLIS_IN_SECOND);
    subscription.downloadStatus = error;
  },
};
Synchronizer.init();<|endoftext|>


<commit_before>/* eslint consistent-this: 0 */
'use strict';

var _ = require('lodash');
var log = require('gulp-util').log;

var Configuration = require('../configuration');
var metadata = require('./metadata');

function ConfigurableTaskFactory(stuff, recipeFactory, registry, settings) {
	this.stuff = stuff;
	this.recipeFactory = recipeFactory;
	this.registry = registry;
	this.settings = settings;
}

function isVisible(task) {
	return task.visibility === Configuration.CONSTANT.VISIBILITY.NORMAL || !('visibility' in task);
}

function isDisabled(task) {
	return task.visibility === Configuration.CONSTANT.VISIBILITY.DISABLED;
}

ConfigurableTaskFactory.prototype.one = function (prefix, rawConfig, parentConfig) {
	var self, configs, taskInfo, recipe, task, tasks;

	self = this;

	taskInfo = Configuration.getTaskRuntimeInfo(rawConfig);

	recipe = this.recipeFactory.create(taskInfo);
	if (!recipe) {
		log('configure', "warning: can't infer to a proper recipe task: " + taskInfo.name);
		return null;
	}

	configs = Configuration.sort(taskInfo, rawConfig, parentConfig, recipe.schema);
	if (isDisabled(configs.taskInfo)) {
		return null;
	}

	task = this.create(prefix, taskInfo, configs.taskConfig, recipe);

	if (configs.taskInfo.task || _.size(configs.subTaskConfigs)) {
		if (recipe.type === 'task') {
			log('configure', "warning: recipe don't accept sub tasks");
		} else {
			if (_.size(configs.subTaskConfigs) !== 0 && configs.taskInfo.task) {
				log('configure', 'warning: both "task" and sub tasks provided, ');
			}
			tasks = configs.taskInfo.task || configs.subTaskConfigs;
			tasks = self.multiple(_prefix(), tasks, configs.taskConfig);
			metadata.tree(task, tasks);
			task.tasks = tasks;
		}
	}
	return task;

	function _prefix() {
		return isVisible(configs.taskInfo) ? prefix + configs.taskInfo.name + ':' : prefix;
	}
};

ConfigurableTaskFactory.prototype.multiple = function (prefix, subTaskConfigs, parentConfig) {
	var self;

	self = this;
	return (array() || arrayify()).reduce(function (result, config) {
		var task;

		task = self.one(prefix, config, parentConfig);
		if (task) {
			result.push(task);
		}
		return result;
	}, []);

	function array() {
		if (Array.isArray(subTaskConfigs)) {
			return subTaskConfigs.map(objectify);
		}

		function objectify(value) {
			if (_.isPlainObject(value)) {
				value.name = value.name || 'anonymous';
				return value;
			}
			if (typeof value === 'string') {
				return {
					task: value
				};
			}
			if (typeof value === 'function') {
				return {
					task: value
				};
			}
			// array etc.
			return {
				task: value
			};
		}
	}

	function arrayify() {
		if (_.isPlainObject(subTaskConfigs)) {
			return _.map(subTaskConfigs, objectify)	// NOTE: becomes an array
				.sort(comparator);
		}
		return [{
			task: subTaskConfigs
		}];

		function objectify(value, name) {
			if (_.isPlainObject(value)) {
				value.name = name;
				return value;
			}
			return {
				name: name,
				task: value
			};
		}

		// tasks not defined "order" property defaults to 0
		function comparator(a, b) {
			return (a.order || 0) - (b.order || 0);
		}
	}
};

ConfigurableTaskFactory.prototype.create = function (prefix, taskInfo, taskConfig, recipe) {
	var name;

	var registry = this.registry;

	// make sure config is inherited at config time and injected, realized at runtime.
	// invoked from stream processor
	var run = function (done) {
		var context;

		context = this;
		// inject and realize runtime configuration.
		context.config = Configuration.realize(taskConfig, context.config);
		return recipe.call(context, done);
	};
	// invoked from gulp
	var configurableTask = function (done) {
		var context;

		// NOTE: gulp 4.0 task now called with gulp context.
		// see: [bind all functions on the gulp instance to allow destructuring](https://github.com/gulpjs/gulp/commit/73eced0969af4d1e87cea1d7172f90b557b8f7b5)
		// deprecated: gulp 4.0 task are called on undefined context. So we need gulp reference from registry here.
		// see: [Gulp 4: invoke task on gulp context](https://github.com/gulpjs/gulp/issues/1377)
		context = {
			gulp: registry.gulp,
			config: taskConfig,
			helper: Configuration
		};
		if (configurableTask.tasks) {
			context.tasks = configurableTask.tasks;
		}
		return run.call(context, done);
	};

	name = taskInfo.name || recipe.displayName || recipe.name || '<anonymous>';
	run.schema = configurableTask.schema;
	configurableTask.displayName = run.displayName = prefix + name;
	set(configurableTask, 'description', taskInfo.description || recipe.description);
	set(configurableTask, 'visibility', taskInfo.visibility);
	set(configurableTask, 'runtime', taskInfo.runtime);
	configurableTask.recipe = recipe;
	configurableTask.run = run;
	configurableTask.config = taskConfig;
	// TODO: also keep raw config for problem diagnosis?
	if (isVisible(configurableTask)) {
		this.registry.set(prefix + name, configurableTask);
		metadata.set(configurableTask, prefix + name);
	} else {
		metadata.set(configurableTask, name);
	}
	return configurableTask;

	function set(target, property, value) {
		if (typeof value !== 'undefined') {
			target[property] = value;
		}
	}
};

module.exports = ConfigurableTaskFactory;
<commit_msg>update information about gulp implementation<commit_after>/* eslint consistent-this: 0 */
'use strict';

var _ = require('lodash');
var log = require('gulp-util').log;

var Configuration = require('../configuration');
var metadata = require('./metadata');

function ConfigurableTaskFactory(stuff, recipeFactory, registry, settings) {
	this.stuff = stuff;
	this.recipeFactory = recipeFactory;
	this.registry = registry;
	this.settings = settings;
}

function isVisible(task) {
	return task.visibility === Configuration.CONSTANT.VISIBILITY.NORMAL || !('visibility' in task);
}

function isDisabled(task) {
	return task.visibility === Configuration.CONSTANT.VISIBILITY.DISABLED;
}

ConfigurableTaskFactory.prototype.one = function (prefix, rawConfig, parentConfig) {
	var self, configs, taskInfo, recipe, task, tasks;

	self = this;

	taskInfo = Configuration.getTaskRuntimeInfo(rawConfig);

	recipe = this.recipeFactory.create(taskInfo);
	if (!recipe) {
		log('configure', "warning: can't infer to a proper recipe task: " + taskInfo.name);
		return null;
	}

	configs = Configuration.sort(taskInfo, rawConfig, parentConfig, recipe.schema);
	if (isDisabled(configs.taskInfo)) {
		return null;
	}

	task = this.create(prefix, taskInfo, configs.taskConfig, recipe);

	if (configs.taskInfo.task || _.size(configs.subTaskConfigs)) {
		if (recipe.type === 'task') {
			log('configure', "warning: recipe don't accept sub tasks");
		} else {
			if (_.size(configs.subTaskConfigs) !== 0 && configs.taskInfo.task) {
				log('configure', 'warning: both "task" and sub tasks provided, ');
			}
			tasks = configs.taskInfo.task || configs.subTaskConfigs;
			tasks = self.multiple(_prefix(), tasks, configs.taskConfig);
			metadata.tree(task, tasks);
			task.tasks = tasks;
		}
	}
	return task;

	function _prefix() {
		return isVisible(configs.taskInfo) ? prefix + configs.taskInfo.name + ':' : prefix;
	}
};

ConfigurableTaskFactory.prototype.multiple = function (prefix, subTaskConfigs, parentConfig) {
	var self;

	self = this;
	return (array() || arrayify()).reduce(function (result, config) {
		var task;

		task = self.one(prefix, config, parentConfig);
		if (task) {
			result.push(task);
		}
		return result;
	}, []);

	function array() {
		if (Array.isArray(subTaskConfigs)) {
			return subTaskConfigs.map(objectify);
		}

		function objectify(value) {
			if (_.isPlainObject(value)) {
				value.name = value.name || 'anonymous';
				return value;
			}
			if (typeof value === 'string') {
				return {
					task: value
				};
			}
			if (typeof value === 'function') {
				return {
					task: value
				};
			}
			// array etc.
			return {
				task: value
			};
		}
	}

	function arrayify() {
		if (_.isPlainObject(subTaskConfigs)) {
			return _.map(subTaskConfigs, objectify)	// NOTE: becomes an array
				.sort(comparator);
		}
		return [{
			task: subTaskConfigs
		}];

		function objectify(value, name) {
			if (_.isPlainObject(value)) {
				value.name = name;
				return value;
			}
			return {
				name: name,
				task: value
			};
		}

		// tasks not defined "order" property defaults to 0
		function comparator(a, b) {
			return (a.order || 0) - (b.order || 0);
		}
	}
};

ConfigurableTaskFactory.prototype.create = function (prefix, taskInfo, taskConfig, recipe) {
	var name;

	var registry = this.registry;

	// make sure config is inherited at config time and injected, realized at runtime.
	// invoked from stream processor
	var run = function (done) {
		var context;

		context = this;
		// inject and realize runtime configuration.
		context.config = Configuration.realize(taskConfig, context.config);
		return recipe.call(context, done);
	};
	// invoked from gulp
	var configurableTask = function (done) {
		var context;

		// NOTE: gulp 4.0 task are called on undefined context. So we need gulp reference from registry here.
		// see: [Gulp 4: invoke task on gulp context](https://github.com/gulpjs/gulp/issues/1377)
		context = {
			gulp: registry.gulp,
			config: taskConfig,
			helper: Configuration
		};
		if (configurableTask.tasks) {
			context.tasks = configurableTask.tasks;
		}
		return run.call(context, done);
	};

	name = taskInfo.name || recipe.displayName || recipe.name || '<anonymous>';
	run.schema = configurableTask.schema;
	configurableTask.displayName = run.displayName = prefix + name;
	set(configurableTask, 'description', taskInfo.description || recipe.description);
	set(configurableTask, 'visibility', taskInfo.visibility);
	set(configurableTask, 'runtime', taskInfo.runtime);
	configurableTask.recipe = recipe;
	configurableTask.run = run;
	configurableTask.config = taskConfig;
	// TODO: also keep raw config for problem diagnosis?
	if (isVisible(configurableTask)) {
		this.registry.set(prefix + name, configurableTask);
		metadata.set(configurableTask, prefix + name);
	} else {
		metadata.set(configurableTask, name);
	}
	return configurableTask;

	function set(target, property, value) {
		if (typeof value !== 'undefined') {
			target[property] = value;
		}
	}
};

module.exports = ConfigurableTaskFactory;
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

const Chalk = require('chalk');
const inquirer = require('inquirer');
const log = console.log;
const Constants = require('../utils/constants');
const fs = require('fs-extra');

// Main code //
const self = module.exports = {
  init: (input, flags) => {
    inquirer.prompt(Constants.CREATE_QUESTIONS)
      .then(answers => {
        const result = self.getFormatedResult(answers);
        const path = `${Constants.HIVE_LIBS_DIR}/${answers.name.toLowerCase()}`

        log('Instructions:\n')
        log(Chalk.green(`mkdir -p ${path}`))
        log(`info.json`)

        const infoContent = JSON.stringify(result, null, '  ');

        self.createFileIn(`${path}`, infoContent)

        self.createPullRequest(result);
    });
  },
  createPullRequest:(result => {
    // TODO
    // log(`I have no idea how to create a PR`);
  }),
  createFileIn:((destination, content) => {
    fs.ensureDirSync(destination);
    const destinationFile = destination + `/info.json`;

    var options = { flag: 'w' };

    // Write the info.json file
    fs.writeFile(destinationFile, content, options, err => {
      if (err) {
        return log(err);
      }

      log(Chalk.green(content));

      log("The file was saved in " + destinationFile);
    });
  }),
  getFormatedResult:(answers => {
    let result = {};

    result.name = answers.name;

    if (answers.description !== '') {
      result.description = answers.description;
    }
    if (answers.homepage !== '') {
      result.homepage = answers.homepage;
    }
    result.dependency = answers.dependency;
    result.compileType = answers.compileType;

    result.repository = {};
    result.repository.server = answers.server;

    if (answers.customRepository !== '') {
      result.repository.url = answers.customRepository;
    }

    return result;
  })
};
<commit_msg>small refactor<commit_after>#!/usr/bin/env node
'use strict';

const Chalk = require('chalk');
const inquirer = require('inquirer');
const log = console.log;
const Constants = require('../utils/constants');
const fs = require('fs-extra');

// Main code //
const self = module.exports = {
  init: (input, flags) => {
    inquirer.prompt(Constants.CREATE_QUESTIONS)
      .then(answers => {
        const result = self.getFormatedResult(answers);
        const path = `${Constants.HIVE_LIBS_DIR}/${answers.name.toLowerCase()}`

        log('Instructions:\n')
        log(Chalk.green(`mkdir -p ${path}`))
        log(`info.json`)

        const infoContent = JSON.stringify(result, null, '  ');

        self.createFileIn(`${path}`, infoContent)

        self.createPullRequest(result);
    });
  },
  createPullRequest:(result => {
    // TODO
    // log(`I have no idea how to create a PR`);
  }),
  createFileIn:((destination, content) => {
    fs.ensureDirSync(destination);
    const destinationFile = destination + `/info.json`;

    var options = { flag: 'w' };

    // Write the info.json file
    fs.writeFile(destinationFile, content, options, err => {
      if (err) {
        return log(err);
      }

      log(Chalk.green(content));

      log("The file was saved in " + destinationFile);
    });
  }),
  getFormatedResult:(answers => {
    let result = {};

    result.name = answers.name;

    if (answers.description !== '') {
      result.description = answers.description;
    }
    if (answers.homepage !== '') {
      result.homepage = answers.homepage;
    }

    result.dependencies = [{
      dependency: answers.dependency,
      compileType: answers.compileType
    }];

    result.repository = {};
    result.repository.server = answers.server;

    if (answers.customRepository !== '') {
      result.repository.url = answers.customRepository;
    }

    return result;
  })
};
<|endoftext|>


<commit_before>const { v4: uuidv4 } = require('uuid');

class TestableStartSuiteTest {

  suiteUuid;
  suiteName;
  uuid;
  name;
  started;

  constructor(startSuite, name, started) {
    this.suiteUuid = startSuite.getUuid();
    this.suiteName = startSuite.getName();
    this.uuid = uuidv4();
    this.name = name;
    this.started = started;
  }

  getSuiteUuid() { return this.suiteUuid; }

  getSuiteName() { return this.suiteName; }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getStarted() { return this.started; }

}

class TestableStartSuite {

  uuid;
  name;
  started;

  constructor(name, started) {
    this.uuid = uuidv4();
    this.name = name;
    this.started = started;
  }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getStarted() { return this.started; }

}

class TestableFinishSuite {

  uuid;
  name;
  finished = new Date().getMilliseconds();
  duration;
  hasError;

  constructor(start, hasError) {
    this.uuid = start.getUuid();
    this.name = start.getName();
    this.duration = new Date().getMilliseconds() - start.getStarted();
    this.hasError = hasError;
  }

  getUuid() {
    return this.uuid;
  }

  getName() {
    return this.name;
  }

  getFinished() {
    return this.finished;
  }

  getDuration() { return this.duration; }

  isHasError() { return this.hasError; }
}

class TestableFinishSuiteTest {

  suiteUuid;
  suiteName;
  uuid;
  name;
  finished;
  duration;
  state;
  errorType;
  error;
  errorTrace;

  constructor(startSuiteTest, passed, e, captureTrace) {
    this.suiteUuid = startSuiteTest.getSuiteUuid();
    this.suiteName = startSuiteTest.getSuiteName();
    this.uuid = startSuiteTest.getUuid();
    this.name = startSuiteTest.getName();
    this.finished = startSuiteTest.getStarted() > 0 ? new Date().getMilliseconds() : 0;
    this.duration = startSuiteTest.getStarted() > 0 ? this.finished - startSuiteTest.getStarted() : 0;
    this.state = e !== null ? "failed" : "passed";
    if ( e !== null ) {
      this.errorType = e.class().simpleName();
      this.error = e.message();
      if ( captureTrace ) {
        sw = new StringWriter();
        pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        this.errorTrace = sw.toString();
      } else {
        this.errorTrace = null;
      }
    } else {
      this.errorType = null;
      this.error = null;
      this.errorTrace = null;
    }
  }

  static passed(startSuiteTest) {
    return new TestableFinishSuiteTest(startSuiteTest, true, null, false);
  }

  static failed(startSuiteTest, e) {
    return new TestableFinishSuiteTest(startSuiteTest, false, e, true);
  }

  static failedException(startSuiteTest, msg) {
    return new TestableFinishSuiteTest(startSuiteTest, false, new Exception(msg), false);
  }

  getSuiteUuid() { return this.suiteUuid; }

  getSuiteName() { return this.suiteName; }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getFinished() { return this.finished; }

  getDuration() { return this.duration; }

  getState() { return this.state; }

  getErrorType() { return this.errorType; }

  getError() { return this.error; }

  getErrorTrace() { return this.errorTrace; }
}

class TestableTest {
  startSuite;
  currentTest = null;
  hasError = false;

  constructor(name) {
    this.startSuite = new TestableStartSuite(name, new Date().getMilliseconds());
    TestableTest.write("StartSuite", this.startSuite);
  }

  runStep(name, step) {
    try {
      this.startStep(name);
      step.run();
      this.finishSuccessfulStep();
    } catch (e) {
      this.finishFailedStep(e);
    }
  }

  startStep(name) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, name, new Date().getMilliseconds());
    TestableTest.write('StartSuiteTest', this.currentTest);
  }

  finishSuccessfulStep() {
    this.finishStep(TestableFinishSuiteTest.passed(this.currentTest));
  }

  finishFailedStep(errorMsg) {
    this.finishStep(TestableFinishSuiteTest.failed(this.currentTest, errorMsg));
  }

  finishStep(finishMsg) {
    if ( this.currentTest !== null ) {
      TestableTest.write("FinishSuiteTest", finishMsg);
      if ( finishMsg.getError() !== null )
        this.hasError = true;
      this.currentTest = null;
    }
  }

  assertionPassed(assertion, duration) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, assertion, new Date().getMilliseconds() - duration);
    TestableTest.write("StartSuiteTest", this.currentTest);
    this.finishStep(TestableFinishSuiteTest.passed(this.currentTest));
  }

  assertionFailed(assertion, duration, errorMessage) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, assertion, new Date().getMilliseconds() - duration);
    TestableTest.write("StartSuiteTest", this.currentTest);
    this.finishStep(TestableFinishSuiteTest.failed(this.currentTest, errorMessage));
  }

  finish() {
    if ( this.currentTest !== null ) {
      TestableTest.write("FinishSuiteTest", TestableFinishSuiteTest.passed(this.currentTest));
    }
    TestableTest.write("FinishSuite", new TestableFinishSuite(this.startSuite, this.hasError));
  }

  static write(type, event) {
    // todo: pending writing the events
    // TestableTest.writeToStream()
  }
}

module.exports = TestableTest;

/*

const test = TestableSelenium.startTest("Google Related");
test.startStep("Open google home page");
driver.get("https://www.google.com");
test.finishSuccessfulStep(); // or test.finishFailedStep("My error message");
test.runStep("Open google news using Runnable", new Runnable() {
  run() {
    driver.get("https://news.google.com"); // any exception here gets logged as a test step failure
  }
});
test.finish();*/
<commit_msg>removed comments<commit_after>const { v4: uuidv4 } = require('uuid');

class TestableStartSuiteTest {

  suiteUuid;
  suiteName;
  uuid;
  name;
  started;

  constructor(startSuite, name, started) {
    this.suiteUuid = startSuite.getUuid();
    this.suiteName = startSuite.getName();
    this.uuid = uuidv4();
    this.name = name;
    this.started = started;
  }

  getSuiteUuid() { return this.suiteUuid; }

  getSuiteName() { return this.suiteName; }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getStarted() { return this.started; }

}

class TestableStartSuite {

  uuid;
  name;
  started;

  constructor(name, started) {
    this.uuid = uuidv4();
    this.name = name;
    this.started = started;
  }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getStarted() { return this.started; }

}

class TestableFinishSuite {

  uuid;
  name;
  finished = new Date().getMilliseconds();
  duration;
  hasError;

  constructor(start, hasError) {
    this.uuid = start.getUuid();
    this.name = start.getName();
    this.duration = new Date().getMilliseconds() - start.getStarted();
    this.hasError = hasError;
  }

  getUuid() {
    return this.uuid;
  }

  getName() {
    return this.name;
  }

  getFinished() {
    return this.finished;
  }

  getDuration() { return this.duration; }

  isHasError() { return this.hasError; }
}

class TestableFinishSuiteTest {

  suiteUuid;
  suiteName;
  uuid;
  name;
  finished;
  duration;
  state;
  errorType;
  error;
  errorTrace;

  constructor(startSuiteTest, passed, e, captureTrace) {
    this.suiteUuid = startSuiteTest.getSuiteUuid();
    this.suiteName = startSuiteTest.getSuiteName();
    this.uuid = startSuiteTest.getUuid();
    this.name = startSuiteTest.getName();
    this.finished = startSuiteTest.getStarted() > 0 ? new Date().getMilliseconds() : 0;
    this.duration = startSuiteTest.getStarted() > 0 ? this.finished - startSuiteTest.getStarted() : 0;
    this.state = e !== null ? "failed" : "passed";
    if ( e !== null ) {
      this.errorType = e.class().simpleName();
      this.error = e.message();
      if ( captureTrace ) {
        sw = new StringWriter();
        pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        this.errorTrace = sw.toString();
      } else {
        this.errorTrace = null;
      }
    } else {
      this.errorType = null;
      this.error = null;
      this.errorTrace = null;
    }
  }

  static passed(startSuiteTest) {
    return new TestableFinishSuiteTest(startSuiteTest, true, null, false);
  }

  static failed(startSuiteTest, e) {
    return new TestableFinishSuiteTest(startSuiteTest, false, e, true);
  }

  static failedException(startSuiteTest, msg) {
    return new TestableFinishSuiteTest(startSuiteTest, false, new Exception(msg), false);
  }

  getSuiteUuid() { return this.suiteUuid; }

  getSuiteName() { return this.suiteName; }

  getUuid() { return this.uuid; }

  getName() { return this.name; }

  getFinished() { return this.finished; }

  getDuration() { return this.duration; }

  getState() { return this.state; }

  getErrorType() { return this.errorType; }

  getError() { return this.error; }

  getErrorTrace() { return this.errorTrace; }
}

class TestableTest {
  startSuite;
  currentTest = null;
  hasError = false;

  constructor(name) {
    this.startSuite = new TestableStartSuite(name, new Date().getMilliseconds());
    TestableTest.write("StartSuite", this.startSuite);
  }

  runStep(name, step) {
    try {
      this.startStep(name);
      step.run();
      this.finishSuccessfulStep();
    } catch (e) {
      this.finishFailedStep(e);
    }
  }

  startStep(name) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, name, new Date().getMilliseconds());
    TestableTest.write('StartSuiteTest', this.currentTest);
  }

  finishSuccessfulStep() {
    this.finishStep(TestableFinishSuiteTest.passed(this.currentTest));
  }

  finishFailedStep(errorMsg) {
    this.finishStep(TestableFinishSuiteTest.failed(this.currentTest, errorMsg));
  }

  finishStep(finishMsg) {
    if ( this.currentTest !== null ) {
      TestableTest.write("FinishSuiteTest", finishMsg);
      if ( finishMsg.getError() !== null )
        this.hasError = true;
      this.currentTest = null;
    }
  }

  assertionPassed(assertion, duration) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, assertion, new Date().getMilliseconds() - duration);
    TestableTest.write("StartSuiteTest", this.currentTest);
    this.finishStep(TestableFinishSuiteTest.passed(this.currentTest));
  }

  assertionFailed(assertion, duration, errorMessage) {
    this.currentTest = new TestableStartSuiteTest(this.startSuite, assertion, new Date().getMilliseconds() - duration);
    TestableTest.write("StartSuiteTest", this.currentTest);
    this.finishStep(TestableFinishSuiteTest.failed(this.currentTest, errorMessage));
  }

  finish() {
    if ( this.currentTest !== null ) {
      TestableTest.write("FinishSuiteTest", TestableFinishSuiteTest.passed(this.currentTest));
    }
    TestableTest.write("FinishSuite", new TestableFinishSuite(this.startSuite, this.hasError));
  }

  static write(type, event) {
    // todo: pending writing the events
    // TestableTest.writeToStream()
  }
}

module.exports = TestableTest;
<|endoftext|>