Source: lib/model/ServiceObject.js

var Promise = require('bluebird');
var _ = require('lodash');
var d = require('debug')("raptorjs:ServiceObject");

var util = require('../util');

var Action = require('./Action');
var Stream = require('./Stream');

/**
 *
 * The Service Object
 *
 * @param {Object} obj A JSON object with the object definition
 * @param {Raptor} container reference to the Raptor sdk wrapper
 *
 * @augments Container
 * @constructor
 */
var ServiceObject = function (obj, container) {

  var me = this;

  this.data = {};

  this.data.id = null;
  this.data.parentId = null;
  this.data.name = null;
  // this.data.createdAt = new Date();

  this.data.settings = {
    storeEnabled: true,
    eventsEnabled: true
  };

  this.data.customFields = {};

  this.data.streams = {};
  this.data.actions = [];

  this.exportProperties();

  if(container) {
    this.setContainer(container);
  }

  if(obj) {
    this.parseJSON(obj);
  }

  this.permissions = (function (p) {
    return {
      get: function (user) {
        return p.get(me, user);
      },
      set: function (user, permissions) {
        return p.set(me, permissions, user);
      }
    };
  })(container.auth.permissions);


};
util.extends(ServiceObject, "Container");

/**
 * @inheritdoc
 */
ServiceObject.prototype.setContainer = function (c) {
  this.container = c;
  this.client = c.client;
};

/**
 * @return {String} id The object id
 */
ServiceObject.prototype.getId = function () {
  return this.data.id || null;
};

/**
 * @return {Number} createdAt The creation date as unix timestamp
 */
ServiceObject.prototype.getCreatedAt = function () {
  return this.data.createdAt || null;
};

/**
 * Return a stream reference
 *
 * @param {String} name stream name
 * @return {Stream} stream requested stream, if available
 */
ServiceObject.prototype.stream = function (name) {
  return this.data.streams[name];
};

/**
 * Return an action reference
 *
 * @param {String} name action name
 * @return {Action} action requested action, if available
 */
ServiceObject.prototype.action = function (name) {
  return this.data.actions[name];
};

/**
 * @inheritdoc
 */
ServiceObject.prototype.toJSON = function () {
  var json = this.__super__.toJSON.call(this);
  var actions = [];
  _.forEach(json.actions, function (action) {
    actions.push(action);
  });
  json.actions = actions;
  return json;
};

/**
 * @inheritdoc
 */
ServiceObject.prototype.parseJSON = function (data) {

  this.__super__.parseJSON.call(this, data);

  if(data.actions !== undefined)
    this.setActions(data.actions);

  if(data.streams !== undefined)
    this.setStreams(data.streams);

  this.validate();
};

/**
 * @inheritdoc
 */
ServiceObject.prototype.validate = function () {

  if(!this.name)
    throw new Error("Object name is required");

  _.forEach(this.streams, function (stream) {
    stream.validate();
  });

  _.forEach(this.actions, function (action) {
    action.validate();
  });

};

/**
 * Set actions to the object
 *
 * @param {Array} actions The list of actions to set
 */
ServiceObject.prototype.setActions = function (actions) {
  var me = this;
  if(!actions) return;

  this.data.actions = {};

  _.forEach(actions, function (raw, name) {

    if(typeof raw === 'string') {
      raw = {
        name: raw
      };
    }

    if(!raw.name && typeof name === 'string')
      raw.name = name;

    var action = new Action(raw, me);
    me.data.actions[action.data.name] = action;
  });

};

/**
 * Add a stream to the object
 *
 * @param {String} name The stream name
 * @param {Object} definition The steam definition
 */
ServiceObject.prototype.addStream = function (name, def) {

  def = def || {};

  if(!def.channels) {
    def = {
      channels: def
    };
  }
  def.name = name;

  var stream = new Stream(def, this);

  this.streams[stream.name] = stream;
};

/**
 * Set streams to the object
 *
 * @param {Array} streams The list of streams to set
 */
ServiceObject.prototype.setStreams = function (streams) {
  var me = this;
  if(!streams) return;

  this.data.streams = {};

  _.forEach(streams, function (raw, name) {
    me.addStream(name, raw);
  });
};

