<commit_before>// memory.js
//
// In-memory storage of data
//
// Copyright 2011,2012 StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require('../databank'),
    Databank = databank.Databank,
    DatabankError = databank.DatabankError,
    AlreadyExistsError = databank.AlreadyExistsError,
    NoSuchThingError = databank.NoSuchThingError;

var MemoryDatabank = function(params) {
    this.schema = params.schema || {};
};

MemoryDatabank.prototype = new Databank();

MemoryDatabank.prototype.freeze = function(value) {
    var dup, i;

    switch (typeof value) {
    case 'number':
        return value;
        break;
    case 'object':
        if (value instanceof Array) {
            dup = new Array(value.length);
            for (i = 0; i < value.length; i++) {
                dup[i] = this.freeze(value[i]);
            }
            return dup;
        }
        // FALL THROUGH
    default:
        return JSON.stringify(value);
    }
};

MemoryDatabank.prototype.melt = function(value) {
    var dup, i;

    switch (typeof value) {
    case 'number':
        return value;
        break;
    case 'object':
        if (value instanceof Array) {
            dup = new Array(value.length);
            for (i = 0; i < value.length; i++) {
                dup[i] = this.melt(value[i]);
            }
            return dup;
        }
        // FALL THROUGH
    default:
        return JSON.parse(value);
    }
};

MemoryDatabank.prototype.connect = function(params, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        var type;
        bank.types = {};
        for (type in bank.schema) {
            bank.types[type] = {};
        }
        onCompletion(null);
    });
};

MemoryDatabank.prototype.disconnect = function(onCompletion) {
    var bank = this;
    process.nextTick(function() {
        delete bank.types;
        // Always succeed
        onCompletion(null);
    });
};

MemoryDatabank.prototype.create = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (id in bank.types[type]) {
            onCompletion(new AlreadyExistsError(type, id), null);
        } else {
            bank.types[type][id] = bank.freeze(value);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.update = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id), null);
        } else {
            bank.types[type][id] = bank.freeze(value);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.read = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        var value;

        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id), null);
        } else {
            value = bank.melt(bank.types[type][id]);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.del = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id));
        } else {
            delete bank.types[type][id];
            onCompletion(null);
        }
    });
};

MemoryDatabank.prototype.save = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        bank.types[type][id] = bank.freeze(value);
        onCompletion(null, value);
    });
};

MemoryDatabank.prototype.search = function(type, criteria, onResult, onCompletion) {
    var bank = this;
    process.nextTick(function() {

        var id, value;

        if (!(type in bank.types)) {
            bank.types[type] = {};
        }

        // FIXME: maybe scanning each and every item isn't
        // the most efficient way to search...?

        for (id in bank.types[type]) {
            value = bank.melt(bank.types[type][id]);
            if (bank.matchesCriteria(value, criteria)) {
                onResult(value);
            }
        }

        onCompletion(null);
    });
};

exports.MemoryDatabank = MemoryDatabank;
<commit_msg>Implement atomicish incr() and decr()<commit_after>// memory.js
//
// In-memory storage of data
//
// Copyright 2011,2012 StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require('../databank'),
    Databank = databank.Databank,
    DatabankError = databank.DatabankError,
    AlreadyExistsError = databank.AlreadyExistsError,
    NoSuchThingError = databank.NoSuchThingError;

var MemoryDatabank = function(params) {
    this.schema = params.schema || {};
};

MemoryDatabank.prototype = new Databank();

MemoryDatabank.prototype.freeze = function(value) {
    var dup, i;

    switch (typeof value) {
    case 'number':
        return value;
        break;
    case 'object':
        if (value instanceof Array) {
            dup = new Array(value.length);
            for (i = 0; i < value.length; i++) {
                dup[i] = this.freeze(value[i]);
            }
            return dup;
        }
        // FALL THROUGH
    default:
        return JSON.stringify(value);
    }
};

MemoryDatabank.prototype.melt = function(value) {
    var dup, i;

    switch (typeof value) {
    case 'number':
        return value;
        break;
    case 'object':
        if (value instanceof Array) {
            dup = new Array(value.length);
            for (i = 0; i < value.length; i++) {
                dup[i] = this.melt(value[i]);
            }
            return dup;
        }
        // FALL THROUGH
    default:
        return JSON.parse(value);
    }
};

MemoryDatabank.prototype.connect = function(params, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        var type;
        bank.types = {};
        for (type in bank.schema) {
            bank.types[type] = {};
        }
        onCompletion(null);
    });
};

MemoryDatabank.prototype.disconnect = function(onCompletion) {
    var bank = this;
    process.nextTick(function() {
        delete bank.types;
        // Always succeed
        onCompletion(null);
    });
};

MemoryDatabank.prototype.create = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (id in bank.types[type]) {
            onCompletion(new AlreadyExistsError(type, id), null);
        } else {
            bank.types[type][id] = bank.freeze(value);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.update = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id), null);
        } else {
            bank.types[type][id] = bank.freeze(value);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.read = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        var value;

        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id), null);
        } else {
            value = bank.melt(bank.types[type][id]);
            onCompletion(null, value);
        }
    });
};

MemoryDatabank.prototype.del = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            onCompletion(new NoSuchThingError(type, id));
        } else {
            delete bank.types[type][id];
            onCompletion(null);
        }
    });
};

MemoryDatabank.prototype.save = function(type, id, value, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        bank.types[type][id] = bank.freeze(value);
        onCompletion(null, value);
    });
};

MemoryDatabank.prototype.search = function(type, criteria, onResult, onCompletion) {
    var bank = this;
    process.nextTick(function() {

        var id, value;

        if (!(type in bank.types)) {
            bank.types[type] = {};
        }

        // FIXME: maybe scanning each and every item isn't
        // the most efficient way to search...?

        for (id in bank.types[type]) {
            value = bank.melt(bank.types[type][id]);
            if (bank.matchesCriteria(value, criteria)) {
                onResult(value);
            }
        }

        onCompletion(null);
    });
};

MemoryDatabank.prototype.incr = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }

        if (!(id in bank.types[type])) {
            bank.types[type][id] = 1;
            onCompletion(null, 1);
        } else {
            bank.types[type][id]++;
            onCompletion(null, bank.types[type][id]);
        }
    });
};

MemoryDatabank.prototype.decr = function(type, id, onCompletion) {
    var bank = this;
    process.nextTick(function() {
        if (!(type in bank.types)) {
            bank.types[type] = {};
        }
        if (!(id in bank.types[type])) {
            bank.types[type][id] = -1;
            onCompletion(null, -1);
        } else {
            bank.types[type][id]--;
            onCompletion(null, bank.types[type][id]);
        }
    });
};

exports.MemoryDatabank = MemoryDatabank;
<|endoftext|>


<commit_before>/*
 * lib/dynamo-restore.js
 *
 * By Steven de Salas
 *
 * AWS Restore to DynamoDB. Streams an S3 backup to a new DynamoDB table.
 *
 */
var URL = require('url');
var util = require('util');
var AWS = require('aws-sdk');
var events = require('events');
var readline = require('readline');
var DYNAMO_CHUNK_SIZE = 25;

function DynamoRestore(options) {
    options = options || {};
    options.concurrency = options.concurrency || 50;
    options.minConcurrency = Math.ceil(options.concurrency * 0.1);
    options.maxConcurrency = options.concurrency;
    options.readcapacity = options.readcapacity || 5;
    options.writecapacity = options.writecapacity || 5;
    options.stopOnFailure = options.stopOnFailure || false;
    options.awsKey = options.awsKey || process.env.AWS_ACCESS_KEY_ID;
    options.awsSecret = options.awsSecret || process.env.AWS_SECRET_ACCESS_KEY;
    options.awsRegion = options.awsRegion || process.env.AWS_DEFAULT_REGION || 'ap-southeast-2';

    AWS.config.update({
        accessKeyId: options.awsKey,
        secretAccessKey: options.awsSecret,
        region: options.awsRegion
    });

    this.options = options;
};

// Stick to prototypal inheritance. While this can be done differently
// in ES6 we'd be making package unusable for older engines (0.10.x->0.12.x)
util.inherits(DynamoRestore, events.EventEmitter);

DynamoRestore.prototype.run = function(finishCallback) {
  this._validateS3Backup(this.options);
  this._validateTable(this.options);
	this._startDownload();
	this.on('finish', (function() {
		var dynamodb = new AWS.DynamoDB(),
			options = this.options;
		// Finish off by updating read/write capacity to end-state
		dynamodb.updateTable({
			TableName: options.table,
			ProvisionedThroughput: {
			    ReadCapacityUnits: options.readcapacity,
			    WriteCapacityUnits: options.writecapacity
			  }
		}, finishCallback);
	}).bind(this));
	// Exit program by default if there are no error listeners attached.
	this.on('error', (function(message) {
		if (finishCallback) {
			finishCallback(message);
		}
		if (this.listeners('error').length <= 1) {
			throw new Error(message);
		}
	}).bind(this));
};

DynamoRestore.prototype._validateS3Backup = function(options) {
	// Check S3 URI
	var url = URL.parse(options.source);
	if (!url || url.protocol !== 's3:') {
		return this.emit('error', 'Please provide an s3 URI as file source (ie s3://mybucketname/folder/mydynamotable.json)');
	}
	if (!url.pathname || !url.hostname || url.search || url.hash || url.auth) {
		return this.emit('error', 'Please provide a simple s3 URI as file source (ie s3://mybucketname/folder/mydynamotable.json)');
	}
	if (url.pathname.substr(-5).toLowerCase() !== '.json') {
		return this.emit('error', 'Please provide a *.json file as source restoring backup.');
	}
	// Break up into individual components
	options.s3bucket = url.hostname;
	options.s3path = url.pathname.substr(1);
	options.s3filename = url.pathname.split('.').pop();
};

