<commit_before>/*
 * CFHTTPRequest.js
 * Objective-J
 *
 * Created by Francisco Tolmasky.
 * Copyright 2010, 280 North, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

var asynchronousTimeoutCount = 0,
    asynchronousTimeoutId = null,
    asynchronousFunctionQueue = [];

function Asynchronous(/*Function*/ aFunction)
{
    var currentAsynchronousTimeoutCount = asynchronousTimeoutCount;

    if (asynchronousTimeoutId === null)
    {
        window.setNativeTimeout(function()
        {
            var queue = asynchronousFunctionQueue,
                index = 0,
                count = asynchronousFunctionQueue.length;

            ++asynchronousTimeoutCount;
            asynchronousTimeoutId = null;
            asynchronousFunctionQueue = [];

            for (; index < count; ++index)
                queue[index]();
        }, 0);
    }

    return function()
    {
        var args = arguments;

        if (asynchronousTimeoutCount > currentAsynchronousTimeoutCount)
            aFunction.apply(this, args);
        else
            asynchronousFunctionQueue.push(function() { aFunction.apply(this, args) });
    };
}

var NativeRequest = null;

// We check ActiveXObject first, because we require local file access and
// overrideMimeType feature (which the native XMLHttpRequest does not have in IE).
if (window.ActiveXObject !== undefined)
{
    // DON'T try 4.0 and 5.0: http://bit.ly/microsoft-msxml-explanation
    var MSXML_XMLHTTP_OBJECTS = ["Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0"],
        index = MSXML_XMLHTTP_OBJECTS.length;

    while (index--)
    {
        try
        {
            var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index];

            new ActiveXObject(MSXML_XMLHTTP);

            NativeRequest = function()
            {
                return new ActiveXObject(MSXML_XMLHTTP);
            }

            break;
        }
        catch (anException)
        {
        }
    }
}

if (!NativeRequest)
    NativeRequest = window.XMLHttpRequest;

GLOBAL(CFHTTPRequest) = function()
{
    this._eventDispatcher = new EventDispatcher(this);
    this._nativeRequest = new NativeRequest();

    var self = this;

    this._nativeRequest.onreadystatechange = function()
    {
        determineAndDispatchHTTPRequestEvents(self);
    }
}

CFHTTPRequest.UninitializedState    = 0;
CFHTTPRequest.LoadingState          = 1;
CFHTTPRequest.LoadedState           = 2;
CFHTTPRequest.InteractiveState      = 3;
CFHTTPRequest.CompleteState         = 4;

CFHTTPRequest.prototype.status = function()
{
    try
    {
        return this._nativeRequest.status || 0;
    }
    catch (anException)
    {
        return 0;
    }
}

CFHTTPRequest.prototype.statusText = function()
{
    try
    {
        return this._nativeRequest.statusText || "";
    }
    catch (anException)
    {
        return "";
    }
}

CFHTTPRequest.prototype.readyState = function()
{
    return this._nativeRequest.readyState;
}

CFHTTPRequest.prototype.success = function()
{
    var status = this.status();

    if (status >= 200 && status < 300)
        return YES;

    // file:// requests return with status 0, to know if they succeeded, we
    // need to know if there was any content.
    return status === 0 && this.responseText() && this.responseText().length;
}

CFHTTPRequest.prototype.responseXML = function()
{
    var responseXML = this._nativeRequest.responseXML;

    if (responseXML && (NativeRequest === window.XMLHttpRequest))
        return responseXML;

    return parseXML(this.responseText());
}

CFHTTPRequest.prototype.responsePropertyList = function()
{
    var responseText = this.responseText();

    if (CFPropertyList.sniffedFormatOfString(responseText) === CFPropertyList.FormatXML_v1_0)
        return CFPropertyList.propertyListFromXML(this.responseXML());

    return CFPropertyList.propertyListFromString(responseText);
}

CFHTTPRequest.prototype.responseText = function()
{
    return this._nativeRequest.responseText;
}

CFHTTPRequest.prototype.setRequestHeader = function(/*String*/ aHeader, /*Object*/ aValue)
{
    return this._nativeRequest.setRequestHeader(aHeader, aValue);
}

CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader)
{
    return this._nativeRequest.getResponseHeader(aHeader);
}

CFHTTPRequest.prototype.getAllResponseHeaders = function()
{
    return this._nativeRequest.getAllResponseHeaders();
}

CFHTTPRequest.prototype.overrideMimeType = function(/*String*/ aMimeType)
{
    if ("overrideMimeType" in this._nativeRequest)
        return this._nativeRequest.overrideMimeType(aMimeType);
}

CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*Boolean*/ isAsynchronous, /*String*/ aUser, /*String*/ aPassword)
{
    return this._nativeRequest.open(aMethod, aURL, isAsynchronous, aUser, aPassword);
}

CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
{
    try
    {
        return this._nativeRequest.send(aBody);
    }
    catch (anException)
    {
        // FIXME: Do something more complex, with 404's?
        this._eventDispatcher.dispatchEvent({ type:"failure", request:this });
    }
}

CFHTTPRequest.prototype.abort = function()
{
    return this._nativeRequest.abort();
}

CFHTTPRequest.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
    this._eventDispatcher.addEventListener(anEventName, anEventListener);
}

CFHTTPRequest.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
    this._eventDispatcher.removeEventListener(anEventName, anEventListener);
}

function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
{
    var eventDispatcher = aRequest._eventDispatcher;

    eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest});

    var nativeRequest = aRequest._nativeRequest,
        readyState = ["uninitialized", "loading", "loaded", "interactive", "complete"][aRequest.readyState()];

    eventDispatcher.dispatchEvent({ type:readyState, request:aRequest});

    if (readyState === "complete")
    {
        var status = "HTTP" + aRequest.status();

        eventDispatcher.dispatchEvent({ type:status, request:aRequest });

        var result = aRequest.success() ? "success" : "failure";

        eventDispatcher.dispatchEvent({ type:result, request:aRequest });
    }
}

function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure)
{
    var request = new CFHTTPRequest();

    if (aURL.pathExtension() === "plist")
        request.overrideMimeType("text/xml");

    if (exports.asyncLoader)
    {
        request.onsuccess = Asynchronous(onsuccess);
        request.onfailure = Asynchronous(onfailure);
    }
    else
    {
        request.onsuccess = onsuccess;
        request.onfailure = onfailure;
    }

    request.open("GET", aURL.absoluteString(), exports.asyncLoader);
    request.send("");
}

#ifdef BROWSER
exports.asyncLoader = YES;
#else
exports.asyncLoader = NO;
#endif

// FIXME: Get rid of these when we no longer need Mock requests in flatten.
exports.Asynchronous = Asynchronous;
exports.determineAndDispatchHTTPRequestEvents = determineAndDispatchHTTPRequestEvents;
<commit_msg>Add global 403 handler for CFHTTPRequest, to make it easier to catch authentication challenges.<commit_after>/*
 * CFHTTPRequest.js
 * Objective-J
 *
 * Created by Francisco Tolmasky.
 * Copyright 2010, 280 North, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

var asynchronousTimeoutCount = 0,
    asynchronousTimeoutId = null,
    asynchronousFunctionQueue = [];

function Asynchronous(/*Function*/ aFunction)
{
    var currentAsynchronousTimeoutCount = asynchronousTimeoutCount;

    if (asynchronousTimeoutId === null)
    {
        window.setNativeTimeout(function()
        {
            var queue = asynchronousFunctionQueue,
                index = 0,
                count = asynchronousFunctionQueue.length;

            ++asynchronousTimeoutCount;
            asynchronousTimeoutId = null;
            asynchronousFunctionQueue = [];

            for (; index < count; ++index)
                queue[index]();
        }, 0);
    }

    return function()
    {
        var args = arguments;

        if (asynchronousTimeoutCount > currentAsynchronousTimeoutCount)
            aFunction.apply(this, args);
        else
            asynchronousFunctionQueue.push(function() { aFunction.apply(this, args) });
    };
}

var NativeRequest = null;

// We check ActiveXObject first, because we require local file access and
// overrideMimeType feature (which the native XMLHttpRequest does not have in IE).
if (window.ActiveXObject !== undefined)
{
    // DON'T try 4.0 and 5.0: http://bit.ly/microsoft-msxml-explanation
    var MSXML_XMLHTTP_OBJECTS = ["Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0"],
        index = MSXML_XMLHTTP_OBJECTS.length;

    while (index--)
    {
        try
        {
            var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index];

            new ActiveXObject(MSXML_XMLHTTP);

            NativeRequest = function()
            {
                return new ActiveXObject(MSXML_XMLHTTP);
            }

            break;
        }
        catch (anException)
        {
        }
    }
}

if (!NativeRequest)
    NativeRequest = window.XMLHttpRequest;

GLOBAL(CFHTTPRequest) = function()
{
    this._eventDispatcher = new EventDispatcher(this);
    this._nativeRequest = new NativeRequest();

    var self = this;

    this._nativeRequest.onreadystatechange = function()
    {
        determineAndDispatchHTTPRequestEvents(self);
    }

    if (CFHTTPRequest.AuthenticationDelegate !== nil)
        this._eventDispatcher.addEventListener("HTTP403", function(){CFHTTPRequest.AuthenticationDelegate(self)});
}

CFHTTPRequest.UninitializedState    = 0;
CFHTTPRequest.LoadingState          = 1;
CFHTTPRequest.LoadedState           = 2;
CFHTTPRequest.InteractiveState      = 3;
CFHTTPRequest.CompleteState         = 4;

//override to forward all CFHTTPRequest authorization failures to a single function
CFHTTPRequest.AuthenticationDelegate = nil;

CFHTTPRequest.prototype.status = function()
{
    try
    {
        return this._nativeRequest.status || 0;
    }
    catch (anException)
    {
        return 0;
    }
}

