<commit_before>class Execute {
    static getByPath(obj, key) {
        if (key.length === 0) {
            return obj;
        }

        let keys = key.split(".");

        for (let i = 0; i < keys.length; i++) {
            obj = obj[keys[i]];
        }

        return obj;
    }

    static addPrefixToPath(path, data) {
        let obj = {};
        let pointer = obj;

        let keys = path.split(".");

        for (let i = 0; i < keys.length; i++) {
            pointer[keys[i]] = {};

            if (i === keys.length - 1) {
                pointer[keys[i]] = data;
            } else {
                pointer = pointer[keys[i]];
            }
        }

        return obj;
    }

    static spreadify(deepCopy) {
        return function () {
            // Holds the processed arguments for use by `fn`
            let spreadArgs = {};

            // Caching length
            let length = arguments.length;

            let currentArg;

            for (let i = 0; i < length; i++) {
                currentArg = arguments[i];

                Object.keys(currentArg).map((key) => {
                    if (deepCopy &&
                        typeof(spreadArgs[key]) === "object" && spreadArgs[key] !== null &&
                        currentArg[key] !== null

                    ) {
                        spreadArgs[key] = Execute.spreadify(deepCopy)(spreadArgs[key], currentArg[key]);
                    } else {
                        spreadArgs[key] = currentArg[key];
                    }
                });
            }
            return spreadArgs;
        };
    }

    constructor(options) {
        let defaultOption = {
            logger: console,
            cache: require("./tiny-cache")
        };

        // Add all action handlers
        this._actions = {
            "default": Execute.defaultAction.bind(this),
            "map": Execute.mapActionHandler.bind(this)
        };

        this._options = Execute.spreadify()(defaultOption, options || {});
    }

    static defaultAction(action, executionData, options) {
        return Promise.resolve(action(executionData, options));
    }

    static mapActionHandler(action, executionData, options) {
        /*
            action should be object with
            {
                array: function that produce an array,
                reducer: child step to execute for each element in the array.
            }
         */
        let final = [];

        return action.array(executionData).reduce((promise, item) => {
            return promise
                .then(() => {
                    return Promise
                        .resolve(action.reducer(item, options))
                        .then(result => {
                            final.push(result);
                            return final;
                        });
                })
                .catch(console.error);

        }, Promise.resolve());
    }

    use(middleware) {
        if(!middleware.type) {
            throw new Error("type is missing in middleware contract");
        }

        switch(middleware.type) {
            case "action":
                return this.addActionMiddleware(middleware);
            default:
                throw new Error("Unknown middleware type");
        }
    }

    addActionMiddleware(middleware) {
        if(!middleware.action) {
            throw new Error("middleware action is missing");
        }

        if(!middleware.name) {
            throw new Error("middleware name is missing");
        }

        this._actions[middleware.name] = middleware.action;
        return true;
    }

    run(executionTree, executionData) {
        return this.processSteps(executionTree, executionData).then((response) => response.result);
    }

    goToNextStep(step, executionData) {
        const testResult = typeof step.test === "function"
            ? step.test(executionData) // call the test with the results from the action
            : step.test;

        this._options.logger.info(`Test result: ${testResult}`);
        // get a reference to the next step based on the test result
        const nextStep = step.if[testResult] || step.if.default;

        if( typeof(nextStep) === "number") {
            // next step is not an execution tree but a predefined signal
            return Promise.resolve({result: {}, signal: nextStep});
        }
        else {
            // TODO: better handeling if the next step is missing.
            return nextStep ?
                this.processSteps(nextStep, executionData) :
                Promise.reject("Unhandled scenario");
        }
    }

    executeStepActionWithRetry(step, executionData) {
        let action = this._actions[step.actionType](step.action, executionData, this._options);

        for (let i = 0; i < step.errorHandling.maxAttempts; i++) {
            action = action.catch((e) => {
                if (step.errorHandling.tryCondition(e)) {
                    this._options.logger.info(`Step: ${step.title} try ${i + 1} failed. Retrying...`);
                    return Promise.resolve(step.action(executionData, this._options));
                }

                return Promise.reject(e);
            });
        }
        return action;
    }

    executeStepActionWithCache(step, executionData) {
        if (step.cache.enable) {
            let cacheKey = step.cache.key(executionData);
            return Promise.resolve(this._options.cache.has(cacheKey))
                .then((hasData) => {
                    console.log("Has Data:", hasData);
                    if (hasData) {
                        return Promise.resolve(this._options.cache.get(cacheKey));
                    } else {
                        return this.executeStepActionWithRetry(step, executionData).then((data) => {
                            return Promise.resolve(this._options.cache.set(cacheKey, data, step.cache.ttl)).then((set_result) => {
                                console.log("Set Data in Cache result:", set_result);
                                return Promise.resolve(data);
                            });
                        });
                    }
                });
        } else {
            return this.executeStepActionWithRetry(step, executionData);
        }
    }

    executeStepActionAndHandleError(step, executionData) {
        return this.executeStepActionWithCache(step, executionData)
            .catch((e) => {
                return step.errorHandling.continueOnError ?
                    Promise.resolve(step.errorHandling.onErrorResponse) :
                    Promise.reject(e);
            });
    }

    processStep(step, executionData) {

        let _step = Execute.spreadify(true)(Execute.executionTreeDefaultSetting.steps[0], step);
        let allData = Execute.spreadify()(executionData, {});

        this._options.logger.info(`Step: ${_step.title}`);

        return new Promise((resolve, reject) => {

            this.executeStepActionAndHandleError(_step, executionData).then((result) => {

                let _output = Execute.spreadify(true)(
                    Execute.executionTreeDefaultSetting.steps[0].output,
                    _step.output);

                let _result = {};

                if (_output.map.destination.length !== 0) {
                    _result = Execute.addPrefixToPath(_output.map.destination, Execute.getByPath(result, _output.map.source));
                }
                else {
                    _result = Execute.getByPath(result, _output.map.source);
                }

                this._options.logger.info(`Action result: ${JSON.stringify(_result)}`);

                if (_output.accessibleToNextSteps) {
                    allData = Execute.spreadify()(allData, _result);
                }

                // if there's a test defined, then actionResult must be a promise
                // so pass the promise response to goToNextStep
                if ("test" in _step) {
                    this.goToNextStep(_step, allData).then((childResponse) => {
                        this._options.logger.info(`Child result: ${JSON.stringify(childResponse.result)}`);

                        childResponse.result = Execute.spreadify(true)(result, childResponse.result);

                        if (_output.map.destination.length !== 0) {
                            childResponse.result = Execute.addPrefixToPath(_output.map.destination, Execute.getByPath(childResponse.result, _output.map.source));
                        }
                        else {
                            childResponse.result = Execute.getByPath(childResponse.result, _output.map.source);
                        }

                        this._options.logger.info(`Total result: ${JSON.stringify(childResponse.result)}`);

                        resolve(childResponse);
                    }).catch((e) => {
                        reject(e);
                    });
                } else {
                    resolve({result: _result, signal: Execute.executionMode.CONTINUE});
                }
            }).catch((e) => {
                reject(e);
            });
        });
    }

    processSteps(executionTree, executionData) {

        let _executionTree;

        if (Object.prototype.toString.call(executionTree) === "[object Array]") {
            // if executionTree is array then we need to convert it to proper object with all missing properties.
            _executionTree = {
                steps: executionTree
            };
        } else {
            _executionTree = executionTree;
        }

        _executionTree = Execute.spreadify()(Execute.executionTreeDefaultSetting, _executionTree);

        let finalResult = {};
        let finalSignal = Execute.executionMode.CONTINUE;

        let allData = Execute.spreadify()(executionData, {});
        let throttleActions = require("./throttle-actions");

        let ps = [];
        _executionTree.steps.map((step) => {

            let _step = Execute.spreadify()(
                Execute.executionTreeDefaultSetting.steps[0],
                step);

            ps.push(() => {
                return this.processStep(step, allData).then((response) => {

                    finalSignal = Math.max(finalSignal, response.signal);

                    let _output = Execute.spreadify()(
                        Execute.executionTreeDefaultSetting.steps[0].output,
                        _step.output);

                    if (_output.accessibleToNextSteps) {
                        allData = Execute.spreadify()(allData, response.result);
                    }

                    let _stepResult = {};

                    if (_output.addToResult) {
                        _stepResult = response.result;
                    }

                    finalResult = Execute.spreadify(true)(finalResult, _stepResult);

                    return {result: finalResult, signal: finalSignal};
                });
            });
        });

        return throttleActions(ps, _executionTree.concurrency).then(() => { return {result: finalResult, signal: finalSignal};});

    }

}

// Because static keyword works only for method
Execute.executionMode = {
    CONTINUE: 0,
    STOP_LEVEL_EXECUTION: 1,
    STOP_ENTIRE_EXECUTION: 2
};

Execute.executionTreeDefaultSetting = {
    concurrency: 1,
    steps: [
        {
            errorHandling: {
                maxAttempts: 0,
                tryCondition: () => true,
                continueOnError: false,
                onErrorResponse: {}
            },
            cache: {
                enable: false,
                ttl: 60
            },
            output: {
                accessibleToNextSteps: true,
                addToResult: true,
                map: {
                    source: "",
                    destination: ""
                }
            },
            actionType: "default",
            action: () => {
                return {};
            }
        }
    ]
};

