<commit_before>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('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 FB_TEXT_LIMIT = 640;

const FACEBOOK_LOCATION = "FACEBOOK_LOCATION";
const FACEBOOK_WELCOME = "FACEBOOK_WELCOME";

class FacebookBot {
    constructor() {
        this.apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
        this.sessionIds = new Map();
        this.messagesDelay = 200;
    }

    doDataResponse(sender, facebookResponseData) {
        if (!Array.isArray(facebookResponseData)) {
            console.log('Response as formatted message');
            this.sendFBMessage(sender, facebookResponseData)
                .catch(err => console.error(err));
        } else {
            async.eachSeries(facebookResponseData, (facebookMessage, callback) => {
                if (facebookMessage.sender_action) {
                    console.log('Response as sender action');
                    this.sendFBSenderAction(sender, facebookMessage.sender_action)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
                else {
                    console.log('Response as formatted message');
                    this.sendFBMessage(sender, facebookMessage)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
            }, (err) => {
                if (err) {
                    console.error(err);
                } else {
                    console.log('Data response completed');
                }
            });
        }
    }

    doRichContentResponse(sender, messages) {
        let facebookMessages = []; // array with result messages

        for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
            let message = messages[messageIndex];

            switch (message.type) {
                //message.type 0 means text message
                case 0:
                    // speech: ["hi"]
                    // we have to get value from fulfillment.speech, because of here is raw speech
                    if (message.speech) {

                        let splittedText = this.splitResponse(message.speech);

                        splittedText.forEach(s => {
                            facebookMessages.push({text: s});
                        });
                    }

                    break;
                //message.type 1 means card message
                case 1: {
                    let carousel = [message];

                    for (messageIndex++; messageIndex < messages.length; messageIndex++) {
                        if (messages[messageIndex].type == 1) {
                            carousel.push(messages[messageIndex]);
                        } else {
                            messageIndex--;
                            break;
                        }
                    }

                    let facebookMessage = {};
                    carousel.forEach((c) => {
                        // buttons: [ {text: "hi", postback: "postback"} ], imageUrl: "", title: "", subtitle: ""

                        let card = {};

                        card.title = c.title;
                        card.image_url = c.imageUrl;
                        if (this.isDefined(c.subtitle)) {
                            card.subtitle = c.subtitle;
                        }
                        //If button is involved in.
                        if (c.buttons.length > 0) {
                            let buttons = [];
                            for (let buttonIndex = 0; buttonIndex < c.buttons.length; buttonIndex++) {
                                let button = c.buttons[buttonIndex];

                                if (button.text) {
                                    let postback = button.postback;
                                    if (!postback) {
                                        postback = button.text;
                                    }

                                    let buttonDescription = {
                                        title: button.text
                                    };

                                    if (postback.startsWith("http")) {
                                        buttonDescription.type = "web_url";
                                        buttonDescription.url = postback;
                                    } else {
                                        buttonDescription.type = "postback";
                                        buttonDescription.payload = postback;
                                    }

                                    buttons.push(buttonDescription);
                                }
                            }

                            if (buttons.length > 0) {
                                card.buttons = buttons;
                            }
                        }

                        if (!facebookMessage.attachment) {
                            facebookMessage.attachment = {type: "template"};
                        }

                        if (!facebookMessage.attachment.payload) {
                            facebookMessage.attachment.payload = {template_type: "generic", elements: []};
                        }

                        facebookMessage.attachment.payload.elements.push(card);
                    });

                    facebookMessages.push(facebookMessage);
                }

                    break;
                //message.type 2 means quick replies message
                case 2: {
                    if (message.replies && message.replies.length > 0) {
                        let facebookMessage = {};

                        facebookMessage.text = message.title ? message.title : 'Choose an item';
                        facebookMessage.quick_replies = [];

                        message.replies.forEach((r) => {
                            facebookMessage.quick_replies.push({
                                content_type: "text",
                                title: r,
                                payload: r
                            });
                        });

                        facebookMessages.push(facebookMessage);
                    }
                }

                    break;
                //message.type 3 means image message
                case 3:

                    if (message.imageUrl) {
                        let facebookMessage = {};

                        // "imageUrl": "http://example.com/image.jpg"
                        facebookMessage.attachment = {type: "image"};
                        facebookMessage.attachment.payload = {url: message.imageUrl};

                        facebookMessages.push(facebookMessage);
                    }

                    break;
                //message.type 4 means custom payload message
                case 4:
                    if (message.payload && message.payload.facebook) {
                        facebookMessages.push(message.payload.facebook);
                    }
                    break;

                default:
                    break;
            }
        }

        return new Promise((resolve, reject) => {
            async.eachSeries(facebookMessages, (msg, callback) => {
                    this.sendFBSenderAction(sender, "typing_on")
                        .then(() => this.sleep(this.messagesDelay))
                        .then(() => this.sendFBMessage(sender, msg))
                        .then(() => callback())
                        .catch(callback);
                },
                (err) => {
                    if (err) {
                        console.error(err);
                        reject(err);
                    } else {
                        console.log('Messages sent');
                        resolve();
                    }
                });
        });

    }

    doTextResponse(sender, responseText) {
        console.log('Response as text message');
        // facebook API limit for text length is 640,
        // so we must split message if needed
        let splittedText = this.splitResponse(responseText);

        async.eachSeries(splittedText, (textPart, callback) => {
            this.sendFBMessage(sender, {text: textPart})
                .then(() => callback())
                .catch(err => callback(err));
        });
    }

    //which webhook event
    getEventText(event) {
        if (event.message) {
            if (event.message.quick_reply && event.message.quick_reply.payload) {
                return event.message.quick_reply.payload;
            }

            if (event.message.text) {
				if(event.message.text=='Alta'){
					console.log("Alta");
					return 'ALTA';
				}
				else{
				procesarPeticion();
				return event.message.text;
				}
                return event.message.text;
            }
        }

        if (event.postback && event.postback.payload) {
            return event.postback.payload;
        }

        return null;

    }

    getFacebookEvent(event) {
        if (event.postback && event.postback.payload) {

            let payload = event.postback.payload;

            switch (payload) {
                case FACEBOOK_WELCOME:
                    return {name: FACEBOOK_WELCOME};

                case FACEBOOK_LOCATION:
                    return {name: FACEBOOK_LOCATION, data: event.postback.data}
            }
        }

        return null;
    }

    processFacebookEvent(event) {
        const sender = event.sender.id.toString();
        const eventObject = this.getFacebookEvent(event);

        if (eventObject) {

            // Handle a text message from this sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
            }

            let apiaiRequest = this.apiAiService.eventRequest(eventObject,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });
            this.doApiAiRequest(apiaiRequest, sender);
        }
    }

    processMessageEvent(event) {
        const sender = event.sender.id.toString();
        const text = this.getEventText(event);

        if (text) {

            // Handle a text message from this sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
				console.log('this.sessionIds= ',this.sessionIds);
            }

            console.log("Text", text);
            //send user's text to api.ai service
            let apiaiRequest = this.apiAiService.textRequest(text,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });

            this.doApiAiRequest(apiaiRequest, sender);
        }
    }

    doApiAiRequest(apiaiRequest, sender) {
        apiaiRequest.on('response', (response) => {
            if (this.isDefined(response.result) && this.isDefined(response.result.fulfillment)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
				console.log('response.result.parameters.nombre: ',response.result.parameters.nombre);
                let responseMessages = response.result.fulfillment.messages;

                if (this.isDefined(responseData) && this.isDefined(responseData.facebook)) {
                    let facebookResponseData = responseData.facebook;
                    this.doDataResponse(sender, facebookResponseData);
                } else if (this.isDefined(responseMessages) && responseMessages.length > 0) {
                    this.doRichContentResponse(sender, responseMessages);
                }
                else if (this.isDefined(responseText)) {
                    this.doTextResponse(sender, responseText);
                }

            }
        });

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

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

        return this.chunkString(str, FB_TEXT_LIMIT);
    }

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

        let output = [];

        while (s[curr]) {
            if (s[curr++] == ' ') {
                output.push(s.substring(prev, curr));
                prev = curr;
                curr += len;
            }
            else {
                let 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));
        return output;
    }

    sendFBMessage(sender, messageData) {
        return new Promise((resolve, reject) => {
            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,
                }
            }, (error, response) => {
                if (error) {
                    console.log('Error sending message: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.log('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    sendFBSenderAction(sender, action) {
        return new Promise((resolve, reject) => {
            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) => {
                if (error) {
                    console.error('Error sending action: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.error('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    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);
                }
            });
    }

    configureGetStartedEvent() {
        request({
                method: 'POST',
                uri: `https://graph.facebook.com/v2.6/me/thread_settings?access_token=${FB_PAGE_ACCESS_TOKEN}`,
                json: {
                    setting_type: "call_to_actions",
                    thread_state: "new_thread",
                    call_to_actions: [
                        {
                            payload: FACEBOOK_WELCOME
                        }
                    ]
                }
            },
            (error, response, body) => {
                if (error) {
                    console.error('Error while subscription', error);
                } else {
                    console.log('Subscription result', response.body);
                }
            });
    }

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

        if (!obj) {
            return false;
        }

        return obj != null;
    }

    sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), delay);
        });
    }

}