CFHTTPRequest.prototype.statusText = function()
{
    try
    {
        return this._nativeRequest.statusText || "";
    }
    catch (anException)
    {
        return "";
    }
}

CFHTTPRequest.prototype.readyState = function()
{
    return this._nativeRequest.readyState;
}

CFHTTPRequest.prototype.success = function()
{
    var status = this.status();

    if (status >= 200 && status < 300)
        return YES;

    // file:// requests return with status 0, to know if they succeeded, we
    // need to know if there was any content.
    return status === 0 && this.responseText() && this.responseText().length;
}

CFHTTPRequest.prototype.responseXML = function()
{
    var responseXML = this._nativeRequest.responseXML;

    if (responseXML && (NativeRequest === window.XMLHttpRequest))
        return responseXML;

    return parseXML(this.responseText());
}

CFHTTPRequest.prototype.responsePropertyList = function()
{
    var responseText = this.responseText();

    if (CFPropertyList.sniffedFormatOfString(responseText) === CFPropertyList.FormatXML_v1_0)
        return CFPropertyList.propertyListFromXML(this.responseXML());

    return CFPropertyList.propertyListFromString(responseText);
}

CFHTTPRequest.prototype.responseText = function()
{
    return this._nativeRequest.responseText;
}

CFHTTPRequest.prototype.setRequestHeader = function(/*String*/ aHeader, /*Object*/ aValue)
{
    return this._nativeRequest.setRequestHeader(aHeader, aValue);
}

CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader)
{
    return this._nativeRequest.getResponseHeader(aHeader);
}

CFHTTPRequest.prototype.getAllResponseHeaders = function()
{
    return this._nativeRequest.getAllResponseHeaders();
}

CFHTTPRequest.prototype.overrideMimeType = function(/*String*/ aMimeType)
{
    if ("overrideMimeType" in this._nativeRequest)
        return this._nativeRequest.overrideMimeType(aMimeType);
}

CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*Boolean*/ isAsynchronous, /*String*/ aUser, /*String*/ aPassword)
{
    return this._nativeRequest.open(aMethod, aURL, isAsynchronous, aUser, aPassword);
}

CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
{
    try
    {
        return this._nativeRequest.send(aBody);
    }
    catch (anException)
    {
        // FIXME: Do something more complex, with 404's?
        this._eventDispatcher.dispatchEvent({ type:"failure", request:this });
    }
}

CFHTTPRequest.prototype.abort = function()
{
    return this._nativeRequest.abort();
}

CFHTTPRequest.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
    this._eventDispatcher.addEventListener(anEventName, anEventListener);
}

CFHTTPRequest.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
{
    this._eventDispatcher.removeEventListener(anEventName, anEventListener);
}

function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
{
    var eventDispatcher = aRequest._eventDispatcher;

    eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest});

    var nativeRequest = aRequest._nativeRequest,
        readyState = ["uninitialized", "loading", "loaded", "interactive", "complete"][aRequest.readyState()];

    eventDispatcher.dispatchEvent({ type:readyState, request:aRequest});

    if (readyState === "complete")
    {
        var status = "HTTP" + aRequest.status();

        eventDispatcher.dispatchEvent({ type:status, request:aRequest });

        var result = aRequest.success() ? "success" : "failure";

        eventDispatcher.dispatchEvent({ type:result, request:aRequest });
    }
}

function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure)
{
    var request = new CFHTTPRequest();

    if (aURL.pathExtension() === "plist")
        request.overrideMimeType("text/xml");

    if (exports.asyncLoader)
    {
        request.onsuccess = Asynchronous(onsuccess);
        request.onfailure = Asynchronous(onfailure);
    }
    else
    {
        request.onsuccess = onsuccess;
        request.onfailure = onfailure;
    }

    request.open("GET", aURL.absoluteString(), exports.asyncLoader);
    request.send("");
}

#ifdef BROWSER
exports.asyncLoader = YES;
#else
exports.asyncLoader = NO;
#endif

// FIXME: Get rid of these when we no longer need Mock requests in flatten.
exports.Asynchronous = Asynchronous;
exports.determineAndDispatchHTTPRequestEvents = determineAndDispatchHTTPRequestEvents;
<|endoftext|>


<commit_before>var promise 	= require('bluebird');
var path        = require('path');
var rivescript  = require("rivescript");

var botconfig   = require('../config/botconfig');

var botutil 	= require('../utils/botutil');
var stringutil 	= require('../utils/stringutil');
var debugutil 	= require('../utils/debugutil');

exports = module.exports = function(instances)
{
	return new Botctl(instances);
}

function Botctl(instances)
{
    this.bot = {};

    if(instances && instances.length!=undefined)
    {
        for (var i = 0; i < instances.length; i++)
        {
            var bot = instances[i];
            bot.language = bot.language || 'en';
            bot.config = bot.config || {};
            bot.path = bot.path || '';
            bot.variables = bot.variables || {};

            this.bot[bot.language] = {};

            this.bot[bot.language].loaded = false;
            this.bot[bot.language].entities = [];
            this.bot[bot.language].variables = bot.variables;
            this.bot[bot.language].brain = new rivescript(bot.config);
            this.bot[bot.language].brain.loadDirectory(bot.path, success_handler.bind(null, bot.language, this), error_handler.bind(null, bot.language));
        }
    }
}

Botctl.prototype.setArray = function setArray(name, value, lang)
{
    if (value === void 0)
      return delete this.bot[lang].brain._array[name];
    else
      return this.bot[lang].brain._array[name] = value;
}

Botctl.prototype.setGlobal = function setGlobal(name, value, lang)
{
    return this.bot[lang].brain.setGlobal(name, value);
}

Botctl.prototype.setVariable = function setVariable(name, value, lang)
{
    return this.bot[lang].brain.setVariable(name, value);
}

Botctl.prototype.setUservar = function setUservar(user, name, value, lang)
{
    return this.bot[lang].brain.setUservar(user, name, value);
}

Botctl.prototype.setUservars = function setUservars(user, data, lang)
{
    return this.bot[lang].brain.setUservars(user, data);
}

Botctl.prototype.setSubroutine = function setSubroutine(name, code, lang)
{
    return this.bot[lang].brain.setSubroutine(name, code);
}

Botctl.prototype.setHandler = function setHandler(language_name, obj, lang)
{
    return this.bot[lang].brain.setHandler(language_name, obj);
}

Botctl.prototype.setSubstitution = function setSubstitution(name, value, lang)
{
    return this.bot[lang].brain.setSubstitution(name, value);
}

Botctl.prototype.setPerson = function setPerson(name, value, lang)
{
    return this.bot[lang].brain.setPerson(name, value);
}

Botctl.prototype.getArray = function getArray(lang)
{
    return this.bot[lang].brain._array;
}

Botctl.prototype.getSubstitution = function getSubstitution(lang)
{
    return this.bot[lang].brain._sub;
}

Botctl.prototype.lastMatch = function lastMatch(user, lang)
{
    return this.bot[lang].brain.lastMatch(user);
}

Botctl.prototype.initialMatch = function initialMatch(user, lang)
{
    return this.bot[lang].brain.initialMatch(user);
}

Botctl.prototype.getUserTopicTriggers = function getUserTopicTriggers(user, lang)
{
    return this.bot[lang].brain.getUserTopicTriggers(user);
}

Botctl.prototype.getVariable = function getVariable(user, name, lang)
{
    return this.bot[lang].brain.getVariable(user, name);
}

Botctl.prototype.getUservar = function getUservar(user, name, lang)
{
    return this.bot[lang].brain.getUservar(user, name);
}

Botctl.prototype.getUservars = function getUservars(user, lang)
{
    return this.bot[lang].brain.getUservars(user);
}

//functions
Botctl.prototype.clearUservars = function clearUservars(user, lang)
{
    return this.bot[lang].brain.clearUservars(user);
}

Botctl.prototype.freezeUservars = function freezeUservars(user, lang)
{
    return this.bot[lang].brain.freezeUservars(user);
}

Botctl.prototype.thawUservars = function thawUservars(user, lang)
{
    return this.bot[lang].brain.thawUservars(user);
}

/**
 * Process bot event
 * @param {object} event, an event object, required keys/values: sender, text, lang
 */
Botctl.prototype.processEvent = function processEvent(event)
{
    var self = this;
	return new promise(function(resolve, reject)
	{
		self.bot[event.lang].brain.replyAsync(event.sender, event.text)
        .then(function(response)
        {
            var reply = botutil.getVariablesObjectFromString(response);
            resolve ({reply:reply, event:event});
        })
        .catch(function(error)
        {
            console.log(error);
        });
	});
}

Botctl.prototype.setEntities = function setEntities(entities, lang)
{
    var self = this;
    return new promise(function(resolve, reject)
	{
        entities = entities || [];
        lang = lang || 'en';

        if(self.bot[lang].loaded)
        {
            processEntities(entities, lang)
            .then(function(response)
            {
                resolve(response);
            });
        }
        else
            self.bot[lang].entities = entities;
    });
}

function success_handler (lang, self)
{
    self.bot[lang].brain.sortReplies();
    self.bot[lang].loaded = true;

    if(self.bot[lang].variables && self.bot[lang].variables.entities)
        processEntities(self,self.bot[lang].variables.entities, lang);

    if(self.bot[lang].entities.length>0)
    {
        processEntities(self, bot[lang].entities, lang);
        self.bot[lang].entities = [];
    }
}

function error_handler (lang, err)
{
	console.log("Error loading brains " + lang + ": " + err + "\n");
}

