<commit_before>/* jshint esversion: 6 */
/* jshint node: true */
'use strict';

var q = require('q');
var hubsConfig = require("./hubsConfig");
var OpenT2TErrorClass = require('opent2t').OpenT2TError;

class HubController {

    constructor() {
        this.supportedHubsCache = undefined;
        this.OpenT2T = require('opent2t').OpenT2T;
    }

    /**
     * returns the list of supported hubs
     * format is as follows:
     * [
     *   {
     *     id: "hubId",
     *     name: hubName",
     *     translator: "name of hub translator",
     *     onboarding: "name of hub onboarding",
     *     // this is the onboardingFlow object as defined by the hub's manifest.xml'
     *     onboardingFlow: {}
     *   }
     * ]
     */
    supportedHubs(hubs, i) {
        // use cache if we have it
        if (this.supportedHubsCache !== undefined) {
            return q(this.supportedHubsCache);
        }

        // this is a recursive function, setup the initial call with initial values
        if (hubs === undefined) {
            hubs = hubsConfig.hubs;
        }

        if (i == undefined) {
            console.log("----------------- supportedHubs");
            i = 0;
        }

        // load info for the current hub
        var hubInfo = hubs[i];
        console.log("------------------------------");
        console.log("i: " + i);

        var LocalPackageSourceClass = require('opent2t/package/LocalPackageSource').LocalPackageSource;
        var localPackageSource = new LocalPackageSourceClass("./node_modules/" + hubInfo.translator);

        return localPackageSource.getAllPackageInfoAsync().then((packages) => {

            // default use the first package
            var p = packages[0];
            if (p.translators.length > 0) {

                var tinfo = p.translators[0];
                console.log("----------------------------- Package Info");
                console.log(tinfo);
                console.log("-----------------------------");

                hubInfo.onboarding = tinfo.onboarding;
                hubInfo.onboardingFlow = tinfo.onboardingFlow;
            }

            // we are done, save as the cache and return
            if (i == hubs.length - 1) {
                this.supportedHubsCache = hubs;
                return this.supportedHubsCache;
            }
            // we are not done, recurse to the next hub
            else {
                return this.supportedHubs(hubs, i + 1);
            }
        }).catch((err) => {
            return this._handleError(err, "supportedHubs");
        });
    }

    /**
     * given a specific hub info, does the onboarding given the onboardingInfo and returns the auth info
     */
    onboard(hubId, onboardingInfo) {
        console.log("----------------- onboard");
        return this._getHubInfo(hubId).then((hubInfo) => {
            // do the onboarding and return token
            var Onboarding = require(hubInfo.onboarding);
            var onboarding = new Onboarding();
            return onboarding.onboard(onboardingInfo);
        }).catch((err) => {
            return this._handleError(err, "onboard");
        });
    }

    /**
     * Given a specific hub info, onboardingInfo, and existing authInfo blob,
     *  does the OAuthToken refresh, and returns the refreshed
     * auth info object back.
     */
    refreshAuthToken(hubId, onboardingInfo, existingAuthInfo){
        console.log("----------------- refreshAuthToken");

        return this._getHubInfo(hubId).then((hubInfo) => {

            // create hub translator for given hubId
            return this._createTranslator(hubInfo.translator, existingAuthInfo).then((hubInstance) => {

                // hub refreshAuthToken
                return this._invokeMethod(hubInstance, "", "refreshAuthToken", [onboardingInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "refreshAuthToken");
        });
    }

    /**
     * given the specific hub id, returns all the platforms which are connected to it
     */
    platforms(hubId, authInfo) {
        console.log("----------------- platforms");
        // will return hub getPlatform contents
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // hub get
                return this._invokeMethod(hubInstance, authInfo, "getPlatforms", [true]);
            });
        }).catch((err) => {
            return this._handleError(err, "platforms");
        });
    }

    /**
     * given the specific hub id and opent2tblob, returns the specific platform
     */
    getPlatform(hubId, authInfo, opent2tBlob) {
        console.log("----------------- getPlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // platform get
                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;
                return this._invokeMethod(opent2tBlob.translator, deviceInfo, "get", [true]);
            });
        }).catch((err) => {
           return this._handleError(err, "getPlatform");
        });
    }

    /**
     * given the specific hub id, opent2tblob, and resourceId, sets it with the given resourceBlob
     */
    setResource(hubId, authInfo, opent2tBlob, deviceId, resoureceId, resourceBlob) {
        console.log("----------------- setResource");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // resource set
                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                var propertyName = "postDevices" + this._capitalizeFirstLetter(resoureceId);

                return this._setProperty(opent2tBlob.translator, deviceInfo, propertyName, deviceId, resourceBlob);
            });
        }).catch((err) => {
            return this._handleError(err, "setResource");
        });
    }

    /**
     * Subscribe for notifications on all resources composing a platform.  Notifications will be posted to to
     * the subscriptionInfo.callbackURL.
     */
    subscribePlatform(hubId, authInfo, opent2tBlob, subscriptionInfo) {
        console.log("----------------- subscribePlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                return this._createTranslator(opent2tBlob.translator, deviceInfo).then(translator => {
                    return this.OpenT2T.invokeMethodAsync(translator, "", "postSubscribe", [subscriptionInfo]);
                });
            });
        }).catch((err) => {
            return this._handleError(err, "subscribePlatform");
        });
    }

    /**
     * Unsubscribe notification on all resources from a platform.
     */
    unsubscribePlatform(hubId, authInfo, opent2tBlob, subscriptionInfo) {
        console.log("----------------- unsubscribePlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                return this._createTranslator(opent2tBlob.translator, deviceInfo).then(translator => {
                    return this.OpenT2T.invokeMethodAsync(translator, "", "deleteSubscribe", [subscriptionInfo]);
                });
            });
        }).catch((err) => {
            return this._handleError(err, "unsubscribePlatform");
        });
    }

    /**
     * Verification step for cloud notifications for providers that require it.
     */
    subscribeVerify(hubId, authInfo, verificationBlob) {
        console.log("----------------- subscribeVerify");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var subscriptionInfo = {};
                subscriptionInfo.verificationRequest = verificationBlob;

                return this.OpenT2T.invokeMethodAsync(hubInstance, "", "postSubscribe", [subscriptionInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "subscribePlatformVerify");
        });
    }

    /**
     * Translate a JSON blob from a provider into an opent2t/OCF schema.  This should be called with the contents of
     * the notification post backs.  Verification is an optional object providing a secret and a hash for verification of the payload.
     * Returns an array of translated platforms, even for a single item (size 1 obviously)
     *
     * @param {Object} verificationInfo
     * @param {string} verificationInfo.key - Secret key used to compute HMAC
     * @param {Object} verificationInfo.header - Headers from the notification which will contain a provider specific HMAC.
     */
    translatePlatforms(hubId, authInfo, providerBlob, verificationInfo) {
        console.log("----------------- translatePlatforms");

        // Create a hub, of the requested type.
        return this._getHubInfo(hubId).then((hubInfo) => {
            // Pass the provider blob off to the hub for translation.
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // The getPlatforms method on the hub can take either single providerSchema, or a list depending
                // on the service that provided the notification.  It's up the the hub to know what to do with the data.
                return this.OpenT2T.invokeMethodAsync(hubInstance, "", "getPlatforms", [true, providerBlob, verificationInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "translatePlatforms");
        });
    }

    /**
     * helper methods
     */
    _getHubInfo(hubId) {
        console.log("----------------- _getHubInfo");
        return this.supportedHubs().then((hubs) => {
            // find the hub referenced by hubId
            var hubInfo = undefined;
            for (var i = 0; hubInfo == undefined && i < hubs.length; i++) {
                var hub = hubs[i];

                // intentional ==
                if (hub.id == hubId) {
                    hubInfo = hub;
                }
            }

            if (hubInfo == undefined) {
                console.log("invalid hub id");
                throw new OpenT2TErrorClass(400, "Invalid hub id");
            }

            return hubInfo;
        });
    }

    _invokeMethod(translator, deviceInfo, methodName, params) {
        console.log("----------------- _invokeMethod " + methodName + " with params "+ JSON.stringify(params));

        if (typeof translator === "object") {
            return this.OpenT2T.invokeMethodAsync(translator, "", methodName, params);
        }
        else {
            return this._createTranslator(translator, deviceInfo).then(translatorInstance => {
                return this.OpenT2T.invokeMethodAsync(translatorInstance, "", methodName, params);
            });
        }
    }

    _setProperty(translatorName, deviceInfo, property, deviceId, value) {
        console.log("----------------- _setProperty " + translatorName + " for " + property + " to:");
        console.log(JSON.stringify(value, null, 2));

        return this._createTranslator(translatorName, deviceInfo).then(translator => {
            return this.OpenT2T.invokeMethodAsync(translator, "", property, [deviceId, value]);
        });
    }

    _capitalizeFirstLetter(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    }

    _createTranslator(translatorName, deviceInfo) {
        console.log("----------------- _createTranslator " + translatorName);
        return this.OpenT2T.createTranslatorAsync(translatorName, deviceInfo).then( translator => {
            return translator;
        });
    }

    _handleError(err, message) {
        let customMessage = "OpenT2T call failed in: " + message + "; Original message: ";

        // This was a result of a failed HTTP Request promise
        // Can also check err.Name
        if ('response' in err && 'statusMessage' in err.response) {
            customMessage = customMessage + err.response.statusMessage;
        }
        else {
            // Likely an simple Error-derived class
            customMessage = customMessage + err.message;
        }

        let customError = new OpenT2TErrorClass(err.statusCode, customMessage, err);
        console.log(" custom error message:           " + customError.message);
        console.log(" custom error name:           " + customError.name);
        console.log(" custom error statusCode:           " + customError.statusCode);
        console.log(" custom error innerError message:           " +  customError.innerError.message);
        console.log(" custom error innerError stack:           " +  customError.innerError.stack);

        return q.reject(customError);
    }
}

