<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';

export default DS.Adapter.extend(Ember.Evented, {
  defaultSerializer: 'localforage',
  queue : LFQueue.create(),
  cache : LFCache.create(),
  caching : 'model',

  /**
    This is the main entry point into finding records. The first parameter to
    this method is the model's name as a string.

    @method find
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
    */
  find: function(store, type, id, snapshot) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
        var allowRecursive = true;
       adapter._namespaceForType(type).then (function (namespace) {
          /**
             * In the case where there are relationships, this method is called again
             * for each relation. Given the relations have references to the main
             * object, we use allowRecursive to avoid going further into infinite
             * recursiveness.
             *
             * Concept from ember-indexdb-adapter
             */
            if (snapshot && typeof snapshot.allowRecursive !== 'undefined') {
              allowRecursive = snapshot.allowRecursive;
            }

          var record = Ember.A(namespace.records[id]);
          if (!record) {
            reject();
            return;
          }

          if (allowRecursive) {
            adapter.loadRelationships(store, type, record).then(function(finalRecord) {
              resolve(finalRecord);
            });
          } else {
            resolve(record);
          }
        });
    });
  },

  findMany: function (store, type, ids) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
    adapter._namespaceForType(type).then (function (namespace) {
      var results = [];

          for (var i = 0; i < ids.length; i++) {
            results.push(Ember.copy(namespace.records[ids[i]]));
          }

          resolve(results);
    });
    }).then(function(records) {
      if (records.get('length')) {
        return adapter.loadRelationshipsForMany(store, type, records);
      } else {
        return records;
      }
    });
  },

  // Supports queries that look like this:
  //
  //   {
  //     <property to query>: <value or regex (for strings) to match>,
  //     ...
  //   }
  //
  // Every property added to the query is an "AND" query, not "OR"
  //
  // Example:
  //
  //  match records with "complete: true" and the name "foo" or "bar"
  //
  //    { complete: true, name: /foo|bar/ }
  findQuery: function (store, type, query, recordArray) {
      var adapter = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespace) {
        var results = adapter.query(namespace.records, query);

          if (results.get('length')) {
            results = adapter.loadRelationshipsForMany(store, type, results);
          }

          resolve(results);
     });
    });

  },

  query: function (records, query) {
    var results = [],
        id, record, property, test, push;
    for (id in records) {
      record = records[id];
      for (property in query) {
        test = query[property];
        push = false;
        if (Object.prototype.toString.call(test) === '[object RegExp]') {
          push = test.test(record[property]);
        } else {
          push = record[property] === test;
        }
      }
      if (push) {
        results.push(record);
      }
    }
    return results;
  },

  findAll: function (store, type) {
    var adapter = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespace) {
        var results = [];

        for (var id in namespace.records) {
          results.push(Ember.copy(namespace.records[id]));
        }
        resolve(results);
      });
    });
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord: function (store, type, snapshot) {
    var adapter = this;
    return this.queue.attach(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespaceRecords) {
           var id = snapshot.id;

        delete namespaceRecords.records[id];

        adapter.persistData(type, namespaceRecords).then(function () {
          resolve();
        });
      });
    });
  },

  generateIdForRecord: function () {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  adapterNamespace: function () {
    return this.get('namespace') || 'DS.LFAdapter';
  },

  loadData: function () {
    var adapter = this;
      return new Ember.RSVP.Promise(function(resolve, reject) {
        window.localforage.getItem(adapter.adapterNamespace()).then (function (storage) {
      var resolved = storage ? storage : {};
        resolve(resolved);
        });
      });
  },

  persistData: function(type, data) {
    var adapter = this;
    var modelNamespace = this.modelNamespace(type);
    return new Ember.RSVP.Promise(function(resolve, reject) {
      if (adapter.caching !== 'none') {
        adapter.cache.set(modelNamespace, data);
      }
      adapter.loadData().then(function (localStorageData) {
        localStorageData[modelNamespace] = data;
          var toBePersisted = localStorageData;
          window.localforage.setItem(adapter.adapterNamespace(), toBePersisted).then (function () {
            resolve();
          });
      });
    });

  },

  _namespaceForType: function (type) {
    var namespace = this.modelNamespace(type);
    var adapter = this;
    var cache;
    var promise;

    if (adapter.caching !== 'none') {
      cache = adapter.cache.get(namespace);
    } else {
      cache = null;
    }
    if (cache) {
      promise = new Ember.RSVP.resolve(cache);
    } else {
      promise = new Ember.RSVP.Promise(function(resolve, reject) {
      window.localforage.getItem(adapter.adapterNamespace()).then (function (storage) {
          var ns = storage ? storage[namespace] || {records: {}} : {records: {}};
          if (adapter.caching === 'model') {
            adapter.cache.set(namespace, ns);
          } else if (adapter.caching === 'all') {
            if (storage) {
              adapter.cache.replace(storage);
            }
          }
          resolve(ns);
        });
      });
    }
    return promise;
  },

  modelNamespace: function(type) {
    return type.url || type.modelName;
  },

  /**
   * This takes a record, then analyzes the model relationships and replaces
   * ids with the actual values.
   *
   * Stolen from ember-indexdb-adapter
   *
   * Consider the following JSON is entered:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   * }
   *
   * This will return:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   *
   *   "_embedded": {
   *     "comment": [{
   *       "_id": 1,
   *       "comment_title": "FIRST"
   *     }, {
   *       "_id": 2,
   *       "comment_title": "Rails is unagi"
   *     }]
   *   }
   * }
   *
   * This way, whenever a resource returned, its relationships will be also
   * returned.
   *
   * @method loadRelationships
   * @private
   * @param {DS.Store} store
   * @param {DS.Model} type
   * @param {Object} record
   */
  loadRelationships: function(store, type, record) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
      var resultJSON = {},
          modelName = type.modelName,
          relationshipNames, relationships,
          relationshipPromises = [];

      relationshipNames = Ember.get(type, 'relationshipNames');
      relationships = relationshipNames.belongsTo;
      relationships = relationships.concat(relationshipNames.hasMany);

      relationships.forEach(function(relationName) {
        var relationModel = type.typeForRelationship(relationName),
            relationEmbeddedId = record[relationName],
            relationProp  = adapter.relationshipProperties(type, relationName),
            relationType  = relationProp.kind,
            /**
             * This is the relationship field.
             */
            promise, embedPromise;

        var opts = {allowRecursive: false};

        /**
         * embeddedIds are ids of relations that are included in the main
         * payload, such as:
         *
         * {
         *    cart: {
         *      id: "s85fb",
         *      customer: "rld9u"
         *    }
         * }
         *
         * In this case, cart belongsTo customer and its id is present in the
         * main payload. We find each of these records and add them to _embedded.
         */
        var embeddedAlways = adapter.isEmbeddedAlways(store, type.modelName, relationProp.key);

        // For embeddedAlways-style data, we assume the data to be present already, so no further loading is needed.
        if (relationEmbeddedId && !embeddedAlways) {
          if (relationType === 'belongsTo' || relationType === 'hasOne') {
            promise = adapter.find(store, relationModel, relationEmbeddedId, opts);
          } else if (relationType === 'hasMany') {
            promise = adapter.findMany(store, relationModel, relationEmbeddedId, opts);
          }

          embedPromise = new Ember.RSVP.Promise(function(resolve, reject) {
            promise.then(function(relationRecord) {
              var finalPayload = adapter.addEmbeddedPayload(record, relationName, relationRecord);
              resolve(finalPayload);
            });
          });

          relationshipPromises.push(embedPromise);
        }
      });

      Ember.RSVP.all(relationshipPromises).then(function() {
        resolve(record);
      });
    });
  },

  /**
   * Given the following payload,
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      }
   *   }
   *
   * With `relationshipName` being `customer` and `relationshipRecord`
   *
   *   {id: "2", name: "Rambo"}
   *
   * This method returns the following payload:
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      },
   *      _embedded: {
   *        customer: {
   *          id: "2",
   *          name: "Rambo"
   *        }
   *      }
   *   }
   *
   * which is then treated by the serializer later.
   *
   * @method addEmbeddedPayload
   * @private
   * @param {Object} payload
   * @param {String} relationshipName
   * @param {Object} relationshipRecord
   */
  addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
    var objectHasId = (relationshipRecord && relationshipRecord.id),
        arrayHasIds = (relationshipRecord.length && relationshipRecord.everyBy("id")),
        isValidRelationship = (objectHasId || arrayHasIds);

    if (isValidRelationship) {
      if (!payload._embedded) {
        payload._embedded = {};
      }

      payload._embedded[relationshipName] = relationshipRecord;
      if (relationshipRecord.length) {
        payload[relationshipName] = relationshipRecord.mapBy('id');
      } else {
        payload[relationshipName] = relationshipRecord.id;
      }
    }

    if (this.isArray(payload[relationshipName])) {
      payload[relationshipName] = payload[relationshipName].filter(function(id) {
        return id;
      });
    }

    return payload;
  },

  isArray: function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  },

  /**
   * Same as `loadRelationships`, but for an array of records.
   *
   * @method loadRelationshipsForMany
   * @private
   * @param {DS.Store} store
   * @param {DS.Model} type
   * @param {Object} recordsArray
   */
  loadRelationshipsForMany: function(store, type, recordsArray) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
      var recordsWithRelationships = [],
          recordsToBeLoaded = [],
          promises = [];

      /**
       * Some times Ember puts some stuff in arrays. We want to clean it so
       * we know exactly what to iterate over.
       */
      for (var i in recordsArray) {
        if (recordsArray.hasOwnProperty(i)) {
          recordsToBeLoaded.push(recordsArray[i]);
        }
      }

      var loadNextRecord = function(record) {
        /**
         * Removes the first item from recordsToBeLoaded
         */
        recordsToBeLoaded = recordsToBeLoaded.slice(1);

        var promise = adapter.loadRelationships(store, type, record);

        promise.then(function(recordWithRelationships) {
          recordsWithRelationships.push(recordWithRelationships);

          if (recordsToBeLoaded[0]) {
            loadNextRecord(recordsToBeLoaded[0]);
          } else {
            resolve(recordsWithRelationships);
          }
        });
      };

      /**
       * We start by the first record
       */
      loadNextRecord(recordsToBeLoaded[0]);
    });
  },

  /**
   *
   * @method relationshipProperties
   * @private
   * @param {DS.Model} type
   * @param {String} relationName
   */
  relationshipProperties: function(type, relationName) {
    var relationships = Ember.get(type, 'relationshipsByName');
    if (relationName) {
      return relationships.get(relationName);
    } else {
      return relationships;
    }
  },

  isEmbeddedAlways: function(store, modelName, relationKey) {
    if (store === undefined || store === null) {
      return false;
    }

    var serializer = store.serializerFor(modelName);
    var embeddedAlways = typeof(serializer.hasEmbeddedAlwaysOption) === 'function' &&
      serializer.hasEmbeddedAlwaysOption(relationKey);
    return embeddedAlways;
  }
});