DynamoRestore.prototype._validateTable = function(options) {
	var dynamodb = new AWS.DynamoDB();
	if (!options.table) {
		return this.emit('error', 'Please provide a Dynamo DB table name to restore to.');
	}
	dynamodb.listTables({}, this._checkTableExists.bind(this));
};

DynamoRestore.prototype._checkTableExists = function(error, data) {
	if (error || !data || !data.TableNames){
		return this.emit('error', 'Fatal Error. Could not connect to AWS DynamoDB engine. Please check your credentials.');
	}
	if (data.TableNames.indexOf(this.options.table) > -1){
		return this.emit('error', 'Fatal Error. The destination table already exists! Exiting process..')
	}
	if (this.options.partitionkey && this.options.partitionkeytype) {
		// Once we know the partition key and data type the rest is a breeze.
		this._createTable();
	} else {
		// No partition key info? Loop until we have it
		setTimeout(this._checkTableExists.bind(this, error, data), 200);
	}
};

DynamoRestore.prototype._startDownload = function() {
	var s3 = new AWS.S3();
	var params = { Bucket: this.options.s3bucket, Key: this.options.s3path };
	// First determine if file exists in s3
	s3.headObject(params, (function (error, meta) {
		if (error) {
			if (error.code === 'NotFound') this.emit('error', util.format('Could not find file in s3. %s', this.options.source));
			else this.emit('error', util.format('Error downloading file from s3: %s', error));
			return;
		}
		var downloadStream = s3.getObject(params).createReadStream();
		downloadStream.pause();
		// All good, start downloading
		this.emit('start-download', meta);
		this.readline = readline.createInterface({ terminal: false, input: downloadStream })
			.on('line', this._processLine.bind(this))
			.on('close', (function() {
				this.emit('finish-download');
				this.batches.push({
					items: this.requestItems.splice(0, DYNAMO_CHUNK_SIZE),
					attempts: 0
				});
				this._finishBatches();
			}).bind(this));
		this.readline.meta = meta;
		this.readline.meta.RemainingLength = meta.ContentLength;
	}).bind(this));
};

DynamoRestore.prototype._processLine = function(line) {
	this.batches = this.batches || [];
	this.requests = this.requests || [];
	this.requestItems = this.requestItems || [];
	// First Line?
	if (!this.template) {
		// Use this to extract schema information
		this.readline.pause();
		this.template = JSON.parse(line);
		this._extractSchema(this.template);
	}
	// Keep tabs on how much data is being consumed
	this.readline.meta.RemainingLength -= line.length + 1;
	// Create batches of 25 records each
	this.requestItems.push({ PutRequest: { Item: JSON.parse(line) } });
	if (this.requestItems.length === DYNAMO_CHUNK_SIZE) {
		this.batches.push({
			items: this.requestItems.splice(0, DYNAMO_CHUNK_SIZE),
			attempts: 0
		});
	}
	// Writing to Dynamo is usually slower than reading from S3,
	// and we want to avoid clogging up memory or writing to disk.
	// The list of batches waiting for DynamoDB to process would
	// quickly get out of hand here, so an easy way around this is to
	// stop reading from S3 when the number of requests in flight goes past a
	// certain size, and then continue reading when the number is reduced.
	if (this.requests.length >= this.options.concurrency) {
		//Too many requests! Pausing download..
		this.readline.pause();
	}
	// Send the next batch if we are not exceeding concurrency (ie max requests)
	else if (this.batches.length) {
		this._sendBatch();
	}
};

DynamoRestore.prototype._extractSchema = function(template) {
	var partitionkey = this.options.partitionkey;
	if (partitionkey) {
		// Check it actually exists
		if (!template[partitionkey]) {
			return this.emit('error', util.format('Fatal Error. The --partitionkey "%s" you provided is not a valid column.', partitionkey));
		}
	} else {
		// Or if unkonwn, find the most likely candidate
		var likelyCandidates = Object.keys(template).filter(function(column) {
			return column.toLowerCase().substr(-2) === 'id'
				||	column.toLowerCase().substr(-3) === 'key'
				||	column.toLowerCase().substr(-3) === 'ref';
		});
		if (likelyCandidates.length === 0) {
			return this.emit('error', 'Fatal Error. Cannot determine --partitionkey from backup, please supply it manually.');
		} else {
			// Pick the shortest one
			partitionkey = likelyCandidates.sort(function(a, b) { return b.length - a.length; }).pop();
		}
		this.options.partitionkey = partitionkey;
		this.options.sortkey = undefined;
	}
	// And find the type for each primary and secondary
	this.options.partitionkeytype = Object.keys(template[partitionkey]).pop();
	if (this.options.sortkey) {
		var sortkey = this.options.sortkey;
		if (!template[sortkey]) {
			return this.emit('error', 'Fatal Error. The --sortkey you provided is not available for some records.')
		}
		this.options.sortkeytype = Object.keys(template[sortkey]).pop();
	}
};

DynamoRestore.prototype._createTable = function(callback) {
	var dynamodb = new AWS.DynamoDB(),
		options = this.options;
	if (!options.table || !options.partitionkey) {
		return this.emit('error', 'Fatal Error. Could not create dynamo table. Not enough information provided.');
	}
	var params = {
		TableName: options.table,
		AttributeDefinitions: [{
			AttributeName: options.partitionkey,
			AttributeType: options.partitionkeytype
		}],
		KeySchema: [{
			AttributeName: options.partitionkey,
			KeyType: 'HASH'
		}],
		ProvisionedThroughput: {
			ReadCapacityUnits: options.readcapacity,
			WriteCapacityUnits: options.concurrency // Need this high for pumping data, but will reduce it later.
		}
	};
	if (options.sortkey) {
		params.AttributeDefinitions.push({ AttributeName: options.sortkey, AttributeType: options.sortkeytype});
		params.KeySchema.push({ AttributeName: options.sortkey, KeyType: 'RANGE' });
	}
	dynamodb.createTable(params, (function(error, data) {
		if (error || !data) {
			return this.emit('error', 'Fatal Error. Failed to create new table. ' + error);
		}
		data = data.TableDescription;
		dynamodb.describeTable({ TableName: data.TableName }, this._checkTableReady.bind(this));
	}).bind(this));
};

DynamoRestore.prototype._checkTableReady = function(error, data) {
	var dynamodb = new AWS.DynamoDB();
	if (error || !data || !data.Table) {
		return this.emit('error', 'Error creating table ' + this.options.table);
	}
	if (data
		&& data.Table
		&& data.Table.TableStatus === 'ACTIVE') {
			// All ready, now we can start inserting records
			this.readline.resume();
	} else {
		setTimeout(dynamodb.describeTable.bind(dynamodb, { TableName: data.Table.TableName }, this._checkTableReady.bind(this)), 200);
	}
};

DynamoRestore.prototype._sendBatch = function() {
	// Prepare
	var params = { RequestItems: {} },
		dynamo = new AWS.DynamoDB(),
		options = this.options;
		batch = this.batches.shift();
	params.RequestItems[options.table] = batch.items;

	// Send
	this.requests.push(dynamo.batchWriteItem(params, (function(error, data) {
		this.requests.shift();
		if (error) {
			// Problem? Check the number of attempts
			if (batch.attempts > options.concurrency) {
				if (options.stopOnFailure) {
					return this.emit('error', 'Fatal Error. Failed to upload batch. Ending process. \n' + JSON.stringify(batch));
				} else {
					this.emit('warning', 'Failed to upload same batch too many times, removing from queue.. \n' + JSON.stringify(batch));
				}
			} else {
				this.emit('warning', 'Error processing batch, putting back in the queue.');
				batch.attempts++;
				this.batches.push(batch);
			}
		}
		var unprocessedItems = data && data.UnprocessedItems && data.UnprocessedItems[options.table] || [];
		if (unprocessedItems.length) {
			// Retry unprocessed items
			this.emit('warning', 'Retrying ' + unprocessedItems.length + ' unprocessed items');
			this.batches.push({
				items: unprocessedItems,
				attempts: batch.attempts + 1
			});
			// Back off a bit..
			options.concurrency--;
			if (options.concurrency < options.minConcurrency) {
				options.concurrency = options.minConcurrency;
			}
		} else {
			// Successful upload, increase concurrency again..
			options.concurrency++;
			if (options.concurrency > options.maxConcurrency) {
				options.concurrency = options.maxConcurrency;
			}
		}
		// Continue downloading data...
		if (this.requests.length < options.concurrency * 0.8) {
			this.readline.resume();
		}
		this.emit('finish-batch', this.requests.length);
	}).bind(this)));

	// Notify
	this.emit('send-batch', this.batches.length, this.requests.length, this.readline.meta);
};

DynamoRestore.prototype._finishBatches = function() {
	if (!this.batches.length) {
		if (!this.requests.length) {
			// Finish only if there is nothing left to wait for.
			this.emit('finish');
			return;
		}
	} else {
		// Send remaining batches
		if (this.requests.length < this.options.concurrency) {
			this._sendBatch();
		}
	}
	// Repeat until finished
	setTimeout(this._finishBatches.bind(this), 200);
}

module.exports = DynamoRestore;
<commit_msg>Wait for table warm-up to avoid initial throttling.<commit_after>/*
 * lib/dynamo-restore.js
 *
 * By Steven de Salas
 *
 * AWS Restore to DynamoDB. Streams an S3 backup to a new DynamoDB table.
 *
 */
var URL = require('url');
var util = require('util');
var AWS = require('aws-sdk');
var events = require('events');
var readline = require('readline');
var DYNAMO_CHUNK_SIZE = 25;

