<commit_before>/*jshint unused:true, undef:true, strict:true*/
/*global global, _, Backbone*/
(function (window) {
  "use strict";

  var globalCache = {};

  /**
   * UniqueModel wrapper converts regular Backbone models into
   * unique ones.
   *
   * Example:
   *   var UniqueUser = UniqueModel(User);
   *
   * If this is model is synced between windows, you need to
   * specify the model's name (string) and a valid storage adapter
   * (currently just 'localStorage').
   *
   * Example:
   *   var SyncedUniqueUser = UniqueModel(User, 'User', 'localStorage');
   */

  function UniqueModel(Model, modelName, storageAdapter) {
    modelName = modelName || _.uniqueId('UniqueModel_');
    storageAdapter = storageAdapter || UniqueModel.STORAGE_DEFAULT_ADAPTER;

    var cache = UniqueModel.addModel(Model, modelName, storageAdapter);

    return cache.modelConstructor;
  }

  UniqueModel.STORAGE_DEFAULT_ADAPTER = 'memory';
  UniqueModel.STORAGE_KEY_DELIMETER = '.';
  UniqueModel.STORAGE_NAMESPACE = 'UniqueModel';

  // Returns the cache associated with the given Model.
  UniqueModel.getModelCache = function (modelName) {
    var cache = globalCache[modelName];
    if (!cache)
      throw "Unrecognized model: " + modelName;

    return cache;
  };

  UniqueModel.addModel = function (Model, modelName, storageAdapter) {
    // Throw error here? (added twice)
    if (globalCache[modelName])
      return globalCache[modelName];

    var cache = new ModelCache(Model, modelName, storageAdapter);
    globalCache[modelName] = cache;
    return cache;
  };

  // Clears all in-memory instances
  UniqueModel.clear = function () {
    for (var modelName in globalCache) {
      if (globalCache.hasOwnProperty(modelName))
        delete globalCache[modelName];
    }
  };

  /*
   * Encapsulates a cache for a single model.
   */

  function ModelCache (Model, modelName, storageAdapter) {
    var self = this;

    this.instances = {};
    this.Model = Model;
    this.modelName = modelName;

    this.storage = null;
    if (storageAdapter === 'localStorage') {
      this.storage = new StorageAdapter(this.modelName, localStorage);
    } else if (storageAdapter === 'sessionStorage') {
      this.storage = new StorageAdapter(this.modelName, sessionStorage);
    }

    if (this.storage) {
      this.storage.on('sync', this.storageSync, this);
      this.storage.on('destroy', this.storageDestroy, this);
    }

    var modelConstructor = function (attrs, options) {
      return self.get(attrs, options);
    };
    _.extend(modelConstructor, Backbone.Events);

    // Backbone collections need prototype of wrapped class
    modelConstructor.prototype = this.Model.prototype;
    this.modelConstructor = modelConstructor;

  }

  _.extend(ModelCache.prototype, {

    newModel: function (attrs, options) {
      var instance = new this.Model(attrs, options);

      if (this.storage) {
        if (instance.id)
          this.storage.save(instance.id, instance.attributes);

        instance.on('sync', this.instanceSync, this);
        instance.on('destroy', this.instanceDestroy, this);
      }

      return instance;
    },

    // Event handler when 'sync' is triggered on an instance
    instanceSync: function (instance) {
      if (this.storage)
        this.storage.save(instance.id, instance.attributes);
    },

    // Event handler when 'destroy' is triggered on an instance
    instanceDestroy: function (instance) {
      if (this.storage)
        this.storage.remove(instance.id);
    },

    // Event handler when 'sync' is triggered on the storage adapter
    storageSync: function (id, attrs) {
      this.get(attrs, { fromStorage: true });
    },

    // Event handler when 'destroy' is triggered on the storage handler
    storageDestroy: function (id) {
      var instance = this.instances[id];
      if (instance) {
        instance.trigger('destroy', instance);
        delete this.instances[id];
      }
    },

    add: function (id, attrs, options) {
      var instance = this.newModel(attrs, options);
      this.instances[id] = instance;

      return instance;
    },

    get: function (attrs, options) {
      options = options || {};
      var Model = this.Model;
      var id = attrs && attrs[Model.prototype.idAttribute];

      // If there's no ID, this model isn't being tracked; return
      // a new instance
      if (!id)
        return this.newModel(attrs, options);

      // Attempt to restore a cached instance
      var instance = this.instances[id];
      if (!instance) {
        // If we haven't seen this instance before, start caching it
        instance = this.add(id, attrs, options);
        if (options.fromStorage)
          this.modelConstructor.trigger('uniquemodel.add', instance);
      } else {
        // Otherwise update the attributes of the cached instance
        instance.set(attrs);
        if (!options.fromStorage)
          this.instanceSync(instance);
      }
      return instance;
    }
  });

  /**
   * Wraps localStorage access and onstorage events. Designed
   * so that this can be swapped out for another adapter (i.e.
   * sessionStorage or a localStorage-backed library like lscache)
   */
  function StorageAdapter (modelName, store) {
    this.modelName = modelName;
    this.store = store;

    StorageAdapter.instances[modelName] = this;

    // Global listener - only listen once
    if (!StorageAdapter.listener) {
      StorageAdapter.listener = window.addEventListener ?
        window.addEventListener('storage', StorageAdapter.onStorage, false) :
        window.attachEvent('onstorage', StorageAdapter.onStorage);
    }
  }

  // Hash of StorageAdapter instances
  StorageAdapter.instances = {};

  // Reference to the global onstorage handler
  StorageAdapter.listener = null;

  StorageAdapter.onStorage = function (evt) {
    // TODO: IE fires onstorage even in the window that fired the
    //       change. Deal with that somehow.
    var key = evt.key;

    // This will process *all* storage events, so make sure not to choke
    // on events we're not interested in.

    // Example regex output: /UniqueModel\.(\w+)\.(.+)/
    var re = new RegExp([
      UniqueModel.STORAGE_NAMESPACE, // namespace (default is UniqueModel)
      '(\\w+)',                      // class name
      '(.+)'                         // key
    ].join('\\' + UniqueModel.STORAGE_KEY_DELIMETER));

    var match = key.match(re);
    if (!match)
      return;

    var modelName = match[1];
    var id = match[2];

    var adapter = StorageAdapter.instances[modelName];
    if (!adapter)
      return;

    adapter.handleStorageEvent(key, id);
  };

  _.extend(StorageAdapter.prototype, {
    handleStorageEvent: function (key, id) {
      var json = this.store.getItem(key);
      if (!json)
        this.trigger('destroy', id);
      else
        this.trigger('sync', id, JSON.parse(json));
    },

    getStorageKey: function (id) {
      // e.g. UniqueModel.User.12345
      var str = [
        UniqueModel.STORAGE_NAMESPACE,
        this.modelName,
        id
      ].join(UniqueModel.STORAGE_KEY_DELIMETER);

      return str;
    },

    save: function (id, attrs) {
      if (!id)
        throw 'Cannot save without id';

      var json = JSON.stringify(attrs);
      this.store.setItem(this.getStorageKey(id), json);
    },

    remove: function (id) {
      if (!id)
        throw 'Cannot remove without id';

      this.store.removeItem(this.getStorageKey(id));
    }
  }, Backbone.Events);

  // Exports
  _.extend(UniqueModel, {
    ModelCache: ModelCache,
    StorageAdapter: StorageAdapter
  });

  window.Backbone.UniqueModel = UniqueModel;

})(typeof global === "object" ? global : this);
<commit_msg>AMD, requirejs and commonjs compatibility<commit_after>/*jshint unused:true, undef:true, strict:true*/
/*global global, _, Backbone*/
(function(root, factory) {

  // try AMD
  if (typeof define === 'function' && define.amd) {
    define(['backbone'], function (Backbone) {
      Backbone.UniqueModel = factory(Backbone);
    });

  // Next for Node.js or CommonJS
  } else if (typeof exports !== 'undefined') {
    var Backbone = require('backbone');
    Backbone.UniqueModel = factory(Backbone);

  // else just attach to the Backbone global
  } else {
    root.Backbone.UniqueModel = factory(root.Backbone);
  }

}(this, function(Backbone) {
  "use strict";

  var globalCache = {};

  /**
   * UniqueModel wrapper converts regular Backbone models into
   * unique ones.
   *
   * Example:
   *   var UniqueUser = UniqueModel(User);
   *
   * If this is model is synced between windows, you need to
   * specify the model's name (string) and a valid storage adapter
   * (currently just 'localStorage').
   *
   * Example:
   *   var SyncedUniqueUser = UniqueModel(User, 'User', 'localStorage');
   */

  function UniqueModel(Model, modelName, storageAdapter) {
    modelName = modelName || _.uniqueId('UniqueModel_');
    storageAdapter = storageAdapter || UniqueModel.STORAGE_DEFAULT_ADAPTER;

    var cache = UniqueModel.addModel(Model, modelName, storageAdapter);

    return cache.modelConstructor;
  }

  UniqueModel.STORAGE_DEFAULT_ADAPTER = 'memory';
  UniqueModel.STORAGE_KEY_DELIMETER = '.';
  UniqueModel.STORAGE_NAMESPACE = 'UniqueModel';

  // Returns the cache associated with the given Model.
  UniqueModel.getModelCache = function (modelName) {
    var cache = globalCache[modelName];
    if (!cache)
      throw "Unrecognized model: " + modelName;

    return cache;
  };

  UniqueModel.addModel = function (Model, modelName, storageAdapter) {
    // Throw error here? (added twice)
    if (globalCache[modelName])
      return globalCache[modelName];

    var cache = new ModelCache(Model, modelName, storageAdapter);
    globalCache[modelName] = cache;
    return cache;
  };

  // Clears all in-memory instances
  UniqueModel.clear = function () {
    for (var modelName in globalCache) {
      if (globalCache.hasOwnProperty(modelName))
        delete globalCache[modelName];
    }
  };

  /*
   * Encapsulates a cache for a single model.
   */

  function ModelCache (Model, modelName, storageAdapter) {
    var self = this;

    this.instances = {};
    this.Model = Model;
    this.modelName = modelName;

    this.storage = null;
    if (storageAdapter === 'localStorage') {
      this.storage = new StorageAdapter(this.modelName, localStorage);
    } else if (storageAdapter === 'sessionStorage') {
      this.storage = new StorageAdapter(this.modelName, sessionStorage);
    }

    if (this.storage) {
      this.storage.on('sync', this.storageSync, this);
      this.storage.on('destroy', this.storageDestroy, this);
    }

    var modelConstructor = function (attrs, options) {
      return self.get(attrs, options);
    };
    _.extend(modelConstructor, Backbone.Events);

    // Backbone collections need prototype of wrapped class
    modelConstructor.prototype = this.Model.prototype;
    this.modelConstructor = modelConstructor;

  }

  _.extend(ModelCache.prototype, {

    newModel: function (attrs, options) {
      var instance = new this.Model(attrs, options);

      if (this.storage) {
        if (instance.id)
          this.storage.save(instance.id, instance.attributes);

        instance.on('sync', this.instanceSync, this);
        instance.on('destroy', this.instanceDestroy, this);
      }

      return instance;
    },

    // Event handler when 'sync' is triggered on an instance
    instanceSync: function (instance) {
      if (this.storage)
        this.storage.save(instance.id, instance.attributes);
    },

    // Event handler when 'destroy' is triggered on an instance
    instanceDestroy: function (instance) {
      if (this.storage)
        this.storage.remove(instance.id);
    },

    // Event handler when 'sync' is triggered on the storage adapter
    storageSync: function (id, attrs) {
      this.get(attrs, { fromStorage: true });
    },

    // Event handler when 'destroy' is triggered on the storage handler
    storageDestroy: function (id) {
      var instance = this.instances[id];
      if (instance) {
        instance.trigger('destroy', instance);
        delete this.instances[id];
      }
    },

    add: function (id, attrs, options) {
      var instance = this.newModel(attrs, options);
      this.instances[id] = instance;

      return instance;
    },

    get: function (attrs, options) {
      options = options || {};
      var Model = this.Model;
      var id = attrs && attrs[Model.prototype.idAttribute];

      // If there's no ID, this model isn't being tracked; return
      // a new instance
      if (!id)
        return this.newModel(attrs, options);

      // Attempt to restore a cached instance
      var instance = this.instances[id];
      if (!instance) {
        // If we haven't seen this instance before, start caching it
        instance = this.add(id, attrs, options);
        if (options.fromStorage)
          this.modelConstructor.trigger('uniquemodel.add', instance);
      } else {
        // Otherwise update the attributes of the cached instance
        instance.set(attrs);
        if (!options.fromStorage)
          this.instanceSync(instance);
      }
      return instance;
    }
  });

  /**
   * Wraps localStorage access and onstorage events. Designed
   * so that this can be swapped out for another adapter (i.e.
   * sessionStorage or a localStorage-backed library like lscache)
   */
  function StorageAdapter (modelName, store) {
    this.modelName = modelName;
    this.store = store;

    StorageAdapter.instances[modelName] = this;

    // Global listener - only listen once
    if (!StorageAdapter.listener) {
      StorageAdapter.listener = window.addEventListener ?
        window.addEventListener('storage', StorageAdapter.onStorage, false) :
        window.attachEvent('onstorage', StorageAdapter.onStorage);
    }
  }

  // Hash of StorageAdapter instances
  StorageAdapter.instances = {};

  // Reference to the global onstorage handler
  StorageAdapter.listener = null;

  StorageAdapter.onStorage = function (evt) {
    // TODO: IE fires onstorage even in the window that fired the
    //       change. Deal with that somehow.
    var key = evt.key;

    // This will process *all* storage events, so make sure not to choke
    // on events we're not interested in.

    // Example regex output: /UniqueModel\.(\w+)\.(.+)/
    var re = new RegExp([
      UniqueModel.STORAGE_NAMESPACE, // namespace (default is UniqueModel)
      '(\\w+)',                      // class name
      '(.+)'                         // key
    ].join('\\' + UniqueModel.STORAGE_KEY_DELIMETER));

    var match = key.match(re);
    if (!match)
      return;

    var modelName = match[1];
    var id = match[2];

    var adapter = StorageAdapter.instances[modelName];
    if (!adapter)
      return;

    adapter.handleStorageEvent(key, id);
  };

  _.extend(StorageAdapter.prototype, {
    handleStorageEvent: function (key, id) {
      var json = this.store.getItem(key);
      if (!json)
        this.trigger('destroy', id);
      else
        this.trigger('sync', id, JSON.parse(json));
    },

    getStorageKey: function (id) {
      // e.g. UniqueModel.User.12345
      var str = [
        UniqueModel.STORAGE_NAMESPACE,
        this.modelName,
        id
      ].join(UniqueModel.STORAGE_KEY_DELIMETER);

      return str;
    },

    save: function (id, attrs) {
      if (!id)
        throw 'Cannot save without id';

      var json = JSON.stringify(attrs);
      this.store.setItem(this.getStorageKey(id), json);
    },

    remove: function (id) {
      if (!id)
        throw 'Cannot remove without id';

      this.store.removeItem(this.getStorageKey(id));
    }
  }, Backbone.Events);

  // Exports
  _.extend(UniqueModel, {
    ModelCache: ModelCache,
    StorageAdapter: StorageAdapter
  });

  return UniqueModel;

}));
<|endoftext|>