module.exports = Execute;
<commit_msg>Integrate action throttle to processSteps<commit_after>class Execute {
    static getByPath(obj, key) {
        if (key.length === 0) {
            return obj;
        }

        let keys = key.split(".");

        for (let i = 0; i < keys.length; i++) {
            obj = obj[keys[i]];
        }

        return obj;
    }

    static addPrefixToPath(path, data) {
        let obj = {};
        let pointer = obj;

        let keys = path.split(".");

        for (let i = 0; i < keys.length; i++) {
            pointer[keys[i]] = {};

            if (i === keys.length - 1) {
                pointer[keys[i]] = data;
            } else {
                pointer = pointer[keys[i]];
            }
        }

        return obj;
    }

    static spreadify(deepCopy) {
        return function () {
            // Holds the processed arguments for use by `fn`
            let spreadArgs = {};

            // Caching length
            let length = arguments.length;

            let currentArg;

            for (let i = 0; i < length; i++) {
                currentArg = arguments[i];

                Object.keys(currentArg).map((key) => {
                    if (deepCopy &&
                        typeof(spreadArgs[key]) === "object" && spreadArgs[key] !== null &&
                        currentArg[key] !== null

                    ) {
                        spreadArgs[key] = Execute.spreadify(deepCopy)(spreadArgs[key], currentArg[key]);
                    } else {
                        spreadArgs[key] = currentArg[key];
                    }
                });
            }
            return spreadArgs;
        };
    }

    constructor(options) {
        let defaultOption = {
            logger: console,
            cache: require("./tiny-cache")
        };

        // Add all action handlers
        this._actions = {
            "default": Execute.defaultAction.bind(this),
            "map": Execute.mapActionHandler.bind(this)
        };

        this._options = Execute.spreadify()(defaultOption, options || {});
    }

    static defaultAction(action, executionData, options) {
        return Promise.resolve(action(executionData, options));
    }

    static mapActionHandler(action, executionData, options) {
        /*
            action should be object with
            {
                array: function that produce an array,
                reducer: child step to execute for each element in the array.
            }
         */
        let final = [];

        return action.array(executionData).reduce((promise, item) => {
            return promise
                .then(() => {
                    return Promise
                        .resolve(action.reducer(item, options))
                        .then(result => {
                            final.push(result);
                            return final;
                        });
                })
                .catch(console.error);

        }, Promise.resolve());
    }

    use(middleware) {
        if (!middleware.type) {
            throw new Error("type is missing in middleware contract");
        }

        switch (middleware.type) {
            case "action":
                return this.addActionMiddleware(middleware);
            default:
                throw new Error("Unknown middleware type");
        }
    }

    addActionMiddleware(middleware) {
        if (!middleware.action) {
            throw new Error("middleware action is missing");
        }

        if (!middleware.name) {
            throw new Error("middleware name is missing");
        }

        this._actions[middleware.name] = middleware.action;
        return true;
    }

    run(executionTree, executionData) {
        return this.processSteps(executionTree, executionData).then((response) => response.result);
    }

    goToNextStep(step, executionData) {
        const testResult = typeof step.test === "function"
            ? step.test(executionData) // call the test with the results from the action
            : step.test;

        this._options.logger.info(`Test result: ${testResult}`);
        // get a reference to the next step based on the test result
        const nextStep = step.if[testResult] || step.if.default;

        if (typeof(nextStep) === "number") {
            // next step is not an execution tree but a predefined signal
            return Promise.resolve({result: {}, signal: nextStep});
        }
        else {
            // TODO: better handeling if the next step is missing.
            return nextStep ?
                this.processSteps(nextStep, executionData) :
                Promise.reject("Unhandled scenario");
        }
    }

    executeStepActionWithRetry(step, executionData) {
        let action = this._actions[step.actionType](step.action, executionData, this._options);

        for (let i = 0; i < step.errorHandling.maxAttempts; i++) {
            action = action.catch((e) => {
                if (step.errorHandling.tryCondition(e)) {
                    this._options.logger.info(`Step: ${step.title} try ${i + 1} failed. Retrying...`);
                    return Promise.resolve(step.action(executionData, this._options));
                }

                return Promise.reject(e);
            });
        }
        return action;
    }

    executeStepActionWithCache(step, executionData) {
        if (step.cache.enable) {
            let cacheKey = step.cache.key(executionData);
            return Promise.resolve(this._options.cache.has(cacheKey))
                .then((hasData) => {
                    console.log("Has Data:", hasData);
                    if (hasData) {
                        return Promise.resolve(this._options.cache.get(cacheKey));
                    } else {
                        return this.executeStepActionWithRetry(step, executionData).then((data) => {
                            return Promise.resolve(this._options.cache.set(cacheKey, data, step.cache.ttl)).then((set_result) => {
                                console.log("Set Data in Cache result:", set_result);
                                return Promise.resolve(data);
                            });
                        });
                    }
                });
        } else {
            return this.executeStepActionWithRetry(step, executionData);
        }
    }

    executeStepActionAndHandleError(step, executionData) {
        return this.executeStepActionWithCache(step, executionData)
            .catch((e) => {
                return step.errorHandling.continueOnError ?
                    Promise.resolve(step.errorHandling.onErrorResponse) :
                    Promise.reject(e);
            });
    }

    processStep(step, executionData) {

        let _step = Execute.spreadify(true)(Execute.executionTreeDefaultSetting.steps[0], step);
        let allData = Execute.spreadify()(executionData, {});

        this._options.logger.info(`Step: ${_step.title}`);

        return new Promise((resolve, reject) => {

            this.executeStepActionAndHandleError(_step, executionData).then((result) => {

                let _output = Execute.spreadify(true)(
                    Execute.executionTreeDefaultSetting.steps[0].output,
                    _step.output);

                let _result = {};

                if (_output.map.destination.length !== 0) {
                    _result = Execute.addPrefixToPath(_output.map.destination, Execute.getByPath(result, _output.map.source));
                }
                else {
                    _result = Execute.getByPath(result, _output.map.source);
                }

                this._options.logger.info(`Action result: ${JSON.stringify(_result)}`);

                if (_output.accessibleToNextSteps) {
                    allData = Execute.spreadify()(allData, _result);
                }

                // if there's a test defined, then actionResult must be a promise
                // so pass the promise response to goToNextStep
                if ("test" in _step) {
                    this.goToNextStep(_step, allData).then((childResponse) => {
                        this._options.logger.info(`Child result: ${JSON.stringify(childResponse.result)}`);

                        childResponse.result = Execute.spreadify(true)(result, childResponse.result);

                        if (_output.map.destination.length !== 0) {
                            childResponse.result = Execute.addPrefixToPath(_output.map.destination, Execute.getByPath(childResponse.result, _output.map.source));
                        }
                        else {
                            childResponse.result = Execute.getByPath(childResponse.result, _output.map.source);
                        }

                        this._options.logger.info(`Total result: ${JSON.stringify(childResponse.result)}`);

                        resolve(childResponse);
                    }).catch((e) => {
                        reject(e);
                    });
                } else {
                    resolve({result: _result, signal: Execute.executionMode.CONTINUE});
                }
            }).catch((e) => {
                reject(e);
            });
        });
    }

    processSteps(executionTree, executionData) {

        let _executionTree;

        if (Object.prototype.toString.call(executionTree) === "[object Array]") {
            // if executionTree is array then we need to convert it to proper object with all missing properties.
            _executionTree = {
                steps: executionTree
            };
        } else {
            _executionTree = executionTree;
        }

        _executionTree = Execute.spreadify()(Execute.executionTreeDefaultSetting, _executionTree);

        let finalResult = {};
        let finalSignal = Execute.executionMode.CONTINUE;

        let allData = Execute.spreadify()(executionData, {});

        let ps = [];
        _executionTree.steps.map((step) => {

            let _step = Execute.spreadify()(
                Execute.executionTreeDefaultSetting.steps[0],
                step);

            ps.push(() => {
                return this.processStep(step, allData)
                    .then(response => {
                        finalSignal = Math.max(finalSignal, response.signal);

                        let _output = Execute.spreadify()(
                            Execute.executionTreeDefaultSetting.steps[0].output,
                            _step.output);

                        if (_output.accessibleToNextSteps) {
                            allData = Execute.spreadify()(allData, response.result);
                        }

                        let _stepResult = {};

                        if (_output.addToResult) {
                            _stepResult = response.result;
                        }

                        finalResult = Execute.spreadify(true)(finalResult, _stepResult);

                        return {result: finalResult, signal: finalSignal};
                    });
            });
        });

        let i = 0;

        function doNextAction() {
            if (i < ps.length) {
                return ps[i++]().then(doNextAction);
            }
        }

        let listOfPromises = [];
        while (i < _executionTree.concurrency && i < ps.length) {
            listOfPromises.push(doNextAction());
        }
        return Promise.all(listOfPromises).then(() => {
            return {result: finalResult, signal: finalSignal};
        });

    }

}

// Because static keyword works only for method
Execute.executionMode = {
    CONTINUE: 0,
    STOP_LEVEL_EXECUTION: 1,
    STOP_ENTIRE_EXECUTION: 2
};

Execute.executionTreeDefaultSetting = {
    concurrency: 1,
    steps: [
        {
            errorHandling: {
                maxAttempts: 0,
                tryCondition: () => true,
                continueOnError: false,
                onErrorResponse: {}
            },
            cache: {
                enable: false,
                ttl: 60
            },
            output: {
                accessibleToNextSteps: true,
                addToResult: true,
                map: {
                    source: "",
                    destination: ""
                }
            },
            actionType: "default",
            action: () => {
                return {};
            }
        }
    ]
};

module.exports = Execute;
<|endoftext|>


<commit_before>'use strict';
const documentation = require('./documentation');
const models = require('./models');
const fs = require('fs');
const downloadDocumentation = require('./downloadDocumentation');

class ServerlessAWSDocumentation {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.options = options;
    this.provider = 'aws';
    this.fs = fs;

    Object.assign(this, models);
    Object.assign(this, documentation());

    this.customVars = this.serverless.variables.service.custom;
    const naming = this.serverless.providers.aws.naming;
    this.getMethodLogicalId = naming.getMethodLogicalId.bind(naming);
    this.normalizePath = naming.normalizePath.bind(naming);

    this._beforeDeploy = this.beforeDeploy.bind(this)
    this._afterDeploy = this.afterDeploy.bind(this)
    this._download = downloadDocumentation.downloadDocumentation.bind(this)

    this.hooks = {
      'before:package:finalize': this._beforeDeploy,
      'after:deploy:deploy': this._afterDeploy,
      'downloadDocumentation:downloadDocumentation': this._download
    };

    this.documentationParts = [];