function DynamoRestore(options) {
    options = options || {};
    options.concurrency = options.concurrency || 200;
    options.minConcurrency = Math.ceil(options.concurrency * 0.1);
    options.maxConcurrency = options.concurrency;
    options.readcapacity = options.readcapacity || 5;
    options.writecapacity = options.writecapacity || 5;
    options.stopOnFailure = options.stopOnFailure || false;
    options.awsKey = options.awsKey || process.env.AWS_ACCESS_KEY_ID;
    options.awsSecret = options.awsSecret || process.env.AWS_SECRET_ACCESS_KEY;
    options.awsRegion = options.awsRegion || process.env.AWS_DEFAULT_REGION || 'ap-southeast-2';

    AWS.config.update({
        accessKeyId: options.awsKey,
        secretAccessKey: options.awsSecret,
        region: options.awsRegion
    });

    this.options = options;
};

// Stick to prototypal inheritance. While this can be done differently
// in ES6 we'd be making package unusable for older engines (0.10.x->0.12.x)
util.inherits(DynamoRestore, events.EventEmitter);

DynamoRestore.prototype.run = function(finishCallback) {
  this._validateS3Backup(this.options);
  this._validateTable(this.options);
	this._startDownload();
	this.on('finish', (function() {
		var dynamodb = new AWS.DynamoDB(),
			options = this.options;
		// Finish off by updating read/write capacity to end-state
		dynamodb.updateTable({
			TableName: options.table,
			ProvisionedThroughput: {
			    ReadCapacityUnits: options.readcapacity,
			    WriteCapacityUnits: options.writecapacity
			  }
		}, finishCallback);
	}).bind(this));
	// Exit program by default if there are no error listeners attached.
	this.on('error', (function(message) {
		if (finishCallback) {
			finishCallback(message);
		}
		if (this.listeners('error').length <= 1) {
			throw new Error(message);
		}
	}).bind(this));
};

DynamoRestore.prototype._validateS3Backup = function(options) {
	// Check S3 URI
	var url = URL.parse(options.source);
	if (!url || url.protocol !== 's3:') {
		return this.emit('error', 'Please provide an s3 URI as file source (ie s3://mybucketname/folder/mydynamotable.json)');
	}
	if (!url.pathname || !url.hostname || url.search || url.hash || url.auth) {
		return this.emit('error', 'Please provide a simple s3 URI as file source (ie s3://mybucketname/folder/mydynamotable.json)');
	}
	if (url.pathname.substr(-5).toLowerCase() !== '.json') {
		return this.emit('error', 'Please provide a *.json file as source restoring backup.');
	}
	// Break up into individual components
	options.s3bucket = url.hostname;
	options.s3path = url.pathname.substr(1);
	options.s3filename = url.pathname.split('.').pop();
};

DynamoRestore.prototype._validateTable = function(options) {
	var dynamodb = new AWS.DynamoDB();
	if (!options.table) {
		return this.emit('error', 'Please provide a Dynamo DB table name to restore to.');
	}
	dynamodb.listTables({}, this._checkTableExists.bind(this));
};

DynamoRestore.prototype._checkTableExists = function(error, data) {
	if (error || !data || !data.TableNames){
		return this.emit('error', 'Fatal Error. Could not connect to AWS DynamoDB engine. Please check your credentials.');
	}
	if (data.TableNames.indexOf(this.options.table) > -1){
		return this.emit('error', 'Fatal Error. The destination table already exists! Exiting process..')
	}
	if (this.options.partitionkey && this.options.partitionkeytype) {
		// Once we know the partition key and data type the rest is a breeze.
		this._createTable();
	} else {
		// No partition key info? Loop until we have it
		setTimeout(this._checkTableExists.bind(this, error, data), 200);
	}
};

DynamoRestore.prototype._startDownload = function() {
	var s3 = new AWS.S3();
	var params = { Bucket: this.options.s3bucket, Key: this.options.s3path };
	// First determine if file exists in s3
	s3.headObject(params, (function (error, meta) {
		if (error) {
			if (error.code === 'NotFound') this.emit('error', util.format('Could not find file in s3. %s', this.options.source));
			else this.emit('error', util.format('Error downloading file from s3: %s', error));
			return;
		}
		var downloadStream = s3.getObject(params).createReadStream();
		downloadStream.pause();
		// All good, start downloading
		this.emit('start-download', meta);
		this.readline = readline.createInterface({ terminal: false, input: downloadStream })
			.on('line', this._processLine.bind(this))
			.on('close', (function() {
				this.emit('finish-download');
				this.batches.push({
					items: this.requestItems.splice(0, DYNAMO_CHUNK_SIZE),
					attempts: 0
				});
				this._finishBatches();
			}).bind(this));
		this.readline.meta = meta;
		this.readline.meta.RemainingLength = meta.ContentLength;
	}).bind(this));
};

DynamoRestore.prototype._processLine = function(line) {
	this.batches = this.batches || [];
	this.requests = this.requests || [];
	this.requestItems = this.requestItems || [];
	// First Line?
	if (!this.template) {
		// Use this to extract schema information
		this.readline.pause();
		this.template = JSON.parse(line);
		this._extractSchema(this.template);
	}
	// Keep tabs on how much data is being consumed
	this.readline.meta.RemainingLength -= line.length + 1;
	// Create batches of 25 records each
	this.requestItems.push({ PutRequest: { Item: JSON.parse(line) } });
	if (this.requestItems.length === DYNAMO_CHUNK_SIZE) {
		this.batches.push({
			items: this.requestItems.splice(0, DYNAMO_CHUNK_SIZE),
			attempts: 0
		});
	}
	// Writing to Dynamo is usually slower than reading from S3,
	// and we want to avoid clogging up memory or writing to disk.
	// The list of batches waiting for DynamoDB to process would
	// quickly get out of hand here, so an easy way around this is to
	// stop reading from S3 when the number of requests in flight goes past a
	// certain size, and then continue reading when the number is reduced.
	if (this.requests.length >= this.options.concurrency) {
		//Too many requests! Pausing download..
		this.readline.pause();
	}
	// Send the next batch if we are not exceeding concurrency (ie max requests)
	else if (this.tableready && this.batches.length) {
		this._sendBatch();
	}
};

DynamoRestore.prototype._extractSchema = function(template) {
	var partitionkey = this.options.partitionkey;
	if (partitionkey) {
		// Check it actually exists
		if (!template[partitionkey]) {
			return this.emit('error', util.format('Fatal Error. The --partitionkey "%s" you provided is not a valid column.', partitionkey));
		}
	} else {
		// Or if unkonwn, find the most likely candidate
		var likelyCandidates = Object.keys(template).filter(function(column) {
			return column.toLowerCase().substr(-2) === 'id'
				||	column.toLowerCase().substr(-3) === 'key'
				||	column.toLowerCase().substr(-3) === 'ref';
		});
		if (likelyCandidates.length === 0) {
			return this.emit('error', 'Fatal Error. Cannot determine --partitionkey from backup, please supply it manually.');
		} else {
			// Pick the shortest one
			partitionkey = likelyCandidates.sort(function(a, b) { return b.length - a.length; }).pop();
		}
		this.options.partitionkey = partitionkey;
		this.options.sortkey = undefined;
	}
	// And find the type for each primary and secondary
	this.options.partitionkeytype = Object.keys(template[partitionkey]).pop();
	if (this.options.sortkey) {
		var sortkey = this.options.sortkey;
		if (!template[sortkey]) {
			return this.emit('error', 'Fatal Error. The --sortkey you provided is not available for some records.')
		}
		this.options.sortkeytype = Object.keys(template[sortkey]).pop();
	}
};

DynamoRestore.prototype._createTable = function(callback) {
	var dynamodb = new AWS.DynamoDB(),
		options = this.options;
	if (!options.table || !options.partitionkey) {
		return this.emit('error', 'Fatal Error. Could not create dynamo table. Not enough information provided.');
	}
	var params = {
		TableName: options.table,
		AttributeDefinitions: [{
			AttributeName: options.partitionkey,
			AttributeType: options.partitionkeytype
		}],
		KeySchema: [{
			AttributeName: options.partitionkey,
			KeyType: 'HASH'
		}],
		ProvisionedThroughput: {
			ReadCapacityUnits: options.readcapacity,
			WriteCapacityUnits: options.concurrency // Need this high for pumping data, but will reduce it later.
		}
	};
	if (options.sortkey) {
		params.AttributeDefinitions.push({ AttributeName: options.sortkey, AttributeType: options.sortkeytype});
		params.KeySchema.push({ AttributeName: options.sortkey, KeyType: 'RANGE' });
	}
	dynamodb.createTable(params, (function(error, data) {
		if (error || !data) {
			return this.emit('error', 'Fatal Error. Failed to create new table. ' + error);
		}
		data = data.TableDescription;
		// Wait before hammering table..
		setTimeout(dynamodb.describeTable.bind(dynamodb, { TableName: data.TableName }, this._checkTableReady.bind(this)), 5000);
	}).bind(this));
};

DynamoRestore.prototype._checkTableReady = function(error, data) {
	var dynamodb = new AWS.DynamoDB();
	if (error || !data || !data.Table) {
		return this.emit('error', 'Error creating table ' + this.options.table);
	}
	if (data
		&& data.Table
		&& data.Table.TableStatus === 'ACTIVE') {
			// All ready, now we can start inserting records
			this.tableready = true;
			this.readline.resume();
			while (this.batches.length) { this._sendBatch(); }
	} else {
		setTimeout(dynamodb.describeTable.bind(dynamodb, { TableName: data.Table.TableName }, this._checkTableReady.bind(this)), 1000);
	}
};