<commit_before>/*
 * Backbone.ViewOptions, v0.2.4
 * Copyright (c)2014 Rotunda Software, LLC.
 * Distributed under MIT license
 * http://github.com/rotundasoftware/backbone.viewOptions
*/

( function( root, factory ) {
	if ( typeof define === 'function' && define.amd ) {
		// AMD
		define([ 'underscore', 'backbone' ], factory );
	} else if (typeof exports === 'object') {
		// Node
		module.exports = factory( require( 'underscore' ), require( 'backbone' ) );
	} else {
		// Browser globals
		root.returnExports = factory( root._, root.Backbone );
	}
} ( this, function( _, Backbone ) {
	Backbone.ViewOptions = {};

	Backbone.ViewOptions.add = function( view, optionsDeclarationsProperty ) {
		if( _.isUndefined( optionsDeclarationsProperty ) ) optionsDeclarationsProperty = "options";

		// ****************** Public methods added to view ******************

		view.setOptions = function( options ) {
			var _this = this;
			var optionsThatWereChanged = {};
			var optionsThatWereChangedPreviousValues = {};

			var optionDeclarations = _.result( this, optionsDeclarationsProperty );

			if( _.isString( options ) ) {
				var optionsAsObject = {};
				optionsAsObject[ arguments[ 0 ] ] = arguments[ 1 ];
				options = optionsAsObject;
			}

			if( ! _.isUndefined( optionDeclarations ) ) {
				var normalizedOptionDeclarations = _normalizeOptionDeclarations( optionDeclarations );

				_.each( normalizedOptionDeclarations, function( thisOptionProperties, thisOptionName ) {
					var thisOptionRequired = thisOptionProperties.required;
					var thisOptionDefaultValue = thisOptionProperties.defaultValue;

					if( thisOptionRequired ) {
						// note we do not throw an error if a required option is not supplied, but it is
						// found on the object itself (due to a prior call of view.setOptions, most likely)

						if( ( ! options || ! _.contains( _.keys( options ), thisOptionName ) ) && _.isUndefined( _this[ thisOptionName ] ) )
							throw new Error( "Required option \"" + thisOptionName + "\" was not supplied." );

						if( options && _.contains( _.keys( options ), thisOptionName ) && _.isUndefined( options[ thisOptionName ] ) )
							throw new Error( "Required option \"" + thisOptionName + "\" can not be set to undefined." );
					}

					// attach the supplied value of this option, or the appropriate default value, to the view object
					if( options && thisOptionName in options && ! _.isUndefined( options[ thisOptionName ] ) ) {
						var oldValue = _this[ thisOptionName ];
						var newValue = options[ thisOptionName ];
						// if this option already exists on the view, and the new value is different,
						// make a note that we will be changing it
						if( ! _.isUndefined( oldValue ) && oldValue !== newValue ) {
							optionsThatWereChangedPreviousValues[ thisOptionName ] = oldValue;
							optionsThatWereChanged[ thisOptionName ] = newValue;
						}
						_this[ thisOptionName ] = newValue;
						// note we do NOT delete the option off the options object here so that
						// multiple views can be passed the same options object without issue.
					}
					else if( _.isUndefined( _this[ thisOptionName ] ) ) {
						// note defaults do not write over any existing properties on the view itself.
						_this[ thisOptionName ] = thisOptionDefaultValue;
					}
				} );
			}

			if( _.keys( optionsThatWereChanged ).length > 0 ) {
				if( _.isFunction( _this.onOptionsChanged ) )
					_this.onOptionsChanged( optionsThatWereChanged, optionsThatWereChangedPreviousValues );
				else if( _.isFunction( _this._onOptionsChanged ) )
					_this._onOptionsChanged( optionsThatWereChanged, optionsThatWereChangedPreviousValues );
			}
		};

		view.getOptions = function( whichOptions ) {
			var optionDeclarations = _.result( this, optionsDeclarationsProperty );
			if( _.isUndefined( optionDeclarations ) ) return {};

			var normalizedOptionDeclarations = _normalizeOptionDeclarations( optionDeclarations );
			var optionsNames = _.keys( normalizedOptionDeclarations );

			if( _.isUndefined( whichOptions ) ) whichOptions = optionsNames;
			else if( _.isString( whichOptions ) ) whichOptions = [ whichOptions ];

			return _.pick( this, optionsNames );
		};
	};

	// ****************** Private Utility Functions ******************

	function _normalizeOptionDeclarations( optionDeclarations ) {
		// convert our short-hand option syntax (with exclamation marks, etc.)
		// to a simple array of standard option declaration objects.

		var normalizedOptionDeclarations = {};

		if( ! _.isArray( optionDeclarations ) ) throw new Error( "Option declarations must be an array." );

		_.each( optionDeclarations, function( thisOptionDeclaration ) {
			var thisOptionName, thisOptionRequired, thisOptionDefaultValue;

			thisOptionRequired = false;
			thisOptionDefaultValue = undefined;

			if( _.isString( thisOptionDeclaration ) )
				thisOptionName = thisOptionDeclaration;
			else if( _.isObject( thisOptionDeclaration ) ) {
				thisOptionName = _.first( _.keys( thisOptionDeclaration ) );
				if( _.isFunction( thisOptionDeclaration[ thisOptionName ] ) || _.isRegExp( thisOptionDeclaration[ thisOptionName ] ) )
					thisOptionDefaultValue = thisOptionDeclaration[ thisOptionName ];
				else
					thisOptionDefaultValue = _.clone( thisOptionDeclaration[ thisOptionName ] );
			}
			else throw new Error( "Each element in the option declarations array must be either a string or an object." );

			if( thisOptionName[ thisOptionName.length - 1 ] === "!" ) {
				thisOptionRequired = true;
				thisOptionName = thisOptionName.slice( 0, thisOptionName.length - 1 );
			}

			normalizedOptionDeclarations[ thisOptionName ] = normalizedOptionDeclarations[ thisOptionName ] || {};
			normalizedOptionDeclarations[ thisOptionName ].required = thisOptionRequired;
			if( ! _.isUndefined( thisOptionDefaultValue ) ) normalizedOptionDeclarations[ thisOptionName ].defaultValue = thisOptionDefaultValue;
		} );

		return normalizedOptionDeclarations;
	}

	return Backbone.ViewOptions;

} ) );
<commit_msg>support option default values as functions<commit_after>/*
 * Backbone.ViewOptions, v0.2.4
 * Copyright (c)2014 Rotunda Software, LLC.
 * Distributed under MIT license
 * http://github.com/rotundasoftware/backbone.viewOptions
*/

