<commit_before>/*
enum TimezoneDirective { "respectTimezone", "ignoreTimezone" };

interface TaskScheduler {
  Promise getPendingTasks();
  Promise add(Date date, TimezoneDirective timezoneDirective, optional any data);
  Promise remove(DOMString taskId);
};
*/

var TimezoneDirective = {respectTimezone: "respectTimezone", ignoreTimezone: "ignoreTimezone"};
//alert (TimezoneDirective['respectTimezone'] );

define(function(require) {
	'use strict';
	var 	WebIDL = require("WebIDL"),
		EventTarget = require("DOM/EventTarget"),
		IDLDate = require("WebIDL/types/Date"),
		IDLBoolean = require("WebIDL/types/Boolean");

	function TaskScheduler() {
		EventTarget.call(this);
	}

	TaskScheduler.prototype = new EventTarget();

	/*
	 *  When invoked, the add(date, timezoneDirective[, data]) method must run the following steps:
	 *
	 * Make a request to the system to schedule a new task for the current application that will trigger at the given date. Depending on the timezoneDirective argument,
	 * 	the system will honor the timezone information of that [ECMASCRIPT] Date object or not. The system must associate the JSON-serializable data with the task if provided.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "InvalidStateError" if the date argument is in the past,
	 * 		"QuotaExceededError" if the data argument exceeds an implementation-dependent size limit, and "UnknownError" otherwise.
	 * 	Run resolver's internal reject algorithm with error as value.
	 * When the operation completes successfully, run these substeps:
	 * 	Let task be a new ScheduledTask object.
	 * 	Set task's id attribute to the unique identifier returned by the system for the newly registered task.
	 * 	Set task's date attribute to the date argument.
	 * 	Set task's timezoneDirective attribute to the timezoneDirective argument.
	 * 	Set task's data attribute to the data argument, if provided.
	 * Run resolver's intenal fulfill algorithm with task as value.
	 *
	 */
	/*
	 *  I've decided to use localStorage to create fake database
	 */

	/*
	 * Executing add function from console:
	 *
	 *  window.tasks.add(new Date(), true).then(function(){ alert("OK")}, function(){ alert("FAIL")});   <-- with Promise
	 *  window.tasks.add(new Date(), true) <-- without Promise
	 *
	 */

	TaskScheduler.prototype.add = function(taskTime, respectTZ, data) {
	    return new Promise(function(resolver) {

		    var idlDate = new IDLDate(taskTime),
			respectTZ = IDLBoolean(respectTZ),
			triggerTime = idlDate.value.getTime() - Date.now();

		    //Check if time for task is not in the past or now()
		    if(triggerTime <= 0) {
			throw new TypeError("Invalid time");
		    }
		    // task data object
		    var task_data = {
			date: idlDate.value,
			respectTZ: (respectTZ === undefined) ? true : !! (respectTZ),
			data: (data === undefined) ? null: (data),
			timeout: setTimeout((function(task_data) {
				return function() {
					var ev = new CustomEvent("task");
					task_data.dispatchEvent(ev);
				};
			}(this)), triggerTime)
		    };

		    // prepare key to store task: it is date  + time + random number
		    var key = "Date:"+task_data['date'].getUTCDate()+"-"+task_data['date'].getUTCMonth()+"-"+task_data['date'].getUTCFullYear()
		    +" "+task_data['date'].getHours() + ":"+task_data['date'].getMinutes() +":"+task_data['date'].getSeconds() + " " + Math.round(Math.random()*100000);

		    console.log("key: "+ key);
		    console.log("item added: "+JSON.stringify(task_data));

		    //Adding item to localStorage
		    var status = false;
		    try{
			localStorage.setItem (key, JSON.stringify(task_data));
			status = true;
		    }
		    catch(err){
			console.log("ERROR!!! Item can NOT be added to localStorage. error:"+ err.message);
		    }
		    // Resolver:
		    resolver.fulfill(status);

		    /*
		     *
		     *	Check if data was properly stored this code can be removed after testing of this interface
		     *
		     */

		    /*
		    //Get item from localStorage
		    try{
		      var stored_task = localStorage.getItem (key);
		    }
		    catch(err){
			console.log("ERROR!!! Item can NOT be get from localStorage. error:"+ err.message);
		    }

		    //Convert data from localStorage to object
		    if (stored_task) {
		      	var task_data_get = JSON.parse(stored_task);
		    }

		    // Check if task is stored correctly, if so we can return success
		    if ( JSON.stringify(task_data['date']) === JSON.stringify(task_data_get['date'])
			&& task_data['respectTZ'] === task_data_get['respectTZ']
			&& task_data['timeout'] === task_data_get['timeout']){

			console.log ("EUREKA! Date in localStorage is correct.");
			resolver.fulfill(true);
		    }
		    else{
			console.log("BUUUUUUUUUUUU! something went terribly wrong. More details below:");

			console.log("date set:"+task_data['date'] + " got: "+  (new Date(task_data_get['date'])));
			console.log("respectTZ set: "+task_data['respectTZ'] + " got: "+  task_data_get['respectTZ']);
			console.log("timeout set:"+task_data['timeout'] + " got: "+  task_data_get['timeout']);

			resolver.fulfill(false);
		    }
		    */
	    });
	};

	/*
	 *  When invoked, the remove(taskId) method must run the following steps:
	 *
	 * Make a request to the system to unregister the task with the given unique taskId identifier.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "UnknownError".
	 * 	Run resolver's internal reject algorithm with error as value.
	 * When the operation completes successfully, run these substeps:
	 * 	Let removed be a boolean value.
	 * 	Set removed to true if the task was removed, and to false if there was no task with the given identifier.
	 * Run resolver's intenal fulfill algorithm with removed as value.
	 *
	 */

	TaskScheduler.prototype.remove = function(taskId) {
	    return new Promise(function(resolver) {
		console.log("Removing task id: "+taskId);
		try{
		    localStorage.removeItem(taskId);
		}
		catch(err){
		    console.log("ERROR!!! Item can NOT be removed from localStorage. error:"+ err.message);
		    resolver.fulfill(false);
		}
		resolver.fulfill(true);
	    });
	};

	/*
	 * When invoked, the getPendingTasks() method must run the following steps:
	 *
	 * Make a request to the system to retrieve the tasks that were registered by the current application and whose scheduled time is in the future.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "UnknownError".
	 * 	Run resolver's internal reject algorithm with error as value.
	 * 	When the operation completes successfully, run these substeps:
	 * Let tasks be a new array containing the ScheduledTask objects that were retrieved.
	 * Run resolver's intenal fulfill algorithm with tasks as value.
	 *
	 */

	TaskScheduler.prototype.getPendingTasks = function() {
	     return new Promise(function(resolver) {

		var all_pending_tasks=[],
		    current_date=new Date(),
		    tmp_stored_item = null,
		    tmp_from_date=null,
		    tmp_date=null,
		    tmp_time=null;

		console.log( localStorage.length);
		// get all tasks from localStorage
		for (var i=1; i <= localStorage.length; i++)  {
		    console.log("item ("+i+ ") with key "+ localStorage.key(i)+": "+localStorage.getItem(i));
		    //check if item is in the future
		    tmp_stored_item = JSON.parse(localStorage.getItem(localStorage.key(i)));

		    //display full item
		    console.log(tmp_stored_item['date']);
		    //date
		    tmp_date = tmp_stored_item['date'].substring(0, tmp_stored_item['date'].indexOf('T'));
		    console.log(tmp_date);
		    //time
		    tmp_time = tmp_stored_item['date'].substring(tmp_stored_item['date'].indexOf('T')+1, tmp_stored_item['date'].indexOf('.'));
		    console.log(tmp_time);

		    //Check date and if fits add to pending tasks array, if date is old remove it
		    tmp_from_date = current_date.getUTCFullYear()+"-"+current_date.getUTCMonth()+"-"+current_date.getUTCDate();
		    if ((new Date(tmp_from_date)) - (new Date (tmp_date)) <= 0){
			//TODO compare time if it is the same date

			//add to pending tasks object
			all_pending_tasks.push({key: localStorage.key(i), data: tmp_stored_item});
		    }
		    else{
			//remove old task
			console.log ("Old task: TO BE REMOVED: " + tmp_date );
			TaskScheduler.prototype.remove(localStorage.key(i));

		    }
		    console.log("List of all PENDING TASKS from date:"+tmp_from_date);
		    console.log(all_pending_tasks);

		    //nulling all variables - just in case
		    tmp_stored_item = null,
		    tmp_from_date=null,
		    tmp_date=null,
		    tmp_time=null;
		}
		resolver.fulfill(true);

	     });
	};

	WebIDL.exportInterface(TaskScheduler, "TaskScheduler");

	return TaskScheduler;
});
<commit_msg>Update TaskScheduler.js<commit_after>/*
enum TimezoneDirective { "respectTimezone", "ignoreTimezone" };

interface TaskScheduler {
  Promise getPendingTasks();
  Promise add(Date date, TimezoneDirective timezoneDirective, optional any data);
  Promise remove(DOMString taskId);
};
*/

