<commit_before>require('../collections/model_array');
require('../collections/model_set');
require('./collection_manager');
require('./belongs_to_manager');
require('../model');
require('./merge_strategies');

var get = Ember.get, set = Ember.set;

Ep.Session = Ember.Object.extend({

  mergeStrategy: Ep.PerField,

  _dirtyCheckingSuspended: false,

  init: function() {
    this._super.apply(this, arguments);
    this.models = Ep.ModelSet.create();
    this.collectionManager = Ep.CollectionManager.create();
    this.belongsToManager = Ep.BelongsToManager.create();
    this.shadows = Ep.ModelSet.create();
    this.originals = Ep.ModelSet.create();
    this.newModels = Ep.ModelSet.create();
  },

  create: function(type, hash) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    var model = type.create(hash);
    model = this.add(model);
    // relationships set during create and relationships
    // from detached instances must be registered
    // (normally done through observers)
    model._registerRelationships();
    return model;
  },

  adopt: function(model) {
    Ember.assert("Models instances cannot be moved between sessions. Use `add` or `update` instead.", !get(model, 'session') || get(model, 'session') === this);
    set(model, 'session', this);
    if(get(model, 'isNew')) {
      this.newModels.add(model);
    }
    // This will overwrite an existing model with the same
    // clientId. This is useful to evict proxies
    this.models.add(model);

    // relationships set during create and relationships
    // from detached instances must be registered
    // (normally done through observers)
    model._registerRelationships();
    return model;
  },

  /**
    Adds a model to this session. Some cases below:

    If the model is detached (meaning not current associated with a session),
    then the model will be re-used in this session. The entire graph of detached
    objects will be traversed and added to the session.

    If the model is already associated with this session in *loaded form* (not necessarily
    the same instance that is passed in), this method is a NO-OP.

    If the model is already associated with a *different* session then the model
    will be copied to this session. In order to prevent large graphs from being
    copied, all relations will be copied in lazily.

    @method add
    @param {Ep.Model} model The model to add to the session
    @param {Number} [depth=2] The depth of relationships to eagerly traverse for before adding lazily
  */
  add: function(model, depth) {
    this.reifyClientId(model);
    var dest = this.getModel(model);
    if(dest && get(dest, 'isLoaded')) return dest;

    if(typeof depth === 'undefined') {
      depth = 2;
    }

    depth--;

    if(get(model, 'isProxy')) {
      var content = get(model, 'content');
      if(content) {
        return this.add(content);
      }
      dest = model.lazyCopy();
      return this.adopt(dest);
    }

    // re-use if detached
    if(get(model, 'isDetached')) {
      dest = model;
    } else {
      dest = model.constructor.create();
      model.copyAttributes(dest);
    }

    this.adopt(dest);

    model.eachRelationship(function(name, relationship) {
      if(relationship.kind === 'belongsTo') {
        var child = get(model, name);
        if(child) {
          // It is possible we are adding a detached model
          // that has a child set to a model inside this session.
          // In this case we simply trigger observers to updated inverses
          if(get(child, 'session') === this) {
            model.belongsToDidChange(model, name);
          } else {
            if(depth >= 0 || get(child, 'isDetached') || get(child, 'isNew')) {
              child = this.add(child, depth);
            } else {
              child = child.lazyCopy();
            }

            // TODO: think through suspending during add more in depth
            dest.suspendRelationshipObservers(function() {
              set(dest, name, child);
            });
          }
        }
      } else if(relationship.kind === 'hasMany') {
        var children = get(model, name);
        var copied = children.map(function(child) {
          if(depth >= 0 || get(child, 'isDetached') || get(child, 'isNew')) {
            child = this.add(child, depth);
          } else {
            child = child.lazyCopy();
          }
          return child;
        }, this);
        set(dest, name, copied);
      }
    }, this);

    return dest;
  },

  /**
    Removes the model from the session.

    This does not mean that the model has been necessarily deleted,
    just that the session should no longer keep track of it.

    @method remove
    @param {Ep.Model} model The model to remove from the session
  */
  remove: function(model) {
    // TODO: think through relationships that still reference the model
    get(this, 'models').remove(model);
    get(this, 'shadows').remove(model);
    get(this, 'originals').remove(model);
  },

  /**
    Updates a model in this session using the passed in model as a reference.

    If the passed in model is not already associated with this session, this
    is equivalent to adding the model to the session.

    If the model already is associated with this session, then the existing
    model will be updated.

    @method update
    @param {Ep.Model} model A model containing updated properties
  */
  update: function(model) {
    this.reifyClientId(model);
    if(get(model, 'isProxy')) {
      var content = get(model, 'content');
      if(content) {
        return this.update(content);
      }
      throw new Ember.Error("Cannot update with an unloaded model: " + model.toString());
    }
    var dest = this.getModel(model);

    // if the model is detached or does not exist
    // in the target session, updating is semantically
    // equivalent to adding
    if(get(model, 'isDetached') || !dest || !get(dest, 'isLoaded')) {
      return this.add(model);
    }

    // handle deletion
    if(get(model, 'isDeleted')) {
      // no-op if already deleted
      if(!get(dest, 'isDeleted')) {
        this.deleteModel(dest);
      }
      return dest;
    }

    model.copyAttributes(dest);

    model.eachRelationship(function(name, relationship) {
      if(relationship.kind === 'belongsTo') {
        var child = get(model, name);
        if(child) {
          set(dest, name, child);
        }
      } else if(relationship.kind === 'hasMany') {
        var children = get(model, name);
        var destChildren = get(dest, name);
        children.copyTo(destChildren);
      }
    }, this);

    return dest;
  },

  deleteModel: function(model) {
    // if the model is new, deleting should essentially just
    // remove the object from the session
    if(get(model, 'isNew')) {
      var newModels = get(this, 'newModels');
      newModels.remove(model);
    } else {
      this.modelWillBecomeDirty(model);
    }
    set(model, 'isDeleted', true);
    this.collectionManager.modelWasDeleted(model);
    this.belongsToManager.modelWasDeleted(model);
  },

  load: function(type, id) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    // always coerce to string
    id = id.toString();

    var cached = this.getForId(type, id);
    if(cached && get(cached, 'isLoaded')) {
      return Ep.resolveModel(cached);
    }

    var session = this;
    return Ep.resolveModel(this.adapter.load(type, id).then(function(model) {
      return session.merge(model);
    }, function(model) {
      throw session.merge(model);
    }), type, id, session);
  },

  find: function(type, query) {
    if (Ember.typeOf(query) === 'object') {
      return this.query(type, query);
    }
    return this.load(type, query);
  },

  query: function(type, query) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    var session = this;
    // TODO: return a model array immediately here
    // and also take into account errors
    return this.adapter.query(type, query).then(function(models) {
      // TODO: model array could automatically add to session?
      var merged = Ep.ModelArray.create({session: session, content: []});
      set(merged, 'meta', get(models, 'meta'));
      models.forEach(function(model) {
        merged.addObject(session.merge(model));
      });
      return merged;
    });
  },

  refresh: function(model) {
    var session = this;
    return this.adapter.refresh(model).then(function(refreshedModel) {
      return session.merge(refreshedModel);
    }, function(refreshedModel) {
      throw session.merge(refreshedModel);
    });
  },

  flush: function() {
    var session = this,
        dirtyModels = get(this, 'dirtyModels'),
        newModels = get(this, 'newModels'),
        shadows = get(this, 'shadows');

    // increment client revisions for all models
    // that could potentially be flushed
    dirtyModels.forEach(function(model) {
      model.incrementProperty('clientRev');
    }, this);

    // the adapter will return a list of models regardless
    // of whether the flush succeeded. it is in the merge
    // logic that the errors property of the model is consumed
    var promise = this.adapter.flush(this).then(function(models) {
      var res = models.map(function(model) {
        return session.merge(model);
      });
      return res;
    }, function(models) {
      var res = models.map(function(model) {
        return session.merge(model);
      });
      throw res;
    });

    // Optimistically assume updates will be
    // successful. Update shadows with current
    // models and add shadow to originals.
    dirtyModels.forEach(function(model) {
      // track original to merge against new data that
      // hasn't seen client updates
      var original = this.originals.getModel(model);
      var shadow = this.shadows.getModel(model);
      if(shadow && (!original || original.get('rev') < shadow.get('rev'))) {
        this.originals.add(shadow);
      }
      // Logically, we should be adding to the shadow here,
      // but instead we remove. This is an optimization.
      // When the model becomes dirty it will be re-added
      this.shadows.remove(model);
    }, this);
    newModels.clear();

    return promise;
  },

  getModel: function(model) {
    return this.models.getModel(model);
  },

  getForId: function(type, id) {
    var clientId = this.adapter.getClientId(type, id);
    return this.models.getForClientId(clientId);
  },

  reifyClientId: function(model) {
    this.adapter.reifyClientId(model);
  },

  remoteCall: function(context, name) {
    var session = this;
    return this.adapter.remoteCall.apply(this.adapter, arguments).then(function(model) {
      return session.merge(model);
    }, function(model) {
      throw session.merge(model);
    });
  },

  modelWillBecomeDirty: function(model) {
    if(this._dirtyCheckingSuspended || get(model, 'isNew')) {
      return;
    }
    var shadow = this.shadows.getModel(model);
    if(!shadow) {
      shadow = model.copy();
      this.shadows.addObject(shadow);
    }
  },

  destroy: function() {
    this._super();
    this.models.forEach(function(model) {
      model.destroy();
    });
    this.models.destroy();
    this.collectionManager.destroy();
    this.belongsToManager.destroy();
    this.shadows.destroy();
    this.originals.destroy();
    this.newModels.destroy();
  },

  dirtyModels: Ember.computed(function() {
    var models = Ep.ModelSet.fromArray(this.shadows.map(function(model) {
      return this.models.getModel(model);
    }, this));

    get(this, 'newModels').forEach(function(model) {
      models.add(model);
    });

    return models;
  }).volatile(),

  suspendDirtyChecking: function(callback, binding) {
    var self = this;

    // could be nested
    if(this._dirtyCheckingSuspended) {
      return callback.call(binding || self);
    }

    try {
      this._dirtyCheckingSuspended = true;
      return callback.call(binding || self);
    } finally {
      this._dirtyCheckingSuspended = false;
    }
  },

  newSession: function() {
    var child = this.container.lookup('session:child');
    set(child, 'parent', this);
    return child;
  },

  lookupType: function(type) {
    return this.container.lookup('model:' + type);
  },

  getShadow: function(model) {
    var shadows = get(this, 'shadows');
    var models = get(this, 'models');
    // shadows are only created when the model is dirtied,
    // if no model exists in the `shadows` property then
    // it is safe to assume the model has not been modified
    return shadows.getModel(model) || models.getModel(model);
  }

});<commit_msg>Update session.js<commit_after>require('../collections/model_array');
require('../collections/model_set');
require('./collection_manager');
require('./belongs_to_manager');
require('../model');
require('./merge_strategies');

var get = Ember.get, set = Ember.set;