DynamoRestore.prototype._sendBatch = function() {
	// Prepare
	var params = { RequestItems: {} },
		dynamo = new AWS.DynamoDB(),
		options = this.options;
		batch = this.batches.shift();
	params.RequestItems[options.table] = batch.items;

	// Send
	this.requests.push(dynamo.batchWriteItem(params, (function(error, data) {
		this.requests.shift();
		if (error) {
			// Problem? Check the number of attempts
			if (batch.attempts > options.concurrency) {
				if (options.stopOnFailure) {
					return this.emit('error', 'Fatal Error. Failed to upload batch. Ending process. \n' + JSON.stringify(batch));
				} else {
					this.emit('warning', 'Failed to upload same batch too many times, removing from queue.. \n' + JSON.stringify(batch));
				}
			} else {
				this.emit('warning', 'Error processing batch, putting back in the queue.');
				batch.attempts++;
				this.batches.push(batch);
			}
		}
		var unprocessedItems = data && data.UnprocessedItems && data.UnprocessedItems[options.table] || [];
		if (unprocessedItems.length) {
			// Retry unprocessed items
			this.emit('warning', 'Retrying ' + unprocessedItems.length + ' unprocessed items');
			this.batches.push({
				items: unprocessedItems,
				attempts: batch.attempts + 1
			});
			// Back off a bit..
			options.concurrency--;
			if (options.concurrency < options.minConcurrency) {
				options.concurrency = options.minConcurrency;
			}
		} else {
			// Successful upload, increase concurrency again..
			options.concurrency++;
			if (options.concurrency > options.maxConcurrency) {
				options.concurrency = options.maxConcurrency;
			}
		}
		// Continue downloading data...
		if (this.tableready && this.requests.length < options.concurrency * 0.8) {
			this.readline.resume();
		}
		this.emit('finish-batch', this.requests.length);
	}).bind(this)));

	// Notify
	this.emit('send-batch', this.batches.length, this.requests.length, this.readline.meta);
};

DynamoRestore.prototype._finishBatches = function() {
	if (!this.batches.length) {
		if (!this.requests.length) {
			// Finish only if there is nothing left to wait for.
			this.emit('finish');
			return;
		}
	} else {
		// Send remaining batches
		if (this.requests.length < this.options.concurrency) {
			this._sendBatch();
		}
	}
	// Repeat until finished
	setTimeout(this._finishBatches.bind(this), 200);
}

module.exports = DynamoRestore;
<|endoftext|>


<commit_before>angular.module("leaflet-directive").directive('bounds', function ($log, $timeout, leafletHelpers, leafletBoundsHelpers) {
    return {
        restrict: "A",
        scope: false,
        replace: false,
        require: [ 'leaflet', 'center' ],

        link: function(scope, element, attrs, controller) {
            var isDefined = leafletHelpers.isDefined,
                createLeafletBounds = leafletBoundsHelpers.createLeafletBounds,
                leafletScope = controller[0].getLeafletScope(),
                mapController = controller[0];

            var emptyBounds = function(bounds) {
                if (bounds._southWest.lat === 0 && bounds._southWest.lng === 0 && bounds._northEast.lat === 0 && bounds._northEast.lng === 0) {
                    return true;
                }
                return false;
            };

            mapController.getMap().then(function (map) {
                leafletScope.$on('boundsChanged', function (event) {
                    var scope = event.currentScope;
                    var bounds = map.getBounds();
                    $log.debug('updated map bounds...', bounds);
                    if (emptyBounds(bounds)) {
                        return;
                    }
                    var newScopeBounds = {
                        northEast: {
                            lat: bounds._northEast.lat,
                            lng: bounds._northEast.lng
                        },
                        southWest: {
                            lat: bounds._southWest.lat,
                            lng: bounds._southWest.lng
                        }
                    };
                    if (!angular.equals(scope.bounds, newScopeBounds)) {
                        $log.debug('Need to update scope bounds.');
                        scope.bounds = newScopeBounds;
                    }
                });
                leafletScope.$watch('bounds', function (bounds) {
                    $log.debug('updated bounds...', bounds);
                    if (!isDefined(bounds)) {
                        $log.error('[AngularJS - Leaflet] Invalid bounds');
                        return;
                    }
                    var leafletBounds = createLeafletBounds(bounds);
                    if (leafletBounds && !map.getBounds().equals(leafletBounds)) {
                        $log.debug('Need to update map bounds.');
                        map.fitBounds(leafletBounds);
                    }
                }, true);
            });
        }
    };
});
<commit_msg>Removed uneeded $log.debug<commit_after>angular.module("leaflet-directive").directive('bounds', function ($log, $timeout, leafletHelpers, leafletBoundsHelpers) {
    return {
        restrict: "A",
        scope: false,
        replace: false,
        require: [ 'leaflet', 'center' ],

        link: function(scope, element, attrs, controller) {
            var isDefined = leafletHelpers.isDefined,
                createLeafletBounds = leafletBoundsHelpers.createLeafletBounds,
                leafletScope = controller[0].getLeafletScope(),
                mapController = controller[0];

            var emptyBounds = function(bounds) {
                if (bounds._southWest.lat === 0 && bounds._southWest.lng === 0 && bounds._northEast.lat === 0 && bounds._northEast.lng === 0) {
                    return true;
                }
                return false;
            };

            mapController.getMap().then(function (map) {
                leafletScope.$on('boundsChanged', function (event) {
                    var scope = event.currentScope;
                    var bounds = map.getBounds();
                    //$log.debug('updated map bounds...', bounds);
                    if (emptyBounds(bounds)) {
                        return;
                    }
                    var newScopeBounds = {
                        northEast: {
                            lat: bounds._northEast.lat,
                            lng: bounds._northEast.lng
                        },
                        southWest: {
                            lat: bounds._southWest.lat,
                            lng: bounds._southWest.lng
                        }
                    };
                    if (!angular.equals(scope.bounds, newScopeBounds)) {
                        //$log.debug('Need to update scope bounds.');
                        scope.bounds = newScopeBounds;
                    }
                });
                leafletScope.$watch('bounds', function (bounds) {
                    //$log.debug('updated bounds...', bounds);
                    if (!isDefined(bounds)) {
                        $log.error('[AngularJS - Leaflet] Invalid bounds');
                        return;
                    }
                    var leafletBounds = createLeafletBounds(bounds);
                    if (leafletBounds && !map.getBounds().equals(leafletBounds)) {
                        //$log.debug('Need to update map bounds.');
                        map.fitBounds(leafletBounds);
                    }
                }, true);
            });
        }
    };
});
<|endoftext|>