module.exports = HubController;<commit_msg>Adding reference to OpenT2TConstants<commit_after>/* jshint esversion: 6 */
/* jshint node: true */
'use strict';

var q = require('q');
var hubsConfig = require("./hubsConfig");
var OpenT2TError = require('opent2t').OpenT2TError;
var OpenT2TConstants = require('opent2t').OpenT2TConstants;

class HubController {

    constructor() {
        this.supportedHubsCache = undefined;
        this.OpenT2T = require('opent2t').OpenT2T;
    }

    /**
     * returns the list of supported hubs
     * format is as follows:
     * [
     *   {
     *     id: "hubId",
     *     name: hubName",
     *     translator: "name of hub translator",
     *     onboarding: "name of hub onboarding",
     *     // this is the onboardingFlow object as defined by the hub's manifest.xml'
     *     onboardingFlow: {}
     *   }
     * ]
     */
    supportedHubs(hubs, i) {
        // use cache if we have it
        if (this.supportedHubsCache !== undefined) {
            return q(this.supportedHubsCache);
        }

        // this is a recursive function, setup the initial call with initial values
        if (hubs === undefined) {
            hubs = hubsConfig.hubs;
        }

        if (i == undefined) {
            console.log("----------------- supportedHubs");
            i = 0;
        }

        // load info for the current hub
        var hubInfo = hubs[i];
        console.log("------------------------------");
        console.log("i: " + i);

        var LocalPackageSourceClass = require('opent2t/package/LocalPackageSource').LocalPackageSource;
        var localPackageSource = new LocalPackageSourceClass("./node_modules/" + hubInfo.translator);

        return localPackageSource.getAllPackageInfoAsync().then((packages) => {

            // default use the first package
            var p = packages[0];
            if (p.translators.length > 0) {

                var tinfo = p.translators[0];
                console.log("----------------------------- Package Info");
                console.log(tinfo);
                console.log("-----------------------------");

                hubInfo.onboarding = tinfo.onboarding;
                hubInfo.onboardingFlow = tinfo.onboardingFlow;
            }

            // we are done, save as the cache and return
            if (i == hubs.length - 1) {
                this.supportedHubsCache = hubs;
                return this.supportedHubsCache;
            }
            // we are not done, recurse to the next hub
            else {
                return this.supportedHubs(hubs, i + 1);
            }
        }).catch((err) => {
            return this._handleError(err, "supportedHubs");
        });
    }

    /**
     * given a specific hub info, does the onboarding given the onboardingInfo and returns the auth info
     */
    onboard(hubId, onboardingInfo) {
        console.log("----------------- onboard");
        return this._getHubInfo(hubId).then((hubInfo) => {
            // do the onboarding and return token
            var Onboarding = require(hubInfo.onboarding);
            var onboarding = new Onboarding();
            return onboarding.onboard(onboardingInfo);
        }).catch((err) => {
            return this._handleError(err, "onboard");
        });
    }