Ep.Session = Ember.Object.extend({

  mergeStrategy: Ep.PerField,

  _dirtyCheckingSuspended: false,

  init: function() {
    this._super.apply(this, arguments);
    this.models = Ep.ModelSet.create();
    this.collectionManager = Ep.CollectionManager.create();
    this.belongsToManager = Ep.BelongsToManager.create();
    this.shadows = Ep.ModelSet.create();
    this.originals = Ep.ModelSet.create();
    this.newModels = Ep.ModelSet.create();
  },

  create: function(type, hash) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    var model = type.create(hash);
    model = this.add(model);
    // relationships set during create and relationships
    // from detached instances must be registered
    // (normally done through observers)
    model._registerRelationships();
    return model;
  },

  adopt: function(model) {
    Ember.assert("Models instances cannot be moved between sessions. Use `add` or `update` instead.", !get(model, 'session') || get(model, 'session') === this);
    set(model, 'session', this);
    if(get(model, 'isNew')) {
      this.newModels.add(model);
    }
    // This will overwrite an existing model with the same
    // clientId. This is useful to evict proxies
    this.models.add(model);

    // relationships set during create and relationships
    // from detached instances must be registered
    // (normally done through observers)
    model._registerRelationships();
    return model;
  },

  /**
    Adds a model to this session. Some cases below:

    If the model is detached (meaning not current associated with a session),
    then the model will be re-used in this session. The entire graph of detached
    objects will be traversed and added to the session.

    If the model is already associated with this session in *loaded form* (not necessarily
    the same instance that is passed in), this method is a NO-OP.

    If the model is already associated with a *different* session then the model
    will be copied to this session. In order to prevent large graphs from being
    copied, all relations will be copied in lazily.

    @method add
    @param {Ep.Model} model The model to add to the session
    @param {Number} [depth=2] The depth of relationships to eagerly traverse for before adding lazily
  */
  add: function(model, depth) {
    this.reifyClientId(model);
    var dest = this.getModel(model);
    if(dest && get(dest, 'isLoaded')) return dest;

    if(typeof depth === 'undefined') {
      depth = 2;
    }

    depth--;

    if(get(model, 'isProxy')) {
      var content = get(model, 'content');
      if(content) {
        return this.add(content);
      }
      dest = model.lazyCopy();
      return this.adopt(dest);
    }

    // re-use if detached
    if(get(model, 'isDetached')) {
      dest = model;
    } else {
      dest = model.constructor.create();
      model.copyAttributes(dest);
    }

    this.adopt(dest);

    model.eachRelationship(function(name, relationship) {
      if(relationship.kind === 'belongsTo') {
        var child = get(model, name);
        if(child) {
          // It is possible we are adding a detached model
          // that has a child set to a model inside this session.
          // In this case we simply trigger observers to updated inverses
          if(get(child, 'session') === this) {
            model.belongsToDidChange(model, name);
          } else {
            if(depth >= 0 || get(child, 'isDetached') || get(child, 'isNew')) {
              child = this.add(child, depth);
            } else {
              child = child.lazyCopy();
            }

            // TODO: think through suspending during add more in depth
            dest.suspendRelationshipObservers(function() {
              set(dest, name, child);
            });
          }
        }
      } else if(relationship.kind === 'hasMany') {
        var children = get(model, name);
        var copied = children.map(function(child) {
          if(depth >= 0 || get(child, 'isDetached') || get(child, 'isNew')) {
            child = this.add(child, depth);
          } else {
            child = child.lazyCopy();
          }
          return child;
        }, this);
        set(dest, name, copied);
      }
    }, this);

    return dest;
  },

  /**
    Removes the model from the session.

    This does not mean that the model has been necessarily deleted,
    just that the session should no longer keep track of it.

    @method remove
    @param {Ep.Model} model The model to remove from the session
  */
  remove: function(model) {
    // TODO: think through relationships that still reference the model
    get(this, 'models').remove(model);
    get(this, 'shadows').remove(model);
    get(this, 'originals').remove(model);
  },

  /**
    Updates a model in this session using the passed in model as a reference.

    If the passed in model is not already associated with this session, this
    is equivalent to adding the model to the session.

    If the model already is associated with this session, then the existing
    model will be updated.

    @method update
    @param {Ep.Model} model A model containing updated properties
  */
  update: function(model) {
    this.reifyClientId(model);
    if(get(model, 'isProxy')) {
      var content = get(model, 'content');
      if(content) {
        return this.update(content);
      }
      throw new Ember.Error("Cannot update with an unloaded model: " + model.toString());
    }
    var dest = this.getModel(model);

    // if the model is detached or does not exist
    // in the target session, updating is semantically
    // equivalent to adding
    if(get(model, 'isDetached') || !dest || !get(dest, 'isLoaded')) {
      return this.add(model);
    }

    // handle deletion
    if(get(model, 'isDeleted')) {
      // no-op if already deleted
      if(!get(dest, 'isDeleted')) {
        this.deleteModel(dest);
      }
      return dest;
    }

    model.copyAttributes(dest);

    model.eachRelationship(function(name, relationship) {
      if(relationship.kind === 'belongsTo') {
        var child = get(model, name);
        if(child) {
          set(dest, name, child);
        }
      } else if(relationship.kind === 'hasMany') {
        var children = get(model, name);
        var destChildren = get(dest, name);
        children.copyTo(destChildren);
      }
    }, this);

    return dest;
  },

  deleteModel: function(model) {
    // if the model is new, deleting should essentially just
    // remove the object from the session
    if(get(model, 'isNew')) {
      var newModels = get(this, 'newModels');
      newModels.remove(model);
    } else {
      this.modelWillBecomeDirty(model);
    }
    set(model, 'isDeleted', true);
    this.collectionManager.modelWasDeleted(model);
    this.belongsToManager.modelWasDeleted(model);
  },

  load: function(type, id) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    // always coerce to string
    id = id.toString();

    var cached = this.getForId(type, id);
    if(cached && get(cached, 'isLoaded')) {
      return Ep.resolveModel(cached);
    }

    var session = this;
    return Ep.resolveModel(this.adapter.load(type, id).then(function(model) {
      return session.merge(model);
    }, function(model) {
      throw session.merge(model);
    }), type, id, session);
  },

  find: function(type, query) {
    if (Ember.typeOf(query) === 'object') {
      return this.query(type, query);
    }
    return this.load(type, query);
  },

  query: function(type, query) {
    if(typeof type === "string") {
      type = this.lookupType(type);
    }
    var session = this;
    // TODO: return a model array immediately here
    // and also take into account errors
    return this.adapter.query(type, query).then(function(models) {
      // TODO: model array could automatically add to session?
      var merged = Ep.ModelArray.create({session: session, content: []});
      set(merged, 'meta', get(models, 'meta'));
      models.forEach(function(model) {
        merged.addObject(session.merge(model));
      });
      return merged;
    });
  },

  refresh: function(model) {
    var session = this;
    return this.adapter.refresh(model).then(function(refreshedModel) {
      return session.merge(refreshedModel);
    }, function(refreshedModel) {
      throw session.merge(refreshedModel);
    });
  },

  flush: function() {
    var session = this,
        dirtyModels = get(this, 'dirtyModels'),
        newModels = get(this, 'newModels'),
        shadows = get(this, 'shadows');

    // increment client revisions for all models
    // that could potentially be flushed
    dirtyModels.forEach(function(model) {
      model.incrementProperty('clientRev');
    }, this);

    // the adapter will return a list of models regardless
    // of whether the flush succeeded. it is in the merge
    // logic that the errors property of the model is consumed
    var promise = this.adapter.flush(this).then(function(models) {
      var res = models.map(function(model) {
        return session.merge(model);
      });
      return res;
    }, function(models) {
      var res = models.map(function(model) {
        return session.merge(model);
      });
      throw res;
    });

    // Optimistically assume updates will be
    // successful. Copy shadow models into
    // originals and remove the shadow.
    dirtyModels.forEach(function(model) {
      // track original to merge against new data that
      // hasn't seen client updates
      var original = this.originals.getModel(model);
      var shadow = this.shadows.getModel(model);
      if(shadow && (!original || original.get('rev') < shadow.get('rev'))) {
        this.originals.add(shadow);
      }
      // Logically, we should be adding to the shadow here,
      // but instead we remove. This is an optimization.
      // When the model becomes dirty it will be re-added
      this.shadows.remove(model);
    }, this);
    newModels.clear();

    return promise;
  },

  getModel: function(model) {
    return this.models.getModel(model);
  },

  getForId: function(type, id) {
    var clientId = this.adapter.getClientId(type, id);
    return this.models.getForClientId(clientId);
  },

  reifyClientId: function(model) {
    this.adapter.reifyClientId(model);
  },

  remoteCall: function(context, name) {
    var session = this;
    return this.adapter.remoteCall.apply(this.adapter, arguments).then(function(model) {
      return session.merge(model);
    }, function(model) {
      throw session.merge(model);
    });
  },

  modelWillBecomeDirty: function(model) {
    if(this._dirtyCheckingSuspended || get(model, 'isNew')) {
      return;
    }
    var shadow = this.shadows.getModel(model);
    if(!shadow) {
      shadow = model.copy();
      this.shadows.addObject(shadow);
    }
  },

  destroy: function() {
    this._super();
    this.models.forEach(function(model) {
      model.destroy();
    });
    this.models.destroy();
    this.collectionManager.destroy();
    this.belongsToManager.destroy();
    this.shadows.destroy();
    this.originals.destroy();
    this.newModels.destroy();
  },

  dirtyModels: Ember.computed(function() {
    var models = Ep.ModelSet.fromArray(this.shadows.map(function(model) {
      return this.models.getModel(model);
    }, this));

    get(this, 'newModels').forEach(function(model) {
      models.add(model);
    });

    return models;
  }).volatile(),

  suspendDirtyChecking: function(callback, binding) {
    var self = this;

    // could be nested
    if(this._dirtyCheckingSuspended) {
      return callback.call(binding || self);
    }

    try {
      this._dirtyCheckingSuspended = true;
      return callback.call(binding || self);
    } finally {
      this._dirtyCheckingSuspended = false;
    }
  },

  newSession: function() {
    var child = this.container.lookup('session:child');
    set(child, 'parent', this);
    return child;
  },

  lookupType: function(type) {
    return this.container.lookup('model:' + type);
  },

  getShadow: function(model) {
    var shadows = get(this, 'shadows');
    var models = get(this, 'models');
    // shadows are only created when the model is dirtied,
    // if no model exists in the `shadows` property then
    // it is safe to assume the model has not been modified
    return shadows.getModel(model) || models.getModel(model);
  }

});
<|endoftext|>


<commit_before>// Internal methods to run migrations used by migrate CLI script and migration
// validation filter

'use strict';

/*global N*/

// stdlib
var path = require('path');

// 3rd-party
var fstools = require('fs-tools');

////////////////////////////////////////////////////////////////////////////////

var MIGRATIONS_DIR = 'db/migrate';

////////////////////////////////////////////////////////////////////////////////

// returns list of migration files for an application
function findMigrations(app) {
  var dirname = path.join(app.root, MIGRATIONS_DIR);
  return fstools.findSorted(dirname, /\d{14}_\w*\.js$/).map(path.basename);
}

// Single migration representation
function Migration(app, step) {
  this.app  = app;
  this.step = step;
}

// internal: appName getter
Object.defineProperty(Migration.prototype, 'appName', {
  get: function () { return this.app.name; }
});

// internal: return full filename of migration script
Object.defineProperty(Migration.prototype, 'filename', {
  get: function () {
    return path.join(this.app.root, MIGRATIONS_DIR, this.step);
  }
});