var TimezoneDirective = {respectTimezone: "respectTimezone", ignoreTimezone: "ignoreTimezone"};
//alert (TimezoneDirective['respectTimezone'] );

define(function(require) {
	'use strict';
	var 	WebIDL = require("WebIDL"),
		EventTarget = require("DOM/EventTarget"),
		IDLDate = require("WebIDL/types/Date"),
		IDLBoolean = require("WebIDL/types/Boolean");

	function TaskScheduler() {
		EventTarget.call(this);
	}

	TaskScheduler.prototype = new EventTarget();

	/*
	 *  When invoked, the add(date, timezoneDirective[, data]) method must run the following steps:
	 *
	 * Make a request to the system to schedule a new task for the current application that will trigger at the given date. Depending on the timezoneDirective argument,
	 * 	the system will honor the timezone information of that [ECMASCRIPT] Date object or not. The system must associate the JSON-serializable data with the task if provided.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "InvalidStateError" if the date argument is in the past,
	 * 		"QuotaExceededError" if the data argument exceeds an implementation-dependent size limit, and "UnknownError" otherwise.
	 * 	Run resolver's internal reject algorithm with error as value.
	 * When the operation completes successfully, run these substeps:
	 * 	Let task be a new ScheduledTask object.
	 * 	Set task's id attribute to the unique identifier returned by the system for the newly registered task.
	 * 	Set task's date attribute to the date argument.
	 * 	Set task's timezoneDirective attribute to the timezoneDirective argument.
	 * 	Set task's data attribute to the data argument, if provided.
	 * Run resolver's intenal fulfill algorithm with task as value.
	 *
	 */
	/*
	 *  I've decided to use localStorage to create fake database
	 */

	/*
	 * Executing add function from console:
	 *
	 *  window.tasks.add(new Date(), true).then(function(){ alert("OK")}, function(){ alert("FAIL")});   <-- with Promise
	 *  window.tasks.add(new Date(), true) <-- without Promise
	 *
	 */

	TaskScheduler.prototype.add = function(taskTime, respectTZ, data) {
	    return new Promise(function(resolver) {

		    var idlDate = new IDLDate(taskTime),
			respectTZ = IDLBoolean(respectTZ),
			triggerTime = idlDate.value.getTime() - Date.now();

		    //Check if time for task is not in the past or now()
		    if(triggerTime <= 0) {
			resolver.reject("Invalid time");
		    }
		    // task data object
		    var task_data = {
			date: idlDate.value,
			respectTZ: (respectTZ === undefined) ? true : !! (respectTZ),
			data: (data === undefined) ? null: (data),
			timeout: setTimeout((function(task_data) {
				return function() {
					var ev = new CustomEvent("task");
					task_data.dispatchEvent(ev);
				};
			}(this)), triggerTime)
		    };

		    // prepare key to store task: it is date  + time + random number
		    var key = "Date:"+task_data['date'].getUTCDate()+"-"+task_data['date'].getUTCMonth()+"-"+task_data['date'].getUTCFullYear()
		    +" "+task_data['date'].getHours() + ":"+task_data['date'].getMinutes() +":"+task_data['date'].getSeconds() + " " + Math.round(Math.random()*100000);

		    console.log("key: "+ key);
		    console.log("item added: "+JSON.stringify(task_data));

		    //Adding item to localStorage
		    var status = false;
		    try{
			localStorage.setItem (key, JSON.stringify(task_data));
			status = true;
		    }
		    catch(err){
			console.log("ERROR!!! Item can NOT be added to localStorage. error:"+ err.message);
		    }
		    // Resolver:
		    resolver.fulfill(status);

		    /*
		     *
		     *	Check if data was properly stored this code can be removed after testing of this interface
		     *
		     */

		    /*
		    //Get item from localStorage
		    try{
		      var stored_task = localStorage.getItem (key);
		    }
		    catch(err){
			console.log("ERROR!!! Item can NOT be get from localStorage. error:"+ err.message);
		    }

		    //Convert data from localStorage to object
		    if (stored_task) {
		      	var task_data_get = JSON.parse(stored_task);
		    }

		    // Check if task is stored correctly, if so we can return success
		    if ( JSON.stringify(task_data['date']) === JSON.stringify(task_data_get['date'])
			&& task_data['respectTZ'] === task_data_get['respectTZ']
			&& task_data['timeout'] === task_data_get['timeout']){

			console.log ("EUREKA! Date in localStorage is correct.");
			resolver.fulfill(true);
		    }
		    else{
			console.log("BUUUUUUUUUUUU! something went terribly wrong. More details below:");

			console.log("date set:"+task_data['date'] + " got: "+  (new Date(task_data_get['date'])));
			console.log("respectTZ set: "+task_data['respectTZ'] + " got: "+  task_data_get['respectTZ']);
			console.log("timeout set:"+task_data['timeout'] + " got: "+  task_data_get['timeout']);

			resolver.fulfill(false);
		    }
		    */
	    });
	};

	/*
	 *  When invoked, the remove(taskId) method must run the following steps:
	 *
	 * Make a request to the system to unregister the task with the given unique taskId identifier.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "UnknownError".
	 * 	Run resolver's internal reject algorithm with error as value.
	 * When the operation completes successfully, run these substeps:
	 * 	Let removed be a boolean value.
	 * 	Set removed to true if the task was removed, and to false if there was no task with the given identifier.
	 * Run resolver's intenal fulfill algorithm with removed as value.
	 *
	 */

	TaskScheduler.prototype.remove = function(taskId) {
	    return new Promise(function(resolver) {
		console.log("Removing task id: "+taskId);
		try{
		    localStorage.removeItem(taskId);
		}
		catch(err){
		    console.log("ERROR!!! Item can NOT be removed from localStorage. error:"+ err.message);
		    resolver.fulfill(false);
		}
		resolver.fulfill(true);
	    });
	};

	/*
	 * When invoked, the getPendingTasks() method must run the following steps:
	 *
	 * Make a request to the system to retrieve the tasks that were registered by the current application and whose scheduled time is in the future.
	 * Let promise be a new Promise object and resolver its associated resolver.
	 * Return promise and run the remaining steps asynchronously.
	 * If an error occurs, run these substeps and then terminate these steps:
	 * 	Let error be a new DOMError object whose name is "UnknownError".
	 * 	Run resolver's internal reject algorithm with error as value.
	 * 	When the operation completes successfully, run these substeps:
	 * Let tasks be a new array containing the ScheduledTask objects that were retrieved.
	 * Run resolver's intenal fulfill algorithm with tasks as value.
	 *
	 */

	TaskScheduler.prototype.getPendingTasks = function() {
	     return new Promise(function(resolver) {

		var all_pending_tasks=[],
		    current_date=new Date(),
		    tmp_stored_item = null,
		    tmp_from_date=null,
		    tmp_date=null,
		    tmp_time=null;

		console.log( localStorage.length);
		// get all tasks from localStorage
		for (var i=1; i <= localStorage.length; i++)  {
		    console.log("item ("+i+ ") with key "+ localStorage.key(i)+": "+localStorage.getItem(i));
		    //check if item is in the future
		    tmp_stored_item = JSON.parse(localStorage.getItem(localStorage.key(i)));

		    //display full item
		    console.log(tmp_stored_item['date']);
		    //date
		    tmp_date = tmp_stored_item['date'].substring(0, tmp_stored_item['date'].indexOf('T'));
		    console.log(tmp_date);
		    //time
		    tmp_time = tmp_stored_item['date'].substring(tmp_stored_item['date'].indexOf('T')+1, tmp_stored_item['date'].indexOf('.'));
		    console.log(tmp_time);

		    //Check date and if fits add to pending tasks array, if date is old remove it
		    tmp_from_date = current_date.getUTCFullYear()+"-"+current_date.getUTCMonth()+"-"+current_date.getUTCDate();
		    if ((new Date(tmp_from_date)) - (new Date (tmp_date)) <= 0){
			//TODO compare time if it is the same date

			//add to pending tasks object
			all_pending_tasks.push({key: localStorage.key(i), data: tmp_stored_item});
		    }
		    else{
			//remove old task
			console.log ("Old task: TO BE REMOVED: " + tmp_date );
			TaskScheduler.prototype.remove(localStorage.key(i));

		    }
		    console.log("List of all PENDING TASKS from date:"+tmp_from_date);
		    console.log(all_pending_tasks);

		    //nulling all variables - just in case
		    tmp_stored_item = null,
		    tmp_from_date=null,
		    tmp_date=null,
		    tmp_time=null;
		}
		resolver.fulfill(true);

	     });
	};

	WebIDL.exportInterface(TaskScheduler, "TaskScheduler");

	return TaskScheduler;
});
<|endoftext|>