function procesarPeticion(){
	console.log("prosesar peticion");
	return null;
	}

let facebookBot = new FacebookBot();

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(() => {
            facebookBot.doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    //console.log("req.sessionID= ",JSONbig.parse(req.sessionID));
	//console.log("req= ",req);
	try {
        const data = JSONbig.parse(req.body);
		//console.log("data= ",data);
        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo) {

                            if (event.message.attachments) {
                                let locations = event.message.attachments.filter(a => a.type === "location");

                                // delete all locations from original message
                                event.message.attachments = event.message.attachments.filter(a => a.type !== "location");

                                if (locations.length > 0) {
                                    locations.forEach(l => {
                                        let locationEvent = {
                                            sender: event.sender,
                                            postback: {
                                                payload: "FACEBOOK_LOCATION",
                                                data: l.payload.coordinates
                                            }
                                        };

                                        facebookBot.processFacebookEvent(locationEvent);
                                    });
                                }
                            }

                            facebookBot.processMessageEvent(event);
                        } else if (event.postback && event.postback.payload) {
                            if (event.postback.payload === "FACEBOOK_WELCOME") {
                                facebookBot.processFacebookEvent(event);
                            } else {
                                facebookBot.processMessageEvent(event);
                            }
                        }
                    });
                }
            });
        }

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

});

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

facebookBot.doSubscribeRequest();
<commit_msg>dcdc<commit_after>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('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 FB_TEXT_LIMIT = 640;

const FACEBOOK_LOCATION = "FACEBOOK_LOCATION";
const FACEBOOK_WELCOME = "FACEBOOK_WELCOME";

class FacebookBot {
    constructor() {
        this.apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
        this.sessionIds = new Map();
        this.messagesDelay = 200;
    }

    doDataResponse(sender, facebookResponseData) {
        if (!Array.isArray(facebookResponseData)) {
            console.log('Response as formatted message');
            this.sendFBMessage(sender, facebookResponseData)
                .catch(err => console.error(err));
        } else {
            async.eachSeries(facebookResponseData, (facebookMessage, callback) => {
                if (facebookMessage.sender_action) {
                    console.log('Response as sender action');
                    this.sendFBSenderAction(sender, facebookMessage.sender_action)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
                else {
                    console.log('Response as formatted message');
                    this.sendFBMessage(sender, facebookMessage)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
            }, (err) => {
                if (err) {
                    console.error(err);
                } else {
                    console.log('Data response completed');
                }
            });
        }
    }

    doRichContentResponse(sender, messages) {
        let facebookMessages = []; // array with result messages

        for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
            let message = messages[messageIndex];

            switch (message.type) {
                //message.type 0 means text message
                case 0:
                    // speech: ["hi"]
                    // we have to get value from fulfillment.speech, because of here is raw speech
                    if (message.speech) {

                        let splittedText = this.splitResponse(message.speech);

                        splittedText.forEach(s => {
                            facebookMessages.push({text: s});
                        });
                    }

                    break;
                //message.type 1 means card message
                case 1: {
                    let carousel = [message];

                    for (messageIndex++; messageIndex < messages.length; messageIndex++) {
                        if (messages[messageIndex].type == 1) {
                            carousel.push(messages[messageIndex]);
                        } else {
                            messageIndex--;
                            break;
                        }
                    }

                    let facebookMessage = {};
                    carousel.forEach((c) => {
                        // buttons: [ {text: "hi", postback: "postback"} ], imageUrl: "", title: "", subtitle: ""

                        let card = {};

                        card.title = c.title;
                        card.image_url = c.imageUrl;
                        if (this.isDefined(c.subtitle)) {
                            card.subtitle = c.subtitle;
                        }
                        //If button is involved in.
                        if (c.buttons.length > 0) {
                            let buttons = [];
                            for (let buttonIndex = 0; buttonIndex < c.buttons.length; buttonIndex++) {
                                let button = c.buttons[buttonIndex];

                                if (button.text) {
                                    let postback = button.postback;
                                    if (!postback) {
                                        postback = button.text;
                                    }

                                    let buttonDescription = {
                                        title: button.text
                                    };

                                    if (postback.startsWith("http")) {
                                        buttonDescription.type = "web_url";
                                        buttonDescription.url = postback;
                                    } else {
                                        buttonDescription.type = "postback";
                                        buttonDescription.payload = postback;
                                    }

                                    buttons.push(buttonDescription);
                                }
                            }

                            if (buttons.length > 0) {
                                card.buttons = buttons;
                            }
                        }

                        if (!facebookMessage.attachment) {
                            facebookMessage.attachment = {type: "template"};
                        }

                        if (!facebookMessage.attachment.payload) {
                            facebookMessage.attachment.payload = {template_type: "generic", elements: []};
                        }

                        facebookMessage.attachment.payload.elements.push(card);
                    });

                    facebookMessages.push(facebookMessage);
                }

                    break;
                //message.type 2 means quick replies message
                case 2: {
                    if (message.replies && message.replies.length > 0) {
                        let facebookMessage = {};

                        facebookMessage.text = message.title ? message.title : 'Choose an item';
                        facebookMessage.quick_replies = [];

                        message.replies.forEach((r) => {
                            facebookMessage.quick_replies.push({
                                content_type: "text",
                                title: r,
                                payload: r
                            });
                        });

                        facebookMessages.push(facebookMessage);
                    }
                }

                    break;
                //message.type 3 means image message
                case 3:

                    if (message.imageUrl) {
                        let facebookMessage = {};

                        // "imageUrl": "http://example.com/image.jpg"
                        facebookMessage.attachment = {type: "image"};
                        facebookMessage.attachment.payload = {url: message.imageUrl};

                        facebookMessages.push(facebookMessage);
                    }

                    break;
                //message.type 4 means custom payload message
                case 4:
                    if (message.payload && message.payload.facebook) {
                        facebookMessages.push(message.payload.facebook);
                    }
                    break;

                default:
                    break;
            }
        }

        return new Promise((resolve, reject) => {
            async.eachSeries(facebookMessages, (msg, callback) => {
                    this.sendFBSenderAction(sender, "typing_on")
                        .then(() => this.sleep(this.messagesDelay))
                        .then(() => this.sendFBMessage(sender, msg))
                        .then(() => callback())
                        .catch(callback);
                },
                (err) => {
                    if (err) {
                        console.error(err);
                        reject(err);
                    } else {
                        console.log('Messages sent');
                        resolve();
                    }
                });
        });

    }

    doTextResponse(sender, responseText) {
        console.log('Response as text message');
        // facebook API limit for text length is 640,
        // so we must split message if needed
        let splittedText = this.splitResponse(responseText);

        async.eachSeries(splittedText, (textPart, callback) => {
            this.sendFBMessage(sender, {text: textPart})
                .then(() => callback())
                .catch(err => callback(err));
        });
    }

    //which webhook event
    getEventText(event) {
        if (event.message) {
            if (event.message.quick_reply && event.message.quick_reply.payload) {
                return event.message.quick_reply.payload;
            }

            if (event.message.text) {
				if(event.message.text=='Alta'){
					console.log("Alta");
					return 'ALTA';
				}
				else{
				procesarPeticion();
				return event.message.text;
				}
                return event.message.text;
            }
        }

        if (event.postback && event.postback.payload) {
            return event.postback.payload;
        }

        return null;

    }

    getFacebookEvent(event) {
        if (event.postback && event.postback.payload) {

            let payload = event.postback.payload;

            switch (payload) {
                case FACEBOOK_WELCOME:
                    return {name: FACEBOOK_WELCOME};

                case FACEBOOK_LOCATION:
                    return {name: FACEBOOK_LOCATION, data: event.postback.data}
            }
        }

        return null;
    }

    processFacebookEvent(event) {
        const sender = event.sender.id.toString();
        const eventObject = this.getFacebookEvent(event);

        if (eventObject) {

            // Handle a text message from this sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
            }

            let apiaiRequest = this.apiAiService.eventRequest(eventObject,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });
            this.doApiAiRequest(apiaiRequest, sender);
        }
    }

    processMessageEvent(event) {
        const sender = event.sender.id.toString();
        const text = this.getEventText(event);

        if (text) {

            // Handle a text message from this sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
				console.log('this.sessionIds= ',this.sessionIds);
            }

            console.log("Text", text);
            //send user's text to api.ai service
            let apiaiRequest = this.apiAiService.textRequest(text,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });

            this.doApiAiRequest(apiaiRequest, sender);
        }
    }

    doApiAiRequest(apiaiRequest, sender) {
        apiaiRequest.on('response', (response) => {
            if (this.isDefined(response.result) && this.isDefined(response.result.fulfillment)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
				console.log('response.result.parameters.nombre: ',response.result.parameters.nombre);
				console.log('response.result.parameters.nombre: ',response.result.contexts[]);
                let responseMessages = response.result.fulfillment.messages;

                if (this.isDefined(responseData) && this.isDefined(responseData.facebook)) {
                    let facebookResponseData = responseData.facebook;
                    this.doDataResponse(sender, facebookResponseData);
                } else if (this.isDefined(responseMessages) && responseMessages.length > 0) {
                    this.doRichContentResponse(sender, responseMessages);
                }
                else if (this.isDefined(responseText)) {
                    this.doTextResponse(sender, responseText);
                }

            }
        });

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

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

        return this.chunkString(str, FB_TEXT_LIMIT);
    }

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

        let output = [];

        while (s[curr]) {
            if (s[curr++] == ' ') {
                output.push(s.substring(prev, curr));
                prev = curr;
                curr += len;
            }
            else {
                let 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));
        return output;
    }

    sendFBMessage(sender, messageData) {
        return new Promise((resolve, reject) => {
            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,
                }
            }, (error, response) => {
                if (error) {
                    console.log('Error sending message: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.log('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    sendFBSenderAction(sender, action) {
        return new Promise((resolve, reject) => {
            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) => {
                if (error) {
                    console.error('Error sending action: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.error('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    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);
                }
            });
    }

    configureGetStartedEvent() {
        request({
                method: 'POST',
                uri: `https://graph.facebook.com/v2.6/me/thread_settings?access_token=${FB_PAGE_ACCESS_TOKEN}`,
                json: {
                    setting_type: "call_to_actions",
                    thread_state: "new_thread",
                    call_to_actions: [
                        {
                            payload: FACEBOOK_WELCOME
                        }
                    ]
                }
            },
            (error, response, body) => {
                if (error) {
                    console.error('Error while subscription', error);
                } else {
                    console.log('Subscription result', response.body);
                }
            });
    }

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

        if (!obj) {
            return false;
        }

        return obj != null;
    }

    sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), delay);
        });
    }

}