function processEntities(self, entities, lang)
{
    return new promise(function(resolve, reject)
	{
        var flatentities = JSON.stringify(entities);
        var flatbotarray = JSON.stringify(self.getArray(lang));
        var flatbotsubs = JSON.stringify(self.getSubstitution(lang));

        flatentities += flatbotarray + flatbotsubs;

        for (var i = 0; i < entities.length; i++)
        {
            var entity = entities[i];
            if(entity.hasOwnProperty('name') && entity.hasOwnProperty('entries'))
            {
                var array = [];
                for (var j = 0; j < entity.entries.length; j++)
                {
                    var entry = entity.entries[j];
                    var prefix = entry.hasOwnProperty('prefix') ? entry.prefix + ' ' : '';
                    var vlr = prefix + entry.value;
                    array.push(vlr);

                    if(entry.hasOwnProperty('synonyms'))
                    {
                        for (var s = 0; s < entry.synonyms.length; s++)
                        {
                            var sub = entry.synonyms[s];

                            if(sub!=vlr)
                            {
                                self.setSubstitution(sub, vlr, lang);

                                if(botconfig.humanize_subs)
                                {
                                    for (var c = 0; c < sub.length; c++)
                                    {
                                        var newword = botutil.dropChar(sub, c);

                                        if(flatentities.indexOf('"'+newword+'"')==-1)
                                            self.setSubstitution(newword, vlr, lang);

                                        newword = botutil.switchChar(sub, c);
                                        if(flatentities.indexOf('"'+newword+'"')==-1)
                                            self.setSubstitution(newword, vlr, lang);

                                        //botutil.addTypo(sub, c); will add too much subs
                                    }
                                }
                            }
                        }
                    }
                }

                self.setArray(entity.name, array, lang);
            }
        }
        resolve(true);
    });
}
<commit_msg>bug correction on bot controller<commit_after>var promise 	= require('bluebird');
var path        = require('path');
var rivescript  = require("rivescript");

var botconfig   = require('../config/botconfig');

var botutil 	= require('../utils/botutil');
var stringutil 	= require('../utils/stringutil');
var debugutil 	= require('../utils/debugutil');

exports = module.exports = function(instances)
{
	return new Botctl(instances);
}

function Botctl(instances)
{
    this.bot = {};

    if(instances && instances.length!=undefined)
    {
        for (var i = 0; i < instances.length; i++)
        {
            var bot = instances[i];
            bot.language = bot.language || 'en';
            bot.config = bot.config || {};
            bot.path = bot.path || '';
            bot.variables = bot.variables || {};

            this.bot[bot.language] = {};

            this.bot[bot.language].loaded = false;
            this.bot[bot.language].entities = [];
            this.bot[bot.language].variables = bot.variables;
            this.bot[bot.language].brain = new rivescript(bot.config);
            this.bot[bot.language].brain.loadDirectory(bot.path, success_handler.bind(null, bot.language, this), error_handler.bind(null, bot.language));
        }
    }
}

Botctl.prototype.setArray = function setArray(name, value, lang)
{
    if (value === void 0)
      return delete this.bot[lang].brain._array[name];
    else
      return this.bot[lang].brain._array[name] = value;
}

Botctl.prototype.setGlobal = function setGlobal(name, value, lang)
{
    return this.bot[lang].brain.setGlobal(name, value);
}

Botctl.prototype.setVariable = function setVariable(name, value, lang)
{
    return this.bot[lang].brain.setVariable(name, value);
}

Botctl.prototype.setUservar = function setUservar(user, name, value, lang)
{
    return this.bot[lang].brain.setUservar(user, name, value);
}

Botctl.prototype.setUservars = function setUservars(user, data, lang)
{
    return this.bot[lang].brain.setUservars(user, data);
}

Botctl.prototype.setSubroutine = function setSubroutine(name, code, lang)
{
    return this.bot[lang].brain.setSubroutine(name, code);
}

Botctl.prototype.setHandler = function setHandler(language_name, obj, lang)
{
    return this.bot[lang].brain.setHandler(language_name, obj);
}

Botctl.prototype.setSubstitution = function setSubstitution(name, value, lang)
{
    return this.bot[lang].brain.setSubstitution(name, value);
}

Botctl.prototype.setPerson = function setPerson(name, value, lang)
{
    return this.bot[lang].brain.setPerson(name, value);
}

Botctl.prototype.getArray = function getArray(lang)
{
    return this.bot[lang].brain._array;
}

Botctl.prototype.getSubstitution = function getSubstitution(lang)
{
    return this.bot[lang].brain._sub;
}

Botctl.prototype.lastMatch = function lastMatch(user, lang)
{
    return this.bot[lang].brain.lastMatch(user);
}

Botctl.prototype.initialMatch = function initialMatch(user, lang)
{
    return this.bot[lang].brain.initialMatch(user);
}

Botctl.prototype.getUserTopicTriggers = function getUserTopicTriggers(user, lang)
{
    return this.bot[lang].brain.getUserTopicTriggers(user);
}

Botctl.prototype.getVariable = function getVariable(user, name, lang)
{
    return this.bot[lang].brain.getVariable(user, name);
}

Botctl.prototype.getUservar = function getUservar(user, name, lang)
{
    return this.bot[lang].brain.getUservar(user, name);
}

Botctl.prototype.getUservars = function getUservars(user, lang)
{
    return this.bot[lang].brain.getUservars(user);
}

//functions
Botctl.prototype.clearUservars = function clearUservars(user, lang)
{
    return this.bot[lang].brain.clearUservars(user);
}

Botctl.prototype.freezeUservars = function freezeUservars(user, lang)
{
    return this.bot[lang].brain.freezeUservars(user);
}

Botctl.prototype.thawUservars = function thawUservars(user, lang)
{
    return this.bot[lang].brain.thawUservars(user);
}

/**
 * Process bot event
 * @param {object} event, an event object, required keys/values: sender, text, lang
 */
Botctl.prototype.processEvent = function processEvent(event)
{
    var self = this;
	return new promise(function(resolve, reject)
	{
		self.bot[event.lang].brain.replyAsync(event.sender, event.text)
        .then(function(response)
        {
            var reply = botutil.getVariablesObjectFromString(response);
            resolve ({reply:reply, event:event});
        })
        .catch(function(error)
        {
            console.log(error);
        });
	});
}

Botctl.prototype.setEntities = function setEntities(entities, lang)
{
    var self = this;
    return new promise(function(resolve, reject)
	{
        entities = entities || [];
        lang = lang || 'en';

		if(self.bot[lang])
		{
	        if(self.bot[lang].loaded)
	        {
	            processEntities(entities, lang)
	            .then(function(response)
	            {
	                resolve(response);
	            });
	        }
	        else
	            self.bot[lang].entities = entities;
		}
    });
}

function success_handler (lang, self)
{
    self.bot[lang].brain.sortReplies();
    self.bot[lang].loaded = true;

    if(self.bot[lang].variables && self.bot[lang].variables.entities)
        processEntities(self,self.bot[lang].variables.entities, lang);

    if(self.bot[lang].entities.length>0)
    {
        processEntities(self, bot[lang].entities, lang);
        self.bot[lang].entities = [];
    }
}

function error_handler (lang, err)
{
	console.log("Error loading brains " + lang + ": " + err + "\n");
}

function processEntities(self, entities, lang)
{
    return new promise(function(resolve, reject)
	{
        var flatentities = JSON.stringify(entities);
        var flatbotarray = JSON.stringify(self.getArray(lang));
        var flatbotsubs = JSON.stringify(self.getSubstitution(lang));

        flatentities += flatbotarray + flatbotsubs;

        for (var i = 0; i < entities.length; i++)
        {
            var entity = entities[i];
            if(entity.hasOwnProperty('name') && entity.hasOwnProperty('entries'))
            {
                var array = [];
                for (var j = 0; j < entity.entries.length; j++)
                {
                    var entry = entity.entries[j];
                    var prefix = entry.hasOwnProperty('prefix') ? entry.prefix + ' ' : '';
                    var vlr = prefix + entry.value;
                    array.push(vlr);

                    if(entry.hasOwnProperty('synonyms'))
                    {
                        for (var s = 0; s < entry.synonyms.length; s++)
                        {
                            var sub = entry.synonyms[s];

                            if(sub!=vlr)
                            {
                                self.setSubstitution(sub, vlr, lang);

                                if(botconfig.humanize_subs)
                                {
                                    for (var c = 0; c < sub.length; c++)
                                    {
                                        var newword = botutil.dropChar(sub, c);

                                        if(flatentities.indexOf('"'+newword+'"')==-1)
                                            self.setSubstitution(newword, vlr, lang);

                                        newword = botutil.switchChar(sub, c);
                                        if(flatentities.indexOf('"'+newword+'"')==-1)
                                            self.setSubstitution(newword, vlr, lang);

                                        //botutil.addTypo(sub, c); will add too much subs
                                    }
                                }
                            }
                        }
                    }
                }

                self.setArray(entity.name, array, lang);
            }
        }
        resolve(true);
    });
}
<|endoftext|>


<commit_before>var config   = require("./conf/mongo-driver"),
    Playlist = require('../../api/Playlist'),
    Media    = require('../../api/Media'),
    mubsub   = require("mubsub"),
    moment   = require("moment"),
    mbc      = require('mbc-common'),
    async    = require('async');

function drop_err(callback, err_handler) {
    return function(err,v) {
        if( !err )
            callback(v);
        else if( err_handler )
            err_handler(err);
    };
}