function updateOrCreate(store, type, snapshot) {
  var adapter = this;
  return this.queue.attach(function(resolve, reject) {
    adapter._namespaceForType(type).then (function (namespaceRecords) {
      var serializer = store.serializerFor(type.modelName);
      var recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      var id = snapshot.id || recordHash.id;

      namespaceRecords.records[id] = recordHash;
      adapter.persistData(type, namespaceRecords).then (function () {
        resolve();
      });
    });
  });
}
<commit_msg>fix loadRelatioships and addRelatioships<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';

export default DS.Adapter.extend(Ember.Evented, {
  defaultSerializer: 'localforage',
  queue : LFQueue.create(),
  cache : LFCache.create(),
  caching : 'model',

  /**
    This is the main entry point into finding records. The first parameter to
    this method is the model's name as a string.

    @method find
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
    */
  find: function(store, type, id, snapshot) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
        var allowRecursive = true;
       adapter._namespaceForType(type).then (function (namespace) {
          /**
             * In the case where there are relationships, this method is called again
             * for each relation. Given the relations have references to the main
             * object, we use allowRecursive to avoid going further into infinite
             * recursiveness.
             *
             * Concept from ember-indexdb-adapter
             */
            if (snapshot && typeof snapshot.allowRecursive !== 'undefined') {
              allowRecursive = snapshot.allowRecursive;
            }

          var record = Ember.A(namespace.records[id]);
          if (!record) {
            reject();
            return;
          }

          if (allowRecursive) {
            adapter.loadRelationships(store, type, record).then(function(finalRecord) {
              resolve(finalRecord);
            });
          } else {
            resolve(record);
          }
        });
    });
  },

  findMany: function (store, type, ids) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
    adapter._namespaceForType(type).then (function (namespace) {
      var results = [];

          for (var i = 0; i < ids.length; i++) {
            results.push(Ember.copy(namespace.records[ids[i]]));
          }

          resolve(results);
    });
    }).then(function(records) {
      if (records.get('length')) {
        return adapter.loadRelationshipsForMany(store, type, records);
      } else {
        return records;
      }
    });
  },

  // Supports queries that look like this:
  //
  //   {
  //     <property to query>: <value or regex (for strings) to match>,
  //     ...
  //   }
  //
  // Every property added to the query is an "AND" query, not "OR"
  //
  // Example:
  //
  //  match records with "complete: true" and the name "foo" or "bar"
  //
  //    { complete: true, name: /foo|bar/ }
  findQuery: function (store, type, query, recordArray) {
      var adapter = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespace) {
        var results = adapter.query(namespace.records, query);

          if (results.get('length')) {
            results = adapter.loadRelationshipsForMany(store, type, results);
          }

          resolve(results);
     });
    });

  },

  query: function (records, query) {
    var results = [],
        id, record, property, test, push;
    for (id in records) {
      record = records[id];
      for (property in query) {
        test = query[property];
        push = false;
        if (Object.prototype.toString.call(test) === '[object RegExp]') {
          push = test.test(record[property]);
        } else {
          push = record[property] === test;
        }
      }
      if (push) {
        results.push(record);
      }
    }
    return results;
  },

  findAll: function (store, type) {
    var adapter = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespace) {
        var results = [];

        for (var id in namespace.records) {
          results.push(Ember.copy(namespace.records[id]));
        }
        resolve(results);
      });
    });
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord: function (store, type, snapshot) {
    var adapter = this;
    return this.queue.attach(function(resolve, reject) {
      adapter._namespaceForType(type).then (function (namespaceRecords) {
           var id = snapshot.id;

        delete namespaceRecords.records[id];

        adapter.persistData(type, namespaceRecords).then(function () {
          resolve();
        });
      });
    });
  },

  generateIdForRecord: function () {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  adapterNamespace: function () {
    return this.get('namespace') || 'DS.LFAdapter';
  },

  loadData: function () {
    var adapter = this;
      return new Ember.RSVP.Promise(function(resolve, reject) {
        window.localforage.getItem(adapter.adapterNamespace()).then (function (storage) {
      var resolved = storage ? storage : {};
        resolve(resolved);
        });
      });
  },

  persistData: function(type, data) {
    var adapter = this;
    var modelNamespace = this.modelNamespace(type);
    return new Ember.RSVP.Promise(function(resolve, reject) {
      if (adapter.caching !== 'none') {
        adapter.cache.set(modelNamespace, data);
      }
      adapter.loadData().then(function (localStorageData) {
        localStorageData[modelNamespace] = data;
          var toBePersisted = localStorageData;
          window.localforage.setItem(adapter.adapterNamespace(), toBePersisted).then (function () {
            resolve();
          });
      });
    });

  },

  _namespaceForType: function (type) {
    var namespace = this.modelNamespace(type);
    var adapter = this;
    var cache;
    var promise;

    if (adapter.caching !== 'none') {
      cache = adapter.cache.get(namespace);
    } else {
      cache = null;
    }
    if (cache) {
      promise = new Ember.RSVP.resolve(cache);
    } else {
      promise = new Ember.RSVP.Promise(function(resolve, reject) {
      window.localforage.getItem(adapter.adapterNamespace()).then (function (storage) {
          var ns = storage ? storage[namespace] || {records: {}} : {records: {}};
          if (adapter.caching === 'model') {
            adapter.cache.set(namespace, ns);
          } else if (adapter.caching === 'all') {
            if (storage) {
              adapter.cache.replace(storage);
            }
          }
          resolve(ns);
        });
      });
    }
    return promise;
  },

  modelNamespace: function(type) {
    return type.url || type.modelName;
  },

  /**
   * This takes a record, then analyzes the model relationships and replaces
   * ids with the actual values.
   *
   * Stolen from ember-indexdb-adapter
   *
   * Consider the following JSON is entered:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   * }
   *
   * This will return:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   *
   *   "_embedded": {
   *     "comment": [{
   *       "_id": 1,
   *       "comment_title": "FIRST"
   *     }, {
   *       "_id": 2,
   *       "comment_title": "Rails is unagi"
   *     }]
   *   }
   * }
   *
   * This way, whenever a resource returned, its relationships will be also
   * returned.
   *
   * @method loadRelationships
   * @private
   * @param {DS.Store} store
   * @param {DS.Model} type
   * @param {Object} record
   */
  loadRelationships: function(store, type, record) {
    var adapter = this;
    if (record) {
      return new Ember.RSVP.Promise(function(resolve) {
        var relationshipNames, relationships,
          relationshipPromises = [];

        relationshipNames = Ember.get(type, 'relationshipNames');
        relationships = relationshipNames.belongsTo;
        relationships = relationships.concat(relationshipNames.hasMany);

        relationships.forEach(function(relationName) {
          var relationModel = type.typeForRelationship(relationName),
            relationEmbeddedId = record[relationName],
            relationProp = adapter.relationshipProperties(type, relationName),
            relationType = relationProp.kind,
            /**
             * This is the relationship field.
             */
            promise, embedPromise;

          var opts = {
            allowRecursive: false
          };

          /**
           * embeddedIds are ids of relations that are included in the main
           * payload, such as:
           *
           * {
           *    cart: {
           *      id: "s85fb",
           *      customer: "rld9u"
           *    }
           * }
           *
           * In this case, cart belongsTo customer and its id is present in the
           * main payload. We find each of these records and add them to _embedded.
           */
          var embeddedAlways = adapter.isEmbeddedAlways(store, type.modelName, relationProp.key);

          // For embeddedAlways-style data, we assume the data to be present already, so no further loading is needed.

          if (relationEmbeddedId && !embeddedAlways) {
            if (relationType === 'belongsTo' || relationType === 'hasOne') {
              promise = adapter.find(store, relationModel, relationEmbeddedId, opts, true);
            } else if (relationType === 'hasMany') {
              promise = adapter.findMany(store, relationModel, relationEmbeddedId, opts, true);
            }

            embedPromise = new Ember.RSVP.Promise(function(resolve) {
              promise.then(function(relationRecord) {
                if (relationRecord) {
                  var finalPayload = adapter.addEmbeddedPayload(record, relationName, relationRecord);
                  resolve(finalPayload);

                }

              });
            });

            relationshipPromises.push(embedPromise);
          }
        });

        Ember.RSVP.all(relationshipPromises).then(function() {
          resolve(record);
        });
      });

    }
    return Ember.RSVP.resolve();
  },
  /**
   * Given the following payload,
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      }
   *   }
   *
   * With `relationshipName` being `customer` and `relationshipRecord`
   *
   *   {id: "2", name: "Rambo"}
   *
   * This method returns the following payload:
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      },
   *      _embedded: {
   *        customer: {
   *          id: "2",
   *          name: "Rambo"
   *        }
   *      }
   *   }
   *
   * which is then treated by the serializer later.
   *
   * @method addEmbeddedPayload
   * @private
   * @param {Object} payload
   * @param {String} relationshipName
   * @param {Object} relationshipRecord
   */
  addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
    var objectHasId = (relationshipRecord && relationshipRecord.id),
      arrayHasIds = (relationshipRecord[0] && relationshipRecord.length && relationshipRecord.everyBy("id")),
      isValidRelationship = (objectHasId || arrayHasIds);

    if (isValidRelationship) {
      if (!payload._embedded) {
        payload._embedded = {};
      }

      payload._embedded[relationshipName] = relationshipRecord;
      if (relationshipRecord.length) {
        payload[relationshipName] = relationshipRecord.mapBy('id');
      } else {
        payload[relationshipName] = relationshipRecord.id;
      }
    }

    if (this.isArray(payload[relationshipName])) {
      payload[relationshipName] = payload[relationshipName].filter(function(id) {
        return id;
      });
    }

    return payload;
  },

  isArray: function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  },

  /**
   * Same as `loadRelationships`, but for an array of records.
   *
   * @method loadRelationshipsForMany
   * @private
   * @param {DS.Store} store
   * @param {DS.Model} type
   * @param {Object} recordsArray
   */
  loadRelationshipsForMany: function(store, type, recordsArray) {
    var adapter = this;

    return new Ember.RSVP.Promise(function(resolve, reject) {
      var recordsWithRelationships = [],
          recordsToBeLoaded = [],
          promises = [];

      /**
       * Some times Ember puts some stuff in arrays. We want to clean it so
       * we know exactly what to iterate over.
       */
      for (var i in recordsArray) {
        if (recordsArray.hasOwnProperty(i)) {
          recordsToBeLoaded.push(recordsArray[i]);
        }
      }

      var loadNextRecord = function(record) {
        /**
         * Removes the first item from recordsToBeLoaded
         */
        recordsToBeLoaded = recordsToBeLoaded.slice(1);

        var promise = adapter.loadRelationships(store, type, record);

        promise.then(function(recordWithRelationships) {
          recordsWithRelationships.push(recordWithRelationships);

          if (recordsToBeLoaded[0]) {
            loadNextRecord(recordsToBeLoaded[0]);
          } else {
            resolve(recordsWithRelationships);
          }
        });
      };

      /**
       * We start by the first record
       */
      loadNextRecord(recordsToBeLoaded[0]);
    });
  },

  /**
   *
   * @method relationshipProperties
   * @private
   * @param {DS.Model} type
   * @param {String} relationName
   */
  relationshipProperties: function(type, relationName) {
    var relationships = Ember.get(type, 'relationshipsByName');
    if (relationName) {
      return relationships.get(relationName);
    } else {
      return relationships;
    }
  },

  isEmbeddedAlways: function(store, modelName, relationKey) {
    if (store === undefined || store === null) {
      return false;
    }

    var serializer = store.serializerFor(modelName);
    var embeddedAlways = typeof(serializer.hasEmbeddedAlwaysOption) === 'function' &&
      serializer.hasEmbeddedAlwaysOption(relationKey);
    return embeddedAlways;
  }
});