    this.commands = {
        downloadDocumentation: {
            usage: 'Download API Gateway documentation from AWS',
            lifecycleEvents: [
              'downloadDocumentation',
            ],
            options: {
                outputFileName: {
                    required: true,
                },
            },
        }
    };
  }

  beforeDeploy() {
    if (!(this.customVars && this.customVars.documentation)) return;

    this.cfTemplate = this.serverless.service.provider.compiledCloudFormationTemplate;

    // The default rest API reference
    let restApiId = {
      Ref: 'ApiGatewayRestApi',
    };

    // Use the provider API gateway if one has been provided.
    if (this.serverless.service.provider.apiGateway && this.serverless.service.provider.apiGateway.restApiId) {
      restApiId = this.serverless.service.provider.apiGateway.restApiId
    }

    if (this.customVars.documentation.models) {
      const cfModelCreator = this.createCfModel(restApiId);

      // Add model resources
      const models = this.customVars.documentation.models.map(cfModelCreator)
        .reduce((modelObj, model) => {
          modelObj[`${model.Properties.Name}Model`] = model;
          return modelObj;
        }, {});
      Object.assign(this.cfTemplate.Resources, models);
    }

    // Add models to method resources
    this.serverless.service.getAllFunctions().forEach(functionName => {
      const func = this.serverless.service.getFunction(functionName);
      func.events.forEach(this.updateCfTemplateFromHttp.bind(this));
    });

    // Add models
    this.cfTemplate.Outputs.AwsDocApiId = {
      Description: 'API ID',
      Value: restApiId,
    };
  }

  afterDeploy() {
    if (!this.customVars.documentation) return;
    const stackName = this.serverless.providers.aws.naming.getStackName(this.options.stage);
    return this.serverless.providers.aws.request('CloudFormation', 'describeStacks', { StackName: stackName },
      this.options.stage,
      this.options.region
    ).then(this._buildDocumentation.bind(this))
    .catch(err => {
      if (err === 'documentation version already exists, skipping upload') {
        return Promise.resolve();
      }

      return Promise.reject(err);
    });
  }

}

module.exports = ServerlessAWSDocumentation;
<commit_msg>Fix documentation download (#81)<commit_after>'use strict';
const documentation = require('./documentation');
const models = require('./models');
const fs = require('fs');
const downloadDocumentation = require('./downloadDocumentation');

class ServerlessAWSDocumentation {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.options = options;
    this.provider = 'aws';
    this.fs = fs;

    Object.assign(this, models);
    Object.assign(this, documentation());
    Object.assign(this, downloadDocumentation);

    this.customVars = this.serverless.variables.service.custom;
    const naming = this.serverless.providers.aws.naming;
    this.getMethodLogicalId = naming.getMethodLogicalId.bind(naming);
    this.normalizePath = naming.normalizePath.bind(naming);

    this._beforeDeploy = this.beforeDeploy.bind(this)
    this._afterDeploy = this.afterDeploy.bind(this)
    this._download = downloadDocumentation.downloadDocumentation.bind(this)

    this.hooks = {
      'before:package:finalize': this._beforeDeploy,
      'after:deploy:deploy': this._afterDeploy,
      'downloadDocumentation:downloadDocumentation': this._download
    };

    this.documentationParts = [];

    this.commands = {
        downloadDocumentation: {
            usage: 'Download API Gateway documentation from AWS',
            lifecycleEvents: [
              'downloadDocumentation',
            ],
            options: {
                outputFileName: {
                    required: true,
                },
            },
        }
    };
  }

  beforeDeploy() {
    if (!(this.customVars && this.customVars.documentation)) return;

    this.cfTemplate = this.serverless.service.provider.compiledCloudFormationTemplate;

    // The default rest API reference
    let restApiId = {
      Ref: 'ApiGatewayRestApi',
    };

    // Use the provider API gateway if one has been provided.
    if (this.serverless.service.provider.apiGateway && this.serverless.service.provider.apiGateway.restApiId) {
      restApiId = this.serverless.service.provider.apiGateway.restApiId
    }

    if (this.customVars.documentation.models) {
      const cfModelCreator = this.createCfModel(restApiId);

      // Add model resources
      const models = this.customVars.documentation.models.map(cfModelCreator)
        .reduce((modelObj, model) => {
          modelObj[`${model.Properties.Name}Model`] = model;
          return modelObj;
        }, {});
      Object.assign(this.cfTemplate.Resources, models);
    }

    // Add models to method resources
    this.serverless.service.getAllFunctions().forEach(functionName => {
      const func = this.serverless.service.getFunction(functionName);
      func.events.forEach(this.updateCfTemplateFromHttp.bind(this));
    });

    // Add models
    this.cfTemplate.Outputs.AwsDocApiId = {
      Description: 'API ID',
      Value: restApiId,
    };
  }

  afterDeploy() {
    if (!this.customVars.documentation) return;
    const stackName = this.serverless.providers.aws.naming.getStackName(this.options.stage);
    return this.serverless.providers.aws.request('CloudFormation', 'describeStacks', { StackName: stackName },
      this.options.stage,
      this.options.region
    ).then(this._buildDocumentation.bind(this))
    .catch(err => {
      if (err === 'documentation version already exists, skipping upload') {
        return Promise.resolve();
      }

      return Promise.reject(err);
    });
  }

}

module.exports = ServerlessAWSDocumentation;
<|endoftext|>


<commit_before>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const _ = require('underscore');
const extend = _.extend;
const pick = _.pick;

var format = require('./utilities/format');
var comparators = require('./utilities/comparators');

const I18nDataV2 = require('./i18n/v2');
const I18nDataV3 = require('./i18n/v3');
const PersonalityProfileV2 = require('./profiles/v2/index');
const PersonalityProfileV3 = require('./profiles/v3/index');
const defaultVersion = 'v2';

const CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS = 'EANOC';

class TextSummary {

  constructor(options) {
    this._options = extend(this.defaultOptions(), pick(options, 'locale', 'format', 'version'));
    this._version = typeof this._options.version !== 'undefined' ? this._options.version : defaultVersion;

    // Select localization dictionary based on the version of PI, V2 or V3
    if (this._version === 'v3'){
      this._i18n = new I18nDataV3(this._options.locale);
    } else{
      this._i18n = new I18nDataV2(this._options.locale);
    }

    this._dictionary = this._i18n.data();
    this._translatorFactory = this._i18n.translatorFactory();
    this._translator = this._translatorFactory.createTranslator(this._dictionary.phrases);

    this.circumplexData = this._dictionary.traits;
    this.facetsData = this._dictionary.facets;
    this.valuesData = this._dictionary.values;
    this.needsData = this._dictionary.needs;
  }

  defaultOptions() {
    return {
      locale: 'en',
      version: 'v2'
    };
  }

  //getSummary(profile) {
  getSummary(profile) {
    var personalityProfile = this._version === 'v3' ? new PersonalityProfileV3(profile) : new PersonalityProfileV2(profile);
    this._personalityProfile = personalityProfile;
    return this.assemble(personalityProfile).map(function (paragraph) { return paragraph.join(' '); }).join('\n');
  }

  //assemble(tree) {
  assemble(profile) {
    return [
      this.assembleTraits(profile.traits()),
      this.assembleFacets(profile.traits()),
      this.assembleNeeds(profile.needs()),
      this.assembleValues(profile.values())
    ];
  }

  //assembleTraits(personalityTree) {
  assembleTraits(traits) {
    var
      sentences = [],
      big5elements = [],
      relevantBig5,
      adj1, adj2, adj3;

    // Sort the Big 5 based on how extreme the number is.
    traits.forEach(function (p) {
      big5elements.push({
        id: p.id,
        percentage: p.score
      });
    });

    big5elements.sort(comparators.compareByRelevance);

    // Remove all traits with percentage values between 32% and 68%, as it's inside the common people.
    relevantBig5 = big5elements.filter(function (item) {
      return Math.abs(0.5 - item.percentage) > 0.18;
    });

    if (relevantBig5.length < 2) {
      // Even if no Big 5 attribute is interesting, you get 1 adjective.
      relevantBig5 = [big5elements[0], big5elements[1]];
    }

    adj1 = relevantBig5.length >=2 ? this.getCircumplexAdjective(relevantBig5[0], relevantBig5[1], 0) : null;
    adj2 = relevantBig5.length >=3 ? this.getCircumplexAdjective(relevantBig5[1], relevantBig5[2], 1) : null;
    adj3 = relevantBig5.length >=4 ? this.getCircumplexAdjective(relevantBig5[2], relevantBig5[3], 2) : null;

    // Generate sentences summarizing personality traits composed of cross trait phrases
    switch (relevantBig5.length) {
    case 2:
      sentences.push(format(this._translator('You are %s'), adj1) + '.');
      break;
    case 3:
      sentences.push(format(this._translator('You are %s and %s'),  adj1, adj2) + '.');
      break;
    case 4:
    case 5:
      sentences.push(format(this._translator('You are %s, %s and %s'),  adj1, adj2, adj3) + '.');
      break;
    }

    return sentences;
  }

  //assembleFacets(personalityTree) {
  assembleFacets(traits) {
    var
      sentences = [],
      facetElements = [],
      info,
      i;

    // Assemble the full list of facets and sort them based on how extreme
    // is the number.
    traits.forEach(function (p) {
      p.facets.forEach(function (f) {
        facetElements.push({
          id: f.id,
          percentage: f.score,
          parent: p
        });
      });
    });
    facetElements.sort(comparators.compareByRelevance);

    info = this.getFacetInfo(facetElements[0]);
    sentences.push(format(this._translator('You are %s'), info.term) + ': ' + info.description + '.');
    info = this.getFacetInfo(facetElements[1]);
    sentences.push(format(this._translator('You are %s'), info.term) + ': ' + info.description + '.');

    // If all the facets correspond to the same feature, continue until a
    // different parent feature is found.
    i = 2;
    if (facetElements[0].parent === facetElements[1].parent) {
      while (facetElements[0].parent === facetElements[i].parent) {
        i += 1;
      }
    }
    info = this.getFacetInfo(facetElements[i]);
    sentences.push(format(this._translator('And you are %s'), info.term) + ': ' + info.description + '.');

    return sentences;
  }