<commit_before>
// Copyright 2016, EMC, Inc.

'use strict';

var di = require('di');
module.exports = EmcRedfishCatalogJobFactory;
di.annotate(EmcRedfishCatalogJobFactory, new di.Provide('Job.Emc.Redfish.Catalog'));
di.annotate(EmcRedfishCatalogJobFactory, new di.Inject(
    'Job.Base',
    'Logger',
    'Promise',
    'Assert',
    'Util',
    'Services.Waterline',
    'Services.Encryption',
    '_',
    'JobUtils.RedfishTool'
));

function EmcRedfishCatalogJobFactory(
    BaseJob,
    Logger,
    Promise,
    assert,
    util,
    waterline,
    encryption,
    _,
    RedfishTool
) {
    var logger = Logger.initialize(EmcRedfishCatalogJobFactory);

    /**
     * @param {Object} options task options object
     * @param {Object} context graph context object
     * @param {String} taskId running task identifier
     * @constructor
     */
    function EmcRedfishCatalogJob(options, context, taskId) {
        EmcRedfishCatalogJob.super_.call(this,
                                   logger,
                                   options,
                                   context,
                                   taskId);
        this.chassis = this.context.chassis || [];
        if(_.isEmpty(this.chassis)) {
            this.chassis = [ this.context.target ]
        }
        this.redfish = new RedfishTool();
    }

    util.inherits(EmcRedfishCatalogJob, BaseJob);

    /**
     * @memberOf EmcRedfishCatalogJob
     */
    EmcRedfishCatalogJob.prototype._run = function() {
        var self = this;
        return Promise.resolve(self.chassis)
        .each(self.catalogElements.bind(self))
        .each(self.catalogHbas.bind(self))
        .each(self.catalogAggregators.bind(self))
        .each(self.catalogSpine.bind(self))
        .then(function() {
            self._done();
        })
        .catch(function(err) {
            self._done(err);
        });
    };

    EmcRedfishCatalogJob.prototype.catalogSpine = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var spineModId = _.get(res.body, 'Oem.Emc.SpineModules', {})['@odata.id'];
            if (_.isUndefined(spineModId)) {
                throw new Error('Missing SpineModules Resource');
            }
            return self.redfish.clientRequest(spineModId)
            .then(function(res) {
                var members = _.get(res, 'body.Members', {});
                assert.ok(Array.isArray(members), 'SpineModules Member List');
                return members;
            });
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'SpineModule Data');
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Type: res.body.Type,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: [],
                Switches: [],
                Vpd: {}
            };
            return Promise.each([
                self.redfish.clientRequest(_.get(res.body, 'Controllers', {})['@odata.id']),
                self.redfish.clientRequest(_.get(res.body, 'Switches', {})['@odata.id'])
            ], function(resList) {
                return Promise.map(_.get(resList.body, 'Members', []), function(member) {
                    var id = member['@odata.id'];
                    return self.redfish.clientRequest(id)
                    .then(function(res) {
                        data[id.match(/Controllers/g) ? 'Controllers' : 'Switches'].push(res.body);
                    });
                });
            })
            .then(function() {
                return self.redfish.clientRequest(_.get(res.body, 'Vpd', {})['@odata.id'])
                .then(function(res) {
                    data['Vpd'] = res.body;
                    catalogData.push(data);
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'SpineModules',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('SpineModules', {
                error: err
            });
            if (err.message !== 'Missing SpineModules Resource') {
                throw err;
            }
            return nodeId;
        });
    });

    EmcRedfishCatalogJob.prototype.catalogHbas = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var hbaId = _.get(res.body, 'Oem.Emc.Hbas', {})['@odata.id'];
            if(_.isUndefined(hbaId)) {
                throw new Error('Missing HBA Resource');
            }
            return self.redfish.clientRequest(hbaId);
        })
        .then(function(res) {
            var members = _.get(res, 'body.Members', {});
            assert.ok(Array.isArray(members), 'HBA Member List');
            return members;
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'HBA Data');
            var ctrlId = _.get(res.body, 'Controllers', {})['@odata.id'];
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: []
            };
            return self.redfish.clientRequest(ctrlId).then(function(ctrlRes) {
                var members = _.get(ctrlRes.body, 'Members', []);
                return Promise.each(members, function(member) {
                    return self.redfish.clientRequest(member['@odata.id'])
                    .then(function(hbaRes) {
                        data.Controllers.push(hbaRes.body);
                        catalogData.push(data);
                    });
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Hbas',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('HBA', { error:err });
            if(err.message === 'Missing HBA Resource') {
                return nodeId; // allow
            }
            throw err;
        });
    });

    EmcRedfishCatalogJob.prototype.catalogAggregators = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var aggrId = _.get(res.body, 'Oem.Emc.Aggregators', {})['@odata.id'];
            if(_.isUndefined(aggrId)) {
                throw new Error('Missing Aggregators Resource');
            }
            return self.redfish.clientRequest(aggrId);
        })
        .then(function(res) {
            var members = _.get(res, 'body.Members', {});
            assert.ok(Array.isArray(members), 'Aggregators Members');
            return members;
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'Aggregators Data');
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Type: res.body.Type,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: [],
                Switches: []
            };
            return Promise.each([
                self.redfish.clientRequest(_.get(res.body, 'Controllers', {})['@odata.id']),
                self.redfish.clientRequest(_.get(res.body, 'Switches', {})['@odata.id'])
            ], function(aggRes) {
                return Promise.map(_.get(aggRes.body, 'Members', []), function(member) {
                    var id = member['@odata.id'];
                    return self.redfish.clientRequest(id)
                    .then(function(res) {
                        data[(id.match(/Controllers/g))?'Controllers':'Switches'].push(res.body);
                        catalogData.push(data);
                    });
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Aggregators',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('Aggregators', { error:err });
            if(err.message === 'Missing Aggregators Resource') {
                return nodeId; // allow
            }
            throw err;
        });
    });

    /**
     * @memberOf EmcRedfishCatalogJob
     */
    EmcRedfishCatalogJob.prototype.catalogElements = Promise.method(function(nodeId) {
        var self = this;
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var elements = _.get(res.body, 'Oem.Emc.Elements');
            if (!elements) {
                throw new Error('Missing Emc Element Data');
            }
            return elements['@odata.id'];
        })
        .then(function(id) {
            assert.string(id, 'Element Identifier');
            return self.redfish.clientRequest(id);
        })
        .then(function(res) {
            assert.object(res, 'Element Resource Object');
            var elements = _.get(res, 'body.Members');
            return elements;
        })
        .map(function(element) {
            return self.redfish.clientRequest(element['@odata.id']);
        })
        .map(function(element) {
            assert.object(element, 'Element Resource Object');
            assert.object(element.body);
            return element.body;
        })
        .map(function(element) {
            return self.redfish.clientRequest(element['@odata.id'])
                .then (function(element){
                    return element;
                });
        })
        .map(function(element) {
            assert.object(element, 'Element Resource Object');
            assert.object(element.body);
            return element.body;
        })
        .map(function(element){
            var newElement = {};
            _.forEach(element,function(value,key){
                if(key === "Processors" || key === "Vpd" || key === "Dimms" || key === "Drives") {
                    if(value instanceof Array){
                        newElement [key]=   self.updatingComponentsArray(element,key);
                    }
                    else {
                        newElement [key]=   self.updatingComponents(element,key);
                    }
                }
                else {
                    newElement[key] = element[key];
                }
            });
            return Promise.props(newElement);
        })
        .then(function(data) {
            assert.ok(Array.isArray(data), 'Element Data');
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Elements',
                data: data
            }).then(function() {
                return nodeId;
            });
        });
    });

    EmcRedfishCatalogJob.prototype.updatingComponents = function (element, property) {
        var self = this;
        return self.redfish.clientRequest(element[property]['@odata.id'])
            .then (function(data){
                return data.body;
            })
            .then (function(element){
                if (element.hasOwnProperty("Members")){
                    var subElement = _(element.Members).map(function(arr){
                        return self.redfish.clientRequest(arr['@odata.id'])
                            .then(function(res){
                                return res.body;
                            });
                    }).value();
                    return Promise.all(subElement)
                }
                else {
                    return element;
                }
            });
    };

    EmcRedfishCatalogJob.prototype.updatingComponentsArray = function (element, property) {
        var self = this;
        var subElement = _(element[property]).map(function(arr){
            return self.redfish.clientRequest(arr['@odata.id'])
                .then(function(res){
                    return res.body;
                });
        }).value();
        return Promise.all(subElement)

    };
    return EmcRedfishCatalogJob;
}
<commit_msg>Handle empty element member list<commit_after>
// Copyright 2016, EMC, Inc.

