<commit_before>(function () {
    'use strict';

    angular
        .module('xr.dailyOutcome')
        .controller('DailyOutcomeController', DailyOutcomeController);

    DailyOutcomeController.$inject = ['ParseService', 'FormService', '$location'];

    function DailyOutcomeController(ParseService, FormService, $location) {
        var vm = this;
        vm.save = save;

        function save() {
            FormService.setAllFieldsToDirty(vm.dailyOutcomeForm);

            if (FormService.allFieldsAreValid(vm.dailyOutcomeForm)) {
                var dailyOutcome = {
                    firstStory: vm.outcome1,
                    secondStory: vm.outcome2,
                    thirdStory: vm.outcome3,
                    date: {
                        '__type': 'Date',
                        'iso': new Date().toISOString()
                    }
                };

                ParseService.postObject('DailyOutcome', dailyOutcome)
                    .then(function () {
                        $location.path('overview');
                    });
            }

        }
    }

})();<commit_msg>Added error to test travis<commit_after>(function () {
    'use strict';

    angular
        .module('xr.dailyOutcome')
        .controller('DailyOutcomeController', DailyOutcomeController);

    DailyOutcomeController.$inject = ['ParseService', 'FormService', '$location'];

    function DailyOutcomeController(ParseService, FormService, $location) {
        var vm = this;
        vm.save = save;

        function save() {
            FormService.setAllFieldsToDirty(vm.dailyOutcomeForm);

            if (FormService.allFieldsAreValid(vm.dailyOutcomeForm)) {
                var dailyOutcome = {
                    firstStory: vm.outcome1,
                    secondStory: vm.outcome2,
                    thirdStory: vm.outcome3,
                    date: {
                        '__type': 'Date',
                        'iso': new Date().toISOString()
                    }
                };

                ParseService.postObject('DailyOutcome', dailyOutcome)
                    .then(function () {
                        //$location.path('overview');
                    });
            }

        }
    }

})();<|endoftext|>


<commit_before>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                //asd
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                let action = response.result.action;
                /*console.log(responseText);
                console.log(responseData);
                console.log(action);*/
                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        responseData.facebook.forEach((facebookMessage) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message});
                            }
                        });
                    }
                //} else if (isDefined(responseText)) {
                } else if (isDefined(action) && isDefined(responseText)) {
                    console.log('Response as text message');
                    // facebook API limit for text length is 320,
                    // so we must split message if needed
                    var splittedText = splitResponse(responseText);
                    /*switch (action){
                        case 'show_prod':
                            console.log('generic');
                            async.eachSeries(splittedText, (textPart, callback) => {
                                sendFBMessage(sender, {text: textPart}, callback);
                            });
                            sendFBMessage(sender, generic_message, null);
                            break;
                        default:
                            console.log('default');
                            async.eachSeries(splittedText, (textPart, callback) => {
                                sendFBMessage(sender, {text: textPart}, callback);
                            });
                    }*/
                    async.eachSeries(splittedText, (textPart, callback) => {
                        sendFBMessage(sender, {text: textPart}, callback);
                    });
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    }
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    //console.log(output);
    //output = ['Length of param message[text] must be less than or equal to 320'];
    return output;
}

/*const generic_message = {
    attachment: {
        type: "template",
        payload: {
            template_type: "generic",
            elements: [{
                title: "rift",
                subtitle: "Next-generation virtual reality",
                item_url: "https://www.oculus.com/en-us/rift/",
                image_url: "http://messengerdemo.parseapp.com/img/rift.png",
                buttons: [{
                    type: "web_url",
                    url: "https://www.oculus.com/en-us/rift/",
                    title: "Open Web URL"
                }, {
                    type: "postback",
                    title: "Call Postback",
                    payload: "Payload for first bubble"
                }]
            }, {
                title: "touch",
                subtitle: "Your Hands, Now in VR",
                item_url: "https://www.oculus.com/en-us/touch/",
                image_url: "http://messengerdemo.parseapp.com/img/touch.png",
                buttons: [{
                    type: "web_url",
                    url: "https://www.oculus.com/en-us/touch/",
                    title: "Open Web URL"
                }, {
                    type: "postback",
                    title: "Call Postback",
                    payload: "Payload for second bubble"
                }]
            }]
        }
    }
};*/

function sendFBMessage(sender, messageData, callback) {
    //console.log(sender);
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
            //message: generic_message
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);
        var asd = data;
        //console.log(asd);
        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        //console.log(event);
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.post('/webhook_apiai/', (req, res) => {

    //var weather_query = new Boolean(false);
    var weather_query = 'false';
    weather_query = false;

    const generic_message = {
        attachment: {
            type: "template",
            payload: {
                template_type: "generic",
                elements: [{
                    title: "Fresh Pizza",
                    subtitle: "Be carefull and don't eat your fingers!!!",
                    item_url: "https://en.wikipedia.org/wiki/Pizza",
                    //image_url: "http://www.cbc.ca/inthekitchen/assets_c/2012/11/MargheritaPizza21-thumb-596x350-247022.jpg",
                    buttons: [{
                        type: "web_url",
                        url: "https://en.wikipedia.org/wiki/Pizza",
                        title: "Open Web URL"
                    }/*, {
                     type: "postback",
                     title: "Call Postback",
                     payload: "Payload for first bubble"
                     }*/]
                }]
            }
        }
    };

    try {
        var data = JSONbig.parse(req.body);
        //console.log(data);
        switch(data.result.action){
            case 'show_prod':
                console.log('pizza');
                switch(data.result.parameters.pizza_type){
                    case 'Margherita':
                        generic_message.attachment.payload.elements[0].title = data.result.parameters.pizza_type;
                        generic_message.attachment.payload.elements[0].image_url = "http://www.cbc.ca/inthekitchen/assets_c/2012/11/MargheritaPizza21-thumb-596x350-247022.jpg";
                }
                break;
            case 'show_weather':
                console.log('weather');

                if(isDefined(data.result.parameters['geo-city']) == true){
                    var city = data.result.parameters['geo-city'];
                    var base_url = "https://query.yahooapis.com/v1/public/yql?" + "q=select+%2A+from+weather.forecast+where+woeid+in+%28select+woeid+from+geo.places%281%29+where+text%3D%27"+city+"%27%29" + "&format=json";
                    weather_query = true;
                    request({
                        url: base_url,
                        method: 'GET'
                    }, function(error, response, body){
                        if(error) {
                            console.log(error);
                            return res.status(400).json({
                                status: "error",
                                error: err
                            });
                        } else {
                            var query = JSON.parse(body).query;
                            var results = query.results;
                            var channel = results.channel;
                            var item = channel.item;
                            var location = channel.location;
                            var units = channel.units;
                            var condition = item.condition;
                            //console.log(channel)
                            var string = "Today in " + location.city + ": " + condition.text + ", the temperature is " + condition.temp + " " + units.temperature
                            generic_message.attachment.payload.elements[0].title = 'Weather in' + city;
                            generic_message.attachment.payload.elements[0].subtitle = string;
                            generic_message.attachment.payload.elements[0].item_url = channel.link;
                            generic_message.attachment.payload.elements[0].image_url = channel.image.url;
                            generic_message.attachment.payload.elements[0].buttons[0].url = channel.link;
                            return res.status(200).json({
                                data: {
                                    facebook: generic_message
                                }
                            });
                        }
                    });
                } else {
                    return res.status(200).json({
                        data: {
                            facebook: {text: 'no city'}
                        }
                    })}
                weather_query = false;
                break;
        }
        console.log(weather_query);
        if (weather_query != true){
            return res.status(200).json({
                data: {
                    facebook: generic_message
                }
            })
        } else {
            return res.status(200).json({
                data: {
                    facebook: {text: 'error'}
                }
            })}
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }
});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();
<commit_msg>'webhook_test'<commit_after>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                //asd
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                let action = response.result.action;
                /*console.log(responseText);
                console.log(responseData);
                console.log(action);*/
                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        responseData.facebook.forEach((facebookMessage) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message});
                            }
                        });
                    }
                //} else if (isDefined(responseText)) {
                } else if (isDefined(action) && isDefined(responseText)) {
                    console.log('Response as text message');
                    // facebook API limit for text length is 320,
                    // so we must split message if needed
                    var splittedText = splitResponse(responseText);
                    /*switch (action){
                        case 'show_prod':
                            console.log('generic');
                            async.eachSeries(splittedText, (textPart, callback) => {
                                sendFBMessage(sender, {text: textPart}, callback);
                            });
                            sendFBMessage(sender, generic_message, null);
                            break;
                        default:
                            console.log('default');
                            async.eachSeries(splittedText, (textPart, callback) => {
                                sendFBMessage(sender, {text: textPart}, callback);
                            });
                    }*/
                    async.eachSeries(splittedText, (textPart, callback) => {
                        sendFBMessage(sender, {text: textPart}, callback);
                    });
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    }
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    //console.log(output);
    //output = ['Length of param message[text] must be less than or equal to 320'];
    return output;
}