  //assembleNeeds(needsTree) {
  assembleNeeds(needs) {
    var
      sentences = [],
      needsList = [],
      word,
      sentence;

    needs.forEach(function (n) {
      needsList.push({
        id: n.id,
        percentage: n.score
      });
    });
    needsList.sort(comparators.compareByValue);

    // Get the words required.
    var need = needsList[0];
    word = (this.needsData[need.id])[0];

    // Form the right sentence for the single need.
    switch (this.intervalFor(needsList[0].percentage)) {
    case 0:
      sentence = this._translator('Experiences that make you feel high %s are generally unappealing to you');
      break;
    case 1:
      sentence = this._translator('Experiences that give a sense of %s hold some appeal to you');
      break;
    case 2:
      sentence = this._translator('You are motivated to seek out experiences that provide a strong feeling of %s');
      break;
    case 3:
      sentence = this._translator('Your choices are driven by a desire for %s');
      break;
    }
    sentence = format(sentence, word).concat('.');
    sentences.push(sentence);

    return sentences;
  }

  assembleValues(values) {
    var
      sentences = [],
      valuesList = [],
      info1, info2,
      sentence,
      valuesInfo,
      i, term1, term2;

    values.forEach(function (v) {
      valuesList.push({
        id: v.id,
        percentage: v.score
      });
    });
    valuesList.sort(comparators.compareByRelevance);

    // Get all the text and data required.
    info1 = this.getValueInfo(valuesList[0]);
    info2 = this.getValueInfo(valuesList[1]);

    if (this.intervalFor(valuesList[0].percentage) === this.intervalFor(valuesList[1].percentage)) {
      // Assemble the first 'both' sentence.
      term1 = info1.term;
      term2 = info2.term;
      switch (this.intervalFor(valuesList[0].percentage)) {
      case 0:
        sentence = format(this._translator('You are relatively unconcerned with both %s and %s'), term1, term2) + '.';
        break;
      case 1:
        sentence = format(this._translator('You don\'t find either %s or %s to be particularly motivating for you'), term1, term2) + '.';
        break;
      case 2:
        sentence = format(this._translator('You value both %s and %s a bit'), term1, term2) + '.';
        break;
      case 3:
        sentence = format(this._translator('You consider both %s and %s to guide a large part of what you do'), term1, term2) + '.';
        break;
      }
      sentences.push(sentence);

      // Assemble the final strings in the correct format.
      sentences.push(info1.description + '.');
      sentences.push(format(this._translator('And %s'), info2.description.toLowerCase()) + '.');
    } else {
      valuesInfo = [info1, info2];
      for (i = 0; i < valuesInfo.length; i += 1) {
        // Process it this way because the code is the same.
        switch (this.intervalFor(valuesList[i].percentage)) {
        case 0:
          sentence = format(this._translator('You are relatively unconcerned with %s'), valuesInfo[i].term);
          break;
        case 1:
          sentence = format(this._translator('You don\'t find %s to be particularly motivating for you'), valuesInfo[i].term);
          break;
        case 2:
          sentence = format(this._translator('You value %s a bit more'),  valuesInfo[i].term);
          break;
        case 3:
          sentence = format(this._translator('You consider %s to guide a large part of what you do'),  valuesInfo[i].term);
          break;
        }
        sentence = sentence.concat(': ').
            concat(valuesInfo[i].description.toLowerCase()).
            concat('.');
        sentences.push(sentence);
      }
    }

    return sentences;
  }

  /**
  *  getCircumplexAdjective generates a qualified cross trait phrase given two personality traits, p1 and p2
  *  p1 and p2: personality traits of the form {"id":<id>,"percentage":<percentage>}
  *  order: determines which qualifier to use for the cross trait adjective
  *      e.g., terms that are viewed negatively in Western culture are qualified with 'a bit' or 'somewhat'
  *         0: 'a bit'
  *         1: 'somewhat'
  *         2: 'can be perceived as'
  *  Returns the qualified cross trait adjective phrase for p1 and p2.
  */
  getCircumplexAdjective(p1, p2, order) {
    var version = this._version;
    var ordered = [p1, p2].sort(function (o1, o2) {
      var i1, i2;
      if (version === 'v3') {
        i1 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o1.id.replace('big5_', '').charAt(0).toUpperCase());
        i2 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o2.id.replace('big5_', '').charAt(0).toUpperCase());
      }else{
        i1 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o1.id.charAt(0));
        i2 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o2.id.charAt(0));
      }
      return i1 < i2 ? -1 : 1;
    });

    // Assemble the identifier as the JSON file stored it.
    var identifier = ordered[0].id.
        concat(ordered[0].percentage > 0.5 ? '_plus_' : '_minus_').
        concat(ordered[1].id).
        concat(ordered[1].percentage > 0.5 ? '_plus' : '_minus');

    var traitMult = this.circumplexData[identifier][0];
    var sentence = '%s';

    if (traitMult.perceived_negatively) {
      switch (order) {
      case 0:
        sentence = this._translator('a bit %s');
        break;
      case 1:
        sentence = this._translator('somewhat %s');
        break;
      case 2:
        sentence = this._translator('can be perceived as %s');
        break;
      }
    }

    return format(sentence, traitMult.word);
  }

  getValueInfo(v) {
    return {
      name: v.id,
      term: this.valuesData[v.id][0].Term.toLowerCase(),
      description: v.percentage > 0.5 ? this.valuesData[v.id][0].HighDescription : this.valuesData[v.id][0].LowDescription
    };
  }

  getFacetInfo(f) {
    return {
      name: f.id,
      term: f.percentage > 0.5 ? this.facetsData[f.id].HighTerm.toLowerCase() : this.facetsData[f.id].LowTerm.toLowerCase(),
      description: f.percentage > 0.5 ? this.facetsData[f.id].HighDescription.toLowerCase() : this.facetsData[f.id].LowDescription.toLowerCase()
    };
  }

  getFacet(id) {
    const facet = this.facetsData[id];
    if (!facet) {
      return Object.assign({}, facet);
    } else {
      return null;
    }
  }

  intervalFor(p) {
    return Math.min(Math.floor(p * 4), 3);
  }

}

module.exports = TextSummary;
<commit_msg>fix bug<commit_after>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const _ = require('underscore');
const extend = _.extend;
const pick = _.pick;

var format = require('./utilities/format');
var comparators = require('./utilities/comparators');

const I18nDataV2 = require('./i18n/v2');
const I18nDataV3 = require('./i18n/v3');
const PersonalityProfileV2 = require('./profiles/v2/index');
const PersonalityProfileV3 = require('./profiles/v3/index');
const defaultVersion = 'v2';

const CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS = 'EANOC';

class TextSummary {

  constructor(options) {
    this._options = extend(this.defaultOptions(), pick(options, 'locale', 'format', 'version'));
    this._version = typeof this._options.version !== 'undefined' ? this._options.version : defaultVersion;

    // Select localization dictionary based on the version of PI, V2 or V3
    if (this._version === 'v3'){
      this._i18n = new I18nDataV3(this._options.locale);
    } else{
      this._i18n = new I18nDataV2(this._options.locale);
    }

    this._dictionary = this._i18n.data();
    this._translatorFactory = this._i18n.translatorFactory();
    this._translator = this._translatorFactory.createTranslator(this._dictionary.phrases);

    this.circumplexData = this._dictionary.traits;
    this.facetsData = this._dictionary.facets;
    this.valuesData = this._dictionary.values;
    this.needsData = this._dictionary.needs;
  }

  defaultOptions() {
    return {
      locale: 'en',
      version: 'v2'
    };
  }

  //getSummary(profile) {
  getSummary(profile) {
    var personalityProfile = this._version === 'v3' ? new PersonalityProfileV3(profile) : new PersonalityProfileV2(profile);
    this._personalityProfile = personalityProfile;
    return this.assemble(personalityProfile).map(function (paragraph) { return paragraph.join(' '); }).join('\n');
  }

  //assemble(tree) {
  assemble(profile) {
    return [
      this.assembleTraits(profile.traits()),
      this.assembleFacets(profile.traits()),
      this.assembleNeeds(profile.needs()),
      this.assembleValues(profile.values())
    ];
  }

  //assembleTraits(personalityTree) {
  assembleTraits(traits) {
    var
      sentences = [],
      big5elements = [],
      relevantBig5,
      adj1, adj2, adj3;

    // Sort the Big 5 based on how extreme the number is.
    traits.forEach(function (p) {
      big5elements.push({
        id: p.id,
        percentage: p.score
      });
    });

    big5elements.sort(comparators.compareByRelevance);

    // Remove all traits with percentage values between 32% and 68%, as it's inside the common people.
    relevantBig5 = big5elements.filter(function (item) {
      return Math.abs(0.5 - item.percentage) > 0.18;
    });

    if (relevantBig5.length < 2) {
      // Even if no Big 5 attribute is interesting, you get 1 adjective.
      relevantBig5 = [big5elements[0], big5elements[1]];
    }

    adj1 = relevantBig5.length >=2 ? this.getCircumplexAdjective(relevantBig5[0], relevantBig5[1], 0) : null;
    adj2 = relevantBig5.length >=3 ? this.getCircumplexAdjective(relevantBig5[1], relevantBig5[2], 1) : null;
    adj3 = relevantBig5.length >=4 ? this.getCircumplexAdjective(relevantBig5[2], relevantBig5[3], 2) : null;

    // Generate sentences summarizing personality traits composed of cross trait phrases
    switch (relevantBig5.length) {
    case 2:
      sentences.push(format(this._translator('You are %s'), adj1) + '.');
      break;
    case 3:
      sentences.push(format(this._translator('You are %s and %s'),  adj1, adj2) + '.');
      break;
    case 4:
    case 5:
      sentences.push(format(this._translator('You are %s, %s and %s'),  adj1, adj2, adj3) + '.');
      break;
    }

    return sentences;
  }