function mongo_driver(conf) {
    var self = this;

    this.newPlaylistCallback    = undefined;
    this.updatePlaylistCallback = undefined;
    this.removePlaylistCallback = undefined;

    console.log("mbc-mosto: [INFO] Creating mongodb playlists driver");

    mongo_driver.prototype.start = function(ops) {
        var config = ops.config;
        var span = ops.span;
        var db = mbc.db(config && config.db);
        var channel = mbc.pubsub();

        self.scheds = db.collection('scheds');
        self.lists = db.collection('lists');

        // these two lines must go, mosto will take care of calling these
        var boundaries = self.validTimes();
        self.readPlaylists(boundaries.from, boundaries.to);

        channel.subscribe({backend: 'schedbackend', method: 'create'}, function(msg) {
            if( self.inTime(msg.model) ) {
                self.createPlaylist(msg.model, drop_err(self.newPlaylistCallback, console.log));
            }
        });
        channel.subscribe({backend: 'schedbackend', method: 'update'}, function(msg) {
            // I forward all create messages
            self.createPlaylist(msg.model, drop_err(self.updatePlaylistCallback, console.log));
        });
        channel.subscribe({backend: 'schedbackend', method: 'delete'}, function(msg) {
            self.removePlaylistCallback(msg.model._id);
        });
    };

    mongo_driver.prototype.registerNewPlaylistListener = function(newPlaylistCallback) {
        self.newPlaylistCallback = newPlaylistCallback;
    };
    mongo_driver.prototype.registerUpdatePlaylistListener = function(updatePlaylistCallback) {
        self.updatePlaylistCallback = updatePlaylistCallback;
    };
    mongo_driver.prototype.registerRemovePlaylistListener = function(removePlaylistCallback) {
        self.removePlaylistCallback = removePlaylistCallback;
    };

    mongo_driver.prototype.validTimes = function() {
        if( self.boundaries ) {
            return self.boundaries;
        } else {
            var now = moment(new Date());
            var until = moment(new Date());
            var span = config.load_time * 60 * 1000;
            until.add(span);
            return {
                from: now,
                to: until,
                span: span
            };
        }
    };

    mongo_driver.prototype.getBoundary(from, to) {
        // Notice that if from = to = undefined then boundaries are
        // set to undefined, and settings file is used again
        if( to === undefined ) {
            // assume from = { from: date, to: date }
            var boundaries = from;
            if( boundaries.from === undefined ) {
                // if boundaries = { to: date }, I assume from = now
                boundaries.from = new Date();
            }
            self.boundaries = {
                from: moment(boundaries.from),
                to: moment(boundaries.to)
            };
        } else
            self.boundaries = { from: moment(from), to: moment(to) };
    };

    mongo_driver.prototype.setBoundaries = function(from, to) {
        self.boundaries = self.getBoundaries(from, to);
        return self.validTimes()
    };

    mongo_driver.prototype.inTime = function(sched) {
        var boundaries = self.validTimes();
        return (sched.start <= boundaries.to.unix() &&
                sched.end >= boundaries.from.unix());
    };

    mongo_driver.prototype.readPlaylists =  function(ops, callback) {
        // read playlists from the database

        /*
         * This gets the database's 'scheds' and 'lists' collections
         * and turn them into a mosto.api.Playlist. Then return one by one to callback
         * which defaults to self.newPlaylistCallback
         */
        var from = ops.from;
        var to = ops.to;
        var setBoundary = ops.setBoundary;
        var boundaries = undefined;

        //console.log("mbc-mosto: [INFO] Start reading playlists from " + config.playlists.to_read);
        var boundaries = self.setBoundaries(from, to);
        self.scheds.findItems({
            start: { $lte: boundaries.to.unix()},
            end: { $gte: boundaries.from.unix() }
        }, function(err, scheds) {
            if( err ) {
                console.log(err);
            } else if( scheds ) {
                console.log("Processing sched list:", scheds);
                async.map(scheds, self.createPlaylist, function(err, playlists) {
                    if( callback )
                        callback(playlists);
                    else
                        playlists.forEach(function(playlist) {
                            self.newPlaylistCallback(playlist);
                        });
                });
            } else {
                console.log('Done');
            }
        });
    };

    mongo_driver.prototype.createPlaylist = function(sched, callback) {
        self.lists.findById(sched.list, function(err, list) {
            if( err ) {
                if( callback )
                    callback(err);
                return err;
            }

            console.log("Processing list:", list);
            var startDate = new Date(sched.start * 1000);
            var endDate   = new Date(sched.end * 1000);
            var name = sched.name;
            var playlist_id = (sched._id.toHexString && sched._id.toHexString()) || sched._id;

            var medias = [];
            list.models.forEach(function(block, order) {
                var block_id = (block._id.toHexString && block._id.toHexString()) || block._id;
                var orig_order = order;
                var actual_order = undefined;
                var clip_name = block.name;
                // TODO: don't know what goes in type
                var type = "default";
                var file = block.file;
                var length = block.durationraw;
                var fps = block.fps;
                medias.push(new Media(block_id, orig_order, actual_order, playlist_id, clip_name, type, file, length, parseFloat(fps)));
            });

            var playlist = new Playlist(playlist_id, name, startDate, medias, endDate);

            if( callback )
                callback(err, playlist);
            else
                return playlist;
        });
    };
}

exports = module.exports = function(conf) {
    var driver = new mongo_driver(conf);
    return driver;
};
<commit_msg>calculate from, to, span as necessary<commit_after>var config   = require("./conf/mongo-driver"),
    Playlist = require('../../api/Playlist'),
    Media    = require('../../api/Media'),
    mubsub   = require("mubsub"),
    moment   = require("moment"),
    mbc      = require('mbc-common'),
    async    = require('async');

function drop_err(callback, err_handler) {
    return function(err,v) {
        if( !err )
            callback(v);
        else if( err_handler )
            err_handler(err);
    };
}

function mongo_driver(conf) {
    var self = this;

    this.newPlaylistCallback    = undefined;
    this.updatePlaylistCallback = undefined;
    this.removePlaylistCallback = undefined;

    console.log("mbc-mosto: [INFO] Creating mongodb playlists driver");

    mongo_driver.prototype.start = function(ops) {
        var config = ops.config;
        var span = ops.span;
        var db = mbc.db(config && config.db);
        var channel = mbc.pubsub();

        self.scheds = db.collection('scheds');
        self.lists = db.collection('lists');

        // these two lines must go, mosto will take care of calling these
        var boundaries = self.validTimes();
        self.readPlaylists(boundaries.from, boundaries.to);

        channel.subscribe({backend: 'schedbackend', method: 'create'}, function(msg) {
            if( self.inTime(msg.model) ) {
                self.createPlaylist(msg.model, drop_err(self.newPlaylistCallback, console.log));
            }
        });
        channel.subscribe({backend: 'schedbackend', method: 'update'}, function(msg) {
            // I forward all create messages
            self.createPlaylist(msg.model, drop_err(self.updatePlaylistCallback, console.log));
        });
        channel.subscribe({backend: 'schedbackend', method: 'delete'}, function(msg) {
            self.removePlaylistCallback(msg.model._id);
        });
    };

    mongo_driver.prototype.registerNewPlaylistListener = function(newPlaylistCallback) {
        self.newPlaylistCallback = newPlaylistCallback;
    };
    mongo_driver.prototype.registerUpdatePlaylistListener = function(updatePlaylistCallback) {
        self.updatePlaylistCallback = updatePlaylistCallback;
    };
    mongo_driver.prototype.registerRemovePlaylistListener = function(removePlaylistCallback) {
        self.removePlaylistCallback = removePlaylistCallback;
    };

    mongo_driver.prototype.validTimes = function() {
        if( self.boundaries ) {
            return self.boundaries;
        } else {
            var now = moment(new Date());
            var until = moment(new Date());
            var span = config.load_time * 60 * 1000;
            until.add(span);
            return {
                from: now,
                to: until,
                span: span
            };
        }
    };

    mongo_driver.prototype.getBoundary(from, to) {
        // Notice that if from = to = undefined then boundaries are
        // set to undefined, and settings file is used again
        if( to === undefined ) {
            // assume from = { from: date, to: date }
            var boundaries = from;
            if( boundaries.from === undefined ) {
                // if boundaries = { to: date }, I assume from = now
                boundaries.from = new moment();
            } else {
                boundaries.from = moment(boundaries.from);
            }
            if( !(boundaries.to || boundaries.span) ) {
                // if neither is present, we use the config file
                boundaries.span = config.load_time * 60 * 1000;
            }
            if( boundaries.to === undefined ) {
                // we asume span is present and calculate it
                boundaries.to = new moment(boundaries.from);
                boundaries.to.add(boundaries.span);
            } else {
                boundaries.to = moment(boundaries.to);
            }
            if( boundaries.span === undefined ) {
                // we calculate it using from and to
                boundaries.span = boundaries.to.diff(boundaries.from);
            }
            self.boundaries = {
                from: moment(boundaries.from),
                to: moment(boundaries.to)
            };
        } else
            self.boundaries = { from: moment(from), to: moment(to) };
    };

    mongo_driver.prototype.setBoundaries = function(from, to) {
        self.boundaries = self.getBoundaries(from, to);
        return self.validTimes()
    };

    mongo_driver.prototype.inTime = function(sched) {
        var boundaries = self.validTimes();
        return (sched.start <= boundaries.to.unix() &&
                sched.end >= boundaries.from.unix());
    };

    mongo_driver.prototype.readPlaylists =  function(ops, callback) {
        // read playlists from the database

        /*
         * This gets the database's 'scheds' and 'lists' collections
         * and turn them into a mosto.api.Playlist. Then return one by one to callback
         * which defaults to self.newPlaylistCallback
         */
        var from = ops.from;
        var to = ops.to;
        var setBoundary = ops.setBoundary;
        var boundaries = undefined;

        //console.log("mbc-mosto: [INFO] Start reading playlists from " + config.playlists.to_read);
        var boundaries = self.setBoundaries(from, to);
        self.scheds.findItems({
            start: { $lte: boundaries.to.unix()},
            end: { $gte: boundaries.from.unix() }
        }, function(err, scheds) {
            if( err ) {
                console.log(err);
            } else if( scheds ) {
                console.log("Processing sched list:", scheds);
                async.map(scheds, self.createPlaylist, function(err, playlists) {
                    if( callback )
                        callback(playlists);
                    else
                        playlists.forEach(function(playlist) {
                            self.newPlaylistCallback(playlist);
                        });
                });
            } else {
                console.log('Done');
            }
        });
    };

    mongo_driver.prototype.createPlaylist = function(sched, callback) {
        self.lists.findById(sched.list, function(err, list) {
            if( err ) {
                if( callback )
                    callback(err);
                return err;
            }

            console.log("Processing list:", list);
            var startDate = new Date(sched.start * 1000);
            var endDate   = new Date(sched.end * 1000);
            var name = sched.name;
            var playlist_id = (sched._id.toHexString && sched._id.toHexString()) || sched._id;

            var medias = [];
            list.models.forEach(function(block, order) {
                var block_id = (block._id.toHexString && block._id.toHexString()) || block._id;
                var orig_order = order;
                var actual_order = undefined;
                var clip_name = block.name;
                // TODO: don't know what goes in type
                var type = "default";
                var file = block.file;
                var length = block.durationraw;
                var fps = block.fps;
                medias.push(new Media(block_id, orig_order, actual_order, playlist_id, clip_name, type, file, length, parseFloat(fps)));
            });

            var playlist = new Playlist(playlist_id, name, startDate, medias, endDate);

            if( callback )
                callback(err, playlist);
            else
                return playlist;
        });
    };
}