/*const generic_message = {
    attachment: {
        type: "template",
        payload: {
            template_type: "generic",
            elements: [{
                title: "rift",
                subtitle: "Next-generation virtual reality",
                item_url: "https://www.oculus.com/en-us/rift/",
                image_url: "http://messengerdemo.parseapp.com/img/rift.png",
                buttons: [{
                    type: "web_url",
                    url: "https://www.oculus.com/en-us/rift/",
                    title: "Open Web URL"
                }, {
                    type: "postback",
                    title: "Call Postback",
                    payload: "Payload for first bubble"
                }]
            }, {
                title: "touch",
                subtitle: "Your Hands, Now in VR",
                item_url: "https://www.oculus.com/en-us/touch/",
                image_url: "http://messengerdemo.parseapp.com/img/touch.png",
                buttons: [{
                    type: "web_url",
                    url: "https://www.oculus.com/en-us/touch/",
                    title: "Open Web URL"
                }, {
                    type: "postback",
                    title: "Call Postback",
                    payload: "Payload for second bubble"
                }]
            }]
        }
    }
};*/

function sendFBMessage(sender, messageData, callback) {
    //console.log(sender);
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
            //message: generic_message
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);
        var asd = data;
        //console.log(asd);
        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        //console.log(event);
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.post('/webhook_apiai/', (req, res) => {

    //var weather_query = new Boolean(false);
    var weather_query = 'false';
    weather_query = false;

    const generic_message = {
        attachment: {
            type: "template",
            payload: {
                template_type: "generic",
                elements: [{
                    title: "Fresh Pizza",
                    subtitle: "Be carefull and don't eat your fingers!!!",
                    item_url: "https://en.wikipedia.org/wiki/Pizza",
                    //image_url: "http://www.cbc.ca/inthekitchen/assets_c/2012/11/MargheritaPizza21-thumb-596x350-247022.jpg",
                    buttons: [{
                        type: "web_url",
                        url: "https://en.wikipedia.org/wiki/Pizza",
                        title: "Open Web URL"
                    }/*, {
                     type: "postback",
                     title: "Call Postback",
                     payload: "Payload for first bubble"
                     }*/]
                }]
            }
        }
    };

    try {
        var data = JSONbig.parse(req.body);
        //console.log(data);
        switch(data.result.action){
            case 'show_prod':
                console.log('pizza');
                switch(data.result.parameters.pizza_type){
                    case 'Margherita':
                        generic_message.attachment.payload.elements[0].title = data.result.parameters.pizza_type;
                        generic_message.attachment.payload.elements[0].image_url = "http://www.cbc.ca/inthekitchen/assets_c/2012/11/MargheritaPizza21-thumb-596x350-247022.jpg";
                }
                break;
            case 'show_weather':
                console.log('weather');

                if(isDefined(data.result.parameters['geo-city']) == true){
                    var city = data.result.parameters['geo-city'];
                    var base_url = "https://query.yahooapis.com/v1/public/yql?" + "q=select+%2A+from+weather.forecast+where+woeid+in+%28select+woeid+from+geo.places%281%29+where+text%3D%27"+city+"%27%29" + "&format=json";
                    weather_query = true;
                    request({
                        url: base_url,
                        method: 'GET'
                    }, function(error, response, body){
                        if(error) {
                            console.log(error);
                            return res.status(400).json({
                                status: "error",
                                error: err
                            });
                        } else {
                            var query = JSON.parse(body).query;
                            var results = query.results;
                            var channel = results.channel;
                            var item = channel.item;
                            var location = channel.location;
                            var units = channel.units;
                            var condition = item.condition;
                            //console.log(channel)
                            var string = "Today in " + location.city + ": " + condition.text + ", the temperature is " + condition.temp + " " + units.temperature
                            generic_message.attachment.payload.elements[0].title = 'Weather in' + city;
                            generic_message.attachment.payload.elements[0].subtitle = string;
                            generic_message.attachment.payload.elements[0].item_url = channel.link;
                            generic_message.attachment.payload.elements[0].image_url = channel.image.url;
                            generic_message.attachment.payload.elements[0].buttons[0].url = channel.link;
                            return res.status(200).json({
                                data: {
                                    facebook: generic_message
                                }
                            });
                        }
                    });
                } else {
                    return res.status(200).json({
                        data: {
                            facebook: {text: 'no city'}
                        }
                    })}
                weather_query = false;
                break;
        }
        console.log(weather_query);
        if (weather_query != true){
            return res.status(200).json({
                data: {
                    facebook: generic_message
                }
            })
        } else {}
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }
});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();
<|endoftext|>


<commit_before>import _ from "underscore";
import Dispatcher from "dispatchers/Dispatcher";
import Store from "stores/Store";
import ClientCredential from "models/ClientCredential";

import NotificationController from "controllers/NotificationController";

/**
 * Author note: (@lenards)
 *
 * This is a mimic of `models/Profile` & `stores/ProfileStore`.
 *
 * Why? Because the functionality of exporting the credentials associated
 * with an "Identity" (aka AtmosphereUser + Provider) did not appear to
 * match the examples of :store -> :collection -> :model that commonly
 * exists within the application.
 *
 * Here we introduce a "jargon-y" term, Client Credential as a manner of
 * capturing what this _thing_ is that we're trying to acquire/export/
 * grab. It is likely not going to be understood by community members,
 * but has been selected to indicate that this "credential" has *value*
 * when used with :cloud: client tools and command-line interfaces.
 *
 * Please approach the below code with any amount of healthy sketicism
 * you can offer - you will likely be rewarded for it
 */

let _isFetching = false;

/**
 * Cache credentials, like a singleton of sorts, by `identity_uuid`
 */