function procesarPeticion(){
	console.log("prosesar peticion");
	return null;
	}

let facebookBot = new FacebookBot();

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(() => {
            facebookBot.doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    //console.log("req.sessionID= ",JSONbig.parse(req.sessionID));
	//console.log("req= ",req);
	try {
        const data = JSONbig.parse(req.body);
		//console.log("data= ",data);
        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo) {

                            if (event.message.attachments) {
                                let locations = event.message.attachments.filter(a => a.type === "location");

                                // delete all locations from original message
                                event.message.attachments = event.message.attachments.filter(a => a.type !== "location");

                                if (locations.length > 0) {
                                    locations.forEach(l => {
                                        let locationEvent = {
                                            sender: event.sender,
                                            postback: {
                                                payload: "FACEBOOK_LOCATION",
                                                data: l.payload.coordinates
                                            }
                                        };

                                        facebookBot.processFacebookEvent(locationEvent);
                                    });
                                }
                            }

                            facebookBot.processMessageEvent(event);
                        } else if (event.postback && event.postback.payload) {
                            if (event.postback.payload === "FACEBOOK_WELCOME") {
                                facebookBot.processFacebookEvent(event);
                            } else {
                                facebookBot.processMessageEvent(event);
                            }
                        }
                    });
                }
            });
        }

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

});

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

facebookBot.doSubscribeRequest();
<|endoftext|>


<commit_before>import Firebase from 'firebase';
import creds from '../../lib/creds';
import axios from 'axios';
import moment from 'moment';

const FBRef = new Firebase(creds.firebase);
const today = new Date();
const theDay = today.getDate();
const theMonth = today.getMonth();
const theYear = today.getFullYear();
const rooms = ['biggie', 'smalls', 'lounge'];
let roomStatus = {
  biggie: {
    name: 'biggie',
    taken: false
  },
  smalls: {
    name: 'smalls',
    taken: false
  },
  lounge: {
    name: 'lounge',
    taken: false
  }
};

function checkDomain(email) {
  const re = /(@brooklynunited.com)/;
  if (re.test(email)) return true;
  return false;
}

function persistCalendar(cal) {
  const id = cal.id.replace(/[^0-9|a-z|A-Z]/g, '');
  if (id.length > 1) {
    FBRef.child('meetingplanner/calendars').child(id).update({
      ...cal
    });
  }
}

function objectToArray(obj) {
  return Object.keys(obj).map(key => {
    return obj[key];
  });
}

function whichRoom(location) {
  let response = null;
  Object.keys(roomStatus).map( room => {
    // const reg = new RegExp('(' + room + ')', 'g');
    // console.log('the ex', room);
    if ((location.toLowerCase()).search(room) > -1) response = room;
  });
  return response;
}

export function getUserImage(usr, cb) {
  const userId = usr.email.replace(/[^0-9|a-z|A-Z]/g, '');
  let profileImg = 'http://www.brooklynunited.com/images/brooklyn-united-header-gear.gif';
  return FBRef.child('meetingplanner/users').child(userId).once('value', snapshot => {
    const profile = snapshot.val();
    if (profile) profileImg = profile.img;
    cb(profileImg);
  });
}

export function getSchedule(data, cb) {
  const token = encodeURIComponent(data);
  const theDate = (new Date()).toISOString();
  const timeMax = (new Date(theYear, theMonth, theDay + 7, 18, 0, 0)).toISOString();
  axios.get(`https://www.googleapis.com/calendar/v3/calendars/primary/events?access_token=${token}&timeMin=${theDate}&timeMax=${timeMax}&orderBy=startTime&singleEvents=true`)
    .then((res) => {
      cb(res.data.items);
    })
    .catch((error) => {
      if (error.hasOwnProperty('data')) cb({error: error.data.error.code});
    });
}

export function getAllEvents(data, cb, timeMin, timeMax) {
  const {calendarID, token} = data;
  rooms.map(room => {
    axios.get(`https://www.googleapis.com/calendar/v3/calendars/${calendarID}/events?access_token=${token}&timeMin=${timeMin}&timeMax=${timeMax}&q=${room}&singleEvents=true`)
    .then(eventList => {
      if (eventList.data.items.length > 0) {
        eventList.data.items.map((event) => {
          cb(event);
        });
      } else {
        cb();
      }
    });
  });
}

export function getCalendars(data, cb) {
  FBRef.child('meetingplanner/calendars').once('value', snapshot => {
    if (snapshot.val()) {
      cb(snapshot.val());
    } else {
      axios.get(`https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=${data}&fields=items%2Fid`)
        .then(res => {
          res.data.items.map(cal => {
            persistCalendar(cal);
          });
          cb(res.data.items);
        });
    }
  });
}

export function checkStatus(event, time) {
  const currently = time || new Date();
  const {start, end} = event;
  let theRoom;
  // If it has a start and end
  if (start.dateTime && end.dateTime) {
    // If it's currently happening
    if ((Date.parse(start.dateTime) <= Date.parse(currently)) && (Date.parse(end.dateTime) >= Date.parse(currently))) {
      theRoom = whichRoom(event.location);
      // If the room exists
      if (roomStatus[theRoom]) {
        roomStatus[theRoom].taken = true;
        roomStatus[theRoom].details = {
          name: event.summary,
          attendees: event.attendees,
          start: event.start.dateTime,
          end: event.end.dateTime
        };
      }
    }
  }
}

export function getAvailability(time, cb) {
  const token = encodeURIComponent(time.token);
  let calsChecked = 0;
  roomStatus = {
    biggie: {
      name: 'biggie',
      taken: false
    },
    smalls: {
      name: 'smalls',
      taken: false
    },
    lounge: {
      name: 'lounge',
      taken: false
    }
  };
  const timeMin = moment(time.date).hour(9).minute(0);
  const timeMax = moment(time.date).hour(18).minute(0);
  getCalendars(token, res => {
    let calendars = res;
    if (typeof(res) === 'object') calendars = objectToArray(res);
    calendars.map(item => {
      const currentItem = item;
      const calendarID = encodeURIComponent(currentItem.id);
      const calData = {
        calendarID: calendarID,
        token: token
      };
      getAllEvents(calData, event => {
        calsChecked++;
        if (event) checkStatus(event, time.date);
        if (calsChecked >= (calendars.length * 3)) cb(roomStatus);
      }, timeMin.toISOString(), timeMax.toISOString());
    });
  });
}