'use strict';

var di = require('di');
module.exports = EmcRedfishCatalogJobFactory;
di.annotate(EmcRedfishCatalogJobFactory, new di.Provide('Job.Emc.Redfish.Catalog'));
di.annotate(EmcRedfishCatalogJobFactory, new di.Inject(
    'Job.Base',
    'Logger',
    'Promise',
    'Assert',
    'Util',
    'Services.Waterline',
    'Services.Encryption',
    '_',
    'JobUtils.RedfishTool'
));

function EmcRedfishCatalogJobFactory(
    BaseJob,
    Logger,
    Promise,
    assert,
    util,
    waterline,
    encryption,
    _,
    RedfishTool
) {
    var logger = Logger.initialize(EmcRedfishCatalogJobFactory);

    /**
     * @param {Object} options task options object
     * @param {Object} context graph context object
     * @param {String} taskId running task identifier
     * @constructor
     */
    function EmcRedfishCatalogJob(options, context, taskId) {
        EmcRedfishCatalogJob.super_.call(this,
                                   logger,
                                   options,
                                   context,
                                   taskId);
        this.chassis = this.context.chassis || [];
        if(_.isEmpty(this.chassis)) {
            this.chassis = [ this.context.target ]
        }
        this.redfish = new RedfishTool();
    }

    util.inherits(EmcRedfishCatalogJob, BaseJob);

    /**
     * @memberOf EmcRedfishCatalogJob
     */
    EmcRedfishCatalogJob.prototype._run = function() {
        var self = this;
        return Promise.resolve(self.chassis)
        .each(self.catalogElements.bind(self))
        .each(self.catalogHbas.bind(self))
        .each(self.catalogAggregators.bind(self))
        .each(self.catalogSpine.bind(self))
        .then(function() {
            self._done();
        })
        .catch(function(err) {
            self._done(err);
        });
    };

    EmcRedfishCatalogJob.prototype.catalogSpine = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var spineModId = _.get(res.body, 'Oem.Emc.SpineModules', {})['@odata.id'];
            if (_.isUndefined(spineModId)) {
                throw new Error('Missing SpineModules Resource');
            }
            return self.redfish.clientRequest(spineModId)
            .then(function(res) {
                var members = _.get(res, 'body.Members', []);
                assert.notEqual(members.length, 0, 'No SpineModules Members');
                return members;
            });
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'SpineModule Data');
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Type: res.body.Type,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: [],
                Switches: [],
                Vpd: {}
            };
            return Promise.each([
                self.redfish.clientRequest(_.get(res.body, 'Controllers', {})['@odata.id']),
                self.redfish.clientRequest(_.get(res.body, 'Switches', {})['@odata.id'])
            ], function(resList) {
                return Promise.map(_.get(resList.body, 'Members', []), function(member) {
                    var id = member['@odata.id'];
                    return self.redfish.clientRequest(id)
                    .then(function(res) {
                        data[id.match(/Controllers/g) ? 'Controllers' : 'Switches'].push(res.body);
                    });
                });
            })
            .then(function() {
                return self.redfish.clientRequest(_.get(res.body, 'Vpd', {})['@odata.id'])
                .then(function(res) {
                    data['Vpd'] = res.body;
                    catalogData.push(data);
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'SpineModules',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('SpineModules', {
                error: err
            });
            if(err.message !== 'Missing SpineModules Resource' &&
               err.message !== 'No SpineModules Members') {
                throw err;
            }
            return nodeId;
        });
    });

    EmcRedfishCatalogJob.prototype.catalogHbas = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var hbaId = _.get(res.body, 'Oem.Emc.Hbas', {})['@odata.id'];
            if(_.isUndefined(hbaId)) {
                throw new Error('Missing HBA Resource');
            }
            return self.redfish.clientRequest(hbaId);
        })
        .then(function(res) {
            var members = _.get(res, 'body.Members', []);
            assert.notEqual(members.length, 0, 'No HBA Members');
            return members;
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'HBA Data');
            var ctrlId = _.get(res.body, 'Controllers', {})['@odata.id'];
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: []
            };
            return self.redfish.clientRequest(ctrlId).then(function(ctrlRes) {
                var members = _.get(ctrlRes.body, 'Members', []);
                return Promise.each(members, function(member) {
                    return self.redfish.clientRequest(member['@odata.id'])
                    .then(function(hbaRes) {
                        data.Controllers.push(hbaRes.body);
                        catalogData.push(data);
                    });
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Hbas',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('HBA', { error:err });
            if(err.message !== 'Missing HBA Resource' &&
               err.message !== 'No HBA Members') {
                throw err;
            }
            return nodeId; // allow
        });
    });

    EmcRedfishCatalogJob.prototype.catalogAggregators = Promise.method(function(nodeId) {
        var self = this;
        var catalogData = [];
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var aggrId = _.get(res.body, 'Oem.Emc.Aggregators', {})['@odata.id'];
            if(_.isUndefined(aggrId)) {
                throw new Error('Missing Aggregators Resource');
            }
            return self.redfish.clientRequest(aggrId);
        })
        .then(function(res) {
            var members = _.get(res, 'body.Members', []);
            assert.notEqual(members.length, 0, 'No Aggregators Members');
            return members;
        })
        .map(function(member) {
            return self.redfish.clientRequest(member['@odata.id']);
        })
        .map(function(res) {
            assert.object(res.body, 'Aggregators Data');
            var data = {
                Id: res.body.Id,
                Name: res.body.Name,
                Type: res.body.Type,
                Manufacturer: res.body.Manufacturer,
                Model: res.body.Model,
                PartNumber: res.body.PartNumber,
                SerialNumber: res.body.SerialNumber,
                Controllers: [],
                Switches: []
            };
            return Promise.each([
                self.redfish.clientRequest(_.get(res.body, 'Controllers', {})['@odata.id']),
                self.redfish.clientRequest(_.get(res.body, 'Switches', {})['@odata.id'])
            ], function(aggRes) {
                return Promise.map(_.get(aggRes.body, 'Members', []), function(member) {
                    var id = member['@odata.id'];
                    return self.redfish.clientRequest(id)
                    .then(function(res) {
                        data[(id.match(/Controllers/g))?'Controllers':'Switches'].push(res.body);
                        catalogData.push(data);
                    });
                });
            });
        })
        .then(function() {
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Aggregators',
                data: _.flattenDeep(catalogData)
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('Aggregators', { error:err });
            if(err.message !== 'Missing Aggregators Resource' &&
               err.message !== 'No Aggregators Members') {
                throw err;
            }
            return nodeId; // allow
        });
    });

    /**
     * @memberOf EmcRedfishCatalogJob
     */
    EmcRedfishCatalogJob.prototype.catalogElements = Promise.method(function(nodeId) {
        var self = this;
        return self.redfish.setup(nodeId)
        .then(function() {
            return self.redfish.clientRequest(self.redfish.settings.root);
        })
        .then(function(res) {
            assert.object(res, 'Chassis Resource Object');
            var elements = _.get(res.body, 'Oem.Emc.Elements');
            if (!elements) {
                throw new Error('Missing Emc Element Data');
            }
            return elements['@odata.id'];
        })
        .then(function(id) {
            assert.string(id, 'Element Identifier');
            return self.redfish.clientRequest(id);
        })
        .then(function(res) {
            var elements = _.get(res, 'body.Members');
            assert.notEqual(elements.length, 0, 'No Element Members');
            return elements;
        })
        .map(function(element) {
            return self.redfish.clientRequest(element['@odata.id']);
        })
        .map(function(element) {
            assert.object(element, 'Element Resource Object');
            assert.object(element.body);
            return element.body;
        })
        .map(function(element) {
            return self.redfish.clientRequest(element['@odata.id'])
                .then (function(element){
                    return element;
                });
        })
        .map(function(element) {
            assert.object(element, 'Element Resource Object');
            assert.object(element.body);
            return element.body;
        })
        .map(function(element){
            var newElement = {};
            _.forEach(element,function(value,key){
                if(key === "Processors" || key === "Vpd" || key === "Dimms" || key === "Drives") {
                    if(value instanceof Array){
                        newElement [key]=   self.updatingComponentsArray(element,key);
                    }
                    else {
                        newElement [key]=   self.updatingComponents(element,key);
                    }
                }
                else {
                    newElement[key] = element[key];
                }
            });
            return Promise.props(newElement);
        })
        .then(function(data) {
            assert.ok(Array.isArray(data), 'Element Data');
            return waterline.catalogs.create({
                node: nodeId,
                source: 'Elements',
                data: data
            }).then(function() {
                return nodeId;
            });
        })
        .catch(function(err) {
            logger.error('Elements', { error:err });
            if(err.message !== 'No Element Members') {
                throw err;
            }
            return nodeId; // allow
        });
    });

    EmcRedfishCatalogJob.prototype.updatingComponents = function (element, property) {
        var self = this;
        return self.redfish.clientRequest(element[property]['@odata.id'])
            .then (function(data){
                return data.body;
            })
            .then (function(element){
                if (element.hasOwnProperty("Members")){
                    var subElement = _(element.Members).map(function(arr){
                        return self.redfish.clientRequest(arr['@odata.id'])
                            .then(function(res){
                                return res.body;
                            });
                    }).value();
                    return Promise.all(subElement)
                }
                else {
                    return element;
                }
            });
    };

    EmcRedfishCatalogJob.prototype.updatingComponentsArray = function (element, property) {
        var self = this;
        var subElement = _(element[property]).map(function(arr){
            return self.redfish.clientRequest(arr['@odata.id'])
                .then(function(res){
                    return res.body;
                });
        }).value();
        return Promise.all(subElement)

    };
    return EmcRedfishCatalogJob;
}
<|endoftext|>