exports = module.exports = function(conf) {
    var driver = new mongo_driver(conf);
    return driver;
};
<|endoftext|>


<commit_before>import * as dgram from 'dgram';
import * as crypto from 'crypto';
import * as os from 'os';
import * as util from 'util';
import uuid from '@libs/uuid';
import Emitter from '@libs/emitter';

const PUUID = uuid.v4();
const HOST_NAME = os.hostname();

const defaultOptions = {
  address: '0.0.0.0',
  port: 11777,
  broadcast: undefined,
  multicast: undefined,
  multicastTTL: 1,
  unicast: undefined,
  key: undefined,
  reuseAddr: true,
  ignoreProcess: true,
  ignoreInstance: true
};

/**
 * Network
 *
 * @export {Function}
 * @param {object} [options=defaultOptions]
 * @returns {object}
 */
export default function Network(options = defaultOptions) {
  const opts = Object.assign({}, options, defaultOptions);

  const state = Object.assign({}, opts, {
    socket: dgram.createSocket({ type: 'udp4', reuseAddr: opts.reuseAddr }),
    emitter: Emitter(),
    instanceUuid: uuid.v4()
  });

  handledMessages(state);

  // PUBLIC API
  return Object.freeze({
    start,
    stop,
    send
  });
}

function handledMessages({ socket, key, emitter, instanceUuid, ignoreProcess, ignoreInstance }) {
  socket.on('message', (data, rinfo) => {
    decode(key, data, (err, decoded) => {
      const { pid, iid, event, data } = decoded;

      if (err) {
        // most decode errors are because we tried
        // to decrypt a packet for which we do not
        // have the key

        // the only other possibility is that the
        // message was split across packet boundaries
        // and that is not handled

        emitter.emit('error', err);
      } else if (pid === PUUID && ignoreProcess && iid !== instanceUuid) {
        return false;
      } else if (iid === instanceUuid && ignoreInstance) {
        return false;
      } else if (event && data) {
        emitter.emit(event, data, decoded, rinfo);
      } else {
        emitter.emit('message', decoded);
      }
    });
  });
}

async function start({ socket, port, address, unicast, broadcast, multicast, multicastTTL }) {
  return new Promise((resolve, reject) => {
    socket.bind(port, address, () => {
      if (unicast !== undefined) {
        if (typeof unicast === 'string' && unicast.indexOf(',') !== -1) {
          return resolve(unicast.split(','));
        }

        return resolve([].concat(unicast));
      }

      if (multicast !== undefined) {
        // Default to using broadcast if multicast address is not specified.
        socket.setBroadcast(true);

        // TODO: get the default broadcast address from os.networkInterfaces()
        // (not currently returned)
        return resolve([broadcast || '255.255.255.255']);
      }

      try {
        // addMembership can throw if there are no interfaces available
        socket.addMembership(multicast);
        socket.setMulticastTTL(multicastTTL);
      } catch (err) {
        return reject(err);
      }

      return resolve([multicast]);
    });
  });
}

async function stop(socket) {
  return util.promisify(socket.close)();
}

async function send(event, data, { socket, port, instanceUuid, destinations }) {
  const obj = {
    event,
    procUuid: PUUID,
    instanceUuid,
    hostName: HOST_NAME,
    data
  };

  encode(obj, (err, contents) => {
    if (err) {
      return false;
    }

    const msg = new Buffer(contents);

    destinations.forEach((destination) => {
      socket.send(msg, 0, msg.length, port, destination);
    });
  });
}

function encode(key, data, callback) {
  let encoded;

  try {
    encoded = key ? encrypt(JSON.stringify(data), key) : JSON.stringify(data);
  } catch (e) {
    return callback(e);
  }

  return callback(undefined, encoded);
}

function decode(key, data, callback) {
  let decoded;

  try {
    if (key) {
      decoded = JSON.parse(decrypt(data.toString(), key));
    } else {
      decoded = JSON.parse(data);
    }
  } catch (e) {
    return callback(e);
  }

  return callback(undefined, decoded);
}

function encrypt(str, key) {
  const cipher = crypto.createCipher('aes256', key);
  const chunk1 = cipher.update(str, 'utf8', 'binary');
  const chunk2 = cipher.final('binary');

  return chunk1 + chunk2;
}

function decrypt(str, key) {
  const decipher = crypto.createDecipher('aes256', key);
  const chunk1 = decipher.update(str, 'binary', 'utf8');
  const chunk2 = decipher.final('utf8');

  return chunk1 + chunk2;
}
<commit_msg>fix: fixed order in Object.assign `options`<commit_after>import * as dgram from 'dgram';
import * as crypto from 'crypto';
import * as os from 'os';
import * as util from 'util';
import uuid from '@libs/uuid';
import Emitter from '@libs/emitter';

const PUUID = uuid.v4();
const HOST_NAME = os.hostname();

const defaultOptions = {
  address: '0.0.0.0',
  port: 11777,
  broadcast: undefined,
  multicast: undefined,
  multicastTTL: 1,
  unicast: undefined,
  key: undefined,
  reuseAddr: true,
  ignoreProcess: true,
  ignoreInstance: true
};

/**
 * Network
 *
 * @export {Function}
 * @param {object} [options=defaultOptions]
 * @returns {object}
 */
export default function Network(options = defaultOptions) {
  const opts = Object.assign({}, defaultOptions, options);

  const state = Object.assign({}, opts, {
    socket: dgram.createSocket({ type: 'udp4', reuseAddr: opts.reuseAddr }),
    emitter: Emitter(),
    instanceUuid: uuid.v4()
  });

  handledMessages(state);

  // PUBLIC API
  return Object.freeze({
    start,
    stop,
    send
  });
}

function handledMessages({ socket, key, emitter, instanceUuid, ignoreProcess, ignoreInstance }) {
  socket.on('message', (data, rinfo) => {
    decode(key, data, (err, decoded) => {
      const { pid, iid, event, data } = decoded;

      if (err) {
        // most decode errors are because we tried
        // to decrypt a packet for which we do not
        // have the key

        // the only other possibility is that the
        // message was split across packet boundaries
        // and that is not handled

        emitter.emit('error', err);
      } else if (pid === PUUID && ignoreProcess && iid !== instanceUuid) {
        return false;
      } else if (iid === instanceUuid && ignoreInstance) {
        return false;
      } else if (event && data) {
        emitter.emit(event, data, decoded, rinfo);
      } else {
        emitter.emit('message', decoded);
      }
    });
  });
}

async function start({ socket, port, address, unicast, broadcast, multicast, multicastTTL }) {
  return new Promise((resolve, reject) => {
    socket.bind(port, address, () => {
      if (unicast !== undefined) {
        if (typeof unicast === 'string' && unicast.indexOf(',') !== -1) {
          return resolve(unicast.split(','));
        }

        return resolve([].concat(unicast));
      }

      if (multicast !== undefined) {
        // Default to using broadcast if multicast address is not specified.
        socket.setBroadcast(true);

        // TODO: get the default broadcast address from os.networkInterfaces()
        // (not currently returned)
        return resolve([broadcast || '255.255.255.255']);
      }

      try {
        // addMembership can throw if there are no interfaces available
        socket.addMembership(multicast);
        socket.setMulticastTTL(multicastTTL);
      } catch (err) {
        return reject(err);
      }

      return resolve([multicast]);
    });
  });
}

async function stop(socket) {
  return util.promisify(socket.close)();
}

async function send(event, data, { socket, port, instanceUuid, destinations }) {
  const obj = {
    event,
    procUuid: PUUID,
    instanceUuid,
    hostName: HOST_NAME,
    data
  };

  encode(obj, (err, contents) => {
    if (err) {
      return false;
    }

    const msg = new Buffer(contents);

    destinations.forEach((destination) => {
      socket.send(msg, 0, msg.length, port, destination);
    });
  });
}

function encode(key, data, callback) {
  let encoded;

  try {
    encoded = key ? encrypt(JSON.stringify(data), key) : JSON.stringify(data);
  } catch (e) {
    return callback(e);
  }

  return callback(undefined, encoded);
}

function decode(key, data, callback) {
  let decoded;

  try {
    if (key) {
      decoded = JSON.parse(decrypt(data.toString(), key));
    } else {
      decoded = JSON.parse(data);
    }
  } catch (e) {
    return callback(e);
  }

  return callback(undefined, decoded);
}

function encrypt(str, key) {
  const cipher = crypto.createCipher('aes256', key);
  const chunk1 = cipher.update(str, 'utf8', 'binary');
  const chunk2 = cipher.final('binary');

  return chunk1 + chunk2;
}

function decrypt(str, key) {
  const decipher = crypto.createDecipher('aes256', key);
  const chunk1 = decipher.update(str, 'binary', 'utf8');
  const chunk2 = decipher.final('utf8');

  return chunk1 + chunk2;
}
<|endoftext|>