export function checkRooms(data, cb) {
  const token = encodeURIComponent(data.token);
  let calsChecked = 0;
  const timeMin = data.timeMin || (new Date(theYear, theMonth, theDay, 9, 0, 0));
  const timeMax = data.timeMax || (new Date(theYear, theMonth, theDay, 18, 0, 0));
  const allMeetings = {
    date: data.timeMin,
    items: []
  };
  getCalendars(token, res => {
    let calendars = res;
    if (typeof(res) === 'object') calendars = objectToArray(res);
    calendars.map(item => {
      const currentItem = item;
      const calendarID = encodeURIComponent(currentItem.id);
      const calData = {
        calendarID: calendarID,
        token: token
      };
      getAllEvents(calData, event => {
        calsChecked++;
        if (event) {
          event.attendees.map(person => {
            if (person.displayName) {
              const roomResource = rooms.indexOf(person.displayName.toLowerCase());
              if ((roomResource > -1) && (event.location !== person.displayName)) {
                event.location += ' - ' + person.displayName;
              }
            }
          });
          allMeetings.items.push(event);
          if (moment(timeMin).isSame(moment(), 'day')) checkStatus(event);
        }
        if (calsChecked >= (calendars.length * 3)) cb(roomStatus, allMeetings);
      }, timeMin.toISOString(), timeMax.toISOString());
    });
  });
}

export function signInWithGoogle(cb) {
  FBRef.authWithOAuthPopup('google', (error, authData) => {
    let valid;
    let res;
    if (authData !== undefined) {
      valid = checkDomain(authData.google.email);
    }
    if (valid) {
      getSchedule(authData.google.accessToken, calEvents => {
        res = {
          error: error,
          authData: authData,
          valid: valid,
          schedule: calEvents
        };
        cb(res);
      });
    } else {
      res = {
        error: error,
        authData: authData,
        valid: valid
      };
      cb(res);
    }
  }, {
    scope: 'email,https://www.googleapis.com/auth/calendar'
  });
}

export function getUserList(cb) {
  return FBRef.child('meetingplanner/users').once('value', snapshot => {
    const users = objectToArray(snapshot.val());
    cb(users);
  });
}

export function persistUser(user) {
  const userId = user.google.email.replace(/[^0-9|a-z|A-Z]/g, '');
  FBRef.child('meetingplanner/users').child(userId).update({
    name: user.google.displayName,
    uid: user.google.id,
    img: user.google.profileImageURL,
    expires: user.expires,
    ...user.google,
    accessToken: null
  });
  FBRef.child('meetingplanner/calendars').child(userId).update({
    email: user.google.email
  });
}

export function saveReservation(res, user) {
  const userId = user.google.email.replace(/[^0-9|a-z|A-Z]/g, '');
  FBRef.child('meetingplanner/users').child(userId).child('schedule').push(res);
}

export function getSession(cb) {
  cb(FBRef.getAuth());
}

export function logOut() {
  FBRef.unauth();
}

<commit_msg>Fixed calendar bug<commit_after>import Firebase from 'firebase';
import creds from '../../lib/creds';
import axios from 'axios';
import moment from 'moment';

const FBRef = new Firebase(creds.firebase);
const today = new Date();
const theDay = today.getDate();
const theMonth = today.getMonth();
const theYear = today.getFullYear();
const rooms = ['biggie', 'smalls', 'lounge'];
let roomStatus = {
  biggie: {
    name: 'biggie',
    taken: false
  },
  smalls: {
    name: 'smalls',
    taken: false
  },
  lounge: {
    name: 'lounge',
    taken: false
  }
};

function checkDomain(email) {
  const re = /(@brooklynunited.com)/;
  if (re.test(email)) return true;
  return false;
}

function persistCalendar(cal) {
  const id = cal.id.replace(/[^0-9|a-z|A-Z]/g, '');
  if (id.length > 1) {
    FBRef.child('meetingplanner/calendars').child(id).update({
      ...cal
    });
  }
}

function objectToArray(obj) {
  return Object.keys(obj).map(key => {
    return obj[key];
  });
}

function whichRoom(location) {
  let response = null;
  Object.keys(roomStatus).map( room => {
    // const reg = new RegExp('(' + room + ')', 'g');
    // console.log('the ex', room);
    if ((location.toLowerCase()).search(room) > -1) response = room;
  });
  return response;
}

export function getUserImage(usr, cb) {
  const userId = usr.email.replace(/[^0-9|a-z|A-Z]/g, '');
  let profileImg = 'http://www.brooklynunited.com/images/brooklyn-united-header-gear.gif';
  return FBRef.child('meetingplanner/users').child(userId).once('value', snapshot => {
    const profile = snapshot.val();
    if (profile) profileImg = profile.img;
    cb(profileImg);
  });
}

export function getSchedule(data, cb) {
  const token = encodeURIComponent(data);
  const theDate = (new Date()).toISOString();
  const timeMax = (new Date(theYear, theMonth, theDay + 7, 18, 0, 0)).toISOString();
  axios.get(`https://www.googleapis.com/calendar/v3/calendars/primary/events?access_token=${token}&timeMin=${theDate}&timeMax=${timeMax}&orderBy=startTime&singleEvents=true`)
    .then(res => {
      cb(res.data.items);
    })
    .catch(error => {
      if (error.hasOwnProperty('data')) cb({error: error.data.error.code});
    });
}

export function getAllEvents(data, cb, timeMin, timeMax) {
  const {calendarID, token} = data;
  rooms.map(room => {
    axios.get(`https://www.googleapis.com/calendar/v3/calendars/${calendarID}/events?access_token=${token}&timeMin=${timeMin}&timeMax=${timeMax}&q=${room}&singleEvents=true`)
    .then(eventList => {
      if (eventList.data.items.length > 0) {
        eventList.data.items.map((event) => {
          cb(event);
        });
      } else {
        cb();
      }
    })
    .catch(err => {
      // console.log(err);
      cb();
    });
  });
}

export function getCalendars(data, cb) {
  FBRef.child('meetingplanner/calendars').once('value', snapshot => {
    if (snapshot.val()) {
      cb(snapshot.val());
    } else {
      axios.get(`https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=${data}&fields=items%2Fid`)
        .then(res => {
          res.data.items.map(cal => {
            persistCalendar(cal);
          });
          cb(res.data.items);
        });
    }
  });
}

export function checkStatus(event, time) {
  const currently = time || new Date();
  const {start, end} = event;
  let theRoom;
  // If it has a start and end
  if (start.dateTime && end.dateTime) {
    // If it's currently happening
    if ((Date.parse(start.dateTime) <= Date.parse(currently)) && (Date.parse(end.dateTime) >= Date.parse(currently))) {
      theRoom = whichRoom(event.location);
      // If the room exists
      if (roomStatus[theRoom]) {
        roomStatus[theRoom].taken = true;
        roomStatus[theRoom].details = {
          name: event.summary,
          attendees: event.attendees,
          start: event.start.dateTime,
          end: event.end.dateTime
        };
      }
    }
  }
}

export function getAvailability(time, cb) {
  const token = encodeURIComponent(time.token);
  let calsChecked = 0;
  roomStatus = {
    biggie: {
      name: 'biggie',
      taken: false
    },
    smalls: {
      name: 'smalls',
      taken: false
    },
    lounge: {
      name: 'lounge',
      taken: false
    }
  };
  const timeMin = moment(time.date).hour(9).minute(0);
  const timeMax = moment(time.date).hour(18).minute(0);
  getCalendars(token, res => {
    let calendars = res;
    if (typeof(res) === 'object') calendars = objectToArray(res);
    calendars.map(item => {
      const currentItem = item;
      const calendarID = encodeURIComponent(currentItem.id);
      const calData = {
        calendarID: calendarID,
        token: token
      };
      getAllEvents(calData, event => {
        calsChecked++;
        if (event) checkStatus(event, time.date);
        if (calsChecked >= (calendars.length * 3)) cb(roomStatus);
      }, timeMin.toISOString(), timeMax.toISOString());
    });
  });
}

export function checkRooms(data, cb) {
  const token = encodeURIComponent(data.token);
  let calsChecked = 0;
  const timeMin = data.timeMin || (new Date(theYear, theMonth, theDay, 9, 0, 0));
  const timeMax = data.timeMax || (new Date(theYear, theMonth, theDay, 18, 0, 0));
  const allMeetings = {
    date: data.timeMin,
    items: []
  };
  getCalendars(token, res => {
    let calendars = res;
    if (typeof(res) === 'object') calendars = objectToArray(res);
    calendars.map(item => {
      const currentItem = item;
      const calendarID = encodeURIComponent(currentItem.id);
      const calData = {
        calendarID: calendarID,
        token: token
      };
      getAllEvents(calData, event => {
        calsChecked++;
        if (event) {
          event.attendees.map(person => {
            if (person.displayName) {
              const roomResource = rooms.indexOf(person.displayName.toLowerCase());
              if ((roomResource > -1) && (event.location !== person.displayName)) {
                event.location += ' - ' + person.displayName;
              }
            }
          });
          allMeetings.items.push(event);
          if (moment(timeMin).isSame(moment(), 'day')) checkStatus(event);
        }
        if (calsChecked >= (calendars.length * 3)) cb(roomStatus, allMeetings);
      }, timeMin.toISOString(), timeMax.toISOString());
    });
  });
}