<commit_before>/**
 * Created by gadyaari on 8/24/15.
 */

var _ = require('underscore');
var persistenceFormat = require('./../../common/PersistenceFormat');
var config = require('./../../common/Configuration');
var fsUtils = require('./../utils/fs-utils');
var path = require('path');
var qio = require('q-io/fs');
var m3u8Parser = require('./promise-m3u8');
var loggerModule = require('../../common/logger');
var backendClient = require('../BackendClientFactory.js').getBackendClient();
var Q = require('q');

function PlaylistGenerator(entryObject, isNewSession) {
    this.entryId = entryObject.entryId;
    this.isNewSession = isNewSession;
    this.streamInfo = entryObject.getStreamInfo();
    this.flavorsObjArray = null;
    this.logger = loggerModule.getLogger("PlaylistGenerator", "[" + this.entryId + "] ");
    this.entryServerNodeId = entryObject.entryServerNodeId;
    this.entryServerType = parseInt(entryObject.serverType);
}

function checkTranscodingProfileChange() {
    var that = this;
    var entryPath = persistenceFormat.getEntryFullPath(that.entryId);
    var reg = new RegExp("(?:" + that.entryId + "\\/)([0-9]*)\\b", "i");
    return qio.listDirectoryTree(entryPath)
        .then(function(result) {
            return _.chain(result)
                .filter(function(d) {
                    return d.match(reg) !== null;
                })
                .map(function(d) {
                    return d.match(reg)[1];
                })
                .uniq(function(f) {
                    return f;
                })
                .value();
        })
        .then(function(result) {
            var playingFlavorsString = _.map(that.flavorsObjArray, function(f) {
                return f.name;
            }).sort().join();
            if (result.sort().join() !== playingFlavorsString) {
                that.logger.info("Transcoding profile changed, cleaning entry folder");
                return fsUtils.cleanFolder(entryPath);
            }
        })
        .catch(function(error) {
            that.logger.error("Error cleaning folder because of transcoding profile change: %j", error);
        });
}

function updateStreamInfo() {
    var that = this;
    return backendClient.updateStreamInfo(this.entryId, this.entryServerNodeId, this.flavorsObjArray)
        .then(function() {
            this.logger.debug("[%s] Flavors updated in DB for entry server node [%s]", that.entryId, that.entryServerNodeId);
        });
}

function savePlaylist() {
    var that = this;
    var playlist = new m3u8Parser.M3U.create();
    playlist.set("version", 3);

    _.each(that.flavorsObjArray, function(flavor) {
        playlist.addStreamItem({
            bandwidth: flavor.bandwidth,
            resolution: flavor.resolution ? flavor.resolution.join("x") : "",
            uri: flavor.name + '/chunklist.m3u8'
        });
    });
    // Get the destination to save the manifest
    var destPath = path.join(config.get('rootFolderPath'), that.entryId, persistenceFormat.getMasterManifestName());
    var playerMasterManifest = playlist.toString();
    that.logger.info("Entry received master manifest info: %j", that.flavorsObjArray);
    return fsUtils.writeFileAtomically(destPath, playerMasterManifest)
        .then(function() {
            that.lastPlaylistUpdate = new Date();
            that.logger.debug("Playlist written to disk");

            var getLiveEntryServerNodeIdPromise =
                that.entryServerNodeId ? Q.Promise.resolve(that.entryServerNodeId)
                    : backendClient.getLiveEntryServerNodes(that.entryId).then(function(liveEntryServerNodes) {

                    that.entryServerNodeId = 0;
                    _.each(liveEntryServerNodes, function(liveEntryServerNode) {
                        if (liveEntryServerNode.serverType === that.entryServerType) {
                            that.entryServerNodeId = liveEntryServerNode.id;
                            return false;
                        }
                    });

                }).catch(function() {
                    that.entryServerNodeId = 0;
                   return null;
                });

            getLiveEntryServerNodeIdPromise.then(function() {
                updateStreamInfo.call(that);
            });

            return that.flavorsObjArray;
        });
}

PlaylistGenerator.prototype.updatePlaylist = function() {
    var that = this;
    // Update the entry's playlist every minute
    that.logger.debug("Retrieving flavors for update");
    return that.streamInfo.getAllFlavors()
        .then(function(flavors) {
            that.flavorsObjArray = flavors;
            return savePlaylist.call(that);
        })
        .catch(function(error) {
            that.logger.error("Unable to update playlist: %s", error);
        });
};

PlaylistGenerator.prototype.createPlaylist = function() {
    var that = this;
    that.logger.info("Retrieving master manifest for entry");
    return that.streamInfo.getAllFlavors()
        .then(function(flavors) {
            that.flavorsObjArray = flavors;
            return (!that.isNewSession) ? checkTranscodingProfileChange.call(that) : flavors;
        })
        .then(function() {
            return savePlaylist.call(that);
        });
};

module.exports = PlaylistGenerator;<commit_msg>handle cases where entry server node used to report stream info is no longer valid<commit_after>/**
 * Created by gadyaari on 8/24/15.
 */

var _ = require('underscore');
var persistenceFormat = require('./../../common/PersistenceFormat');
var config = require('./../../common/Configuration');
var fsUtils = require('./../utils/fs-utils');
var path = require('path');
var qio = require('q-io/fs');
var m3u8Parser = require('./promise-m3u8');
var loggerModule = require('../../common/logger');
var backendClient = require('../BackendClientFactory.js').getBackendClient();
var Q = require('q');

function PlaylistGenerator(entryObject, isNewSession) {
    this.entryId = entryObject.entryId;
    this.isNewSession = isNewSession;
    this.streamInfo = entryObject.getStreamInfo();
    this.flavorsObjArray = null;
    this.logger = loggerModule.getLogger("PlaylistGenerator", "[" + this.entryId + "] ");
    this.entryServerNodeId = entryObject.entryServerNodeId;
    this.entryServerType = parseInt(entryObject.serverType);
}

function checkTranscodingProfileChange() {
    var that = this;
    var entryPath = persistenceFormat.getEntryFullPath(that.entryId);
    var reg = new RegExp("(?:" + that.entryId + "\\/)([0-9]*)\\b", "i");
    return qio.listDirectoryTree(entryPath)
        .then(function(result) {
            return _.chain(result)
                .filter(function(d) {
                    return d.match(reg) !== null;
                })
                .map(function(d) {
                    return d.match(reg)[1];
                })
                .uniq(function(f) {
                    return f;
                })
                .value();
        })
        .then(function(result) {
            var playingFlavorsString = _.map(that.flavorsObjArray, function(f) {
                return f.name;
            }).sort().join();
            if (result.sort().join() !== playingFlavorsString) {
                that.logger.info("Transcoding profile changed, cleaning entry folder");
                return fsUtils.cleanFolder(entryPath);
            }
        })
        .catch(function(error) {
            that.logger.error("Error cleaning folder because of transcoding profile change: %j", error);
        });
}

function updateStreamInfo() {
    var that = this;
    return backendClient.updateStreamInfo(this.entryId, this.entryServerNodeId, this.flavorsObjArray)
        .then(function() {
            that.logger.debug("[%s] Flavors updated in DB for entry server node [%s]", that.entryId, that.entryServerNodeId);
        })
        .catch(function() {
            that.logger.debug("[%s] Failed to update streamInfo in DB for entry server node [%s]", that.entryId, that.entryServerNodeId);
            that.entryServerNodeId = null;
        });
}