<commit_before>var express = require('express');
var token = require('../helpers/token')
var uuid = require('uuid');
var slack = require('../helpers/slack');
var router = express();

function getMfa(req, res) {
  var client_secret = process.env.CLIENT_SECRET || req.webtaskContext.data.client_secret;
  var mongo_connection = process.env.MONGO_CONNECTION || req.webtaskContext.data.mongo_connection;
  var secret = new Buffer(client_secret, 'base64');

  var decodedToken;
  var signedToken;

  token.verify(req.query.token, secret, mongo_connection).then(function (decoded) {
     if (!decoded.slack_username) { throw new Error('JWT does not contain a slack_mfa_username'); }

     decodedToken = decoded;
     return createMfaToken(secret, decoded.sub, decoded.aud, mongo_connection);
  }).then(function (st) {
    signedToken = st;
    var baseUrl = process.env.URL || 'https://webtask.it.auth0.com/api/run/' + req.x_wt.container + '/' + req.x_wt.jtn;
    var slackOptions = {
      verifyUrl: baseUrl + '/verify?token=' + signedToken,
      cancelUrl: baseUrl + '/cancel?token=' + signedToken,
      username: decodedToken.slack_username.toLowerCase().trim(),
      token: process.env.SLACK_API_TOKEN || req.webtaskContext.data.slack_api_token
    };

    return slack.sendDM(slackOptions);
  }).then(function() {
    res.render('mfa', {
      token: signedToken,
      slack_username: decodedToken.slack_username,
      slack_enrolled: decodedToken.slack_enrolled
    });
  }).catch(function (err) {
    console.log(err);
    res.status(500).send('Error.').end();
  });
}

function createMfaToken(secret, sub, aud, connectionString) {
  var options = { expiresIn: '5m'};
  var payload = {
    sub: sub,
    aud: aud,
    jti: uuid.v4(),
    iat: new Date().getTime() / 1000,
    iss: 'urn:sgmeyer:slack:mfaverify'
  };

  return token.issue(payload, secret, options, connectionString);
}

router.get('/mfa', getMfa);

module.exports = router;
<commit_msg>revoking mea token and refactoring mfa conn string name<commit_after>var express = require('express');
var token = require('../helpers/token')
var uuid = require('uuid');
var slack = require('../helpers/slack');
var router = express();

function getMfa(req, res) {
  var client_secret = process.env.CLIENT_SECRET || req.webtaskContext.data.client_secret;
  var connectionString = process.env.MONGO_CONNECTION || req.webtaskContext.data.mongo_connection;
  var secret = new Buffer(client_secret, 'base64');

  var decodedToken;
  var signedToken;

  token.verify(req.query.token, secret, connectionString).then(function (decoded) {
    if (!decoded.slack_username) { throw new Error('JWT does not contain a slack_mfa_username'); }

    decodedToken = decoded;
    return token.revoke(decoded, connectionString);
  }).then(function () {
     return createMfaToken(secret, decodedToken.sub, decodedToken.aud, connectionString);
  }).then(function (mfaToken) {
    signedToken = mfaToken;
    var baseUrl = process.env.URL || 'https://webtask.it.auth0.com/api/run/' + req.x_wt.container + '/' + req.x_wt.jtn;
    var slackOptions = {
      verifyUrl: baseUrl + '/verify?token=' + signedToken,
      cancelUrl: baseUrl + '/cancel?token=' + signedToken,
      username: decodedToken.slack_username.toLowerCase().trim(),
      token: process.env.SLACK_API_TOKEN || req.webtaskContext.data.slack_api_token
    };

    return slack.sendDM(slackOptions);
  }).then(function() {
    res.render('mfa', {
      token: signedToken,
      slack_username: decodedToken.slack_username,
      slack_enrolled: decodedToken.slack_enrolled
    });
  }).catch(function (err) {
    console.log(err);
    res.status(500).send('Error.').end();
  });
}

function createMfaToken(secret, sub, aud, connectionString) {
  var options = { expiresIn: '5m'};
  var payload = {
    sub: sub,
    aud: aud,
    jti: uuid.v4(),
    iat: new Date().getTime() / 1000,
    iss: 'urn:sgmeyer:slack:mfaverify'
  };

  return token.issue(payload, secret, options, connectionString);
}

router.get('/mfa', getMfa);

module.exports = router;
<|endoftext|>


<commit_before>var config   = require("./conf/mongo-driver"),
    Playlist = require('../../api/Playlist'),
    Media    = require('../../api/Media'),
    mubsub   = require("mubsub"),
    moment   = require("moment"),
    mbc      = require('mbc-common'),
    async    = require('async');

function mongo_driver() {
    var self = this;

    this.newPlaylistCallback    = undefined;
    this.updatePlaylistCallback = undefined;
    this.removePlaylistCallback = undefined;

    console.log("mbc-mosto: [INFO] Creating mongodb playlists driver");

    mongo_driver.prototype.start = function(config) {
        var db = mbc.db(config && config.db);
        var client = mubsub(db);

        var channel = client.channel('messages', { size: 10000000, max: 5000 });

        self.scheds = db.collection('scheds');
        self.lists = db.collection('lists');
        var boundaries = self.validTimes();
        self.readPlaylists(boundaries.from, boundaries.to);

        channel.subscribe({channel: 'schedbackend', method: 'create'}, function(msg) {
            if( self.inTime(msg.model) ) {
                self.createPlaylist(msg.model, self.newPlaylistCallback);
            }
        });
        channel.subscribe({channel: 'schedbackend', method: 'update'}, function(msg) {
            self.createPlaylist(msg.model, self.updatePlaylistCallback);
        });
        channel.subscribe({channel: 'schedbackend', method: 'delete'}, function(msg) {
            self.removePlaylistCallback(msg.model._id);
        });
    };

    mongo_driver.prototype.registerNewPlaylistListener = function(newPlaylistCallback) {
        self.newPlaylistCallback = newPlaylistCallback;
    };
    mongo_driver.prototype.registerUpdatePlaylistListener = function(updatePlaylistCallback) {
        self.updatePlaylistCallback = updatePlaylistCallback;
    };
    mongo_driver.prototype.registerRemovePlaylistListener = function(removePlaylistCallback) {
        self.removePlaylistCallback = removePlaylistCallback;
    };

    mongo_driver.prototype.validTimes = function() {
        if( self.boundaries ) {
            return self.boundaries;
        } else {
            var now = moment(new Date());
            var until = moment(new Date());
            until.add(config.load_time * 60 * 1000);
            return {
                from: now,
                to: until
            }
        }
    };

    mongo_driver.prototype.setBoundaries = function(from, to) {
        // Notice that if from = to = undefined then boundaries are
        // set to undefined, and settings file is used again
        if( to === undefined )
            // assume from = { from: date, to: date }
            self.boundaries = from;
        else
            self.boundaries = { from: from, to: to };
        return self.validTimes()
    };

    mongo_driver.prototype.inTime = function(sched) {
        var boundaries = self.validTimes();
        return (sched.start <= boundaries.to.unix() &&
                sched.end >= boundaries.from.unix());
    };

    mongo_driver.prototype.readPlaylists =  function(from, to, callback) {
        // read playlists from the database

        /*
         * This should get the database's 'scheds' and 'lists' collections
         * and turn them into a mosto.api.Playlist
         */
        //console.log("mbc-mosto: [INFO] Start reading playlists from " + config.playlists.to_read);
        var boundaries = self.setBoundaries(from, to);
        self.scheds.findItems({
            start: { $lte: boundaries.to.unix()},
            end: { $gte: boundaries.from.unix() }
        }, function(err, scheds) {
            if( err ) {
                console.log(err);
            } else if( scheds ) {
                console.log("Processing sched list:", scheds);
                async.map(scheds, self.createPlaylist, function(err, playlists) {
                    if( callback )
                        callback(playlists);
                    else
                        playlists.forEach(function(playlist) {
                            self.newPlaylistCallback(playlist);
                        });
                });
            } else {
                console.log('Done');
            }
        });
    };

    mongo_driver.prototype.createPlaylist = function(sched, callback) {
        self.lists.findById(sched.list, function(err, list) {
            console.log("Processing list:", list);
            var startDate = new Date(sched.start * 1000);
            var endDate   = new Date(sched.end * 1000);
            var name = (sched._id.toHexString && sched._id.toHexString()) || sched._id;

            var medias = [];
            list.models.forEach(function(block) {
                // TODO: don't know what goes in type
                var type = "default";
                var file = block.file;
                var length = block.durationraw;
                var fps = block.fps;
                medias.push(new Media(type, file, length, parseFloat(fps)));
            });
            var playlist = new Playlist(name, startDate, medias, endDate);
            if( callback )
                callback(err, playlist);
            else
                return playlist;
        });
    };
}

exports = module.exports = function() {
    var driver = new mongo_driver();
    return driver;
};
<commit_msg>pass error to callback if there is one<commit_after>var config   = require("./conf/mongo-driver"),
    Playlist = require('../../api/Playlist'),
    Media    = require('../../api/Media'),
    mubsub   = require("mubsub"),
    moment   = require("moment"),
    mbc      = require('mbc-common'),
    async    = require('async');