( function( root, factory ) {
	if ( typeof define === 'function' && define.amd ) {
		// AMD
		define([ 'underscore', 'backbone' ], factory );
	} else if (typeof exports === 'object') {
		// Node
		module.exports = factory( require( 'underscore' ), require( 'backbone' ) );
	} else {
		// Browser globals
		root.returnExports = factory( root._, root.Backbone );
	}
} ( this, function( _, Backbone ) {
	Backbone.ViewOptions = {};

	Backbone.ViewOptions.add = function( view, optionsDeclarationsProperty ) {
		if( _.isUndefined( optionsDeclarationsProperty ) ) optionsDeclarationsProperty = "options";

		// ****************** Public methods added to view ******************

		view.setOptions = function( options ) {
			var _this = this;
			var optionsThatWereChanged = {};
			var optionsThatWereChangedPreviousValues = {};

			var optionDeclarations = _.result( this, optionsDeclarationsProperty );

			if( _.isString( options ) ) {
				var optionsAsObject = {};
				optionsAsObject[ arguments[ 0 ] ] = arguments[ 1 ];
				options = optionsAsObject;
			}

			if( ! _.isUndefined( optionDeclarations ) ) {
				var normalizedOptionDeclarations = _normalizeOptionDeclarations( optionDeclarations );

				_.each( normalizedOptionDeclarations, function( thisOptionProperties, thisOptionName ) {
					var thisOptionRequired = thisOptionProperties.required;
					var thisOptionDefaultValue = thisOptionProperties.defaultValue;

					if( thisOptionRequired ) {
						// note we do not throw an error if a required option is not supplied, but it is
						// found on the object itself (due to a prior call of view.setOptions, most likely)

						if( ( ! options || ! _.contains( _.keys( options ), thisOptionName ) ) && _.isUndefined( _this[ thisOptionName ] ) )
							throw new Error( "Required option \"" + thisOptionName + "\" was not supplied." );

						if( options && _.contains( _.keys( options ), thisOptionName ) && _.isUndefined( options[ thisOptionName ] ) )
							throw new Error( "Required option \"" + thisOptionName + "\" can not be set to undefined." );
					}

					// attach the supplied value of this option, or the appropriate default value, to the view object
					if( options && thisOptionName in options && ! _.isUndefined( options[ thisOptionName ] ) ) {
						var oldValue = _this[ thisOptionName ];
						var newValue = options[ thisOptionName ];
						// if this option already exists on the view, and the new value is different,
						// make a note that we will be changing it
						if( ! _.isUndefined( oldValue ) && oldValue !== newValue ) {
							optionsThatWereChangedPreviousValues[ thisOptionName ] = oldValue;
							optionsThatWereChanged[ thisOptionName ] = newValue;
						}
						_this[ thisOptionName ] = newValue;
						// note we do NOT delete the option off the options object here so that
						// multiple views can be passed the same options object without issue.
					}
					else if( _.isUndefined( _this[ thisOptionName ] ) ) {
						// note defaults do not write over any existing properties on the view itself.
						_this[ thisOptionName ] = _.isFunction( thisOptionDefaultValue ) ? thisOptionDefaultValue.call( view ) : thisOptionDefaultValue;
					}
				} );
			}

			if( _.keys( optionsThatWereChanged ).length > 0 ) {
				if( _.isFunction( _this.onOptionsChanged ) )
					_this.onOptionsChanged( optionsThatWereChanged, optionsThatWereChangedPreviousValues );
				else if( _.isFunction( _this._onOptionsChanged ) )
					_this._onOptionsChanged( optionsThatWereChanged, optionsThatWereChangedPreviousValues );
			}
		};

		view.getOptions = function( whichOptions ) {
			var optionDeclarations = _.result( this, optionsDeclarationsProperty );
			if( _.isUndefined( optionDeclarations ) ) return {};

			var normalizedOptionDeclarations = _normalizeOptionDeclarations( optionDeclarations );
			var optionsNames = _.keys( normalizedOptionDeclarations );

			if( _.isUndefined( whichOptions ) ) whichOptions = optionsNames;
			else if( _.isString( whichOptions ) ) whichOptions = [ whichOptions ];

			return _.pick( this, optionsNames );
		};
	};

	// ****************** Private Utility Functions ******************

	function _normalizeOptionDeclarations( optionDeclarations ) {
		// convert our short-hand option syntax (with exclamation marks, etc.)
		// to a simple array of standard option declaration objects.

		var normalizedOptionDeclarations = {};

		if( ! _.isArray( optionDeclarations ) ) throw new Error( "Option declarations must be an array." );

		_.each( optionDeclarations, function( thisOptionDeclaration ) {
			var thisOptionName, thisOptionRequired, thisOptionDefaultValue;

			thisOptionRequired = false;
			thisOptionDefaultValue = undefined;

			if( _.isString( thisOptionDeclaration ) )
				thisOptionName = thisOptionDeclaration;
			else if( _.isObject( thisOptionDeclaration ) ) {
				thisOptionName = _.first( _.keys( thisOptionDeclaration ) );
				if( _.isFunction( thisOptionDeclaration[ thisOptionName ] ) || _.isRegExp( thisOptionDeclaration[ thisOptionName ] ) )
					thisOptionDefaultValue = thisOptionDeclaration[ thisOptionName ];
				else
					thisOptionDefaultValue = _.clone( thisOptionDeclaration[ thisOptionName ] );
			}
			else throw new Error( "Each element in the option declarations array must be either a string or an object." );

			if( thisOptionName[ thisOptionName.length - 1 ] === "!" ) {
				thisOptionRequired = true;
				thisOptionName = thisOptionName.slice( 0, thisOptionName.length - 1 );
			}

			normalizedOptionDeclarations[ thisOptionName ] = normalizedOptionDeclarations[ thisOptionName ] || {};
			normalizedOptionDeclarations[ thisOptionName ].required = thisOptionRequired;
			if( ! _.isUndefined( thisOptionDefaultValue ) ) normalizedOptionDeclarations[ thisOptionName ].defaultValue = thisOptionDefaultValue;
		} );

		return normalizedOptionDeclarations;
	}

	return Backbone.ViewOptions;

} ) );
<|endoftext|>


<commit_before>'use strict';

const wrapper = require('./util/result-wrapper');
const Backendless = require('backendless');

const SHUTDOWN_CODE = 32768;
const SHUTDOWN_ACTION = 'SHUTDOWN';

/**
 * @typedef {Object} InitAppData
 * @property {string} secretKey
 * @property {string} url
 * @property {string} appVersionName
 */

/**
 * @typedef {Object} CodeRunnerTask
 * @property {String} ___jsonclass
 * @property {String} applicationId;
 * @property {String} appVersionId;
 * @property {InitAppData} initAppData;
 * @property {Number} timeout
 * @property {String} relativePath
 */

const executor = module.exports = {};

executor.RMI = 'com.backendless.coderunner.commons.protocol.RequestMethodInvocation';
executor.RAI = 'com.backendless.coderunner.commons.protocol.RequestActionInvocation';
executor.RSI = 'com.backendless.coderunner.commons.protocol.RequestServiceInvocation';

const executors = {
  [executor.RMI]: './invoke-handler',
  [executor.RAI]: './invoke-action',
  [executor.RSI]: './invoke-service'
};

/**
 * @param {?Error|ExceptionWrapper|String} err
 * @param {*} result
 * @returns {String}
 */
function invocationResult(err, result) {
  return JSON.stringify(wrapper.invocationResult(err, result));
}

/**
 * @param {CodeRunnerTask} task
 * @returns {Function} task executor
 */
function getTaskExecutor(task) {
  const taskClass = task.___jsonclass;

  if (!executors[taskClass]) {
    throw new Error(`Unknown task [${taskClass}]`);
  }

  return require(executors[taskClass]);
}

/**
 * @param {CodeRunnerTask} task
 */
function initClientSdk(task) {
  if (task.initAppData) {
    Backendless.serverURL = task.initAppData.url;
    Backendless.initApp(task.applicationId, task.initAppData.secretKey, task.initAppData.appVersionName);
  }
}

/**
 * A temporal workaround for http://bugs.backendless.com/browse/BKNDLSS-12041
 *
 * @param {CodeRunnerTask} task
 */
function fixTask(task) {
  if (task.___jsonclass === executor.RMI && task.eventId === SHUTDOWN_CODE) {
    task.___jsonclass = executor.RAI;
    task.actionType = SHUTDOWN_ACTION;
  }
}

/**
 * @param {CodeRunnerTask} task
 * @param {Object} runnerOpts
 * @param {ServerCodeModel=} model
 * @returns {Promise.<string>} InvocationResult JSON string
 */
executor.execute = function(task, runnerOpts, model) {
  return Promise.resolve()
    .then(() => initClientSdk(task))
    .then(() => fixTask(task))
    .then(() => getTaskExecutor(task))
    .then((taskExecutor) => taskExecutor(task, runnerOpts, model))
    .then(result => result && invocationResult(null, result))
    .catch(invocationResult);
};

<commit_msg>refactor (runner) log handler error to the output before sending to Backendless<commit_after>'use strict';

const wrapper = require('./util/result-wrapper');
const logger = require('../../../util/logger');
const Backendless = require('backendless');

const SHUTDOWN_CODE = 32768;
const SHUTDOWN_ACTION = 'SHUTDOWN';

/**
 * @typedef {Object} InitAppData
 * @property {string} secretKey
 * @property {string} url
 * @property {string} appVersionName
 */

/**
 * @typedef {Object} CodeRunnerTask
 * @property {String} ___jsonclass
 * @property {String} applicationId;
 * @property {String} appVersionId;
 * @property {InitAppData} initAppData;
 * @property {Number} timeout
 * @property {String} relativePath
 */

const executor = module.exports = {};

executor.RMI = 'com.backendless.coderunner.commons.protocol.RequestMethodInvocation';
executor.RAI = 'com.backendless.coderunner.commons.protocol.RequestActionInvocation';
executor.RSI = 'com.backendless.coderunner.commons.protocol.RequestServiceInvocation';

const executors = {
  [executor.RMI]: './invoke-handler',
  [executor.RAI]: './invoke-action',
  [executor.RSI]: './invoke-service'
};

/**
 * @param {?Error|ExceptionWrapper|String} err
 * @param {*=} result
 * @returns {String}
 */
function invocationResult(err, result) {
  return JSON.stringify(wrapper.invocationResult(err, result));
}

/**
 * @param {CodeRunnerTask} task
 * @returns {Function} task executor
 */
function getTaskExecutor(task) {
  const taskClass = task.___jsonclass;

  if (!executors[taskClass]) {
    throw new Error(`Unknown task [${taskClass}]`);
  }

  return require(executors[taskClass]);
}

/**
 * @param {CodeRunnerTask} task
 */
function initClientSdk(task) {
  if (task.initAppData) {
    Backendless.serverURL = task.initAppData.url;
    Backendless.initApp(task.applicationId, task.initAppData.secretKey, task.initAppData.appVersionName);
  }
}

/**
 * A temporal workaround for http://bugs.backendless.com/browse/BKNDLSS-12041
 *
 * @param {CodeRunnerTask} task
 */
function fixTask(task) {
  if (task.___jsonclass === executor.RMI && task.eventId === SHUTDOWN_CODE) {
    task.___jsonclass = executor.RAI;
    task.actionType = SHUTDOWN_ACTION;
  }
}

/**
 * @param {CodeRunnerTask} task
 * @param {Object} runnerOpts
 * @param {ServerCodeModel=} model
 * @returns {Promise.<string>} InvocationResult JSON string
 */
executor.execute = function(task, runnerOpts, model) {
  return Promise.resolve()
    .then(() => initClientSdk(task))
    .then(() => fixTask(task))
    .then(() => getTaskExecutor(task))
    .then((taskExecutor) => taskExecutor(task, runnerOpts, model))
    .then(result => result && invocationResult(null, result))
    .catch((err) => {
      logger.error(err);
      return invocationResult(err);
    });
};

<|endoftext|>


<commit_before>'use strict';

var getWrappedService = require('../util/api-gateway-retry-wrapper');
var awsApiGateway = getWrappedService({ apiVersion: '2015-07-09' });
var async = require('async');
var ApiMethodEvent = require('./ApiMethodEvent');
var Constants = require('../Constants');
var logger = require('../util/logger');

var pub = {};

/*
    Due to unexpected issues during method creation a slight delay between operations was introduced.
    The issue at hand is that Amazon responds with ConcurrentModificationsException.
    After discussions with the AWS support it was deemed best to add a slight delay to ensure that each call
    was completed on the server side before executing the next request.
 */
var PUT_METHOD_STEP_DELAY_MILLIS = 100;

pub.getForResponse = function getForResponse(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.getMethod(params, function (error, apiMethod) {
        if (error) {
            logger.log('Error ApiMethodService::getForResponse', { error: error, params: params });
        }
        return callback(error, apiMethod);
    });
};