let _creds = {};

let fetchCredential = function(identityId) {
    if (!_isFetching) {
        // the Backbone.Model will need an `identity_uuid` when
        // fetching the associated information, so pass it on
        // initialize.
        let cred = new ClientCredential({
            "identity_uuid": identityId
        });

        _isFetching = true;

        cred.fetch().then(function() {
            _isFetching = false;
            _creds[identityId] = cred;
            ClientCredentialStore.emitChange();
        }).fail(function(result) {
            let errorMsg = `There was an error exporting credentials. ${result.responseText}`;

            NotificationController.error(
                null,
                errorMsg,
                {
                    "positionClass": "toast-top-full-width",
                    "timeOut": "0",
                    "extendedTimeOut": "0"
                }
            );
        });
    }
}

//
// Define Store
//
let ClientCredentialStore = {
    getForIdentity: function(identityId) {
        if (!_creds[identityId]) {
            fetchCredential(identityId);
        }
        return _creds[identityId];
    }
};

Dispatcher.register(function(payload) {
    var action = payload.action;

    switch (action.actionType) {
// TODO
// - eventually a "reset" of credentials will be needed
//   this will require the `identity-uuid` to reset;
//   other details will be forthcoming
//
//        case ClientCredentialConstants.RESET_CREDENTIAL:
//            update(action.name, action.identity);
//            break;

        default:
            return true;
    }

    ClientCredentialStore.emitChange();

    return true;
});

_.extend(ClientCredentialStore, Store);

export default ClientCredentialStore;
<commit_msg>Avoid un-reachable code within store<commit_after>import _ from "underscore";
import Dispatcher from "dispatchers/Dispatcher";
import Store from "stores/Store";
import ClientCredential from "models/ClientCredential";

import NotificationController from "controllers/NotificationController";

/**
 * Author note: (@lenards)
 *
 * This is a mimic of `models/Profile` & `stores/ProfileStore`.
 *
 * Why? Because the functionality of exporting the credentials associated
 * with an "Identity" (aka AtmosphereUser + Provider) did not appear to
 * match the examples of :store -> :collection -> :model that commonly
 * exists within the application.
 *
 * Here we introduce a "jargon-y" term, Client Credential as a manner of
 * capturing what this _thing_ is that we're trying to acquire/export/
 * grab. It is likely not going to be understood by community members,
 * but has been selected to indicate that this "credential" has *value*
 * when used with :cloud: client tools and command-line interfaces.
 *
 * Please approach the below code with any amount of healthy sketicism
 * you can offer - you will likely be rewarded for it
 */

let _isFetching = false;

/**
 * Cache credentials, like a singleton of sorts, by `identity_uuid`
 */
let _creds = {};

let fetchCredential = function(identityId) {
    if (!_isFetching) {
        // the Backbone.Model will need an `identity_uuid` when
        // fetching the associated information, so pass it on
        // initialize.
        let cred = new ClientCredential({
            "identity_uuid": identityId
        });

        _isFetching = true;

        cred.fetch().then(function() {
            _isFetching = false;
            _creds[identityId] = cred;
            ClientCredentialStore.emitChange();
        }).fail(function(result) {
            let errorMsg = `There was an error exporting credentials. ${result.responseText}`;

            NotificationController.error(
                null,
                errorMsg,
                {
                    "positionClass": "toast-top-full-width",
                    "timeOut": "0",
                    "extendedTimeOut": "0"
                }
            );
        });
    }
}

//
// Define Store
//
let ClientCredentialStore = {
    getForIdentity: function(identityId) {
        if (!_creds[identityId]) {
            fetchCredential(identityId);
        }
        return _creds[identityId];
    }
};

Dispatcher.register(function(payload) {
    var action = payload.action;

    switch (action.actionType) {
// TODO
// - eventually a "reset" of credentials will be needed
//   this will require the `identity-uuid` to reset;
//   other details will be forthcoming
//
//        case ClientCredentialConstants.RESET_CREDENTIAL:
//            update(action.name, action.identity);
//            break;

        default:
            // do nothing; for now
            break;
    }

    ClientCredentialStore.emitChange();

    return true;
});

_.extend(ClientCredentialStore, Store);

export default ClientCredentialStore;
<|endoftext|>


<commit_before>dojo.provide("dijit._Templated");

dojo.require("dijit._Widget");
dojo.require("dojo.string");
dojo.require("dojo.parser");

dojo.declare("dijit._Templated",
	null,
	{
		//	summary:
		//		Mixin for widgets that are instantiated from a template
		//
		// templateNode: DomNode
		//		a node that represents the widget template. Pre-empts both templateString and templatePath.
		templateNode: null,

		// templateString: String
		//		a string that represents the widget template. Pre-empts the
		//		templatePath. In builds that have their strings "interned", the
		//		templatePath is converted to an inline templateString, thereby
		//		preventing a synchronous network call.
		templateString: null,

		// templatePath: String
		//	Path to template (HTML file) for this widget relative to dojo.baseUrl
		templatePath: null,

		// widgetsInTemplate: Boolean
		//		should we parse the template to find widgets that might be
		//		declared in markup inside it? false by default.
		widgetsInTemplate: false,

		// skipNodeCache: Boolean
		//		if using a cached widget template node poses issues for a
		//		particular widget class, it can set this property to ensure
		//		that its template is always re-built from a string
		_skipNodeCache: false,

		_stringRepl: function(tmpl){
			var className = this.declaredClass, _this = this;
			// Cache contains a string because we need to do property replacement
			// do the property replacement
			return dojo.string.substitute(tmpl, this, function(value, key){
				if(key.charAt(0) == '!'){ value = _this[key.substr(1)]; }
				if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
				if(value == null){ return ""; }

				// Substitution keys beginning with ! will skip the transform step,
				// in case a user wishes to insert unescaped markup, e.g. ${!foo}
				return key.charAt(0) == "!" ? value :
					// Safer substitution, see heading "Attribute values" in
					// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
					value.toString().replace(/"/g,"""); //TODO: add &amp? use encodeXML method?
			}, this);
		},

		// method over-ride
		buildRendering: function(){
			// summary:
			//		Construct the UI for this widget from a template, setting this.domNode.

			// Lookup cached version of template, and download to cache if it
			// isn't there already.  Returns either a DomNode or a string, depending on
			// whether or not the template contains ${foo} replacement parameters.
			var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);

			var node;
			if(dojo.isString(cached)){
				node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
			}else{
				// if it's a node, all we have to do is clone it
				node = cached.cloneNode(true);
			}

			this.domNode = node;

			// recurse through the node, looking for, and attaching to, our
			// attachment points and events, which should be defined on the template node.
			this._attachTemplateNodes(node);

			var source = this.srcNodeRef;
			if(source && source.parentNode){
				source.parentNode.replaceChild(node, source);
			}

			if(this.widgetsInTemplate){
				var cw = (this._supportingWidgets = dojo.parser.parse(node));
				this._attachTemplateNodes(cw, function(n,p){
					return n[p];
				});
			}

			this._fillContent(source);
		},

		_fillContent: function(/*DomNode*/ source){
			// summary:
			//		relocate source contents to templated container node
			//		this.containerNode must be able to receive children, or exceptions will be thrown
			var dest = this.containerNode;
			if(source && dest){
				while(source.hasChildNodes()){
					dest.appendChild(source.firstChild);
				}
			}
		},

		_attachTemplateNodes: function(rootNode, getAttrFunc){
			// summary: Iterate through the template and attach functions and nodes accordingly.
			// description:
			//		Map widget properties and functions to the handlers specified in
			//		the dom node and it's descendants. This function iterates over all
			//		nodes and looks for these properties:
			//			* dojoAttachPoint
			//			* dojoAttachEvent
			//			* waiRole
			//			* waiState
			// rootNode: DomNode|Array[Widgets]
			//		the node to search for properties. All children will be searched.
			// getAttrFunc: function?
			//		a function which will be used to obtain property for a given
			//		DomNode/Widget

			getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };

			var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
			var x = dojo.isArray(rootNode) ? 0 : -1;
			var attrs = {};
			for(; x<nodes.length; x++){
				var baseNode = (x == -1) ? rootNode : nodes[x];
				if(this.widgetsInTemplate && getAttrFunc(baseNode, "dojoType")){
					continue;
				}
				// Process dojoAttachPoint
				var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
				if(attachPoint){
					var point, points = attachPoint.split(/\s*,\s*/);
					while((point = points.shift())){
						if(dojo.isArray(this[point])){
							this[point].push(baseNode);
						}else{
							this[point]=baseNode;
						}
					}
				}

				// Process dojoAttachEvent
				var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
				if(attachEvent){
					// NOTE: we want to support attributes that have the form
					// "domEvent: nativeEvent; ..."
					var event, events = attachEvent.split(/\s*,\s*/);
					var trim = dojo.trim;
					while((event = events.shift())){
						if(event){
							var thisFunc = null;
							if(event.indexOf(":") != -1){
								// oh, if only JS had tuple assignment
								var funcNameArr = event.split(":");
								event = trim(funcNameArr[0]);
								thisFunc = trim(funcNameArr[1]);
							}else{
								event = trim(event);
							}
							if(!thisFunc){
								thisFunc = event;
							}
							this.connect(baseNode, event, thisFunc);
						}
					}
				}

				// waiRole, waiState
				var role = getAttrFunc(baseNode, "waiRole");
				if(role){
					dijit.setWaiRole(baseNode, role);
				}
				var values = getAttrFunc(baseNode, "waiState");
				if(values){
					dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
						if(stateValue.indexOf('-') != -1){
							var pair = stateValue.split('-');
							dijit.setWaiState(baseNode, pair[0], pair[1]);
						}
					});
				}
			}
		}
	}
);