    /**
     * Given a specific hub info, onboardingInfo, and existing authInfo blob,
     *  does the OAuthToken refresh, and returns the refreshed
     * auth info object back.
     */
    refreshAuthToken(hubId, onboardingInfo, existingAuthInfo){
        console.log("----------------- refreshAuthToken");

        return this._getHubInfo(hubId).then((hubInfo) => {

            // create hub translator for given hubId
            return this._createTranslator(hubInfo.translator, existingAuthInfo).then((hubInstance) => {

                // hub refreshAuthToken
                return this._invokeMethod(hubInstance, "", "refreshAuthToken", [onboardingInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "refreshAuthToken");
        });
    }

    /**
     * given the specific hub id, returns all the platforms which are connected to it
     */
    platforms(hubId, authInfo) {
        console.log("----------------- platforms");
        // will return hub getPlatform contents
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // hub get
                return this._invokeMethod(hubInstance, authInfo, "getPlatforms", [true]);
            });
        }).catch((err) => {
            return this._handleError(err, "platforms");
        });
    }

    /**
     * given the specific hub id and opent2tblob, returns the specific platform
     */
    getPlatform(hubId, authInfo, opent2tBlob) {
        console.log("----------------- getPlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // platform get
                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;
                return this._invokeMethod(opent2tBlob.translator, deviceInfo, "get", [true]);
            });
        }).catch((err) => {
           return this._handleError(err, "getPlatform");
        });
    }

    /**
     * given the specific hub id, opent2tblob, and resourceId, sets it with the given resourceBlob
     */
    setResource(hubId, authInfo, opent2tBlob, deviceId, resoureceId, resourceBlob) {
        console.log("----------------- setResource");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // resource set
                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                var propertyName = "postDevices" + this._capitalizeFirstLetter(resoureceId);

                return this._setProperty(opent2tBlob.translator, deviceInfo, propertyName, deviceId, resourceBlob);
            });
        }).catch((err) => {
            return this._handleError(err, "setResource");
        });
    }

    /**
     * Subscribe for notifications on all resources composing a platform.  Notifications will be posted to to
     * the subscriptionInfo.callbackURL.
     */
    subscribePlatform(hubId, authInfo, opent2tBlob, subscriptionInfo) {
        console.log("----------------- subscribePlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                return this._createTranslator(opent2tBlob.translator, deviceInfo).then(translator => {
                    return this.OpenT2T.invokeMethodAsync(translator, "", "postSubscribe", [subscriptionInfo]);
                });
            });
        }).catch((err) => {
            return this._handleError(err, "subscribePlatform");
        });
    }

    /**
     * Unsubscribe notification on all resources from a platform.
     */
    unsubscribePlatform(hubId, authInfo, opent2tBlob, subscriptionInfo) {
        console.log("----------------- unsubscribePlatform");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var deviceInfo = {};
                deviceInfo.hub = hubInstance;
                deviceInfo.deviceInfo = {};
                deviceInfo.deviceInfo.opent2t = opent2tBlob;

                return this._createTranslator(opent2tBlob.translator, deviceInfo).then(translator => {
                    return this.OpenT2T.invokeMethodAsync(translator, "", "deleteSubscribe", [subscriptionInfo]);
                });
            });
        }).catch((err) => {
            return this._handleError(err, "unsubscribePlatform");
        });
    }

    /**
     * Verification step for cloud notifications for providers that require it.
     */
    subscribeVerify(hubId, authInfo, verificationBlob) {
        console.log("----------------- subscribeVerify");
        return this._getHubInfo(hubId).then((hubInfo) => {
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {

                var subscriptionInfo = {};
                subscriptionInfo.verificationRequest = verificationBlob;

                return this.OpenT2T.invokeMethodAsync(hubInstance, "", "postSubscribe", [subscriptionInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "subscribePlatformVerify");
        });
    }

    /**
     * Translate a JSON blob from a provider into an opent2t/OCF schema.  This should be called with the contents of
     * the notification post backs.  Verification is an optional object providing a secret and a hash for verification of the payload.
     * Returns an array of translated platforms, even for a single item (size 1 obviously)
     *
     * @param {Object} verificationInfo
     * @param {string} verificationInfo.key - Secret key used to compute HMAC
     * @param {Object} verificationInfo.header - Headers from the notification which will contain a provider specific HMAC.
     */
    translatePlatforms(hubId, authInfo, providerBlob, verificationInfo) {
        console.log("----------------- translatePlatforms");

        // Create a hub, of the requested type.
        return this._getHubInfo(hubId).then((hubInfo) => {
            // Pass the provider blob off to the hub for translation.
            return this._createTranslator(hubInfo.translator, authInfo).then((hubInstance) => {
                // The getPlatforms method on the hub can take either single providerSchema, or a list depending
                // on the service that provided the notification.  It's up the the hub to know what to do with the data.
                return this.OpenT2T.invokeMethodAsync(hubInstance, "", "getPlatforms", [true, providerBlob, verificationInfo]);
            });
        }).catch((err) => {
            return this._handleError(err, "translatePlatforms");
        });
    }

    /**
     * helper methods
     */
    _getHubInfo(hubId) {
        console.log("----------------- _getHubInfo");
        return this.supportedHubs().then((hubs) => {
            // find the hub referenced by hubId
            var hubInfo = undefined;
            for (var i = 0; hubInfo === undefined && i < hubs.length; i++) {
                var hub = hubs[i];

                // intentional ==
                if (hub.id == hubId) {
                    hubInfo = hub;
                }
            }

            if (!hubInfo) {
                console.log("invalid hub id");
                throw new OpenT2TError(404, OpenT2TConstants.InvalidHubId);
            }

            return hubInfo;
        });
    }

    _invokeMethod(translator, deviceInfo, methodName, params) {
        console.log("----------------- _invokeMethod " + methodName + " with params "+ JSON.stringify(params));

        if (typeof translator === "object") {
            return this.OpenT2T.invokeMethodAsync(translator, "", methodName, params);
        }
        else {
            return this._createTranslator(translator, deviceInfo).then(translatorInstance => {
                return this.OpenT2T.invokeMethodAsync(translatorInstance, "", methodName, params);
            });
        }
    }

    _setProperty(translatorName, deviceInfo, property, deviceId, value) {
        console.log("----------------- _setProperty " + translatorName + " for " + property + " to:");
        console.log(JSON.stringify(value, null, 2));

        return this._createTranslator(translatorName, deviceInfo).then(translator => {
            return this.OpenT2T.invokeMethodAsync(translator, "", property, [deviceId, value]);
        });
    }

    _capitalizeFirstLetter(string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
    }

    _createTranslator(translatorName, deviceInfo) {
        console.log("----------------- _createTranslator " + translatorName);
        return this.OpenT2T.createTranslatorAsync(translatorName, deviceInfo).then( translator => {
            return translator;
        });
    }

    _handleError(err, message) {
        let customMessage = "OpenT2T call failed in: " + message + "; Original message: ";

        // This was a result of a failed HTTP Request promise
        // Can also check err.Name
        if ('response' in err && 'statusMessage' in err.response) {
            customMessage = customMessage + err.response.statusMessage;
        }
        else {
            // Likely an simple Error-derived class
            customMessage = customMessage + err.message;
        }

        let customError = new OpenT2TError(err.statusCode, customMessage, err);
        console.log(" custom error message:           " + customError.message);
        console.log(" custom error name:           " + customError.name);
        console.log(" custom error statusCode:           " + customError.statusCode);
        console.log(" custom error innerError message:           " +  customError.innerError.message);
        console.log(" custom error innerError stack:           " +  customError.innerError.stack);

        return q.reject(customError);
    }
}

module.exports = HubController;<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018, Two Sigma Open Source
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of git-meta nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

const assert    = require("chai").assert;
const ChildProcess = require("child-process-promise");
const co        = require("co");
const path      = require("path");
const GitUtil   = require("../util/git_util");

/**
 * Run git-meta hook with given hook name.
 * @async
 * @param {String} name
 * @param {String[]} args
 * @return {String}
 */
exports.execHook = co.wrap(function *(name, args=[]) {
    assert.isString(name);

    const hookPath = path.join(GitUtil.getRootGitDirectory(), ".git/hooks");
    const absPath = path.resolve(hookPath, name);

    try {
        const result = yield ChildProcess.execFile(absPath, args);
        return result.stdout;
    } catch (e) {
        // ignore the exception
        // Fixme: Need to be sync with git. If the hook is not executable,
        // should give user hints.
    }
});
<commit_msg>Change to root git directory before executing hooks<commit_after>/*
 * Copyright (c) 2018, Two Sigma Open Source
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of git-meta nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
"use strict";

const assert    = require("chai").assert;
const ChildProcess = require("child-process-promise");
const co        = require("co");
const path      = require("path");
const GitUtil   = require("../util/git_util");
const process   = require("process");

/**
 * Run git-meta hook with given hook name.
 * @async
 * @param {String} name
 * @param {String[]} args
 * @return {String}
 */
exports.execHook = co.wrap(function *(name, args=[]) {
    assert.isString(name);

    const rootDirectory = GitUtil.getRootGitDirectory();
    const hookPath = path.join(rootDirectory, ".git/hooks");
    const absPath = path.resolve(hookPath, name);

    try {
        process.chdir(rootDirectory);
        const result = yield ChildProcess.execFile(absPath, args);
        return result.stdout;
    } catch (e) {
        // ignore the exception
        // Fixme: Need to be sync with git. If the hook is not executable,
        // should give user hints.
    }
});
<|endoftext|>


<commit_before>$(document).ready(function () {
    new Clipboard('.btn');
    $(".btn-login").click(function () {
        var $btn = $(this);
        var $form = $(".login-form");
        $btn.html('');
        console.log($form.val());
        // send login information to server and validate
        var code = {
            "code": $(".login-form").val()
        };
        console.log(code);
        $.ajax({
            url: "/login",
            method: "POST",
            data: code,
            success: function (res) {
                // success: log user in, disable VIP Code input
                if (res.err) {
                    notie.alert("error", res.err, 2);
                    $btn.html('');
                } else {
                    notie.alert("success", "You can now use Premium features", 2);
                    // refresh page
                    setTimeout(location.reload.bind(location), 1000);
                }
            },
            error: function (req, status, err) {
                // unknown error: show error message, reset inputs
                notie.alert("error", req.responseText, 2);
                $btn.html('');
            }
        });

    });

    // VIP CODE: listen for enter key press
    $(".login-form").keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-login').click();
    });

    // make request to shorten link on button click
    $(".btn-shorten").click(function () {
        var url = $(".input-url").val();
        var customLink = $(".input-custom-link").val();
        if(customLink == undefined)
            customLink = "";
        console.log(url);
        console.log(customLink);
        var json = {
                "shortlink": customLink,
                "longlink": url
            }
            // validate url
        if (isURL(url)) {
            $.ajax({
                url: "/link",
                method: "POST",
                data: json,
                dataType: "json",
                success: function (res, status) {
                    console.log(res);
                    if (!res.error) {
                        $(".input-url").val(res.shortLink);
                        $(".input-url").select();

                        // create well for first shortened link
                        if (!$(".result").hasClass("well")) {
                            $(".result").addClass("well");
                        }
                        displayShortenedLink(res.longLink, res.shortLink, res.shortURL);
                    } else {
                        notie.alert("error", res.error, 2);
                    }
                },
                error: function (obj, status, err) {
                    if (err === "") {
                        err = "An error occured while processing your request.";
                    }
                    notie.alert('error', err, 2);
                }
            });
        } else {
            notie.alert('error', 'Unable to shorten that link. It is not a valid url.', 2);
        }

    });

    // listen for enter keypress
    $('.input-url').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-shorten').click();
    });

    $('.input-custom-link').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-shorten').click();
    });
    var count = 36187;
    setInterval(function () {
        var rnd = Math.floor(Math.random() * 100);
        count += rnd;
        $(".count").html(count);
    }, 1000);

    // Admin functionality
    $(".btn-generate").click(function () {
        // TODO: generate random Premium Code
        $(".input-code").val(token());
    });

    $('#input-password').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-signin').click();
    });

    $('.input-code').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-code').click();
    });

    // Admin Page SignIn
    $(".btn-signin").click(function () {
        var passwd = {
            password: $("#input-password").val()
        };
        $.ajax({
            url: "/admin",
            data: passwd,
            method: "POST",
            success: function (data) {
                if (!data.err) {
                    location.reload();
                } else {
                    notie.alert("error", data.err, 2)
                }
            }
        });
    });

    // Submit new Premium Code
    $(".btn-code").click(function () {
        var code = {
            "code": $(".input-code").val()
        };
        $.ajax({
            url: "/code",
            data: code,
            method: "POST",
            success: function (data) {
                if (!data.err) {
                    notie.alert("success", "Succesfully created Premium Code '" + data.code + "'", 2);
                } else {
                    notie.alert("error", data.err, 2)
                }
            }
        });
    });
});

// validates a URL
function isURL(str) {
    var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
        '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name
        '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
        '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
        '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
        '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
    return pattern.test(str);
}