<commit_before>var playState = {

    create: function(){
        this.background = game.add.sprite(0, 0, 'cidade');
        this.background.width = 1300;
        this.background.height = 650;

        graphics = game.add.graphics(0, 0);

        this.groupCidade = game.add.group();

        this.groupCidade.inputEnableChildren = true;

        var x = 100;

        for (var i = 0; i < 3; i++){
            // Gera os retangulos que ficarão atras das imagens
            graphics.beginFill(0xFFFFFF);
            graphics.lineStyle(3, 0x05005e, 1);
            graphics.drawRoundedRect(x, 200, 315, 190, 10);
            graphics.endFill();

            var button = this.groupCidade.create(x, 200, graphics.generateTexture());
            button.tint = 0xff8800;
            button.name = 'opcao-' + i;

            x = x + 400;
        }
        graphics.destroy();

        this.cachorro = game.add.sprite(110, 210, 'cachorro');
        this.cachorro.width = 300;
        this.cachorro.height = 170;

        // Desenha o gato e a borda da box

        this.gato = game.add.sprite(510, 210, 'gato');
        this.gato.width = 300;
        this.gato.height = 170;

        // Desenha o passaro e a borda da box

        this.passaro = game.add.sprite(910, 210, 'passaro');
        this.passaro.width = 300;
        this.passaro.height = 170;

        this.statusAcerto=false;
        this.contador=0;

        game.time.events.add(Phaser.Timer.SECOND * 2, this.start, this);

    },

    start: function (){
        this.ocultarOpcoes(1);
        this.groupCidade.children[0].x += 230;
        this.cachorro.x += 230;
        this.groupCidade.children[0].y -= 70;
        this.cachorro.y -= 70;
        game.add.tween(this.groupCidade.children[0].scale).to( { x: 2, y: 2 }, 3000, Phaser.Easing.Elastic.Out, true);
        game.add.tween(this.cachorro.scale).to( { x: 1, y: 1 }, 3000, Phaser.Easing.Elastic.Out, true);

        game.time.events.add(Phaser.Timer.SECOND * 5, this.initial, this);
    },

    initial: function (){
        game.add.tween(this.groupCidade.children[0].scale).to( { x: 1, y: 1 }, 1000, Phaser.Easing.Linear.In, true);
        game.add.tween(this.cachorro.scale).to( { x: 0.5, y: 0.5 }, 1000, Phaser.Easing.Linear.In, true);
        this.groupCidade.children[0].x -= 230;
        this.cachorro.x -= 230;
        this.groupCidade.children[0].y += 70;
        this.cachorro.y += 70;
        this.background.alpha = 0.5;

        this.apresentarOpcoes();

    },

    ocultarOpcoes: function (){
        this.background.alpha = 0.5;
        if(1 == 1){
            this.groupCidade.children[1].alpha = 0;
            this.groupCidade.children[2].alpha = 0;
            this.gato.alpha = 0;
            this.passaro.alpha = 0;
        }
    },

    apresentarOpcoes: function(){
        game.add.tween(this.background).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
        if(1 == 1){
            game.add.tween(this.groupCidade.children[1]).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.groupCidade.children[2]).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.gato).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.passaro).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            console.log(this.groupCidade);
        }
    },

    verificaSelecionado: function(sprite){
        if(sprite.name == 'opcao-0'){

        }
        else
            console.log('puts');
    },

    update: function(){

        game.time.events.add(Phaser.Timer.SECOND * 7, activateBotoes, this);

        function activateBotoes(){
            this.groupCidade.onChildInputDown.add(onDown, this);
            this.groupCidade.onChildInputOver.add(onOver, this);
            this.groupCidade.onChildInputOut.add(onOut, this);
        }

        function onDown (sprite) {
            sprite.tint = 0x00ff00;
            this.verificaSelecionado(sprite);
        }

        function onOver (sprite) {
            sprite.tint = 0xffff00;
        }

        function onOut (sprite) {
            sprite.tint = 0xff8800;
            // sprite.tint = Math.random() * 0xffffff;

        }
    },

    Win: function(){
        game.state.start('win');
    }
};<commit_msg>Correções na 1ª parte<commit_after>var playState = {

    create: function(){
        this.background = game.add.sprite(0, 0, 'cidade');
        this.background.width = 1300;
        this.background.height = 650;

        graphics = game.add.graphics(0, 0);

        this.groupCidade = game.add.group();

        this.groupCidade.inputEnableChildren = true;

        var x = 100;

        for (var i = 0; i < 3; i++){
            // Gera os retangulos que ficarão atras das imagens
            graphics.beginFill(0xFFFFFF);
            graphics.lineStyle(3, 0x05005e, 1);
            graphics.drawRoundedRect(x, 200, 315, 190, 10);
            graphics.endFill();

            var button = this.groupCidade.create(x, 200, graphics.generateTexture());
            button.tint = 0xff8800;
            button.name = 'opcao-' + i;

            x = x + 400;
        }
        graphics.destroy();

        this.cachorro = game.add.sprite(110, 210, 'cachorro');
        this.cachorro.width = 300;
        this.cachorro.height = 170;

        // Desenha o gato e a borda da box

        this.gato = game.add.sprite(510, 210, 'gato');
        this.gato.width = 300;
        this.gato.height = 170;

        // Desenha o passaro e a borda da box

        this.passaro = game.add.sprite(910, 210, 'passaro');
        this.passaro.width = 300;
        this.passaro.height = 170;

        this.statusAcerto=false;
        this.contador=0;

        game.time.events.add(Phaser.Timer.SECOND * 2, this.start, this);

    },

    start: function (){
        this.ocultarOpcoes(1);
        this.groupCidade.children[0].x += 230;
        this.cachorro.x += 230;
        this.groupCidade.children[0].y -= 70;
        this.cachorro.y -= 70;
        game.add.tween(this.groupCidade.children[0].scale).to( { x: 2, y: 2 }, 3000, Phaser.Easing.Elastic.Out, true);
        game.add.tween(this.cachorro.scale).to( { x: 1, y: 1 }, 3000, Phaser.Easing.Elastic.Out, true);

        game.time.events.add(Phaser.Timer.SECOND * 5, this.initial, this);
    },

    initial: function (){
        game.add.tween(this.groupCidade.children[0].scale).to( { x: 1, y: 1 }, 1000, Phaser.Easing.Linear.In, true);
        game.add.tween(this.cachorro.scale).to( { x: 0.5, y: 0.5 }, 1000, Phaser.Easing.Linear.In, true);
        this.groupCidade.children[0].x -= 230;
        this.cachorro.x -= 230;
        this.groupCidade.children[0].y += 70;
        this.cachorro.y += 70;
        this.background.alpha = 0.5;

        this.apresentarOpcoes();

    },

    ocultarOpcoes: function (){
        this.background.alpha = 0.5;
        if(1 == 1){
            this.groupCidade.children[1].alpha = 0;
            this.groupCidade.children[2].alpha = 0;
            this.gato.alpha = 0;
            this.passaro.alpha = 0;
        }
    },

    apresentarOpcoes: function(){
        game.add.tween(this.background).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
        if(1 == 1){
            game.add.tween(this.groupCidade.children[1]).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.groupCidade.children[2]).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.gato).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            game.add.tween(this.passaro).to( { alpha: 1 }, 1000, Phaser.Easing.Linear.In, true);
            console.log(this.groupCidade);
        }
    },

    verificaSelecionado: function(sprite){
        if(sprite.name == 'opcao-0'){
            console.log('sucesso');
        }
        else
            console.log('puts');
    },

    update: function(){

        game.time.events.add(Phaser.Timer.SECOND * 7, activateBotoes, this);

        function activateBotoes(){
            this.groupCidade.onChildInputDown.add(onDown, this);
            this.groupCidade.onChildInputOver.add(onOver, this);
            this.groupCidade.onChildInputOut.add(onOut, this);
        }

        function onDown (sprite) {
            sprite.tint = 0x00ff00;
            this.verificaSelecionado(sprite);
        }

        function onOver (sprite) {
            sprite.tint = 0xffff00;
        }

        function onOut (sprite) {
            sprite.tint = 0xff8800;
            // sprite.tint = Math.random() * 0xffffff;

        }
    },

    Win: function(){
        game.state.start('win');
    }
};<|endoftext|>