export function signInWithGoogle(cb) {
  FBRef.authWithOAuthPopup('google', (error, authData) => {
    let valid;
    let res;
    if (authData !== undefined) {
      valid = checkDomain(authData.google.email);
    }
    if (valid) {
      getSchedule(authData.google.accessToken, calEvents => {
        res = {
          error: error,
          authData: authData,
          valid: valid,
          schedule: calEvents
        };
        cb(res);
      });
    } else {
      res = {
        error: error,
        authData: authData,
        valid: valid
      };
      cb(res);
    }
  }, {
    scope: 'email,https://www.googleapis.com/auth/calendar'
  });
}

export function getUserList(cb) {
  return FBRef.child('meetingplanner/users').once('value', snapshot => {
    const users = objectToArray(snapshot.val());
    cb(users);
  });
}

export function persistUser(user) {
  const userId = user.google.email.replace(/[^0-9|a-z|A-Z]/g, '');
  FBRef.child('meetingplanner/users').child(userId).update({
    name: user.google.displayName,
    uid: user.google.id,
    img: user.google.profileImageURL,
    expires: user.expires,
    ...user.google,
    accessToken: null
  });
  FBRef.child('meetingplanner/calendars').child(userId).update({
    email: user.google.email
  });
}

export function saveReservation(res, user) {
  const userId = user.google.email.replace(/[^0-9|a-z|A-Z]/g, '');
  FBRef.child('meetingplanner/users').child(userId).child('schedule').push(res);
}

export function getSession(cb) {
  cb(FBRef.getAuth());
}

export function logOut() {
  FBRef.unauth();
}

<|endoftext|>


<commit_before>define(['app', 'jquery', 'underscore', 'toastr', 'ngStorage'],
 function (app, $, _) { 'use strict';

	app.factory('localStorageService',  ["$http","$location", "$rootScope","toastr", "$localStorage",
	 function($http,$location, $rootScope, toastr, $localStorage) {

		return new function(){

            this.get = function(key){
                var existing = angular.copy($localStorage[key]);

                if(!existing)
                    return;

                if(new Date() < new Date(existing.expiry))
                    return existing.data;

                //remove expired data from storage;
                console.log(key);
                this.remove(key);

                return;
            };

            this.set = function(key, data){
                var ldata = {};

                var expiryDate = new Date();
                expiryDate.setDate(expiryDate.getDate() + 2);

                ldata.expiry = expiryDate;
                ldata.data   = data;

                $localStorage[key] = ldata;
            };

            this.remove = function(key){
                delete $localStorage[key];
            };

            this.removeAll = function(){
                $localStorage.$reset();
            };

            this.hasStorageExpired = function(key, expiryDate){
                var today = new Date();
                return expiryDate.getFullYear() < today.getFullYear()
                    && expiryDate.getDate()     < today.getDate()
                    && expiryDate.getMonth()    < today.getMonth();
            };
		};

    }]);
});
<commit_msg>diable localstorage<commit_after>define(['app', 'jquery', 'underscore', 'toastr', 'ngStorage'],
 function (app, $, _) { 'use strict';

	app.factory('localStorageService',  ["$http","$location", "$rootScope","toastr", "$localStorage",
	 function($http,$location, $rootScope, toastr, $localStorage) {

		return new function(){

            this.get = function(key){
                return;

                var existing = angular.copy($localStorage[key]);

                if(!existing)
                    return;

                if(new Date() < new Date(existing.expiry))
                    return existing.data;

                //remove expired data from storage;
                console.log(key);
                this.remove(key);

                return;
            };

            this.set = function(key, data){
                var ldata = {};

                var expiryDate = new Date();
                expiryDate.setDate(expiryDate.getDate() + 2);

                ldata.expiry = expiryDate;
                ldata.data   = data;

                $localStorage[key] = ldata;
            };

            this.remove = function(key){
                delete $localStorage[key];
            };

            this.removeAll = function(){
                $localStorage.$reset();
            };

            this.hasStorageExpired = function(key, expiryDate){
                var today = new Date();
                return expiryDate.getFullYear() < today.getFullYear()
                    && expiryDate.getDate()     < today.getDate()
                    && expiryDate.getMonth()    < today.getMonth();
            };
		};

    }]);
});
<|endoftext|>


<commit_before>const _ = require('underscore');

function isApiKeyFound(apikey) {
    return apikey.type !== null &&
        apikey.user !== null &&
        apikey.databasePassword !== null &&
        apikey.databaseRole !== null;
}

function UserDatabaseService(metadataBackend) {
    this.metadataBackend = metadataBackend;
}

function errorUserNotFoundMessageTemplate (user) {
    return `Sorry, we can't find CARTO user '${user}'. Please check that you have entered the correct domain.`;
}

function isOauthAuthorization({ apikeyToken, authenticated }) {
    return authenticated && !apikeyToken;
}

/**
 * Callback is invoked with `dbParams` and `authDbParams`.
 * `dbParams` depends on AuthApi verification so it might return a public user with just SELECT permission, where
 * `authDbParams` will always return connection params as AuthApi had authorized the connection.
 * That might be useful when you have to run a query with and without permissions.
 *
 * @param {AuthApi} authApi
 * @param {String} cdbUsername
 * @param {Function} callback (err, dbParams, authDbParams)
 */
UserDatabaseService.prototype.getConnectionParams = function (username, apikeyToken, authenticated, callback) {
    this.metadataBackend.getAllUserDBParams(username, (err, dbParams) => {
        if (err) {
            err.http_status = 404;
            err.message = errorUserNotFoundMessageTemplate(username);

            return callback(err);
        }

        const commonDBConfiguration = {
            port: global.settings.db_port,
            host: dbParams.dbhost,
            dbname: dbParams.dbname,
        };

        this.metadataBackend.getMasterApikey(username, (err, masterApikey) => {

            if (err) {
                err.http_status = 404;
                err.message = errorUserNotFoundMessageTemplate(username);

                return callback(err);
            }

            if (!isApiKeyFound(masterApikey)) {
                const apiKeyNotFoundError = new Error('Unauthorized');
                apiKeyNotFoundError.type = 'auth';
                apiKeyNotFoundError.subtype = 'api-key-not-found';
                apiKeyNotFoundError.http_status = 401;

                return callback(apiKeyNotFoundError);
            }

            const masterDBConfiguration = Object.assign({
                user: masterApikey.databaseRole,
                pass: masterApikey.databasePassword
            },
                commonDBConfiguration);

            if (isOauthAuthorization({apikeyToken,authenticated})) {
                callback(null, masterDBConfiguration, masterDBConfiguration);
            }

            // Default Api key fallback
            apikeyToken = apikeyToken || 'default_public';

            this.metadataBackend.getApikey(username, apikeyToken, (err, apikey) => {
                if (err) {
                    err.http_status = 404;
                    err.message = errorUserNotFoundMessageTemplate(username);

                    return callback(err);
                }

                if (!isApiKeyFound(apikey)) {
                    const apiKeyNotFoundError = new Error('Unauthorized');
                    apiKeyNotFoundError.type = 'auth';
                    apiKeyNotFoundError.subtype = 'api-key-not-found';
                    apiKeyNotFoundError.http_status = 401;

                    return callback(apiKeyNotFoundError);
                }

                const DBConfiguration = Object.assign({
                    user: apikey.databaseRole,
                    pass: apikey.databasePassword
                },
                    commonDBConfiguration);

                callback(null, DBConfiguration, masterDBConfiguration);
            });
        });
    });
};

module.exports = UserDatabaseService;
<commit_msg>please jshint: remove unnecessary require (underscore)<commit_after>function isApiKeyFound(apikey) {
    return apikey.type !== null &&
        apikey.user !== null &&
        apikey.databasePassword !== null &&
        apikey.databaseRole !== null;
}

function UserDatabaseService(metadataBackend) {
    this.metadataBackend = metadataBackend;
}

function errorUserNotFoundMessageTemplate (user) {
    return `Sorry, we can't find CARTO user '${user}'. Please check that you have entered the correct domain.`;
}

function isOauthAuthorization({ apikeyToken, authenticated }) {
    return authenticated && !apikeyToken;
}

/**
 * Callback is invoked with `dbParams` and `authDbParams`.
 * `dbParams` depends on AuthApi verification so it might return a public user with just SELECT permission, where
 * `authDbParams` will always return connection params as AuthApi had authorized the connection.
 * That might be useful when you have to run a query with and without permissions.
 *
 * @param {AuthApi} authApi
 * @param {String} cdbUsername
 * @param {Function} callback (err, dbParams, authDbParams)
 */