function updateOrCreate(store, type, snapshot) {
  var adapter = this;
  return this.queue.attach(function(resolve, reject) {
    adapter._namespaceForType(type).then (function (namespaceRecords) {
      var serializer = store.serializerFor(type.modelName);
      var recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      var id = snapshot.id || recordHash.id;

      namespaceRecords.records[id] = recordHash;
      adapter.persistData(type, namespaceRecords).then (function () {
        resolve();
      });
    });
  });
}
<|endoftext|>


<commit_before>!function(){"use strict";var a=angular.module("feedback",[]);a.provider("feedback",function(){this.$get=["$document","$compile","$rootScope","$timeout",function(a,b,c,d){var e,f,g={duration:3e3,type:"info",sticky:!1},h={infoClass:"fdb-info",errorClass:"fdb-error",successClass:"fdb-success",warnClass:"fdb-warn",grimaceClass:"fdb-grimace",neutralClass:"fdb-neutral"},i=c.$new();i.isActive=!1;var j=b('

{{ message }}

')(i);a.find("body").append(j);var k=function(a){var b=(a||g.type)+"Class";return h[b]||h.infoClass},l=function(){i.isActive=!1,i.feedbackClass="",i.message="",i.isLoading=!1,delete i.type,delete i.sticky};i.dismiss=function(){i.feedbackClass+=i.isLoading?" fdb-unloading":" fdb-contract",f=d(function(){l()},500)};var m={config:function(a){a=a||{},angular.extend(g,a)},notify:function(a,b){var c={};if(a){d.cancel(e),d.cancel(f),"object"==typeof b?c=angular.extend({},g,b):(c=angular.extend({},g),c.type=b);var h=k(c.type)+" ";h+=c.sticky?" fdb-sticky":"",h+=" fdb-expand",i.isActive=!0,i.feedbackClass=h,i.isLoading=!1,i.message=a,i.type=c.type,i.sticky=c.sticky,c.sticky||(e=d(function(){i.dismiss()},c.duration))}},load:function(){d.cancel(e),d.cancel(f);var a=k("neutral")+"  fdb-loading";i.isActive=!0,i.feedbackClass=a,i.message="",i.isLoading=!0,delete i.type,delete i.sticky},dismiss:function(){i.dismiss()},isActive:function(){return!!i.isActive},isLoading:function(){return!!i.isLoading},isSticky:function(){return!!i.sticky},getType:function(){return i.type},getMessage:function(){return i.message},addType:function(a,b){a&&b&&(h[a+"Class"]=b)}};return m}]})}();<commit_msg>Build code<commit_after>!function(){"use strict";var a=angular.module("angular-feedback",[]);a.provider("feedback",function(){this.$get=["$document","$compile","$rootScope","$timeout",function(a,b,c,d){var e,f,g={duration:3e3,type:"info",sticky:!1},h={info:"fdb-info",error:"fdb-error",success:"fdb-success",warn:"fdb-warn",grimace:"fdb-grimace",neutral:"fdb-neutral"},i=c.$new();i.isActive=!1;var j=b('

{{ message }}

')(i);a.find("body").append(j);var k=function(){i.isActive=!1,i.feedbackClass="",i.message="",i.isLoading=!1,delete i.type,delete i.sticky};i.dismiss=function(){i.feedbackClass+=i.isLoading?" fdb-unloading":" fdb-contract",f=d(function(){k()},500)};var l={config:function(a){a=a||{},angular.extend(g,a)},notify:function(a,b){var c={};if(a){d.cancel(e),d.cancel(f),"object"==typeof b?c=angular.extend({},g,b):(c=angular.extend({},g),b&&h[b]&&(c.type=b));var j=h[c.type]+" ";j+=c.sticky?" fdb-sticky":"",j+=" fdb-expand",i.isActive=!0,i.feedbackClass=j,i.isLoading=!1,i.message=a,i.type=c.type,i.sticky=c.sticky,c.sticky||(e=d(function(){i.dismiss()},c.duration))}},load:function(){d.cancel(e),d.cancel(f);var a=h.neutral+"  fdb-loading";i.isActive=!0,i.feedbackClass=a,i.message="",i.isLoading=!0,delete i.type,delete i.sticky},dismiss:function(){i.dismiss()},isActive:function(){return!!i.isActive},isLoading:function(){return!!i.isLoading},isSticky:function(){return!!i.sticky},getType:function(){return i.type},getMessage:function(){return i.message},addType:function(a,b){a&&b&&(h[a]=b)}};return l}]})}();<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const _ = require('lodash');
const rootPath = path.normalize(path.join(__dirname, '..'));
const defaultAppPath = path.join(rootPath, 'app');

const CLIENT_SIDE_KEYS = [
  'es',
  'features',
  'googleAnalyticsPropertyID',
  'search',
  'siteSubTitle',
  'siteTitle',
  'sortOptions',
  'translations',
  'types'
];

// Default config parameters goes here
const defaults = {
  allowRobots: true,
  keystone: {
    nav: {
      users: 'users',
      pages: 'pages',
      menus: ['menu-items'],
      galleries: ['galleries', 'gallery-items']
    }
  },
  root: rootPath,
  search: {
    path: 'search'
  },
  siteTitle: 'Collections Online',
  thumbnailSize: 350,
  types: {
    asset: {
      router: 'collections-online/lib/routers/asset'
    }
  }
};

function override(config) {
  const path = require('path');
  const defaultAppPath = path.normalize(path.join(__dirname, '..', 'app'));
  config.appPaths = _.concat(config.appPaths, defaultAppPath);
  if(config.features.keystone && config.siteTitle) {
    config.keystone.options.name = config.siteTitle;
    config.keystone.options.brand = config.siteTitle;
  }
}

var config = {};

config.setChildPath = (childPath) => {
  config.childPath = childPath;
  config.reload();
};

config.getClientSideConfig = () => {
  // Copy over the values that are not confirdential
  let result = {};
  CLIENT_SIDE_KEYS.forEach(key => {
    result[key] = config[key];
  });
  return result;
};

config.setClientSideConfig = (clientSideConfig) => {
  config = _.merge(config, clientSideConfig);
};

config.reload = () => {
  const fs = require('fs');
  const path = require('path');
  // Check pre-conditions
  if(!config.childPath) {
    throw new Error('Call setChildPath before re-loading the configuration');
  }
  let childConfigPath = path.join(config.childPath, 'config.js');
  if(!fs.existsSync(childConfigPath)) {
    throw new Error('Expected a child configuration here: ', childConfigPath);
  }
  // Invalidate the require cache (if any)
  Object.keys(require.cache).forEach(p => {
    if(p.indexOf(path.join(config.childPath, 'config')) !== -1) {
      delete require.cache[p];
    }
  });
  // Require the child configuration
  let childConfig = require(childConfigPath);
  config = _.merge(config, defaults, childConfig);
  // Override anything
  override(config);
};

module.exports = config;
<commit_msg>Merging mutates<commit_after>'use strict';

const path = require('path');
const _ = require('lodash');
const rootPath = path.normalize(path.join(__dirname, '..'));
const defaultAppPath = path.join(rootPath, 'app');

const CLIENT_SIDE_KEYS = [
  'es',
  'features',
  'googleAnalyticsPropertyID',
  'search',
  'siteSubTitle',
  'siteTitle',
  'sortOptions',
  'translations',
  'types'
];

// Default config parameters goes here
const defaults = {
  allowRobots: true,
  keystone: {
    nav: {
      users: 'users',
      pages: 'pages',
      menus: ['menu-items'],
      galleries: ['galleries', 'gallery-items']
    }
  },
  root: rootPath,
  search: {
    path: 'search'
  },
  siteTitle: 'Collections Online',
  thumbnailSize: 350,
  types: {
    asset: {
      router: 'collections-online/lib/routers/asset'
    }
  }
};

function override(config) {
  const path = require('path');
  const defaultAppPath = path.normalize(path.join(__dirname, '..', 'app'));
  config.appPaths = _.concat(config.appPaths, defaultAppPath);
  if(config.features.keystone && config.siteTitle) {
    config.keystone.options.name = config.siteTitle;
    config.keystone.options.brand = config.siteTitle;
  }
}

var config = {};

config.setChildPath = (childPath) => {
  config.childPath = childPath;
  config.reload();
};

config.getClientSideConfig = () => {
  // Copy over the values that are not confirdential
  let result = {};
  CLIENT_SIDE_KEYS.forEach(key => {
    result[key] = config[key];
  });
  return result;
};

config.setClientSideConfig = (clientSideConfig) => {
  // Merge in the client side config
  _.merge(config, clientSideConfig);
};

config.reload = () => {
  const fs = require('fs');
  const path = require('path');
  // Check pre-conditions
  if(!config.childPath) {
    throw new Error('Call setChildPath before re-loading the configuration');
  }
  let childConfigPath = path.join(config.childPath, 'config.js');
  if(!fs.existsSync(childConfigPath)) {
    throw new Error('Expected a child configuration here: ', childConfigPath);
  }
  // Invalidate the require cache (if any)
  Object.keys(require.cache).forEach(p => {
    if(p.indexOf(path.join(config.childPath, 'config')) !== -1) {
      delete require.cache[p];
    }
  });
  // Require the child configuration
  let childConfig = require(childConfigPath);
  // Merge the existing config with defaults and child config
  _.merge(config, defaults, childConfig);
  // Override anything
  override(config);
};

module.exports = config;
<|endoftext|>


<commit_before>var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
	_hasAuth: false,

	writing: {
		root: function() {
			this.fs.copyTpl(
				this.templatePath('gitignore'),
				this.destinationPath('.gitignore')
			);

			this.fs.copyTpl(
				this.templatePath('karma.conf.js'),
				this.destinationPath('karma.conf.js')
			);
		},

		assets: function() {
			this.fs.copyTpl(
				this.templatePath('assets/applications/web/app.js'),
				this.destinationPath('assets/applications/web/app.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/app.js'),
				this.destinationPath('assets/applications/mobile/app.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/constants.web.js'),
				this.destinationPath('assets/constants.web.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/constants.mobile.js'),
				this.destinationPath('assets/constants.mobile.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/index.html'),
				this.destinationPath('assets/index.html')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/web/index.html'),
				this.destinationPath('assets/applications/web/index.html')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/index.html'),
				this.destinationPath('assets/applications/mobile/index.html')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/layout.web.sass'),
				this.destinationPath('assets/applications/web/layout.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/layout.mobile.sass'),
				this.destinationPath('assets/applications/mobile/layout.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/mixins.web.sass'),
				this.destinationPath('assets/applications/web/mixins.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/mixins.mobile.sass'),
				this.destinationPath('assets/applications/mobile/mixins.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/routes.js'),
				this.destinationPath('assets/applications/web/routes.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/routes.js'),
				this.destinationPath('assets/applications/mobile/routes.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/style.web.sass'),
				this.destinationPath('assets/applications/web/style.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/style.mobile.sass'),
				this.destinationPath('assets/applications/mobile/style.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/variables.web.sass'),
				this.destinationPath('assets/applications/web/variables.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/variables.mobile.sass'),
				this.destinationPath('assets/applications/mobile/variables.mobile.sass')
			);
		},

		collections: function() {
			this.mkdir('assets/collections');
		},
		constants: function() {
			this.mkdir('assets/constants');
		},

		gulp: function() {
			this.fs.copyTpl(
		      this.templatePath('gulpfile.js'),
		      this.destinationPath('gulpfile.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/build.task.js'),
		      this.destinationPath('gulp_tasks/build.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/clean.task.js'),
		      this.destinationPath('gulp_tasks/clean.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/config.js'),
		      this.destinationPath('gulp_tasks/config.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/css.task.js'),
		      this.destinationPath('gulp_tasks/css.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/fonts.task.js'),
		      this.destinationPath('gulp_tasks/fonts.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/html.task.js'),
		      this.destinationPath('gulp_tasks/html.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/images.task.js'),
		      this.destinationPath('gulp_tasks/images.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/javascript.task.js'),
		      this.destinationPath('gulp_tasks/javascript.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/push.task.js'),
		      this.destinationPath('gulp_tasks/push.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/secrets.js'),
		      this.destinationPath('gulp_tasks/secrets.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/serve.task.js'),
		      this.destinationPath('gulp_tasks/serve.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/spec.task.js'),
		      this.destinationPath('gulp_tasks/spec.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/vendor.task.js'),
		      this.destinationPath('gulp_tasks/vendor.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/watch.task.js'),
		      this.destinationPath('gulp_tasks/watch.task.js')
		    );
		},
		fonts: function() {
			this.mkdir('assets/static/fonts');
		},
		images: function() {
			this.mkdir('assets/static/images');
		},
		models: function() {
			this.mkdir('assets/models');
		},
		pages: function() {
			this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.controller.js'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.js')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.controller.js'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.js')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.style.sass'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.sass')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.style.sass'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.sass')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.template.html'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.html')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.template.html'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.html')
	    );
		},
	},

	install: {
		npm: function() {
			this.npmInstall([
				"aws-sdk",
				"del",
				"gulp",
				"gulp-awspublish",
				"gulp-concat",
				"gulp-connect",
				"gulp-flatten",
				"gulp-jasmine",
				"gulp-jshint",
				"gulp-livereload",
				"gulp-load-plugins",
				"gulp-minify-css",
				"gulp-minify-html",
				"gulp-ng-annotate",
				"gulp-plumber",
				"gulp-rename",
				"gulp-sass",
				"gulp-sourcemaps",
				"gulp-uglify",
				"jasmine-core",
				"jshint-stylish",
				"karma",
				"karma-jasmine",
				"karma-phantomjs-launcher",
				"mime",
				"path",
				"phantomjs",
				"progress",
				"run-sequence",
				"through2"
			], { 'saveDev': true });
		},
		bower: function() {
			this.bowerInstall([
				"angular-route",
				"underscore",
				"angular",
				"angular-mocks",
				"angular-resource",
				"angular-messages",
				"angular-websocket"
			], { 'save': true });
		}
	}
});
<commit_msg>third time is a charm<commit_after>var generators = require('yeoman-generator');