<commit_before>module.exports = function() {
  return {
    restrict: 'E',
    replace: true,
    scope: {
      src: '=?',
      formAction: '=?',
      form: '=?',
      submission: '=?',
      readOnly: '=?',
      hideComponents: '=?',
      requireComponents: '=?',
      disableComponents: '=?',
      formioOptions: '=?',
      options: '=?'
    },
    controller: [
      '$scope',
      '$http',
      '$element',
      'FormioScope',
      'Formio',
      'FormioUtils',
      function(
        $scope,
        $http,
        $element,
        FormioScope,
        Formio,
        FormioUtils
      ) {
        $scope._src = $scope.src || '';
        $scope.formioAlerts = [];
        // Shows the given alerts (single or array), and dismisses old alerts
        this.showAlerts = $scope.showAlerts = function(alerts) {
          $scope.formioAlerts = [].concat(alerts);
        };

        // Add the live form parameter to the url.
        if ($scope._src && ($scope._src.indexOf('live=') === -1)) {
          $scope._src += ($scope._src.indexOf('?') === -1) ? '?' : '&';
          $scope._src += 'live=1';
        }

        // Build the display map.
        $scope.show = {};
        var boolean = {
          'true': true,
          'false': false
        };

        var updateComponents = function() {
          $scope.form.components = $scope.form.components || [];
          FormioUtils.eachComponent($scope.form.components, function(component) {
            // Display every component by default
            $scope.show[component.key] = ($scope.show[component.key] === undefined)
              ? true
              : $scope.show[component.key];

            // Only change display options if all required conditional properties are present.
            if (
              component.conditional
              && (component.conditional.show !== null && component.conditional.show !== '')
              && (component.conditional.when !== null && component.conditional.when !== '')
            ) {
              // Default the conditional values.
              component.conditional.show = boolean[component.conditional.show];
              component.conditional.eq = component.conditional.eq || '';

              // Get the conditional component.
              var cond = FormioUtils.getComponent($scope.form.components, component.conditional.when.toString());
              if (!cond) {
                return;
              }
              var value = $scope.submission.data[cond.key];

              if (value && typeof value !== 'object') {
                // Check if the conditional value is equal to the trigger value
                $scope.show[component.key] = value.toString() === component.conditional.eq.toString()
                  ? boolean[component.conditional.show]
                  : !boolean[component.conditional.show];
              }
              // Special check for check boxes component.
              else if (value && typeof value === 'object') {
                // Check if the conditional trigger value is true.
                $scope.show[component.key] = boolean[value[component.conditional.eq].toString()];
              }
              // Check against the components default value, if present and the components hasnt been interacted with.
              else if (!value && cond.defaultValue) {
                $scope.show[component.key] = cond.defaultValue.toString() === component.conditional.eq.toString()
                  ? boolean[component.conditional.show]
                  : !boolean[component.conditional.show];
              }
              // If there is no value, we still need to process as not equal.
              else {
                $scope.show[component.key] = !boolean[component.conditional.show];
              }

              // Update the visibility, if its possible a change occurred.
              component.hide = !$scope.show[component.key];
            }
            // Custom conditional logic.
            else if (component.customConditional) {
              try {
                // Create a child block, and expose the submission data.
                {
                  var data = $scope.submission.data;
                  var show = true; // show by default.
                  // Eval the custom conditional and update the show value.
                  show = eval('(function() { ' + component.customConditional.toString() + '; return show; })()');
                  $scope.show[component.key] = boolean[show];
                }
              }
              catch(e) {
                console.log('Custom Conditional Error: ');
                console.log(e);
                $scope.show[component.key] = true;
              }

              // Update the visibility, if its possible a change occurred.
              component.hide = !$scope.show[component.key];
            }

            // Set hidden if specified
            if ($scope.hideComponents) {
              component.hidden = $scope.hideComponents.indexOf(component.key) !== -1;
            }

            // Set required if specified
            if ($scope.requireComponents && component.hasOwnProperty('validate')) {
              component.validate.required = $scope.requireComponents.indexOf(component.key) !== -1;
            }

            // Set disabled if specified
            if ($scope.disableComponents) {
              component.disabled = $scope.disableComponents.indexOf(component.key) !== -1;
            }
          }, true);
        };

        // Update the components on the initial form render and all subsequent submission data changes.
        $scope.$on('formRender', updateComponents);
        $scope.$watchCollection('submission.data', updateComponents);

        if (!$scope._src) {
          $scope.$watch('src', function(src) {
            if (!src) {
              return;
            }
            $scope._src = src;
            $scope.formio = FormioScope.register($scope, $element, {
              form: true,
              submission: true
            });
          });
        }

        // Create the formio object.
        $scope.formio = FormioScope.register($scope, $element, {
          form: true,
          submission: true
        });

        // Called when the form is submitted.
        $scope.onSubmit = function(form) {
          if (!form.$valid || form.submitting) return;
          form.submitting = true;

          // Create a sanitized submission object.
          var submissionData = {data: {}};
          if ($scope.submission._id) {
            submissionData._id = $scope.submission._id;
          }
          if ($scope.submission.data._id) {
            submissionData._id = $scope.submission.data._id;
          }

          var grabIds = function(input) {
            if (!input) {
              return [];
            }

            if (!(input instanceof Array)) {
              input = [input];
            }

            var final = [];
            input.forEach(function(element) {
              if (element && element._id) {
                final.push(element._id);
              }
            });

            return final;
          };

          var defaultPermissions = {};
          FormioUtils.eachComponent($scope.form.components, function(component) {
            if (component.type === 'resource' && component.key && component.defaultPermission) {
              defaultPermissions[component.key] = component.defaultPermission;
            }
            if ($scope.submission.data.hasOwnProperty(component.key) && $scope.show[component.key]) {
              var value = $scope.submission.data[component.key];
              if (component.type === 'number') {
                submissionData.data[component.key] = value ? parseFloat(value) : 0;
              }
              else {
                submissionData.data[component.key] = value;
              }
            }
          });

          angular.forEach($scope.submission.data, function(value, key) {
            if (value && !value.hasOwnProperty('_id')) {
              submissionData.data[key] = value;
            }

            // Setup the submission access.
            var perm = defaultPermissions[key];
            if (perm) {
              submissionData.access = submissionData.access || [];

              // Coerce value into an array for plucking.
              if (!(value instanceof Array)) {
                value = [value];
              }

              // Try to find and update an existing permission.
              var found = false;
              submissionData.access.forEach(function(permission) {
                if (permission.type === perm) {
                  found = true;
                  permission.resources = permission.resources || [];
                  permission.resources.concat(grabIds(value));
                }
              });

              // Add a permission, because one was not found.
              if (!found) {
                submissionData.access.push({
                  type: perm,
                  resources: grabIds(value)
                });
              }
            }
          });

          // Show the submit message and say the form is no longer submitting.
          var onSubmit = function(submission, message) {
            $scope.showAlerts({
              type: 'success',
              message: message
            });
            form.submitting = false;
          };

          // Called when a submission has been made.
          var onSubmitDone = function(method, submission) {
            var message = '';
            if ($scope.options && $scope.options.submitMessage) {
              message = $scope.options.submitMessage;
            }
            else {
              message = 'Submission was ' + ((method === 'put') ? 'updated' : 'created') + '.';
            }
            onSubmit(submission, message);
            // Trigger the form submission.
            $scope.$emit('formSubmission', submission);
          };

          // Allow the form to be completed externally.
          $scope.$on('submitDone', function(event, submission, message) {
            onSubmit(submission, message);
          });

          // Allow an error to be thrown externally.
          $scope.$on('submitError', function(event, error) {
            FormioScope.onError($scope, $element)(error);
          });

          var submitEvent = $scope.$emit('formSubmit', submissionData);
          if (submitEvent.defaultPrevented) {
            // Listener wants to cancel the form submission
            form.submitting = false;
            return;
          }

          // Make sure to make a copy of the submission data to remove bad characters.
          submissionData = angular.copy(submissionData);

          // Allow custom action urls.
          if ($scope.action) {
            var method = submissionData._id ? 'put' : 'post';
            $http[method]($scope.action, submissionData).success(function(submission) {
              Formio.clearCache();
              onSubmitDone(method, submission);
            }).error(FormioScope.onError($scope, $element))
              .finally(function() {
                form.submitting = false;
              });
          }

          // If they wish to submit to the default location.
          else if ($scope.formio) {
            // copy to remove angular $$hashKey
            $scope.formio.saveSubmission(submissionData, $scope.formioOptions).then(function(submission) {
              onSubmitDone(submission.method, submission);
            }, FormioScope.onError($scope, $element)).finally(function() {
              form.submitting = false;
            });
          }
          else {
            $scope.$emit('formSubmission', submissionData);
          }
        };
      }
    ],
    templateUrl: 'formio.html'
  };
};
<commit_msg>Fixing lint errors.<commit_after>module.exports = function() {
  return {
    restrict: 'E',
    replace: true,
    scope: {
      src: '=?',
      formAction: '=?',
      form: '=?',
      submission: '=?',
      readOnly: '=?',
      hideComponents: '=?',
      requireComponents: '=?',
      disableComponents: '=?',
      formioOptions: '=?',
      options: '=?'
    },
    controller: [
      '$scope',
      '$http',
      '$element',
      'FormioScope',
      'Formio',
      'FormioUtils',
      function(
        $scope,
        $http,
        $element,
        FormioScope,
        Formio,
        FormioUtils
      ) {
        $scope._src = $scope.src || '';
        $scope.formioAlerts = [];
        // Shows the given alerts (single or array), and dismisses old alerts
        this.showAlerts = $scope.showAlerts = function(alerts) {
          $scope.formioAlerts = [].concat(alerts);
        };

        // Add the live form parameter to the url.
        if ($scope._src && ($scope._src.indexOf('live=') === -1)) {
          $scope._src += ($scope._src.indexOf('?') === -1) ? '?' : '&';
          $scope._src += 'live=1';
        }

        // Build the display map.
        $scope.show = {};
        var boolean = {
          'true': true,
          'false': false
        };

        var updateComponents = function() {
          $scope.form.components = $scope.form.components || [];
          FormioUtils.eachComponent($scope.form.components, function(component) {
            // Display every component by default
            $scope.show[component.key] = ($scope.show[component.key] === undefined)
              ? true
              : $scope.show[component.key];

            // Only change display options if all required conditional properties are present.
            if (
              component.conditional
              && (component.conditional.show !== null && component.conditional.show !== '')
              && (component.conditional.when !== null && component.conditional.when !== '')
            ) {
              // Default the conditional values.
              component.conditional.show = boolean[component.conditional.show];
              component.conditional.eq = component.conditional.eq || '';

              // Get the conditional component.
              var cond = FormioUtils.getComponent($scope.form.components, component.conditional.when.toString());
              if (!cond) {
                return;
              }
              var value = $scope.submission.data[cond.key];

              if (value && typeof value !== 'object') {
                // Check if the conditional value is equal to the trigger value
                $scope.show[component.key] = value.toString() === component.conditional.eq.toString()
                  ? boolean[component.conditional.show]
                  : !boolean[component.conditional.show];
              }
              // Special check for check boxes component.
              else if (value && typeof value === 'object') {
                // Check if the conditional trigger value is true.
                $scope.show[component.key] = boolean[value[component.conditional.eq].toString()];
              }
              // Check against the components default value, if present and the components hasnt been interacted with.
              else if (!value && cond.defaultValue) {
                $scope.show[component.key] = cond.defaultValue.toString() === component.conditional.eq.toString()
                  ? boolean[component.conditional.show]
                  : !boolean[component.conditional.show];
              }
              // If there is no value, we still need to process as not equal.
              else {
                $scope.show[component.key] = !boolean[component.conditional.show];
              }

              // Update the visibility, if its possible a change occurred.
              component.hide = !$scope.show[component.key];
            }
            // Custom conditional logic.
            else if (component.customConditional) {
              try {
                // Create a child block, and expose the submission data.
                {
                  var data = $scope.submission.data; // eslint-disable-line no-unused-vars
                  var show = true; // show by default.
                  // Eval the custom conditional and update the show value.
                  show = eval('(function() { ' + component.customConditional.toString() + '; return show; })()');
                  $scope.show[component.key] = boolean[show];
                }
              }
              catch (e) {
                $scope.show[component.key] = true;
              }

              // Update the visibility, if its possible a change occurred.
              component.hide = !$scope.show[component.key];
            }

            // Set hidden if specified
            if ($scope.hideComponents) {
              component.hidden = $scope.hideComponents.indexOf(component.key) !== -1;
            }

            // Set required if specified
            if ($scope.requireComponents && component.hasOwnProperty('validate')) {
              component.validate.required = $scope.requireComponents.indexOf(component.key) !== -1;
            }

            // Set disabled if specified
            if ($scope.disableComponents) {
              component.disabled = $scope.disableComponents.indexOf(component.key) !== -1;
            }
          }, true);
        };

        // Update the components on the initial form render and all subsequent submission data changes.
        $scope.$on('formRender', updateComponents);
        $scope.$watchCollection('submission.data', updateComponents);

        if (!$scope._src) {
          $scope.$watch('src', function(src) {
            if (!src) {
              return;
            }
            $scope._src = src;
            $scope.formio = FormioScope.register($scope, $element, {
              form: true,
              submission: true
            });
          });
        }

        // Create the formio object.
        $scope.formio = FormioScope.register($scope, $element, {
          form: true,
          submission: true
        });

        // Called when the form is submitted.
        $scope.onSubmit = function(form) {
          if (!form.$valid || form.submitting) return;
          form.submitting = true;

          // Create a sanitized submission object.
          var submissionData = {data: {}};
          if ($scope.submission._id) {
            submissionData._id = $scope.submission._id;
          }
          if ($scope.submission.data._id) {
            submissionData._id = $scope.submission.data._id;
          }

          var grabIds = function(input) {
            if (!input) {
              return [];
            }

            if (!(input instanceof Array)) {
              input = [input];
            }

            var final = [];
            input.forEach(function(element) {
              if (element && element._id) {
                final.push(element._id);
              }
            });

            return final;
          };

          var defaultPermissions = {};
          FormioUtils.eachComponent($scope.form.components, function(component) {
            if (component.type === 'resource' && component.key && component.defaultPermission) {
              defaultPermissions[component.key] = component.defaultPermission;
            }
            if ($scope.submission.data.hasOwnProperty(component.key) && $scope.show[component.key]) {
              var value = $scope.submission.data[component.key];
              if (component.type === 'number') {
                submissionData.data[component.key] = value ? parseFloat(value) : 0;
              }
              else {
                submissionData.data[component.key] = value;
              }
            }
          });

          angular.forEach($scope.submission.data, function(value, key) {
            if (value && !value.hasOwnProperty('_id')) {
              submissionData.data[key] = value;
            }

            // Setup the submission access.
            var perm = defaultPermissions[key];
            if (perm) {
              submissionData.access = submissionData.access || [];

              // Coerce value into an array for plucking.
              if (!(value instanceof Array)) {
                value = [value];
              }

              // Try to find and update an existing permission.
              var found = false;
              submissionData.access.forEach(function(permission) {
                if (permission.type === perm) {
                  found = true;
                  permission.resources = permission.resources || [];
                  permission.resources.concat(grabIds(value));
                }
              });

              // Add a permission, because one was not found.
              if (!found) {
                submissionData.access.push({
                  type: perm,
                  resources: grabIds(value)
                });
              }
            }
          });

          // Show the submit message and say the form is no longer submitting.
          var onSubmit = function(submission, message) {
            $scope.showAlerts({
              type: 'success',
              message: message
            });
            form.submitting = false;
          };

          // Called when a submission has been made.
          var onSubmitDone = function(method, submission) {
            var message = '';
            if ($scope.options && $scope.options.submitMessage) {
              message = $scope.options.submitMessage;
            }
            else {
              message = 'Submission was ' + ((method === 'put') ? 'updated' : 'created') + '.';
            }
            onSubmit(submission, message);
            // Trigger the form submission.
            $scope.$emit('formSubmission', submission);
          };

          // Allow the form to be completed externally.
          $scope.$on('submitDone', function(event, submission, message) {
            onSubmit(submission, message);
          });

          // Allow an error to be thrown externally.
          $scope.$on('submitError', function(event, error) {
            FormioScope.onError($scope, $element)(error);
          });

          var submitEvent = $scope.$emit('formSubmit', submissionData);
          if (submitEvent.defaultPrevented) {
            // Listener wants to cancel the form submission
            form.submitting = false;
            return;
          }

          // Make sure to make a copy of the submission data to remove bad characters.
          submissionData = angular.copy(submissionData);

          // Allow custom action urls.
          if ($scope.action) {
            var method = submissionData._id ? 'put' : 'post';
            $http[method]($scope.action, submissionData).success(function(submission) {
              Formio.clearCache();
              onSubmitDone(method, submission);
            }).error(FormioScope.onError($scope, $element))
              .finally(function() {
                form.submitting = false;
              });
          }

          // If they wish to submit to the default location.
          else if ($scope.formio) {
            // copy to remove angular $$hashKey
            $scope.formio.saveSubmission(submissionData, $scope.formioOptions).then(function(submission) {
              onSubmitDone(submission.method, submission);
            }, FormioScope.onError($scope, $element)).finally(function() {
              form.submitting = false;
            });
          }
          else {
            $scope.$emit('formSubmission', submissionData);
          }
        };
      }
    ],
    templateUrl: 'formio.html'
  };
};
<|endoftext|>