UserDatabaseService.prototype.getConnectionParams = function (username, apikeyToken, authenticated, callback) {
    this.metadataBackend.getAllUserDBParams(username, (err, dbParams) => {
        if (err) {
            err.http_status = 404;
            err.message = errorUserNotFoundMessageTemplate(username);

            return callback(err);
        }

        const commonDBConfiguration = {
            port: global.settings.db_port,
            host: dbParams.dbhost,
            dbname: dbParams.dbname,
        };

        this.metadataBackend.getMasterApikey(username, (err, masterApikey) => {

            if (err) {
                err.http_status = 404;
                err.message = errorUserNotFoundMessageTemplate(username);

                return callback(err);
            }

            if (!isApiKeyFound(masterApikey)) {
                const apiKeyNotFoundError = new Error('Unauthorized');
                apiKeyNotFoundError.type = 'auth';
                apiKeyNotFoundError.subtype = 'api-key-not-found';
                apiKeyNotFoundError.http_status = 401;

                return callback(apiKeyNotFoundError);
            }

            const masterDBConfiguration = Object.assign({
                user: masterApikey.databaseRole,
                pass: masterApikey.databasePassword
            },
                commonDBConfiguration);

            if (isOauthAuthorization({apikeyToken,authenticated})) {
                callback(null, masterDBConfiguration, masterDBConfiguration);
            }

            // Default Api key fallback
            apikeyToken = apikeyToken || 'default_public';

            this.metadataBackend.getApikey(username, apikeyToken, (err, apikey) => {
                if (err) {
                    err.http_status = 404;
                    err.message = errorUserNotFoundMessageTemplate(username);

                    return callback(err);
                }

                if (!isApiKeyFound(apikey)) {
                    const apiKeyNotFoundError = new Error('Unauthorized');
                    apiKeyNotFoundError.type = 'auth';
                    apiKeyNotFoundError.subtype = 'api-key-not-found';
                    apiKeyNotFoundError.http_status = 401;

                    return callback(apiKeyNotFoundError);
                }

                const DBConfiguration = Object.assign({
                    user: apikey.databaseRole,
                    pass: apikey.databasePassword
                },
                    commonDBConfiguration);

                callback(null, DBConfiguration, masterDBConfiguration);
            });
        });
    });
};

module.exports = UserDatabaseService;
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */

import React from 'react';

import PropTypes from 'prop-types';

import { connect } from 'react-redux';

import { selectUsingVaccines } from '../selectors/modules';
import { SyncState } from './SyncState';
import { FlexRow } from './FlexRow';
import { TemperatureSyncState } from './TemperatureSyncState';

const mapStateToProps = state => {
  const usingVaccines = selectUsingVaccines(state);

  return { usingVaccines };
};

const HeaderRightComponent = ({ usingVaccines }) => (
  <FlexRow>
    <SyncState />
    {usingVaccines && <TemperatureSyncState />}
  </FlexRow>
);

HeaderRightComponent.propTypes = {
  usingVaccines: PropTypes.bool.isRequired,
};

export const HeaderRight = connect(mapStateToProps)(HeaderRightComponent);
<commit_msg>Add switch sync icons around<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */

import React from 'react';

import PropTypes from 'prop-types';

import { connect } from 'react-redux';

import { selectUsingVaccines } from '../selectors/modules';
import { SyncState } from './SyncState';
import { FlexRow } from './FlexRow';
import { TemperatureSyncState } from './TemperatureSyncState';

const mapStateToProps = state => {
  const usingVaccines = selectUsingVaccines(state);

  return { usingVaccines };
};

const HeaderRightComponent = ({ usingVaccines }) => (
  <FlexRow>
    {usingVaccines && <TemperatureSyncState />}
    <SyncState />
  </FlexRow>
);

HeaderRightComponent.propTypes = {
  usingVaccines: PropTypes.bool.isRequired,
};

export const HeaderRight = connect(mapStateToProps)(HeaderRightComponent);
<|endoftext|>


<commit_before>/* eslint-disable react/forbid-prop-types */
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View, FlatList, TouchableOpacity } from 'react-native';
import Cell from './DataTable/Cell';
import { dataTableStyles } from '../globalStyles/index';
import { HeaderCell, HeaderRow } from './DataTable/index';
import { getItemLayout, recordKeyExtractor } from '../pages/dataTableUtilities';

/**
 * Simple table component for rendering a large list of un-changing data.
 * Only offers a selection feature.
 *
 * Usage:
 * Pass an array - data: [ { id, .. }, { id, .. }, .. ] (Also accepts a Realm Results array)
 * Also an array - columns: [ { key, alignText, title } ] (example: getColumns.js)
 * Such that each column.key is a field within an object in the data array.
 *
 * selectRow should be a function: (rowId) => { ... }
 *
 * extraData, an object with the shape: {rowId1: [bool], rowId2: [bool], ... } used
 * to trigger styling effects on selected rows.
 */
export const SimpleTable = React.memo(
  React.forwardRef(({ data, columns, selectRow, extraData }, ref) => {
    const {
      cellText,
      cellContainer,
      selectedRow: selectedRowStyle,
      alternateRow: alternateRowStyle,
      row: basicRowStyle,
      headerRow,
      headerCells,
    } = dataTableStyles;

    const renderCells = useCallback(rowData => {
      const { id } = rowData;

      return columns.map(({ key, width, alignText }, index) => (
        <Cell
          value={rowData[key]}
          viewStyle={cellContainer[alignText]}
          textStyle={cellText[alignText]}
          isLastCell={index === columns.length - 1}
          width={width}
          key={`${id}${key}`}
        />
      ));
    }, []);

    const renderRow = useCallback(
      ({ item, index }) => {
        const { id } = item;
        const isSelected = extraData?.[id];
        const rowStyle = isSelected
          ? selectedRowStyle
          : (index % 2 === 0 && alternateRowStyle) || basicRowStyle;

        return (
          <SimpleRow
            rowData={item}
            rowIndex={index}
            rowKey={id}
            style={rowStyle}
            renderCells={renderCells}
            onPress={selectRow}
            isSelected={isSelected}
          />
        );
      },
      [extraData]
    );

    const renderHeaderCells = useCallback(
      () =>
        columns.map(({ title, width, alignText }, index) => (
          <HeaderCell
            key={title}
            title={title}
            containerStyle={headerCells[alignText]}
            textStyle={cellText[alignText]}
            isLastCell={index === columns.length - 1}
            width={width}
          />
        )),
      [columns]
    );

    const renderHeaders = useCallback(
      () => <HeaderRow style={headerRow} columns={columns} renderCells={renderHeaderCells} />,
      [columns]
    );

    return (
      <FlatList
        ref={ref}
        data={data}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
        renderItem={renderRow}
        stickyHeaderIndices={[0]}
        ListHeaderComponent={renderHeaders}
        extraData={extraData}
      />
    );
  })
);

SimpleTable.defaultProps = {
  extraData: {},
};

SimpleTable.propTypes = {
  data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
  columns: PropTypes.array.isRequired,
  selectRow: PropTypes.func.isRequired,
  extraData: PropTypes.object,
};

const SimpleRow = React.memo(({ rowData, style, rowKey, renderCells, onPress }) => {
  const onSelect = useCallback(() => onPress(rowKey), [rowKey, onPress]);
  return (
    <TouchableOpacity onPress={onSelect} key={rowKey}>
      <View style={style}>{renderCells(rowData)}</View>
    </TouchableOpacity>
  );
});

SimpleRow.defaultProps = {
  onPress: null,
};

SimpleRow.propTypes = {
  rowData: PropTypes.object.isRequired,
  rowKey: PropTypes.string.isRequired,
  style: PropTypes.object.isRequired,
  renderCells: PropTypes.func.isRequired,
  onPress: PropTypes.func,
};
<commit_msg>Add disabling logging to rows in SimpleTable<commit_after>/* eslint-disable react/no-unused-prop-types */
/* eslint-disable react/forbid-prop-types */
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { View, FlatList, TouchableOpacity } from 'react-native';
import Cell from './DataTable/Cell';
import { dataTableStyles, GREY } from '../globalStyles/index';
import { HeaderCell, HeaderRow, TouchableNoFeedback } from './DataTable/index';
import { getItemLayout, recordKeyExtractor } from '../pages/dataTableUtilities';

/**
 * Simple table component for rendering a large list of un-changing data.
 * Only offers a selection feature.
 *
 * Usage:
 * Pass an array - data: [ { id, .. }, { id, .. }, .. ] (Also accepts a Realm Results array)
 * Also an array - columns: [ { key, alignText, title } ] (example: getColumns.js)
 * Such that each column.key is a field within an object in the data array.
 *
 * selectRow should be a function: (rowId) => { ... }
 *
 * selectedRows, an object with the shape: {rowId1: [bool], rowId2: [bool], ... } used
 * to trigger styling effects on selected rows.
 */