module.exports = generators.Base.extend({
	_hasAuth: false,

	writing: {
		root: function() {
			this.fs.copyTpl(
				this.templatePath('gitignore'),
				this.destinationPath('.gitignore')
			);

			this.fs.copyTpl(
				this.templatePath('karma.conf.js'),
				this.destinationPath('karma.conf.js')
			);
		},

		assets: function() {
			this.fs.copyTpl(
				this.templatePath('assets/applications/web/app.js'),
				this.destinationPath('assets/applications/web/app.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/app.js'),
				this.destinationPath('assets/applications/mobile/app.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/constants/constants.web.js'),
				this.destinationPath('assets/constants/constants.web.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/constants/constants.mobile.js'),
				this.destinationPath('assets/constants/constants.mobile.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/index.html'),
				this.destinationPath('assets/index.html')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/web/index.html'),
				this.destinationPath('assets/applications/web/index.html')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/index.html'),
				this.destinationPath('assets/applications/mobile/index.html')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/layout.web.sass'),
				this.destinationPath('assets/applications/web/layout.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/layout.mobile.sass'),
				this.destinationPath('assets/applications/mobile/layout.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/mixins.web.sass'),
				this.destinationPath('assets/applications/web/mixins.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/mixins.mobile.sass'),
				this.destinationPath('assets/applications/mobile/mixins.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/routes.js'),
				this.destinationPath('assets/applications/web/routes.js')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/routes.js'),
				this.destinationPath('assets/applications/mobile/routes.js')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/style.web.sass'),
				this.destinationPath('assets/applications/web/style.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/style.mobile.sass'),
				this.destinationPath('assets/applications/mobile/style.mobile.sass')
			);

			this.fs.copyTpl(
				this.templatePath('assets/applications/web/variables.web.sass'),
				this.destinationPath('assets/applications/web/variables.web.sass')
			);
			this.fs.copyTpl(
				this.templatePath('assets/applications/mobile/variables.mobile.sass'),
				this.destinationPath('assets/applications/mobile/variables.mobile.sass')
			);
		},

		collections: function() {
			this.mkdir('assets/collections');
		},
		constants: function() {
			this.mkdir('assets/constants');
		},

		gulp: function() {
			this.fs.copyTpl(
		      this.templatePath('gulpfile.js'),
		      this.destinationPath('gulpfile.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/build.task.js'),
		      this.destinationPath('gulp_tasks/build.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/clean.task.js'),
		      this.destinationPath('gulp_tasks/clean.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/config.js'),
		      this.destinationPath('gulp_tasks/config.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/css.task.js'),
		      this.destinationPath('gulp_tasks/css.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/fonts.task.js'),
		      this.destinationPath('gulp_tasks/fonts.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/html.task.js'),
		      this.destinationPath('gulp_tasks/html.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/images.task.js'),
		      this.destinationPath('gulp_tasks/images.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/javascript.task.js'),
		      this.destinationPath('gulp_tasks/javascript.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/push.task.js'),
		      this.destinationPath('gulp_tasks/push.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/secrets.js'),
		      this.destinationPath('gulp_tasks/secrets.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/serve.task.js'),
		      this.destinationPath('gulp_tasks/serve.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/spec.task.js'),
		      this.destinationPath('gulp_tasks/spec.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/vendor.task.js'),
		      this.destinationPath('gulp_tasks/vendor.task.js')
		    );

		    this.fs.copyTpl(
		      this.templatePath('gulp_tasks/watch.task.js'),
		      this.destinationPath('gulp_tasks/watch.task.js')
		    );
		},
		fonts: function() {
			this.mkdir('assets/static/fonts');
		},
		images: function() {
			this.mkdir('assets/static/images');
		},
		models: function() {
			this.mkdir('assets/models');
		},
		pages: function() {
			this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.controller.js'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.js')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.controller.js'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.js')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.style.sass'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.sass')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.style.sass'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.sass')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.template.html'),
	      this.destinationPath('assets/components/dashboard/dashboard.web.html')
	    );
	    this.fs.copyTpl(
	      this.templatePath('assets/components/dashboard/dashboard.template.html'),
	      this.destinationPath('assets/components/dashboard/dashboard.mobile.html')
	    );
		},
	},

	install: {
		npm: function() {
			this.npmInstall([
				"aws-sdk",
				"del",
				"gulp",
				"gulp-awspublish",
				"gulp-concat",
				"gulp-connect",
				"gulp-flatten",
				"gulp-jasmine",
				"gulp-jshint",
				"gulp-livereload",
				"gulp-load-plugins",
				"gulp-minify-css",
				"gulp-minify-html",
				"gulp-ng-annotate",
				"gulp-plumber",
				"gulp-rename",
				"gulp-sass",
				"gulp-sourcemaps",
				"gulp-uglify",
				"jasmine-core",
				"jshint-stylish",
				"karma",
				"karma-jasmine",
				"karma-phantomjs-launcher",
				"mime",
				"path",
				"phantomjs",
				"progress",
				"run-sequence",
				"through2"
			], { 'saveDev': true });
		},
		bower: function() {
			this.bowerInstall([
				"angular-route",
				"underscore",
				"angular",
				"angular-mocks",
				"angular-resource",
				"angular-messages",
				"angular-websocket"
			], { 'save': true });
		}
	}
});
<|endoftext|>