<commit_before>define([
	"assets/resource",
	"engine/input/input",
	"assets/loader",
	"basic/utils",
	"engine/map/layer"
], function(
	Resource,
	Input,
	Loader,
	Utils,
	Layer
) {
	// Repository of loaded json-maps
	var MapRepository = {};

	var Map = Resource.subclass({
		initialize: function(mapName) {
			if(typeof mapName !== "undefined")
				Resource.prototype.initialize.apply(this, arguments);
			this.mapName = mapName || "";
			this.callbacks = [];
			this.layers = [];
		},

		load: function(callback) {
			callback = callback || function() {};
			// TODO: look, if already fetched from server (implemented by assetmanager)
			// if so: convert json to map and call callbacks
			// load otherwise
			Loader.loadMap(
				this.mapName,
				Utils.Function.bind(
					function(json) {
						// convert given json to map
						Map.fromJson(json, this);

						// fire callback
						callback(this);
					},
					this
				)
			);

			return this;
		},

		update: function() {
			this.sortLayers();
			for(var index in this.layers) {
				if(!this.layers.hasOwnProperty(index)) continue;

				this.layers[index].update();
			}
		},

		draw: function(renderer) {
			// draw each layer separately
			var layerCount = this.layers.length;
			for(var i = 0; i < layerCount; i++) {
				var layer = this.layers[i];

				layer.draw(renderer);
			}

			this.combineLayers(renderer);
		},

		combineLayers: function(renderer) {
			var layerCount = this.layers.length;
			for(var i = 0; i < layerCount; i++) {
				var layer = this.layers[i];

				if(layer.enabled)
					renderer.drawLayer(layer);
			}
		},

		// -------------------------------------------------------------------------
		// Layers

		addLayer: function(layer) {
			this.layers.push(layer);
		},

		getLayers: function() {
			return this.layers;
		},

		sortLayers: function() {
			this.layers = Utils.Array.sort(this.layers, "zIndex");
		},

		removeLayerByName: function( name ) {
			for( var i = 0; i < this.layers.length; i++ ) {
				if( this.layers[i].name == name ) {
					this.layers.splice( i, 1 );
					return true;
				}
			}
			return false;
		},

		// -------------------------------------------------------------------------
		// Interactions

		initDatGui: function(datGui) {
			if(!Bloob.debug || !Bloob.debug.datGui) return;

			var mapFolder = datGui.addFolder('Map');
			mapFolder.open();
			for(var index in this.layers) {
				if(!this.layers.hasOwnProperty(index)) continue;

				this.layers[index].initDatGui(mapFolder, index);
			}

			return this;
		}
	});

	Map.prototype.toJson = function(map) {
		var resultJson = {
			"name": this.mapName,
			"spawnPoints": {
				"default": {"x": 0, "y": 0}
			},
			"layers": []
		};

		// Convert Layers to json.
		for(var index in this.layers) {
			if(!this.layers.hasOwnProperty(index)) continue;
			resultJson.layers.push(this.layers[index].toJson());
		}

		return resultJson;
	};

	// TODO: move to JsonToMapConverter
	//add convenient method
	Map.fromJson = function(mapJson, map) {
		map = map || new Map();

		// convert layers
		for(var index in mapJson.layers) {
			if(!mapJson.layers.hasOwnProperty(index)) continue;

			map.layers.push(Layer.fromJson(mapJson.layers[index]));
		}

		return map;
	};

	return Map;
});
<commit_msg>Layer: corrected indention and use forEach<commit_after>define(function(require) { 'use strict';
  var Resource = require('assets/resource'),
      Input = require('engine/input/input'),
      Loader = require('assets/loader'),
      Utils = require('basic/utils'),
      Layer = require('engine/map/layer');

  // Repository of loaded json-maps
  var MapRepository = {};

  var Map = Resource.subclass({
    initialize: function(mapName) {
      if(typeof mapName !== 'undefined')
        Resource.prototype.initialize.apply(this, arguments);
      this.mapName = mapName || '';
      this.callbacks = [];
      this.layers = [];
    },

    load: function(callback) {
      callback = callback || function() {};
      // TODO: look, if already fetched from server (implemented by assetmanager)
      // if so: convert json to map and call callbacks
      // load otherwise
      Loader.loadMap(
        this.mapName,
        Utils.Function.bind(
          function(json) {
            // convert given json to map
            Map.fromJson(json, this);

            // fire callback
            callback(this);
          },
          this
        )
      );

      return this;
    },

    update: function() {
      this.sortLayers();
      this.layers.forEach(function(layer) {
        layer.update();
      });
    },

    draw: function(renderer) {
      // draw each layer separately
      this.layers.forEach(function(layer) {
        layer.draw(renderer);
      });

      this.combineLayers(renderer);
    },

    combineLayers: function(renderer) {
      this.layers.forEach(function(layer) {
        if(layer.enabled) {
          renderer.drawLayer(layer);
        }
      });
    },

    // -------------------------------------------------------------------------
    // Layers

    addLayer: function(layer) {
      this.layers.push(layer);
    },

    getLayers: function() {
      return this.layers;
    },

    sortLayers: function() {
      this.layers = Utils.Array.sort(this.layers, 'zIndex');
    },

    removeLayerByName: function( name ) {
      for( var i = 0; i < this.layers.length; i++ ) {
        if( this.layers[i].name == name ) {
          this.layers.splice( i, 1 );
          return true;
        }
      }
      return false;
    },

    // -------------------------------------------------------------------------
    // Interactions

    initDatGui: function(datGui) {
      if(!Bloob.debug || !Bloob.debug.datGui) return;

      var mapFolder = datGui.addFolder('Map');
      mapFolder.open();
      this.layers.forEach(function(layer, index) {
        layer.initDatGui(mapFolder, index);
      });

      return this;
    }
  });

  Map.prototype.toJson = function(map) {
    var resultJson = {
      'name': this.mapName,
      'spawnPoints': {
        'default': {'x': 0, 'y': 0}
      },
      'layers': []
    };

    // Convert Layers to json.
    this.layers.forEach(function(layer) {
      resultJson.layers.push(layer.toJson());
    });

    return resultJson;
  };

  // TODO: move to JsonToMapConverter
  //add convenient method
  Map.fromJson = function(mapJson, map) {
    map = map || new Map();

    // convert layers
    mapJson.layers.forEach(function(layer) {
      map.layers.push(Layer.fromJson(layer));
    });

    return map;
  };

  return Map;
});
<|endoftext|>