  //assembleFacets(personalityTree) {
  assembleFacets(traits) {
    var
      sentences = [],
      facetElements = [],
      info,
      i;

    // Assemble the full list of facets and sort them based on how extreme
    // is the number.
    traits.forEach(function (p) {
      p.facets.forEach(function (f) {
        facetElements.push({
          id: f.id,
          percentage: f.score,
          parent: p
        });
      });
    });
    facetElements.sort(comparators.compareByRelevance);

    info = this.getFacetInfo(facetElements[0]);
    sentences.push(format(this._translator('You are %s'), info.term) + ': ' + info.description + '.');
    info = this.getFacetInfo(facetElements[1]);
    sentences.push(format(this._translator('You are %s'), info.term) + ': ' + info.description + '.');

    // If all the facets correspond to the same feature, continue until a
    // different parent feature is found.
    i = 2;
    if (facetElements[0].parent === facetElements[1].parent) {
      while (facetElements[0].parent === facetElements[i].parent) {
        i += 1;
      }
    }
    info = this.getFacetInfo(facetElements[i]);
    sentences.push(format(this._translator('And you are %s'), info.term) + ': ' + info.description + '.');

    return sentences;
  }

  //assembleNeeds(needsTree) {
  assembleNeeds(needs) {
    var
      sentences = [],
      needsList = [],
      word,
      sentence;

    needs.forEach(function (n) {
      needsList.push({
        id: n.id,
        percentage: n.score
      });
    });
    needsList.sort(comparators.compareByValue);

    // Get the words required.
    var need = needsList[0];
    word = (this.needsData[need.id])[0];

    // Form the right sentence for the single need.
    switch (this.intervalFor(needsList[0].percentage)) {
    case 0:
      sentence = this._translator('Experiences that make you feel high %s are generally unappealing to you');
      break;
    case 1:
      sentence = this._translator('Experiences that give a sense of %s hold some appeal to you');
      break;
    case 2:
      sentence = this._translator('You are motivated to seek out experiences that provide a strong feeling of %s');
      break;
    case 3:
      sentence = this._translator('Your choices are driven by a desire for %s');
      break;
    }
    sentence = format(sentence, word).concat('.');
    sentences.push(sentence);

    return sentences;
  }

  assembleValues(values) {
    var
      sentences = [],
      valuesList = [],
      info1, info2,
      sentence,
      valuesInfo,
      i, term1, term2;

    values.forEach(function (v) {
      valuesList.push({
        id: v.id,
        percentage: v.score
      });
    });
    valuesList.sort(comparators.compareByRelevance);

    // Get all the text and data required.
    info1 = this.getValueInfo(valuesList[0]);
    info2 = this.getValueInfo(valuesList[1]);

    if (this.intervalFor(valuesList[0].percentage) === this.intervalFor(valuesList[1].percentage)) {
      // Assemble the first 'both' sentence.
      term1 = info1.term;
      term2 = info2.term;
      switch (this.intervalFor(valuesList[0].percentage)) {
      case 0:
        sentence = format(this._translator('You are relatively unconcerned with both %s and %s'), term1, term2) + '.';
        break;
      case 1:
        sentence = format(this._translator('You don\'t find either %s or %s to be particularly motivating for you'), term1, term2) + '.';
        break;
      case 2:
        sentence = format(this._translator('You value both %s and %s a bit'), term1, term2) + '.';
        break;
      case 3:
        sentence = format(this._translator('You consider both %s and %s to guide a large part of what you do'), term1, term2) + '.';
        break;
      }
      sentences.push(sentence);

      // Assemble the final strings in the correct format.
      sentences.push(info1.description + '.');
      sentences.push(format(this._translator('And %s'), info2.description.toLowerCase()) + '.');
    } else {
      valuesInfo = [info1, info2];
      for (i = 0; i < valuesInfo.length; i += 1) {
        // Process it this way because the code is the same.
        switch (this.intervalFor(valuesList[i].percentage)) {
        case 0:
          sentence = format(this._translator('You are relatively unconcerned with %s'), valuesInfo[i].term);
          break;
        case 1:
          sentence = format(this._translator('You don\'t find %s to be particularly motivating for you'), valuesInfo[i].term);
          break;
        case 2:
          sentence = format(this._translator('You value %s a bit more'),  valuesInfo[i].term);
          break;
        case 3:
          sentence = format(this._translator('You consider %s to guide a large part of what you do'),  valuesInfo[i].term);
          break;
        }
        sentence = sentence.concat(': ').
            concat(valuesInfo[i].description.toLowerCase()).
            concat('.');
        sentences.push(sentence);
      }
    }

    return sentences;
  }

  /**
  *  getCircumplexAdjective generates a qualified cross trait phrase given two personality traits, p1 and p2
  *  p1 and p2: personality traits of the form {"id":<id>,"percentage":<percentage>}
  *  order: determines which qualifier to use for the cross trait adjective
  *      e.g., terms that are viewed negatively in Western culture are qualified with 'a bit' or 'somewhat'
  *         0: 'a bit'
  *         1: 'somewhat'
  *         2: 'can be perceived as'
  *  Returns the qualified cross trait adjective phrase for p1 and p2.
  */
  getCircumplexAdjective(p1, p2, order) {
    var version = this._version;
    var ordered = [p1, p2].sort(function (o1, o2) {
      var i1, i2;
      if (version === 'v3') {
        i1 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o1.id.replace('big5_', '').charAt(0).toUpperCase());
        i2 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o2.id.replace('big5_', '').charAt(0).toUpperCase());
      }else{
        i1 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o1.id.charAt(0));
        i2 = CIRCUMPLEX_ORDER_OF_PERSONALITY_TRAITS.indexOf(o2.id.charAt(0));
      }
      return i1 < i2 ? -1 : 1;
    });

    // Assemble the identifier as the JSON file stored it.
    var identifier = ordered[0].id.
        concat(ordered[0].percentage > 0.5 ? '_plus_' : '_minus_').
        concat(ordered[1].id).
        concat(ordered[1].percentage > 0.5 ? '_plus' : '_minus');

    var traitMult = this.circumplexData[identifier][0];
    var sentence = '%s';

    if (traitMult.perceived_negatively) {
      switch (order) {
      case 0:
        sentence = this._translator('a bit %s');
        break;
      case 1:
        sentence = this._translator('somewhat %s');
        break;
      case 2:
        sentence = this._translator('can be perceived as %s');
        break;
      }
    }

    return format(sentence, traitMult.word);
  }

  getValueInfo(v) {
    return {
      name: v.id,
      term: this.valuesData[v.id][0].Term.toLowerCase(),
      description: v.percentage > 0.5 ? this.valuesData[v.id][0].HighDescription : this.valuesData[v.id][0].LowDescription
    };
  }

  getFacetInfo(f) {
    return {
      name: f.id,
      term: f.percentage > 0.5 ? this.facetsData[f.id].HighTerm.toLowerCase() : this.facetsData[f.id].LowTerm.toLowerCase(),
      description: f.percentage > 0.5 ? this.facetsData[f.id].HighDescription.toLowerCase() : this.facetsData[f.id].LowDescription.toLowerCase()
    };
  }

  getFacet(id) {
    const facet = this.facetsData[id];
    if (facet) {
      return Object.assign({}, facet);
    } else {
      return null;
    }
  }

  intervalFor(p) {
    return Math.min(Math.floor(p * 4), 3);
  }

}

module.exports = TextSummary;
<|endoftext|>


<commit_before>/**
    Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.

    Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at

        http://aws.amazon.com/apache2.0/

    or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
    */

/**
 * This sample shows how to create a Lambda function for handling Alexa Skill requests that:
 *
 * - Session State: Handles a multi-turn dialog model.
 * - Custom slot type: demonstrates using custom slot types to handle a finite set of known values
 * - SSML: Using SSML tags to control how Alexa renders the text-to-speech.
 *
 * Examples:
 * Dialog model:
 *  User: "Alexa, ask Wise Guy to tell me a knock knock joke."
 *  Alexa: "Knock knock"
 *  User: "Who's there?"
 *  Alexa: "<phrase>"
 *  User: "<phrase> who"
 *  Alexa: "<Punchline>"
 */

/**
 * App ID for the skill
 */
var APP_ID = undefined;//replace with 'amzn1.echo-sdk-ams.app.[your-unique-value-here]';

/**
 * The AlexaSkill prototype and helper functions
 */
 var AlexaSkill = require('./AlexaSkill');

/**
 * BabysitterSkill is a child of AlexaSkill.
 * To read more about inheritance in JavaScript, see the link below.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Inheritance
 */
 var BabysitterSkill = function () {
  AlexaSkill.call(this, APP_ID);
};

// Extend AlexaSkill
BabysitterSkill.prototype = Object.create(AlexaSkill.prototype);
BabysitterSkill.prototype.constructor = BabysitterSkill;

/**
 * Overriden to show that a subclass can override this function to initialize session state.
 */
 BabysitterSkill.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
  console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
    + ", sessionId: " + session.sessionId);

    // Any session init logic would go here.
  };

/**
 * If the user launches without specifying an intent, route to the correct function.
 */
 BabysitterSkill.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
  console.log("BabysitterSkill onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);

  initialGreeting(session, response);
};

/**
 * Overriden to show that a subclass can override this function to teardown session state.
 */
 BabysitterSkill.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
  console.log("onSessionEnded requestId: " + sessionEndedRequest.requestId
    + ", sessionId: " + session.sessionId);

    //Any session cleanup logic would go here.
  };

  BabysitterSkill.prototype.intentHandlers = {

    "AMAZON.YesIntent": function (intent, session, response) {
      yesResponse(session, response);
    },

    "AMAZON.NoIntent": function (intent, session, response) {
      noResponse(session, response);
    },

    "AMAZON.HelpIntent": function (intent, session, response) {
      var speechText = "";

      switch (session.attributes.stage) {
        case 0:
        speechText = "Knock knock jokes are a fun call and response type of joke. " +
        "To start the joke, just ask by saying tell me a joke, or you can say exit.";
        break;
        case 1:
        speechText = "You can ask, who's there, or you can say exit.";
        break;
        case 2:
        speechText = "You can ask, who, or you can say exit.";
        break;
        default:
        speechText = "Knock knock jokes are a fun call and response type of joke. " +
        "To start the joke, just ask by saying tell me a joke, or you can say exit.";
      }

      var speechOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
      };
      var repromptOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
      };
        // For the repromptText, play the speechOutput again
        response.ask(speechOutput, repromptOutput);
      },

      "AMAZON.StopIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
      },

      "AMAZON.CancelIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
      }
    };