// public: runs migartion and fires `callback(err)` at the end
Migration.prototype.up = function (callback) {
  require(this.filename).up(callback);
};

////////////////////////////////////////////////////////////////////////////////

//  checkMigrations(currentMigrations) -> Array
//  - currentMigrations (Array): array of already used migrations
//    `{ <appName> : [ <step>, ... ], ... }`
//
//  Compare `currentMigrations` with available and returns an array of
//  outstanding migrations as an array of _migrations_.
//
//  ##### Migration
//
//  - **appName**:      module name
//  - **step**:         migration step name
//  - **up(callback)**: runs migration up
//
exports.checkMigrations = function checkMigrations(currentMigrations) {
  var delta = [];

  currentMigrations = currentMigrations || {};

  N.runtime.apps.forEach(function (app) {
    var appMigrations = currentMigrations[app.name] || [];

    findMigrations(app).forEach(function (step) {
      if (-1 === appMigrations.indexOf(step)) {
        delta.push(new Migration(app, step));
      }
    });
  });

  return delta;
};
<commit_msg>Fix findMigrations<commit_after>// Internal methods to run migrations used by migrate CLI script and migration
// validation filter

'use strict';

/*global N*/

// stdlib
var path = require('path');

// 3rd-party
var fstools = require('fs-tools');

////////////////////////////////////////////////////////////////////////////////

var MIGRATIONS_DIR = 'db/migrate';

////////////////////////////////////////////////////////////////////////////////

// returns list of migration files for an application
function findMigrations(app) {
  var
  dirname = path.join(app.root, MIGRATIONS_DIR),
  results = [];

  fstools.walkSync(dirname, /\d{14}_\w*\.js$/, function (file) {
    results.push(path.basename(file));
  });

  return results;
}

// Single migration representation
function Migration(app, step) {
  this.app  = app;
  this.step = step;
}

// internal: appName getter
Object.defineProperty(Migration.prototype, 'appName', {
  get: function () { return this.app.name; }
});

// internal: return full filename of migration script
Object.defineProperty(Migration.prototype, 'filename', {
  get: function () {
    return path.join(this.app.root, MIGRATIONS_DIR, this.step);
  }
});

// public: runs migartion and fires `callback(err)` at the end
Migration.prototype.up = function (callback) {
  require(this.filename).up(callback);
};

////////////////////////////////////////////////////////////////////////////////

//  checkMigrations(currentMigrations) -> Array
//  - currentMigrations (Array): array of already used migrations
//    `{ <appName> : [ <step>, ... ], ... }`
//
//  Compare `currentMigrations` with available and returns an array of
//  outstanding migrations as an array of _migrations_.
//
//  ##### Migration
//
//  - **appName**:      module name
//  - **step**:         migration step name
//  - **up(callback)**: runs migration up
//
exports.checkMigrations = function checkMigrations(currentMigrations) {
  var delta = [];

  currentMigrations = currentMigrations || {};

  N.runtime.apps.forEach(function (app) {
    var appMigrations = currentMigrations[app.name] || [];

    findMigrations(app).forEach(function (step) {
      if (-1 === appMigrations.indexOf(step)) {
        delta.push(new Migration(app, step));
      }
    });
  });

  return delta;
};
<|endoftext|>


<commit_before>"use strict";

var changing = require('best-globals').changing;
var coalesce = require('best-globals').coalesce;
var typeStore = require('type-store');

const escapeStringRegexp = require('escape-string-regexp');

function completeEditablesProperties(def,defaultValue,other){
    var other = other || {};
    ['insert','delete','update','select'].forEach(function(actionName){
        if(!(actionName in def.allow)){
            def.allow[actionName] = (
                actionName!=='select' && 'editable' in def ?
                !!def.editable :
                (actionName in other?other[actionName]:defaultValue)
            );
        }
    });
    def.allow.filter = coalesce(def.allow.filter,true);
    def.allow.import = coalesce(def.allow.import,def.allow.export && def.allow.update,false);
}