function savePlaylist() {
    var that = this;
    var playlist = new m3u8Parser.M3U.create();
    playlist.set("version", 3);

    _.each(that.flavorsObjArray, function(flavor) {
        playlist.addStreamItem({
            bandwidth: flavor.bandwidth,
            resolution: flavor.resolution ? flavor.resolution.join("x") : "",
            uri: flavor.name + '/chunklist.m3u8'
        });
    });
    // Get the destination to save the manifest
    var destPath = path.join(config.get('rootFolderPath'), that.entryId, persistenceFormat.getMasterManifestName());
    var playerMasterManifest = playlist.toString();
    that.logger.info("Entry received master manifest info: %j", that.flavorsObjArray);
    return fsUtils.writeFileAtomically(destPath, playerMasterManifest)
        .then(function() {
            that.lastPlaylistUpdate = new Date();
            that.logger.debug("Playlist written to disk");

            var getLiveEntryServerNodeIdPromise =
                that.entryServerNodeId ? Q.Promise.resolve(that.entryServerNodeId)
                    : backendClient.getLiveEntryServerNodes(that.entryId).then(function(liveEntryServerNodes) {

                    that.entryServerNodeId = 0;
                    _.each(liveEntryServerNodes, function(liveEntryServerNode) {
                        if (liveEntryServerNode.serverType === that.entryServerType) {
                            that.entryServerNodeId = liveEntryServerNode.id;
                            return false;
                        }
                    });

                }).catch(function() {
                    that.entryServerNodeId = 0;
                   return null;
                });

            getLiveEntryServerNodeIdPromise.then(function() {
                updateStreamInfo.call(that);
            });

            return that.flavorsObjArray;
        });
}

PlaylistGenerator.prototype.updatePlaylist = function() {
    var that = this;
    // Update the entry's playlist every minute
    that.logger.debug("Retrieving flavors for update");
    return that.streamInfo.getAllFlavors()
        .then(function(flavors) {
            that.flavorsObjArray = flavors;
            return savePlaylist.call(that);
        })
        .catch(function(error) {
            that.logger.error("Unable to update playlist: %s", error);
        });
};

PlaylistGenerator.prototype.createPlaylist = function() {
    var that = this;
    that.logger.info("Retrieving master manifest for entry");
    return that.streamInfo.getAllFlavors()
        .then(function(flavors) {
            that.flavorsObjArray = flavors;
            return (!that.isNewSession) ? checkTranscodingProfileChange.call(that) : flavors;
        })
        .then(function() {
            return savePlaylist.call(that);
        });
};

module.exports = PlaylistGenerator;<|endoftext|>


<commit_before>/**
 * @flow
 */
import { NativeModules } from 'react-native';

import Query from './query.js';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import { ReferenceBase } from './../base';
import { promisify, isFunction, isObject, tryJSONParse, tryJSONStringify, generatePushID } from './../../utils';

const FirestackDatabase = NativeModules.FirestackDatabase;

// https://firebase.google.com/docs/reference/js/firebase.database.Reference

/**
 * @class Reference
 */
export default class Reference extends ReferenceBase {

  db: FirestackDatabase;
  query: Query;

  constructor(db: FirestackDatabase, path: Array<string>, existingModifiers?: Array<string>) {
    super(db.firestack, path);
    this.db = db;
    this.namespace = 'firestack:db:ref';
    this.query = new Query(this, path, existingModifiers);
    this.log.debug('Created new Reference', this.db._dbHandle(path, existingModifiers));
  }

  /**
   *
   * @param bool
   * @returns {*}
   */
  keepSynced(bool: boolean) {
    const path = this._dbPath();
    return promisify('keepSynced', FirestackDatabase)(path, bool);
  }

  /**
   *
   * @param value
   * @returns {*}
   */
  set(value: any) {
    const path = this._dbPath();
    const _value = this._serializeAnyType(value);
    return promisify('set', FirestackDatabase)(path, _value);
  }

  /**
   *
   * @param val
   * @returns {*}
   */
  update(val: Object) {
    const path = this._dbPath();
    const value = this._serializeObject(val);
    return promisify('update', FirestackDatabase)(path, value);
  }

  /**
   *
   * @returns {*}
   */
  remove() {
    return promisify('remove', FirestackDatabase)(this._dbPath());
  }

  /**
   *
   * @param value
   * @param onComplete
   * @returns {*}
   */
  push(value: any, onComplete: Function) {
    if (value === null || value === undefined) {
      // todo add server timestamp to push id call.
      const _paths = this.path.concat([generatePushID()]);
      return new Reference(this.db, _paths);
    }

    const path = this._dbPath();
    const _value = this._serializeAnyType(value);
    return promisify('push', FirestackDatabase)(path, _value)
      .then(({ ref }) => {
        const newRef = new Reference(this.db, ref.split('/'));
        if (isFunction(onComplete)) return onComplete(null, newRef);
        return newRef;
      }).catch((e) => {
        if (isFunction(onComplete)) return onComplete(e, null);
        return e;
      });
  }

  on(eventName: string, cb: () => any) {
    const path = this._dbPath();
    const modifiers = this.query.getModifiers();
    const modifiersString = this.query.getModifiersString();
    this.log.debug('adding reference.on', path, modifiersString, eventName);
    return this.db.on(path, modifiersString, modifiers, eventName, cb);
  }

  once(eventName: string = 'once', cb: (snapshot: Object) => void) {
    const path = this._dbPath();
    const modifiers = this.query.getModifiers();
    const modifiersString = this.query.getModifiersString();
    return promisify('onOnce', FirestackDatabase)(path, modifiersString, modifiers, eventName)
      .then(({ snapshot }) => new Snapshot(this, snapshot))
      .then((snapshot) => {
        if (isFunction(cb)) cb(snapshot);
        return snapshot;
      });
  }

  off(eventName?: string = '', origCB?: () => any) {
    const path = this._dbPath();
    const modifiersString = this.query.getModifiersString();
    this.log.debug('ref.off(): ', path, modifiersString, eventName);
    return this.db.off(path, modifiersString, eventName, origCB);
  }

  /**
   * MODIFIERS
   */

  /**
   *
   * @returns {Reference}
   */
  orderByKey(): Reference {
    return this.orderBy('orderByKey');
  }

  /**
   *
   * @returns {Reference}
   */
  orderByPriority(): Reference {
    return this.orderBy('orderByPriority');
  }

  /**
   *
   * @returns {Reference}
   */
  orderByValue(): Reference {
    return this.orderBy('orderByValue');
  }

  /**
   *
   * @param key
   * @returns {Reference}
   */
  orderByChild(key: string): Reference {
    return this.orderBy('orderByChild', key);
  }

  /**
   *
   * @param name
   * @param key
   * @returns {Reference}
   */
  orderBy(name: string, key?: string): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setOrderBy(name, key);
    return newRef;
  }

  /**
   * LIMITS
   */

  /**
   *
   * @param limit
   * @returns {Reference}
   */
  limitToLast(limit: number): Reference {
    return this.limit('limitToLast', limit);
  }

  /**
   *
   * @param limit
   * @returns {Reference}
   */
  limitToFirst(limit: number): Reference {
    return this.limit('limitToFirst', limit);
  }

  /**
   *
   * @param name
   * @param limit
   * @returns {Reference}
   */
  limit(name: string, limit: number): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setLimit(name, limit);
    return newRef;
  }

  /**
   * FILTERS
   */

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  equalTo(value: any, key?: string): Reference {
    return this.filter('equalTo', value, key);
  }

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  endAt(value: any, key?: string): Reference {
    return this.filter('endAt', value, key);
  }

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  startAt(value: any, key?: string): Reference {
    return this.filter('startAt', value, key);
  }

  /**
   *
   * @param name
   * @param value
   * @param key
   * @returns {Reference}
   */
  filter(name: string, value: any, key?: string): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setFilter(name, value, key);
    return newRef;
  }

  // TODO why is this presence here on DB ref? its unrelated?
  presence(path: string) {
    const presence = this.firestack.presence;
    const ref = path ? this.child(path) : this;
    return presence.ref(ref, this._dbPath());
  }

  onDisconnect() {
    return new Disconnect(this);
  }

  child(...paths: Array<string>) {
    return new Reference(this.db, this.path.concat(paths));
  }

  toString(): string {
    return this._dbPath();
  }

  /**
   * GETTERS
   */

  /**
   * Returns the current key of this ref - i.e. /foo/bar returns 'bar'
   * @returns {*}
   */
  get key(): string|null {
    if (!this.path.length) return null;
    return this.path.slice(this.path.length - 1, this.path.length)[0];
  }

  /**
   * Returns the parent ref of the current ref i.e. a ref of /foo/bar would return a new ref to '/foo'
   * @returns {*}
   */
  get parent(): Reference|null {
    if (!this.path.length || this.path.length === 1) return null;
    return new Reference(this.db, this.path.slice(0, -1));
  }

  /**
   * Returns a ref to the root of db - '/'
   * @returns {Reference}
   */
  get root(): Reference {
    return new Reference(this.db, []);
  }

  /**
   * INTERNALS
   */

  _dbPath(paths?: Array<string>): string {
    const path = paths || this.path;
    const pathStr = (path.length > 0 ? path.join('/') : '/');

    if (pathStr[0] !== '/') {
      return `/${pathStr}`;
    }

    return pathStr;
  }

  /**
   *
   * @param obj
   * @returns {Object}
   * @private
   */
  _serializeObject(obj: Object) {
    if (!isObject(obj)) return obj;

    // json stringify then parse it calls toString on Objects / Classes
    // that support it i.e new Date() becomes a ISO string.
    return tryJSONParse(tryJSONStringify(obj));
  }

  /**
   *
   * @param value
   * @returns {*}
   * @private
   */
  _serializeAnyType(value: any) {
    if (isObject(value)) {
      return {
        type: 'object',
        value: this._serializeObject(value),
      };
    }

    return {
      type: typeof value,
      value,
    };
  }
}
<commit_msg>Update reference.js<commit_after>/**
 * @flow
 */