export const SimpleTable = React.memo(
  React.forwardRef(({ data, columns, selectRow, selectedRows, disabledRows }, ref) => {
    const {
      cellText,
      cellContainer,
      selectedRow: selectedRowStyle,
      alternateRow: alternateRowStyle,
      row: basicRowStyle,
      headerRow,
      headerCells,
    } = dataTableStyles;
    const renderCells = useCallback(
      rowData => {
        const { id } = rowData;

        return columns.map(({ key, width, alignText }, index) => {
          const disabledStyle = disabledRows?.[id] ? { color: GREY } : {};
          const textStyle = { ...cellText[alignText], ...disabledStyle };
          return (
            <Cell
              value={rowData[key]}
              viewStyle={cellContainer[alignText]}
              textStyle={textStyle}
              isLastCell={index === columns.length - 1}
              width={width}
              key={`${id}${key}`}
            />
          );
        });
      },
      [disabledRows]
    );

    const renderRow = useCallback(
      ({ item, index }) => {
        const { id } = item;
        const isDisabled = disabledRows?.[id];
        const isSelected = selectedRows?.[id];
        const rowStyle = isSelected
          ? selectedRowStyle
          : (index % 2 === 0 && alternateRowStyle) || basicRowStyle;

        return (
          <SimpleRow
            rowData={item}
            rowIndex={index}
            rowKey={id}
            style={rowStyle}
            renderCells={renderCells}
            onPress={isDisabled ? null : selectRow}
            isSelected={isSelected}
          />
        );
      },
      [selectedRows, disabledRows]
    );

    const renderHeaderCells = useCallback(
      () =>
        columns.map(({ title, width, alignText }, index) => (
          <HeaderCell
            key={title}
            title={title}
            containerStyle={headerCells[alignText]}
            textStyle={cellText[alignText]}
            isLastCell={index === columns.length - 1}
            width={width}
          />
        )),
      [columns]
    );

    const renderHeaders = useCallback(
      () => <HeaderRow style={headerRow} columns={columns} renderCells={renderHeaderCells} />,
      [columns]
    );

    return (
      <FlatList
        ref={ref}
        data={data}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
        renderItem={renderRow}
        stickyHeaderIndices={[0]}
        ListHeaderComponent={renderHeaders}
        extraData={selectedRows}
      />
    );
  })
);

SimpleTable.defaultProps = {
  selectedRows: {},
  disabledRows: null,
};

SimpleTable.propTypes = {
  data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
  columns: PropTypes.array.isRequired,
  selectRow: PropTypes.func.isRequired,
  selectedRows: PropTypes.object,
  disabledRows: PropTypes.object,
};

const SimpleRow = React.memo(({ rowData, style, rowKey, renderCells, onPress }) => {
  const onSelect = useCallback(() => onPress(rowKey), [rowKey, onPress]);
  const Container = onPress ? TouchableOpacity : TouchableNoFeedback;
  return (
    <Container onPress={onSelect} key={rowKey}>
      <View style={style}>{renderCells(rowData)}</View>
    </Container>
  );
});

SimpleRow.defaultProps = {
  onPress: null,
};

SimpleRow.propTypes = {
  rowData: PropTypes.object.isRequired,
  rowKey: PropTypes.string.isRequired,
  style: PropTypes.object.isRequired,
  renderCells: PropTypes.func.isRequired,
  onPress: PropTypes.func,
};
<|endoftext|>