<commit_before><commit_msg>#21 on network switch: rather then reload just set the new page address directly<commit_after><|endoftext|>


<commit_before>// Create our grid module namespace
var grid = angular.module('game.grid', []);

// Returns a tile object
grid.factory('TileModel', function() {
  var Tile = function(pos, id, val) {
    this.x = pos.x;
    this.y = pos.y;
    this.img = this.getImage(id);
    this.val = val || 2;
    this.orientation = 0;
    this.meeples = null;
  }
  Tile.prototype.getImage = function(id) {
    // Grab an image from our asset folder to set as img attribute
    return 'assets/img/Tiles/' + id.toUpperCase() + '.png'
  };
  return Tile;
});

grid.service('GridService', function(TileModel) {

  this.createEmptyGameBoard = function() {
    var grid = [];
    var size = 72;
    for (var x = 0; x < size; x++) {
      grid[x] = [];
      for (var y = 0; y < size; y++) {
        grid[x][y] = null;
      }
    }
    return grid;
  };

});

grid.service('DeckService', function(TileModel) {
  this.deckSpec = {
    a: 2,
    b: 5,
    c: 1,
    d: 4,
    e: 5,
    f: 2,
    g: 1,
    h: 3,
    i: 2,
    j: 3,
    k: 3,
    l: 3,
    m: 2,
    n: 3,
    o: 2,
    p: 3,
    q: 1,
    r: 3,
    s: 2,
    t: 1,
    u: 8,
    v: 9,
    w: 4,
    x: 1
  };

  this.createNewDeck = function() {
    var deck = [];

    for (key in this.deckSpec) {
      for (var i = 0; i < this.deckSpec[key]; i++) {
        deck.push(key);
      }
    }

    return deck;
  };

  this.shuffle = function(deck) {
    if (deck.length === 0) {
      console.log('you dun goofed, your deck is empty');
    }
    var currentIndex = deck.length, temporaryValue, randomIndex;
    while (0 !== currentIndex){
      randomIndex = Math.floor( Math.random () * currentIndex );
      currentIndex -= 1;
      temporaryValue = deck[currentIndex];
      deck[currentIndex] = deck[randomIndex];
      deck[randomIndex] = temporaryValue;
    }
  };

});