/* eslint max-nested-callbacks: 0 */
pub.createMethod = function createMethod(parameters, callback) {
    var validParameters = ApiMethodEvent.validateParameters(parameters.params);

    pub.getCorsOrigin(validParameters, function (getError, corsOrigin) {
        if (getError) {
            return callback(getError);
        }

        if (corsOrigin) {
            validParameters = appendCorsProperties(validParameters, corsOrigin);
        }

        putMethod(validParameters, function (putError, apiMethod) {
            if (putError) {
                return callback(putError);
            }
            createIntegration(validParameters, function (integrationError) {
                if (integrationError) {
                    return callback(integrationError);
                }
                createResponses(validParameters, function (responseError) {
                    if (responseError) {
                        return callback(responseError);
                    }
                    createIntegrationResponses(validParameters, function (integrationResponseError) {
                        if (integrationResponseError) {
                            return callback(integrationResponseError);
                        }
                        return callback(undefined, apiMethod);
                    });
                });
            });
        });
    });
};

pub.getMethod = function getMethod(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.getMethod(params, function (error, method) {
        if (error && error.code !== 'NotFoundException') {
            logger.log('Error ApiMethodService::getMethod', { error: error, params: params });
            return callback(error);
        }
        callback(undefined, method);
    });
};

pub.getCorsOrigin = function getCorsOrigin(parameters, callback) {
    // If we are creating an OPTIONS method we don't need to get the origin
    if (parameters.method.httpMethod === Constants.CORS_OPTIONS_METHOD) {
        return callback();
    }

    pub.getMethod(parameters.restApiId, parameters.resourceId, Constants.CORS_OPTIONS_METHOD, function (error, method) {
        if (error) {
            return callback(error);
        }

        var origin;
        if (method && method.methodIntegration && method.methodIntegration.integrationResponses) {
            /* eslint max-len: 0 */
            var allowedMethods = method.methodIntegration.integrationResponses[Constants.CORS_STATUS_CODE].responseParameters['method.response.header.' + Constants.AWS_HEADER_PATHS.ALLOW_METHODS];
            if (allowedMethods.indexOf(parameters.method.httpMethod) > -1) {
                /* eslint max-len: 0 */
                origin = method.methodIntegration.integrationResponses[Constants.CORS_STATUS_CODE].responseParameters['method.response.header.' + Constants.AWS_HEADER_PATHS.ALLOW_ORIGIN];
            }
        }
        callback(error, origin);
    });
};

pub.deleteMethod = function deleteMethod(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.deleteMethod(params, function (error) {
        if (error && error.code !== 'NotFoundException') {
            logger.log('Error ApiMethodService::deleteMethod', { error: error, params: params });
            return callback(error);
        }
        return callback();
    });
};

module.exports = pub;