function initialGreeting(session, response) {
      var speechText = "Welcome to Babysitter. Have you already saved a child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say yes or no";

    session.lastQuestion = "greeting"

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }

// If user says that they already have saved a child
function yesResponse(session, response) {
  if(session.lastQuestion === "greeting") {
    var speechText = "Would you like to edit, delete, or add a new child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say edit, delete, or new";
  }

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }

// If user says that they have not yet saved a child
function noResponse(session, response) {
  if(session.lastQuestion === "greeting") {
    var speechText = "Would you like to add a new child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say yes or no";
  }

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }
/**
 * Responds to the user saying "Who's there".
 */
 function handleWhosThereIntent(session, response) {
  var speechText = "";
  var repromptText = "";

  if (session.attributes.stage) {
    if (session.attributes.stage === 1) {
            //Retrieve the joke's setup text.
            speechText = session.attributes.setup;

            //Advance the stage of the dialogue.
            session.attributes.stage = 2;

            repromptText = "You can ask, " + speechText + " who?";
          } else {
            session.attributes.stage = 1;
            speechText = "That's not how knock knock jokes work! <break time=\"0.3s\" /> "
            + "knock knock";

            repromptText = "You can ask, who's there."
          }
        } else {

        //If the session attributes are not found, the joke must restart.
        speechText = "Sorry, I couldn't correctly retrieve the joke. "
        + "You can say, tell me a joke";

        repromptText = "You can say, tell me a joke";
      }

      var speechOutput = {
        speech: '<speak>' + speechText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
      var repromptOutput = {
        speech: '<speak>' + repromptText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
      response.ask(speechOutput, repromptOutput);
    }

/**
 * Delivers the punchline of the joke after the user responds to the setup.
 */
 function handleSetupNameWhoIntent(session, response) {
  var speechText = "",
  repromptText = "",
  speechOutput,
  repromptOutput,
  cardOutput;

  if (session.attributes.stage) {
    if (session.attributes.stage === 2) {
      speechText = session.attributes.speechPunchline;
      cardOutput = session.attributes.cardPunchline;
      speechOutput = {
        speech: '<speak>' + speechText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
            //If the joke completes successfully, this function uses a "tell" response.
            response.tellWithCard(speechOutput, "Wise Guy", cardOutput);
          } else {

            session.attributes.stage = 1;
            speechText = "That's not how knock knock jokes work! <break time=\"0.3s\" /> "
            + "Knock knock!";
            cardOutput = "That's not how knock knock jokes work! "
            + "Knock knock!";

            repromptText = "You can ask who's there.";

            speechOutput = {
              speech: speechText,
              type: AlexaSkill.speechOutputType.SSML
            };
            repromptOutput = {
              speech: repromptText,
              type: AlexaSkill.speechOutputType.PLAIN_TEXT
            };
            //If the joke has to be restarted, this function uses an "ask" response.
            response.askWithCard(speechOutput, repromptOutput, "Wise Guy", cardOutput);
          }
        } else {
          speechText = "Sorry, I couldn't correctly retrieve the joke. "
          + "You can say, tell me a joke";

          repromptText = "You can say, tell me a joke";

          speechOutput = {
            speech: speechText,
            type: AlexaSkill.speechOutputType.PLAIN_TEXT
          };
          repromptOutput = {
            speech: repromptText,
            type: AlexaSkill.speechOutputType.PLAIN_TEXT
          };
          response.askWithCard(speechOutput, repromptOutput, "Wise Guy", speechOutput);
        }
      }

// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
    // Create an instance of the WiseGuy Skill.
    var skill = new BabysitterSkill();
    skill.execute(event, context);
  };
<commit_msg>Fixed session attributes<commit_after>/**
    Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.

    Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at

        http://aws.amazon.com/apache2.0/

    or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
    */

/**
 * This sample shows how to create a Lambda function for handling Alexa Skill requests that:
 *
 * - Session State: Handles a multi-turn dialog model.
 * - Custom slot type: demonstrates using custom slot types to handle a finite set of known values
 * - SSML: Using SSML tags to control how Alexa renders the text-to-speech.
 *
 * Examples:
 * Dialog model:
 *  User: "Alexa, ask Wise Guy to tell me a knock knock joke."
 *  Alexa: "Knock knock"
 *  User: "Who's there?"
 *  Alexa: "<phrase>"
 *  User: "<phrase> who"
 *  Alexa: "<Punchline>"
 */

/**
 * App ID for the skill
 */
var APP_ID = undefined;//replace with 'amzn1.echo-sdk-ams.app.[your-unique-value-here]';

/**
 * The AlexaSkill prototype and helper functions
 */
 var AlexaSkill = require('./AlexaSkill');

/**
 * BabysitterSkill is a child of AlexaSkill.
 * To read more about inheritance in JavaScript, see the link below.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Inheritance
 */
 var BabysitterSkill = function () {
  AlexaSkill.call(this, APP_ID);
};

// Extend AlexaSkill
BabysitterSkill.prototype = Object.create(AlexaSkill.prototype);
BabysitterSkill.prototype.constructor = BabysitterSkill;

/**
 * Overriden to show that a subclass can override this function to initialize session state.
 */
 BabysitterSkill.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
  console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
    + ", sessionId: " + session.sessionId);

    // Any session init logic would go here.
  };

/**
 * If the user launches without specifying an intent, route to the correct function.
 */
 BabysitterSkill.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
  console.log("BabysitterSkill onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);

  initialGreeting(session, response);
};

/**
 * Overriden to show that a subclass can override this function to teardown session state.
 */
 BabysitterSkill.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
  console.log("onSessionEnded requestId: " + sessionEndedRequest.requestId
    + ", sessionId: " + session.sessionId);

    //Any session cleanup logic would go here.
  };

  BabysitterSkill.prototype.intentHandlers = {

    "AMAZON.YesIntent": function (intent, session, response) {
      yesResponse(session, response);
    },

    "AMAZON.NoIntent": function (intent, session, response) {
      noResponse(session, response);
    },

    "AMAZON.HelpIntent": function (intent, session, response) {
      var speechText = "";

      switch (session.attributes.stage) {
        case 0:
        speechText = "Knock knock jokes are a fun call and response type of joke. " +
        "To start the joke, just ask by saying tell me a joke, or you can say exit.";
        break;
        case 1:
        speechText = "You can ask, who's there, or you can say exit.";
        break;
        case 2:
        speechText = "You can ask, who, or you can say exit.";
        break;
        default:
        speechText = "Knock knock jokes are a fun call and response type of joke. " +
        "To start the joke, just ask by saying tell me a joke, or you can say exit.";
      }

      var speechOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
      };
      var repromptOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
      };
        // For the repromptText, play the speechOutput again
        response.ask(speechOutput, repromptOutput);
      },

      "AMAZON.StopIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
      },

      "AMAZON.CancelIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
      }
    };

function initialGreeting(session, response) {
      var speechText = "Welcome to Babysitter. Have you already saved a child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say yes or no";

    session.attributes.lastQuestion = "greeting"

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }

// If user says that they already have saved a child
function yesResponse(session, response) {
  if(session.attributes.lastQuestion === "greeting") {
    var speechText = "Would you like to edit, delete, or add a new child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say edit, delete, or new";
  }

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }

// If user says that they have not yet saved a child
function noResponse(session, response) {
  if(session.attributes.lastQuestion === "greeting") {
    var speechText = "Would you like to add a new child?";

    //Reprompt speech will be triggered if the user doesn't respond.
    var repromptText = "Please say yes or no";
  }

    var speechOutput = {
      speech: speechText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
      speech: repromptText,
      type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    response.askWithCard(speechOutput, repromptOutput, "Babysitter", speechText);
  }
/**
 * Responds to the user saying "Who's there".
 */
 function handleWhosThereIntent(session, response) {
  var speechText = "";
  var repromptText = "";

  if (session.attributes.stage) {
    if (session.attributes.stage === 1) {
            //Retrieve the joke's setup text.
            speechText = session.attributes.setup;

            //Advance the stage of the dialogue.
            session.attributes.stage = 2;

            repromptText = "You can ask, " + speechText + " who?";
          } else {
            session.attributes.stage = 1;
            speechText = "That's not how knock knock jokes work! <break time=\"0.3s\" /> "
            + "knock knock";

            repromptText = "You can ask, who's there."
          }
        } else {

        //If the session attributes are not found, the joke must restart.
        speechText = "Sorry, I couldn't correctly retrieve the joke. "
        + "You can say, tell me a joke";

        repromptText = "You can say, tell me a joke";
      }

      var speechOutput = {
        speech: '<speak>' + speechText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
      var repromptOutput = {
        speech: '<speak>' + repromptText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
      response.ask(speechOutput, repromptOutput);
    }

/**
 * Delivers the punchline of the joke after the user responds to the setup.
 */
 function handleSetupNameWhoIntent(session, response) {
  var speechText = "",
  repromptText = "",
  speechOutput,
  repromptOutput,
  cardOutput;

  if (session.attributes.stage) {
    if (session.attributes.stage === 2) {
      speechText = session.attributes.speechPunchline;
      cardOutput = session.attributes.cardPunchline;
      speechOutput = {
        speech: '<speak>' + speechText + '</speak>',
        type: AlexaSkill.speechOutputType.SSML
      };
            //If the joke completes successfully, this function uses a "tell" response.
            response.tellWithCard(speechOutput, "Wise Guy", cardOutput);
          } else {

            session.attributes.stage = 1;
            speechText = "That's not how knock knock jokes work! <break time=\"0.3s\" /> "
            + "Knock knock!";
            cardOutput = "That's not how knock knock jokes work! "
            + "Knock knock!";

            repromptText = "You can ask who's there.";

            speechOutput = {
              speech: speechText,
              type: AlexaSkill.speechOutputType.SSML
            };
            repromptOutput = {
              speech: repromptText,
              type: AlexaSkill.speechOutputType.PLAIN_TEXT
            };
            //If the joke has to be restarted, this function uses an "ask" response.
            response.askWithCard(speechOutput, repromptOutput, "Wise Guy", cardOutput);
          }
        } else {
          speechText = "Sorry, I couldn't correctly retrieve the joke. "
          + "You can say, tell me a joke";

          repromptText = "You can say, tell me a joke";

          speechOutput = {
            speech: speechText,
            type: AlexaSkill.speechOutputType.PLAIN_TEXT
          };
          repromptOutput = {
            speech: repromptText,
            type: AlexaSkill.speechOutputType.PLAIN_TEXT
          };
          response.askWithCard(speechOutput, repromptOutput, "Wise Guy", speechOutput);
        }
      }

// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
    // Create an instance of the WiseGuy Skill.
    var skill = new BabysitterSkill();
    skill.execute(event, context);
  };
<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

// Helpers for creating connection types in the schema
export {
  connectionArgs,
  connectionDefinitions,
} from './connection/connection.js';

// Helpers for creating connections from arrays
export {
  connectionFromArray,
  connectionFromPromisedArray,
  cursorForObjectInConnection,
  cursorToOffset,
} from './connection/arrayconnection.js';

// Helper for creating mutations with client mutation IDs
export {
  mutationWithClientMutationId,
} from './mutation/mutation.js';

// Helper for creating node definitions
export {
  nodeDefinitions,
} from './node/node.js';

// Utilities for creating global IDs in systems that don't have them.
export {
  fromGlobalId,
  globalIdField,
  toGlobalId,
} from './node/node.js';
<commit_msg>Make new functions available to consumers of NPM package<commit_after>/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

// Helpers for creating connection types in the schema
export {
  connectionArgs,
  connectionDefinitions,
} from './connection/connection.js';

// Helpers for creating connections from arrays
export {
  connectionFromArray,
  connectionFromArraySlice,
  connectionFromPromisedArray,
  connectionFromPromisedArraySlice,
  cursorForObjectInConnection,
  cursorToOffset,
} from './connection/arrayconnection.js';

// Helper for creating mutations with client mutation IDs
export {
  mutationWithClientMutationId,
} from './mutation/mutation.js';

// Helper for creating node definitions
export {
  nodeDefinitions,
} from './node/node.js';

// Utilities for creating global IDs in systems that don't have them.
export {
  fromGlobalId,
  globalIdField,
  toGlobalId,
} from './node/node.js';
<|endoftext|>


<commit_before>import pathToRegexp from 'path-to-regexp'
import React from 'react'
import {default as NextLink} from 'next/link'
import {default as NextRouter} from 'next/router'

module.exports = opts => new Routes(opts)

class Routes {
  constructor({Link = NextLink, Router = NextRouter} = {}) {
    this.routes = []
    this.Link = this.getLink(Link)
    this.Router = this.getRouter(Router)
  }

  add(name, pattern, page) {
    const route = new Route(name, pattern, page)
    this.routes.push(route)
  }

  findByName(name) {
    return this.routes.find(route => route.name === name)
  }

  match(path) {
    let params
    const route = this.routes.find(route => params = route.match(path))
    return {route, params}
  }

  getRequestHandler(app) {
    const nextHandler = app.getRequestHandler()

    return (req, res) => {
      const {path, query} = req
      const {route, params} = this.match(path)

      if (route) {
        app.render(req, res, route.page, {...query, ...params})
      } else {
        nextHandler(req, res)
      }
    }
  }

  getLink(Link) {
    return props => {
      const {route, params, ...newProps} = props

      if (route) {
        Object.assign(newProps, this.findByName(route).getLinkProps(params))
      }

      return <Link {...newProps} />
    }
  }

  getRouter(Router) {
    const pushRoute = (name, params = {}) => {
      const {href, as} = this.findByName(name).getLinkProps(params)
      return Router.push(href, as)
    }

    const replaceRoute = (name, params = {}) => {
      const {href, as} = this.findByName(name).getLinkProps(params)
      return Router.replace(href, as)
    }

    return {...Router, pushRoute, replaceRoute}
  }
}

class Route {
  constructor(name, pattern, page = name) {
    this.name = name
    this.pattern = pattern
    this.page = page
    this.regex = pathToRegexp(pattern, this.keys = [])
    this.getAs = pathToRegexp.compile(pattern)
  }

  match(path) {
    const values = this.regex.exec(path)
    if (values) {
      return this.valuesToParams(values.slice(1))
    }
  }

  valuesToParams(values) {
    return values.reduce((params, val, i) => (
      Object.assign(params, {[this.keys[i].name]: val})
    ), {})
  }

  getHref(params) {
    const qs = Object.keys(params).map(key => [
      encodeURIComponent(key),
      encodeURIComponent(params[key])
    ].join('=')).join('&')

    return `/${this.page}?${qs}`
  }

  getLinkProps(params) {
    const as = this.getAs(params)
    const href = this.getHref(params)
    return {as, href}
  }
}
<commit_msg>fix path<commit_after>import pathToRegexp from 'path-to-regexp'
import React from 'react'
import {default as NextLink} from 'next/link'
import {default as NextRouter} from 'next/router'

module.exports = opts => new Routes(opts)

class Routes {
  constructor({Link = NextLink, Router = NextRouter} = {}) {
    this.routes = []
    this.Link = this.getLink(Link)
    this.Router = this.getRouter(Router)
  }

  add(name, pattern, page) {
    const route = new Route(name, pattern, page)
    this.routes.push(route)
  }

  findByName(name) {
    return this.routes.find(route => route.name === name)
  }

  match(path) {
    let params
    const route = this.routes.find(route => params = route.match(path))
    return {route, params}
  }

  getRequestHandler(app) {
    const nextHandler = app.getRequestHandler()

    return (req, res) => {
      const {path, query} = req
      const {route, params} = this.match(path)

      if (route) {
        app.render(req, res, route.page, {...query, ...params})
      } else {
        nextHandler(req, res)
      }
    }
  }

  getLink(Link) {
    return props => {
      const {route, params, ...newProps} = props

      if (route) {
        Object.assign(newProps, this.findByName(route).getLinkProps(params))
      }

      return <Link {...newProps} />
    }
  }

  getRouter(Router) {
    const pushRoute = (name, params = {}) => {
      const {href, as} = this.findByName(name).getLinkProps(params)
      return Router.push(href, as)
    }

    const replaceRoute = (name, params = {}) => {
      const {href, as} = this.findByName(name).getLinkProps(params)
      return Router.replace(href, as)
    }

    return {...Router, pushRoute, replaceRoute}
  }
}

class Route {
  constructor(name, pattern, page = name) {
    this.name = name
    this.pattern = pattern
    this.page = page.replace(/^\/?(.*)/, '/$1')
    this.regex = pathToRegexp(pattern, this.keys = [])
    this.getAs = pathToRegexp.compile(pattern)
  }

  match(path) {
    const values = this.regex.exec(path)
    if (values) {
      return this.valuesToParams(values.slice(1))
    }
  }

  valuesToParams(values) {
    return values.reduce((params, val, i) => (
      Object.assign(params, {[this.keys[i].name]: val})
    ), {})
  }

  getHref(params) {
    const qs = Object.keys(params).map(key => [
      encodeURIComponent(key),
      encodeURIComponent(params[key])
    ].join('=')).join('&')

    return `${this.page}?${qs}`
  }

  getLinkProps(params) {
    const as = this.getAs(params)
    const href = this.getHref(params)
    return {as, href}
  }
}
<|endoftext|>


<commit_before>const got = require('got')
const cacheManager = require('cache-manager')
const querystring = require('querystring')
const pkg = require('../package.json')

class Onionoo {

  // Constructor returns a new object so instance properties are private
  constructor (options = {}) {
    // Set default options
    this.options = Object.assign({}, {
      baseUrl: 'https://onionoo.torproject.org',
      endpoints: [
        'summary',
        'details',
        'bandwidth',
        'weights',
        'clients',
        'uptime'
      ]
    }, options)
    if (options.cache !== false) {
      this.options.cache = cacheManager.caching(Object.assign({}, {
        store: 'memory',
        max: 500
      }, options.cache))
    }

    // Return object containing endpoint methods
    return this.options.endpoints.reduce((onionoo, endpoint) => {
      onionoo[endpoint] = this.createEndpointMethod(endpoint)

      return onionoo
    }, {})
  }

  // Returns cache max age from response headers
  calculateResponseMaxAge (response) {
    // Get max age ms
    const cacheControl = response.headers['cache-control']
    const maxAgeRegex = /max-age=(\d+)/
    let maxAge = cacheControl && cacheControl.match(maxAgeRegex)
    maxAge = maxAge ? maxAge[1] : 0

    // Take current age into account
    if (response.headers.age) {
      maxAge -= response.headers.age
    }

    // Don't return negative values
    return Math.max(0, maxAge)
  }

  // Returns a function to make requests to a given endpoint
  createEndpointMethod (endpoint) {
    return args => new Promise((resolve, reject) => {
      // Build query string (don't encode ':' for search filters)
      const qs = querystring.encode(args).replace(/%3A/g, ':')

      // Build url
      const url = `${this.options.baseUrl}/${endpoint}?${qs}`

      // Check for url in cache
      if (this.options.cache) {
        this.options.cache.get(url).then(cachedResult => resolve(cachedResult || this.makeRequest(url)))
      } else {
        resolve(this.makeRequest(url))
      }
    })
  }

  // Returns a promise for a request
  makeRequest (url) {
    const options = {
      json: true,
      'user-agent': `onionoo-node-client v${pkg.version} (${pkg.homepage})`
    }
    return got(url, options)
      .then(response => {
        // Cache response
        if (this.options.cache) {
          const ttl = this.calculateResponseMaxAge(response)
          if (ttl) {
            this.options.cache.set(url, response.body, { ttl })
          }
        }

        // Resolve response
        return response.body
      })
  }
}

module.exports = Onionoo
<commit_msg>Set user agent correctly<commit_after>const got = require('got')
const cacheManager = require('cache-manager')
const querystring = require('querystring')
const pkg = require('../package.json')

class Onionoo {

  // Constructor returns a new object so instance properties are private
  constructor (options = {}) {
    // Set default options
    this.options = Object.assign({}, {
      baseUrl: 'https://onionoo.torproject.org',
      endpoints: [
        'summary',
        'details',
        'bandwidth',
        'weights',
        'clients',
        'uptime'
      ]
    }, options)
    if (options.cache !== false) {
      this.options.cache = cacheManager.caching(Object.assign({}, {
        store: 'memory',
        max: 500
      }, options.cache))
    }

    // Return object containing endpoint methods
    return this.options.endpoints.reduce((onionoo, endpoint) => {
      onionoo[endpoint] = this.createEndpointMethod(endpoint)

      return onionoo
    }, {})
  }

  // Returns cache max age from response headers
  calculateResponseMaxAge (response) {
    // Get max age ms
    const cacheControl = response.headers['cache-control']
    const maxAgeRegex = /max-age=(\d+)/
    let maxAge = cacheControl && cacheControl.match(maxAgeRegex)
    maxAge = maxAge ? maxAge[1] : 0

    // Take current age into account
    if (response.headers.age) {
      maxAge -= response.headers.age
    }

    // Don't return negative values
    return Math.max(0, maxAge)
  }

  // Returns a function to make requests to a given endpoint
  createEndpointMethod (endpoint) {
    return args => new Promise((resolve, reject) => {
      // Build query string (don't encode ':' for search filters)
      const qs = querystring.encode(args).replace(/%3A/g, ':')

      // Build url
      const url = `${this.options.baseUrl}/${endpoint}?${qs}`

      // Check for url in cache
      if (this.options.cache) {
        this.options.cache.get(url).then(cachedResult => resolve(cachedResult || this.makeRequest(url)))
      } else {
        resolve(this.makeRequest(url))
      }
    })
  }

  // Returns a promise for a request
  makeRequest (url) {
    const options = {
      json: true,
      headers: {
        'user-agent': `onionoo-node-client v${pkg.version} (${pkg.homepage})`
      }
    }
    return got(url, options)
      .then(response => {
        // Cache response
        if (this.options.cache) {
          const ttl = this.calculateResponseMaxAge(response)
          if (ttl) {
            this.options.cache.set(url, response.body, { ttl })
          }
        }

        // Resolve response
        return response.body
      })
  }
}

module.exports = Onionoo
<|endoftext|>


<commit_before>const optionsDefault = {
	indent: {
		type: 'space',
		size: 2
	}
};

const clean = tree => tree
	.filter(node => {
		return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node)));
	})
	.map(node => {
		if (Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = clean(node.content);
		}
		return typeof node === 'string' ? node.trim() : node;
	});

