Source: service.js

/**
* service.js - The Service Core Component<br/>
* The <b>Service</b> extension adds extensive abilities to the service (Node.js) layer.<br/>
* This extension adds:<br/>
* <ul>
* <li>DataSource</li>
* <li>Entity</li>
* <li>Resource</li>
* </ul>
*
* @author Bob Warren
*
* @requires augmentedjs ("augmentedjs" in npm)
* @requires node
* @requires http
* @requires https
* @module Augmented.Service
* @version 1.4.3
* @license Apache-2.0
*/
(function(moduleFactory) {
  if (typeof exports === "object") {
    module.exports = moduleFactory(require("augmentedjs"));
  }
}(function(Augmented) {
  "use strict";

  const http = require("http");
  const https = require("https");

  /**
  * The base namespece for all of the Service module.
  * @namespace Service
  * @memberof Augmented
  */
  Augmented.Service = {};

  /**
  * The standard version property
  * @constant VERSION
  * @memberof Augmented.Service
  */
  Augmented.Service.VERSION = "1.4.3";

  /**
  * A nice console logger with prefix for service messages
  * @class Logger
  * @memberof Augmented.Service
  */
  Augmented.Service.Logger = {
    _logger: null,
    _prefix: "SERVICE",
    /**
    * Set the prefix of the logger
    * @method setPrefix
    * @param {string} prefix The prefix for the logger message
    * @memberof Augmented.Service.Logger
    */
    setPrefix: function(prefix) {
      this._prefix = prefix;
    },
    log: function(message) {
      this._logger.log(this._prefix + ": " + message);
    },
    info: function(message) {
      this._logger.info(this._prefix + ": " + message);
    },
    debug: function(message) {
      this._logger.debug(this._prefix + ": " + message);
    },
    warn: function(message) {
      this._logger.warn(this._prefix + ": " + message);
    },
    error: function(message) {
      this._logger.error(this._prefix + ": " + message);
    },
    /**
    * Get an instance class of the service color logger
    * @method getServiceLogger
    * @param {Augmented.Logger.Level} level The logger level
    * @param {string} prefix Optional prefix for the logger message
    * @memberof Augmented.Service.Logger
    */
    getLogger: function(level, prefix) {
      if (!level) {
        level = Augmented.Logger.Level.info;
      }
      this._logger = Augmented.Logger.LoggerFactory.getLogger(
        Augmented.Logger.Type.colorConsole, level);
        if (prefix) {
          this.setPrefix(prefix);
        }
        return this;
      }
    };

    /**
    * A private logger for use in the framework only
    * @private
    */
    const logger = Augmented.Service.Logger.getLogger(Augmented.Configuration.LoggerLevel, "SERVICE");

    /**
    * The datasource object for use as an interface for a datasource
    * @interface DataSource
    * @memberof Augmented.Service
    */
    Augmented.Service.DataSource = function(client) {
      this.connected = false;
      this.style = "database";

      /**
      * @property {object} client The client for use in the DataSource
      * @memberof Augmented.Service.DataSource
      */
      this.client = (client) ? client : null;
      /**
      * @property {string} url The url for the datasource (if applicable)
      * @memberof Augmented.Service.DataSource
      */
      this.url = "";
      /**
      * @property {object} db The database (or simular) for the datasource (if applicable)
      * @memberof Augmented.Service.DataSource
      */
      this.db = null;
      /**
      * @property {object} collection The collection for use in the DataSource
      * @memberof Augmented.Service.DataSource
      */
      this.collection = null;
      /**
      * @method getConnection Get a connection to the DataSource
      * @memberof Augmented.Service.DataSource
      * @returns {boolean} Returns true if a connection is established
      */
      this.getConnection = function() { return false; };
      /**
      * @method closeConnection Close a connection to the DataSource (depending on type may not be needed)
      * @memberof Augmented.Service.DataSource
      * @returns {boolean} Returns true if a connection is established
      */
      this.closeConnection = function() {};
      /**
      * @method insert Insert data
      * @memberof Augmented.Service.DataSource
      * @param {object} data Data to insert
      */
      this.insert = function(data) {};
      /**
      * @method remove Remove data
      * @memberof Augmented.Service.DataSource
      * @param {object} data Data to remove
      */
      this.remove = function(data) {};
      /**
      * @method update Update data
      * @memberof Augmented.Service.DataSource
      * @param {object} data Data to update
      */
      this.update = function(data) {};
      /**
      * @method query Query data
      * @memberof Augmented.Service.DataSource
      * @param {object} query The query object
      * @param {function} callback A callback to execute during the query
      * @returns {object} Returns a value from the query or response code
      */
      this.query = function(query, callback) { return null; };

      /**
      * @method getCollection Get the collection
      * @memberof Augmented.Service.DataSource
      * @returns {object} Returns the collection
      */
      this.getCollection = function() {
        return this.collection;
      };

      /**
      * @method setCollection Set the collection by name
      * @memberof Augmented.Service.DataSource
      * @param {string} name The name of the collection
      */
      this.setCollection = function(name) {

      };
    };

    Augmented.Service.MemoryDataSource = function(client) {
      Augmented.Service.DataSource.call(this, client);
      this.style = "array";
      this.db = [];

      this.getConnection = function(url, collection) {
        this.connected = true;
        if (collection) {
          this.collection = collection;
        }

        this.url = url;
        this.style = "array";
        return true;
      };

      this.closeConnection = function() {
        if (this.db && this.connected) {
          this.connected = false;
          this.db = null;
          this.collection = null;
        }
      };

      this.insert = function(data) {
        this.db.push(data);
      };


    }

    /**
    * The MongoDB datasource instance class
    * @constructor MongoDataSource
    * @implements {Augmented.Service.DataSource}
    * @augments Augmented.Service.DataSource
    * @memberof Augmented.Service
    */
    Augmented.Service.MongoDataSource = function(client) {
      Augmented.Service.DataSource.call(this, client);

      this.setCollection = function(name) {
        //logger.debug("setCollection: " + name);
        if (name && Augmented.isString(name)) {
          //logger.debug("collection: " + name);
          this.collection = this.db.collection(name);
        } //else {
          //    logger.debug("no collection set");
          //}
        };

        this.getConnection = function(url, collection) {
          this.connected = false;
          var that = this;
          if (this.client && !this.connected) {
            this.client.connect(url, function(err, db) {
              if(!err) {
                if (collection) {
                  //logger.debug("getConnection: collection: " + collection);
                  that.collection = db.collection(collection);
                } //else {
                  //logger.debug("getConnection: no collection");
                  //}
                  that.db = db;
                  that.url = url;
                  that.connected = true;
                  that.style = "database";
                } else {
                  //logger.error(err);
                  throw new Error(err);
                }
              });
              return true;
            } else {
              logger.error("no client was passed.");
            }
            return false;
          };

          this.closeConnection = function() {
            if (this.db && this.connected) {
              this.db.close();
              this.connected = false;
              this.db = null;
              this.collection = null;
            }
          };

          this.query = function(query, callback) {
            var ret = {};
            if (this.collection && this.connected) {
              //logger.debug("The query: " + query);
              const myQuery = query;
              if (Augmented.isFunction(query)) {
                myQuery = query();
              }

              this.collection.find(myQuery).toArray(function(err, results) {
                if(!err) {
                  //logger.debug("Results: " + JSON.stringify(results));

                  if (results) {
                    ret = results;
                  }
                  if (callback) {
                    callback(ret);
                  } else {
                    //logger.debug("MongoDatasource, no callback");
                  }
                } else {
                  //logger.error(err);
                  throw new Error(err);
                }
              });
            } else {
              logger.error("no collection defined or not connected to db.");
            }
            //logger.debug("ret: " + JSON.stringify(ret));
            return ret;
          };

          this.insert = function(data, callback) {
            var ret = {};
            if (this.collection && this.connected) {
              if (Array.isArray(data)) {
                this.collection.insertMany(data, function(err, result) {
                  if(!err) {
                    //logger.debug("Result: " + JSON.stringify(result));
                    if (result) {
                      ret = result;
                      if (callback) {
                        callback(ret);
                      }
                    }
                  } else {
                    //logger.error(err);
                    throw new Error(err);
                  }
                });
              } else {
                this.collection.insertOne(data, function(err, result) {
                  if(!err) {
                    //logger.debug("Result: " + JSON.stringify(result));
                    if (result) {
                      ret = result;
                      if (callback) {
                        callback(ret);
                      }
                    }
                  } else {
                    //logger.error(err);
                    throw new Error(err);
                  }
                });
              }
            } else {
              logger.error("no collection defined or not connected to db.");
            }
            //logger.debug("ret: " + JSON.stringify(ret));
            return ret;
          };

          this.update = function(query, data, callback) {
            if (this.collection && this.connected) {
              //logger.debug("The query: " + query);
              const myQuery = query;
              if (Augmented.isFunction(query)) {
                myQuery = query();
              }

              this.collection.update(myQuery, data, function(err, result) {
                if(!err) {
                  //logger.debug("Result: " + JSON.stringify(result));
                } else {
                  //logger.error(err);
                  throw new Error(err);
                }
              });

              if (callback) {
                callback(data);
              }
            } else {
              logger.error("no collection defined or not connected to db.");
            }
            return data;
          };

          this.remove = function(query, callback) {
            var ret = {};
            if (this.collection && this.connected) {
              //logger.debug("The query: " + query);
              const myQuery = query;
              if (Augmented.isFunction(query)) {
                myQuery = query();
              }
              this.collection.remove(myQuery, function(err, results) {
                if(!err) {
                  if (callback) {
                    callback();
                  }
                } else {
                  //logger.error(err);
                  throw new Error(err);
                }
              });
            } else {
              logger.error("no collection defined or not connected to db.");
            }
            return ret;
          };
        };

        Augmented.Service.MongoDataSource.prototype = Object.create(Augmented.Service.DataSource.prototype);
        Augmented.Service.MongoDataSource.prototype.constructor = Augmented.Service.MongoDataSource;

        /**
        * The SOLR datasource instance class
        * @constructor SOLRDataSource
        * @implements {Augmented.Service.DataSource}
        * @augments Augmented.Service.DataSource
        * @memberof Augmented.Service
        */
        Augmented.Service.SOLRDataSource = function(client) {
          Augmented.Service.DataSource.call(client, this,arguments);

          this.getConnection = function(url, collection) {
            this.connected = false;
            var that = this;
            if (this.client && !this.connected) {
              this.client.ping(function(err, db){
                if(!err) {
                  //logger.debug("collection: " + collection);
                  that.collection = collection;
                  that.db = db;
                  that.url = url;
                  that.connected = true;
                  that.style = "search";
                } else {
                  //logger.error(err);
                  throw new Error(err);
                }
              });
            } else {
              logger.error("no client was passed.");
            }
            return this.connected;
          };

          this.closeConnection = function() {
            if (this.db && this.connected) {
              this.connected = false;
              this.db = null;
              this.collection = null;
            }
          };

          this.query = function(query, callback) {
            var ret = {};

            return ret;
          };

          this.insert = function(data, callback) {
            var ret = {};

            return ret;
          };

          this.update = function(query, data, callback) {

            return data;
          };

          this.remove = function(query, callback) {
            var ret = {};

            return ret;
          };
        };

        Augmented.Service.SOLRDataSource.prototype = Object.create(Augmented.Service.DataSource.prototype);
        Augmented.Service.SOLRDataSource.prototype.constructor = Augmented.Service.SOLRDataSource;

        /**
        * The datasource factory to return an instance of a datasource configured by type
        * @namespace DataSourceFactory
        * @memberof Augmented.Service
        */
        Augmented.Service.DataSourceFactory = {
          Type: {
            "Memory": "memory",
            "MongoDB": "mongodb",
            "SOLR": "solr"
          },
          getDatasource: function(type, client) {
            if (type === "mongodb") {
              return new Augmented.Service.MongoDataSource(client);
            } else if (type === "solr") {
              return new Augmented.Service.SOLRDataSource(client);
            } else if (type === "memory") {
              return new Augmented.Service.MemoryDataSource(client);
            }
            return null;
          }
        };

        /**
        * Collection class to handle REST</br/>
        *
        * @constructor Augmented.Service.ResourceCollection
        * @memberof Augmented.Service
        */
        Augmented.Service.ResourceCollection = Augmented.Collection.extend({
          /**
          * Collection name for us in a datasource or an identifier
          * @property {string} name The name of the collection
          * @memberof Augmented.Service.ResourceCollection
          */
          name: "collection",
          /**
          * @property {string} url The url for the datasource (if applicable)
          * @memberof Augmented.Service.ResourceCollection
          */
          url: "",
          /**
          * @method setURL Set the url for the ResourceCollection
          * @param {string|function} url The URL or a function to retun a URL object
          * @memberof Augmented.Service.ResourceCollection
          */
          setURL: function(url) {
            this.url = url;
          },

        });

        /**
        * Collection class to handle ORM to a datasource</br/>
        * <em>Note: Datasource property is required</em>
        *
        * @constructor Augmented.Service.EntityCollection
        * @memberof Augmented.Service
        */
        Augmented.Service.Collection = Augmented.Service.EntityCollection = Augmented.Collection.extend({
          /**
          * Collection name for us in a datasource or an identifier
          * @property {string} name The name of the collection
          * @memberof Augmented.Service.EntityCollection
          */
          name: "collection",
          /**
          * The query to use for the query - defaults to "id" selection
          * @method {any} query The query string to use for selection
          * @memberof Augmented.Service.EntityCollection
          */
          query: null,
          /**
          * @property {string} url The url for the datasource (if applicable)
          * @memberof Augmented.Service.EntityCollection
          */
          url: "",
          /**
          * @method initialize Initialize the model with needed wireing
          * @param {object} options Any options to pass
          * @memberof Augmented.Service.EntityCollection
          */
          initialize: function(options) {
            if (options) {
              //logger.debug("calling initialize with options: " + JSON.stringify(options));

              if (options.datasource) {
                this.datasource = options.datasource;
              }
              if (options.query) {
                this.query = options.query;
              }
              if (options.name) {
                this.name = options.name;
              }
              if (options.url) {
                this.url = options.url;
              }
            }
            if (this.datasource && (this.url === "")) {
              this.url =  this.datasource.url;
            }

            this.setDataSourceCollection(this.name);

            this.init(options);
          },
          /**
          * @method init Custom init method for the model (called at initialize)
          * @param {object} options Any options to pass
          * @memberof Augmented.Service.EntityCollection
          */
          init: function(options) {},
          /**
          * @property {Augmented.Service.DataSource} datasource Datasource instance
          * @memberof Augmented.Service.EntityCollection
          */
          datasource: null,
          /**
          * @method setDatasource Set the datasource for the Collection
          * @param {object} datasource The datasource object
          * @memberof Augmented.Service.EntityCollection
          */
          setDatasource: function(datasource) {
            this.datasource = datasource;
          },
          /**
          * @method sync Sync method to handle datasource functions for the Collection
          * @param {string} method the operation to perform
          * @param {object} options Any options to pass
          * @memberof Augmented.Service.EntityCollection
          */
          sync: function(method, options) {
            //logger.debug("sync " + method);
            if (this.datasource) {
              var that = this;
              try {
                var j = {}, q;
                if (method === "create") {
                  j = this.toJSON();
                  this.datasource.insert(j, function() {
                    that.reset(j);
                    if (options && options.success && (typeof options.success === "function")) {
                      options.success();
                    }
                  });
                } else if (method === "update") {
                  j = this.toJSON();
                  if (options && options.query) {
                    q = options.query;
                  } else {
                    q = this.query;
                  }

                  this.datasource.update(q, j, function() {
                    //that.reset(j);
                    if (options && options.success && (typeof options.success === "function")) {
                      options.success();
                    }
                  });
                } else if (method === "delete") {
                  if (options && options.query) {
                    q = options.query;
                  } else {
                    q = this.query;
                  }
                  this.datasource.remove(q, function() {
                    that.reset();
                    if (options && options.success && (typeof options.success === "function")) {
                      options.success();
                    }
                  });
                } else {
                  // read
                  //logger.log("reading");

                  if (options && options.query) {
                    q = options.query;
                  } else {
                    q = this.query;
                  }

                  //logger.debug("query " + JSON.stringify(q));
                  this.datasource.query(q, function(data) {
                    that.reset(data);

                    //logger.debug("returned: " + JSON.stringify(data));
                    if (options && options.success && (typeof options.success === "function")) {
                      options.success(data);
                    }
                  });
                }
              } catch(e) {
                if (options && options.error && (typeof options.error === "function")) {
                  options.error(e);
                }
                //throw(e);
              }
            } //else {
              //logger.warn("no datasource");
              //}
              return {};
            },
            /**
            * @method fetch Fetch the entity
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.EntityCollection
            */
            fetch: function(options) {
              this.sync("read", options);
            },
            /**
            * @method save Save the entity
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.EntityCollection
            */
            save: function(options) {
              this.sync("create", options);
            },
            /**
            * @method update Update the entity
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.EntityCollection
            */
            update: function(options) {
              this.sync("update", options);
            },
            /**
            * @method destroy Destroy the entity
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.EntityCollection
            */
            destroy: function(options) {
              this.sync("delete", options);
            },
            setDataSourceCollection: function(name) {
              if (name && Augmented.isString(name) && this.datasource) {
                //logger.debug("service: setting collection name: " + name);
                this.name = name;
                this.datasource.setCollection(name);
              }
            }
          });

          /**
          * Entity class to handle ORM to a datasource</br/>
          * <em>Note: Datasource property is required</em>
          *
          * @constructor Augmented.Service.Entity
          * @extends Augmented.Model
          * @memberof Augmented.Service
          */
          Augmented.Service.Entity = Augmented.Model.extend({
            id: "",
            /**
            * The query to use for the query - defaults to "id" selection
            * @method {any} query The query string to use for selection
            * @memberof Augmented.Service.Entity
            */
            query: {},
            /**
            * @property {string|function} url The url for the datasource (if applicable)
            * @memberof Augmented.Service.Entity
            */
            url: "",
            /**
            * @property {string} collection The collection for the datasource (if applicable)
            * @memberof Augmented.Service.Entity
            */
            collection:  "collection",
            /**
            * @method initialize Initialize the model with needed wireing
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.Entity
            */
            initialize: function(options) {
              if (options) {
                if (options.collection) {
                  this.collection = options.collection;
                }
                if (options.datasource) {
                  this.datasource = options.datasource;
                }
                if (options.url) {
                  this.url = this.datasource.url;
                }
                if (options.id) {
                  this.id = options.id;
                }
                if (options.query) {
                  this.query = options.query;
                }
              }
              // don't save this as data, but properties via the object base class options copy.
              this.unset("datasource");
              this.unset("url");
              this.unset("query");
              this.unset("collection");
              this.unset("id");
              if (this.datasource) {
                this.datasource.setCollection(this.collection);
              }
              this.init(options);
            },
            /**
            * @method init Custom init method for the model (called at inititlize)
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.Entity
            */
            init: function(options) {},
            /**
            * @property {Augmented.Service.DataSource} datasource Datasource instance
            * @memberof Augmented.Service.Entity
            */
            datasource: null,
            /**
            * @method sync Sync method to handle datasource functions for the model
            * @param {string} method the operation to perform
            * @param {object} options Any options to pass
            * @memberof Augmented.Service.Entity
            */
            sync: function(method, options) {
              //logger.debug("sync " + method);
              if (this.datasource) {
                var that = this;
                try {
                  var j = {}, q;
                  if (method === "create") {
                    j = that.attributes;
                    this.datasource.insert(j, function() {
                      that.reset(j);
                      if (options && options.success && (typeof options.success === "function")) {
                        options.success();
                      }
                    });
                  } else if (method === "update") {
                    j = that.attributes;

                    //logger.debug("The object: " + JSON.stringify(j));

                    if (options && options.query) {
                      q = options.query;
                    } else {
                      q = this.query;
                    }

                    this.datasource.update(q, j, function() {
                      //that.reset(j);
                      if (options && options.success && (typeof options.success === "function")) {
                        options.success();
                      }
                    });
                  } else if (method === "delete") {
                    if (options && options.query) {
                      q = options.query;
                    } else {
                      q = this.query;
                    }
                    this.datasource.remove(q, function() {
                      that.reset();
                      if (options && options.success && (typeof options.success === "function")) {
                        options.success();
                      }
                    });
                  } else {
                    // read
                    //logger.debug("reading");

                    if (options && options.query) {
                      q = options.query;
                    } else {
                      q = that.query;
                    }

                    var myQuery = q;
                    if (Augmented.isFunction(q)) {
                      var x = q();
                      //logger.debug("x " + x);
                      myQuery = x;
                    }

                    //logger.debug("query " + JSON.stringify(myQuery));
                    this.datasource.query(myQuery, function(data) {
                      if (data === null) {
                        throw new Error("No Data Returned!");
                      }
                      if (Array.isArray(data) && data.length > 0) {
                        that.reset(data[0]);
                      } else if (Array.isArray(data) && data.length === 0) {
                        that.reset();
                      } else {
                        that.reset(data);
                      }

                      //logger.debug("returned: " + JSON.stringify(data));
                      if (options && options.success && (typeof options.success === "function")) {
                        options.success(data);
                      }
                    });
                  }
                } catch(e) {
                  if (options && options.error && (typeof options.error === "function")) {
                    options.error(e);
                  }
                  //throw(e);
                }
              } //else {
                //    logger.warn("no datasource");
                //}
                return {};
              },
              /**
              * @method fetch Fetch the entity
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Entity
              */
              fetch: function(options) {
                this.sync("read", options);
              },
              /**
              * @method save Save the entity
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Entity
              */
              save: function(options) {
                this.sync("create", options);
              },
              /**
              * @method update Update the entity
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Entity
              */
              update: function(options) {
                this.sync("update", options);
              },
              /**
              * @method destroy Destroy the entity
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Entity
              */
              destroy: function(options) {
                this.sync("delete", options);
              }
            });

            /**
            * Resource class to handle REST from Node</br/>
            * <em>Note: URL property is required</em>
            *
            * @constructor Augmented.Service.Resource
            * @extends Augmented.Model
            * @memberof Augmented.Service
            */
            Augmented.Service.Resource = Augmented.Model.extend({
              /**
              * @property {string} secure The secure flag
              * @memberof Augmented.Service.Resource
              */
              secure: false,

              id: "",
              /**
              * @property {string} url The url for the REST Service
              * @memberof Augmented.Service.Resource
              */
              url: "",
              /**
              * @method initialize Initialize the model with needed wiring
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Resource
              */
              initialize: function(options) {
                //logger.log("initialize");
                if (options && options.url) {
                  this.url = options.url;
                }
                // don't save this as data, but properties via the object base class options copy.
                this.unset("url");
                this.init(options);
              },
              /**
              * @method init Custom init method for the model (called at inititlize)
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Resource
              */
              init: function(options) {},
              /**
              * @method fetch Fetch the Resource
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Resource
              */
              fetch: function(options) {
                this.sync("read", options);
              },
              /**
              * @method sync Sync method to handle REST functions for the model
              * @param {string} method the operation to perform
              * @param {object} options Any options to pass
              * @memberof Augmented.Service.Resource
              */
              sync: function(method, options) {
                //logger.debug("sync " + method);
                if (this.url) {
                  var that = this, success, error;
                  if (options && options.success && (typeof options.success === "function")) {
                    success = options.success;
                  }
                  if (options && options.error && (typeof options.error === "function")) {
                    error = options.error;
                  }

                  try {
                    var j = {}, q, u = (typeof this.url === "function") ? this.url() : this.url;
                    if (method === "create") {
                      j = that.attributes;
                      var options = {
                        path: u,
                        method: "POST",
                        headers: {
                          "Content-Type": "application/json",
                        }
                      };
                      const h = (this.secure) ? https : http;

                      let req = h.request(options, function(res) {
                        //logger.debug("Status: " + res.statusCode);
                        //logger.debug("Headers: " + JSON.stringify(res.headers));
                        res.setEncoding("utf8");
                        res.on("data", function (body) {
                          //logger.debug("Body: " + body);
                        });

                        res.once("end", function() {
                          if (success) {
                            success(req.statusCode);
                          }
                        });
                      });
                      req.on("error", function(e) {
                        logger.error("problem with request: " + e.message);
                        if (error) {
                          error(500, e);
                        }
                      });
                      // write data to request body
                      req.write(that.toJSON());
                      req.end();

                    } else if (method === "update") {
                      j = that.attributes;
                      let options = {
                        path: u,
                        method: "PUT",
                        headers: {
                          "Content-Type": "application/json",
                        }
                      };

                      const h = (this.secure) ? https : http;

                      let req = h.request(options, function(res) {
                        //logger.debug("Status: " + res.statusCode);
                        //logger.debug("Headers: " + JSON.stringify(res.headers));
                        res.setEncoding("utf8");
                        res.on("data", function (body) {
                          //logger.debug("Body: " + body);
                        });

                        res.once("end", function() {
                          if (success) {
                            success(req.statusCode, req.statusMessage);
                          }
                        });
                      });
                      req.on("error", function(e) {
                        //logger.error("problem with request: " + e.message);
                        if (error) {
                          error(req.statusCode, e);
                        }
                      });
                      // write data to request body
                      req.write(that.toJSON());
                      req.end();

                    } else if (method === "delete") {
                      let options = {
                        path: u,
                        method: "DELETE"
                      };

                      const h = (this.secure) ? https : http;

                      let req = h.request(options, function(res) {
                        //logger.debug("Status: " + res.statusCode);
                        res.setEncoding("utf8");
                        res.once("end", function() {
                          if (success) {
                            success(req.statusCode, req.statusMessage);
                          }
                        });
                      });
                      req.on("error", function(e) {
                        //logger.error("problem with request: " + e.message);
                        if (error) {
                          error(500, e);
                        }
                      });
                      req.end();

                    } else {
                      // read
                      //logger.debug("reading from " + u);
                      //logger.debug("have options? " + (options));

                      const h = (this.secure) ? https : http;

                      h.get(u, function(res) {
                        var body = ""; // Will contain the final response
                        // Received data is a buffer.
                        // Adding it to our body
                        res.on("data", function(data){
                          body += data;
                        });
                        // After the response is completed, parse it and log it to the console

                        if (res.statusCode >= 200 && res.statusCode < 300) {
                          res.once("end", function() {
                            //logger.debug("Got data: " + body);
                            var parsed = {};
                            try {
                              parsed = JSON.parse(body);
                              that.set(parsed);
                              if (success) {
                                success(res.statusCode, res.statusMessage);
                              }
                            } catch(e) {
                              //logger.error("Not JSON response, can't add to resource.  Exception: " + e);
                              if (error) {
                                error(res.statusCode, e);
                              }
                            }
                          });
                        } else {
                          //logger.error("Unsuccessfull Fetch - " + res.statusCode + " " + res.statusMessage);
                          if (error) {
                            error(res.statusCode, res.statusMessage);
                          }
                        }
                      })
                      // If any error has occured, log error to console
                      .once("error", function(e, options) {
                        //logger.error("Got error: " + e.message);
                        if (error) {
                          error(500, e);
                        }
                      });
                    }
                  } catch(e) {
                    //logger.error("Got exception: " + e);
                    if (error) {
                      error(500, e);
                    }
                  }
                } else {
                  logger.warn("no url");
                }
                return {};
              }
            });

            return Augmented.Service;
          }));