// key is either templatePath or templateString; object is either string or DOM tree
dijit._Templated._templateCache = {};

dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
	// summary:
	//		Static method to get a template based on the templatePath or
	//		templateString key
	// templatePath: String
	//		The URL to get the template from. dojo.uri.Uri is often passed as well.
	// templateString: String?
	//		a string to use in lieu of fetching the template from a URL. Takes precedence
	//		over templatePath
	// Returns: Mixed
	//	Either string (if there are ${} variables that need to be replaced) or just
	//	a DOM tree (if the node can be cloned directly)

	// is it already cached?
	var tmplts = dijit._Templated._templateCache;
	var key = templateString || templatePath;
	var cached = tmplts[key];
	if(cached){
		if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
			// string or node of the same document
			return cached;
		}
		// destroy the old cached node of a different document
		dojo._destroyElement(cached);
	}

	// If necessary, load template string from template path
	if(!templateString){
		templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
	}

	templateString = dojo.string.trim(templateString);

	if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
		// there are variables in the template so all we can do is cache the string
		return (tmplts[key] = templateString); //String
	}else{
		// there are no variables in the template so we can cache the DOM tree
		return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
	}
};

dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
	// summary:
	//		Strips <?xml ...?> declarations so that external SVG and XML
	// 		documents can be added to a document without worry. Also, if the string
	//		is an HTML document, only the part inside the body tag is returned.
	if(tString){
		tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
		var matches = tString.match(/]*>\s*([\s\S]+)\s*<\/body>/im);
		if(matches){
			tString = matches[1];
		}
	}else{
		tString = "";
	}
	return tString; //String
};

if(dojo.isIE){
	dojo.addOnWindowUnload(function(){
		var cache = dijit._Templated._templateCache;
		for(var key in cache){
			var value = cache[key];
			if(!isNaN(value.nodeType)){ // isNode equivalent
				dojo._destroyElement(value);
			}
			delete cache[key];
		}
	});
}