function mongo_driver() {
    var self = this;

    this.newPlaylistCallback    = undefined;
    this.updatePlaylistCallback = undefined;
    this.removePlaylistCallback = undefined;

    console.log("mbc-mosto: [INFO] Creating mongodb playlists driver");

    mongo_driver.prototype.start = function(config) {
        var db = mbc.db(config && config.db);
        var client = mubsub(db);

        var channel = client.channel('messages', { size: 10000000, max: 5000 });

        self.scheds = db.collection('scheds');
        self.lists = db.collection('lists');
        var boundaries = self.validTimes();
        self.readPlaylists(boundaries.from, boundaries.to);

        channel.subscribe({channel: 'schedbackend', method: 'create'}, function(msg) {
            if( self.inTime(msg.model) ) {
                self.createPlaylist(msg.model, self.newPlaylistCallback);
            }
        });
        channel.subscribe({channel: 'schedbackend', method: 'update'}, function(msg) {
            self.createPlaylist(msg.model, self.updatePlaylistCallback);
        });
        channel.subscribe({channel: 'schedbackend', method: 'delete'}, function(msg) {
            self.removePlaylistCallback(msg.model._id);
        });
    };

    mongo_driver.prototype.registerNewPlaylistListener = function(newPlaylistCallback) {
        self.newPlaylistCallback = newPlaylistCallback;
    };
    mongo_driver.prototype.registerUpdatePlaylistListener = function(updatePlaylistCallback) {
        self.updatePlaylistCallback = updatePlaylistCallback;
    };
    mongo_driver.prototype.registerRemovePlaylistListener = function(removePlaylistCallback) {
        self.removePlaylistCallback = removePlaylistCallback;
    };

    mongo_driver.prototype.validTimes = function() {
        if( self.boundaries ) {
            return self.boundaries;
        } else {
            var now = moment(new Date());
            var until = moment(new Date());
            until.add(config.load_time * 60 * 1000);
            return {
                from: now,
                to: until
            }
        }
    };

    mongo_driver.prototype.setBoundaries = function(from, to) {
        // Notice that if from = to = undefined then boundaries are
        // set to undefined, and settings file is used again
        if( to === undefined )
            // assume from = { from: date, to: date }
            self.boundaries = from;
        else
            self.boundaries = { from: from, to: to };
        return self.validTimes()
    };

    mongo_driver.prototype.inTime = function(sched) {
        var boundaries = self.validTimes();
        return (sched.start <= boundaries.to.unix() &&
                sched.end >= boundaries.from.unix());
    };

    mongo_driver.prototype.readPlaylists =  function(from, to, callback) {
        // read playlists from the database

        /*
         * This should get the database's 'scheds' and 'lists' collections
         * and turn them into a mosto.api.Playlist
         */
        //console.log("mbc-mosto: [INFO] Start reading playlists from " + config.playlists.to_read);
        var boundaries = self.setBoundaries(from, to);
        self.scheds.findItems({
            start: { $lte: boundaries.to.unix()},
            end: { $gte: boundaries.from.unix() }
        }, function(err, scheds) {
            if( err ) {
                console.log(err);
            } else if( scheds ) {
                console.log("Processing sched list:", scheds);
                async.map(scheds, self.createPlaylist, function(err, playlists) {
                    if( callback )
                        callback(playlists);
                    else
                        playlists.forEach(function(playlist) {
                            self.newPlaylistCallback(playlist);
                        });
                });
            } else {
                console.log('Done');
            }
        });
    };

    mongo_driver.prototype.createPlaylist = function(sched, callback) {
        self.lists.findById(sched.list, function(err, list) {
            if( err ) {
                if( callback )
                    callback(err);
                return err;
            }

            console.log("Processing list:", list);
            var startDate = new Date(sched.start * 1000);
            var endDate   = new Date(sched.end * 1000);
            var name = (sched._id.toHexString && sched._id.toHexString()) || sched._id;

            var medias = [];
            list.models.forEach(function(block) {
                // TODO: don't know what goes in type
                var type = "default";
                var file = block.file;
                var length = block.durationraw;
                var fps = block.fps;
                medias.push(new Media(type, file, length, parseFloat(fps)));
            });
            var playlist = new Playlist(name, startDate, medias, endDate);
            if( callback )
                callback(err, playlist);
            else
                return playlist;
        });
    };
}

exports = module.exports = function() {
    var driver = new mongo_driver();
    return driver;
};
<|endoftext|>


<commit_before>'use strict';

var deepEqual = require('deep-equal');

/**
 * All functions exported in this module will become part of the Test class but
 * should not rely on the `this` context of the call. Each function's interface
 * should be the following:
 *
 *     function(arg1, ...argN, function callback(err, opts))
 *
 * where
 * - `err` is the assertion error or falsy value in case of successful assertion
 * - `opts` may have the following fields
 *     opts = {
 *         expected,
 *         notExpected,
 *         actual,
 *         skip
 *     }
 *
 * WARNING: All function arguments should be defined explicitly, so that
 * function's `length` property returns the correct value
 */

module.exports = Assertions;

function Assertions() {
}

Assertions.prototype.fail = function(cb) {
    cb(check(false), {
        message: 'fail'
    });
};

Assertions.prototype.pass = function(cb) {
    cb(check(true));
};

//todo move it to test, make it not visible by the plan()?
Assertions.prototype.skip = function(cb) {
    cb(check(true), {
        skip: true
    });
};

Assertions.prototype.ok
= Assertions.prototype.assert
= Assertions.prototype.truthy
= function(value, cb) {
    cb(check(value), {
        message: 'should be true',
        expected: true,
        actual: value
    });
};

Assertions.prototype.notOk
= Assertions.prototype.falsy
= function(value, cb) {
    cb(check(!value), {
        message: 'should be false',
        expected: false,
        actual: value
    });
};

Assertions.prototype.noError
= function(err, cb) {
    cb(check(!err), {
        message: 'should not be an error',
        expected: false,
        actual: err,
        error: err
    });
};