import { NativeModules } from 'react-native';

import Query from './query.js';
import Snapshot from './snapshot';
import Disconnect from './disconnect';
import { ReferenceBase } from './../base';
import { promisify, isFunction, isObject, tryJSONParse, tryJSONStringify, generatePushID } from './../../utils';

const FirestackDatabase = NativeModules.FirestackDatabase;

// https://firebase.google.com/docs/reference/js/firebase.database.Reference

/**
 * @class Reference
 */
export default class Reference extends ReferenceBase {

  db: FirestackDatabase;
  query: Query;

  constructor(db: FirestackDatabase, path: Array<string>, existingModifiers?: Array<string>) {
    super(db.firestack, path);
    this.db = db;
    this.namespace = 'firestack:db:ref';
    this.query = new Query(this, path, existingModifiers);
    this.log.debug('Created new Reference', this.db._handle(path, existingModifiers));
  }

  /**
   *
   * @param bool
   * @returns {*}
   */
  keepSynced(bool: boolean) {
    const path = this._dbPath();
    return promisify('keepSynced', FirestackDatabase)(path, bool);
  }

  /**
   *
   * @param value
   * @returns {*}
   */
  set(value: any) {
    const path = this._dbPath();
    const _value = this._serializeAnyType(value);
    return promisify('set', FirestackDatabase)(path, _value);
  }

  /**
   *
   * @param val
   * @returns {*}
   */
  update(val: Object) {
    const path = this._dbPath();
    const value = this._serializeObject(val);
    return promisify('update', FirestackDatabase)(path, value);
  }

  /**
   *
   * @returns {*}
   */
  remove() {
    return promisify('remove', FirestackDatabase)(this._dbPath());
  }

  /**
   *
   * @param value
   * @param onComplete
   * @returns {*}
   */
  push(value: any, onComplete: Function) {
    if (value === null || value === undefined) {
      // todo add server timestamp to push id call.
      const _paths = this.path.concat([generatePushID()]);
      return new Reference(this.db, _paths);
    }

    const path = this._dbPath();
    const _value = this._serializeAnyType(value);
    return promisify('push', FirestackDatabase)(path, _value)
      .then(({ ref }) => {
        const newRef = new Reference(this.db, ref.split('/'));
        if (isFunction(onComplete)) return onComplete(null, newRef);
        return newRef;
      }).catch((e) => {
        if (isFunction(onComplete)) return onComplete(e, null);
        return e;
      });
  }

  on(eventName: string, cb: () => any) {
    const path = this._dbPath();
    const modifiers = this.query.getModifiers();
    const modifiersString = this.query.getModifiersString();
    this.log.debug('adding reference.on', path, modifiersString, eventName);
    return this.db.on(path, modifiersString, modifiers, eventName, cb);
  }

  once(eventName: string = 'once', cb: (snapshot: Object) => void) {
    const path = this._dbPath();
    const modifiers = this.query.getModifiers();
    const modifiersString = this.query.getModifiersString();
    return promisify('onOnce', FirestackDatabase)(path, modifiersString, modifiers, eventName)
      .then(({ snapshot }) => new Snapshot(this, snapshot))
      .then((snapshot) => {
        if (isFunction(cb)) cb(snapshot);
        return snapshot;
      });
  }

  off(eventName?: string = '', origCB?: () => any) {
    const path = this._dbPath();
    const modifiersString = this.query.getModifiersString();
    this.log.debug('ref.off(): ', path, modifiersString, eventName);
    return this.db.off(path, modifiersString, eventName, origCB);
  }

  /**
   * MODIFIERS
   */

  /**
   *
   * @returns {Reference}
   */
  orderByKey(): Reference {
    return this.orderBy('orderByKey');
  }

  /**
   *
   * @returns {Reference}
   */
  orderByPriority(): Reference {
    return this.orderBy('orderByPriority');
  }

  /**
   *
   * @returns {Reference}
   */
  orderByValue(): Reference {
    return this.orderBy('orderByValue');
  }

  /**
   *
   * @param key
   * @returns {Reference}
   */
  orderByChild(key: string): Reference {
    return this.orderBy('orderByChild', key);
  }

  /**
   *
   * @param name
   * @param key
   * @returns {Reference}
   */
  orderBy(name: string, key?: string): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setOrderBy(name, key);
    return newRef;
  }

  /**
   * LIMITS
   */

  /**
   *
   * @param limit
   * @returns {Reference}
   */
  limitToLast(limit: number): Reference {
    return this.limit('limitToLast', limit);
  }

  /**
   *
   * @param limit
   * @returns {Reference}
   */
  limitToFirst(limit: number): Reference {
    return this.limit('limitToFirst', limit);
  }

  /**
   *
   * @param name
   * @param limit
   * @returns {Reference}
   */
  limit(name: string, limit: number): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setLimit(name, limit);
    return newRef;
  }

  /**
   * FILTERS
   */

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  equalTo(value: any, key?: string): Reference {
    return this.filter('equalTo', value, key);
  }

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  endAt(value: any, key?: string): Reference {
    return this.filter('endAt', value, key);
  }

  /**
   *
   * @param value
   * @param key
   * @returns {Reference}
   */
  startAt(value: any, key?: string): Reference {
    return this.filter('startAt', value, key);
  }

  /**
   *
   * @param name
   * @param value
   * @param key
   * @returns {Reference}
   */
  filter(name: string, value: any, key?: string): Reference {
    const newRef = new Reference(this.db, this.path, this.query.getModifiers());
    newRef.query.setFilter(name, value, key);
    return newRef;
  }

  // TODO why is this presence here on DB ref? its unrelated?
  presence(path: string) {
    const presence = this.firestack.presence;
    const ref = path ? this.child(path) : this;
    return presence.ref(ref, this._dbPath());
  }

  onDisconnect() {
    return new Disconnect(this);
  }

  child(...paths: Array<string>) {
    return new Reference(this.db, this.path.concat(paths));
  }

  toString(): string {
    return this._dbPath();
  }

  /**
   * GETTERS
   */

  /**
   * Returns the current key of this ref - i.e. /foo/bar returns 'bar'
   * @returns {*}
   */
  get key(): string|null {
    if (!this.path.length) return null;
    return this.path.slice(this.path.length - 1, this.path.length)[0];
  }

  /**
   * Returns the parent ref of the current ref i.e. a ref of /foo/bar would return a new ref to '/foo'
   * @returns {*}
   */
  get parent(): Reference|null {
    if (!this.path.length || this.path.length === 1) return null;
    return new Reference(this.db, this.path.slice(0, -1));
  }

  /**
   * Returns a ref to the root of db - '/'
   * @returns {Reference}
   */
  get root(): Reference {
    return new Reference(this.db, []);
  }

  /**
   * INTERNALS
   */

  _dbPath(paths?: Array<string>): string {
    const path = paths || this.path;
    const pathStr = (path.length > 0 ? path.join('/') : '/');

    if (pathStr[0] !== '/') {
      return `/${pathStr}`;
    }

    return pathStr;
  }

  /**
   *
   * @param obj
   * @returns {Object}
   * @private
   */
  _serializeObject(obj: Object) {
    if (!isObject(obj)) return obj;

    // json stringify then parse it calls toString on Objects / Classes
    // that support it i.e new Date() becomes a ISO string.
    return tryJSONParse(tryJSONStringify(obj));
  }

  /**
   *
   * @param value
   * @returns {*}
   * @private
   */
  _serializeAnyType(value: any) {
    if (isObject(value)) {
      return {
        type: 'object',
        value: this._serializeObject(value),
      };
    }

    return {
      type: typeof value,
      value,
    };
  }
}
<|endoftext|>


<commit_before>'use strict';

const shorthands = require('./shorthands');
const filters = require('./filters');