(function(){
	var tagMap = {
		cell: {re: /^<t[dh][\s\r\n>]/i, pre: "

", post: "

"},
		row: {re: /^
]/i, pre: "

", post: "

"},
		section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "
", post: "
"}
	};

	// dummy container node used temporarily to hold nodes being created
	var tn;

	dijit._Templated._createNodesFromText = function(/*String*/text){
		// summary:
		//	Attempts to create a set of nodes based on the structure of the passed text.

		if(tn && tn.ownerDocument != dojo.doc){
			// destroy dummy container of a different document
			dojo._destroyElement(tn);
			tn = undefined;
		}
		if(!tn){
			tn = dojo.doc.createElement("div");
			tn.style.display="none";
			dojo.body().appendChild(tn);
		}
		var tableType = "none";
		var rtext = text.replace(/^\s+/, "");
		for(var type in tagMap){
			var map = tagMap[type];
			if(map.re.test(rtext)){
				tableType = type;
				text = map.pre + text + map.post;
				break;
			}
		}

		tn.innerHTML = text;
		if(tn.normalize){
			tn.normalize();
		}

		var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
		var _parent = (typeof tag != "undefined") ?
						tn.getElementsByTagName(tag)[0] :
						tn;

		var nodes = [];
		while(_parent.firstChild){
			nodes.push(_parent.removeChild(_parent.firstChild));
		}
		tn.innerHTML="";
		return nodes;	//	Array
	}
})();

// These arguments can be specified for widgets which are used in templates.
// Since any widget can be specified as sub widgets in template, mix it
// into the base widget class.  (This is a hack, but it's effective.)
dojo.extend(dijit._Widget,{
	dojoAttachEvent: "",
	dojoAttachPoint: "",
	waiRole: "",
	waiState:""
})
<commit_msg>Fixes #7993 !strict: allow nested attribute access even with !, ex: ${!i18n.foo}<commit_after>dojo.provide("dijit._Templated");

dojo.require("dijit._Widget");
dojo.require("dojo.string");
dojo.require("dojo.parser");

dojo.declare("dijit._Templated",
	null,
	{
		//	summary:
		//		Mixin for widgets that are instantiated from a template
		//
		// templateNode: DomNode
		//		a node that represents the widget template. Pre-empts both templateString and templatePath.
		templateNode: null,

		// templateString: String
		//		a string that represents the widget template. Pre-empts the
		//		templatePath. In builds that have their strings "interned", the
		//		templatePath is converted to an inline templateString, thereby
		//		preventing a synchronous network call.
		templateString: null,

		// templatePath: String
		//	Path to template (HTML file) for this widget relative to dojo.baseUrl
		templatePath: null,

		// widgetsInTemplate: Boolean
		//		should we parse the template to find widgets that might be
		//		declared in markup inside it? false by default.
		widgetsInTemplate: false,

		// skipNodeCache: Boolean
		//		if using a cached widget template node poses issues for a
		//		particular widget class, it can set this property to ensure
		//		that its template is always re-built from a string
		_skipNodeCache: false,

		_stringRepl: function(tmpl){
			var className = this.declaredClass, _this = this;
			// Cache contains a string because we need to do property replacement
			// do the property replacement
			return dojo.string.substitute(tmpl, this, function(value, key){
				if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), _this); }
				if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
				if(value == null){ return ""; }

				// Substitution keys beginning with ! will skip the transform step,
				// in case a user wishes to insert unescaped markup, e.g. ${!foo}
				return key.charAt(0) == "!" ? value :
					// Safer substitution, see heading "Attribute values" in
					// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
					value.toString().replace(/"/g,"""); //TODO: add &amp? use encodeXML method?
			}, this);
		},

		// method over-ride
		buildRendering: function(){
			// summary:
			//		Construct the UI for this widget from a template, setting this.domNode.

			// Lookup cached version of template, and download to cache if it
			// isn't there already.  Returns either a DomNode or a string, depending on
			// whether or not the template contains ${foo} replacement parameters.
			var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);

			var node;
			if(dojo.isString(cached)){
				node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
			}else{
				// if it's a node, all we have to do is clone it
				node = cached.cloneNode(true);
			}

			this.domNode = node;

			// recurse through the node, looking for, and attaching to, our
			// attachment points and events, which should be defined on the template node.
			this._attachTemplateNodes(node);

			var source = this.srcNodeRef;
			if(source && source.parentNode){
				source.parentNode.replaceChild(node, source);
			}

			if(this.widgetsInTemplate){
				var cw = (this._supportingWidgets = dojo.parser.parse(node));
				this._attachTemplateNodes(cw, function(n,p){
					return n[p];
				});
			}

			this._fillContent(source);
		},

		_fillContent: function(/*DomNode*/ source){
			// summary:
			//		relocate source contents to templated container node
			//		this.containerNode must be able to receive children, or exceptions will be thrown
			var dest = this.containerNode;
			if(source && dest){
				while(source.hasChildNodes()){
					dest.appendChild(source.firstChild);
				}
			}
		},

		_attachTemplateNodes: function(rootNode, getAttrFunc){
			// summary: Iterate through the template and attach functions and nodes accordingly.
			// description:
			//		Map widget properties and functions to the handlers specified in
			//		the dom node and it's descendants. This function iterates over all
			//		nodes and looks for these properties:
			//			* dojoAttachPoint
			//			* dojoAttachEvent
			//			* waiRole
			//			* waiState
			// rootNode: DomNode|Array[Widgets]
			//		the node to search for properties. All children will be searched.
			// getAttrFunc: function?
			//		a function which will be used to obtain property for a given
			//		DomNode/Widget

			getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };

			var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
			var x = dojo.isArray(rootNode) ? 0 : -1;
			var attrs = {};
			for(; x<nodes.length; x++){
				var baseNode = (x == -1) ? rootNode : nodes[x];
				if(this.widgetsInTemplate && getAttrFunc(baseNode, "dojoType")){
					continue;
				}
				// Process dojoAttachPoint
				var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
				if(attachPoint){
					var point, points = attachPoint.split(/\s*,\s*/);
					while((point = points.shift())){
						if(dojo.isArray(this[point])){
							this[point].push(baseNode);
						}else{
							this[point]=baseNode;
						}
					}
				}

				// Process dojoAttachEvent
				var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
				if(attachEvent){
					// NOTE: we want to support attributes that have the form
					// "domEvent: nativeEvent; ..."
					var event, events = attachEvent.split(/\s*,\s*/);
					var trim = dojo.trim;
					while((event = events.shift())){
						if(event){
							var thisFunc = null;
							if(event.indexOf(":") != -1){
								// oh, if only JS had tuple assignment
								var funcNameArr = event.split(":");
								event = trim(funcNameArr[0]);
								thisFunc = trim(funcNameArr[1]);
							}else{
								event = trim(event);
							}
							if(!thisFunc){
								thisFunc = event;
							}
							this.connect(baseNode, event, thisFunc);
						}
					}
				}

				// waiRole, waiState
				var role = getAttrFunc(baseNode, "waiRole");
				if(role){
					dijit.setWaiRole(baseNode, role);
				}
				var values = getAttrFunc(baseNode, "waiState");
				if(values){
					dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
						if(stateValue.indexOf('-') != -1){
							var pair = stateValue.split('-');
							dijit.setWaiState(baseNode, pair[0], pair[1]);
						}
					});
				}
			}
		}
	}
);

// key is either templatePath or templateString; object is either string or DOM tree
dijit._Templated._templateCache = {};

dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
	// summary:
	//		Static method to get a template based on the templatePath or
	//		templateString key
	// templatePath: String
	//		The URL to get the template from. dojo.uri.Uri is often passed as well.
	// templateString: String?
	//		a string to use in lieu of fetching the template from a URL. Takes precedence
	//		over templatePath
	// Returns: Mixed
	//	Either string (if there are ${} variables that need to be replaced) or just
	//	a DOM tree (if the node can be cloned directly)

	// is it already cached?
	var tmplts = dijit._Templated._templateCache;
	var key = templateString || templatePath;
	var cached = tmplts[key];
	if(cached){
		if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
			// string or node of the same document
			return cached;
		}
		// destroy the old cached node of a different document
		dojo._destroyElement(cached);
	}

	// If necessary, load template string from template path
	if(!templateString){
		templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
	}

	templateString = dojo.string.trim(templateString);

	if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
		// there are variables in the template so all we can do is cache the string
		return (tmplts[key] = templateString); //String
	}else{
		// there are no variables in the template so we can cache the DOM tree
		return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
	}
};

dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
	// summary:
	//		Strips <?xml ...?> declarations so that external SVG and XML
	// 		documents can be added to a document without worry. Also, if the string
	//		is an HTML document, only the part inside the body tag is returned.
	if(tString){
		tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
		var matches = tString.match(/]*>\s*([\s\S]+)\s*<\/body>/im);
		if(matches){
			tString = matches[1];
		}
	}else{
		tString = "";
	}
	return tString; //String
};

if(dojo.isIE){
	dojo.addOnWindowUnload(function(){
		var cache = dijit._Templated._templateCache;
		for(var key in cache){
			var value = cache[key];
			if(!isNaN(value.nodeType)){ // isNode equivalent
				dojo._destroyElement(value);
			}
			delete cache[key];
		}
	});
}