<commit_before>(function (global) {
    var MediaViewModel,
        app = global.app = global.app || {};

    MediaViewModel = kendo.data.ObservableObject.extend({
        skipMedia: function() {
            kendo.mobile.application.navigate("#tabstrip-home");
            var tabstrip = $("#footer-tabstrip").data("kendoMobileTabStrip");
            tabstrip.switchTo("#tabstrip-home");
        },

        takePicture: function() {
            document.addEventListener("deviceready", onDeviceReady, false);

            function onDeviceReady() {

            navigator.camera.getPicture(
                uploadPhoto,
                function(message) {
                    navigator.notification.alert(message,
                        function () { }, "Camera failed", 'OK');
                },
                {
                    destinationType : navigator.camera.DestinationType.FILE_URI,
                    sourceType      : navigator.camera.PictureSourceType.CAMERA,
                    saveToPhotoAlbum : true
                }
                );
            }

            function uploadPhoto(imageURI) {
                var options = new FileUploadOptions();
                options.fileKey="image";
                options.fileName=imageURI.substr(imageURI.lastIndexOf('/')+1);
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                var ft = new FileTransfer();
                options.params = params;
                kendo.mobile.application.showLoading();
                ft.upload(imageURI, encodeURI(app.serverEndpoint + "attachments/pictures"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            // Where innocent kittens die
            function fail(error) {
                kendo.mobile.application.hideLoading();
                navigator.notification.alert("Unable to upload picture.",
                        function () { }, "Upload picture failed", 'OK');
            }

        },

        takeVideo: function() {
            navigator.device.capture.captureVideo(
                captureSuccess, captureFail, {limit: 1, duration: 15});

            function captureSuccess(mediaFiles) {
                for (i = 0, len = mediaFiles.length; i < len; i += 1) {
                    uploadVideo(mediaFiles[i]);
                }
                app.mediaService.viewModel.skipMedia();
            }

            function captureFail(error) {
                navigator.notification.alert("Unable to take video.",
                        function () { }, "Taking video failed", 'OK');
            }

            function uploadVideo(mediaFile) {
                var options = new FileUploadOptions();
                options.fileKey="video";
                options.fileName=mediaFile.name;
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                options.params = params;
                var ft = new FileTransfer();
                kendo.mobile.application.showLoading();
                ft.upload(mediaFile.fullPath, encodeURI(app.serverEndpoint + "attachments/videos"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            function fail(error) {
                kendo.mobile.application.hideLoading();
                navigator.notification.alert("Unable to upload video.",
                        function () { }, "Uploading video failed", 'OK');
            }
        },

        recordAudio: function() {
            navigator.device.capture.captureAudio(captureSuccess, captureFail, {limit: 1, duration: 15});

            function captureSuccess(mediaFiles) {
                for (i = 0, len = mediaFiles.length; i < len; i += 1) {
                    uploadAudio(mediaFiles[i]);
                }

                app.mediaService.viewModel.skipMedia();
            }

            function captureFail(error) {
                navigator.notification.alert("Unable to record audio.",
                        function () { }, "Recording audio failed", 'OK');
            }

            function uploadAudio(mediaFile) {
                var options = new FileUploadOptions();
                options.fileKey="audio";
                options.fileName=mediaFile.name;
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                options.params = params;
                var ft = new FileTransfer();
                kendo.mobile.application.showLoading();
                ft.upload(mediaFile.fullPath, encodeURI(app.serverEndpoint + "attachments/audios"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            function fail(error) {
                kendo.mobile.application.hideLoading();
                navigator.notification.alert("Unable to upload audio.",
                        function () { }, "Uploading audio failed", 'OK');
            }
        },

        setID: function(id) {
            var that = this;
            that.set("id", id);
        }
    });

    app.mediaService = {
        viewModel: new MediaViewModel(),

        show: function (e) {
            if(e.view.params.id) {
                app.mediaService.viewModel.setID(e.view.params.id);
            }
        }

    };
})(window);
<commit_msg>Custom messages for file transfer errors<commit_after>(function (global) {
    var MediaViewModel,
        app = global.app = global.app || {};

    MediaViewModel = kendo.data.ObservableObject.extend({
        skipMedia: function() {
            kendo.mobile.application.navigate("#tabstrip-home");
            var tabstrip = $("#footer-tabstrip").data("kendoMobileTabStrip");
            tabstrip.switchTo("#tabstrip-home");
        },

        takePicture: function() {
            document.addEventListener("deviceready", onDeviceReady, false);

            function onDeviceReady() {

            navigator.camera.getPicture(
                uploadPhoto,
                function(message) {
                    navigator.notification.alert(message,
                        function () { }, "Camera failed", 'OK');
                },
                {
                    destinationType : navigator.camera.DestinationType.FILE_URI,
                    sourceType      : navigator.camera.PictureSourceType.CAMERA,
                    saveToPhotoAlbum : true
                }
                );
            }

            function uploadPhoto(imageURI) {
                var options = new FileUploadOptions();
                options.fileKey="image";
                options.fileName=imageURI.substr(imageURI.lastIndexOf('/')+1);
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                var ft = new FileTransfer();
                options.params = params;
                kendo.mobile.application.showLoading();
                ft.upload(imageURI, encodeURI(app.serverEndpoint + "attachments/pictures"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            // Where innocent kittens die
            function fail(error) {
                kendo.mobile.application.hideLoading();
                app.mediaService.fail(error, "image");
            }

        },

        takeVideo: function() {
            navigator.device.capture.captureVideo(
                captureSuccess, captureFail, {limit: 1, duration: 15});

            function captureSuccess(mediaFiles) {
                for (i = 0, len = mediaFiles.length; i < len; i += 1) {
                    uploadVideo(mediaFiles[i]);
                }
                app.mediaService.viewModel.skipMedia();
            }

            function captureFail(error) {
                navigator.notification.alert("Unable to take video.",
                        function () { }, "Taking video failed", 'OK');
            }

            function uploadVideo(mediaFile) {
                var options = new FileUploadOptions();
                options.fileKey="video";
                options.fileName=mediaFile.name;
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                options.params = params;
                var ft = new FileTransfer();
                kendo.mobile.application.showLoading();
                ft.upload(mediaFile.fullPath, encodeURI(app.serverEndpoint + "attachments/videos"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            function fail(error) {
                kendo.mobile.application.hideLoading();
                app.mediaService.fail(error, "video");
            }
        },

        recordAudio: function() {
            navigator.device.capture.captureAudio(captureSuccess, captureFail, {limit: 1, duration: 15});

            function captureSuccess(mediaFiles) {
                for (i = 0, len = mediaFiles.length; i < len; i += 1) {
                    uploadAudio(mediaFiles[i]);
                }

                app.mediaService.viewModel.skipMedia();
            }

            function captureFail(error) {
                navigator.notification.alert("Unable to record audio.",
                        function () { }, "Recording audio failed", 'OK');
            }

            function uploadAudio(mediaFile) {
                var options = new FileUploadOptions();
                options.fileKey="audio";
                options.fileName=mediaFile.name;
                options.mimeType="multipart/form-data";

                var params = {};
                params.performance_id = app.mediaService.viewModel.get("id");

                options.params = params;
                var ft = new FileTransfer();
                kendo.mobile.application.showLoading();
                ft.upload(mediaFile.fullPath, encodeURI(app.serverEndpoint + "attachments/audios"), success, fail, options);
            }

            function success(result) {
                kendo.mobile.application.hideLoading();
                app.mediaService.viewModel.skipMedia();
            }

            function fail(error) {
                kendo.mobile.application.hideLoading();
                app.mediaService.fail(error, "audio");
            }
        },

        setID: function(id) {
            var that = this;
            that.set("id", id);
        }
    });

    app.mediaService = {
        viewModel: new MediaViewModel(),

        show: function (e) {
            if(e.view.params.id) {
                app.mediaService.viewModel.setID(e.view.params.id);
            }
        },

        fail: function(error, type) {
         switch (error.code) {
            case FileTransferError.FILE_NOT_FOUND_ERR:
                navigator.notification.alert(type + " file not found.",
                        function () { }, "Uploading " + type + " failed", 'OK');
                break;
            case FileTransferError.INVALID_URL_ERR:
                navigator.notification.alert("Bad " + type + " URL.",
                        function () { }, "Uploading " + type + " failed", 'OK');
                break;
            case FileTransferError.CONNECTION_ERR:
                navigator.notification.alert("Connection error. Check your internet connection!",
                        function () { }, "Uploading " + type + " failed", 'OK');
                break;
            }
        }

    };
})(window);
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const express = require('express');
const https = require('https');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const openBrowser = require('react-dev-utils/openBrowser');

const config = require('../config');
const paths = require('../config/paths');
const webpackConfig = require('../config/webpack.dev.conf');
const shopify = require('../lib/shopify-deploy');

const fakeCert = fs.readFileSync(path.join(__dirname, '../ssl/server.pem'));
const sslOptions = {
  key: fakeCert,
  cert: fakeCert,
};

const app = express();
const server = https.createServer(sslOptions, app);
const compiler = webpack(webpackConfig);

const shopifyUrl = `https://${config.shopify.development.store}`;
const previewUrl = `${shopifyUrl}?preview_theme_id=${config.shopify.development.theme_id}`;
app.use((req, res, next) => {
  res.set('Access-Control-Allow-Origin', shopifyUrl);
  next();
});

app.use(webpackDevMiddleware(compiler, {
  noInfo: true,
  reload: false,
}));

const hotMiddleware = webpackHotMiddleware(compiler);
app.use(hotMiddleware);

compiler.plugin('done', (stats) => {
  let files = [];

  Object.keys(stats.compilation.assets).forEach((key) => {
    const asset = stats.compilation.assets[key];

    if (asset.emitted) {
      // webpack-dev-server doesn't write assets to disk, see webpack.base.conf.js
      // where we use WriteFileWebpackPlugin to write certain assets to disk
      // (the ones to be uploaded) (the others are served from memory)
      if (fs.existsSync(asset.existsAt)) {
        files = [...files, asset.existsAt.replace(paths.dist, '')];
      }
    }
  });

  shopify.sync({ upload: files }).then(() => {
    // Do not warn about updating theme.liquid, it's also updated when styles
    // and scripts are updated.
    if (files.length === 1 && files[0] === '/layout/theme.liquid') {
      return;
    }

    hotMiddleware.publish({ action: 'shopify_upload_finished' });
  // eslint-disable-next-line
  }).catch((err) => console.log(err || 'Could not deploy to Shopify.'));
});

server.listen(config.port, (err) => {
  if (err) {
    console.log(chalk.red(err));
    return;
  }

  openBrowser(previewUrl);
});
<commit_msg>Better logging of what's going on, with colors<commit_after>/* eslint-disable no-console */
const fs = require('fs');
const chalk = require('chalk');
const path = require('path');
const express = require('express');
const https = require('https');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const openBrowser = require('react-dev-utils/openBrowser');
const clearConsole = require('react-dev-utils/clearConsole');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');

const config = require('../config');
const paths = require('../config/paths');
const webpackConfig = require('../config/webpack.dev.conf');
const shopify = require('../lib/shopify-deploy');

const fakeCert = fs.readFileSync(path.join(__dirname, '../ssl/server.pem'));
const sslOptions = {
  key: fakeCert,
  cert: fakeCert,
};

const app = express();
const server = https.createServer(sslOptions, app);
const compiler = webpack(webpackConfig);

const shopifyUrl = `https://${config.shopify.development.store}`;
const previewUrl = `${shopifyUrl}?preview_theme_id=${config.shopify.development.theme_id}`;

function getFilesFromAssets(assets) {
  let files = [];

  Object.keys(assets).forEach((key) => {
    const asset = assets[key];

    if (asset.emitted) {
      // webpack-dev-server doesn't write assets to disk, see webpack.base.conf.js
      // where we use WriteFileWebpackPlugin to write certain assets to disk
      // (the ones to be uploaded) (the others are served from memory)
      if (fs.existsSync(asset.existsAt)) {
        files = [...files, asset.existsAt.replace(paths.dist, '')];
      }
    }
  });

  return files;
}

app.use((req, res, next) => {
  res.set('Access-Control-Allow-Origin', shopifyUrl);
  next();
});

app.use(webpackDevMiddleware(compiler, {
  quiet: true,
  reload: false,
}));

const hotMiddleware = webpackHotMiddleware(compiler);
app.use(hotMiddleware);

compiler.plugin('invalid', () => {
  clearConsole();
  console.log('Compiling...');
});

compiler.plugin('done', (stats) => {
  clearConsole();

  // webpack messages massaging and logging gracioulsy provided by create-react-app.
  const messages = formatWebpackMessages(stats.toJson({}, true));

  // If errors exist, only show errors.
  if (messages.errors.length) {
    console.log(chalk.red('Failed to compile.'));
    console.log();
    messages.errors.forEach((message) => {
      console.log(message);
      console.log();
    });
    return;
  }

  // Show warnings if no errors were found.
  if (messages.warnings.length) {
    console.log(chalk.yellow('Compiled with warnings.'));
    console.log();
    messages.warnings.forEach((message) => {
      console.log(message);
      console.log();
    });
    // Teach some ESLint tricks.
    console.log('You may use special comments to disable some warnings.');
    console.log(`Use ${chalk.yellow('// eslint-disable-next-line')} to ignore the next line.`);
    console.log(`Use ${chalk.yellow('/* eslint-disable */')} to ignore all warnings in a file.`);
  }

  if (!messages.errors.length && !messages.warnings.length) {
    console.log(chalk.green('Compiled successfully!'));
    console.log();
    console.log('The app is running at:');
    console.log();
    console.log(`  ${chalk.cyan(previewUrl)}`);
    console.log();
  }

  const files = getFilesFromAssets(stats.compilation.assets);

  console.log(chalk.cyan('Uploading files to Shopify...'));
  console.log();
  files.forEach((file) => {
    console.log(`  ${file}`);
  });
  console.log();

  shopify.sync({ upload: files }).then(() => {
    // Do not warn about updating theme.liquid, it's also updated when styles
    // and scripts are updated.
    if (files.length === 1 && files[0] === '/layout/theme.liquid') {
      return;
    }

    hotMiddleware.publish({ action: 'shopify_upload_finished' });

    console.log();
    console.log(chalk.green('Files uploaded successfully!'));
    console.log();
  }).catch((err) => {
    console.log(chalk.red(err));
  });
});

server.listen(config.port, (err) => {
  if (err) {
    console.log(chalk.red(err));
    return;
  }

  openBrowser(previewUrl);
});
<|endoftext|>