<commit_before>module.exports = function initUsers(app){
    "use strict";

    var users = {},
        internal = {},
        _ = require("underscore"),
        async = require("async"),
        AVAILABLE_USER_ROLES = ['editor', 'author', 'reader', 'admin', 'none'],
        MINIMUM_USER_NAME_LENGTH = 4,
        MINIMUM_PASSWORD_LENGTH = 6,
        LOGGING_CATEGORY = "GRASSHOPPER-USER";

    internal.app = app;

    function validateUser(userObj, cb){
        var props = Object.keys(userObj),
            mandatoryProps = [
                'name',
                'password',
                'email',
                'role',
                'login'];

        async.each(mandatoryProps, function(prop, callback) {
                if(!_.contains(props, prop)){
                    return callback(new Error("'" + prop + "' property not found when validating the user."));
                }
                else if(prop == "login" && (userObj[prop] == null || (userObj[prop] != null && userObj[prop].trim().length < MINIMUM_USER_NAME_LENGTH))){
                    return callback(new Error("The login property cannot be null and has to have a minimum length of " + MINIMUM_USER_NAME_LENGTH + " characters."));
                }
                else if(prop == "password" && (userObj[prop] == null || (userObj[prop] != null && userObj[prop].trim().length < MINIMUM_PASSWORD_LENGTH))){
                    return callback(new Error("The password property cannot be null and has to have a minimum length of " + MINIMUM_PASSWORD_LENGTH+ " characters."));
                }
                else if(prop == "role" && !_.contains(AVAILABLE_USER_ROLES, userObj[prop])){
                    return callback(new Error("The role provided is not understood. Available roles are: " + AVAILABLE_USER_ROLES.join(",")));
                }

                return callback(null);

            },function (err) {
                //All required fields have been checked. Now check optional params.
                if(err){
                    internal.app.log.error(LOGGING_CATEGORY, err.message);
                }
                else {
                    if(userObj["permissions"] != null){
                        //If permissions exist it has to be an array.
                        if(userObj["permissions"] instanceof Array){
                            //Empty array is ok, just validate of params exist.
                            if(userObj["permissions"].length > 0){
                                for(var x = 0; x < userObj["permissions"].length; x++){
                                    //Each element must be an object
                                    if(userObj["permissions"][x] instanceof Object){
                                        var mandatoryProps = ["nodeid", "role"];

                                        for(var y = 0; y < mandatoryProps.length; y++){
                                            var props = Object.keys(userObj["permissions"][x]);

                                            if(!_.contains(props, mandatoryProps[y])){
                                                err = new Error(mandatoryProps[y] + " not supplied.");
                                                break;
                                            }
                                            else if(mandatoryProps[y] == "role" && !_.contains(AVAILABLE_USER_ROLES, userObj["permissions"][x][mandatoryProps[y]])){
                                                err = new Error("role provided is not valid. ");
                                                break;
                                            }
                                        }

                                    }
                                    else {
                                        err = new Error("permissions array elements must be objects.");
                                        break;
                                    }
                                }
                            }
                        }
                        else {
                            err = new Error("permissions object must be a valid array.");
                        }
                    }
                }

                cb.call(this, err);
            }
        );

    }

    /**
     * Method to be called when you want to create a user in the system.
     * When saving a user all mandatory fields will be checked.
     * @param userObj
     * @param callback
     */
    users.create = function(userObj, callback){

        validateUser(userObj,function(err){
            if(err){
                callback.call(this, err);
            }
            else {
                internal.app.db.createUser(userObj, function(err, val){
                    callback.call(this, err, val);
                });
            }
        });

    };

    users.getById = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            callback.call(this, err, val);
        });
    };

    users.getByUserName = function(userName, callback){

    };

    users.getByEmail = function(userEmail, callback){

    };

    users.update = function(userObj, callback){
        validateUser(userObj,function(err){
            if(err){
                callback.call(this, err);
            }
            else {
                internal.app.db.updateUser(userObj, function(err){
                    callback.call(this, err);
                });
            }
        });
    };

    users.disable = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                if(val != null){
                    val.enabled = false;

                    internal.app.db.updateUser(val, function(err){
                        callback.call(this, err);
                    });
                }
                else {
                    callback.call(this, new Error("Cannot disable user. User with provided ID could not be found."));
                }
            }
        });
    };

    users.enable = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                if(val != null){
                    val.enabled = true;

                    internal.app.db.updateUser(val, function(err){
                        callback.call(this, err);
                    });
                }
                else {
                    callback.call(this, new Error("Cannot enable user. User with provided ID could not be found."));
                }
            }
        });
    };

    users.delete = function(userId, callback){

        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                internal.app.db.deleteUserById(userId, function(err){
                    if(err){
                        console.log(err);
                        callback.call(this, err);
                    }
                    else {
                        callback.call(this, null);
                    }
                });
            }
        });
    };

    return users;
};

<commit_msg>Auto set 'enabled' if it is not set<commit_after>module.exports = function initUsers(app){
    "use strict";

    var users = {},
        internal = {},
        _ = require("underscore"),
        async = require("async"),
        AVAILABLE_USER_ROLES = ['editor', 'author', 'reader', 'admin', 'none'],
        MINIMUM_USER_NAME_LENGTH = 4,
        MINIMUM_PASSWORD_LENGTH = 6,
        LOGGING_CATEGORY = "GRASSHOPPER-USER";

    internal.app = app;

    function validateUser(userObj, cb){
        var props = Object.keys(userObj),
            mandatoryProps = [
                'name',
                'password',
                'email',
                'role',
                'login'];

        async.each(mandatoryProps, function(prop, callback) {
                if(!_.contains(props, prop)){
                    return callback(new Error("'" + prop + "' property not found when validating the user."));
                }
                else if(prop == "login" && (userObj[prop] == null || (userObj[prop] != null && userObj[prop].trim().length < MINIMUM_USER_NAME_LENGTH))){
                    return callback(new Error("The login property cannot be null and has to have a minimum length of " + MINIMUM_USER_NAME_LENGTH + " characters."));
                }
                else if(prop == "password" && (userObj[prop] == null || (userObj[prop] != null && userObj[prop].trim().length < MINIMUM_PASSWORD_LENGTH))){
                    return callback(new Error("The password property cannot be null and has to have a minimum length of " + MINIMUM_PASSWORD_LENGTH+ " characters."));
                }
                else if(prop == "role" && !_.contains(AVAILABLE_USER_ROLES, userObj[prop])){
                    return callback(new Error("The role provided is not understood. Available roles are: " + AVAILABLE_USER_ROLES.join(",")));
                }

                return callback(null);

            },function (err) {
                //All required fields have been checked. Now check optional params.
                if(err){
                    internal.app.log.error(LOGGING_CATEGORY, err.message);
                }
                else {
                    if(userObj["permissions"] != null){
                        //If permissions exist it has to be an array.
                        if(userObj["permissions"] instanceof Array){
                            //Empty array is ok, just validate of params exist.
                            if(userObj["permissions"].length > 0){
                                for(var x = 0; x < userObj["permissions"].length; x++){
                                    //Each element must be an object
                                    if(userObj["permissions"][x] instanceof Object){
                                        var mandatoryProps = ["nodeid", "role"];

                                        for(var y = 0; y < mandatoryProps.length; y++){
                                            var props = Object.keys(userObj["permissions"][x]);

                                            if(!_.contains(props, mandatoryProps[y])){
                                                err = new Error(mandatoryProps[y] + " not supplied.");
                                                break;
                                            }
                                            else if(mandatoryProps[y] == "role" && !_.contains(AVAILABLE_USER_ROLES, userObj["permissions"][x][mandatoryProps[y]])){
                                                err = new Error("role provided is not valid. ");
                                                break;
                                            }
                                        }

                                    }
                                    else {
                                        err = new Error("permissions array elements must be objects.");
                                        break;
                                    }
                                }
                            }
                        }
                        else {
                            err = new Error("permissions object must be a valid array.");
                        }
                    }
                }

                cb.call(this, err);
            }
        );

    }

    /**
     * Method to be called when you want to create a user in the system.
     * When saving a user all mandatory fields will be checked.
     * @param userObj
     * @param callback
     */
    users.create = function(userObj, callback){

        validateUser(userObj,function(err){
            if(err){
                callback.call(this, err);
            }
            else {
                if(userObj.enabled == null){
                    userObj.enabled = true;
                }

                internal.app.db.createUser(userObj, function(err, val){
                    callback.call(this, err, val);
                });
            }
        });

    };

    users.getById = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            callback.call(this, err, val);
        });
    };

    users.getByEmail = function(userEmail, callback){

    };

    users.getByLogin = function(userLogin, callback){

    };

    users.update = function(userObj, callback){
        validateUser(userObj,function(err){
            if(err){
                callback.call(this, err);
            }
            else {
                internal.app.db.updateUser(userObj, function(err){
                    callback.call(this, err);
                });
            }
        });
    };

    users.disable = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                if(val != null){
                    val.enabled = false;

                    internal.app.db.updateUser(val, function(err){
                        callback.call(this, err);
                    });
                }
                else {
                    callback.call(this, new Error("Cannot disable user. User with provided ID could not be found."));
                }
            }
        });
    };

    users.enable = function(userId, callback){
        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                if(val != null){
                    val.enabled = true;

                    internal.app.db.updateUser(val, function(err){
                        callback.call(this, err);
                    });
                }
                else {
                    callback.call(this, new Error("Cannot enable user. User with provided ID could not be found."));
                }
            }
        });
    };

    users.delete = function(userId, callback){

        internal.app.db.getUserById(userId, function(err, val){
            if(err){
                callback.call(this, err, val);
            }
            else {
                internal.app.db.deleteUserById(userId, function(err){
                    if(err){
                        console.log(err);
                        callback.call(this, err);
                    }
                    else {
                        callback.call(this, null);
                    }
                });
            }
        });
    };

    return users;
};

<|endoftext|>