/**
 * Create a new Object instance
 *
 * @return {Promise} object the newly created reference
 */
ServiceObject.prototype.create = function () {
  return this.client.post('/', this.toJSON())
    .bind(this)
    .then(function (res) {
      d("Created new object " + res.id);
      this.id = res.id;
      this.createdAt = res.createdAt;
      return Promise.resolve(this);
    });
};

/**
 * Load the Object definition
 *
 * @param {String} id The object Id
 *
 * @return {Promise} Promise of the request with the ServiceObject as argument
 */
ServiceObject.prototype.load = function (id) {
  return this.client.get('/' + (id || this.getId()))
    .bind(this)
    .then(function (obj) {
      this.parseJSON(obj);
      return Promise.resolve(this);
    });
};

/**
 * Update an Object
 *
 * @return {Promise} Promise of the request with the ServiceObject as argument
 */
ServiceObject.prototype.update = function (data) {
  return this.client.put('/' + this.getId(), data || this.toJSON())
    .bind(this)
    .then(function (obj) {
      this.parseJSON(obj);
      return Promise.resolve(this);
    });
};

/**
 * Delete a Service Object
 *
 * @param {String} Optional, the objectId to delete
 * @return {Promise} Promise of the request with a new empty so as argument
 */
ServiceObject.prototype.delete = function (objectId) {
  return this.client.delete('/' + (objectId || this.getId()))
    .bind(this)
    .then(function () {
      this.parseJSON({});
      return Promise.resolve(null);
    });
};

/**
 * Get children
 *
 * @return {Promise} Promise of the request with a the list of children
 */
ServiceObject.prototype.getChildren = function () {
  return this.client.get('/' + this.getId() + '/tree')
    .bind(this)
    .then(function (list) {
      var me = this;
      return Promise.resolve(list.map(function (raw) {
        return me.getContainer().fromJSON(raw);
      }));
    });
};

/**
 * Set children
 *
 * @return {Promise} Promise of the request with a the list of children
 */
ServiceObject.prototype.setChildren = function (list) {
  list = list instanceof Array ? list : [list]
  list = list.map(function (raw) {
    return raw.id || raw;
  })
  return this.client.post('/' + this.getId() + '/tree', list).bind(this)
};

/**
 * Add a child
 *
 * @param {ServiceObject} child The object instance or id to add
 * @return {Promise} Promise of the request with a the list of children
 */
ServiceObject.prototype.addChild = function (obj) {
  return this.client.put('/' + this.getId() + '/tree/' + (obj.id || obj))
    .bind(this)
    .then(function (list) {
      if(obj.id) {
        list.forEach(function (o1) {
          if(obj.id === o1.id) {
            obj.parentId = o1.parentId
            obj.path = o1.path
          }
        })
      }
      return Promise.resolve(list)
    })
    .bind(this)
};

/**
 * Remove a child
 *
 * @param {ServiceObject} child The object instance or id to remove
 * @return {Promise} Promise of the request with a the list of children
 */
ServiceObject.prototype.removeChild = function (obj) {
  return this.client.delete('/' + this.getId() + '/tree/' + (obj.id || obj))
    .then(function (list) {
      if(obj.id) {
        obj.parentId = null
        obj.path = null
      }
      return Promise.resolve(list)
    })
    .bind(this)
};

/**
 * Callback called on an event notification
 *
 * @callback ServiceObjectOnEvent
 * @param {Object} event event information
 */

/**
 * Subscribe for Service Object updates
 *
 * @param {ServiceObjectOnEvent} cb The callback that handles an event notification
 * @return {EventEmitter} An emitter that will notify of updates
 */
ServiceObject.prototype.subscribe = function (fn) {
  var topic = this.getId() + "/events";
  return this.client.subscribe(topic, fn)
    .bind(this)
    .then(function (emitter) {
      return Promise.resolve(emitter);
    });
};

/**
 * Unsubscribe from Service Object updates
 */
ServiceObject.prototype.unsubscribe = function (fn) {
  var topic = this.getId() + "/events";
  return this.client.unsubscribe(topic, fn)
    .bind(this)
    .then(function (emitter) {
      return Promise.resolve(emitter);
    });
};

module.exports = ServiceObject;