function displayShortenedLink(long, short, shortUrl) {
    $(".result").append("
");
        $(".result").append("
" + long + ": " + "" + short + "
");
        $(".result").append('
 QR Code:
');
        $(".result").append('
Copy
');
        $(".result").append('Statistiken zum Link');
    $(".result").append("
");
}

var rand = function() {
    return Math.random().toString(36).substr(20); // remove `0.`
};

var token = function() {
    return rand() + rand(); // to make it longer
};
<commit_msg>remove clientside logging for debug<commit_after>$(document).ready(function () {
    new Clipboard('.btn');
    $(".btn-login").click(function () {
        var $btn = $(this);
        var $form = $(".login-form");
        $btn.html('');
        // send login information to server and validate
        var code = {
            "code": $form.val()
        };
        $.ajax({
            url: "/login",
            method: "POST",
            data: code,
            success: function (res) {
                // success: log user in, disable VIP Code input
                if (res.err) {
                    notie.alert("error", res.err, 2);
                    $btn.html('');
                } else {
                    notie.alert("success", "You can now use Premium features", 2);
                    // refresh page
                    setTimeout(location.reload.bind(location), 1000);
                }
            },
            error: function (req, status, err) {
                // unknown error: show error message, reset inputs
                notie.alert("error", req.responseText, 2);
                $btn.html('');
            }
        });

    });

    // VIP CODE: listen for enter key press
    $(".login-form").keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-login').click();
    });

    // make request to shorten link on button click
    $(".btn-shorten").click(function () {
        var url = $(".input-url").val();
        var customLink = $(".input-custom-link").val();
        if(customLink == undefined)
            customLink = "";
        var json = {
                "shortlink": customLink,
                "longlink": url
            }
            // validate url
        if (isURL(url)) {
            $.ajax({
                url: "/link",
                method: "POST",
                data: json,
                dataType: "json",
                success: function (res, status) {
                    console.log(res);
                    if (!res.error) {
                        $(".input-url").val(res.shortLink);
                        $(".input-url").select();

                        // create well for first shortened link
                        if (!$(".result").hasClass("well")) {
                            $(".result").addClass("well");
                        }
                        displayShortenedLink(res.longLink, res.shortLink, res.shortURL);
                    } else {
                        notie.alert("error", res.error, 2);
                    }
                },
                error: function (obj, status, err) {
                    if (err === "") {
                        err = "An error occured while processing your request.";
                    }
                    notie.alert('error', err, 2);
                }
            });
        } else {
            notie.alert('error', 'Unable to shorten that link. It is not a valid url.', 2);
        }

    });

    // listen for enter keypress
    $('.input-url').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-shorten').click();
    });

    $('.input-custom-link').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-shorten').click();
    });
    var count = 36187;
    setInterval(function () {
        var rnd = Math.floor(Math.random() * 100);
        count += rnd;
        $(".count").html(count);
    }, 1000);

    // Admin functionality
    $(".btn-generate").click(function () {
        // TODO: generate random Premium Code
        $(".input-code").val(token());
    });

    $('#input-password').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-signin').click();
    });

    $('.input-code').keypress(function (e) {
        if (e.keyCode == 13)
            $('.btn-code').click();
    });

    // Admin Page SignIn
    $(".btn-signin").click(function () {
        var passwd = {
            password: $("#input-password").val()
        };
        $.ajax({
            url: "/admin",
            data: passwd,
            method: "POST",
            success: function (data) {
                if (!data.err) {
                    location.reload();
                } else {
                    notie.alert("error", data.err, 2)
                }
            }
        });
    });

    // Submit new Premium Code
    $(".btn-code").click(function () {
        var code = {
            "code": $(".input-code").val()
        };
        $.ajax({
            url: "/code",
            data: code,
            method: "POST",
            success: function (data) {
                if (!data.err) {
                    notie.alert("success", "Succesfully created Premium Code '" + data.code + "'", 2);
                } else {
                    notie.alert("error", data.err, 2)
                }
            }
        });
    });
});

// validates a URL
function isURL(str) {
    var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol
        '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' + // domain name
        '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
        '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
        '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
        '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator
    return pattern.test(str);
}