function tableDefAdapt(tableDef){
    var be = this;
    var resultTableDef = changing({
        title: tableDef.name,
        field: {},
        allow: {select: true, orientation: false, "vertical-edit": true, export:true, import:true},
        actionNamesList: [],
        foreignKeys: [],
        filterColumns: [],
        detailTables: [],
        sql:{
            fields:{}
        },
        layout:{
            vertical:false,
        }
    },tableDef);
    completeEditablesProperties(resultTableDef, false);
    var prefixRegExp;
    if(resultTableDef.prefix){
        prefixRegExp=new RegExp('^'+escapeStringRegexp(resultTableDef.prefix)+'_');
    }else{
        prefixRegExp=/^/;
    }
    resultTableDef.fields = resultTableDef.fields.map(function(fieldDef){
        var resultFieldDef = changing({
            allow:{},
            title: fieldDef.name.replace(prefixRegExp,'').replace(/_/g, ' '),
        }, fieldDef);
        typeStore.completeTypeInfo(resultFieldDef);
        resultFieldDef.label = resultFieldDef.label||resultFieldDef.title;
        completeEditablesProperties(resultFieldDef, !!resultTableDef.editableFieldDef, resultTableDef.allow);
        if(resultTableDef.allow.insert){
            resultFieldDef.allow.insert=true;
        }
        if(fieldDef.typeName==='enum'){
            resultFieldDef.options = resultFieldDef.options.map(function(option){
                if(typeof option !== 'object'){
                    return {option:option, label:option};
                }else{
                    return option;
                }
            });
        }
        return resultFieldDef;
    }).filter(function(fieldDef){
        return fieldDef.allow.select;
    }).map(function(fieldDef){
        resultTableDef.field[fieldDef.name]=fieldDef;
        return fieldDef;
    });
    (resultTableDef.primaryKey||[]).forEach(function(fieldName, i){
        resultTableDef.field[fieldName].isPk=i+1;
    });
    resultTableDef.foreignKeys.map(function(fk){
        fk.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.detailTables.map(function(detail){
        detail.label=detail.label||detail.table;
        detail.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.sql.select = resultTableDef.sql.select || resultTableDef.fields.map(function(fieldDef){
        var resultName = be.db.quoteObject(fieldDef.name)
        var exprAs='';
        if(fieldDef.clientSide && !fieldDef.inTable){
            exprAs="null::text as ";
        }else if((resultTableDef.sql.fields[fieldDef.name]||{}).expr){
            exprAs=resultTableDef.sql.fields[fieldDef.name].expr+' as ';
        }
        return exprAs+resultName;
    });
    resultTableDef.sql.isTable = coalesce(resultTableDef.sql.isTable, !resultTableDef.sql.from);
    resultTableDef.sql.from = resultTableDef.sql.from || be.db.quoteObject(resultTableDef.name);
    resultTableDef.sql.postCreateSqls = resultTableDef.sql.postCreateSqls || '';
    return resultTableDef;
}

module.exports = tableDefAdapt;<commit_msg>left join para fk<commit_after>"use strict";

var changing = require('best-globals').changing;
var coalesce = require('best-globals').coalesce;
var typeStore = require('type-store');

const escapeStringRegexp = require('escape-string-regexp');

function completeEditablesProperties(def,defaultValue,other){
    var other = other || {};
    ['insert','delete','update','select'].forEach(function(actionName){
        if(!(actionName in def.allow)){
            def.allow[actionName] = (
                actionName!=='select' && 'editable' in def ?
                !!def.editable :
                (actionName in other?other[actionName]:defaultValue)
            );
        }
    });
    def.allow.filter = coalesce(def.allow.filter,true);
    def.allow.import = coalesce(def.allow.import,def.allow.export && def.allow.update,false);
}

function tableDefAdapt(tableDef, context){
    var be = this;
    var resultTableDef = changing({
        title: tableDef.name,
        field: {},
        allow: {select: true, orientation: false, "vertical-edit": true, export:true, import:true},
        actionNamesList: [],
        foreignKeys: [],
        filterColumns: [],
        detailTables: [],
        sql:{
            fields:{}
        },
        layout:{
            vertical:false,
        }
    },tableDef);
    completeEditablesProperties(resultTableDef, false);
    var prefixRegExp;
    if(resultTableDef.prefix){
        prefixRegExp=new RegExp('^'+escapeStringRegexp(resultTableDef.prefix)+'_');
    }else{
        prefixRegExp=/^/;
    }
    resultTableDef.fields = resultTableDef.fields.map(function(fieldDef){
        var resultFieldDef = changing({
            allow:{},
            title: fieldDef.name.replace(prefixRegExp,'').replace(/_/g, ' '),
        }, fieldDef);
        typeStore.completeTypeInfo(resultFieldDef);
        resultFieldDef.label = resultFieldDef.label||resultFieldDef.title;
        completeEditablesProperties(resultFieldDef, !!resultTableDef.editableFieldDef, resultTableDef.allow);
        if(resultTableDef.allow.insert){
            resultFieldDef.allow.insert=true;
        }
        if(fieldDef.typeName==='enum'){
            resultFieldDef.options = resultFieldDef.options.map(function(option){
                if(typeof option !== 'object'){
                    return {option:option, label:option};
                }else{
                    return option;
                }
            });
        }
        return resultFieldDef;
    }).filter(function(fieldDef){
        return fieldDef.allow.select;
    }).map(function(fieldDef){
        resultTableDef.field[fieldDef.name]=fieldDef;
        return fieldDef;
    });
    (resultTableDef.primaryKey||[]).forEach(function(fieldName, i){
        resultTableDef.field[fieldName].isPk=i+1;
    });
    resultTableDef.foreignKeys.map(function(fk){
        fk.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.detailTables.map(function(detail){
        detail.label=detail.label||detail.table;
        detail.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.sql.select = resultTableDef.sql.select || resultTableDef.fields.map(function(fieldDef){
        var resultName = be.db.quoteObject(fieldDef.name)
        var exprAs='';
        if(fieldDef.clientSide && !fieldDef.inTable){
            exprAs="null::text as ";
        }else if((resultTableDef.sql.fields[fieldDef.name]||{}).expr){
            exprAs=resultTableDef.sql.fields[fieldDef.name].expr+' as ';
        }
        return exprAs+resultName;
    });
    resultTableDef.sql.isTable = coalesce(resultTableDef.sql.isTable, !resultTableDef.sql.from);
    resultTableDef.alias = resultTableDef.alias || resultTableDef.name;
    resultTableDef.sql.from = (resultTableDef.sql.from || be.db.quoteObject(resultTableDef.name)) + ' as ' + be.db.quoteObject(resultTableDef.alias);
    if(resultTableDef.sql.from.endsWith(' x')){
        console.log('hay que quitar el " as x"')
        throw Error('tableDefAdapt from ends with x "'+resultTableDef.name+'"');
    }
    if(context){
        resultTableDef.foreignKeys.forEach(function(fkDef){
            var fkTableDef = be.tableStructures[fkDef.references];
            if(!fkTableDef){
                throw new Error('tableDefAdapt: table "'+fkDef.references+'" must be declared before "'+resultTableDef.name+'"');
            }
            fkTableDef=fkTableDef(context);
            resultTableDef.sql.from += '\n    left join ' + fkTableDef.sql.from + ' as ' + be.db.quoteObject(fkTableDef.alias) + ' on ' +
                fkDef.fields.map(function(pair){
                    return be.db.quoteObject(resultTableDef.alias)+'.'+be.db.quoteObject(pair.source)
                        + ' = ' + be.db.quoteObject(fkTableDef.alias)+'.'+be.db.quoteObject(pair.target);
                }).join(' AND ');

        });
    }else{
        if(resultTableDef.foreignKeys.length){
            console.log("ATENCIÓN. Conviene pasar context como segundo parámetro a tableDefAdapt para que tome las FK en tabla "+resultTableDef.name)
        }
    }
    resultTableDef.sql.postCreateSqls = resultTableDef.sql.postCreateSqls || '';
    return resultTableDef;
}

module.exports = tableDefAdapt;<|endoftext|>


<commit_before>/* eslint-disable @typescript-eslint/no-var-requires */
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const dotenv = require ('dotenv');
const fs = require('fs');

const BUILD_DIR = path.resolve(__dirname, '../../dist');
const APP_DIR = path.resolve(__dirname, 'app');
const RESOURCES_DIR = path.resolve(__dirname, 'resources');

const UI_DIR = path.resolve(__dirname, '..', 'ui');
const VENDOR_DIR = path.resolve(__dirname, 'node_modules');

module.exports = (env) => {
  const IS_PROD = env.NODE_ENV === 'production';
  const IS_DEV = env.NODE_ENV === 'development';

  const entry = IS_PROD
    ? path.resolve(APP_DIR, 'index.js')
    : [
      'react-hot-loader/patch',
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      path.resolve(APP_DIR, 'index.js')
    ];
  const output = {
    path: BUILD_DIR,
    filename: 'renderer.js'
  };
  const optimization = {
    namedModules: true
  };
  const jsxRule = {
    test: /\.(js|jsx|tsx|ts)$/,
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      presets: [
        ['@babel/preset-env', {
          targets: {
            electron: '4.2'
          }
        }],
        '@babel/preset-react',
        '@babel/preset-typescript'
      ],
      plugins: [
        ['@babel/plugin-proposal-decorators', { 'legacy': true }],
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-proposal-object-rest-spread'
      ],
      ignore: [/node_modules\/(?!@nuclear).*/]
    }
  };
  const contentSecurity = 'connect-src *; style-src \'unsafe-inline\' https:; font-src https: data:; img-src https: data: file:;';
  const plugins = [
    new HtmlWebpackPlugin({
      meta: {
        charset: {
          charset: 'UTF-8'
        },
        'Content-Security-Policy': {
          'http-equiv': 'Content-Security-Policy',
          content: IS_DEV
            ? `${contentSecurity} script-src 'unsafe-eval' 'unsafe-inline' localhost:8080`
            : contentSecurity
        }
      },
      template: path.resolve(__dirname, 'index.html'),
      minify: {
        html5: true,
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true
      },
      inject: true
    }),
    new webpack.DefinePlugin(
      Object.entries(
        dotenv.parse(
          fs.readFileSync(path.resolve(__dirname, '../../.env'))
        )
      )
        .reduce((acc, [key, value]) => ({
          ...acc,
          [`process.env.${key}`]: JSON.stringify(value)
        }), {})
    )
  ];

  if (IS_PROD) {
    jsxRule.loader = 'ts-loader';
    jsxRule.options = {};
    jsxRule.include = [
      APP_DIR,
      path.resolve('..')
    ];
    jsxRule.exclude = [
      /node_modules\/electron-timber\/preload\.js/,
      /node_modules\/(?!@nuclear).*/
    ];
    optimization.splitChunks = {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    };
  } else {
    output.publicPath = '/';
    jsxRule.exclude = /node_modules\/(?!@nuclear).*/;
    plugins.push(new webpack.HotModuleReplacementPlugin());
  }

  const config = {
    entry,
    output,
    mode: IS_PROD ? 'production' : 'development',
    optimization,
    resolve: {
      extensions: ['*', '.js', '.ts', '.jsx', '.tsx', '.json'],
      alias: {
        react: path.resolve(__dirname, 'node_modules/react'),
        'styled-component': path.resolve(__dirname, 'node_modules/styled-component')
      }
    },
    stats: 'errors-only',
    node: {
      fs: 'empty'
    },
    module: {
      rules: [
        jsxRule,
        {
          test: /.scss$/,
          use: [
            'style-loader',
            {
              loader: 'css-loader',
              options: {
                importLoaders: 1,
                modules: true,
                localIdentName: '[local]'
              }
            },
            'sass-loader'
          ]
        },
        {
          test: /\.css/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|jpg|gif)$/,
          loader: 'url-loader',
          include: [
            RESOURCES_DIR,
            APP_DIR,
            UI_DIR,
            VENDOR_DIR
          ]
        },
        {
          test: /\.(ttf|eot|woff|woff2|svg)$/,
          loader: 'url-loader',
          include: [
            UI_DIR,
            APP_DIR,
            VENDOR_DIR
          ]
        },
        {
          test: /\.svg$/,
          loader: 'svg-inline-loader'
        }
      ]
    },
    plugins,
    target: 'electron-renderer'
  };

  if (IS_DEV) {
    config.devServer =  {
      hot: true,
      contentBase: '/',
      publicPath: '/'
    };
  }

  return config;
};
<commit_msg>Include i18n and core<commit_after>/* eslint-disable @typescript-eslint/no-var-requires */
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const dotenv = require ('dotenv');
const fs = require('fs');

const BUILD_DIR = path.resolve(__dirname, '../../dist');
const APP_DIR = path.resolve(__dirname, 'app');
const RESOURCES_DIR = path.resolve(__dirname, 'resources');

const CORE_DIR = path.resolve(__dirname, '..', 'core');
const I18N_DIR = path.resolve(__dirname, '..', 'i18n');
const UI_DIR = path.resolve(__dirname, '..', 'ui');
const VENDOR_DIR = path.resolve(__dirname, 'node_modules');

module.exports = (env) => {
  const IS_PROD = env.NODE_ENV === 'production';
  const IS_DEV = env.NODE_ENV === 'development';

  const entry = IS_PROD
    ? path.resolve(APP_DIR, 'index.js')
    : [
      'react-hot-loader/patch',
      'webpack-dev-server/client?http://localhost:8080',
      'webpack/hot/only-dev-server',
      path.resolve(APP_DIR, 'index.js')
    ];
  const output = {
    path: BUILD_DIR,
    filename: 'renderer.js'
  };
  const optimization = {
    namedModules: true
  };
  const jsxRule = {
    test: /\.(js|jsx|tsx|ts)$/,
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      presets: [
        ['@babel/preset-env', {
          targets: {
            electron: '4.2'
          }
        }],
        '@babel/preset-react',
        '@babel/preset-typescript'
      ],
      plugins: [
        ['@babel/plugin-proposal-decorators', { 'legacy': true }],
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-proposal-object-rest-spread'
      ],
      ignore: [/node_modules\/(?!@nuclear).*/]
    }
  };
  const contentSecurity = 'connect-src *; style-src \'unsafe-inline\' https:; font-src https: data:; img-src https: data: file:;';
  const plugins = [
    new HtmlWebpackPlugin({
      meta: {
        charset: {
          charset: 'UTF-8'
        },
        'Content-Security-Policy': {
          'http-equiv': 'Content-Security-Policy',
          content: IS_DEV
            ? `${contentSecurity} script-src 'unsafe-eval' 'unsafe-inline' localhost:8080`
            : contentSecurity
        }
      },
      template: path.resolve(__dirname, 'index.html'),
      minify: {
        html5: true,
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true,
        useShortDoctype: true,
        removeEmptyAttributes: true,
        removeStyleLinkTypeAttributes: true,
        keepClosingSlash: true
      },
      inject: true
    }),
    new webpack.DefinePlugin(
      Object.entries(
        dotenv.parse(
          fs.readFileSync(path.resolve(__dirname, '../../.env'))
        )
      )
        .reduce((acc, [key, value]) => ({
          ...acc,
          [`process.env.${key}`]: JSON.stringify(value)
        }), {})
    )
  ];

  if (IS_PROD) {
    jsxRule.loader = 'ts-loader';
    jsxRule.options = {};
    jsxRule.include = [
      APP_DIR,
      CORE_DIR,
      I18N_DIR,
      UI_DIR
    ];
    jsxRule.exclude = [
      /node_modules\/electron-timber\/preload\.js/,
      /node_modules\/(?!@nuclear).*/
    ];
    optimization.splitChunks = {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    };
  } else {
    output.publicPath = '/';
    jsxRule.exclude = /node_modules\/(?!@nuclear).*/;
    plugins.push(new webpack.HotModuleReplacementPlugin());
  }

  const config = {
    entry,
    output,
    mode: IS_PROD ? 'production' : 'development',
    optimization,
    resolve: {
      extensions: ['*', '.js', '.ts', '.jsx', '.tsx', '.json'],
      alias: {
        react: path.resolve(__dirname, 'node_modules/react'),
        'styled-component': path.resolve(__dirname, 'node_modules/styled-component')
      }
    },
    stats: 'errors-only',
    node: {
      fs: 'empty'
    },
    module: {
      rules: [
        jsxRule,
        {
          test: /.scss$/,
          use: [
            'style-loader',
            {
              loader: 'css-loader',
              options: {
                importLoaders: 1,
                modules: true,
                localIdentName: '[local]'
              }
            },
            'sass-loader'
          ]
        },
        {
          test: /\.css/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|jpg|gif)$/,
          loader: 'url-loader',
          include: [
            RESOURCES_DIR,
            APP_DIR,
            UI_DIR,
            VENDOR_DIR
          ]
        },
        {
          test: /\.(ttf|eot|woff|woff2|svg)$/,
          loader: 'url-loader',
          include: [
            UI_DIR,
            APP_DIR,
            VENDOR_DIR
          ]
        },
        {
          test: /\.svg$/,
          loader: 'svg-inline-loader'
        }
      ]
    },
    plugins,
    target: 'electron-renderer'
  };

  if (IS_DEV) {
    config.devServer =  {
      hot: true,
      contentBase: '/',
      publicPath: '/'
    };
  }

  return config;
};
<|endoftext|>


<commit_before>import { auth } from 'arsenal';
import { UtapiClient } from 'utapi';
import commander from 'commander';
import http from 'http';
import https from 'https';
import { logger } from '../utilities/logger';
import _config from '../Config';
// setup utapi client
const utapi = new UtapiClient(_config.utapi);

function _listMetrics(host,
                      port,
                      metric,
                      metricType,
                      timeRange,
                      accessKey,
                      secretKey,
                      verbose,
                      ssl) {
    const options = {
        host,
        port,
        method: 'POST',
        path: `/${metric}?Action=ListMetrics`,
        headers: {
            'content-type': 'application/json',
            'cache-control': 'no-cache',
        },
        rejectUnauthorized: false,
    };
    const transport = ssl ? https : http;
    const request = transport.request(options, response => {
        if (verbose) {
            logger.info('response status code', {
                statusCode: response.statusCode,
            });
            logger.info('response headers', { headers: response.headers });
        }
        const body = [];
        response.setEncoding('utf8');
        response.on('data', chunk => body.push(chunk));
        response.on('end', () => {
            const responseBody = JSON.parse(body.join(''));
            if (response.statusCode >= 200 && response.statusCode < 300) {
                process.stdout.write(JSON.stringify(responseBody, null, 2));
                process.stdout.write('\n');
                process.exit(0);
            } else {
                logger.error('request failed with HTTP Status ', {
                    statusCode: response.statusCode,
                    body: responseBody,
                });
                process.exit(1);
            }
        });
    });
    // TODO: cleanup with refactor of generateV4Headers
    request.path = `/${metric}`;
    auth.client.generateV4Headers(request, { Action: 'ListMetrics' },
        accessKey, secretKey, 's3');
    request.path = `/${metric}?Action=ListMetrics`;
    if (verbose) {
        logger.info('request headers', { headers: request._headers });
    }
    const requestObj = { timeRange };
    requestObj[metric] = metricType;
    request.write(JSON.stringify(requestObj));
    request.end();
}

/**
 * This function is used as a binary to send a request to utapi server
 * to list metrics for buckets or accounts
 * @param {string} [metricType] - (optional) Defined as 'buckets' if old style
 * bucket metrics listing
 * @return {undefined}
 */
export function listMetrics(metricType) {
    commander
        .version('0.0.1')
        .option('-a, --access-key <accessKey>', 'Access key id')
        .option('-k, --secret-key <secretKey>', 'Secret access key');
    // We want to continue support of previous bucket listing. Hence the ability
    // to specify `metricType`. Remove `if` statement and
    // bin/list_bucket_metrics.js when prior method of listing bucket metrics is
    // no longer supported.
    if (metricType === 'buckets') {
        commander
            .option('-b, --buckets <buckets>', 'Name of bucket(s) with ' +
            'a comma separator if more than one');
    } else {
        commander
            .option('-m, --metric <metric>', 'Metric type')
            .option('--buckets <buckets>', 'Name of bucket(s) with a comma ' +
                'separator if more than one')
            .option('--accounts <accounts>', 'Account ID(s) with a comma ' +
                'separator if more than one')
            .option('--users <users>', 'User ID(s) with a comma separator if ' +
                'more than one')
            .option('--service <service>', 'Name of service');
    }
    commander
        .option('-s, --start <start>', 'Start of time range')
        .option('-e --end <end>', 'End of time range')
        .option('-h, --host <host>', 'Host of the server')
        .option('-p, --port <port>', 'Port of the server')
        .option('--ssl', 'Enable ssl')
        .option('-v, --verbose')
        .parse(process.argv);

    const { host, port, accessKey, secretKey, start, end, verbose, ssl } =
        commander;
    const requiredOptions = { host, port, accessKey, secretKey };
    // If not old style bucket metrics, we require usage of the metric option
    if (metricType !== 'buckets') {
        requiredOptions.metric = commander.metric;
        const validMetrics = ['buckets', 'accounts', 'users', 'service'];
        if (validMetrics.indexOf(commander.metric) < 0) {
            logger.error('metric must be \'buckets\', \'accounts\', ' +
                '\'users\', or \'service\'');
            commander.outputHelp();
            process.exit(1);
            return;
        }
    }
    // If old style bucket metrics, `metricType` will be 'buckets'. Otherwise,
    // `commander.metric` should be defined.
    const metric = metricType === 'buckets' ? 'buckets' : commander.metric;
    requiredOptions[metric] = commander[metric];

    Object.keys(requiredOptions).forEach(option => {
        if (!requiredOptions[option]) {
            logger.error(`missing required option: ${option}`);
            commander.outputHelp();
            process.exit(1);
            return;
        }
    });

    const numStart = Number.parseInt(start, 10);
    if (!numStart) {
        logger.error('start must be a number');
        commander.outputHelp();
        process.exit(1);
    }

    const timeRange = [numStart];

    if (end) {
        const numEnd = Number.parseInt(end, 10);
        if (!numEnd) {
            logger.error('if provide end, end must be a number');
            commander.outputHelp();
            process.exit(1);
        }
        timeRange.push(numEnd);
    }
    // The string `commander[metric]` is a comma-separated list of resources
    // given by the user.
    const resources = commander[metric].split(',');
    _listMetrics(host, port, metric, resources, timeRange, accessKey, secretKey,
        verbose, ssl);
}

/**
 * Call the Utapi Client `pushMetric` method with the associated parameters
 * @param {string} action - the metric action to push a metric for
 * @param {object} log - werelogs logger
 * @param {object} metricObj - the object containing the relevant data for
 * pushing metrics in Utapi
 * @param {string} [metricObj.bucket] - (optional) bucket name
 * @param {AuthInfo} [metricObj.authInfo] - (optional) Instance of AuthInfo
 * class with requester's info
 * @param {number} [metricObj.byteLength] - (optional) current object size
 * (used, for example, for pushing 'deleteObject' metrics)
 * @param {number} [metricObj.newByteLength] - (optional) new object size
 * @param {number|null} [metricObj.oldByteLength] - (optional) old object size
 * (obj. overwrites)
 * @param {number} [metricObj.numberOfObjects] - (optional) number of obects
 * added/deleted
 * @return {function} - `utapi.pushMetric`
 */
export function pushMetric(action, log, metricObj) {
    const { bucket, byteLength, newByteLength, oldByteLength, numberOfObjects,
        authInfo } = metricObj;
    const utapiObj = {
        bucket,
        byteLength,
        newByteLength,
        oldByteLength,
        numberOfObjects,
    };
    // If `authInfo` is included by the API, get the account's canonical ID for
    // account-level metrics and the shortId for user-level metrics.
    if (authInfo) {
        utapiObj.accountId = authInfo.getCanonicalID();
        utapiObj.userId = authInfo.isRequesterAnIAMUser() ?
            authInfo.getShortid() : undefined;
    }
    return utapi.pushMetric(action, log.getSerializedUids(), utapiObj);
}
<commit_msg>FT: Add recent metrics listing option<commit_after>import { auth } from 'arsenal';
import { UtapiClient } from 'utapi';
import commander from 'commander';
import http from 'http';
import https from 'https';
import { logger } from '../utilities/logger';
import _config from '../Config';
// setup utapi client
const utapi = new UtapiClient(_config.utapi);

function _listMetrics(host,
                      port,
                      metric,
                      metricType,
                      timeRange,
                      accessKey,
                      secretKey,
                      verbose,
                      recent,
                      ssl) {
    const listAction = recent ? 'ListRecentMetrics' : 'ListMetrics';
    const options = {
        host,
        port,
        method: 'POST',
        path: `/${metric}?Action=${listAction}`,
        headers: {
            'content-type': 'application/json',
            'cache-control': 'no-cache',
        },
        rejectUnauthorized: false,
    };
    const transport = ssl ? https : http;
    const request = transport.request(options, response => {
        if (verbose) {
            logger.info('response status code', {
                statusCode: response.statusCode,
            });
            logger.info('response headers', { headers: response.headers });
        }
        const body = [];
        response.setEncoding('utf8');
        response.on('data', chunk => body.push(chunk));
        response.on('end', () => {
            const responseBody = JSON.parse(body.join(''));
            if (response.statusCode >= 200 && response.statusCode < 300) {
                process.stdout.write(JSON.stringify(responseBody, null, 2));
                process.stdout.write('\n');
                process.exit(0);
            } else {
                logger.error('request failed with HTTP Status ', {
                    statusCode: response.statusCode,
                    body: responseBody,
                });
                process.exit(1);
            }
        });
    });
    // TODO: cleanup with refactor of generateV4Headers
    request.path = `/${metric}`;
    auth.client.generateV4Headers(request, { Action: listAction },
        accessKey, secretKey, 's3');
    request.path = `/${metric}?Action=${listAction}`;
    if (verbose) {
        logger.info('request headers', { headers: request._headers });
    }
    // If recent listing, we do not provide `timeRange` in the request
    const requestObj = recent ? {} : { timeRange };
    requestObj[metric] = metricType;
    request.write(JSON.stringify(requestObj));
    request.end();
}

/**
 * This function is used as a binary to send a request to utapi server
 * to list metrics for buckets or accounts
 * @param {string} [metricType] - (optional) Defined as 'buckets' if old style
 * bucket metrics listing
 * @return {undefined}
 */
export function listMetrics(metricType) {
    commander
        .version('0.0.1')
        .option('-a, --access-key <accessKey>', 'Access key id')
        .option('-k, --secret-key <secretKey>', 'Secret access key');
    // We want to continue support of previous bucket listing. Hence the ability
    // to specify `metricType`. Remove `if` statement and
    // bin/list_bucket_metrics.js when prior method of listing bucket metrics is
    // no longer supported.
    if (metricType === 'buckets') {
        commander
            .option('-b, --buckets <buckets>', 'Name of bucket(s) with ' +
            'a comma separator if more than one');
    } else {
        commander
            .option('-m, --metric <metric>', 'Metric type')
            .option('--buckets <buckets>', 'Name of bucket(s) with a comma ' +
                'separator if more than one')
            .option('--accounts <accounts>', 'Account ID(s) with a comma ' +
                'separator if more than one')
            .option('--users <users>', 'User ID(s) with a comma separator if ' +
                'more than one')
            .option('--service <service>', 'Name of service');
    }
    commander
        .option('-s, --start <start>', 'Start of time range')
        .option('-r, --recent', 'List metrics including the previous and ' +
            'current 15 minute interval')
        .option('-e --end <end>', 'End of time range')
        .option('-h, --host <host>', 'Host of the server')
        .option('-p, --port <port>', 'Port of the server')
        .option('--ssl', 'Enable ssl')
        .option('-v, --verbose')
        .parse(process.argv);

    const { host, port, accessKey, secretKey, start, end, verbose, recent,
        ssl } =
        commander;
    const requiredOptions = { host, port, accessKey, secretKey };
    // If not old style bucket metrics, we require usage of the metric option
    if (metricType !== 'buckets') {
        requiredOptions.metric = commander.metric;
        const validMetrics = ['buckets', 'accounts', 'users', 'service'];
        if (validMetrics.indexOf(commander.metric) < 0) {
            logger.error('metric must be \'buckets\', \'accounts\', ' +
                '\'users\', or \'service\'');
            commander.outputHelp();
            process.exit(1);
            return;
        }
    }
    // If old style bucket metrics, `metricType` will be 'buckets'. Otherwise,
    // `commander.metric` should be defined.
    const metric = metricType === 'buckets' ? 'buckets' : commander.metric;
    requiredOptions[metric] = commander[metric];
    // If not recent listing, the start option must be provided
    if (!recent) {
        requiredOptions.start = commander.start;
    }
    Object.keys(requiredOptions).forEach(option => {
        if (!requiredOptions[option]) {
            logger.error(`missing required option: ${option}`);
            commander.outputHelp();
            process.exit(1);
            return;
        }
    });

    const timeRange = [];
    // If recent listing, we disregard any start or end option given
    if (!recent) {
        const numStart = Number.parseInt(start, 10);
        if (!numStart) {
            logger.error('start must be a number');
            commander.outputHelp();
            process.exit(1);
            return;
        }
        timeRange.push(numStart);
        if (end) {
            const numEnd = Number.parseInt(end, 10);
            if (!numEnd) {
                logger.error('end must be a number');
                commander.outputHelp();
                process.exit(1);
                return;
            }
            timeRange.push(numEnd);
        }
    }
    // The string `commander[metric]` is a comma-separated list of resources
    // given by the user.
    const resources = commander[metric].split(',');
    _listMetrics(host, port, metric, resources, timeRange, accessKey, secretKey,
        verbose, recent, ssl);
}

/**
 * Call the Utapi Client `pushMetric` method with the associated parameters
 * @param {string} action - the metric action to push a metric for
 * @param {object} log - werelogs logger
 * @param {object} metricObj - the object containing the relevant data for
 * pushing metrics in Utapi
 * @param {string} [metricObj.bucket] - (optional) bucket name
 * @param {AuthInfo} [metricObj.authInfo] - (optional) Instance of AuthInfo
 * class with requester's info
 * @param {number} [metricObj.byteLength] - (optional) current object size
 * (used, for example, for pushing 'deleteObject' metrics)
 * @param {number} [metricObj.newByteLength] - (optional) new object size
 * @param {number|null} [metricObj.oldByteLength] - (optional) old object size
 * (obj. overwrites)
 * @param {number} [metricObj.numberOfObjects] - (optional) number of obects
 * added/deleted
 * @return {function} - `utapi.pushMetric`
 */
export function pushMetric(action, log, metricObj) {
    const { bucket, byteLength, newByteLength, oldByteLength, numberOfObjects,
        authInfo } = metricObj;
    const utapiObj = {
        bucket,
        byteLength,
        newByteLength,
        oldByteLength,
        numberOfObjects,
    };
    // If `authInfo` is included by the API, get the account's canonical ID for
    // account-level metrics and the shortId for user-level metrics.
    if (authInfo) {
        utapiObj.accountId = authInfo.getCanonicalID();
        utapiObj.userId = authInfo.isRequesterAnIAMUser() ?
            authInfo.getShortid() : undefined;
    }
    return utapi.pushMetric(action, log.getSerializedUids(), utapiObj);
}
<|endoftext|>


<commit_before>module.exports = exports;

var path = require('path')
  , semver = require('semver')
  , url = require('url')
  , abi_crosswalk = require('./abi_crosswalk.json')

function get_node_abi(runtime, target) {
    if (target) {
        // abi_crosswalk generated with ./scripts/abi_crosswalk.js
        var abi = '';
        if (runtime === 'node-webkit') {
            return runtime + '-v' + target;
        } else {
            if (abi_crosswalk[target]) {
                abi = abi_crosswalk[target].node_abi;
            }
        }
        if (!abi) {
            throw new Error("Unsupported target version: " + target);
        }
        if (abi > 1) {
            return runtime+'-v' + (+abi);
        } else {
            // no support for node-webkit unless > 0.10.x
            if (runtime != 'node') {
                throw new Error("Runtime '" + runtime + "' unsupported for target version: " + target);
            }
            if (!abi_crosswalk[target]) {
                throw new Error("Unsupported target version: " + target);
            }
            abi = abi_crosswalk[target].v8;
            return 'v8-' + abi;
        }
    } else {
        // process.versions.modules added in >= v0.10.4 and v0.11.7
        // https://github.com/joyent/node/commit/ccabd4a6fa8a6eb79d29bc3bbe9fe2b6531c2d8e
        return process.versions.modules ? runtime+'-v' + (+process.versions.modules) :
               'v8-' + process.versions.v8.split('.').slice(0,2).join('.');
    }
}

var required_parameters = [
    'module_name',
    'module_path',
    'host'
];

function validate_config(package_json,callback) {
    var msg = package_json.name + ' package.json is not node-pre-gyp ready:\n';
    var missing = [];
    if (!package_json.main) {
        missing.push('main');
    }
    if (!package_json.name) {
        missing.push('name');
    }
    if (!package_json.binary) {
        missing.push('binary');
    }
    var o = package_json.binary;
    required_parameters.forEach(function(p) {
        if (missing.indexOf('binary') > -1) {
            missing.pop('binary');
        }
        if (!o || o[p] == undefined) {
            missing.push('binary.' + p);
        }
    });
    if (missing.length >= 1) {
        throw new Error(msg+"package.json must declare these properties: \n" + missing.join('\n'));
    }
    if (o) {
        // enforce https over http
        var protocol = url.parse(o.host).protocol;
        if (protocol === 'http:') {
            throw new Error("'host' protocol ("+protocol+") is invalid - only 'https:' is accepted");
        }
    }
};

module.exports.validate_config = validate_config;

function eval_template(template,opts) {
    Object.keys(opts).forEach(function(key) {
        var pattern = '{'+key+'}';
        while (template.indexOf(pattern) > -1) {
            template = template.replace(pattern,opts[key]);
        }
    });
    return template;
}

// url.resolve needs single trailing slash
// to behave correctly, otherwise a double slash
// may end up in the url which breaks requests
// and a lacking slash may not lead to proper joining
function add_trailing_slash(pathname) {
    if (pathname.slice(-1) != '/') {
        return pathname + '/';
    }
    return pathname;
}

var default_package_name = '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
var default_remote_path = '';

module.exports.evaluate = function(package_json,options) {
    options = options || {};
    validate_config(package_json);
    var v = package_json.version;
    var module_version = semver.parse(v);
    var runtime = options.runtime || (process.versions['node-webkit'] ? 'node-webkit' : 'node');
    var opts = {
          name: package_json.name
        , configuration: Boolean(options.debug) ? 'Debug' : 'Release'
        , debug: options.debug
        , module_name: package_json.binary.module_name
        , version: module_version.version
        , prerelease: module_version.prerelease.length ? v.slice(v.indexOf(module_version.prerelease[0])) : ''
        , major: module_version.major
        , minor: module_version.minor
        , patch: module_version.patch
        , runtime: runtime
        , node_abi: get_node_abi(runtime,options.target)
        , target: options.target || ''
        , platform: options.target_platform || process.platform
        , target_platform: options.target_platform || process.platform
        , arch: options.target_arch || process.arch
        , target_arch: options.target_arch || process.arch
        , module_main: package_json.main
    }
    opts.host = add_trailing_slash(eval_template(package_json.binary.host,opts));
    opts.module_path = eval_template(package_json.binary.module_path,opts);
    // now we resolve the module_path to ensure it is absolute so that binding.gyp variables work predictably
    if (options.module_root) {
        // resolve relative to known module root: works for pre-binding require
        opts.module_path = path.join(options.module_root,opts.module_path);
    } else {
        // resolve relative to current working directory: works for node-pre-gyp commands
        opts.module_path = path.resolve(opts.module_path);
    }
    opts.module = path.join(opts.module_path,opts.module_name + '.node')
    opts.remote_path = package_json.binary.remote_path ? add_trailing_slash(eval_template(package_json.binary.remote_path,opts)) : default_remote_path;
    var package_name = package_json.binary.package_name ? package_json.binary.package_name : default_package_name;
    opts.package_name = eval_template(package_name,opts);
    opts.staged_tarball = path.join('build/stage',opts.remote_path,opts.package_name);
    opts.hosted_path = url.resolve(opts.host,opts.remote_path);
    opts.hosted_tarball = url.resolve(opts.hosted_path,opts.package_name);
    return opts;
}
<commit_msg>fix versioning for node-webkit when `target` is empty<commit_after>module.exports = exports;

var path = require('path')
  , semver = require('semver')
  , url = require('url')
  , abi_crosswalk = require('./abi_crosswalk.json')

function get_node_abi(runtime, target) {
    if (target) {
        // abi_crosswalk generated with ./scripts/abi_crosswalk.js
        var abi = '';
        if (runtime === 'node-webkit') {
            return runtime + '-v' + target;
        } else {
            if (abi_crosswalk[target]) {
                abi = abi_crosswalk[target].node_abi;
            }
        }
        if (!abi) {
            throw new Error("Unsupported target version: " + target);
        }
        if (abi > 1) {
            return runtime+'-v' + (+abi);
        } else {
            // no support for node-webkit unless > 0.10.x
            if (runtime != 'node') {
                throw new Error("Runtime '" + runtime + "' unsupported for target version: " + target);
            }
            if (!abi_crosswalk[target]) {
                throw new Error("Unsupported target version: " + target);
            }
            abi = abi_crosswalk[target].v8;
            return 'v8-' + abi;
        }
    } else {
        if (runtime === 'node-webkit') {
            if (typeof process.versions['node-webkit'] === 'undefined') {
                // erroneous CLI call
                throw new Error("Empty target version is not supported if node-webkit is the target.");
            }
            return runtime + '-v' + process.versions['node-webkit'];
        }
        // process.versions.modules added in >= v0.10.4 and v0.11.7
        // https://github.com/joyent/node/commit/ccabd4a6fa8a6eb79d29bc3bbe9fe2b6531c2d8e
        return process.versions.modules ? runtime+'-v' + (+process.versions.modules) :
               'v8-' + process.versions.v8.split('.').slice(0,2).join('.');
    }
}

var required_parameters = [
    'module_name',
    'module_path',
    'host'
];

function validate_config(package_json,callback) {
    var msg = package_json.name + ' package.json is not node-pre-gyp ready:\n';
    var missing = [];
    if (!package_json.main) {
        missing.push('main');
    }
    if (!package_json.name) {
        missing.push('name');
    }
    if (!package_json.binary) {
        missing.push('binary');
    }
    var o = package_json.binary;
    required_parameters.forEach(function(p) {
        if (missing.indexOf('binary') > -1) {
            missing.pop('binary');
        }
        if (!o || o[p] == undefined) {
            missing.push('binary.' + p);
        }
    });
    if (missing.length >= 1) {
        throw new Error(msg+"package.json must declare these properties: \n" + missing.join('\n'));
    }
    if (o) {
        // enforce https over http
        var protocol = url.parse(o.host).protocol;
        if (protocol === 'http:') {
            throw new Error("'host' protocol ("+protocol+") is invalid - only 'https:' is accepted");
        }
    }
};

module.exports.validate_config = validate_config;

function eval_template(template,opts) {
    Object.keys(opts).forEach(function(key) {
        var pattern = '{'+key+'}';
        while (template.indexOf(pattern) > -1) {
            template = template.replace(pattern,opts[key]);
        }
    });
    return template;
}

// url.resolve needs single trailing slash
// to behave correctly, otherwise a double slash
// may end up in the url which breaks requests
// and a lacking slash may not lead to proper joining
function add_trailing_slash(pathname) {
    if (pathname.slice(-1) != '/') {
        return pathname + '/';
    }
    return pathname;
}

var default_package_name = '{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz';
var default_remote_path = '';

module.exports.evaluate = function(package_json,options) {
    options = options || {};
    validate_config(package_json);
    var v = package_json.version;
    var module_version = semver.parse(v);
    var runtime = options.runtime || (process.versions['node-webkit'] ? 'node-webkit' : 'node');
    var opts = {
          name: package_json.name
        , configuration: Boolean(options.debug) ? 'Debug' : 'Release'
        , debug: options.debug
        , module_name: package_json.binary.module_name
        , version: module_version.version
        , prerelease: module_version.prerelease.length ? v.slice(v.indexOf(module_version.prerelease[0])) : ''
        , major: module_version.major
        , minor: module_version.minor
        , patch: module_version.patch
        , runtime: runtime
        , node_abi: get_node_abi(runtime,options.target)
        , target: options.target || ''
        , platform: options.target_platform || process.platform
        , target_platform: options.target_platform || process.platform
        , arch: options.target_arch || process.arch
        , target_arch: options.target_arch || process.arch
        , module_main: package_json.main
    }
    opts.host = add_trailing_slash(eval_template(package_json.binary.host,opts));
    opts.module_path = eval_template(package_json.binary.module_path,opts);
    // now we resolve the module_path to ensure it is absolute so that binding.gyp variables work predictably
    if (options.module_root) {
        // resolve relative to known module root: works for pre-binding require
        opts.module_path = path.join(options.module_root,opts.module_path);
    } else {
        // resolve relative to current working directory: works for node-pre-gyp commands
        opts.module_path = path.resolve(opts.module_path);
    }
    opts.module = path.join(opts.module_path,opts.module_name + '.node')
    opts.remote_path = package_json.binary.remote_path ? add_trailing_slash(eval_template(package_json.binary.remote_path,opts)) : default_remote_path;
    var package_name = package_json.binary.package_name ? package_json.binary.package_name : default_package_name;
    opts.package_name = eval_template(package_name,opts);
    opts.staged_tarball = path.join('build/stage',opts.remote_path,opts.package_name);
    opts.hosted_path = url.resolve(opts.host,opts.remote_path);
    opts.hosted_tarball = url.resolve(opts.hosted_path,opts.package_name);
    return opts;
}
<|endoftext|>


<commit_before>var assign = require('lodash.assign');

var properties = require('./properties');
var refEncoders = require('./refEncoders');
var test = require('./test');
var types = require('./types');

var utilities = module.exports = assign({
    convertDateToSerial: function JSDateToExcelDate(inDate) {
        // assumes date given is GMT
        if (!(inDate instanceof Date)) inDate = new Date(inDate);
        var returnDateTime = 25569.0 + (inDate.getTime() ? (inDate / 86400000) : -25568.0);
        return returnDateTime;
    },
    dirname: function (path) {
        return path ? path.split('/').slice(0, -1).join('/') : '';
    },
}, properties, refEncoders, test, types);
<commit_msg>utilities: add upperFirst method<commit_after>var assign = require('lodash.assign');

var properties = require('./properties');
var refEncoders = require('./refEncoders');
var test = require('./test');
var types = require('./types');

var utilities = module.exports = assign({
    convertDateToSerial: function JSDateToExcelDate(inDate) {
        // assumes date given is GMT
        if (!(inDate instanceof Date)) inDate = new Date(inDate);
        var returnDateTime = 25569.0 + (inDate.getTime() ? (inDate / 86400000) : -25568.0);
        return returnDateTime;
    },
    dirname: function (path) {
        return path ? path.split('/').slice(0, -1).join('/') : '';
    },
    upperFirst: function (str) {
        if (!str || typeof str !== 'string') return '';
        return str.charAt(0).toUpperCase() + str.slice(1);
    }
}, properties, refEncoders, test, types);
<|endoftext|>


<commit_before>
exports = module.exports = lifecycle
exports.cmd = cmd

var log = require("./log")
  , exec = require("./exec")
  , npm = require("../../npm")
  , path = require("path")
  , readJson = require("./read-json")
  , fs = require("./graceful-fs")
  , chain = require("./chain")

function lifecycle (pkg, stage, cb) {
  while (pkg && pkg._data) pkg = pkg._data
  if (!pkg) return cb(new Error("Invalid package data"))
  log.verbose(pkg._id, "lifecycle "+stage)

  // set the env variables, then run scripts as a child process.
  var env = makeEnv(pkg)
  env.npm_lifecycle_event = stage

  var packageLifecycle = pkg.scripts && (stage in pkg.scripts)

  if (packageLifecycle) {
    // define this here so it's available to all scripts.
    env.npm_lifecycle_script = pkg.scripts[stage]
  }

  log.silly(env, "lifecycle env")
  chain
    ( packageLifecycle && [runPackageLifecycle, pkg, env]
    , [runHookLifecycle, pkg, env]
    , cb
    )
}

function runPackageLifecycle (pkg, env, cb) {
  // run package lifecycle scripts in the package root, or the nearest parent.
  var stage = env.npm_lifecycle_event
  var d = path.join(npm.dir, pkg.name, pkg.version, "package")
  while (d) {
    try {
      process.chdir(d)
      break
    } catch (ex) {
      d = path.dirname(d)
    }
  }
  log(pkg._id, stage)

  exec("sh", ["-c", env.npm_lifecycle_script], env, function (er) {
    if (er && !npm.ROLLBACK) {
      log("Failed to exec "+stage+" script", pkg._id)
      er.message = pkg._id + " " + stage + ": `" + env.npm_lifecycle_script+"`\n"
                 + er.message
      er.errno = npm.ELIFECYCLE
      er.pkgid = pkg._id
      er.stage = stage
      er.script = env.npm_lifecycle_script
      er.pkgname = pkg.name
      return cb(er)
    } else if (er) {
      log.error(er, pkg._id+"."+stage)
      log.error("failed, but continuing anyway", pkg._id+"."+stage)
    }
    cb(er)
  })
}

function runHookLifecycle (pkg, env, cb) {
  // check for a hook script, run if present.
  var stage = env.npm_lifecycle_event
  var hook = path.join(npm.dir, ".hooks", stage)
  fs.stat(hook, function (er) {
    if (er) return cb()
    exec(hook, [], env, function (er) {
      if (er) {
        er.message += "\nFailed to exec "+stage+" hook script"
        log(er, pkg._id)
      }
      if (npm.ROLLBACK) return cb()
      cb(er)
    })
  })
}

function makeEnv (data, prefix, env) {
  if (data._lifecycleEnv) return data._lifecycleEnv
  prefix = prefix || "npm_package_"
  if (!env) {
    env = {}
    for (var i in process.env) env[i] = process.env[i]
  } else {
    Object.defineProperty(data, "_lifecycleEnv",
      { value : env
      , enumerable : false
      })
  }

  for (var i in data) if (i.charAt(0) !== "_") {
    var envKey = (prefix+i).replace(/[^a-zA-Z0-9_]/g, '_')
    if (data[i] && typeof(data[i]) === "object") {
      env[envKey] = JSON.stringify(data[i])
      makeEnv(data[i], envKey+"_", env)
    } else {
      env[envKey] = String(data[i])
    }
  }
  if (prefix !== "npm_package_") return env
  prefix = "npm_config_"
  var conf = npm.config.get()
  for (var i in conf) if (i.charAt(0) !== "_") {
    var envKey = (prefix+i).replace(/[^a-zA-Z0-9_]/g, '_')
    env[envKey] = String(conf[i])
  }
  return env
}

function cmd (stage, req) { return function (args, cb_) {
  var d = args.length
  function cb (er) {
    if (er) return cb_(er)
    if (-- d === 0) return cb_()
  }
  args.forEach(function (arg) {
    var pkg = arg.split("@")
      , name = pkg.shift()
      , ver = pkg.join("@")
      , json = path.join(npm.dir, name, ver || "active", "package/package.json")
    readJson(json, function (er, data) {
      if (er) return log.er(cb, "Couldn't find "+name + "@" + ver)(er)
      if (ver && ver !== data.version) {
        data.version = ver
        data._id = data.name+"@"+ver
      }
      if ( !data.scripts
          || !(stage in data.scripts)
          && !("pre"+stage in data.scripts)) {
        log("Nothing to do", stage)
        return cb(req ? new Error("Nothing to do") : null)
      }
      lifecycle(data, "pre"+stage, function (er) {
        if (er) return log.er(cb, "Failed pre"+stage)(er)
        lifecycle(data, stage, cb)
      })
    })
  })
}}
<commit_msg>Log the stages on "info".  It's really helpful for debugging where something went wrong.<commit_after>
exports = module.exports = lifecycle
exports.cmd = cmd

var log = require("./log")
  , exec = require("./exec")
  , npm = require("../../npm")
  , path = require("path")
  , readJson = require("./read-json")
  , fs = require("./graceful-fs")
  , chain = require("./chain")

function lifecycle (pkg, stage, cb) {
  while (pkg && pkg._data) pkg = pkg._data
  if (!pkg) return cb(new Error("Invalid package data"))
  log(pkg._id, stage)

  // set the env variables, then run scripts as a child process.
  var env = makeEnv(pkg)
  env.npm_lifecycle_event = stage

  var packageLifecycle = pkg.scripts && (stage in pkg.scripts)

  if (packageLifecycle) {
    // define this here so it's available to all scripts.
    env.npm_lifecycle_script = pkg.scripts[stage]
  }

  log.silly(env, "lifecycle env")
  chain
    ( packageLifecycle && [runPackageLifecycle, pkg, env]
    , [runHookLifecycle, pkg, env]
    , cb
    )
}

function runPackageLifecycle (pkg, env, cb) {
  // run package lifecycle scripts in the package root, or the nearest parent.
  var stage = env.npm_lifecycle_event
  var d = path.join(npm.dir, pkg.name, pkg.version, "package")
  while (d) {
    try {
      process.chdir(d)
      break
    } catch (ex) {
      d = path.dirname(d)
    }
  }

  exec("sh", ["-c", env.npm_lifecycle_script], env, function (er) {
    if (er && !npm.ROLLBACK) {
      log("Failed to exec "+stage+" script", pkg._id)
      er.message = pkg._id + " " + stage + ": `" + env.npm_lifecycle_script+"`\n"
                 + er.message
      er.errno = npm.ELIFECYCLE
      er.pkgid = pkg._id
      er.stage = stage
      er.script = env.npm_lifecycle_script
      er.pkgname = pkg.name
      return cb(er)
    } else if (er) {
      log.error(er, pkg._id+"."+stage)
      log.error("failed, but continuing anyway", pkg._id+"."+stage)
    }
    cb(er)
  })
}

function runHookLifecycle (pkg, env, cb) {
  // check for a hook script, run if present.
  var stage = env.npm_lifecycle_event
  var hook = path.join(npm.dir, ".hooks", stage)
  fs.stat(hook, function (er) {
    if (er) return cb()
    exec(hook, [], env, function (er) {
      if (er) {
        er.message += "\nFailed to exec "+stage+" hook script"
        log(er, pkg._id)
      }
      if (npm.ROLLBACK) return cb()
      cb(er)
    })
  })
}

function makeEnv (data, prefix, env) {
  if (data._lifecycleEnv) return data._lifecycleEnv
  prefix = prefix || "npm_package_"
  if (!env) {
    env = {}
    for (var i in process.env) env[i] = process.env[i]
  } else {
    Object.defineProperty(data, "_lifecycleEnv",
      { value : env
      , enumerable : false
      })
  }

  for (var i in data) if (i.charAt(0) !== "_") {
    var envKey = (prefix+i).replace(/[^a-zA-Z0-9_]/g, '_')
    if (data[i] && typeof(data[i]) === "object") {
      env[envKey] = JSON.stringify(data[i])
      makeEnv(data[i], envKey+"_", env)
    } else {
      env[envKey] = String(data[i])
    }
  }
  if (prefix !== "npm_package_") return env
  prefix = "npm_config_"
  var conf = npm.config.get()
  for (var i in conf) if (i.charAt(0) !== "_") {
    var envKey = (prefix+i).replace(/[^a-zA-Z0-9_]/g, '_')
    env[envKey] = String(conf[i])
  }
  return env
}

function cmd (stage, req) { return function (args, cb_) {
  var d = args.length
  function cb (er) {
    if (er) return cb_(er)
    if (-- d === 0) return cb_()
  }
  args.forEach(function (arg) {
    var pkg = arg.split("@")
      , name = pkg.shift()
      , ver = pkg.join("@")
      , json = path.join(npm.dir, name, ver || "active", "package/package.json")
    readJson(json, function (er, data) {
      if (er) return log.er(cb, "Couldn't find "+name + "@" + ver)(er)
      if (ver && ver !== data.version) {
        data.version = ver
        data._id = data.name+"@"+ver
      }
      if ( !data.scripts
          || !(stage in data.scripts)
          && !("pre"+stage in data.scripts)) {
        log("Nothing to do", stage)
        return cb(req ? new Error("Nothing to do") : null)
      }
      lifecycle(data, "pre"+stage, function (er) {
        if (er) return log.er(cb, "Failed pre"+stage)(er)
        lifecycle(data, stage, cb)
      })
    })
  })
}}
<|endoftext|>


<commit_before>'use strict';

const logError = require('./logError');

/*!
 * Compare two routes
 * @param {Object} r1 The first route { verb: 'get', path: '/:id' }
 * @param [Object} r2 The second route route: { verb: 'get', path: '/findOne' }
 * @returns {number} 1: r1 comes after r2, -1: r1 comes before r2, 0: equal
 */
module.exports = function sortRoute(r1, r2) {
  // Normalize the verbs
  let verb1 = r1.verb.toLowerCase();
  let verb2 = r2.verb.toLowerCase();

  if (r1.name === r2.name) {
    logError(`Conflict method '${r1.name}' detected`);
  }

  if (verb1 === verb2 && r1.path === r2.path && verb1 !== 'use') {
    logError(`Conflict route path and verb detected for method '${r2.name}' and method '${r1.name}'`);
  }

  if (verb1 === 'use' && verb2 === 'use') {
    return 0;
  } else if (verb1 === 'use' && verb2 !== 'use') {
    return -1;
  } else if (verb1 !== 'use' && verb2 === 'use') {
    return 1;
  }

  // First sort by verb
  if (verb1 > verb2) {
    return -1;
  } else if (verb1 < verb2) {
    return 1;
  }

  // Sort by path part by part using the / delimiter
  // For example '/:id' will become ['', ':id'], '/findOne' will become
  // ['', 'findOne']
  let p1 = r1.path.split('/');
  let p2 = r2.path.split('/');
  let len = Math.min(p1.length, p2.length);

  // Loop through the parts and decide which path should come first
  for (let i = 0; i < len; i++) {
    // Asterisk has lower weight
    if (p1[i] === '*' && p2[i] !== '*') {
      return 1;
    } else if (p1[i] !== '*' && p2[i] === '*') {
      return -1;
    }

    // Empty part has lower weight
    if (p1[i] === '' && p2[i] !== '') {
      return 1;
    } else if (p1[i] !== '' && p2[i] === '') {
      return -1;
    }

    // Wildcard has lower weight
    if (p1[i][0] === ':' && p2[i][0] !== ':') {
      return 1;
    } else if (p1[i][0] !== ':' && p2[i][0] === ':') {
      return -1;
    }

    // Now the regular string comparision
    if (p1[i] > p2[i]) {
      return 1;
    } else if (p1[i] < p2[i]) {
      return -1;
    }
  }

  // Both paths have the common parts. The longer one should come before the
  // shorter one
  return p2.length - p1.length;
};
<commit_msg>fix typos<commit_after>'use strict';

const logError = require('./logError');

/*!
 * Compare two routes
 * @param {Object} r1 The first route { verb: 'get', path: '/:id' }
 * @param {Object} r2 The second route route: { verb: 'get', path: '/findOne' }
 * @returns {number} 1: r1 comes after r2, -1: r1 comes before r2, 0: equal
 */
module.exports = function sortRoute(r1, r2) {
  // Normalize the verbs
  let verb1 = r1.verb.toLowerCase();
  let verb2 = r2.verb.toLowerCase();

  if (r1.name === r2.name) {
    logError(`Conflict method '${r1.name}' detected`);
  }

  if (verb1 === verb2 && r1.path === r2.path && verb1 !== 'use') {
    logError(`Conflict route path and verb detected for method '${r2.name}' and method '${r1.name}'`);
  }

  if (verb1 === 'use' && verb2 === 'use') {
    return 0;
  } else if (verb1 === 'use' && verb2 !== 'use') {
    return -1;
  } else if (verb1 !== 'use' && verb2 === 'use') {
    return 1;
  }

  // First sort by verb
  if (verb1 > verb2) {
    return -1;
  } else if (verb1 < verb2) {
    return 1;
  }

  // Sort by path part by part using the / delimiter
  // For example '/:id' will become ['', ':id'], '/findOne' will become
  // ['', 'findOne']
  let p1 = r1.path.split('/');
  let p2 = r2.path.split('/');
  let len = Math.min(p1.length, p2.length);

  // Loop through the parts and decide which path should come first
  for (let i = 0; i < len; i++) {
    // Asterisk has lower weight
    if (p1[i] === '*' && p2[i] !== '*') {
      return 1;
    } else if (p1[i] !== '*' && p2[i] === '*') {
      return -1;
    }

    // Empty part has lower weight
    if (p1[i] === '' && p2[i] !== '') {
      return 1;
    } else if (p1[i] !== '' && p2[i] === '') {
      return -1;
    }

    // Wildcard has lower weight
    if (p1[i][0] === ':' && p2[i][0] !== ':') {
      return 1;
    } else if (p1[i][0] !== ':' && p2[i][0] === ':') {
      return -1;
    }

    // Now the regular string comparision
    if (p1[i] > p2[i]) {
      return 1;
    } else if (p1[i] < p2[i]) {
      return -1;
    }
  }

  // Both paths have the common parts. The longer one should come before the
  // shorter one
  return p2.length - p1.length;
};
<|endoftext|>


<commit_before>var mixin = require('utils-merge');
var q = require('q');

var node = module.exports = {};

node.init = function (prev) {
  this.prev = prev;
  this.next = null;
  this.attr = null;
  this.message = 'invalid';
  this.validationArgs = [];
  this.negative = false;

  if(typeof prev == 'undefined') {
    this.first = this;
  } else {
    this.first = prev.first;
  }
}

node.that = function(arg) {
  if (typeof arg == 'string') {
    this.attr = arg;
  } else if(typeof arg == 'function') {
    this.validation = arg;
  }
  return this;
}

node.is = function() {
  if(arguments.length == 0){
    throw("It's need to define least one argument to is()")
  } else {
    this.validation = arguments[0];
    for(var i = 1; i< arguments.length; i++) {
      this.validationArgs.push(arguments[i])
    }
  }
  return this;
}

node.isNot = function(){
  this.negative = true;
  this.is.apply(this, arguments);
  return this;
}

node.orSay = function(message) {
  this.message = message;
  return this;
}

node.and = function(){
  this.next = node.run;

  mixin(this.next, node);
  this.next.init(this);

  return this.next;
}

node.end = function() {
  return this.first;
}

node.computedValidationArgs = function(obj) {
  var args = [];
  if (this.attr) {
    args.push(obj[this.attr]);
  }
  if(this.validationArgs) {
    args = args.concat(this.validationArgs);
  }
  return args;
}

// node.validate = function(obj) {
//   applyArgs = typeof this.validation.validate == 'function' ? [obj[this.attr]] : this.computedValidationArgs;
//   if (typeof this.validation.validate == 'function') {
//     return this.validation.apply(null, [obj[this.attr]])
//   } else {
//     var deferred = q.defer();
//     var result = this.validation.apply(null, this.computedValidationArgs)
//     var error = null;
//     if (typeof result.then == 'function') {
//       result.then(function(err) {
//         deferred.resolve(err);
//       });
//     } else {
//       if(result && !this.negative) {
//         var error = {};
//         deferred.resolve(error[this.attr] = [this.message]);
//       } else {
//         deferred.resolve(null);
//       }
//     }
//     return deferred.promise;
//   }
// }

node.validate = function(obj) {
  var d = q.defer();
  var result = this.validation.apply(null, this.computedValidationArgs(obj))
  if(result) {
    d.resolve(null); // no error
  } else {
    var error = {}
    error[this.attr] = this.message;
    d.resolve(error);
  }
  return d.promise;
}

node.run = function(obj) {
  var d = new q.defer();
  var current = this.first;
  var error = null;
  var promisses = [];

  while(current) {
    promisses.push(current.validate(obj));
    current = current.next;
  }

  q.all(promisses)
    .then(function(results){
      console.log(results);
      for (var i = 0; i < results.length; i++) {
        var r = results[i];
        if (r) {
          if(error == null) {
            error = {}
          }
          error['attrs'] = error['attrs'] || {}
          error = mixin(error['attrs'], r);
        }
      }
      d.resolve(error);
    })

  return d.promise;
}<commit_msg>Remove useless comment<commit_after>var mixin = require('utils-merge');
var q = require('q');

var node = module.exports = {};

node.init = function (prev) {
  this.prev = prev;
  this.next = null;
  this.attr = null;
  this.message = 'invalid';
  this.validationArgs = [];
  this.negative = false;

  if(typeof prev == 'undefined') {
    this.first = this;
  } else {
    this.first = prev.first;
  }
}

node.that = function(arg) {
  if (typeof arg == 'string') {
    this.attr = arg;
  } else if(typeof arg == 'function') {
    this.validation = arg;
  }
  return this;
}

node.is = function() {
  if(arguments.length == 0){
    throw("It's need to define least one argument to is()")
  } else {
    this.validation = arguments[0];
    for(var i = 1; i< arguments.length; i++) {
      this.validationArgs.push(arguments[i])
    }
  }
  return this;
}

node.isNot = function(){
  this.negative = true;
  this.is.apply(this, arguments);
  return this;
}

node.orSay = function(message) {
  this.message = message;
  return this;
}

node.and = function(){
  this.next = node.run;

  mixin(this.next, node);
  this.next.init(this);

  return this.next;
}

node.end = function() {
  return this.first;
}

node.computedValidationArgs = function(obj) {
  var args = [];
  if (this.attr) {
    args.push(obj[this.attr]);
  }
  if(this.validationArgs) {
    args = args.concat(this.validationArgs);
  }
  return args;
}

node.validate = function(obj) {
  var d = q.defer();
  var result = this.validation.apply(null, this.computedValidationArgs(obj))
  if(result) {
    d.resolve(null); // no error
  } else {
    var error = {}
    error[this.attr] = this.message;
    d.resolve(error);
  }
  return d.promise;
}

node.run = function(obj) {
  var d = new q.defer();
  var current = this.first;
  var error = null;
  var promisses = [];

  while(current) {
    promisses.push(current.validate(obj));
    current = current.next;
  }

  q.all(promisses)
    .then(function(results){
      console.log(results);
      for (var i = 0; i < results.length; i++) {
        var r = results[i];
        if (r) {
          if(error == null) {
            error = {}
          }
          error['attrs'] = error['attrs'] || {}
          error = mixin(error['attrs'], r);
        }
      }
      d.resolve(error);
    })

  return d.promise;
}<|endoftext|>