(function(){
	var tagMap = {
		cell: {re: /^<t[dh][\s\r\n>]/i, pre: "

", post: "

"},
		row: {re: /^
]/i, pre: "

", post: "

"},
		section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "
", post: "
"}
	};

	// dummy container node used temporarily to hold nodes being created
	var tn;

	dijit._Templated._createNodesFromText = function(/*String*/text){
		// summary:
		//	Attempts to create a set of nodes based on the structure of the passed text.

		if(tn && tn.ownerDocument != dojo.doc){
			// destroy dummy container of a different document
			dojo._destroyElement(tn);
			tn = undefined;
		}
		if(!tn){
			tn = dojo.doc.createElement("div");
			tn.style.display="none";
			dojo.body().appendChild(tn);
		}
		var tableType = "none";
		var rtext = text.replace(/^\s+/, "");
		for(var type in tagMap){
			var map = tagMap[type];
			if(map.re.test(rtext)){
				tableType = type;
				text = map.pre + text + map.post;
				break;
			}
		}

		tn.innerHTML = text;
		if(tn.normalize){
			tn.normalize();
		}

		var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
		var _parent = (typeof tag != "undefined") ?
						tn.getElementsByTagName(tag)[0] :
						tn;

		var nodes = [];
		while(_parent.firstChild){
			nodes.push(_parent.removeChild(_parent.firstChild));
		}
		tn.innerHTML="";
		return nodes;	//	Array
	}
})();

// These arguments can be specified for widgets which are used in templates.
// Since any widget can be specified as sub widgets in template, mix it
// into the base widget class.  (This is a hack, but it's effective.)
dojo.extend(dijit._Widget,{
	dojoAttachEvent: "",
	dojoAttachPoint: "",
	waiRole: "",
	waiState:""
})
<|endoftext|>


<commit_before>var hmac = require('crypto-browserify/create-hmac')
var Buffer = require('buffer/').Buffer
var through = require('through2')
var utils = require('utils')

module.exports = function Bricoleur (db, user, config) { // >>>>>>>>>>>>>>>>>>>

  localStorage.library = JSON.stringify(config.library)

  if (config.rendering) var render = require(config.rendering)

  var s = through.obj(function interface (d, enc, next) {
    if (!d.key) { next(); return false }

    var path = d.key.split(':')[0]

    if (path === 'put' || path === 'library') {
      if (typeof d.value === 'string') {
        d.type = (!d.value.match('>')) ? 'module' : 'pipe'
        api.put(d)
      } else if (d.value instanceof Array) {
        d.value.forEach(function (item) {
          var cmd = {}
          cmd.type = (!item.match('>')) ? 'module' : 'pipe'
          cmd.value = (cmd.type === 'pipe') ? item.split('>') : item
          api.put(cmd)
        })
      }
    } else if (api[path]) {
      api[path](d, handleOutput)
    }

    function handleOutput (e, res) {
      if (e) handleError(e)
      if (res) {
        console.log(res)
        if (res.to) { res.from = id; s.push(res) }
      }
    }

    next()
  })

  db.liveStream({reverse : true})
    .on('data', sync)

  function sync (d) {
    var path = d.key.toString().split(':')[0]
  }

  // BRICOLEUR API >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  var id = utils.UID('brico')
  var canvas = { index : {} }
  canvas.index[id+':brico'] = {id : id}
  s.id = id
  canvas[id] = s

  window.cvs = canvas

  var api = {}

  api.get = function (d, cb) {
    db.get(d.value, function returnResult (e, res) {
      if (e) cb(e, null)
      if (!e) {
        var result = (d.from) ? {to:d.from, value:res} : {value:res}
        cb(null, result)
      }
    })
  }

  api.data = function (d, cb) {
    db.put(d.value.key, d.value.data, cb)
  }

  api.canvas = function (d, cb) {
    var type = d.key.split(':')[1]
    if (type === 'save') {
      var idx = canvas.index
      var safeIdx = {}
      for (item in idx) {
        if (!item.match(config.editor) && !item.match('brico')) {
          safeIdx[item] = item.split(':')[0]
        }
      }
      db.put('canvas:'+d.value, JSON.stringify(safeIdx), cb)
    } else if (type === 'open') {
      db.get('canvas:'+d.value, function (e, val) {
        if (e) handleError(e)
        if (val) console.log(val)

        // clear current canvas then draw!

        // for (item in idx) {

        // }
      })
    }
  }

  api.put = function (d, cb) {
    if (d.type === 'pipe') {
      var conn = d.value

      var a = canvas[utils.search(canvas.index, conn[0]).id]
      var b = canvas[utils.search(canvas.index, conn[1]).id]

      var hash = utils.UID(conn)

      if (!a.pipe || !b.pipe) cb(new Error('unpipeable!'), null)

      a.pipe(b)

      canvas.index[hash+':'+conn[0]+'|'+conn[1]] = [a.id, b.id] // call db

      var res = (!d.from) ? {code: 200} : {code:200, to: d.from}

      if (cb) cb(null, res)

    } else if (d.type === 'module') {
      var nameOrPkg = d.value
      var hash

      if (nameOrPkg.split(':') > 1) {
        hash = nameOrPkg.split(':')[0]
        nameOrPkg = nameOrPkg.split(':')[1]
      }

      var pkg = (typeof nameOrPkg !== 'object') // mod
        ? utils.search(config.library.master, nameOrPkg)
        : nameOrPkg

      if (!pkg) handleError(e)

      if (!hash && pkg.nimoy.data) {
        var val = JSON.stringify(pkg.nimoy.data)
        db.put('module:'+hash, val, function (e) {
          if (e) handleError(e)
        })
      }

      function put (e, data) {
        if (e) handleError(e)
        if (data) pkg.data = data
        pkg.id = hash
        canvas[hash] = render(pkg, hash)
        canvas.index[hash+':'+pkg.name] = pkg
        var res = (!d.from) ? {code: 200} : {code:200, to: d.from}
        if (cb) cb(null, res)
      }

      if (hash) {
        db.get('module:'+hash, put)
      }

      if (!hash) { hash = utils.UID(pkg.name); put() }
    }
  }

  api.del = function (d, cb) {
    if (d.type === 'pipe') { // parse input
      var conn = d.value
      var a = canvas[conn][0]
      var b = canvas[conn][1]
      a.unpipe(b)
      delete canvas.index[hash]
    } else if (d.type === 'module') { // mod
      var mod = search(canvas, hash)
      if (mod) {
        document.body.removeChild(document.getElementById(hash))
        delete mod
      }
      delete canvas.index[hash]
    }
  }

  api.auth = function (d, cb) {
    if (!d.session && d.target.id === 'loginForm') { // is dom event
      d.preventDefault()
      var img = new Buffer(config.uImg).toString()
      var pass = hmac('sha256', img).update(d.target[0].value).digest('hex')
      db.auth({name: 'edit', pass: pass}, handleAuth)
    } else if (d.session) {
      db.auth({name: d.name, session: d.session}, handleAuth)
    }

    function handleAuth (e, res) {
      if (e) {
        if (!document.querySelector('.login')) {
          document.body.appendChild(login)
          login.querySelector('input').focus()
        }
        handleError(e)
      } else if (!e) {
        sessionStorage[res.name] = res.token
        if (document.querySelector('.login')) document.body.removeChild(login)
        s.write({
          key:'put',
          value: [config.editor,config.editor+'>brico','brico>'+config.editor]
        })
        // load page!
        return false
      }
    }
  }

  api.deauth = function (d, cb) {
    db.deauth(function () {
      // remove editor!
      delete sessionStorage[user]
      var path = (!getPath()) ? home : home + getPath()
    })
  }
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  // login ui >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  login = document.createElement('div')
  login.className = 'login'
  login.innerHTML = '<form id="loginForm">'
    + '<input type="password" placeholder="enter password" />'
    + '<input type="submit" value="edit" style="display:none;" />'
    + '</form>'

  login.querySelector('#loginForm')
    .addEventListener('submit', api.auth, false)
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  // boot >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  if (sessionStorage[user])
    api.auth({ name : user, session : sessionStorage[user] })

  if (!sessionStorage[user] && user !== 'default') {
    document.body.appendChild(login)
    login.querySelector('input').focus()
  }
  // end boot <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  return s

} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

function handleError (e) {
  console.error(e)
}
<commit_msg>fix api.put to set hash correctly<commit_after>var hmac = require('crypto-browserify/create-hmac')
var Buffer = require('buffer/').Buffer
var through = require('through2')
var utils = require('utils')

module.exports = function Bricoleur (db, user, config) { // >>>>>>>>>>>>>>>>>>>

  localStorage.library = JSON.stringify(config.library)

  if (config.rendering) var render = require(config.rendering)

  var s = through.obj(function interface (d, enc, next) {
    if (!d.key) { next(); return false }

    var path = d.key.split(':')[0]

    if (path === 'put' || path === 'library') {
      if (typeof d.value === 'string') {
        d.type = (!d.value.match('>')) ? 'module' : 'pipe'
        api.put(d)
      } else if (d.value instanceof Array) {
        d.value.forEach(function (item) {
          var cmd = {}
          cmd.type = (!item.match('>')) ? 'module' : 'pipe'
          cmd.value = (cmd.type === 'pipe') ? item.split('>') : item
          api.put(cmd)
        })
      }
    } else if (api[path]) {
      api[path](d, handleOutput)
    }

    function handleOutput (e, res) {
      if (e) handleError(e)
      if (res) {
        console.log(res)
        if (res.to) { res.from = id; s.push(res) }
      }
    }

    next()
  })

  db.liveStream({reverse : true})
    .on('data', sync)

  function sync (d) {
    var path = d.key.toString().split(':')[0]
  }

  // BRICOLEUR API >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  var id = utils.UID('brico')
  var canvas = { index : {} }
  canvas.index[id+':brico'] = {id : id}
  s.id = id
  canvas[id] = s

  window.cvs = canvas

  var api = {}

  api.get = function (d, cb) {
    db.get(d.value, function returnResult (e, res) {
      if (e) cb(e, null)
      if (!e) {
        var result = (d.from) ? {to:d.from, value:res} : {value:res}
        cb(null, result)
      }
    })
  }

  api.data = function (d, cb) {
    db.put(d.value.key, d.value.data, cb)
  }

  api.canvas = function (d, cb) {
    var type = d.key.split(':')[1]
    if (type === 'save') {
      var idx = canvas.index
      var safeIdx = {}
      for (item in idx) {
        if (!item.match(config.editor) && !item.match('brico')) {
          safeIdx[item] = item.split(':')[0]
        }
      }
      db.put('canvas:'+d.value, JSON.stringify(safeIdx), cb)
    } else if (type === 'open') {
      db.get('canvas:'+d.value, function (e, val) {
        if (e) handleError(e)
        if (val) console.log(val)

        // clear current canvas then draw!

        // for (item in idx) {

        // }
      })
    }
  }

  api.put = function (d, cb) {
    if (d.type === 'pipe') {
      var conn = d.value

      var a = canvas[utils.search(canvas.index, conn[0]).id]
      var b = canvas[utils.search(canvas.index, conn[1]).id]

      var hash = utils.UID(conn)

      if (!a.pipe || !b.pipe) cb(new Error('unpipeable!'), null)

      a.pipe(b)

      canvas.index[hash+':'+conn[0]+'|'+conn[1]] = [a.id, b.id] // call db

      var res = (!d.from) ? {code: 200} : {code:200, to: d.from}

      if (cb) cb(null, res)

    } else if (d.type === 'module') {
      var nameOrPkg = d.value
      var hash

      if (nameOrPkg.split(':') > 1) {
        hash = nameOrPkg.split(':')[0]
        nameOrPkg = nameOrPkg.split(':')[1]
      }

      var pkg = (typeof nameOrPkg !== 'object') // mod
        ? utils.search(config.library.master, nameOrPkg)
        : nameOrPkg

      if (!pkg) handleError(e)

      function put (e, data) {
        if (e) handleError(e)
        if (data) pkg.data = data
        pkg.id = hash
        canvas[hash] = render(pkg, hash)
        canvas.index[hash+':'+pkg.name] = pkg
        var res = (!d.from) ? {code: 200} : {code:200, to: d.from}
        if (cb) cb(null, res)
      }

      if (hash) db.get('module:'+hash, put)

      if (!hash) {
        hash = utils.UID(pkg.name)
        if (pkg.nimoy.data) {
          var val = JSON.stringify(pkg.nimoy.data)
          db.put('module:'+hash, val, put)
        } else put()
      }
    }
  }

  api.del = function (d, cb) {
    if (d.type === 'pipe') { // parse input
      var conn = d.value
      var a = canvas[conn][0]
      var b = canvas[conn][1]
      a.unpipe(b)
      delete canvas.index[hash]
    } else if (d.type === 'module') { // mod
      var mod = search(canvas, hash)
      if (mod) {
        document.body.removeChild(document.getElementById(hash))
        delete mod
      }
      delete canvas.index[hash]
    }
  }

  api.auth = function (d, cb) {
    if (!d.session && d.target.id === 'loginForm') { // is dom event
      d.preventDefault()
      var img = new Buffer(config.uImg).toString()
      var pass = hmac('sha256', img).update(d.target[0].value).digest('hex')
      db.auth({name: 'edit', pass: pass}, handleAuth)
    } else if (d.session) {
      db.auth({name: d.name, session: d.session}, handleAuth)
    }

    function handleAuth (e, res) {
      if (e) {
        if (!document.querySelector('.login')) {
          document.body.appendChild(login)
          login.querySelector('input').focus()
        }
        handleError(e)
      } else if (!e) {
        sessionStorage[res.name] = res.token
        if (document.querySelector('.login')) document.body.removeChild(login)
        s.write({
          key:'put',
          value: [config.editor,config.editor+'>brico','brico>'+config.editor]
        })
        // load page!
        return false
      }
    }
  }

  api.deauth = function (d, cb) {
    db.deauth(function () {
      // remove editor!
      delete sessionStorage[user]
      var path = (!getPath()) ? home : home + getPath()
    })
  }
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  // login ui >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  login = document.createElement('div')
  login.className = 'login'
  login.innerHTML = '<form id="loginForm">'
    + '<input type="password" placeholder="enter password" />'
    + '<input type="submit" value="edit" style="display:none;" />'
    + '</form>'

  login.querySelector('#loginForm')
    .addEventListener('submit', api.auth, false)
  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  // boot >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  if (sessionStorage[user])
    api.auth({ name : user, session : sessionStorage[user] })

  if (!sessionStorage[user] && user !== 'default') {
    document.body.appendChild(login)
    login.querySelector('input').focus()
  }
  // end boot <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  return s

} // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

function handleError (e) {
  console.error(e)
}
<|endoftext|>


<commit_before><commit_msg>Init Angular Module Auth App<commit_after>angular // add module dependencies and configure it
  .module('authApp', ['auth0', 'angular-storage', 'angular-jwt', 'ngMaterial', 'ui.router'])
  .config(function($locationProvider, jwtOptionsProvider, $provide, authProvider, $urlRouterProvider, $stateProvider, $httpProvider, jwtInterceptorProvider) {

    authProvider.init({ // for auth0 lock (login)
      domain: 'sparrow-angluarjs.auth0.com',
      clientID: 'NJ9hqwCSD6bSnTQ1PlBNNWmxP4aYRFPC',
      loginUrl: '/#/home'
    });

    $stateProvider
      .state('home', {
        url:'/home',
        templateUrl: 'components/home/home-tpl.html'
      })
      .state('profile', {
        url: '/profile',
        templateUrl: 'components/profile/profile-tpl.html',
        controller: 'profileController as user'
      });

    $urlRouterProvider.otherwise('/home');
    // remove ! from hash /#!/
    $locationProvider.hashPrefix('');
    // have jwt come with all requests to endpoints
    jwtInterceptorProvider.tokenGetter = function(store) {
      // this will go to local storage and return that token and give it to jwtInterceptorProvider to attach as an auth header on our requests

      // we need to push this into an array of http interceptors that comes from angular
      return store.get('id_token');
    }
  })
<|endoftext|>


<commit_before>import promiseMiddleware from 'redux-promise';
import thunk from 'redux-thunk';
//import { getSchema, refreshSchema } from '../../services/context-service';
//import fetch from '../../utils/fetch';
import { push } from 'react-router-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import R from 'ramda';
import reducer, * as actions from '../../../../src/store/ducks/schema';
import { expect } from 'chai';

describe('schema duck', () => {
  let state, dispatch;
  beforeEach(() => {
    let store = createStore(
      combineReducers({ schema: reducer }),
      {},
      applyMiddleware(thunk, promiseMiddleware),
    );
    dispatch = store.dispatch;
    store.subscribe(() => (state = store.getState().schema));
  });

  describe('add new identity', () => {
    it('identity should be added to the state', () => {
      dispatch(actions.addNewIdentity('user'));
      expect(state).to.have
        .property('user')
        .to.deep.include({ local: {}, remote: null, isSaving: false });
    });
  });

  describe('Manage identity properties', () => {
    it('identity property can be added', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
    });

    it('identity property can be updated', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
      dispatch(actions.upsertIdentityProperty('user', 'age', 40));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 40 } });
    });

    it('identity property can be removed', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
      dispatch(actions.removeIdentityProperty('user', 'age'));
      expect(state).to.have.property('user').with.property('local').to.not.have.property('age');
    });
  });

  describe('Remote Updates', () => {
    let originalFetch = global.fetch;
    beforeEach(() => {
      global.fetch = jest.fn(async (url, fetchArgs) => {
        fetchArgs = fetchArgs || { method: 'get' };
        if (fetchArgs.url === '/api/schema' && fetchArgs.method.toUpperCase() === 'GET') {
          return { ok: true, json: async () => R.map(x => x.remote)(state) };
        }
        return { ok: true, json: async () => ({}) };
      });
    });
    afterEach(() => {
      global.fetch = originalFetch;
    });

    it('saving new identity', async () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      const savePromise = dispatch(actions.saveSchema('user'));
      expect(state).to.have.property('user').with.property('isSaving', true);
      await savePromise;
      expect(state).to.have.property('user').with.property('isSaving', false);
      let [url, { method, body }] = fetch.mock.calls[0];
      expect(url.toLowerCase()).to.eq('/api/schema/user');
      expect(method).to.eq('POST');
      expect(JSON.parse(body)).to.deep.equal({ age: 30 });
      expect(state).to.have.property('user').with.property('remote').deep.eq({ age: 30 });
    });

    it('updating existing identity', async () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      const savePromise = await dispatch(actions.saveSchema('user'));
      await savePromise;
      let [url, { method, body }] = fetch.mock.calls[0];
      expect(url.toLowerCase()).to.eq('/api/schema/user');
      expect(method).to.eq('POST');
      expect(JSON.parse(body)).to.deep.equal({ age: 30 });
    });
  });
});
<commit_msg>fix tests<commit_after>import promiseMiddleware from 'redux-promise';
import thunk from 'redux-thunk';
import jsondiffpatch from 'jsondiffpatch';
import { push } from 'react-router-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import R from 'ramda';
import reducer, * as actions from '../../../../src/store/ducks/schema';
import { expect } from 'chai';