const methods = {
	extract: function (prop) {
		this._setExtraction('extraction');
		this.query.target_property = prop;
		return this;
	},

	count: function (prop) {
		if (prop) {
			this._setExtraction('count_unique')
			this.query.target_property = prop;
		} else {
			this._setExtraction('count')
		}
		return this;
	},

	min: function (prop) {
		this._setExtraction('minimum');
		this.query.target_property = prop;
		return this;
	},

	max: function (prop) {
		this._setExtraction('maximum');
		this.query.target_property = prop;
		return this;
	},

	sum: function (prop) {
		this._setExtraction('sum');
		this.query.target_property = prop;
		return this;
	},

	avg: function (prop) {
		this._setExtraction('average');
		this.query.target_property = prop;
		return this;
	},

	median: function (prop) {
		this._setExtraction('median');
		this.query.target_property = prop;
		return this;
	},

	percentile: function (prop, pct) {
		this._setExtraction('percentile');
		this.query.target_property = prop;
		this.query.percentile = pct;
		return this;
	},

	with: function (prop) {
		this.query.actor_property = prop;
		return this;
	},

	interval: function (interval) {
		const instance = this._getInstance();

		// Keen extractions are different to all other queries. They do not support grouping or intervals.
		if (this.extraction === "extraction") {
			console.log("Removing instance() from the query as it's not supported in keen extractions.");
			return instance;

			// Todo: Decide whether it's better to "magically fix" the query, or throw an error
			// throw new Error('Keen extractions are different to all other queries. They do not support grouping or intervals');
		}

		if (instance.dimensions.indexOf('timeframe') === -1) {
			instance.dimensions.unshift('timeframe');
		}

		const intervalParts = /^(?:(\d+)_)?([a-z]+)$/.exec(interval).slice(1);
		const intervalUnit = shorthands.intervalUnits[intervalParts[1]] || intervalParts[1];
		instance.intervalUnit = intervalParts[0] ? `${intervalParts[0]} ${intervalUnit}s` : intervalUnit;
		instance.query.interval = intervalParts[0] ? `every_${intervalParts[0]}_${intervalUnit}s` : shorthands.intervals[intervalUnit];

		instance.timeframe = instance.timeframe || 'this_14_days';
		return instance;
	},

	absTime: function (start, end, interval) {
		const instance = this._getInstance();
		instance.timeframe = JSON.stringify({
			start: typeof start === 'string' ? start : start.toISOString(),
			end: typeof end === 'string' ? end : end.toISOString()
		});

		if (interval) {
			instance.interval(interval);
		}

		return instance;
	},

	relTime: function (time, interval) {
		const instance = this._getInstance();
		if (time) {
			if (parseInt(time, 10) === Number(time)) {
				time = time + '_days';
			}
		} else {
			time = '14_days';
		}
		instance.timeframe = (time.indexOf('this_') === 0 || time.indexOf('previous_') === 0) ? time : 'this_' + time;

		if (interval) {
			instance.interval(interval);
		}

		return instance;
	},

	group: function () {
		const instance = this._getInstance();

		// Keen extractions are different to all other queries. They do not support grouping or intervals.
		if (this.extraction === "extraction") {
			console.log("Removing group() from the query as it's not supported in keen extractions.");
			return this._getInstance();

			// Todo: Decide whether it's better to "magically fix" the query, or throw an error
			// throw new Error('Keen extractions are different to all other queries. They do not support grouping or intervals');
		}

		instance.groupedBy = instance.groupedBy || [];
		instance.groupedBy = instance.groupedBy.concat([].slice.call(arguments));
		instance.dimensions = instance.dimensions.concat([].slice.call(arguments));
		return instance;
	},

	filter: function (filter) {
		const instance = this._getInstance();
		if (typeof filter === 'string') {
			filter = filters.parse(filter);
		}

		if (!Array.isArray(filter)) {
			filter = [filter]
		}
		instance.filters = instance.filters.concat(filter)
		return instance;
	},

	tidy: function (extraStrict) {
		const instance = this._getInstance();
		instance.excludeNulls = true;
		if (extraStrict) {
			instance.excludeFalsy = true;
		}
		return instance;
	}
};

const methodNames = Object.keys(methods);

const extendables = [];

module.exports.mixin = function (proto, transform) {
	methodNames.forEach(name => {
		proto[name] = transform ? transform(methods[name], name) : methods[name];
	});
	extendables.push({proto, transform})
}

module.exports.define = function (name, func) {
	extendables.forEach(obj => {
		obj.proto[name] = obj.transform ? obj.transform(func, name) : func;
	})
}
<commit_msg>If the keen API doesn't error, do nothing. If it does, throw an appropriate error.<commit_after>'use strict';

const shorthands = require('./shorthands');
const filters = require('./filters');

const methods = {
	extract: function (prop) {
		this._setExtraction('extraction');
		this.query.target_property = prop;
		return this;
	},

	count: function (prop) {
		if (prop) {
			this._setExtraction('count_unique')
			this.query.target_property = prop;
		} else {
			this._setExtraction('count')
		}
		return this;
	},

	min: function (prop) {
		this._setExtraction('minimum');
		this.query.target_property = prop;
		return this;
	},

	max: function (prop) {
		this._setExtraction('maximum');
		this.query.target_property = prop;
		return this;
	},

	sum: function (prop) {
		this._setExtraction('sum');
		this.query.target_property = prop;
		return this;
	},

	avg: function (prop) {
		this._setExtraction('average');
		this.query.target_property = prop;
		return this;
	},

	median: function (prop) {
		this._setExtraction('median');
		this.query.target_property = prop;
		return this;
	},

	percentile: function (prop, pct) {
		this._setExtraction('percentile');
		this.query.target_property = prop;
		this.query.percentile = pct;
		return this;
	},

	with: function (prop) {
		this.query.actor_property = prop;
		return this;
	},

	interval: function (interval) {
		const instance = this._getInstance();

		if (this.extraction === "extraction") {
			throw new Error('Keen extractions are different to all other queries. They do not support grouping or intervals');
		}

		if (instance.dimensions.indexOf('timeframe') === -1) {
			instance.dimensions.unshift('timeframe');
		}

		const intervalParts = /^(?:(\d+)_)?([a-z]+)$/.exec(interval).slice(1);
		const intervalUnit = shorthands.intervalUnits[intervalParts[1]] || intervalParts[1];
		instance.intervalUnit = intervalParts[0] ? `${intervalParts[0]} ${intervalUnit}s` : intervalUnit;
		instance.query.interval = intervalParts[0] ? `every_${intervalParts[0]}_${intervalUnit}s` : shorthands.intervals[intervalUnit];

		instance.timeframe = instance.timeframe || 'this_14_days';
		return instance;
	},

	absTime: function (start, end, interval) {
		const instance = this._getInstance();
		instance.timeframe = JSON.stringify({
			start: typeof start === 'string' ? start : start.toISOString(),
			end: typeof end === 'string' ? end : end.toISOString()
		});

		if (interval) {
			instance.interval(interval);
		}

		return instance;
	},

	relTime: function (time, interval) {
		const instance = this._getInstance();
		if (time) {
			if (parseInt(time, 10) === Number(time)) {
				time = time + '_days';
			}
		} else {
			time = '14_days';
		}
		instance.timeframe = (time.indexOf('this_') === 0 || time.indexOf('previous_') === 0) ? time : 'this_' + time;

		if (interval) {
			instance.interval(interval);
		}

		return instance;
	},

	group: function () {
		const instance = this._getInstance();

		if (this.extraction === "extraction") {
			throw new Error('Keen extractions are different to all other queries. They do not support grouping or intervals');
		}

		instance.groupedBy = instance.groupedBy || [];
		instance.groupedBy = instance.groupedBy.concat([].slice.call(arguments));
		instance.dimensions = instance.dimensions.concat([].slice.call(arguments));
		return instance;
	},

	filter: function (filter) {
		const instance = this._getInstance();
		if (typeof filter === 'string') {
			filter = filters.parse(filter);
		}

		if (!Array.isArray(filter)) {
			filter = [filter]
		}
		instance.filters = instance.filters.concat(filter)
		return instance;
	},

	tidy: function (extraStrict) {
		const instance = this._getInstance();
		instance.excludeNulls = true;
		if (extraStrict) {
			instance.excludeFalsy = true;
		}
		return instance;
	}
};

const methodNames = Object.keys(methods);

const extendables = [];

module.exports.mixin = function (proto, transform) {
	methodNames.forEach(name => {
		proto[name] = transform ? transform(methods[name], name) : methods[name];
	});
	extendables.push({proto, transform})
}

module.exports.define = function (name, func) {
	extendables.forEach(obj => {
		obj.proto[name] = obj.transform ? obj.transform(func, name) : func;
	})
}
<|endoftext|>