// Create our grid controller
grid.controller('gridCtrl', function($scope, TileModel, GridService, DeckService){
  // A function that returns an array of length n
  $scope.orientation = 0;

  $scope.range = function(n) {
    return new Array(n);
  };
  $scope.rotate = function() {
    $scope.orientation = ($scope.orientation + 1) % 4;
  };
  $scope.init = function() {
    // Create board, deck, and then shuffle deck
    $scope.grid = GridService.createEmptyGameBoard();
    $scope.deck = DeckService.createNewDeck();
    DeckService.shuffle($scope.deck);

    socket.on('nextTurn', function(gamestate) {
      updateGrid(gamestate.lastTile.x, gamestate.lastTile.y, gamestate.lastTile);
<<<<<<< HEAD
      setCell(gamestate.lastTile);
=======
    });
  };

  $scope.clickCell = function(event, x, y) {
    if (!cellAlreadyExists(x, y)) {
      // Draw from deck
      var draw = getRandomTile();
      // Create our tile model
      var tile = new TileModel({x:x, y:y}, draw);
      tile.orientation = $scope.orientation;
      $scope.src = tile.img;
      // We push a new tile onto the grid at xy
      updateGrid(x, y, tile);
      // We set the cell's tile background
      setCell(event.target, tile);

      // emit endturn
      socket.emit('endTurn', tile);
    }
  };

  // These functions should be moved into a factory/service
  var updateGrid = function(x, y, tile) {
    $scope.grid[y][x] = tile;
    console.log($scope.grid[y]);
  };

  var cellAlreadyExists = function(x, y) {
    return $scope.grid[y][x] !== null;
  };

  var setCell = function(tile) {
<<<<<<< HEAD
    var id = '#' + 'x-' + tile.x + '-y-' + tile.y;
    var domElement = angular.element(document.querySelector(id));
=======
    domElement.css('background-size', 'contain');
    domElement.css('background-image', 'url(' + tile.img + ')');
    domElement.css('transform', 'rotate(' + tile.orientation*90 + 'deg)');
  };

  // var setCell = function(target, tile) {
  //   angular.element(target).css('background-size', 'contain');
  //   angular.element(target).css('background-image', 'url(' + tile.img + ')');
  //   angular.element(target).css('transform', 'rotate(' + tile.orientation*90 + 'deg)');
  // };

  var getRandomTile = function() {
    var id = $scope.deck.pop();
    return id;
  };

  // Initialize game board
  $scope.init();
});
<commit_msg>more rebase stuff<commit_after>// Create our grid module namespace
var grid = angular.module('game.grid', []);

// Returns a tile object
grid.factory('TileModel', function() {
  var Tile = function(pos, id, val) {
    this.x = pos.x;
    this.y = pos.y;
    this.img = this.getImage(id);
    this.val = val || 2;
    this.orientation = 0;
    this.meeples = null;
  }
  Tile.prototype.getImage = function(id) {
    // Grab an image from our asset folder to set as img attribute
    return 'assets/img/Tiles/' + id.toUpperCase() + '.png'
  };
  return Tile;
});

grid.service('GridService', function(TileModel) {

  this.createEmptyGameBoard = function() {
    var grid = [];
    var size = 72;
    for (var x = 0; x < size; x++) {
      grid[x] = [];
      for (var y = 0; y < size; y++) {
        grid[x][y] = null;
      }
    }
    return grid;
  };

});

grid.service('DeckService', function(TileModel) {
  this.deckSpec = {
    a: 2,
    b: 5,
    c: 1,
    d: 4,
    e: 5,
    f: 2,
    g: 1,
    h: 3,
    i: 2,
    j: 3,
    k: 3,
    l: 3,
    m: 2,
    n: 3,
    o: 2,
    p: 3,
    q: 1,
    r: 3,
    s: 2,
    t: 1,
    u: 8,
    v: 9,
    w: 4,
    x: 1
  };

  this.createNewDeck = function() {
    var deck = [];

    for (key in this.deckSpec) {
      for (var i = 0; i < this.deckSpec[key]; i++) {
        deck.push(key);
      }
    }

    return deck;
  };

  this.shuffle = function(deck) {
    if (deck.length === 0) {
      console.log('you dun goofed, your deck is empty');
    }
    var currentIndex = deck.length, temporaryValue, randomIndex;
    while (0 !== currentIndex){
      randomIndex = Math.floor( Math.random () * currentIndex );
      currentIndex -= 1;
      temporaryValue = deck[currentIndex];
      deck[currentIndex] = deck[randomIndex];
      deck[randomIndex] = temporaryValue;
    }
  };

});

// Create our grid controller
grid.controller('gridCtrl', function($scope, TileModel, GridService, DeckService){
  // A function that returns an array of length n
  $scope.orientation = 0;

  $scope.range = function(n) {
    return new Array(n);
  };
  $scope.rotate = function() {
    $scope.orientation = ($scope.orientation + 1) % 4;
  };
  $scope.init = function() {
    // Create board, deck, and then shuffle deck
    $scope.grid = GridService.createEmptyGameBoard();
    $scope.deck = DeckService.createNewDeck();
    DeckService.shuffle($scope.deck);

    socket.on('nextTurn', function(gamestate) {
      updateGrid(gamestate.lastTile.x, gamestate.lastTile.y, gamestate.lastTile);
      setCell(gamestate.lastTile);

    });
  };

  $scope.clickCell = function(event, x, y) {
    if (!cellAlreadyExists(x, y)) {
      // Draw from deck
      var draw = getRandomTile();
      // Create our tile model
      var tile = new TileModel({x:x, y:y}, draw);
      tile.orientation = $scope.orientation;
      $scope.src = tile.img;
      // We push a new tile onto the grid at xy
      updateGrid(x, y, tile);
      // We set the cell's tile background
      setCell(event.target, tile);

      // emit endturn
      socket.emit('endTurn', tile);
    }
  };

  // These functions should be moved into a factory/service
  var updateGrid = function(x, y, tile) {
    $scope.grid[y][x] = tile;
    console.log($scope.grid[y]);
  };

  var cellAlreadyExists = function(x, y) {
    return $scope.grid[y][x] !== null;
  };

  var setCell = function(tile) {
    var id = '#' + 'x-' + tile.x + '-y-' + tile.y;
    var domElement = angular.element(document.querySelector(id));
    domElement.css('background-size', 'contain');
    domElement.css('background-image', 'url(' + tile.img + ')');
    domElement.css('transform', 'rotate(' + tile.orientation*90 + 'deg)');
  };

  // var setCell = function(target, tile) {
  //   angular.element(target).css('background-size', 'contain');
  //   angular.element(target).css('background-image', 'url(' + tile.img + ')');
  //   angular.element(target).css('transform', 'rotate(' + tile.orientation*90 + 'deg)');
  // };

  var getRandomTile = function() {
    var id = $scope.deck.pop();
    return id;
  };

  // Initialize game board
  $scope.init();
});
<|endoftext|>


<commit_before>// @flow
import queryString from "query-string";
import type { Config } from "../Domain/Config";
import type { EventList } from "../Domain/Event";
import type { Trigger, TriggerList, TriggerState } from "../Domain/Trigger";
import type { Settings } from "../Domain/Settings";
import type { TagStat } from "../Domain/Tag";
import type { PatternList } from "../Domain/Pattern";
import type { NotificationList } from "../Domain/Notification";
import type { Contact, ContactList } from "../Domain/Contact";
import type { ContactCreateInfo } from "../Domain/ContactCreateInfo";
import type { Subscription } from "../Domain/Subscription";
import type { Schedule } from "../Domain/Schedule";

export type SubscriptionCreateInfo = {|
    sched: Schedule,
    tags: Array<string>,
    throttling: boolean,
    contacts: Array<string>,
    enabled: boolean,
    user: string,
|};

export type TagList = {|
    list: Array<string>,
|};

export type TagStatList = {|
    list: Array<TagStat>,
|};

export interface IMoiraApi {
    getSettings(): Promise<Settings>;
    getConfig(): Promise<Config>;
    getContactList(): Promise<ContactList>;
    addContact(contact: ContactCreateInfo): Promise<Contact>;
    updateContact(contact: Contact): Promise<Contact>;
    testContact(contactId: string): Promise<void>;
    addSubscription(subscription: SubscriptionCreateInfo): Promise<Subscription>;
    updateSubscription(subscription: Subscription): Promise<Subscription>;
    deleteSubscription(subscriptionId: string): Promise<void>;
    testSubscription(subscriptionId: string): Promise<void>;
    deleteContact(contactId: string): Promise<void>;
    getPatternList(): Promise<PatternList>;
    delPattern(pattern: string): Promise<void>;
    getTagList(): Promise<TagList>;
    getTagStats(): Promise<TagStatList>;
    delTag(tag: string): Promise<void>;
    getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList>;
    getTrigger(id: string): Promise<Trigger>;
    addTrigger(data: $Shape<Trigger>): Promise<{ [key: string]: string }>;
    setTrigger(id: string, data: $Shape<Trigger>): Promise<{ [key: string]: string }>;
    delTrigger(id: string): Promise<void>;
    setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void>;
    getTriggerState(id: string): Promise<TriggerState>;
    getTriggerEvents(id: string, page: number): Promise<EventList>;
    delThrottling(triggerId: string): Promise<void>;
    delMetric(triggerId: string, metric: string): Promise<void>;
    getNotificationList(): Promise<NotificationList>;
    delNotification(id: string): Promise<void>;
    delAllNotifications(): Promise<void>;
    delAllNotificationEvents(): Promise<void>;
    delSubscription(id: string): Promise<void>;
}

export default class MoiraApi implements IMoiraApi {
    apiUrl: string;
    config: Config;
    triggerListPageSize: number = 20;
    eventHistoryPageSize: number = 100;

    constructor(apiUrl: string) {
        this.apiUrl = apiUrl;
    }

    async checkStatus(response: Response): Promise<void> {
        if (!(response.status >= 200 && response.status < 300)) {
            const errorText = await response.text();
            let serverResponse;
            try {
                serverResponse = JSON.parse(errorText);
            } catch (error) {
                serverResponse = null;
            }
            if (serverResponse != null) {
                throw new Error(serverResponse.error);
            }
            throw new Error(errorText);
        }
    }