describe('schema duck', () => {
  let state, dispatch;
  beforeEach(() => {
    let store = createStore(
      combineReducers({ schema: reducer }),
      {},
      applyMiddleware(thunk, promiseMiddleware),
    );
    dispatch = store.dispatch;
    store.subscribe(() => (state = store.getState().schema));
  });

  describe('add new identity', () => {
    it('identity should be added to the state', () => {
      dispatch(actions.addNewIdentity('user'));
      expect(state).to.have
        .property('user')
        .to.deep.include({ local: {}, remote: null, isSaving: false });
    });
  });

  describe('Manage identity properties', () => {
    it('identity property can be added', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
    });

    it('identity property can be updated', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
      dispatch(actions.upsertIdentityProperty('user', 'age', 40));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 40 } });
    });

    it('identity property can be removed', () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      expect(state).to.have.property('user').to.deep.include({ local: { age: 30 } });
      dispatch(actions.removeIdentityProperty('user', 'age'));
      expect(state).to.have.property('user').with.property('local').to.not.have.property('age');
    });
  });

  describe('Remote Updates', () => {
    let originalFetch = global.fetch;
    beforeEach(() => {
      global.fetch = jest.fn(async (url, fetchArgs) => {
        fetchArgs = fetchArgs || { method: 'get' };
        if (fetchArgs.url === '/api/schema' && fetchArgs.method.toUpperCase() === 'GET') {
          return { ok: true, json: async () => R.map(x => x.remote)(state) };
        }
        return { ok: true, json: async () => ({}) };
      });
    });
    afterEach(() => {
      global.fetch = originalFetch;
    });

    it('saving new identity', async () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      const savePromise = dispatch(actions.saveSchema('user'));
      expect(state).to.have.property('user').with.property('isSaving', true);
      await savePromise;
      expect(state).to.have.property('user').with.property('isSaving', false);
      let [url, { method, body }] = fetch.mock.calls[0];
      expect(url.toLowerCase()).to.eq('/api/schema/user');
      expect(method).to.eq('POST');
      expect(JSON.parse(body)).to.deep.equal({ age: 30 });
      expect(state).to.have.property('user').with.property('remote').deep.eq({ age: 30 });
    });

    it('updating existing identity', async () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      await dispatch(actions.saveSchema('user'));
      const oldUserState = R.clone(state.user.local);
      dispatch(actions.upsertIdentityProperty('user', 'age', 40));
      dispatch(actions.upsertIdentityProperty('user', 'gender', 'female'));
      await dispatch(actions.saveSchema('user'));
      const [_, { body }] = fetch.mock.calls.find(
        ([url, { method }]) => method === 'PATCH' && url === '/api/schema/user',
      );
      const patch = JSON.parse(body);
      const newUserState = R.clone(state.user.local);
      expect(newUserState).to.deep.include({ age: 40, gender: 'female' });
      const patchedState = jsondiffpatch.patch(oldUserState, patch);
      expect(patchedState).to.deep.equal(newUserState);
    });

    it('delete existing identity', async () => {
      dispatch(actions.addNewIdentity('user'));
      dispatch(actions.upsertIdentityProperty('user', 'age', 30));
      await dispatch(actions.deleteIdentity('user'));
      let [url, { method }] = fetch.mock.calls[0];
      expect(url.toLowerCase()).to.eq('/api/schema/user');
      expect(method).to.eq('DELETE');
      expect(state).to.not.have.property('user');
    });
  });
});
<|endoftext|>