const indent = (tree, {indent: {type, size}}) => {
	const indentString = type === 'space' ? ' '.repeat(size) : '\t';

	const getIndent = level => `\n${indentString.repeat(level)}`;

	const setIndent = (tree, level = 0) => tree.reduce((previousValue, currentValue, index) => {
		if (typeof currentValue === 'object' && Object.prototype.hasOwnProperty.call(currentValue, 'content')) {
			currentValue.content = setIndent(currentValue.content, ++level);
			--level;
		}

		if (tree.length === 1 && typeof tree[index] === 'string') {
			console.log(tree);
			return [...previousValue, currentValue];
		}

		if (level === 0 && (tree.length - 1) === index) {
			return [...previousValue, currentValue];
		}

		if (level === 0) {
			return [...previousValue, currentValue, getIndent(level)];
		}

		if ((tree.length - 1) === index) {
			return [...previousValue, getIndent(level), currentValue, getIndent(--level)];
		}

		return [...previousValue, getIndent(level), currentValue, getIndent(0)];
	}, []);

	return setIndent(tree);
};

function beautify(tree, options) {
	return new Promise(resolve => resolve(tree))
		.then(tree => clean(tree))
		.then(tree => indent(tree, options))
		.then(tree => tree);
}

export default (options = {}) => {
	return tree => new Promise((resolve, reject) => {
		if (!Array.isArray(tree)) {
			reject(new Error(`tree is not Array`));
		}

		if (tree.length === 0) {
			resolve(tree);
		}

		resolve(beautify(tree, Object.assign({}, optionsDefault, options)));
	});
};
<commit_msg>Remove comment<commit_after>const optionsDefault = {
	indent: {
		type: 'space',
		size: 2
	}
};

const clean = tree => tree
	.filter(node => {
		return typeof node === 'object' || (typeof node === 'string' && (node.trim().length !== 0 || /doctype/gi.test(node)));
	})
	.map(node => {
		if (Object.prototype.hasOwnProperty.call(node, 'content')) {
			node.content = clean(node.content);
		}
		return typeof node === 'string' ? node.trim() : node;
	});

const indent = (tree, {indent: {type, size}}) => {
	const indentString = type === 'space' ? ' '.repeat(size) : '\t';

	const getIndent = level => `\n${indentString.repeat(level)}`;

	const setIndent = (tree, level = 0) => tree.reduce((previousValue, currentValue, index) => {
		if (typeof currentValue === 'object' && Object.prototype.hasOwnProperty.call(currentValue, 'content')) {
			currentValue.content = setIndent(currentValue.content, ++level);
			--level;
		}

		if (tree.length === 1 && typeof tree[index] === 'string') {
			return [...previousValue, currentValue];
		}

		if (level === 0 && (tree.length - 1) === index) {
			return [...previousValue, currentValue];
		}

		if (level === 0) {
			return [...previousValue, currentValue, getIndent(level)];
		}

		if ((tree.length - 1) === index) {
			return [...previousValue, getIndent(level), currentValue, getIndent(--level)];
		}

		return [...previousValue, getIndent(level), currentValue, getIndent(0)];
	}, []);

	return setIndent(tree);
};

function beautify(tree, options) {
	return new Promise(resolve => resolve(tree))
		.then(tree => clean(tree))
		.then(tree => indent(tree, options))
		.then(tree => tree);
}

export default (options = {}) => {
	return tree => new Promise((resolve, reject) => {
		if (!Array.isArray(tree)) {
			reject(new Error(`tree is not Array`));
		}

		if (tree.length === 0) {
			resolve(tree);
		}

		resolve(beautify(tree, Object.assign({}, optionsDefault, options)));
	});
};
<|endoftext|>


<commit_before>import Express from 'express'
import path from 'path'

import config from './config'
import WebpackIsomorphicTools from 'webpack-isomorphic-tools'
import webpackIsomorphicToolsConfiguration from './webpack-isomorphic-tools-configuration'

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicToolsConfiguration)
  .development(process.env.NODE_ENV === 'development')
  .server(path.resolve(__dirname, '..'), () => {
    const app = Express()
    const server = require('./server').
      default(app).
      listen(config.PORT, (err) => {
        if (err) {
          console.log(err)
        } else {
          console.log('Server listening at http://%s:%s',
            server.address().address,
            server.address().port)
          console.log('Press Ctrl+C to quit.')
        }
      })
  })<commit_msg>Binding all IP addresses<commit_after>import Express from 'express'
import path from 'path'

import config from './config'
import WebpackIsomorphicTools from 'webpack-isomorphic-tools'
import webpackIsomorphicToolsConfiguration from './webpack-isomorphic-tools-configuration'

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicToolsConfiguration)
  .development(process.env.NODE_ENV === 'development')
  .server(path.resolve(__dirname, '..'), () => {
    const app = Express()
    const server = require('./server').
      default(app).
      listen(config.PORT, '0.0.0.0', (err) => {
        if (err) {
          console.log(err)
        } else {
          console.log('Server listening at http://%s:%s',
            server.address().address,
            server.address().port)
          console.log('Press Ctrl+C to quit.')
        }
      })
  })<|endoftext|>


<commit_before>var fs = require('fs')
  , falafel = require('falafel')
  , colors = require('colors')
  , path = require('path')
  , spawn = require('child_process').spawn;

var colonize = require('./colonize');

/**
 * Bytecode
 */

var compile_lua = process.platform != 'win32'
  ? __dirname + '/../bin/build/Release/compile_lua'
  : __dirname + '/../bin/compile_lua_x64.exe';

function toBytecode (lua, f, next) {
  next = typeof f == 'function' ? f : next;
  f = typeof f == 'string' ? f : 'usercode.js';

  var bufs = [];
  var c = spawn(compile_lua, ['@' + f]);
  c.stdout.on('data', function (buf) {
    bufs.push(buf);
  });
  c.stdout.on('close', function (code) {
    var res = Buffer.concat(bufs);
    next(code, res);
  });
  c.stdin.write(lua);
  c.stdin.end();
}

/**
 * Module API
 */

exports.colonize = colonize;
exports.toBytecode = toBytecode;<commit_msg>Check Debug folder for colony, otherwise skip<commit_after>var fs = require('fs')
  , falafel = require('falafel')
  , colors = require('colors')
  , path = require('path')
  , spawn = require('child_process').spawn;

var colonize = require('./colonize');

/**
 * Bytecode
 */

var compile_lua = process.platform != 'win32'
  ? fs.existsSync(__dirname + '/../bin/build/Release')
    ? __dirname + '/../bin/build/Release/compile_lua'
    : __dirname + '/../bin/build/Debug/compile_lua'
  : __dirname + '/../bin/compile_lua_x64.exe';

function toBytecode (lua, f, next) {
  next = typeof f == 'function' ? f : next;
  f = typeof f == 'string' ? f : 'usercode.js';

  if (!fs.existsSync(compile_lua)) {
    console.error('WARNING: Bytecode compiler was not compiled for module "colony". Check that node-gyp is installed properly on your system and reinstall. Skipping for now...');
    setImmediate(next, lua, 0);
    return;
  }

  var bufs = [];
  var c = spawn(compile_lua, ['@' + f]);
  c.stdout.on('data', function (buf) {
    bufs.push(buf);
  });
  c.stdout.on('close', function (code) {
    var res = Buffer.concat(bufs);
    next(code, res);
  });
  c.stdin.write(lua);
  c.stdin.end();
}

/**
 * Module API
 */

exports.colonize = colonize;
exports.toBytecode = toBytecode;<|endoftext|>