    async get<T>(url: string): Promise<T> {
        const fullUrl = this.apiUrl + url;
        const response = await fetch(fullUrl, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    getConfig(): Promise<Config> {
        return this.get("/config");
    }

    async getSettings(): Promise<Settings> {
        const result = await this.get("/user/settings");
        result.subscriptions = (result.subscriptions || []).map(x => ({
            ...x,
            sendNotificationsOnTriggerDegradedOnly:
                "sendNotificationsOnTriggerDegradedOnly" in x
                    ? x.sendNotificationsOnTriggerDegradedOnly
                    : (x.tags || []).includes("DEGRADATION"),
            doNotSendWarnNotifications:
                "doNotSendWarnNotifications" in x ? x.doNotSendWarnNotifications : (x.tags || []).includes("ERROR"),
        }));
        return result;
    }

    async getContactList(): Promise<ContactList> {
        const url = this.apiUrl + "/contact";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addContact(contact: ContactCreateInfo): Promise<Contact> {
        const url = this.apiUrl + "/contact";
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(contact),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async testContact(contactId: string): Promise<void> {
        const url = this.apiUrl + "/contact/" + contactId + "/test";
        const response = await fetch(url, {
            method: "POST",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async updateContact(contact: Contact): Promise<Contact> {
        const url = this.apiUrl + "/contact/" + contact.id;
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(contact),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addSubscription(subscription: SubscriptionCreateInfo): Promise<Subscription> {
        const url = this.apiUrl + "/subscription";
        if (subscription.id != null) {
            throw new Error("InvalidProgramState: id of subscription must be null or undefined");
        }
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(subscription),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async updateSubscription(subscription: Subscription): Promise<Subscription> {
        const url = this.apiUrl + "/subscription/" + subscription.id;
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(subscription),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async deleteSubscription(subscriptionId: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + subscriptionId;
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async testSubscription(subscriptionId: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + subscriptionId + "/test";
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async deleteContact(contactId: string): Promise<void> {
        const url = this.apiUrl + "/contact/" + contactId;
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getPatternList(): Promise<PatternList> {
        const url = this.apiUrl + "/pattern";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delPattern(pattern: string): Promise<void> {
        const url = this.apiUrl + "/pattern/" + encodeURI(pattern);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTagList(): Promise<TagList> {
        const url = this.apiUrl + "/tag";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTagStats(): Promise<TagStatList> {
        const url = this.apiUrl + "/tag/stats";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delTag(tag: string): Promise<void> {
        const url = this.apiUrl + "/tag/" + encodeURI(tag);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList> {
        const url =
            this.apiUrl +
            "/trigger/page?" +
            queryString.stringify(
                {
                    /* eslint-disable */
                    p: page,
                    /* eslint-enable */
                    size: this.triggerListPageSize,
                    tags,
                    onlyProblems,
                },
                { arrayFormat: "index", encode: true }
            );
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTrigger(id: string): Promise<Trigger> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addTrigger(data: $Shape<Trigger>): Promise<{ [key: string]: string }> {
        const url = this.apiUrl + "/trigger";
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async setTrigger(id: string, data: $Shape<Trigger>): Promise<{ [key: string]: string }> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delTrigger(id: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/maintenance";
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTriggerState(id: string): Promise<TriggerState> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id) + "/state";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTriggerEvents(id: string, page: number): Promise<EventList> {
        const url = this.apiUrl + "/event/" + encodeURI(id) + "?p=" + page + "&size=" + this.eventHistoryPageSize;
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delThrottling(triggerId: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/throttling";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delMetric(triggerId: string, metric: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/metrics?name=" + encodeURI(metric);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getNotificationList(): Promise<NotificationList> {
        const url = this.apiUrl + "/notification?start=0&end=-1";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delNotification(id: string): Promise<void> {
        const url = this.apiUrl + "/notification?id=" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delAllNotifications(): Promise<void> {
        const url = this.apiUrl + "/notification/all";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delAllNotificationEvents(): Promise<void> {
        const url = this.apiUrl + "/event/all";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delSubscription(id: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }
}
<commit_msg>Fixed new subscription type<commit_after>// @flow
import queryString from "query-string";
import type { Config } from "../Domain/Config";
import type { EventList } from "../Domain/Event";
import type { Trigger, TriggerList, TriggerState } from "../Domain/Trigger";
import type { Settings } from "../Domain/Settings";
import type { TagStat } from "../Domain/Tag";
import type { PatternList } from "../Domain/Pattern";
import type { NotificationList } from "../Domain/Notification";
import type { Contact, ContactList } from "../Domain/Contact";
import type { ContactCreateInfo } from "../Domain/ContactCreateInfo";
import type { Subscription } from "../Domain/Subscription";
import type { Schedule } from "../Domain/Schedule";

export type SubscriptionCreateInfo = {|
    sched: Schedule,
    tags: Array<string>,
    throttling: boolean,
    contacts: Array<string>,
    enabled: boolean,
    user: string,
    id?: string,
|};

export type TagList = {|
    list: Array<string>,
|};

export type TagStatList = {|
    list: Array<TagStat>,
|};

export interface IMoiraApi {
    getSettings(): Promise<Settings>;
    getConfig(): Promise<Config>;
    getContactList(): Promise<ContactList>;
    addContact(contact: ContactCreateInfo): Promise<Contact>;
    updateContact(contact: Contact): Promise<Contact>;
    testContact(contactId: string): Promise<void>;
    addSubscription(subscription: SubscriptionCreateInfo): Promise<Subscription>;
    updateSubscription(subscription: Subscription): Promise<Subscription>;
    deleteSubscription(subscriptionId: string): Promise<void>;
    testSubscription(subscriptionId: string): Promise<void>;
    deleteContact(contactId: string): Promise<void>;
    getPatternList(): Promise<PatternList>;
    delPattern(pattern: string): Promise<void>;
    getTagList(): Promise<TagList>;
    getTagStats(): Promise<TagStatList>;
    delTag(tag: string): Promise<void>;
    getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList>;
    getTrigger(id: string): Promise<Trigger>;
    addTrigger(data: $Shape<Trigger>): Promise<{ [key: string]: string }>;
    setTrigger(id: string, data: $Shape<Trigger>): Promise<{ [key: string]: string }>;
    delTrigger(id: string): Promise<void>;
    setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void>;
    getTriggerState(id: string): Promise<TriggerState>;
    getTriggerEvents(id: string, page: number): Promise<EventList>;
    delThrottling(triggerId: string): Promise<void>;
    delMetric(triggerId: string, metric: string): Promise<void>;
    getNotificationList(): Promise<NotificationList>;
    delNotification(id: string): Promise<void>;
    delAllNotifications(): Promise<void>;
    delAllNotificationEvents(): Promise<void>;
    delSubscription(id: string): Promise<void>;
}

export default class MoiraApi implements IMoiraApi {
    apiUrl: string;
    config: Config;
    triggerListPageSize: number = 20;
    eventHistoryPageSize: number = 100;

    constructor(apiUrl: string) {
        this.apiUrl = apiUrl;
    }

    async checkStatus(response: Response): Promise<void> {
        if (!(response.status >= 200 && response.status < 300)) {
            const errorText = await response.text();
            let serverResponse;
            try {
                serverResponse = JSON.parse(errorText);
            } catch (error) {
                serverResponse = null;
            }
            if (serverResponse != null) {
                throw new Error(serverResponse.error);
            }
            throw new Error(errorText);
        }
    }

    async get<T>(url: string): Promise<T> {
        const fullUrl = this.apiUrl + url;
        const response = await fetch(fullUrl, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    getConfig(): Promise<Config> {
        return this.get("/config");
    }

    async getSettings(): Promise<Settings> {
        const result = await this.get("/user/settings");
        result.subscriptions = (result.subscriptions || []).map(x => ({
            ...x,
            sendNotificationsOnTriggerDegradedOnly:
                "sendNotificationsOnTriggerDegradedOnly" in x
                    ? x.sendNotificationsOnTriggerDegradedOnly
                    : (x.tags || []).includes("DEGRADATION"),
            doNotSendWarnNotifications:
                "doNotSendWarnNotifications" in x ? x.doNotSendWarnNotifications : (x.tags || []).includes("ERROR"),
        }));
        return result;
    }

    async getContactList(): Promise<ContactList> {
        const url = this.apiUrl + "/contact";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addContact(contact: ContactCreateInfo): Promise<Contact> {
        const url = this.apiUrl + "/contact";
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(contact),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async testContact(contactId: string): Promise<void> {
        const url = this.apiUrl + "/contact/" + contactId + "/test";
        const response = await fetch(url, {
            method: "POST",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async updateContact(contact: Contact): Promise<Contact> {
        const url = this.apiUrl + "/contact/" + contact.id;
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(contact),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addSubscription(subscription: SubscriptionCreateInfo): Promise<Subscription> {
        const url = this.apiUrl + "/subscription";
        if (subscription.id != null) {
            throw new Error("InvalidProgramState: id of subscription must be null or undefined");
        }
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(subscription),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async updateSubscription(subscription: Subscription): Promise<Subscription> {
        const url = this.apiUrl + "/subscription/" + subscription.id;
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
            body: JSON.stringify(subscription),
        });
        await this.checkStatus(response);
        return response.json();
    }

    async deleteSubscription(subscriptionId: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + subscriptionId;
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async testSubscription(subscriptionId: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + subscriptionId + "/test";
        const response = await fetch(url, {
            method: "PUT",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async deleteContact(contactId: string): Promise<void> {
        const url = this.apiUrl + "/contact/" + contactId;
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getPatternList(): Promise<PatternList> {
        const url = this.apiUrl + "/pattern";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delPattern(pattern: string): Promise<void> {
        const url = this.apiUrl + "/pattern/" + encodeURI(pattern);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTagList(): Promise<TagList> {
        const url = this.apiUrl + "/tag";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTagStats(): Promise<TagStatList> {
        const url = this.apiUrl + "/tag/stats";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delTag(tag: string): Promise<void> {
        const url = this.apiUrl + "/tag/" + encodeURI(tag);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList> {
        const url =
            this.apiUrl +
            "/trigger/page?" +
            queryString.stringify(
                {
                    /* eslint-disable */
                    p: page,
                    /* eslint-enable */
                    size: this.triggerListPageSize,
                    tags,
                    onlyProblems,
                },
                { arrayFormat: "index", encode: true }
            );
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTrigger(id: string): Promise<Trigger> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async addTrigger(data: $Shape<Trigger>): Promise<{ [key: string]: string }> {
        const url = this.apiUrl + "/trigger";
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async setTrigger(id: string, data: $Shape<Trigger>): Promise<{ [key: string]: string }> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delTrigger(id: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/maintenance";
        const response = await fetch(url, {
            method: "PUT",
            body: JSON.stringify(data),
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getTriggerState(id: string): Promise<TriggerState> {
        const url = this.apiUrl + "/trigger/" + encodeURI(id) + "/state";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async getTriggerEvents(id: string, page: number): Promise<EventList> {
        const url = this.apiUrl + "/event/" + encodeURI(id) + "?p=" + page + "&size=" + this.eventHistoryPageSize;
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delThrottling(triggerId: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/throttling";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delMetric(triggerId: string, metric: string): Promise<void> {
        const url = this.apiUrl + "/trigger/" + encodeURI(triggerId) + "/metrics?name=" + encodeURI(metric);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async getNotificationList(): Promise<NotificationList> {
        const url = this.apiUrl + "/notification?start=0&end=-1";
        const response = await fetch(url, {
            method: "GET",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
        return response.json();
    }

    async delNotification(id: string): Promise<void> {
        const url = this.apiUrl + "/notification?id=" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delAllNotifications(): Promise<void> {
        const url = this.apiUrl + "/notification/all";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delAllNotificationEvents(): Promise<void> {
        const url = this.apiUrl + "/event/all";
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }

    async delSubscription(id: string): Promise<void> {
        const url = this.apiUrl + "/subscription/" + encodeURI(id);
        const response = await fetch(url, {
            method: "DELETE",
            credentials: "same-origin",
        });
        await this.checkStatus(response);
    }
}
<|endoftext|>


<commit_before>// Load dependencies
var amanda = require('../../../src/amanda.js');

/**
 * Test #1
 */
exports['Test #1'] = function(test) {

  var count = 0;

  var schema = {
    required: true,
    format: 'percentage'
  };

  var percentage = [
    96,
    78,
    55,
    3,
    67,
    49,
    70,
    7,
    8.92,
    12.12,
    31,
    73,
    10,
    77,
    99,
    82,
    98,
    69,
    53,
    66,
    51,
    85,
    8,
    58,
    71,
    5,
    79,
    57,
    61,
    27,
    13,
    20,
    48,
    43,
    39,
    23,
    93,
    87,
    74,
    29,
    22,
    64,
    65,
    91,
    21,
    12,
    76,
    14,
    17,
    24,
    45,
    84,
    88,
    1,
    50,
    56,
    52,
    97,
    81,
    72,
    11,
    94,
    36,
    89,
    42,
    41,
    4,
    46,
    40,
    35,
    33,
    100,
    15,
    6,
    59,
    60,
    92,
    80,
    83,
    19,
    47,
    26,
    62,
    95,
    9,
    68,
    30,
    38,
    34,
    90,
    28,
    37,
    44,
    54,
    86,
    75,
    16,
    18,
    63,
    32,
    2,
    25
  ];

  percentage.forEach(function(data) {
    amanda.validate(data, schema, function(error) {
      count += 1;
      test.equal(error, undefined);
    });
  });

  test.equal(count, percentage.length);
  test.done();

};<commit_msg>Update the path to Amanda in the ‘percentage.js’ file<commit_after>// Load dependencies
var amanda = require('../../../dist/latest.js');

/**
 * Test #1
 */
exports['Test #1'] = function(test) {

  var count = 0;

  var schema = {
    required: true,
    format: 'percentage'
  };

  var percentage = [
    96,
    78,
    55,
    3,
    67,
    49,
    70,
    7,
    8.92,
    12.12,
    31,
    73,
    10,
    77,
    99,
    82,
    98,
    69,
    53,
    66,
    51,
    85,
    8,
    58,
    71,
    5,
    79,
    57,
    61,
    27,
    13,
    20,
    48,
    43,
    39,
    23,
    93,
    87,
    74,
    29,
    22,
    64,
    65,
    91,
    21,
    12,
    76,
    14,
    17,
    24,
    45,
    84,
    88,
    1,
    50,
    56,
    52,
    97,
    81,
    72,
    11,
    94,
    36,
    89,
    42,
    41,
    4,
    46,
    40,
    35,
    33,
    100,
    15,
    6,
    59,
    60,
    92,
    80,
    83,
    19,
    47,
    26,
    62,
    95,
    9,
    68,
    30,
    38,
    34,
    90,
    28,
    37,
    44,
    54,
    86,
    75,
    16,
    18,
    63,
    32,
    2,
    25
  ];

  percentage.forEach(function(data) {
    amanda.validate(data, schema, function(error) {
      count += 1;
      test.equal(error, undefined);
    });
  });

  test.equal(count, percentage.length);
  test.done();

};<|endoftext|>


<commit_before>/**
 * Verificar se um valor é um número, da língua portuguesa, valido.
 *
 * @method isValidNumber
 * @param {string} val Um valor para ser verificado.
 * @returns {boolean} Verificação do valor.
 */
export const isValidNumber = (val) => {

  // Verifica se é um número
  if (
       /^-?\d{1,3}\d?((\.\d{3})+)?$/.test(val) // ...formatado
    || /^-?\d{1,3}\d?((\.\d{3})+)?,\d+$/.test(val) // ...decimal formatado
    || /^-?\d+$/.test(val) // ...não formatado
    || /^-?\d+,\d+/.test(val) // ...decimal não formatado
  ) {
    return true
  }

  return false
}

/**
 * Analisar um número.
 *
 * @method parseNumber
 * @param {string} val Um número para ser analisado
 * @returns {object} Objeto com as informações do número
 */
export const parseNumber = (num) => {
  const isNegative = /^-/.test(num)
  const normalized = num.replace(/(-|\.)/g, '')

  if (normalized.includes(',')) {
    const [ integer, decimal ] = normalized.split(',').map((val) => val.replace(/^0+$/, '0'))

    return {
      isNegative,
      integer,
      decimal
    }
  }

  return {
    isNegative,
    integer: normalized,
    decimal: '0'
  }
}<commit_msg>Adiciona novas condições para validação de um número<commit_after>/**
 * Verificar se um valor é um número, da língua portuguesa, valido.
 *
 * @method isValidNumber
 * @param {string} val Um valor para ser verificado.
 * @returns {boolean} Verificação do valor.
 */
export const isValidNumber = (val) => {
  if (typeof val === 'number' && !Number.isSafeInteger(val)) {
    return false
  }

  // Verifica se é um número
  if (
       /^-?\d{1,3}\d?((\.\d{3})+)?$/.test(val) // ...formatado
    || /^-?\d{1,3}\d?((\.\d{3})+)?,\d+$/.test(val) // ...decimal formatado
    || /^-?\d+$/.test(val) // ...não formatado
    || /^-?\d+,\d+/.test(val) // ...decimal não formatado
  ) {
    return true
  }

  return false
}

/**
 * Analisar um número.
 *
 * @method parseNumber
 * @param {string} val Um número para ser analisado
 * @returns {object} Objeto com as informações do número
 */
export const parseNumber = (num) => {
  const isNegative = /^-/.test(num)
  const normalized = num.replace(/(-|\.)/g, '')

  if (normalized.includes(',')) {
    const [ integer, decimal ] = normalized.split(',').map((val) => val.replace(/^0+$/, '0'))

    return {
      isNegative,
      integer,
      decimal
    }
  }

  return {
    isNegative,
    integer: normalized,
    decimal: '0'
  }
}<|endoftext|>