function putMethod(parameters, callback) {
    var params = {
        restApiId: parameters.restApiId,
        resourceId: parameters.resourceId,
        httpMethod: parameters.method.httpMethod,
        authorizationType: parameters.method.authorizationType,
        apiKeyRequired: parameters.method.apiKeyRequired,
        requestModels: parameters.method.requestModels
    };

    if (parameters.method.authorizationType === 'CUSTOM') {
        params.authorizerId = parameters.method.authorizerId;
    }

    if (parameters.method.parameters) {
        params.requestParameters = {};
        for (var i = 0; i < parameters.method.parameters.length; i++) {
            params.requestParameters['method.request.' + parameters.method.parameters[i]] = true;
        }
    }
    awsApiGateway.putMethod(params, function (error, apiMethod) {
        if (error) {
            logger.log('Error ApiMethodService::putMethod', { error: error, params: params });
        }
        setTimeout(function () {
            return callback(error, apiMethod);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createIntegration(parameters, callback) {
    var params = {
        restApiId: parameters.restApiId,
        resourceId: parameters.resourceId,
        httpMethod: parameters.method.httpMethod,
        type: parameters.integration.type,
        credentials: parameters.integration.credentials,
        integrationHttpMethod: parameters.integration.httpMethod,
        requestParameters: parameters.integration.requestParameters,
        requestTemplates: parameters.integration.requestTemplates,
        uri: parameters.integration.uri
    };
    if (parameters.integration.cacheKeyParameters) {
        params.cacheKeyParameters = parameters.integration.cacheKeyParameters;
        params.cacheNamespace = parameters.integration.cacheNamespace;
    }
    awsApiGateway.putIntegration(params, function (error) {
        if (error) {
            logger.log('Error ApiMethodService::createIntegration', { error: error, params: params });
        }
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createResponses(parameters, callback) {
    async.mapSeries(parameters.responses, function (response, asyncCallback) {
        var params = {
            restApiId: parameters.restApiId,
            resourceId: parameters.resourceId,
            httpMethod: parameters.method.httpMethod,
            statusCode: response.statusCode,
            responseModels: response.responseModels
        };

        if (response.headers) {
            var headers = Object.getOwnPropertyNames(response.headers);
            params.responseParameters = {};
            for (var i = 0; i < headers.length; i++) {
                params.responseParameters['method.response.header.' + headers[i]] = true;
            }
        }

        awsApiGateway.putMethodResponse(params, function (error) {
            if (error) {
                logger.log('Error ApiMethodService::createResponses', { error: error, params: params });
            }
            asyncCallback(error);
        });
    }, function (error) {
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createIntegrationResponses(parameters, callback) {
    async.mapSeries(parameters.responses, function (response, asyncCallback) {
        var params = {
            restApiId: parameters.restApiId,
            resourceId: parameters.resourceId,
            httpMethod: parameters.method.httpMethod,
            statusCode: response.statusCode,
            selectionPattern: response.selectionPattern,
            responseTemplates: response.responseTemplates
        };
        if (response.headers) {
            var headers = Object.getOwnPropertyNames(response.headers);
            params.responseParameters = {};
            for (var i = 0; i < headers.length; i++) {
                params.responseParameters['method.response.header.' + headers[i]] = response.headers[headers[i]];
            }
        }
        awsApiGateway.putIntegrationResponse(params, function (error, data) {
            if (error) {
                logger.log('Error ApiMethodService::createIntegrationResponses', { error: error, params: params });
            }
            asyncCallback(error, data);
        });
    }, function (error) {
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function appendCorsProperties(parameters, corsOrigin) {
    if (parameters.responses) {
        for (var i = 0; i < parameters.responses.length; i++) {
            if (!parameters.responses[i].headers) {
                parameters.responses[i].headers = {};
            }
            parameters.responses[i].headers[Constants.AWS_HEADER_PATHS.ALLOW_ORIGIN] = corsOrigin;
        }
    }
    return parameters;
}
<commit_msg>Error handling on method delete<commit_after>'use strict';

var getWrappedService = require('../util/api-gateway-retry-wrapper');
var awsApiGateway = getWrappedService({ apiVersion: '2015-07-09' });
var async = require('async');
var ApiMethodEvent = require('./ApiMethodEvent');
var Constants = require('../Constants');
var logger = require('../util/logger');

var pub = {};

/*
    Due to unexpected issues during method creation a slight delay between operations was introduced.
    The issue at hand is that Amazon responds with ConcurrentModificationsException.
    After discussions with the AWS support it was deemed best to add a slight delay to ensure that each call
    was completed on the server side before executing the next request.
 */
var PUT_METHOD_STEP_DELAY_MILLIS = 100;

pub.getForResponse = function getForResponse(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.getMethod(params, function (error, apiMethod) {
        if (error) {
            logger.log('Error ApiMethodService::getForResponse', { error: error, params: params });
        }
        return callback(error, apiMethod);
    });
};

/* eslint max-nested-callbacks: 0 */
pub.createMethod = function createMethod(parameters, callback) {
    var validParameters = ApiMethodEvent.validateParameters(parameters.params);

    pub.getCorsOrigin(validParameters, function (getError, corsOrigin) {
        if (getError) {
            return callback(getError);
        }

        if (corsOrigin) {
            validParameters = appendCorsProperties(validParameters, corsOrigin);
        }

        putMethod(validParameters, function (putError, apiMethod) {
            if (putError) {
                return callback(putError);
            }
            createIntegration(validParameters, function (integrationError) {
                if (integrationError) {
                    return callback(integrationError);
                }
                createResponses(validParameters, function (responseError) {
                    if (responseError) {
                        return callback(responseError);
                    }
                    createIntegrationResponses(validParameters, function (integrationResponseError) {
                        if (integrationResponseError) {
                            return callback(integrationResponseError);
                        }
                        return callback(undefined, apiMethod);
                    });
                });
            });
        });
    });
};

pub.getMethod = function getMethod(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.getMethod(params, function (error, method) {
        if (error && error.code !== 'NotFoundException') {
            logger.log('Error ApiMethodService::getMethod', { error: error, params: params });
            return callback(error);
        }
        callback(undefined, method);
    });
};

pub.getCorsOrigin = function getCorsOrigin(parameters, callback) {
    // If we are creating an OPTIONS method we don't need to get the origin
    if (parameters.method.httpMethod === Constants.CORS_OPTIONS_METHOD) {
        return callback();
    }

    pub.getMethod(parameters.restApiId, parameters.resourceId, Constants.CORS_OPTIONS_METHOD, function (error, method) {
        if (error) {
            return callback(error);
        }

        var origin;
        if (method && method.methodIntegration && method.methodIntegration.integrationResponses) {
            /* eslint max-len: 0 */
            var allowedMethods = method.methodIntegration.integrationResponses[Constants.CORS_STATUS_CODE].responseParameters['method.response.header.' + Constants.AWS_HEADER_PATHS.ALLOW_METHODS];
            if (allowedMethods.indexOf(parameters.method.httpMethod) > -1) {
                /* eslint max-len: 0 */
                origin = method.methodIntegration.integrationResponses[Constants.CORS_STATUS_CODE].responseParameters['method.response.header.' + Constants.AWS_HEADER_PATHS.ALLOW_ORIGIN];
            }
        }
        callback(error, origin);
    });
};

pub.deleteMethod = function deleteMethod(restApiId, resourceId, httpMethod, callback) {
    var params = {
        restApiId: restApiId,
        resourceId: resourceId,
        httpMethod: httpMethod
    };
    awsApiGateway.deleteMethod(params, function (error) {
        if (error && error.code !== 'NotFoundException' && error.code !== 'MissingRequiredParameter') {
            logger.log('Error ApiMethodService::deleteMethod', { error: error, params: params });
            return callback(error);
        }
        return callback();
    });
};

module.exports = pub;

function putMethod(parameters, callback) {
    var params = {
        restApiId: parameters.restApiId,
        resourceId: parameters.resourceId,
        httpMethod: parameters.method.httpMethod,
        authorizationType: parameters.method.authorizationType,
        apiKeyRequired: parameters.method.apiKeyRequired,
        requestModels: parameters.method.requestModels
    };

    if (parameters.method.authorizationType === 'CUSTOM') {
        params.authorizerId = parameters.method.authorizerId;
    }

    if (parameters.method.parameters) {
        params.requestParameters = {};
        for (var i = 0; i < parameters.method.parameters.length; i++) {
            params.requestParameters['method.request.' + parameters.method.parameters[i]] = true;
        }
    }
    awsApiGateway.putMethod(params, function (error, apiMethod) {
        if (error) {
            logger.log('Error ApiMethodService::putMethod', { error: error, params: params });
        }
        setTimeout(function () {
            return callback(error, apiMethod);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createIntegration(parameters, callback) {
    var params = {
        restApiId: parameters.restApiId,
        resourceId: parameters.resourceId,
        httpMethod: parameters.method.httpMethod,
        type: parameters.integration.type,
        credentials: parameters.integration.credentials,
        integrationHttpMethod: parameters.integration.httpMethod,
        requestParameters: parameters.integration.requestParameters,
        requestTemplates: parameters.integration.requestTemplates,
        uri: parameters.integration.uri
    };
    if (parameters.integration.cacheKeyParameters) {
        params.cacheKeyParameters = parameters.integration.cacheKeyParameters;
        params.cacheNamespace = parameters.integration.cacheNamespace;
    }
    awsApiGateway.putIntegration(params, function (error) {
        if (error) {
            logger.log('Error ApiMethodService::createIntegration', { error: error, params: params });
        }
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createResponses(parameters, callback) {
    async.mapSeries(parameters.responses, function (response, asyncCallback) {
        var params = {
            restApiId: parameters.restApiId,
            resourceId: parameters.resourceId,
            httpMethod: parameters.method.httpMethod,
            statusCode: response.statusCode,
            responseModels: response.responseModels
        };

        if (response.headers) {
            var headers = Object.getOwnPropertyNames(response.headers);
            params.responseParameters = {};
            for (var i = 0; i < headers.length; i++) {
                params.responseParameters['method.response.header.' + headers[i]] = true;
            }
        }

        awsApiGateway.putMethodResponse(params, function (error) {
            if (error) {
                logger.log('Error ApiMethodService::createResponses', { error: error, params: params });
            }
            asyncCallback(error);
        });
    }, function (error) {
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function createIntegrationResponses(parameters, callback) {
    async.mapSeries(parameters.responses, function (response, asyncCallback) {
        var params = {
            restApiId: parameters.restApiId,
            resourceId: parameters.resourceId,
            httpMethod: parameters.method.httpMethod,
            statusCode: response.statusCode,
            selectionPattern: response.selectionPattern,
            responseTemplates: response.responseTemplates
        };
        if (response.headers) {
            var headers = Object.getOwnPropertyNames(response.headers);
            params.responseParameters = {};
            for (var i = 0; i < headers.length; i++) {
                params.responseParameters['method.response.header.' + headers[i]] = response.headers[headers[i]];
            }
        }
        awsApiGateway.putIntegrationResponse(params, function (error, data) {
            if (error) {
                logger.log('Error ApiMethodService::createIntegrationResponses', { error: error, params: params });
            }
            asyncCallback(error, data);
        });
    }, function (error) {
        setTimeout(function () {
            return callback(error);
        }, PUT_METHOD_STEP_DELAY_MILLIS);
    });
}

function appendCorsProperties(parameters, corsOrigin) {
    if (parameters.responses) {
        for (var i = 0; i < parameters.responses.length; i++) {
            if (!parameters.responses[i].headers) {
                parameters.responses[i].headers = {};
            }
            parameters.responses[i].headers[Constants.AWS_HEADER_PATHS.ALLOW_ORIGIN] = corsOrigin;
        }
    }
    return parameters;
}
<|endoftext|>


<commit_before>var assert = require("assert");

var bitset = function(value,bit) {
  return !!(value & (1 << bit));
};

var bitsSet = function(value) {
  return [0];
};

describe('Diversion', function() {
  it('has a bit that is set', function() {
    assert(bitset(1,0));
    assert(bitset(256,8));
  });

  it('has bits that are not set', function() {
    assert(!bitset(257,7));
  });

  it('returns the binary representation', function() {
    assert.deepEqual([0], bitsSet(0));
    assert.deepEqual([0,1], bitsSet(2));
  });

  it('bit shift operations', function() {
    assert.equal(1, 1 << 0);
    assert.equal(2, 1 << 1);
    assert.equal(4, 1 << 2);
  });
});<commit_msg>failing test case for max number of bits<commit_after>var assert = require("assert");

var bitset = function(value,bit) {
  return !!(value & (1 << bit));
};

var bitsSet = function(value) {
  return [0];
};

describe('Diversion', function() {
  it('has a bit that is set', function() {
    assert(bitset(1,0));
    assert(bitset(256,8));
  });

  it('number of bits within a value', function() {
    assert.equal(8, numberOfBits(256));
  });

  it('has bits that are not set', function() {
    assert(!bitset(257,7));
  });

  it('returns the binary representation', function() {
    assert.deepEqual([0], bitsSet(0));
    assert.deepEqual([0,1], bitsSet(2));
  });

  it('bit shift operations', function() {
    assert.equal(1, 1 << 0);
    assert.equal(2, 1 << 1);
    assert.equal(4, 1 << 2);
  });
});<|endoftext|>


<commit_before>/**
 *  Copyright (C) 2014 3D Repo Ltd
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var mongoose = require('mongoose');
var ModelFactory = require('./factory/modelFactory');
var utils = require('../utils');
var uuid = require('node-uuid');
var _ = require('lodash');
var Schema = mongoose.Schema;
var Mesh = require('./mesh');
var responseCodes = require('../response_codes.js');
var Meta = require('./meta');
var systemLogger = require("../logger.js").systemLogger;
var ifcIdMaps;

var groupSchema = Schema({
	// no extra attributes
	_id: Object,
	objects: [{
		_id : false,
		shared_id: Object,
		account: String,
		model: String,
		ifc_guid: String
	}],
	issue_id: Object,
	color: [Number]
});

groupSchema.statics.ifcGuidsToUUIDs = function(account, model, ifcGuids) {
	const query = { type: "meta", "metadata.IFC GUID": {$in: ifcGuids }};
	const project = { parents: 1, _id: 0 };

	const db = require("../db/db");
	return db.getCollection(account, model+ ".scene").then(dbCol => {
		return dbCol.find(query, project).toArray().then(results => {
			return results;
		});
	});

};

groupSchema.statics.uuidToIfcGuids = function(obj) {
	var account = obj.account;
	var model = obj.model;
	var uid = obj.shared_id;
	if ("[object String]" !== Object.prototype.toString.call(uid)) {
		uid = utils.uuidToString(uid);
	}
	var parent = utils.stringToUUID(uid);
	//Meta.find({ account, model }, { type: "meta", parents: { $in: objects } }, { "parents": 1, "metadata.IFC GUID": 1 })
	return Meta.find({ account, model }, { type: "meta", parents: parent, "metadata.IFC GUID": {$exists: true} }, { "parents": 1, "metadata.IFC GUID": 1 })
		.then(results => {
			let ifcGuids = [];
			results.forEach(res => {
				if (this.isIfcGuid(res.metadata['IFC GUID'])) {
					ifcGuids.push(res.metadata['IFC GUID']);
				}
			});
			return ifcGuids;
		});
};

function uuidsToIfcGuids(account, model, ids) {
	const query = { type: "meta", parents: {$in: ids}, "metadata.IFC GUID": {$exists: true} };
	const project =  { "metadata.IFC GUID": 1 };
	const db = require("../db/db");
	return db.getCollection(account, model+ ".scene").then(dbCol => {
		return dbCol.find(query, project).toArray().then(results => {
			return results;
		});
	});
};

/**
 * IFC Guid definition: [0-9,A-Z,a-z,_$]* (length = 22)
 */
groupSchema.statics.isIfcGuid = function(value) {
	return value && 22 === value.length;
};

groupSchema.statics.findIfcGroupByUID = function(dbCol, uid){
	'use strict';

	// Extract a unique list of IDs only
	let groupObjectsMap = [];

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) })
		.then(group => {
			let ifcGuidPromises = [];

			for (let i = 0; group && i < group.objects.length; i++) {
				const obj = group.objects[i];
				if (obj.ifc_guid) {
					groupObjectsMap[obj.ifc_guid] = obj;
				}
				if (obj.shared_id) {
					// Convert sharedIds to IFC Guids
					ifcGuidPromises.push(
						this.uuidToIfcGuids(obj).then(ifcGuids => {
							if (ifcGuids && ifcGuids.length > 0) {
								for (let i = 0; i < ifcGuids.length; i++) {
									obj.ifc_guid = ifcGuids[i];
									delete obj.shared_id;
									groupObjectsMap[obj.ifc_guid] = obj;
								}
							} else {
								groupObjectsMap[obj.shared_id] = obj;
							}
						})
					);
				}
			}

			return Promise.all(ifcGuidPromises).then(() => {
				if (groupObjectsMap && groupObjectsMap.length > 0) {
					group.objects = [];
					for (let id in groupObjectsMap) {
						group.objects.push(groupObjectsMap[id]);
					}
				}
				return group;
			});
		});
};

groupSchema.statics.findByUID = function(dbCol, uid){
	'use strict';

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) })
		.then(group => {
			const sharedIdObjects = [];
			const sharedIdPromises = [];
			const ifcObjectByAccount = {};

			for (let i = 0; i < group.objects.length; i++) {
				if (this.isIfcGuid(group.objects[i].ifc_guid)) {
					const namespace = group.objects[i].account + "__" + group.objects[i].model;
					if(!ifcObjectByAccount[namespace]) {
						ifcObjectByAccount[namespace] = [];
					}
					ifcObjectByAccount[namespace].push(group.objects[i].ifc_guid);
				}
				else {
					sharedIdObjects.push(group.objects[i]);
				}

			}

			for (let namespace in ifcObjectByAccount) {
				const nsSplitArr = namespace.split("__");
				const account = nsSplitArr[0];
				const model = nsSplitArr[1];
				if(account && model) {
					sharedIdPromises.push(this.ifcGuidsToUUIDs(account, model,
						ifcObjectByAccount[namespace]).then(results => {
						for (let i = 0; i < results.length; i++) {
							results[i].parents.forEach( id => {
								sharedIdObjects.push({account, model, shared_id: utils.uuidToString(id)});
							});
						}
					}));
				}

			}

			return Promise.all(sharedIdPromises).then(() => {
				let returnGroup = { _id: utils.uuidToString(group._id), color: group.color}
				returnGroup.objects = sharedIdObjects;
				return returnGroup;
			});
		});
};

groupSchema.statics.listGroups = function(dbCol){
	'use strict';

	return this.find(dbCol, {});
};

groupSchema.statics.updateIssueId = function(dbCol, uid, issueId) {
	'use strict';

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) }).then(group => {
		const issueIdData = {
			issue_id: issueId
		};

		return group.updateAttrs(issueIdData);
	});
};

groupSchema.methods.updateAttrs = function(data){
	'use strict';

	const ifcGuidPromises = [];
	const sharedIdsByAccount = {};
	let modifiedObjectList = null;

	if (data.objects) {
		modifiedObjectList = []
		for (let i = 0; i < data.objects.length; i++) {
			const obj = data.objects[i];

			if (obj.shared_id) {
				const ns = obj.account + "__" + obj.model;
				if ("[object String]" === Object.prototype.toString.call(obj.id)) {
					obj.id = utils.stringToUUID(obj.shared_id);
				}
				if(!sharedIdsByAccount[ns]) {
					sharedIdsByAccount[ns] = { sharedIDArr : [], org: []};
				}
				sharedIdsByAccount[ns].sharedIDArr.push(obj.id);
				sharedIdsByAccount[ns].org.push(obj);

			}
			else {
				modifiedObjectList.push(obj);
			}
		}

		for (let namespace in sharedIdsByAccount) {
			const nsSplitArr = namespace.split("__");
			const account = nsSplitArr[0];
			const model = nsSplitArr[1];
			ifcGuidPromises.push(
				uuidsToIfcGuids(account, model, sharedIdsByAccount[namespace].sharedIDArr).then(ifcGuids => {
					if (ifcGuids && ifcGuids.length > 0) {
						for (let i = 0; i < ifcGuids.length; i++) {
							modifiedObjectList.push({account, model, ifc_guid: ifcGuids[i].metadata["IFC GUID"]});
						}
					}
					else {
						//this isn't a IFC GUID model.
						modifiedObjectList = modifiedObjectList.concat(sharedIdsByAccount[namespace].org);
					}

				})
			);
		}

	}

	return Promise.all(ifcGuidPromises).then(() => {

		this.name = data.name || this.name;
		this.objects = modifiedObjectList || this.objects;
		this.color = data.color || this.color;
		this.issue_id = data.issue_id || this.issue_id;

		this.markModified('objects');
		return this.save();
	});
};

groupSchema.statics.createGroup = function(dbCol, data){
	'use strict';

	let group = this.model('Group').createInstance({
		account: dbCol.account,
		model: dbCol.model
	});

	group._id = utils.stringToUUID(uuid.v1());
	return group.updateAttrs(data);
};

groupSchema.methods.clean = function(){
	'use strict';

	let cleaned = this.toObject();
	cleaned._id = utils.uuidToString(cleaned._id);
	cleaned.issue_id = cleaned.issue_id && utils.uuidToString(cleaned.issue_id);
	if (cleaned.objects) {
		for (let i = 0; i < cleaned.objects.length; i++) {
			const object = cleaned.objects[i];
			if (object.shared_id &&
				"[object String]" !== Object.prototype.toString.call(object.shared_id)) {
				//object.id = utils.uuidToString(object.id);
				object.shared_id = utils.uuidToString(object.shared_id);
			}
		}
	}
	return cleaned;

};

groupSchema.statics.deleteGroup = function(dbCol, id){
	'use strict';

	return Group.findOneAndRemove(dbCol, { _id : utils.stringToUUID(id)}).then(group => {

		if(!group){
			return Promise.reject(responseCodes.GROUP_NOT_FOUND);
		}

		let removePromises = [];

		group.objects.forEach(obj => removePromises.push(
			Mesh.removeGroup(
				obj.account,
				obj.model,
				utils.uuidToString(obj.id),
				id
			)
		));

		return Promise.all(removePromises);

	});
};

var Group = ModelFactory.createClass(
	'Group',
	groupSchema,
	arg => {
		return `${arg.model}.groups`;
	}
);

module.exports = Group;
<commit_msg>ISSUE #848 convert shared_ids  to string before passing it back<commit_after>/**
 *  Copyright (C) 2014 3D Repo Ltd
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var mongoose = require('mongoose');
var ModelFactory = require('./factory/modelFactory');
var utils = require('../utils');
var uuid = require('node-uuid');
var _ = require('lodash');
var Schema = mongoose.Schema;
var Mesh = require('./mesh');
var responseCodes = require('../response_codes.js');
var Meta = require('./meta');
var systemLogger = require("../logger.js").systemLogger;
var ifcIdMaps;

var groupSchema = Schema({
	// no extra attributes
	_id: Object,
	objects: [{
		_id : false,
		shared_id: Object,
		account: String,
		model: String,
		ifc_guid: String
	}],
	issue_id: Object,
	color: [Number]
});

groupSchema.statics.ifcGuidsToUUIDs = function(account, model, ifcGuids) {
	const query = { type: "meta", "metadata.IFC GUID": {$in: ifcGuids }};
	const project = { parents: 1, _id: 0 };

	const db = require("../db/db");
	return db.getCollection(account, model+ ".scene").then(dbCol => {
		return dbCol.find(query, project).toArray().then(results => {
			return results;
		});
	});

};

groupSchema.statics.uuidToIfcGuids = function(obj) {
	var account = obj.account;
	var model = obj.model;
	var uid = obj.shared_id;
	if ("[object String]" !== Object.prototype.toString.call(uid)) {
		uid = utils.uuidToString(uid);
	}
	var parent = utils.stringToUUID(uid);
	//Meta.find({ account, model }, { type: "meta", parents: { $in: objects } }, { "parents": 1, "metadata.IFC GUID": 1 })
	return Meta.find({ account, model }, { type: "meta", parents: parent, "metadata.IFC GUID": {$exists: true} }, { "parents": 1, "metadata.IFC GUID": 1 })
		.then(results => {
			let ifcGuids = [];
			results.forEach(res => {
				if (this.isIfcGuid(res.metadata['IFC GUID'])) {
					ifcGuids.push(res.metadata['IFC GUID']);
				}
			});
			return ifcGuids;
		});
};

function uuidsToIfcGuids(account, model, ids) {
	const query = { type: "meta", parents: {$in: ids}, "metadata.IFC GUID": {$exists: true} };
	const project =  { "metadata.IFC GUID": 1 };
	const db = require("../db/db");
	return db.getCollection(account, model+ ".scene").then(dbCol => {
		return dbCol.find(query, project).toArray().then(results => {
			return results;
		});
	});
};

/**
 * IFC Guid definition: [0-9,A-Z,a-z,_$]* (length = 22)
 */
groupSchema.statics.isIfcGuid = function(value) {
	return value && 22 === value.length;
};

groupSchema.statics.findIfcGroupByUID = function(dbCol, uid){
	'use strict';

	// Extract a unique list of IDs only
	let groupObjectsMap = [];

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) })
		.then(group => {
			let ifcGuidPromises = [];

			for (let i = 0; group && i < group.objects.length; i++) {
				const obj = group.objects[i];
				if (obj.ifc_guid) {
					groupObjectsMap[obj.ifc_guid] = obj;
				}
				if (obj.shared_id) {
					// Convert sharedIds to IFC Guids
					ifcGuidPromises.push(
						this.uuidToIfcGuids(obj).then(ifcGuids => {
							if (ifcGuids && ifcGuids.length > 0) {
								for (let i = 0; i < ifcGuids.length; i++) {
									obj.ifc_guid = ifcGuids[i];
									delete obj.shared_id;
									groupObjectsMap[obj.ifc_guid] = obj;
								}
							} else {
								groupObjectsMap[obj.shared_id] = obj;
							}
						})
					);
				}
			}

			return Promise.all(ifcGuidPromises).then(() => {
				if (groupObjectsMap && groupObjectsMap.length > 0) {
					group.objects = [];
					for (let id in groupObjectsMap) {
						group.objects.push(groupObjectsMap[id]);
					}
				}
				return group;
			});
		});
};

groupSchema.statics.findByUID = function(dbCol, uid){
	'use strict';

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) })
		.then(group => {
			const sharedIdObjects = [];
			const sharedIdPromises = [];
			const ifcObjectByAccount = {};

			for (let i = 0; i < group.objects.length; i++) {
				if (this.isIfcGuid(group.objects[i].ifc_guid)) {
					const namespace = group.objects[i].account + "__" + group.objects[i].model;
					if(!ifcObjectByAccount[namespace]) {
						ifcObjectByAccount[namespace] = [];
					}
					ifcObjectByAccount[namespace].push(group.objects[i].ifc_guid);
				}
				else {
					group.objects[i].shared_id = utils.uuidToString(group.objects[i].shared_id);
					sharedIdObjects.push(group.objects[i]);
				}

			}

			for (let namespace in ifcObjectByAccount) {
				const nsSplitArr = namespace.split("__");
				const account = nsSplitArr[0];
				const model = nsSplitArr[1];
				if(account && model) {
					sharedIdPromises.push(this.ifcGuidsToUUIDs(account, model,
						ifcObjectByAccount[namespace]).then(results => {
						for (let i = 0; i < results.length; i++) {
							results[i].parents.forEach( id => {
								sharedIdObjects.push({account, model, shared_id: utils.uuidToString(id)});
							});
						}
					}));
				}

			}

			return Promise.all(sharedIdPromises).then(() => {
				let returnGroup = { _id: utils.uuidToString(group._id), color: group.color}
				returnGroup.objects = sharedIdObjects;
				return returnGroup;
			});
		});
};

groupSchema.statics.listGroups = function(dbCol){
	'use strict';

	return this.find(dbCol, {});
};

groupSchema.statics.updateIssueId = function(dbCol, uid, issueId) {
	'use strict';

	return this.findOne(dbCol, { _id: utils.stringToUUID(uid) }).then(group => {
		const issueIdData = {
			issue_id: issueId
		};

		return group.updateAttrs(issueIdData);
	});
};

groupSchema.methods.updateAttrs = function(data){
	'use strict';

	const ifcGuidPromises = [];
	const sharedIdsByAccount = {};
	let modifiedObjectList = null;

	if (data.objects) {
		modifiedObjectList = []
		for (let i = 0; i < data.objects.length; i++) {
			const obj = data.objects[i];

			if (obj.shared_id) {
				const ns = obj.account + "__" + obj.model;
				if ("[object String]" === Object.prototype.toString.call(obj.id)) {
					obj.id = utils.stringToUUID(obj.shared_id);
				}
				if(!sharedIdsByAccount[ns]) {
					sharedIdsByAccount[ns] = { sharedIDArr : [], org: []};
				}
				sharedIdsByAccount[ns].sharedIDArr.push(obj.id);
				sharedIdsByAccount[ns].org.push(obj);

			}
			else {
				modifiedObjectList.push(obj);
			}
		}

		for (let namespace in sharedIdsByAccount) {
			const nsSplitArr = namespace.split("__");
			const account = nsSplitArr[0];
			const model = nsSplitArr[1];
			ifcGuidPromises.push(
				uuidsToIfcGuids(account, model, sharedIdsByAccount[namespace].sharedIDArr).then(ifcGuids => {
					if (ifcGuids && ifcGuids.length > 0) {
						for (let i = 0; i < ifcGuids.length; i++) {
							modifiedObjectList.push({account, model, ifc_guid: ifcGuids[i].metadata["IFC GUID"]});
						}
					}
					else {
						//this isn't a IFC GUID model.
						modifiedObjectList = modifiedObjectList.concat(sharedIdsByAccount[namespace].org);
					}

				})
			);
		}

	}

	return Promise.all(ifcGuidPromises).then(() => {

		this.name = data.name || this.name;
		this.objects = modifiedObjectList || this.objects;
		this.color = data.color || this.color;
		this.issue_id = data.issue_id || this.issue_id;

		this.markModified('objects');
		return this.save();
	});
};

groupSchema.statics.createGroup = function(dbCol, data){
	'use strict';

	let group = this.model('Group').createInstance({
		account: dbCol.account,
		model: dbCol.model
	});

	group._id = utils.stringToUUID(uuid.v1());
	return group.updateAttrs(data);
};

groupSchema.methods.clean = function(){
	'use strict';

	let cleaned = this.toObject();
	cleaned._id = utils.uuidToString(cleaned._id);
	cleaned.issue_id = cleaned.issue_id && utils.uuidToString(cleaned.issue_id);
	if (cleaned.objects) {
		for (let i = 0; i < cleaned.objects.length; i++) {
			const object = cleaned.objects[i];
			if (object.shared_id &&
				"[object String]" !== Object.prototype.toString.call(object.shared_id)) {
				//object.id = utils.uuidToString(object.id);
				object.shared_id = utils.uuidToString(object.shared_id);
			}
		}
	}
	return cleaned;

};

groupSchema.statics.deleteGroup = function(dbCol, id){
	'use strict';

	return Group.findOneAndRemove(dbCol, { _id : utils.stringToUUID(id)}).then(group => {

		if(!group){
			return Promise.reject(responseCodes.GROUP_NOT_FOUND);
		}

		let removePromises = [];

		group.objects.forEach(obj => removePromises.push(
			Mesh.removeGroup(
				obj.account,
				obj.model,
				utils.uuidToString(obj.id),
				id
			)
		));

		return Promise.all(removePromises);

	});
};

var Group = ModelFactory.createClass(
	'Group',
	groupSchema,
	arg => {
		return `${arg.model}.groups`;
	}
);

module.exports = Group;
<|endoftext|>


<commit_before>const express = require('express');
const jwt = require('jsonwebtoken');
const logger = require('winston');
const config = require('../../config');
const realmModel = require('../models/realm');
const realmManager = require('../managers/realmmanager');
const leaseManager = require('../managers/leasemanager');
const rentManager = require('../managers/rentmanager');
const occupantManager = require('../managers/occupantmanager');
const documentManager = require('../managers/documentmanager');
const templateManager = require('../managers/templatemanager');
const propertyManager = require('../managers/propertymanager');
const ownerManager = require('../managers/ownermanager');
const notificationManager = require('../managers/notificationmanager');
const accountingManager = require('../managers/accountingmanager');
const emailManager = require('../managers/emailmanager');

module.exports = function () {
  const router = express.Router();

  // protect the api access by checking the access token
  router.use((req, res, next) => {
    if (!req.headers.authorization) {
      return res.sendStatus(403);
    }

    const accessToken = req.headers.authorization.split(' ')[1];
    if (!accessToken) {
      return res.sendStatus(403);
    }

    try {
      const decoded = jwt.verify(accessToken, config.ACCESS_TOKEN_SECRET);
      req.user = decoded.account;
    } catch (err) {
      logger.warn(err);
      return res.sendStatus(403);
    }

    next();
  });

  // update req with the user organizations
  router.use((req, res, next) => {
    if (req.path !== '/realms' && !req.headers.organizationid) {
      return res.sendStatus(404);
    }

    realmModel.findByEmail(req.user.email, (err, realms = []) => {
      if (err) {
        return next(err);
      }
      if (realms.length) {
        req.realms = realms;
        const realmId = req.headers.organizationid;
        if (realmId) {
          req.realm = req.realms.find(
            (realm) => realm._id.toString() === realmId
          );
          if (req.path !== '/realms' && !req.realm) {
            return res.sendStatus(404);
          }
        }
      } else {
        delete req.realm;
        delete req.realms;
      }
      next();
    });
  });

  const realmsRouter = express.Router();
  realmsRouter.get('/', realmManager.all);
  realmsRouter.get('/:id', realmManager.one);
  realmsRouter.post('/', realmManager.add);
  realmsRouter.patch('/:id', realmManager.update);
  router.use('/realms', realmsRouter);

  const leasesRouter = express.Router();
  leasesRouter.get('/', leaseManager.all);
  leasesRouter.get('/:id', leaseManager.one);
  leasesRouter.post('/', leaseManager.add);
  leasesRouter.patch('/:id', leaseManager.update);
  leasesRouter.delete('/:ids', leaseManager.remove);
  router.use('/leases', leasesRouter);

  const occupantsRouter = express.Router();
  occupantsRouter.get('/', occupantManager.all);
  occupantsRouter.get('/:id', occupantManager.one);
  occupantsRouter.post('/', occupantManager.add);
  occupantsRouter.patch('/:id', occupantManager.update);
  occupantsRouter.delete('/:ids', occupantManager.remove);
  router.use('/tenants', occupantsRouter);

  const documentsRouter = express.Router();
  documentsRouter.get('/:document/:id/:term', documentManager.get);
  documentsRouter.patch('/:id', documentManager.update);
  router.use('/documents', documentsRouter);

  const templatesRouter = express.Router();
  templatesRouter.get('/', templateManager.all);
  templatesRouter.get('/:id', templateManager.one);
  templatesRouter.post('/', templateManager.add);
  templatesRouter.put('/', templateManager.update);
  templatesRouter.delete('/:ids', templateManager.remove);
  router.use('/templates', templatesRouter);

  const notificationsRouter = express.Router();
  notificationsRouter.get('/', notificationManager.all);
  router.use('/notifications', notificationsRouter);

  const rentsRouter = express.Router();
  rentsRouter.patch('/payment/:id/:term', rentManager.updateByTerm);
  rentsRouter.get('/tenant/:id', rentManager.rentsOfOccupant);
  rentsRouter.get('/tenant/:id/:term', rentManager.rentOfOccupantByTerm);
  rentsRouter.get('/:year/:month', rentManager.all);
  router.use('/rents', rentsRouter);

  const propertiesRouter = express.Router();
  propertiesRouter.get('/', propertyManager.all);
  propertiesRouter.get('/:id', propertyManager.one);
  propertiesRouter.post('/', propertyManager.add);
  propertiesRouter.patch('/:id', propertyManager.update);
  propertiesRouter.delete('/:ids', propertyManager.remove);
  router.use('/properties', propertiesRouter);

  router.get('/accounting/:year', accountingManager.all);

  const ownerRouter = express.Router();
  ownerRouter.get('/', ownerManager.all);
  ownerRouter.patch('/:id', ownerManager.update);
  router.use('/owner', ownerRouter);

  const emailRouter = express.Router();
  emailRouter.post('/', emailManager.send);
  router.use('/emails', emailRouter);

  const apiRouter = express.Router();
  apiRouter.use('/api/v2', router);
  return apiRouter;
};
<commit_msg>fixed issue on organization/realm when db is empty<commit_after>const express = require('express');
const jwt = require('jsonwebtoken');
const logger = require('winston');
const config = require('../../config');
const realmModel = require('../models/realm');
const realmManager = require('../managers/realmmanager');
const leaseManager = require('../managers/leasemanager');
const rentManager = require('../managers/rentmanager');
const occupantManager = require('../managers/occupantmanager');
const documentManager = require('../managers/documentmanager');
const templateManager = require('../managers/templatemanager');
const propertyManager = require('../managers/propertymanager');
const ownerManager = require('../managers/ownermanager');
const notificationManager = require('../managers/notificationmanager');
const accountingManager = require('../managers/accountingmanager');
const emailManager = require('../managers/emailmanager');

module.exports = function () {
  const router = express.Router();

  // protect the api access by checking the access token
  router.use((req, res, next) => {
    if (!req.headers.authorization) {
      return res.sendStatus(403);
    }

    const accessToken = req.headers.authorization.split(' ')[1];
    if (!accessToken) {
      return res.sendStatus(403);
    }

    try {
      const decoded = jwt.verify(accessToken, config.ACCESS_TOKEN_SECRET);
      req.user = decoded.account;
    } catch (err) {
      logger.warn(err);
      return res.sendStatus(403);
    }

    next();
  });

  // update req with the user organizations
  router.use((req, res, next) => {
    if (req.path !== '/realms' && !req.headers.organizationid) {
      return res.sendStatus(404);
    }

    realmModel.findByEmail(req.user.email, (err, realms = []) => {
      if (err) {
        return next(err);
      }
      if (realms && realms.length) {
        req.realms = realms;
        const realmId = req.headers.organizationid;
        if (realmId) {
          req.realm = req.realms.find(
            (realm) => realm._id.toString() === realmId
          );
          if (req.path !== '/realms' && !req.realm) {
            return res.sendStatus(404);
          }
        }
      } else {
        delete req.realm;
        req.realms = [];
      }
      next();
    });
  });

  const realmsRouter = express.Router();
  realmsRouter.get('/', realmManager.all);
  realmsRouter.get('/:id', realmManager.one);
  realmsRouter.post('/', realmManager.add);
  realmsRouter.patch('/:id', realmManager.update);
  router.use('/realms', realmsRouter);

  const leasesRouter = express.Router();
  leasesRouter.get('/', leaseManager.all);
  leasesRouter.get('/:id', leaseManager.one);
  leasesRouter.post('/', leaseManager.add);
  leasesRouter.patch('/:id', leaseManager.update);
  leasesRouter.delete('/:ids', leaseManager.remove);
  router.use('/leases', leasesRouter);

  const occupantsRouter = express.Router();
  occupantsRouter.get('/', occupantManager.all);
  occupantsRouter.get('/:id', occupantManager.one);
  occupantsRouter.post('/', occupantManager.add);
  occupantsRouter.patch('/:id', occupantManager.update);
  occupantsRouter.delete('/:ids', occupantManager.remove);
  router.use('/tenants', occupantsRouter);

  const documentsRouter = express.Router();
  documentsRouter.get('/:document/:id/:term', documentManager.get);
  documentsRouter.patch('/:id', documentManager.update);
  router.use('/documents', documentsRouter);

  const templatesRouter = express.Router();
  templatesRouter.get('/', templateManager.all);
  templatesRouter.get('/:id', templateManager.one);
  templatesRouter.post('/', templateManager.add);
  templatesRouter.put('/', templateManager.update);
  templatesRouter.delete('/:ids', templateManager.remove);
  router.use('/templates', templatesRouter);

  const notificationsRouter = express.Router();
  notificationsRouter.get('/', notificationManager.all);
  router.use('/notifications', notificationsRouter);

  const rentsRouter = express.Router();
  rentsRouter.patch('/payment/:id/:term', rentManager.updateByTerm);
  rentsRouter.get('/tenant/:id', rentManager.rentsOfOccupant);
  rentsRouter.get('/tenant/:id/:term', rentManager.rentOfOccupantByTerm);
  rentsRouter.get('/:year/:month', rentManager.all);
  router.use('/rents', rentsRouter);

  const propertiesRouter = express.Router();
  propertiesRouter.get('/', propertyManager.all);
  propertiesRouter.get('/:id', propertyManager.one);
  propertiesRouter.post('/', propertyManager.add);
  propertiesRouter.patch('/:id', propertyManager.update);
  propertiesRouter.delete('/:ids', propertyManager.remove);
  router.use('/properties', propertiesRouter);

  router.get('/accounting/:year', accountingManager.all);

  const ownerRouter = express.Router();
  ownerRouter.get('/', ownerManager.all);
  ownerRouter.patch('/:id', ownerManager.update);
  router.use('/owner', ownerRouter);

  const emailRouter = express.Router();
  emailRouter.post('/', emailManager.send);
  router.use('/emails', emailRouter);

  const apiRouter = express.Router();
  apiRouter.use('/api/v2', router);
  return apiRouter;
};
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var admin = require("firebase-admin");

var client_id = 'SywgbqwVSP4an161TdKS';
var client_secret = 'DhTfuacMl_';
var state = "RANDOM_STATE";
var redirectURI = encodeURI("http://localhost:3000/users/auth");
var api_url = "";

router.get('/naverlogin', function (req, res) {
  api_url = 'https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirectURI + '&state=' + state;
  res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
  res.end("");
});

router.get('/auth', function(req, res, next) {
  code = req.query.code;
  state = req.query.state;
  api_url = 'https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id='
      + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirectURI + '&code=' + code + '&state=' + state;
  var request = require('request');
  var options = {
    url: api_url,
    headers: {'X-Naver-Client-Id':client_id, 'X-Naver-Client-Secret': client_secret}
  };

  request.get(options, (error, response, body) => {
    if (!error && response.statusCode == 200) {
      req.session.token = JSON.parse(body).access_token;
      var options = {
        url: 'https://openapi.naver.com/v1/nid/me',
        headers: {'Authorization': "Bearer " + req.session.token}
      };
      request.get(options, function (error, response, body) {
        if (!error && response.statusCode == 200) {
          const email = JSON.parse(body).response.email;
          admin.database().ref('users').orderByChild('email').equalTo(email).once('value')
            .then((user) => {
              if (user.val() === null) {
                console.log("new user");
                const newRef = admin.database().ref('users').push();
                const newKey = newRef.key;
                const userObj = {
                  id: newKey,
                  email: email
                };
                newRef.set(userObj)
                  .then(() => {
                    req.session.userId = newKey;
                    res.status(200).json({ id: req.session.userId });
                  })
                  .catch((err) => {
                    res.status(400).json({ err_msg: err.message });
                  });
              } else {
                console.log("exists");
                req.session.userId = Object.keys(user.val())[0];
                res.status(200).json({ id: req.session.userId });
              }
            });
        } else {
          console.log('error');
          if(response != null) {
            res.status(response.statusCode).end();
            console.log('error in signin = ' + response.statusCode);
          }
        }
      });
    } else {
      res.status(response.statusCode).end();
      console.log('error = ' + response.statusCode);
    }
  });
});

router.get('/add', function(req, res, next) {
  const newRef = admin.database().ref('users').push();
  const newKey = newRef.key;
  const userObj = {
    id: newKey,
    name: "sarah"
  };
  newRef.set(userObj)
    .then(() => {
      return admin.database().ref('users').child(newKey).once('value');
    })
    .then((snap) => res.status(200).json(snap))
    .catch((err) => {
      res.status(400).json({ err_msg: err.message });
    });
});

router.post('/account', function(req, res, next) {
  const id = "-KqCstYTIIxeecYJa8-n"; // req.session.userId;
  admin.database().ref('users').orderByChild('id').equalTo(id).once('value')
    .then((user) => {
      if (user.val() === null) {
        throw new Error("No matching user");
      }
      const vals = Object.keys(user.val())
        .map((key) => user.val()[key]);
      return vals[0];
    })
    .then((val) => {
      return admin.database().ref('users').child(val.id).set({
        id: id,
        email: val.email,
        bank: req.body.bank,
        accountNumber: req.body.accountNumber
      });
    })
    .then(() => {
      res.status(200).json({ message: "success adding account info"});
    })
    .catch((err) => {
      res.status(400).json({ err_msg: err.message });
    });
});

module.exports = router;
<commit_msg>사용자 세션 활성 상태 조회<commit_after>var express = require('express');
var router = express.Router();
var admin = require("firebase-admin");

var client_id = 'SywgbqwVSP4an161TdKS';
var client_secret = 'DhTfuacMl_';
var state = "RANDOM_STATE";
var redirectURI = encodeURI("http://localhost:3000/users/auth");
var api_url = "";

router.get('/naverlogin', function (req, res) {
  api_url = 'https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirectURI + '&state=' + state;
  res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
  res.end("");
});

router.get('/auth', function(req, res, next) {
  code = req.query.code;
  state = req.query.state;
  api_url = 'https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id='
      + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirectURI + '&code=' + code + '&state=' + state;
  var request = require('request');
  var options = {
    url: api_url,
    headers: {'X-Naver-Client-Id':client_id, 'X-Naver-Client-Secret': client_secret}
  };

  request.get(options, (error, response, body) => {
    if (!error && response.statusCode == 200) {
      req.session.token = JSON.parse(body).access_token;
      var options = {
        url: 'https://openapi.naver.com/v1/nid/me',
        headers: {'Authorization': "Bearer " + req.session.token}
      };
      request.get(options, function (error, response, body) {
        if (!error && response.statusCode == 200) {
          const email = JSON.parse(body).response.email;
          admin.database().ref('users').orderByChild('email').equalTo(email).once('value')
            .then((user) => {
              if (user.val() === null) {
                console.log("new user");
                const newRef = admin.database().ref('users').push();
                const newKey = newRef.key;
                const userObj = {
                  id: newKey,
                  email: email
                };
                newRef.set(userObj)
                  .then(() => {
                    req.session.userId = newKey;
                    res.status(200).json({ id: req.session.userId });
                  })
                  .catch((err) => {
                    res.status(400).json({ err_msg: err.message });
                  });
              } else {
                console.log("exists");
                req.session.userId = Object.keys(user.val())[0];
                res.status(200).json({ id: req.session.userId });
              }
            });
        } else {
          console.log('error');
          if(response != null) {
            res.status(response.statusCode).end();
            console.log('error in signin = ' + response.statusCode);
          }
        }
      });
    } else {
      res.status(response.statusCode).end();
      console.log('error = ' + response.statusCode);
    }
  });
});

router.get('/add', function(req, res, next) {
  const newRef = admin.database().ref('users').push();
  const newKey = newRef.key;
  const userObj = {
    id: newKey,
    name: "sarah"
  };
  newRef.set(userObj)
    .then(() => {
      return admin.database().ref('users').child(newKey).once('value');
    })
    .then((snap) => res.status(200).json(snap))
    .catch((err) => {
      res.status(400).json({ err_msg: err.message });
    });
});

router.post('/account', function(req, res, next) {
  const id = "-KqCstYTIIxeecYJa8-n"; // req.session.userId;
  admin.database().ref('users').orderByChild('id').equalTo(id).once('value')
    .then((user) => {
      if (user.val() === null) {
        throw new Error("No matching user");
      }
      const vals = Object.keys(user.val())
        .map((key) => user.val()[key]);
      return vals[0];
    })
    .then((val) => {
      return admin.database().ref('users').child(val.id).set({
        id: id,
        email: val.email,
        bank: req.body.bank,
        accountNumber: req.body.accountNumber
      });
    })
    .then(() => {
      res.status(200).json({ message: "success adding account info"});
    })
    .catch((err) => {
      res.status(400).json({ err_msg: err.message });
    });
});

router.get('/session', function(req, res, next) {
  if (req.session.userId) {
    res.status(200).json({ message: "active!" })
  } else {
    res.status(400).json({ message: "inactive!" });
  }
});

module.exports = router;
<|endoftext|>


<commit_before>$.ajaxSetup({
  async: false
});

var activeView = 0,
    loadedViews = [];

$.get("http://127.0.0.1:1338/views", function (ajaxViews) {
    loadedViews = ajaxViews;

	$('.nav-sidebar').html("");

	loadedViews.forEach(function(view, index){
		$(".nav-sidebar").append(`<li data-view="` + index + `"><a href="#" onclick="loadData('` + index + `')">` + view.name + `</a></li>`);
	});

    loadData(0);
});

function loadData(index) {
    $('#findTeamNumber').val($('#findTeamNumber').val);
    $('#findEvent').val($('#findEvent').val);

	if(!index)
		index = activeView
	else
		activeView = index;

	$("li[data-view]").removeClass("active");
	$("li[data-view=" + index + "]").addClass("active");

    $(".main").html("");

	loadedViews[index].views.forEach(function(rootView){
        $.get("http://127.0.0.1:1338/data/" + rootView.name, function (views) {
            views.forEach(function (view) {
                var table = `<h3>` + view.name + `</h3>`;
                table += `<div class="table-responsive"><table class="table table-striped" id="` + rootView.name + `"><thead><tr>`;

                view.headers.forEach(function (header) {
                    table += "" + header.text + "	";
                });

                table += `</tr></thead><tbody>`;

                view.data.forEach(function (datum) {
                    table += `<tr>`;

                    view.headers.forEach(function (header) {
                        var val = getDescendantProp(datum, header.value);

                        table += "" + renderTypes(val) + "	";
                    });

                    table += `</tr>`;
                });

                table += `</tbody></table></div><hr>`

                $(".main").append(table);

                 $('table#' + rootView.name).DataTable({
                    "paging": rootView.disablePaging ? !rootView.disablePaging : true,
                    "searching": rootView.disableSearching ? !rootView.disableSearching : true,
                    "info": rootView.disableInfo ? !rootView.disableInfo : true,
                     "search": {
                        "regex": true
                      },
                    "lengthMenu": rootView.lengthMenu ? rootView.lengthMenu : null,
                    "order": [],
                     "columnDefs": [
                        { "searchable": true, "targets": 0 }, // Team Number
                        { "searchable": true, /*"visible": false,*/ "targets": 1 }, // Event Key
                        { "searchable": false, "targets": "_all" },
                      ]
                 });
            });
        });
    });
}

$('#findTeamNumber').bind('input', function() {
    if($(this).val() == "")
        $.fn.dataTable.tables( { api: true } )
            .columns(0)
                .search("")
                .draw();
    else
        $.fn.dataTable.tables( { api: true } )
            .columns(0)
                .search("^" + $(this).val() + "$", true, false, true)
                .draw();
});

$('#findEvent').bind('input', function() {
    if($(this).val() == "")
        $.fn.dataTable.tables( { api: true } )
            .columns(1)
                .search("")
                .draw();
    else
        $.fn.dataTable.tables( { api: true } )
            .columns(1)
                .search("^" + $(this).val() + "$", true, false, true)
                .draw();
});

/* http://stackoverflow.com/a/8052100 */
function getDescendantProp(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
}

/* http://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places */
function renderTypes(num) {
	const numCast = +(Math.round(num + "e+2")  + "e-2");

	if(!Number.isNaN(numCast))
		return numCast;
	else
		return num;
}
<commit_msg>fix bugs<commit_after>$.ajaxSetup({
  async: false
});

var activeView = 0,
    loadedViews = [];

$.get("http://127.0.0.1:1338/views", function (ajaxViews) {
    loadedViews = ajaxViews;

	$('.nav-sidebar').html("");

	loadedViews.forEach(function(view, index){
		$(".nav-sidebar").append(`<li data-view="` + index + `"><a href="#" onclick="loadData('` + index + `')">` + view.name + `</a></li>`);
	});

    loadData(0);
});

function loadData(index) {
    $('#findTeamNumber').val($('#findTeamNumber').val());
    $('#findEvent').val($('#findEvent').val());

	if(!index)
		index = activeView
	else
		activeView = index;

	$("li[data-view]").removeClass("active");
	$("li[data-view=" + index + "]").addClass("active");

    $(".main").html("");

	loadedViews[index].views.forEach(function(rootView){
        $.get("http://127.0.0.1:1338/data/" + rootView.name, function (views) {
            views.forEach(function (view) {
                var table = `<h3>` + view.name + `</h3>`;
                table += `<div class="table-responsive"><table class="table table-striped" id="` + rootView.name + `"><thead><tr>`;

                view.headers.forEach(function (header) {
                    table += "" + header.text + "	";
                });

                table += `</tr></thead><tbody>`;

                view.data.forEach(function (datum) {
                    table += `<tr>`;

                    view.headers.forEach(function (header) {
                        var val = getDescendantProp(datum, header.value);

                        table += "" + renderTypes(val) + "	";
                    });

                    table += `</tr>`;
                });

                table += `</tbody></table></div><hr>`

                $(".main").append(table);

                 $('table#' + rootView.name).DataTable({
                    "paging": rootView.disablePaging ? !rootView.disablePaging : true,
                    "searching": rootView.disableSearching ? !rootView.disableSearching : true,
                    "info": rootView.disableInfo ? !rootView.disableInfo : true,
                     "search": {
                        "regex": true
                      },
                    "lengthMenu": rootView.lengthMenu ? rootView.lengthMenu : null,
                    "order": [],
                     "columnDefs": [
                        { "searchable": true, "targets": 0 }, // Team Number
                        { "searchable": true, /*"visible": false,*/ "targets": 1 }, // Event Key
                        { "searchable": false, "targets": "_all" },
                      ]
                 });
            });
        });
    });
}

$('#findTeamNumber').bind('input', function() {
    if($(this).val() == "")
        $.fn.dataTable.tables( { api: true } )
            .columns(0)
                .search("")
                .draw();
    else
        $.fn.dataTable.tables( { api: true } )
            .columns(0)
                .search("^" + $(this).val() + "$", true, false, true)
                .draw();
});

$('#findEvent').bind('input', function() {
    if($(this).val() == "")
        $.fn.dataTable.tables( { api: true } )
            .columns(1)
                .search("")
                .draw();
    else
        $.fn.dataTable.tables( { api: true } )
            .columns(1)
                .search("^" + $(this).val() + "$", true, false, true)
                .draw();
});

/* http://stackoverflow.com/a/8052100 */
function getDescendantProp(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
}

/* http://stackoverflow.com/questions/11832914/round-to-at-most-2-decimal-places */
function renderTypes(num) {
	const numCast = +(Math.round(num + "e+2")  + "e-2");

	if(!Number.isNaN(numCast))
		return numCast;
	else
		return num;
}
<|endoftext|>