Assertions.prototype.equal = function(actual, expected, cb) {
    cb(check(actual === expected), {
        message: 'should be equal',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.notEqual = function(actual, notExpected, cb) {
    cb(check(actual !== notExpected), {
        message: 'should not be equal',
        actual: actual,
        notExpected: notExpected
    });
};

Assertions.prototype.deepEqual = function(actual, expected, cb) {
    cb(check(deepEqual(actual, expected, {strict: true})), {
        message: 'should be equivalent',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.deepLooseEqual
= Assertions.prototype.looseEqual
= function(actual, expected, cb) {
    cb(check(deepEqual(actual, expected)), {
        message: 'should be equivalent',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.notDeepEqual = function(actual, notExpected, cb) {
    cb(check(!deepEqual(actual, notExpected, {strict: true})), {
        message: 'should not be equivalent',
        actual: actual,
        notExpected: notExpected
    });
};

Assertions.prototype.notDeepLooseEqual
= Assertions.prototype.notLooseEqual
= function(actual, notExpected, cb) {
    cb(check(!deepEqual(actual, notExpected)), {
        message: 'should not be equivalent',
        actual: actual,
        expected: notExpected
    });
};

Assertions.prototype['throws'] = function(fn, expected, cb) {
    if (arguments.length < 3) {
        cb = expected;
        expected = undefined;
    }

    var caught = null;
    try {
        fn();
    } catch (err) {
        caught = err;
    }

    var passed = expected instanceof RegExp
            ? expected.test(caught)
            : caught
            ? caught == expected
            : caught;

    cb(check(passed), {
        message: 'should throw',
        actual: caught,
        expected: expected && String(expected),
        error: !passed && caught
    });
};

Assertions.prototype.doesNotThrow = function(fn, expected, cb) {
    if (typeof expected == 'function' && !cb) {
        cb = expected;
        expected = undefined;
    }
    var caught = null;
    try {
        fn();
    } catch (err) {
        caught = { error: err };
    }

    cb(check(!caught), {
        message: 'should not throw',
        actual: caught && caught.error,
        expected: expected && String(expected),
        error: caught && caught.error
    });
};

function check(condition) {
    return condition ? null : new Error();
}
<commit_msg>Assert methods rename `throws` --> `throwing` and `doesNotThrow` --> `notThrowing`<commit_after>'use strict';

var deepEqual = require('deep-equal');

/**
 * All functions exported in this module will become part of the Test class but
 * should not rely on the `this` context of the call. Each function's interface
 * should be the following:
 *
 *     function(arg1, ...argN, function callback(err, opts))
 *
 * where
 * - `err` is the assertion error or falsy value in case of successful assertion
 * - `opts` may have the following fields
 *     opts = {
 *         expected,
 *         notExpected,
 *         actual,
 *         skip
 *     }
 *
 * WARNING: All function arguments should be defined explicitly, so that
 * function's `length` property returns the correct value
 */

module.exports = Assertions;

function Assertions() {
}

Assertions.prototype.fail = function(cb) {
    cb(check(false), {
        message: 'fail'
    });
};

Assertions.prototype.pass = function(cb) {
    cb(check(true));
};

//todo move it to test, make it not visible by the plan()?
Assertions.prototype.skip = function(cb) {
    cb(check(true), {
        skip: true
    });
};

Assertions.prototype.ok
= Assertions.prototype.assert
= Assertions.prototype.truthy
= function(value, cb) {
    cb(check(value), {
        message: 'should be true',
        expected: true,
        actual: value
    });
};

Assertions.prototype.notOk
= Assertions.prototype.falsy
= function(value, cb) {
    cb(check(!value), {
        message: 'should be false',
        expected: false,
        actual: value
    });
};

Assertions.prototype.noError
= function(err, cb) {
    cb(check(!err), {
        message: 'should not be an error',
        expected: false,
        actual: err,
        error: err
    });
};

Assertions.prototype.equal = function(actual, expected, cb) {
    cb(check(actual === expected), {
        message: 'should be equal',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.notEqual = function(actual, notExpected, cb) {
    cb(check(actual !== notExpected), {
        message: 'should not be equal',
        actual: actual,
        notExpected: notExpected
    });
};

Assertions.prototype.deepEqual = function(actual, expected, cb) {
    cb(check(deepEqual(actual, expected, {strict: true})), {
        message: 'should be equivalent',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.deepLooseEqual
= Assertions.prototype.looseEqual
= function(actual, expected, cb) {
    cb(check(deepEqual(actual, expected)), {
        message: 'should be equivalent',
        actual: actual,
        expected: expected
    });
};

Assertions.prototype.notDeepEqual = function(actual, notExpected, cb) {
    cb(check(!deepEqual(actual, notExpected, {strict: true})), {
        message: 'should not be equivalent',
        actual: actual,
        notExpected: notExpected
    });
};

Assertions.prototype.notDeepLooseEqual
= Assertions.prototype.notLooseEqual
= function(actual, notExpected, cb) {
    cb(check(!deepEqual(actual, notExpected)), {
        message: 'should not be equivalent',
        actual: actual,
        expected: notExpected
    });
};

Assertions.prototype.throwing = function(fn, expected, cb) {
    if (arguments.length < 3) {
        cb = expected;
        expected = undefined;
    }

    var caught = null;
    try {
        fn();
    } catch (err) {
        caught = err;
    }

    var passed = expected instanceof RegExp
            ? expected.test(caught)
            : caught
            ? caught == expected
            : caught;

    cb(check(passed), {
        message: 'should throw',
        actual: caught,
        expected: expected && String(expected),
        error: !passed && caught
    });
};

Assertions.prototype.notThrowing = function(fn, cb) {
    var caught = null;
    try {
        fn();
    } catch (err) {
        caught = { error: err };
    }

    cb(check(!caught), {
        message: 'should not throw',
        actual: caught && caught.error,
        error: caught && caught.error
    });
};

function check(condition) {
    return condition ? null : new Error();
}
<|endoftext|>


<commit_before>'use strict';

const { get, getOr, isFunction, first, isNil } = require('lodash/fp');

const { GraphQLObjectType } = require('graphql');
const { createPoliciesMiddleware } = require('./policy');

const introspectionQueries = [
  '__Schema',
  '__Type',
  '__Field',
  '__InputValue',
  '__EnumValue',
  '__Directive',
];

/**
 * Wrap the schema's resolvers if they've been
 * customized using the GraphQL extension service
 * @param {object} options
 * @param {GraphQLSchema} options.schema
 * @param {object} options.strapi
 * @param {object} options.extension
 * @return {GraphQLSchema}
 */
const wrapResolvers = ({ schema, strapi, extension = {} }) => {
  // Get all the registered resolvers configuration
  const { resolversConfig = {} } = extension;

  // Fields filters
  const isValidFieldName = ([field]) => !field.startsWith('__');

  const typeMap = schema.getTypeMap();

  // Iterate over every field from every type within the
  // schema's type map and wrap its resolve attribute if needed
  Object.entries(typeMap).forEach(([type, definition]) => {
    const isGraphQLObjectType = definition instanceof GraphQLObjectType;
    const isIgnoredType = introspectionQueries.includes(type);

    if (!isGraphQLObjectType || isIgnoredType) {
      return;
    }

    const fields = definition.getFields();
    const fieldsToProcess = Object.entries(fields).filter(isValidFieldName);

    for (const [fieldName, fieldDefinition] of fieldsToProcess) {
      const defaultResolver = get(fieldName);

      const path = `${type}.${fieldName}`;
      const resolverConfig = getOr({}, path, resolversConfig);

      const { resolve: baseResolver = defaultResolver } = fieldDefinition;

      // Parse & initialize the middlewares
      const middlewares = parseMiddlewares(resolverConfig, strapi);

      // Generate the policy middleware
      const policyMiddleware = createPoliciesMiddleware(resolverConfig, { strapi });

      // Add the policyMiddleware at the end of the middlewares collection
      middlewares.push(policyMiddleware);

      // Bind every middleware to the next one
      const boundMiddlewares = middlewares.map((middleware, index, collection) => {
        return (...args) =>
          middleware(
            // Make sure the last middleware in the list calls the baseResolver
            index >= collection.length - 1 ? baseResolver : boundMiddlewares[index + 1],
            ...args
          );
      });

      /**
       * GraphQL authorization flow
       * @param {object} context
       * @return {Promise<void>}
       */
      const authorize = async ({ context }) => {
        const authConfig = get('auth', resolverConfig);
        const authContext = get('state.auth', context);

        const isMutationOrQuery = ['Mutation', 'Query'].includes(type);
        const hasConfig = !isNil(authConfig);

        const isAuthDisabled = authConfig === false;

        if ((isMutationOrQuery || hasConfig) && !isAuthDisabled) {
          try {
            await strapi.auth.verify(authContext, authConfig);
          } catch (error) {
            // TODO: [v4] Throw GraphQL Error instead
            throw new Error('Forbidden access');
          }
        }
      };

      /**
       * Base resolver wrapper that handles authorization, middlewares & policies
       * @param {object} parent
       * @param {object} args
       * @param {object} context
       * @param {object} info
       * @return {Promise<any>}
       */
      fieldDefinition.resolve = async (parent, args, context, info) => {
        await authorize({ context });

        // Execute middlewares (including the policy middleware which will always be included)
        return first(boundMiddlewares).call(null, parent, args, context, info);
      };
    }
  });

  return schema;
};

/**
 * Get & parse middlewares definitions from the resolver's config
 * @param {object} resolverConfig
 * @param {object} strapi
 * @return {function[]}
 */
const parseMiddlewares = (resolverConfig, strapi) => {
  const resolverMiddlewares = getOr([], 'middlewares', resolverConfig);

  // TODO: [v4] to factorize with compose endpoints (routes)
  return resolverMiddlewares.map(middleware => {
    if (isFunction(middleware)) {
      return middleware;
    }

    if (typeof middleware === 'string') {
      return strapi.middleware(middleware);
    }

    if (typeof middleware === 'object') {
      const { name, options = {} } = middleware;

      return strapi.middleware(name)(options);
    }
  });
};

module.exports = { wrapResolvers };
<commit_msg>Handle subscriptions in wrapResolvers<commit_after>'use strict';

const { get, getOr, isFunction, first, isNil } = require('lodash/fp');

const { GraphQLObjectType } = require('graphql');
const { createPoliciesMiddleware } = require('./policy');

const introspectionQueries = [
  '__Schema',
  '__Type',
  '__Field',
  '__InputValue',
  '__EnumValue',
  '__Directive',
];

/**
 * Wrap the schema's resolvers if they've been
 * customized using the GraphQL extension service
 * @param {object} options
 * @param {GraphQLSchema} options.schema
 * @param {object} options.strapi
 * @param {object} options.extension
 * @return {GraphQLSchema}
 */
const wrapResolvers = ({ schema, strapi, extension = {} }) => {
  // Get all the registered resolvers configuration
  const { resolversConfig = {} } = extension;

  // Fields filters
  const isValidFieldName = ([field]) => !field.startsWith('__');

  const typesMaps = [schema.getTypeMap(), schema.getSubscriptionType().getFields()];

  typesMaps.forEach(typeMap =>
    // Iterate over every field from every type within the
    // schema's type map and wrap its resolve attribute if needed
    Object.entries(typeMap).forEach(([type, definition]) => {
      const isGraphQLObjectType = definition instanceof GraphQLObjectType;
      const isIgnoredType = introspectionQueries.includes(type);

      if (!isGraphQLObjectType || isIgnoredType) {
        return;
      }

      const fields = definition.getFields();
      const fieldsToProcess = Object.entries(fields).filter(isValidFieldName);

      for (const [fieldName, fieldDefinition] of fieldsToProcess) {
        const defaultResolver = get(fieldName);

        const path = `${type}.${fieldName}`;
        const resolverConfig = getOr({}, path, resolversConfig);

        const { resolve: baseResolver = defaultResolver } = fieldDefinition;

        // Parse & initialize the middlewares
        const middlewares = parseMiddlewares(resolverConfig, strapi);

        // Generate the policy middleware
        const policyMiddleware = createPoliciesMiddleware(resolverConfig, { strapi });

        // Add the policyMiddleware at the end of the middlewares collection
        middlewares.push(policyMiddleware);

        // Bind every middleware to the next one
        const boundMiddlewares = middlewares.map((middleware, index, collection) => {
          return (...args) =>
            middleware(
              // Make sure the last middleware in the list calls the baseResolver
              index >= collection.length - 1 ? baseResolver : boundMiddlewares[index + 1],
              ...args
            );
        });

        /**
         * GraphQL authorization flow
         * @param {object} context
         * @return {Promise<void>}
         */
        const authorize = async ({ context }) => {
          const authConfig = get('auth', resolverConfig);
          const authContext = get('state.auth', context);

          const isValidType = ['Mutation', 'Query', 'Subscription'].includes(type);
          const hasConfig = !isNil(authConfig);

          const isAuthDisabled = authConfig === false;

          if ((isValidType || hasConfig) && !isAuthDisabled) {
            try {
              await strapi.auth.verify(authContext, authConfig);
            } catch (error) {
              // TODO: [v4] Throw GraphQL Error instead
              throw new Error('Forbidden access');
            }
          }
        };

        /**
         * Base resolver wrapper that handles authorization, middlewares & policies
         * @param {object} parent
         * @param {object} args
         * @param {object} context
         * @param {object} info
         * @return {Promise<any>}
         */
        fieldDefinition.resolve = async (parent, args, context, info) => {
          await authorize({ context });

          // Execute middlewares (including the policy middleware which will always be included)
          return first(boundMiddlewares).call(null, parent, args, context, info);
        };
      }
    })
  );

  return schema;
};

/**
 * Get & parse middlewares definitions from the resolver's config
 * @param {object} resolverConfig
 * @param {object} strapi
 * @return {function[]}
 */
const parseMiddlewares = (resolverConfig, strapi) => {
  const resolverMiddlewares = getOr([], 'middlewares', resolverConfig);

  // TODO: [v4] to factorize with compose endpoints (routes)
  return resolverMiddlewares.map(middleware => {
    if (isFunction(middleware)) {
      return middleware;
    }

    if (typeof middleware === 'string') {
      return strapi.middleware(middleware);
    }

    if (typeof middleware === 'object') {
      const { name, options = {} } = middleware;

      return strapi.middleware(name)(options);
    }
  });
};

module.exports = { wrapResolvers };
<|endoftext|>