function displayShortenedLink(long, short, shortUrl) {
    $(".result").append("
");
        $(".result").append("
" + long + ": " + "" + short + "
");
        $(".result").append('
 QR Code:
');
        $(".result").append('
Copy
');
        $(".result").append('Statistiken zum Link');
    $(".result").append("
");
}

var rand = function() {
    return Math.random().toString(36).substr(20); // remove `0.`
};

var token = function() {
    return rand() + rand(); // to make it longer
};
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var result = require('./result');
//TODO
var source_url = {sick: '/gakf/askDoctor.html', doctor: '/gakf/question.html'};
var target_url = {sick: '/gakf/askDoctor.html', doctor: '/gakf/question.html'};

router.get('/message', function (req, res, next) {
    var session_id = req.param('session_id');
    var pageNo = req.param('page_no') || 0;
    var pageSize = req.param('page_no') || 10;
    req.models.message.find({session_id: 'session_id'}).order("-id")
        .limit(pageSize).offset(pageNo * pageSize).run(function (err, data) {
            if (err) {
                res.json(result(false, '', err));
            } else {
                res.json(result(true, '', data));
            }
        });
});

router.get('/session', function (req, res, next) {
    var sick_id = req.param('sick_id');
    var doctor_id = req.param('doctor_id');
    var pageNo = req.param('page_no') || 0, pageSize = req.param('page_size') || 10;
    console.info('sick_id=' + sick_id + ",doctor_id=" + doctor_id
        + ",page_no=" + pageNo + ",pageSize=" + pageSize);
    var query = {};
    if (sick_id) {
        query.sick_id = sick_id;
    }
    if (doctor_id) {
        query.doctor_id = doctor_id;
    }
    req.models.message_session.find(query).order("-id").limit(pageSize).offset(pageNo * pageSize)
        .run(function (err, data) {
            if (err) {
                console.error(err);
                res.json(result(false, '', {}));
            } else {
                res.json(result(true, '', data));
            }
        });
});

router.post('/speak', function (req, res, next) {
    //生成multiparty对象，并配置下载目标路径
    var form = new multiparty.Form({uploadDir: './public/images/'});
    //下载后处理
    form.parse(req, function (err, fields, files) {
        var filesTmp = JSON.stringify(files, null, 2);
        var session_id, sick_id, doctor_id, name, id, content, title, type;
        try {
            session_id = fields['session_id'];
            sick_id = fields['sick_id'];
            doctor_id = fields['doctor_id'];
            name = fields['name'];
            content = fields['content'];
            title = fields['title'];
            id = sick_id || doctor_id;
            type = sick_id ? "sick" : "doctor";
        } catch (e) {
            console.error(e);
            err = e;
        }
        if (err) {
            console.log('parse image error: ' + err);
            res.redirect(source_url[type] + "?err=1");
        } else {
            console.log('parse files: ' + filesTmp);
            var pic = [];
            for (var i = 0; i < files.inputFile.length; i++) {
                pic[i] = files.inputFile[i].path;
            }

            if (!session_id) {
                var msg = {
                    day: new Date(),
                    title: title,
                    content: content,
                    speaker: name,
                    speaker_id: id
                };
                req.models.message_session.create({
                    day: new Date(),
                    sick_id: id,
                    doctor_id: doctor_id,
                    message: msg
                }, function (err, item) {
                    msg.session_id = item.id;
                    req.models.message.create(msg, function (err) {
                        if (err) {
                            res.redirect(source_url[type] + "?err=2");
                        } else {
                            res.redirect(target_url[type]);
                        }
                    });
                });
            } else {
                req.models.message.create({
                    session_id: session_id,
                    day: new Date(),
                    title: title,
                    content: content,
                    speaker: name,
                    speaker_id: id
                }, function (err, item) {
                    if (err) {
                        res.redirect(source_url[type] + "?err=2");
                    } else {
                        res.redirect(target_url[type]);
                    }
                });
            }
        }
    });

});

module.exports = router;<commit_msg>fix answer<commit_after>var express = require('express');
var router = express.Router();
var result = require('./result');
//TODO
var source_url = {sick: '/gakf/askDoctor.html', doctor: '/gakf/question.html'};
var target_url = {sick: '/gakf/askDoctor.html', doctor: '/gakf/question.html'};

router.get('/message', function (req, res, next) {
    var session_id = req.param('session_id');
    var pageNo = req.param('page_no') || 0;
    var pageSize = req.param('page_no') || 10;
    req.models.message.find({session_id: 'session_id'}).order("-id")
        .limit(pageSize).offset(pageNo * pageSize).run(function (err, data) {
            if (err) {
                res.json(result(false, '', err));
            } else {
                res.json(result(true, '', data));
            }
        });
});

router.get('/session', function (req, res, next) {
    var sick_id = req.param('sick_id');
    var doctor_id = req.param('doctor_id');
    var pageNo = req.param('page_no') || 0, pageSize = req.param('page_size') || 10;
    console.info('sick_id=' + sick_id + ",doctor_id=" + doctor_id
        + ",page_no=" + pageNo + ",pageSize=" + pageSize);
    var query = {};
    if (sick_id) {
        query.sick_id = sick_id;
    }
    if (doctor_id) {
        query.doctor_id = doctor_id;
    }
    req.models.message_session.find(query).order("-id").limit(pageSize).offset(pageNo * pageSize)
        .run(function (err, data) {
            if (err) {
                res.json(result(false, '', {}));
            } else {
                res.json(result(true, '', data));
            }
        });
});

router.post('/speak', function (req, res, next) {
    //生成multiparty对象，并配置下载目标路径
    var form = new multiparty.Form({uploadDir: './public/images/'});
    //下载后处理
    form.parse(req, function (err, fields, files) {
        var filesTmp = JSON.stringify(files, null, 2);
        var session_id, sick_id, doctor_id, name, id, content, title, type;
        try {
            session_id = fields['session_id'];
            sick_id = fields['sick_id'];
            doctor_id = fields['doctor_id'];
            name = fields['name'];
            content = fields['content'];
            title = fields['title'];
            id = sick_id || doctor_id;
            type = sick_id ? "sick" : "doctor";
        } catch (e) {
            console.error(e);
            err = e;
        }
        if (err) {
            console.log('parse image error: ' + err);
            res.redirect(source_url[type] + "?err=1");
        } else {
            console.log('parse files: ' + filesTmp);
            var pic = [];
            for (var i = 0; i < files.inputFile.length; i++) {
                pic[i] = files.inputFile[i].path;
            }

            if (!session_id) {
                var msg = {
                    day: new Date(),
                    title: title,
                    content: content,
                    speaker: name,
                    speaker_id: id
                };
                req.models.message_session.create({
                    day: new Date(),
                    sick_id: id,
                    doctor_id: doctor_id,
                    message: msg
                }, function (err, item) {
                    msg.session_id = item.id;
                    req.models.message.create(msg, function (err) {
                        if (err) {
                            res.redirect(source_url[type] + "?err=2");
                        } else {
                            res.redirect(target_url[type]);
                        }
                    });
                });
            } else {
                req.models.message.create({
                    session_id: session_id,
                    day: new Date(),
                    title: title,
                    content: content,
                    speaker: name,
                    speaker_id: id
                }, function (err, item) {
                    if (err) {
                        res.redirect(source_url[type] + "?err=2");
                    } else {
                        res.redirect(target_url[type]);
                    }
                });
            }
        }
    });

});

module.exports = router;<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

function renderMainPage(req, res, auth) {
    req.db.get('trails').find( {}, {fields: { 'trailname':1 }}, function(err, doc) {
	if (err) {
	    doc = [];
	}
	res.render('main', { title: 'Just Another Trail Map', authenticated: auth, trails: doc });
    });
}

router.get('/', function(req, res) {
    var user = req.cookies.username;
    var pass = req.cookies.password;
    var auth = false;

    if (user && pass) {
	req.db.get('users').find( { username: user }, { fields: {password: 1, _id: 0 } }, function(err, doc) {
	    if (err || doc == null) {
		throw err;
	    }
	    if (doc.length == 1 && doc[0].password == pass) {
		auth = true;
	    }
	    renderMainPage(req, res, auth);
	});
    }
    else {
	renderMainPage(req, res, auth);
    }
});

router.get('/trailbuilder', function(req, res) {
    res.render('trailbuilder');
});

module.exports = router;
<commit_msg>Updated.<commit_after>var express = require('express');
var router = express.Router();

function renderMainPage(req, res, auth) {
    req.db.get('trails').find( {}, {fields: { 'trailname':1, 'location':1 }}, function(err, doc) {
	if (err) {
	    doc = [];
	}
	res.render('main', { title: 'Just Another Trail Map', authenticated: auth, trails: doc });
    });
}

router.get('/', function(req, res) {
    var user = req.cookies.username;
    var pass = req.cookies.password;
    var auth = false;

    if (user && pass) {
	req.db.get('users').find( { username: user }, { fields: {password: 1, _id: 0 } }, function(err, doc) {
	    if (err || doc == null) {
		throw err;
	    }
	    if (doc.length == 1 && doc[0].password == pass) {
		auth = true;
	    }
	    renderMainPage(req, res, auth);
	});
    }
    else {
	renderMainPage(req, res, auth);
    }
});

router.get('/trailbuilder', function(req, res) {
    res.render('trailbuilder');
});

module.exports = router;
<|endoftext|>


<commit_before>var userService = require('./../services/user-service');

/*
 * POST /follow
 */
exports.follow = function (req, res) {
    var followerId = req.body.from;
    var followeeId = req.body.to;
    console.log("user.follow started with fromUserId", followerId, "toUserId", followeeId);

    userService.addFollower(followerId, followeeId);

    res.send(200);
};

/*
 * POST /listen
 */
exports.listen = function (req, res) {
    var userId = req.body.user;
    var musicId = req.body.music;
    console.log("user.listen started with userId", userId, "musicId", musicId);

    res.send(200);
};

/*
 * GET /recommendations
 */
exports.recommendations = function (req, res) {
    var userId = req.query.user;
    console.log("user.recommendations started with userId", userId);

    res.send(200, {list: ["abc", "xyz", "123"]});
};<commit_msg>Integrated validator for /follow endpoint<commit_after>var userService = require('./../services/user-service');
var validator = require('./../util/validator.js');

/*
 * POST /follow
 */
exports.follow = function (req, res) {
    var followerId = req.body.from;
    var followeeId = req.body.to;

    console.log("user.follow started with fromUserId", followerId, "toUserId", followeeId);

    var isFollowerIdValid = validator.validate(followerId, validator.alphaNumericPattern, false, 32);
    if (!isFollowerIdValid) {
        res.send(400, validationError("from"));
        return;
    }
    var isFolloweeIdValid = validator.validate(followeeId, validator.alphaNumericPattern, false, 32);
    if (!isFolloweeIdValid) {
        res.send(400, validationError("to"));
        return;
    }

    userService.addFollower(followerId, followeeId);

    res.send(200);
};

/*
 * POST /listen
 */
exports.listen = function (req, res) {
    var userId = req.body.user;
    var musicId = req.body.music;
    console.log("user.listen started with userId", userId, "musicId", musicId);

    res.send(200);
};

/*
 * GET /recommendations
 */
exports.recommendations = function (req, res) {
    var userId = req.query.user;
    console.log("user.recommendations started with userId", userId);

    res.send(200, {list: ["abc", "xyz", "123"]});
};

function validationError(invalidFieldName) {
    return {
        error: {
            code: "ValidationError",
            message: "Invalid request parameter: '" + invalidFieldName + "'"
        }
    };
}<|endoftext|>


<commit_before>var apns = require('apn'),
    uuid = require('node-uuid'),
    log  = require('../log').logger,
    redis = require('redis');

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

// set up redis
var port = config.redis.port || 6379,
    host = config.redis.host || 'localhost';

var redisClient = redis.createClient(port, host);
redisClient.on("error", function (err) {
  log.error("Redis Error: " + err);
});

// place to hold status for open connections
var status = { };

var connections = { };

var errorMap = {
  1: "Processing Error",
  2: "Missing Device Token",
  3: "Missing Topic",
  4: "Missing Payload",
  5: "Invalid Token Size",
  6: "Invalid Topic Size",
  7: "Invalid Payload Size",
  8: "Invalid Token",
  23: "Bad Certificate",
  255: "Unknown Error"
};

// get a connection from the pool if possible, otherwise create a new connection
function getConnection (name, options, uuid) {
  var connection = connections[name];

  // if there is no connection, create a new one
  if (connection === undefined) {
    options.name = name;

    log.debug("Establishing new connection for "+options.name);

    connection = new apns.Connection(options);

    connection.on('disconnected', function () {
      log.info(this.options.name + " disconnected");
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("disconnected", {uuid: uuid, errorDescription: "APNS disconnected the socket! Most likely this is due to a bad or expired certificate, or trying to use a sandbox certificate on the production APNS servers."});
      }
    });

    connection.on('error', function () {
      log.warn("Connection error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("error", {uuid: uuid, errorDescription: "Unknown connection error"});
      }
    });

    connection.on('notificationError', function () {
      log.warn("Connection error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
      }
    });

    // This error is returned when Apple closes the connection in the middle of sending a push notification
    // Most often it's due to a malformed payload sent to Apple, or because of an invalid device ID
    connection.on('transmissionError', function () {
      log.warn("Transmission error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;

        var errorDescription = "An unknown error occurred while trying to send the push notification. Most often this is caused by a malformed payload or invalid device ID.";
        if (arguments[0] && errorMap[arguments[0]] !== undefined) {
          errorDescription = errorMap[arguments[0]];
        }

        errorCallback("transmissionError", {uuid: uuid, errorDescription: errorDescription});
      }
    });

    connection.on('socketError', function () {
      console.dir(arguments);
      log.warn("Socket error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("socketError", {uuid: uuid});
      }
    });

    connections[name] = connection;
  } else {
    log.debug("Found existing connection for "+name);
  }

  return connection;
}

// callback from sendMesage error callback, known error state
function errorCallback(err, options) {
  if (options === undefined) {
    log.warn("errorCallback called but no options passed", err);
    return;
  }
  var connection = status[options.uuid];

  if (connection === undefined) {
    log.info("errorCallback called but no connection was active. uuid: " + options.uuid);
    return;
  }

  var response = connection.response;

  if (err) {
    // check for bad certificate, set it to our known error if it is the case
    if (err.toString().indexOf("PEM") !== -1) {
      err = 23;
    }

    // known error state, handle it normally
    var errResponse = errorMap[err];
    if (errorMap[err] === undefined) {
      errResponse = err.toString();
    }
    response.end(JSON.stringify({ response: "error", error: errResponse, error_description: options.errorDescription }));
  } else {
    // whoa, something weird happened, log it and return a result
    log.warn("errorCallback returned with unknown error: " + err);
    response.end(JSON.stringify({ response: "error", error: "Unknown" }));
  }
  status[options.id] = undefined;
}

// callback from setTimeout - could be anything
function notificationCallback(id) {
  // check to see if there has been a previously handled error
  var connection = status[id];

  if (connection === undefined) {
    // nothing to see here, the error handler has already
    // handled this connection
  } else {
    var response = status[id].response,
        request  = status[id].request;

    // clear it now from status
    status[id] = undefined;

    // if we have a timeout of "0", just notify that we sent it
    if (request.body.timeout === 0) {
      response.end(JSON.stringify({ response: "sent" }));
    } else {
      response.end(JSON.stringify({ response: "ok" }));
    }
  }
}

// actually attempt to send a message, finally
function sendMessage(request, response) {
  var payload = request.body.notification;

  var timeout = config.connectionTimeout || 1000;

  // set up the connection options
  var options = {
    certData:          request.body.cert,
    keyData:           request.body.key,
    deviceId:          request.body.deviceId,
    mode:              request.body.mode,
    connectionTimeout: timeout,
    enhanced:          true,
    errorCallback:     errorCallback,
    cacheLength:       256,
    debug:             true
  };

  if (request.body.mode === 'production') {
    options.gateway = 'gateway.push.apple.com';
  } else {
    options.gateway = 'gateway.sandbox.push.apple.com';
  }

  // set up the notification based on data passed
  var notification = new apns.Notification();

  if (payload.badge !== undefined) {
    notification.badge = payload.badge;
    payload.badge = null;
  }

  if (payload.sound !== undefined) {
    notification.sound = payload.sound;
    payload.sound = null;
  }

  if (payload.alert !== undefined) {
    notification.alert = payload.alert;
    payload.alert = null;
  }

  if (payload.payload !== undefined) {
    notification.payload = payload;
  }

  notification.uuid = uuid.v4();

  log.debug(JSON.stringify(notification));

  // stash the connection away
  status[notification.uuid] = {
    request: request,
    response: response,
    options: options
  };

  notification.device = new apns.Device(request.body.deviceId);

  var connection = getConnection(request.body.appId + "_" + request.body.mode, options, notification.uuid);

  // set a timeout to check to see if an error occurred
  setTimeout(function () {
    notificationCallback(notification.uuid);
  }, request.body.timeout);

  // send the notification
  connection.pushNotification(notification, notification.device);
}

// if there is no certificate, see if one can be found
function handleExistingAuth (request, response) {
  var appId = request.body.appId,
      mode  = request.body.mode;

  // check redis for an existing certificate for this appId
  if (redisClient && redisClient.connected) {
    redisClient.multi([ [ "mget", appId + "_" + mode + "_cert", appId + "_" + mode + "_key" ]]).exec(function (err, replies) {
      if (replies === undefined || replies.length !== 1 || replies[0].length !== 2 || replies[0][0] === null || replies[0][1] === null) {
        log.debug("No cert found in Redis for "+appId+" ("+mode+")");
        response.end(JSON.stringify({ "response": "error", "error": "missing certificate" }));
      } else {
        log.debug("Found a cert in Redis for "+appId+" ("+mode+")");
        request.body.cert = replies[0][0];
        request.body.key = replies[0][1];

        sendMessage(request, response);
      }
    });
  } else {
    log.info("No redis connection, can't check for existing certificate");
    response.end(JSON.stringify({ "status": "error", "error": "Internal server error" }));
  }
}

// certificate passed in, yay!
function handleNewAuth (request, response) {
  var appId = request.body.appId,
      mode  = request.body.mode,
      cert  = request.body.cert,
      key   = request.body.key;

  if (redisClient && redisClient.connected) {
    redisClient.multi([ [ "mset", appId + "_" + mode + "_cert", cert, appId + "_" + mode + "_key", key ]]).exec(function (err, replies) {
      log.debug("Saved cert in Redis");
      sendMessage(request, response);
    });
  } else {
    log.info("No Redis connection, can't store cert");
    sendMessage(request, response);
  }
}

// entry for the module, handle the message
function handleMessage (request, response) {
  log.debug("Push to "+request.body.deviceId);

  if (request.body.cert !== undefined && request.body.key !== undefined) {
    // If a certificate is provided, store it in redis
    log.debug("New cert provided in request");
    handleNewAuth(request, response);
  } else {
    log.debug("No cert provided, attempt to look up cert in the cache");
    handleExistingAuth(request, response);
  }
}

exports.handleMessage = handleMessage;
<commit_msg>More removing of doubling up of notifications for apn<commit_after>var apns = require('apn'),
    uuid = require('node-uuid'),
    log  = require('../log').logger,
    redis = require('redis');

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

// set up redis
var port = config.redis.port || 6379,
    host = config.redis.host || 'localhost';

var redisClient = redis.createClient(port, host);
redisClient.on("error", function (err) {
  log.error("Redis Error: " + err);
});

// place to hold status for open connections
var status = { };

var connections = { };

var errorMap = {
  1: "Processing Error",
  2: "Missing Device Token",
  3: "Missing Topic",
  4: "Missing Payload",
  5: "Invalid Token Size",
  6: "Invalid Topic Size",
  7: "Invalid Payload Size",
  8: "Invalid Token",
  23: "Bad Certificate",
  255: "Unknown Error"
};

// get a connection from the pool if possible, otherwise create a new connection
function getConnection (name, options, uuid) {
  var connection = connections[name];

  // if there is no connection, create a new one
  if (connection === undefined) {
    options.name = name;

    log.debug("Establishing new connection for "+options.name);

    connection = new apns.Connection(options);

    connection.on('disconnected', function () {
      log.info(this.options.name + " disconnected");
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("disconnected", {uuid: uuid, errorDescription: "APNS disconnected the socket! Most likely this is due to a bad or expired certificate, or trying to use a sandbox certificate on the production APNS servers."});
      }
    });

    connection.on('error', function () {
      log.warn("Connection error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("error", {uuid: uuid, errorDescription: "Unknown connection error"});
      }
    });

    connection.on('notificationError', function () {
      log.warn("Connection error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
      }
    });

    // This error is returned when Apple closes the connection in the middle of sending a push notification
    // Most often it's due to a malformed payload sent to Apple, or because of an invalid device ID
    connection.on('transmissionError', function () {
      log.warn("Transmission error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;

        var errorDescription = "An unknown error occurred while trying to send the push notification. Most often this is caused by a malformed payload or invalid device ID.";
        if (arguments[0] && errorMap[arguments[0]] !== undefined) {
          errorDescription = errorMap[arguments[0]];
        }

        errorCallback("transmissionError", {uuid: uuid, errorDescription: errorDescription});
      }
    });

    connection.on('socketError', function () {
      console.dir(arguments);
      log.warn("Socket error for "+this.options.name);
      // remove the connection from the connection pool
      if (this.options && this.options.name) {
        connections[this.options.name] = undefined;
        errorCallback("socketError", {uuid: uuid});
      }
    });

    connections[name] = connection;
  } else {
    log.debug("Found existing connection for "+name);
  }

  return connection;
}

// callback from sendMesage error callback, known error state
function errorCallback(err, options) {
  if (options === undefined) {
    log.warn("errorCallback called but no options passed", err);
    return;
  }
  var connection = status[options.uuid];

  if (connection === undefined) {
    log.info("errorCallback called but no connection was active. uuid: " + options.uuid);
    return;
  }

  var response = connection.response;

  if (err) {
    // check for bad certificate, set it to our known error if it is the case
    if (err.toString().indexOf("PEM") !== -1) {
      err = 23;
    }

    // known error state, handle it normally
    var errResponse = errorMap[err];
    if (errorMap[err] === undefined) {
      errResponse = err.toString();
    }
    response.end(JSON.stringify({ response: "error", error: errResponse, error_description: options.errorDescription }));
  } else {
    // whoa, something weird happened, log it and return a result
    log.warn("errorCallback returned with unknown error: " + err);
    response.end(JSON.stringify({ response: "error", error: "Unknown" }));
  }
  status[options.id] = undefined;
}

// callback from setTimeout - could be anything
function notificationCallback(id) {
  // check to see if there has been a previously handled error
  var connection = status[id];

  if (connection === undefined) {
    // nothing to see here, the error handler has already
    // handled this connection
  } else {
    var response = status[id].response,
        request  = status[id].request;

    // clear it now from status
    status[id] = undefined;

    // if we have a timeout of "0", just notify that we sent it
    if (request.body.timeout === 0) {
      response.end(JSON.stringify({ response: "sent" }));
    } else {
      response.end(JSON.stringify({ response: "ok" }));
    }
  }
}

// actually attempt to send a message, finally
function sendMessage(request, response) {
  var payload = request.body.notification;

  var timeout = config.connectionTimeout || 1000;

  // set up the connection options
  var options = {
    certData:          request.body.cert,
    keyData:           request.body.key,
    deviceId:          request.body.deviceId,
    mode:              request.body.mode,
    connectionTimeout: timeout,
    enhanced:          true,
    errorCallback:     errorCallback,
    cacheLength:       256,
    debug:             true
  };

  if (request.body.mode === 'production') {
    options.gateway = 'gateway.push.apple.com';
  } else {
    options.gateway = 'gateway.sandbox.push.apple.com';
  }

  // set up the notification based on data passed
  var notification = new apns.Notification();

  if (payload.badge !== undefined) {
    notification.badge = payload.badge;
    delete payload.badge;
  }

  if (payload.sound !== undefined) {
    notification.sound = payload.sound;
    delete payload.sound;
  }

  if (payload.alert !== undefined) {
    notification.alert = payload.alert;
    delete payload.alert;
  }

  if (payload.payload !== undefined &&
      Object.keys(payload.payload).length != 0) {
    notification.payload = payload;
  }

  notification.uuid = uuid.v4();

  log.debug(JSON.stringify(notification));

  // stash the connection away
  status[notification.uuid] = {
    request: request,
    response: response,
    options: options
  };

  notification.device = new apns.Device(request.body.deviceId);

  var connection = getConnection(request.body.appId + "_" + request.body.mode, options, notification.uuid);

  // set a timeout to check to see if an error occurred
  setTimeout(function () {
    notificationCallback(notification.uuid);
  }, request.body.timeout);

  // send the notification
  connection.pushNotification(notification, notification.device);
}

// if there is no certificate, see if one can be found
function handleExistingAuth (request, response) {
  var appId = request.body.appId,
      mode  = request.body.mode;

  // check redis for an existing certificate for this appId
  if (redisClient && redisClient.connected) {
    redisClient.multi([ [ "mget", appId + "_" + mode + "_cert", appId + "_" + mode + "_key" ]]).exec(function (err, replies) {
      if (replies === undefined || replies.length !== 1 || replies[0].length !== 2 || replies[0][0] === null || replies[0][1] === null) {
        log.debug("No cert found in Redis for "+appId+" ("+mode+")");
        response.end(JSON.stringify({ "response": "error", "error": "missing certificate" }));
      } else {
        log.debug("Found a cert in Redis for "+appId+" ("+mode+")");
        request.body.cert = replies[0][0];
        request.body.key = replies[0][1];

        sendMessage(request, response);
      }
    });
  } else {
    log.info("No redis connection, can't check for existing certificate");
    response.end(JSON.stringify({ "status": "error", "error": "Internal server error" }));
  }
}

// certificate passed in, yay!
function handleNewAuth (request, response) {
  var appId = request.body.appId,
      mode  = request.body.mode,
      cert  = request.body.cert,
      key   = request.body.key;

  if (redisClient && redisClient.connected) {
    redisClient.multi([ [ "mset", appId + "_" + mode + "_cert", cert, appId + "_" + mode + "_key", key ]]).exec(function (err, replies) {
      log.debug("Saved cert in Redis");
      sendMessage(request, response);
    });
  } else {
    log.info("No Redis connection, can't store cert");
    sendMessage(request, response);
  }
}

// entry for the module, handle the message
function handleMessage (request, response) {
  log.debug("Push to "+request.body.deviceId);

  if (request.body.cert !== undefined && request.body.key !== undefined) {
    // If a certificate is provided, store it in redis
    log.debug("New cert provided in request");
    handleNewAuth(request, response);
  } else {
    log.debug("No cert provided, attempt to look up cert in the cache");
    handleExistingAuth(request, response);
  }
}

exports.handleMessage = handleMessage;
<|endoftext|>


<commit_before>const fs = require('../core/fs.js');
const async = require('async');
const ProcessLauncher = require('../core/processes/processLauncher');
const utils = require('../utils/utils.js');
const constants = require('../constants');

class Pipeline {

  constructor(options) {
    this.env = options.env;
    this.buildDir = options.buildDir;
    this.contractsFiles = options.contractsFiles;
    this.assetFiles = options.assetFiles;
    this.events = options.events;
    this.logger = options.logger;
    this.plugins = options.plugins;
    this.webpackConfigName = options.webpackConfigName;
    this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
  }

  build(abi, contractsJSON, path, callback) {
    let self = this;
    const importsList = {};
    let placeholderPage;

    if (!this.assetFiles || !Object.keys(this.assetFiles).length) {
      return callback();
    }

    async.waterfall([
      function createPlaceholderPage(next){
        self.events.request('embark-building-placeholder', (html) => {
          fs.mkdirpSync(self.buildDir); // create dist/ folder if not already exists
          fs.writeFile(self.buildDir + 'index.html', html, next);
          // ^ ?? should this use path.join instead? templates' embark.json
          // files have a '/' hardcoded on the end of buildDir but that doesn't
          // seem too resilient
        });
      },
      function buildTheContracts(next) {
        self.buildContracts(next);
      },
      function buildWeb3(next) {
        self.buildWeb3JS(next);
      },
      function createImportList(next) {
        importsList["Embark/EmbarkJS"] = fs.dappPath(".embark", 'embark.js');
        importsList["Embark/web3"] = fs.dappPath(".embark", 'web3_instance.js');
        importsList["Embark/contracts"] = fs.dappPath(".embark/contracts", '');

        self.plugins.getPluginsProperty('imports', 'imports').forEach(function (importObject) {
          let [importName, importLocation] = importObject;
          importsList[importName] = importLocation;
        });

        next();
      },
      function writeContracts(next) {
        self.events.request('contracts:list', (_err, contracts) => {
          // ensure the .embark/contracts directory exists (create if not exists)
          fs.mkdirp(fs.dappPath(".embark/contracts", ''), (err) => {
            if(err) return next(err);

            // Create a file .embark/contracts/index.js that requires all contract files
            // Used to enable alternate import syntax:
            // e.g. import {Token} from 'Embark/contracts'
            // e.g. import * as Contracts from 'Embark/contracts'
            let importsHelperFile = fs.createWriteStream(fs.dappPath(".embark/contracts", 'index.js'));
            importsHelperFile.write('module.exports = {\n');

            async.eachOf(contracts, (contract, idx, eachCb) => {
              self.events.request('code-generator:contract', contract.className, (contractCode) => {
                let filePath = fs.dappPath(".embark/contracts", contract.className + '.js');
                importsList["Embark/contracts/" + contract.className] = filePath;
                fs.writeFile(filePath, contractCode, eachCb);

                // add the contract to the exports list to support alternate import syntax
                importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default`);
                if(idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add
              });
            }, function(){
              importsHelperFile.write('\n}'); // close the module.exports = {}
              importsHelperFile.close(next); // close the write stream
            });
          });
        });
      },
      function runWebpack(next) {
        self.logger.info(__('running webpack...'));
        let built = false;
        const webpackProcess = new ProcessLauncher({
          modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
          logger: self.logger,
          events: self.events,
          exitCallback: function (code) {
            if (!built) {
              return next(`Webpack build exited with code ${code} before the process finished`);
            }
            if (code) {
              self.logger(__('Webpack build process exited with code ', code));
            }
          }
        });
        webpackProcess.send({action: constants.pipeline.init, options: {webpackConfigName: self.webpackConfigName}});
        webpackProcess.send({action: constants.pipeline.build, assets: self.assetFiles, importsList});

        webpackProcess.once('result', constants.pipeline.built, (msg) => {
          built = true;
          webpackProcess.kill();
          return next(msg.error);
        });
      },
      function assetFileWrite(next) {
        async.eachOf(self.assetFiles, function (files, targetFile, cb) {
            async.map(files,
            const isDir = targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1;
            // if it's not a directory
            if (!isDir) {
              self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim);
            }
              function (file, fileCb) {
                self.logger.trace("reading " + file.filename);

                // Not a JS file
                if (file.filename.indexOf('.js') < 0) {
                  return file.content(function (fileContent) {
                    self.runPlugins(file, fileContent, fileCb);
                  });
                }

                // JS files
                async.waterfall([
                  function readFile(next) {
                    fs.readFile('./.embark/' + file.filename, (err, data) => {
                      if (err) {
                        return next(err);
                      }
                      next(null, data.toString());
                    });
                  },

                  function runPluginsOnContent(fileContent, next) {
                    self.runPlugins(file, fileContent, next);
                  }

                ], function (err, contentFile) {
                  if (err) {
                    self.logger.error(err.message || err);
                    return fileCb(err);
                  }

                  fileCb(null, contentFile);
                });
              },
              function (err, contentFiles) {
                if (err) {
                  self.logger.error(__('errors found while generating') + ' ' + targetFile);
                }
                let dir = targetFile.split('/').slice(0, -1).join('/');
                self.logger.trace("creating dir " + self.buildDir + dir);
                fs.mkdirpSync(self.buildDir + dir);

                // if it's a directory
                if (isDir) {
                  let targetDir = targetFile;

                  if (targetDir.slice(-1) !== '/') {
                    targetDir = targetDir + '/';
                  }

                  async.each(contentFiles, function (file, mapCb) {
                    let filename = file.filename.replace(file.basedir + '/', '');
                    self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);

                    fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb);
                  }, cb);
                  return;
                }

                let content = contentFiles.map(function (file) {
                  if (file === undefined) {
                    return "";
                  }
                  return file.content;
                }).join("\n");

                if(new RegExp(/^index.html?/i).test(targetFile)){
                  targetFile = targetFile.replace('index', 'index-temp');
                  placeholderPage = targetFile;
                }
                fs.writeFile(self.buildDir + targetFile, content, cb);
              }
            );
          },
          next);
      },
      function removePlaceholderPage(next){
        let placeholderFile = self.buildDir + placeholderPage;
        fs.access(self.buildDir + placeholderPage, (err) => {
          if (err) return next(); // index-temp doesn't exist, do nothing

          // rename index-temp.htm/l to index.htm/l, effectively replacing our placeholder page
          // with the contents of the built index.html page
          fs.move(placeholderFile, placeholderFile.replace('index-temp', 'index'), {overwrite: true}, next);
        });
      }
    ], callback);
  }

  runPlugins(file, fileContent, fileCb) {
    const self = this;
    if (self.pipelinePlugins.length <= 0) {
      return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
    }
    async.eachSeries(self.pipelinePlugins,
      function(plugin, pluginCB) {
        if (file.options && file.options.skipPipeline) {
          return pluginCB();
        }

        fileContent = plugin.runPipeline({targetFile: file.filename, source: fileContent});
        file.modified = true;
        pluginCB();
      },
      function (err) {
        if (err) {
          self.logger.error(err.message);
        }
        return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
      }
    );
  }

  buildContracts(cb) {
    const self = this;

    async.waterfall([
      function makeDirectory(next) {
        fs.mkdirp(fs.dappPath(self.buildDir, 'contracts'), (err, _result) => {
          next(err);
        });
      },
      function getContracts(next) {
        self.events.request('contracts:list', (err, contracts) => {
          next(err, contracts);
        });
      },
      function writeContractsJSON(contracts, next) {
        async.each(contracts, (contract, eachCb) => {
          fs.writeJson(fs.dappPath(self.buildDir, 'contracts', contract.className + ".json"), contract, {spaces: 2}, eachCb);
        }, () => { next(); });
      }
    ], cb);
  }

  buildWeb3JS(cb) {
    const self = this;
    async.waterfall([
      function makeDirectory(next) {
        fs.mkdirp(fs.dappPath(".embark"), (err, _result) => {
          next(err);
        });
      },
      function getWeb3Code(next) {
        self.events.request('code-generator:web3js', next);
      },
      function writeFile(code, next) {
        fs.writeFile(fs.dappPath(".embark", 'web3_instance.js'), code, next);
      }
    ], cb);
  }
}

module.exports = Pipeline;
<commit_msg>formatting<commit_after>const fs = require('../core/fs.js');
const async = require('async');
const ProcessLauncher = require('../core/processes/processLauncher');
const utils = require('../utils/utils.js');
const constants = require('../constants');

class Pipeline {

  constructor(options) {
    this.env = options.env;
    this.buildDir = options.buildDir;
    this.contractsFiles = options.contractsFiles;
    this.assetFiles = options.assetFiles;
    this.events = options.events;
    this.logger = options.logger;
    this.plugins = options.plugins;
    this.webpackConfigName = options.webpackConfigName;
    this.pipelinePlugins = this.plugins.getPluginsFor('pipeline');
  }

  build(abi, contractsJSON, path, callback) {
    let self = this;
    const importsList = {};
    let placeholderPage;

    if (!this.assetFiles || !Object.keys(this.assetFiles).length) {
      return callback();
    }

    async.waterfall([
      function createPlaceholderPage(next){
        self.events.request('embark-building-placeholder', (html) => {
          fs.mkdirpSync(self.buildDir); // create dist/ folder if not already exists
          fs.writeFile(self.buildDir + 'index.html', html, next);
          // ^ ?? should this use path.join instead? templates' embark.json
          // files have a '/' hardcoded on the end of buildDir but that doesn't
          // seem too resilient
        });
      },
      function buildTheContracts(next) {
        self.buildContracts(next);
      },
      function buildWeb3(next) {
        self.buildWeb3JS(next);
      },
      function createImportList(next) {
        importsList["Embark/EmbarkJS"] = fs.dappPath(".embark", 'embark.js');
        importsList["Embark/web3"] = fs.dappPath(".embark", 'web3_instance.js');
        importsList["Embark/contracts"] = fs.dappPath(".embark/contracts", '');

        self.plugins.getPluginsProperty('imports', 'imports').forEach(function (importObject) {
          let [importName, importLocation] = importObject;
          importsList[importName] = importLocation;
        });

        next();
      },
      function writeContracts(next) {
        self.events.request('contracts:list', (_err, contracts) => {
          // ensure the .embark/contracts directory exists (create if not exists)
          fs.mkdirp(fs.dappPath(".embark/contracts", ''), (err) => {
            if(err) return next(err);

            // Create a file .embark/contracts/index.js that requires all contract files
            // Used to enable alternate import syntax:
            // e.g. import {Token} from 'Embark/contracts'
            // e.g. import * as Contracts from 'Embark/contracts'
            let importsHelperFile = fs.createWriteStream(fs.dappPath(".embark/contracts", 'index.js'));
            importsHelperFile.write('module.exports = {\n');

            async.eachOf(contracts, (contract, idx, eachCb) => {
              self.events.request('code-generator:contract', contract.className, (contractCode) => {
                let filePath = fs.dappPath(".embark/contracts", contract.className + '.js');
                importsList["Embark/contracts/" + contract.className] = filePath;
                fs.writeFile(filePath, contractCode, eachCb);

                // add the contract to the exports list to support alternate import syntax
                importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default`);
                if(idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add
              });
            }, function(){
              importsHelperFile.write('\n}'); // close the module.exports = {}
              importsHelperFile.close(next); // close the write stream
            });
          });
        });
      },
      function runWebpack(next) {
        self.logger.info(__('running webpack...'));
        let built = false;
        const webpackProcess = new ProcessLauncher({
          modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
          logger: self.logger,
          events: self.events,
          exitCallback: function (code) {
            if (!built) {
              return next(`Webpack build exited with code ${code} before the process finished`);
            }
            if (code) {
              self.logger(__('Webpack build process exited with code ', code));
            }
          }
        });
        webpackProcess.send({action: constants.pipeline.init, options: {webpackConfigName: self.webpackConfigName}});
        webpackProcess.send({action: constants.pipeline.build, assets: self.assetFiles, importsList});

        webpackProcess.once('result', constants.pipeline.built, (msg) => {
          built = true;
          webpackProcess.kill();
          return next(msg.error);
        });
      },
      function assetFileWrite(next) {
        async.eachOf(
          self.assetFiles,
          function (files, targetFile, cb) {
            const isDir = targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1;
            // if it's not a directory
            if (!isDir) {
              self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim);
            }
            async.map(
              files,
              function (file, fileCb) {
                self.logger.trace("reading " + file.filename);

                // Not a JS file
                if (file.filename.indexOf('.js') < 0) {
                  return file.content(function (fileContent) {
                    self.runPlugins(file, fileContent, fileCb);
                  });
                }

                // JS files
                async.waterfall([
                  function readFile(next) {
                    fs.readFile('./.embark/' + file.filename, (err, data) => {
                      if (err) {
                        return next(err);
                      }
                      next(null, data.toString());
                    });
                  },

                  function runPluginsOnContent(fileContent, next) {
                    self.runPlugins(file, fileContent, next);
                  }

                ], function (err, contentFile) {
                  if (err) {
                    self.logger.error(err.message || err);
                    return fileCb(err);
                  }

                  fileCb(null, contentFile);
                });
              },
              function (err, contentFiles) {
                if (err) {
                  self.logger.error(__('errors found while generating') + ' ' + targetFile);
                }
                let dir = targetFile.split('/').slice(0, -1).join('/');
                self.logger.trace("creating dir " + self.buildDir + dir);
                fs.mkdirpSync(self.buildDir + dir);

                // if it's a directory
                if (isDir) {
                  let targetDir = targetFile;

                  if (targetDir.slice(-1) !== '/') {
                    targetDir = targetDir + '/';
                  }

                  async.each(contentFiles, function (file, mapCb) {
                    let filename = file.filename.replace(file.basedir + '/', '');
                    self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);

                    fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb);
                  }, cb);
                  return;
                }

                let content = contentFiles.map(function (file) {
                  if (file === undefined) {
                    return "";
                  }
                  return file.content;
                }).join("\n");

                if(new RegExp(/^index.html?/i).test(targetFile)){
                  targetFile = targetFile.replace('index', 'index-temp');
                  placeholderPage = targetFile;
                }
                fs.writeFile(self.buildDir + targetFile, content, cb);
              }
            );
          },
          next
        );
      },
      function removePlaceholderPage(next){
        let placeholderFile = self.buildDir + placeholderPage;
        fs.access(self.buildDir + placeholderPage, (err) => {
          if (err) return next(); // index-temp doesn't exist, do nothing

          // rename index-temp.htm/l to index.htm/l, effectively replacing our placeholder page
          // with the contents of the built index.html page
          fs.move(placeholderFile, placeholderFile.replace('index-temp', 'index'), {overwrite: true}, next);
        });
      }
    ], callback);
  }

  runPlugins(file, fileContent, fileCb) {
    const self = this;
    if (self.pipelinePlugins.length <= 0) {
      return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
    }
    async.eachSeries(
      self.pipelinePlugins,
      function(plugin, pluginCB) {
        if (file.options && file.options.skipPipeline) {
          return pluginCB();
        }

        fileContent = plugin.runPipeline({targetFile: file.filename, source: fileContent});
        file.modified = true;
        pluginCB();
      },
      function (err) {
        if (err) {
          self.logger.error(err.message);
        }
        return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
      }
    );
  }

  buildContracts(cb) {
    const self = this;

    async.waterfall([
      function makeDirectory(next) {
        fs.mkdirp(fs.dappPath(self.buildDir, 'contracts'), (err, _result) => {
          next(err);
        });
      },
      function getContracts(next) {
        self.events.request('contracts:list', (err, contracts) => {
          next(err, contracts);
        });
      },
      function writeContractsJSON(contracts, next) {
        async.each(contracts, (contract, eachCb) => {
          fs.writeJson(fs.dappPath(self.buildDir, 'contracts', contract.className + ".json"), contract, {spaces: 2}, eachCb);
        }, () => { next(); });
      }
    ], cb);
  }

  buildWeb3JS(cb) {
    const self = this;
    async.waterfall([
      function makeDirectory(next) {
        fs.mkdirp(fs.dappPath(".embark"), (err, _result) => {
          next(err);
        });
      },
      function getWeb3Code(next) {
        self.events.request('code-generator:web3js', next);
      },
      function writeFile(code, next) {
        fs.writeFile(fs.dappPath(".embark", 'web3_instance.js'), code, next);
      }
    ], cb);
  }
}

module.exports = Pipeline;
<|endoftext|>