<commit_before>// Copyright (c) 2016, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import * as Sentry from '@sentry/node';
import fs from 'fs-extra';
import httpProxy from 'http-proxy';
import path from 'path';
import PromClient from 'prom-client';
import temp from 'temp';
import _ from 'underscore';
import which from 'which';

import { getCompilerTypeByKey } from '../compilers';
import { logger } from '../logger';
import * as utils from '../utils';

temp.track();

let hasSetUpAutoClean = false;

function initialise(compilerEnv) {
    if (hasSetUpAutoClean) return;
    hasSetUpAutoClean = true;
    const tempDirCleanupSecs = compilerEnv.ceProps('tempDirCleanupSecs', 600);
    logger.info(`Cleaning temp dirs every ${tempDirCleanupSecs} secs`);

    let cyclesBusy = 0;
    setInterval(() => {
        const status = compilerEnv.compilationQueue.status();
        if (status.busy) {
            cyclesBusy++;
            logger.warn(
                `temp cleanup skipped, pending: ${status.pending}, waiting: ${status.size}, cycles: ${cyclesBusy}`);
            return;
        }

        cyclesBusy = 0;

        temp.cleanup((err, stats) => {
            if (err) logger.error('temp cleanup error', err);
            if (stats) logger.debug('temp cleanup stats', stats);
        });
    }, tempDirCleanupSecs * 1000);
}

export class CompileHandler {
    constructor(compilationEnvironment, awsProps) {
        this.compilersById = {};
        this.compilerEnv = compilationEnvironment;
        this.textBanner = this.compilerEnv.ceProps('textBanner');
        this.proxy = httpProxy.createProxyServer({});
        this.awsProps = awsProps;
        initialise(this.compilerEnv);

        this.compileGauge = new PromClient.Gauge({
            name: 'ce_compilations',
            help: 'Number of compilations',
            labelNames: ['language'],
        });

        this.executeGauge = new PromClient.Gauge({
            name: 'ce_executions',
            help: 'Number of executions',
            labelNames: ['language'],
        });

        // Mostly cribbed from
        // https://github.com/nodejitsu/node-http-proxy/blob/master/examples/middleware/bodyDecoder-middleware.js
        // We just keep the body as-is though: no encoding using queryString.stringify(), as we don't use a form
        // decoding middleware.
        this.proxy.on('proxyReq', function (proxyReq, req) {
            if (!req.body || Object.keys(req.body).length === 0) {
                return;
            }

            const contentType = proxyReq.getHeader('Content-Type');
            let bodyData;

            if (contentType === 'application/json') {
                bodyData = JSON.stringify(req.body);
            }

            if (contentType === 'application/x-www-form-urlencoded') {
                bodyData = req.body;
            }

            if (bodyData) {
                proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
                proxyReq.write(bodyData);
            }
        });
    }

    async create(compiler) {
        const type = compiler.compilerType || 'default';
        const compilerClass = getCompilerTypeByKey(type);

        // attempt to resolve non absolute exe paths
        if (compiler.exe && !path.isAbsolute(compiler.exe)) {
            const exe = await which(compiler.exe).catch(() => null);
            if (exe) {
                logger.debug(`Resolved '${compiler.exe}' to path '${exe}'`);
            } else {
                // errors resolving to absolute path are not fatal for backwards compatibility sake
                logger.error(`Unable to resolve '${compiler.exe}'`);
            }
        }

        if (compiler.exe && path.isAbsolute(compiler.exe)) {
            // Try stat'ing the compiler to cache its mtime and only re-run it if it
            // has changed since the last time.
            try {
                const res = await fs.stat(compiler.exe);
                const cached = this.findCompiler(compiler.lang, compiler.id);
                if (cached && cached.mtime.getTime() === res.mtime.getTime()) {
                    logger.debug(`${compiler.id} is unchanged`);
                    return cached;
                }
                const compilerObj = new compilerClass(compiler, this.compilerEnv);
                return compilerObj.initialise(res.mtime);
            } catch (err) {
                logger.warn(`Unable to stat ${compiler.id} compiler binary: `, err);
                return null;
            }
        } else {
            return new compilerClass(compiler, this.compilerEnv);
        }
    }

    async setCompilers(compilers) {
        // Be careful not to update this.compilersById until we can replace it entirely.
        const compilersById = {};
        try {
            const createdCompilers = _.compact(await Promise.all(_.map(compilers, this.create, this)));
            for (const compiler of createdCompilers) {
                const langId = compiler.compiler.lang;
                if (!compilersById[langId]) compilersById[langId] = {};
                compilersById[langId][compiler.compiler.id] = compiler;
            }
            if (this.awsProps) {
                logger.info('Fetching possible arguments from storage');
                await Promise.all(createdCompilers.map(
                    compiler => compiler.possibleArguments.loadFromStorage(this.awsProps)));
            }
            this.compilersById = compilersById;
            return createdCompilers.map(compiler => compiler.getInfo());
        } catch (err) {
            logger.error('Exception while processing compilers:', err);
        }
    }

    findCompiler(langId, compilerId) {
        if (!compilerId) return;

        const langCompilers = this.compilersById[langId];
        if (langCompilers && langCompilers[compilerId]) {
            return langCompilers[compilerId];
        }
        // If the lang is bad, try to find it in every language
        let response;
        _.each(this.compilersById, compilerInLang => {
            if (response === undefined) {
                _.each(compilerInLang, compiler => {
                    if (response === undefined &&
                        (compiler.compiler.id === compilerId || compiler.compiler.alias === compilerId)) {

                        response = compiler;
                    }
                });
            }
        });
        return response;
    }

    compilerFor(req) {
        if (req.is('json')) {
            const compiler = this.findCompiler(req.lang || req.body.lang, req.params.compiler);
            if (!compiler) {
                const withoutBody = _.extend({}, req.body, {source: '<removed>'});
                logger.warn(`Unable to find compiler with lang ${req.lang} for request`, withoutBody);
            }
            return compiler;
        } else if (req.body && req.body.compiler) {
            const compiler = this.findCompiler(req.body.lang, req.body.compiler);
            if (!compiler) {
                const withoutBody = _.extend({}, req.body, {source: '<removed>'});
                logger.warn(`Unable to find compiler with lang ${req.body.lang} for request`, withoutBody);
            }
            return compiler;
        } else {
            const compiler = this.findCompiler(req.lang, req.params.compiler);
            if (!compiler) {
                logger.warn(`Unable to find compiler with lang ${req.lang} for request params`, req.params);
            }
            return compiler;
        }
    }

    parseRequest(req, compiler) {
        let source, options, backendOptions = {}, filters, bypassCache = false, tools, executionParameters = {};
        let libraries = [];
        // IF YOU MODIFY ANYTHING HERE PLEASE UPDATE THE DOCUMENTATION!
        if (req.is('json')) {
            // JSON-style request
            const requestOptions = req.body.options;
            source = req.body.source;
            if (req.body.bypassCache)
                bypassCache = true;
            options = requestOptions.userArguments;
            const execParams = requestOptions.executeParameters || {};
            executionParameters.args = execParams.args;
            executionParameters.stdin = execParams.stdin;
            backendOptions = requestOptions.compilerOptions || {};
            filters = requestOptions.filters || compiler.getDefaultFilters();
            tools = requestOptions.tools;
            libraries = requestOptions.libraries || [];
        } else if (req.body && req.body.compiler) {
            source = req.body.source;
            if (req.body.bypassCache)
                bypassCache = true;
            options = req.body.userArguments;
            executionParameters.args = req.body.executeParametersArgs;
            executionParameters.stdin = req.body.executeParametersStdin;

            filters = compiler.getDefaultFilters();
            _.each(filters, (value, item) => {
                filters[item] = (req.body[item] === 'true');
            });

            backendOptions.skipAsm = req.body.skipAsm === 'true';
        } else {
            // API-style
            source = req.body;
            options = req.query.options;
            // By default we get the default filters.
            filters = compiler.getDefaultFilters();
            // If specified exactly, we'll take that with ?filters=a,b,c
            if (req.query.filters) {
                filters = _.object(_.map(req.query.filters.split(','), filter => [filter, true]));
            }
            // Add a filter. ?addFilters=binary
            _.each((req.query.addFilters || '').split(','), filter => {
                if (filter) filters[filter] = true;
            });
            // Remove a filter. ?removeFilter=intel
            _.each((req.query.removeFilters || '').split(','), filter => {
                if (filter) delete filters[filter];
            });
            // Ask for asm not to be returned
            backendOptions.skipAsm = req.query.skipAsm === 'true';

        }
        options = utils.splitArguments(options);
        if (!Array.isArray(executionParameters.args)) {
            executionParameters.args = utils.splitArguments(executionParameters.args);
        }

        tools = tools || [];
        tools.forEach((tool) => {
            tool.args = utils.splitArguments(tool.args);
        });
        return {source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries};
    }

    handlePopularArguments(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) return next();
        res.send(compiler.possibleArguments.getPopularArguments(this.getUsedOptions(req)));
    }

    handleOptimizationArguments(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) return next();
        res.send(compiler.possibleArguments.getOptimizationArguments(this.getUsedOptions(req)));
    }

    getUsedOptions(req) {
        if (req.body) {
            const data = (typeof req.body === 'string') ? JSON.parse(req.body) : req.body;

            if (data.presplit) {
                return data.usedOptions;
            } else {
                return utils.splitArguments(data.usedOptions);
            }
        }
        return false;
    }

    handle(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) {
            return next();
        }
        const {
            source, options, backendOptions, filters,
            bypassCache, tools, executionParameters, libraries,
        } = this.parseRequest(req, compiler);
        const remote = compiler.getRemote();
        if (remote) {
            req.url = remote.path;
            this.proxy.web(req, res, {target: remote.target, changeOrigin: true}, e => {
                logger.error('Proxy error: ', e);
                next(e);
            });
            return;
        }

        if (source === undefined) {
            logger.warn('No body found in request', req);
            return next(new Error('Bad request'));
        }

        function textify(array) {
            return _.pluck(array || [], 'text').join('\n');
        }

        this.compileGauge.inc({language: compiler.lang.name});
        // eslint-disable-next-line promise/catch-or-return
        compiler.compile(source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries)
            .then(
                result => {
                    if (req.accepts(['text', 'json']) === 'json') {
                        res.send(result);
                    } else {
                        res.set('Content-Type', 'text/plain');
                        try {
                            if (!_.isEmpty(this.textBanner)) res.write('# ' + this.textBanner + '\n');
                            res.write(textify(result.asm));
                            if (result.code !== 0) res.write('\n# Compiler exited with result code ' + result.code);
                            if (!_.isEmpty(result.stdout)) res.write('\nStandard out:\n' + textify(result.stdout));
                            if (!_.isEmpty(result.stderr)) res.write('\nStandard error:\n' + textify(result.stderr));

                            if (result.execResult) {
                                this.executeGauge.inc({language: compiler.lang.name});
                                res.write('\n\n# Execution result with exit code ' + result.execResult.code + '\n');
                                if (!_.isEmpty(result.execResult.stdout)) {
                                    res.write('# Standard out:\n' + textify(result.execResult.stdout));
                                }
                                if (!_.isEmpty(result.execResult.stderr)) {
                                    res.write('\n# Standard error:\n' + textify(result.execResult.stderr));
                                }
                            }
                        } catch (ex) {
                            Sentry.captureException(ex);
                            res.write(`Error handling request: ${ex}`);
                        }
                        res.end('\n');
                    }
                },
                error => {
                    if (typeof (error) !== 'string') {
                        if (error.stack) {
                            logger.error('Error during compilation: ', error);
                            Sentry.captureException(error);
                        } else if (error.code) {
                            logger.error('Error during compilation: ', error.code);
                            if (typeof (error.stderr) === 'string') {
                                error.stdout = utils.parseOutput(error.stdout);
                                error.stderr = utils.parseOutput(error.stderr);
                            }
                            res.end(JSON.stringify(error));
                            return;
                        } else {
                            logger.error('Error during compilation: ', error);
                        }

                        error = `Internal Compiler Explorer error: ${error.stack || error}`;
                    } else {
                        logger.error('Error during compilation: ', {error});
                    }
                    res.end(JSON.stringify({code: -1, stderr: [{text: error}]}));
                });
    }
}

export function SetTestMode() {
    hasSetUpAutoClean = true;
}
<commit_msg>Address @apmorton comments<commit_after>// Copyright (c) 2016, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import * as Sentry from '@sentry/node';
import fs from 'fs-extra';
import httpProxy from 'http-proxy';
import path from 'path';
import PromClient from 'prom-client';
import temp from 'temp';
import _ from 'underscore';
import which from 'which';

import { getCompilerTypeByKey } from '../compilers';
import { logger } from '../logger';
import * as utils from '../utils';

temp.track();

let hasSetUpAutoClean = false;

function initialise(compilerEnv) {
    if (hasSetUpAutoClean) return;
    hasSetUpAutoClean = true;
    const tempDirCleanupSecs = compilerEnv.ceProps('tempDirCleanupSecs', 600);
    logger.info(`Cleaning temp dirs every ${tempDirCleanupSecs} secs`);

    let cyclesBusy = 0;
    setInterval(() => {
        const status = compilerEnv.compilationQueue.status();
        if (status.busy) {
            cyclesBusy++;
            logger.warn(
                `temp cleanup skipped, pending: ${status.pending}, waiting: ${status.size}, cycles: ${cyclesBusy}`);
            return;
        }

        cyclesBusy = 0;

        temp.cleanup((err, stats) => {
            if (err) logger.error('temp cleanup error', err);
            if (stats) logger.debug('temp cleanup stats', stats);
        });
    }, tempDirCleanupSecs * 1000);
}

export class CompileHandler {
    constructor(compilationEnvironment, awsProps) {
        this.compilersById = {};
        this.compilerEnv = compilationEnvironment;
        this.textBanner = this.compilerEnv.ceProps('textBanner');
        this.proxy = httpProxy.createProxyServer({});
        this.awsProps = awsProps;
        initialise(this.compilerEnv);

        this.compileCounter = new PromClient.Counter({
            name: 'ce_compilations_total',
            help: 'Number of compilations',
            labelNames: ['language'],
        });

        this.executeCounter = new PromClient.Counter({
            name: 'ce_executions_total',
            help: 'Number of executions',
            labelNames: ['language'],
        });

        // Mostly cribbed from
        // https://github.com/nodejitsu/node-http-proxy/blob/master/examples/middleware/bodyDecoder-middleware.js
        // We just keep the body as-is though: no encoding using queryString.stringify(), as we don't use a form
        // decoding middleware.
        this.proxy.on('proxyReq', function (proxyReq, req) {
            if (!req.body || Object.keys(req.body).length === 0) {
                return;
            }

            const contentType = proxyReq.getHeader('Content-Type');
            let bodyData;

            if (contentType === 'application/json') {
                bodyData = JSON.stringify(req.body);
            }

            if (contentType === 'application/x-www-form-urlencoded') {
                bodyData = req.body;
            }

            if (bodyData) {
                proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
                proxyReq.write(bodyData);
            }
        });
    }

    async create(compiler) {
        const type = compiler.compilerType || 'default';
        const compilerClass = getCompilerTypeByKey(type);

        // attempt to resolve non absolute exe paths
        if (compiler.exe && !path.isAbsolute(compiler.exe)) {
            const exe = await which(compiler.exe).catch(() => null);
            if (exe) {
                logger.debug(`Resolved '${compiler.exe}' to path '${exe}'`);
            } else {
                // errors resolving to absolute path are not fatal for backwards compatibility sake
                logger.error(`Unable to resolve '${compiler.exe}'`);
            }
        }

        if (compiler.exe && path.isAbsolute(compiler.exe)) {
            // Try stat'ing the compiler to cache its mtime and only re-run it if it
            // has changed since the last time.
            try {
                const res = await fs.stat(compiler.exe);
                const cached = this.findCompiler(compiler.lang, compiler.id);
                if (cached && cached.mtime.getTime() === res.mtime.getTime()) {
                    logger.debug(`${compiler.id} is unchanged`);
                    return cached;
                }
                const compilerObj = new compilerClass(compiler, this.compilerEnv);
                return compilerObj.initialise(res.mtime);
            } catch (err) {
                logger.warn(`Unable to stat ${compiler.id} compiler binary: `, err);
                return null;
            }
        } else {
            return new compilerClass(compiler, this.compilerEnv);
        }
    }

    async setCompilers(compilers) {
        // Be careful not to update this.compilersById until we can replace it entirely.
        const compilersById = {};
        try {
            const createdCompilers = _.compact(await Promise.all(_.map(compilers, this.create, this)));
            for (const compiler of createdCompilers) {
                const langId = compiler.compiler.lang;
                if (!compilersById[langId]) compilersById[langId] = {};
                compilersById[langId][compiler.compiler.id] = compiler;
            }
            if (this.awsProps) {
                logger.info('Fetching possible arguments from storage');
                await Promise.all(createdCompilers.map(
                    compiler => compiler.possibleArguments.loadFromStorage(this.awsProps)));
            }
            this.compilersById = compilersById;
            return createdCompilers.map(compiler => compiler.getInfo());
        } catch (err) {
            logger.error('Exception while processing compilers:', err);
        }
    }

    findCompiler(langId, compilerId) {
        if (!compilerId) return;

        const langCompilers = this.compilersById[langId];
        if (langCompilers && langCompilers[compilerId]) {
            return langCompilers[compilerId];
        }
        // If the lang is bad, try to find it in every language
        let response;
        _.each(this.compilersById, compilerInLang => {
            if (response === undefined) {
                _.each(compilerInLang, compiler => {
                    if (response === undefined &&
                        (compiler.compiler.id === compilerId || compiler.compiler.alias === compilerId)) {

                        response = compiler;
                    }
                });
            }
        });
        return response;
    }

    compilerFor(req) {
        if (req.is('json')) {
            const compiler = this.findCompiler(req.lang || req.body.lang, req.params.compiler);
            if (!compiler) {
                const withoutBody = _.extend({}, req.body, {source: '<removed>'});
                logger.warn(`Unable to find compiler with lang ${req.lang} for request`, withoutBody);
            }
            return compiler;
        } else if (req.body && req.body.compiler) {
            const compiler = this.findCompiler(req.body.lang, req.body.compiler);
            if (!compiler) {
                const withoutBody = _.extend({}, req.body, {source: '<removed>'});
                logger.warn(`Unable to find compiler with lang ${req.body.lang} for request`, withoutBody);
            }
            return compiler;
        } else {
            const compiler = this.findCompiler(req.lang, req.params.compiler);
            if (!compiler) {
                logger.warn(`Unable to find compiler with lang ${req.lang} for request params`, req.params);
            }
            return compiler;
        }
    }

    parseRequest(req, compiler) {
        let source, options, backendOptions = {}, filters, bypassCache = false, tools, executionParameters = {};
        let libraries = [];
        // IF YOU MODIFY ANYTHING HERE PLEASE UPDATE THE DOCUMENTATION!
        if (req.is('json')) {
            // JSON-style request
            const requestOptions = req.body.options;
            source = req.body.source;
            if (req.body.bypassCache)
                bypassCache = true;
            options = requestOptions.userArguments;
            const execParams = requestOptions.executeParameters || {};
            executionParameters.args = execParams.args;
            executionParameters.stdin = execParams.stdin;
            backendOptions = requestOptions.compilerOptions || {};
            filters = requestOptions.filters || compiler.getDefaultFilters();
            tools = requestOptions.tools;
            libraries = requestOptions.libraries || [];
        } else if (req.body && req.body.compiler) {
            source = req.body.source;
            if (req.body.bypassCache)
                bypassCache = true;
            options = req.body.userArguments;
            executionParameters.args = req.body.executeParametersArgs;
            executionParameters.stdin = req.body.executeParametersStdin;

            filters = compiler.getDefaultFilters();
            _.each(filters, (value, item) => {
                filters[item] = (req.body[item] === 'true');
            });

            backendOptions.skipAsm = req.body.skipAsm === 'true';
        } else {
            // API-style
            source = req.body;
            options = req.query.options;
            // By default we get the default filters.
            filters = compiler.getDefaultFilters();
            // If specified exactly, we'll take that with ?filters=a,b,c
            if (req.query.filters) {
                filters = _.object(_.map(req.query.filters.split(','), filter => [filter, true]));
            }
            // Add a filter. ?addFilters=binary
            _.each((req.query.addFilters || '').split(','), filter => {
                if (filter) filters[filter] = true;
            });
            // Remove a filter. ?removeFilter=intel
            _.each((req.query.removeFilters || '').split(','), filter => {
                if (filter) delete filters[filter];
            });
            // Ask for asm not to be returned
            backendOptions.skipAsm = req.query.skipAsm === 'true';

        }
        options = utils.splitArguments(options);
        if (!Array.isArray(executionParameters.args)) {
            executionParameters.args = utils.splitArguments(executionParameters.args);
        }

        tools = tools || [];
        tools.forEach((tool) => {
            tool.args = utils.splitArguments(tool.args);
        });
        return {source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries};
    }

    handlePopularArguments(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) return next();
        res.send(compiler.possibleArguments.getPopularArguments(this.getUsedOptions(req)));
    }

    handleOptimizationArguments(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) return next();
        res.send(compiler.possibleArguments.getOptimizationArguments(this.getUsedOptions(req)));
    }

    getUsedOptions(req) {
        if (req.body) {
            const data = (typeof req.body === 'string') ? JSON.parse(req.body) : req.body;

            if (data.presplit) {
                return data.usedOptions;
            } else {
                return utils.splitArguments(data.usedOptions);
            }
        }
        return false;
    }

    handle(req, res, next) {
        const compiler = this.compilerFor(req);
        if (!compiler) {
            return next();
        }
        const {
            source, options, backendOptions, filters,
            bypassCache, tools, executionParameters, libraries,
        } = this.parseRequest(req, compiler);
        const remote = compiler.getRemote();
        if (remote) {
            req.url = remote.path;
            this.proxy.web(req, res, {target: remote.target, changeOrigin: true}, e => {
                logger.error('Proxy error: ', e);
                next(e);
            });
            return;
        }

        if (source === undefined) {
            logger.warn('No body found in request', req);
            return next(new Error('Bad request'));
        }

        function textify(array) {
            return _.pluck(array || [], 'text').join('\n');
        }

        this.compileCounter.inc({language: compiler.lang.id});
        // eslint-disable-next-line promise/catch-or-return
        compiler.compile(source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries)
            .then(
                result => {
                    if (req.accepts(['text', 'json']) === 'json') {
                        res.send(result);
                    } else {
                        res.set('Content-Type', 'text/plain');
                        try {
                            if (!_.isEmpty(this.textBanner)) res.write('# ' + this.textBanner + '\n');
                            res.write(textify(result.asm));
                            if (result.code !== 0) res.write('\n# Compiler exited with result code ' + result.code);
                            if (!_.isEmpty(result.stdout)) res.write('\nStandard out:\n' + textify(result.stdout));
                            if (!_.isEmpty(result.stderr)) res.write('\nStandard error:\n' + textify(result.stderr));

                            if (result.execResult) {
                                this.executeCounter.inc({language: compiler.lang.id});
                                res.write('\n\n# Execution result with exit code ' + result.execResult.code + '\n');
                                if (!_.isEmpty(result.execResult.stdout)) {
                                    res.write('# Standard out:\n' + textify(result.execResult.stdout));
                                }
                                if (!_.isEmpty(result.execResult.stderr)) {
                                    res.write('\n# Standard error:\n' + textify(result.execResult.stderr));
                                }
                            }
                        } catch (ex) {
                            Sentry.captureException(ex);
                            res.write(`Error handling request: ${ex}`);
                        }
                        res.end('\n');
                    }
                },
                error => {
                    if (typeof (error) !== 'string') {
                        if (error.stack) {
                            logger.error('Error during compilation: ', error);
                            Sentry.captureException(error);
                        } else if (error.code) {
                            logger.error('Error during compilation: ', error.code);
                            if (typeof (error.stderr) === 'string') {
                                error.stdout = utils.parseOutput(error.stdout);
                                error.stderr = utils.parseOutput(error.stderr);
                            }
                            res.end(JSON.stringify(error));
                            return;
                        } else {
                            logger.error('Error during compilation: ', error);
                        }

                        error = `Internal Compiler Explorer error: ${error.stack || error}`;
                    } else {
                        logger.error('Error during compilation: ', {error});
                    }
                    res.end(JSON.stringify({code: -1, stderr: [{text: error}]}));
                });
    }
}

export function SetTestMode() {
    hasSetUpAutoClean = true;
}
<|endoftext|>


<commit_before>/**
 * @file ArrowStep component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import classNames from 'classnames';

import ArrowStepItem from '../_ArrowStepItem';

import PureRender from '../_vendors/PureRender';

@PureRender
class ArrowStep extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            activatedStep: props.activatedStep,
            finishedStep: props.finishedStep
        };

        this.touchTapHandler = ::this.touchTapHandler;

    }

    touchTapHandler(activatedStep) {

        const {onChange} = this.props;

        this.setState({
            activatedStep
        }, () => {
            onChange && onChange({
                activatedStep,
                finishedStep: this.state.finishedStep
            });
        });

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.activatedStep !== this.state.activatedStep
            || nextProps.finishedStep !== this.state.finishedStep) {
            this.setState({
                activatedStep: nextProps.activatedStep,
                finishedStep: nextProps.finishedStep
            });
        }
    }

    render() {

        const {className, style, steps} = this.props,
            {activatedStep, finishedStep} = this.state,

            stepClassName = classNames('arrow-step', {
                [className]: className
            });

        return (

                {
                    steps.map((item, index) =>
                        <ArrowStepItem key={index}
                                       index={index}
                                       className={item.className}
                                       style={{
                                           ...item.style,
                                           width: `${100 / steps.length}%`,
                                           zIndex: steps.length - index
                                       }}
                                       isActivatedStep={activatedStep === index}
                                       isFinishedStep={finishedStep >= index}
                                       value={item}
                                       isFirst={index === 0}
                                       isLast={index === steps.length - 1}
                                       onTouchTap={this.touchTapHandler}/>
                    )
                }

        );

    }
};

if (process.env.NODE_ENV === 'development') {

    const PropTypes = require('prop-types');

    ArrowStep.propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * The render content of step.
         */
        steps: PropTypes.arrayOf(PropTypes.shape({

            /**
             * The CSS class name of step element.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the step element.
             */
            style: PropTypes.object,

            /**
             * The text value of step.
             */
            title: PropTypes.string

        })).isRequired,

        /**
         * Sets the step as active.
         */
        activatedStep: PropTypes.number,

        /**
         * The final step.
         */
        finishedStep: PropTypes.number,

        /**
         * Callback function fired when step change.
         */
        onChange: PropTypes.func

    };

}

ArrowStep.defaultProps = {

    className: '',
    style: null,

    steps: [],

    activatedStep: 0,
    finishedStep: 0

};

export default ArrowStep;<commit_msg>update ArrowStep<commit_after>/**
 * @file ArrowStep component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import classNames from 'classnames';

import ArrowStepItem from '../_ArrowStepItem';

import PureRender from '../_vendors/PureRender';

@PureRender
class ArrowStep extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            activatedStep: props.activatedStep,
            finishedStep: props.finishedStep
        };

        this.touchTapHandler = ::this.touchTapHandler;

    }

    touchTapHandler(activatedStep) {

        const {onChange} = this.props;

        this.setState({
            activatedStep
        }, () => {
            onChange && onChange({
                activatedStep,
                finishedStep: this.state.finishedStep
            });
        });

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.activatedStep !== this.state.activatedStep
            || nextProps.finishedStep !== this.state.finishedStep) {
            this.setState({
                activatedStep: nextProps.activatedStep,
                finishedStep: nextProps.finishedStep
            });
        }
    }

    render() {

        const {className, style, steps} = this.props,
            {activatedStep, finishedStep} = this.state,

            stepClassName = classNames('arrow-step', {
                [className]: className
            });

        return (

                {
                    steps.map((item, index) =>
                        <ArrowStepItem key={index}
                                       index={index}
                                       className={item.className}
                                       style={{
                                           ...item.style,
                                           width: `${100 / steps.length}%`,
                                           zIndex: steps.length - index
                                       }}
                                       isActivatedStep={activatedStep === index}
                                       isFinishedStep={finishedStep >= index}
                                       value={item}
                                       isFirst={index === 0}
                                       isLast={index === steps.length - 1}
                                       onTouchTap={this.touchTapHandler}/>
                    )
                }

        );

    }
};

if (process.env.NODE_ENV === 'development') {

    const PropTypes = require('prop-types');

    ArrowStep.propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * The render content of step.
         */
        steps: PropTypes.arrayOf(PropTypes.shape({

            /**
             * The CSS class name of step element.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the step element.
             */
            style: PropTypes.object,

            /**
             * The text value of step.
             */
            title: PropTypes.string

        })).isRequired,

        /**
         * Sets the step as active.
         */
        activatedStep: PropTypes.number,

        /**
         * The final step.
         */
        finishedStep: PropTypes.number,

        /**
         * Callback function fired when step change.
         */
        onChange: PropTypes.func

    };

}

ArrowStep.defaultProps = {

    steps: [],

    activatedStep: 0,
    finishedStep: 0

};

export default ArrowStep;<|endoftext|>


<commit_before>// ==UserScript==
// @name        Zendesk Chat Notifications
// @namespace   http://www.software-architects.at
// @description Adds desktop notifications to zendesk chat.
// @match       https://*.zendesk.com/agent/*
// @grant       none
// @version     1.1
// @copyright   2014 software architects gmbh
// ==/UserScript==

var currentNotifications = [];
var chatsDiv = null;

function processChatsAdded(event) {
	// find invite nodes
	if (Notification.permission === "granted") {
		var invites = chatsDiv.find('.ember-view.invite');
		$.each(invites, function( index, value ) {
			if ($.inArray(value, currentNotifications) < 0) {
				var username = chatsDiv.find('a.username').text();
				var subject = chatsDiv.find('div.chat-subject').text();
				if (username && subject) {
					console.debug('ZendeskChat: new chat request found - notifying');
					var title = "Chat request from: " + username;
					var options = {
						body : subject,
						icon : 'http://www.zendesk.com/favicon.ico'
					};

					var notification = new Notification(title, options);
					notification.onclick = function(x) { window.focus(); };
					currentNotifications.push(value);
					$(value).on("remove", function () {
						console.debug('ZendeskChat: chat request ended - removing notification');
						currentNotifications.splice( $.inArray(this, currentNotifications), 1 );
						notification.close();
					});
				}
			}
		});
	}
}

// try to find the main chat dom element
function findChat() {
	chatsDiv = $('#incoming-chats');
	if (chatsDiv.length === 0) {
		// not found -> DOM not fully built -> retry in due time
		window.setTimeout(function () { findChat(); }, 1000);
	}
	else {
		console.debug('ZendeskChat: found incoming-chats');
		// if the current div gets lost -> look for it again
		chatsDiv.on("remove", function () {
			console.debug('ZendeskChat: incoming-chats lost - rediscovering');
			findChat();
		});

		chatsDiv.on('DOMNodeInserted', processChatsAdded);

		// if permissions were not yet granted provide button
		if (Notification.permission !== "granted" && Notification.permission !== 'denied') {
			console.debug('ZendeskChat: notification permission not yet granted - offering request');

			var button = $('enable notifications');
			$('#chat-header').append(button);
			button.click(function() {
				console.debug('ZendeskChat: requesting notification permission');
				Notification.requestPermission(function (permission) {
					console.debug('ZendeskChat: notification permission request handled: ' + permission);
					if(!('permission' in Notification)) {
						Notification.permission = permission;
					}

					button.remove();
				});
			});
		}
	}
}

// check for notification support
if (("Notification" in window)) {
	console.debug('ZendeskChat: notifications supported');
	window.setTimeout(function () { findChat(); }, 500);
}
else {
	console.debug('ZendeskChat: notifications not supported');
}<commit_msg>added first sound looping<commit_after>// ==UserScript==
// @name        Zendesk Chat Notifications
// @namespace   http://www.software-architects.at
// @description Adds desktop notifications to zendesk chat.
// @match       https://*.zendesk.com/agent/*
// @grant       GM_getValue
// @grant       GM_setValue
// @version     1.2
// @copyright   2014 software architects gmbh
// ==/UserScript==

var currentNotifications = [];
var chatsDiv = null;
var loopTimer = null;
var enableDesktop = false;
var enableLoop = false;

function processChatsAdded(event) {
    if (enableDesktop && Notification.permission === "granted") {
        // find invite nodes
        var invites = chatsDiv.find('.ember-view.invite');
        $.each(invites, function( index, value ) {

            // only handle each request once
            if ($.inArray(value, currentNotifications) < 0) {
                var username = chatsDiv.find('a.username').text();
                var subject = chatsDiv.find('div.chat-subject').text();

                // only if we could gather all the info
                if (username && subject) {
                    console.debug('ZendeskChat: new chat request - notifying');
                    var title = "Chat request: " + username;
                    var options = {
                        body : subject,
                        icon : 'http://www.zendesk.com/favicon.ico'
                    };

                    var notification = new Notification(title, options);
                    notification.onclick = function(x) { window.focus(); };
                    currentNotifications.push(value);

                    // sound looping
                    if (enableLoop && loopTimer === null) {
                        console.debug('ZendeskChat: looping sound');
                        loopTimer = window.setInterval(function () {
                            soundManager.sounds.invite.play();
                            console.debug('ZendeskChat: beeping');
                        }, 4000);
                    }

                    // when the zendesk-internal notification closes
                    $(value).on("remove", function () {
                        console.debug('ZendeskChat: chat request ended');
                        currentNotifications.splice( $.inArray(this, currentNotifications), 1 );
                        if (currentNotifications.length === 0) {
                            console.debug('ZendeskChat: ending sound loop');
                            window.clearInterval(loopTimer);
                            loopTimer = null;
                        }

                        notification.close();
                    });
                }
            }
        });
    }
}

// try to find the main chat dom element
function findChat() {
    chatsDiv = $('#incoming-chats');
    if (chatsDiv.length === 0) {
        // not found -> DOM not fully built -> retry in due time
        window.setTimeout(function () { findChat(); }, 1000);
    }
    else {
        console.debug('ZendeskChat: found incoming-chats');
        // if the current div gets lost -> look for it again
        chatsDiv.on("remove", function () {
            console.debug('ZendeskChat: incoming-chats lost - rediscovering');
            findChat();
        });

        chatsDiv.on('DOMNodeInserted', processChatsAdded);

        // for pre-1.2 scripts assume that a granted permission means notify
        if (typeof GM_getValue('desktop') === 'undefined' && Notification.permission === "granted") {
            GM_setValue('desktop', true);
        }

        // add notify checkbox
        var notifications = $('<input type="checkbox">notify</input>');
        enableDesktop = GM_getValue('desktop', false);
        notifications.prop('checked', enableDesktop);
        $('#chat-header').append(notifications);
        notifications.change(function() {
            enableDesktop = $(this).is(':checked');
            GM_setValue('desktop', enableDesktop);
            if (enableDesktop && Notification.permission !== "granted" && Notification.permission !== 'denied') {
                console.debug('ZendeskChat: requesting notification permission');
                Notification.requestPermission(function (permission) {
                    console.debug('ZendeskChat: permission: ' + permission);
                    if(!('permission' in Notification)) {
                        Notification.permission = permission;
                    }
                });
            }
        });

        // add loop checkbox
        var loop = $('<input type="checkbox">loop</input>');
        enableLoop = GM_getValue('loop', false);
        loop.prop('checked', enableLoop);
        $('#chat-header').append(loop);
        loop.change(function() {
            enableLoop = $(this).is(':checked');
            GM_setValue('loop', enableLoop);
        });
    }
}

// check for notification support
if (("Notification" in window)) {
    console.debug('ZendeskChat: notifications supported');
    window.setTimeout(function () { findChat(); }, 500);
}
else {
    console.debug('ZendeskChat: notifications not supported');
}<|endoftext|>


<commit_before>/**
 * @jest-environment jest-environment-webdriver
 */

const path = require('path')
const ActionHero = require(path.join(__dirname, '..', '..', 'index.js'))
const actionhero = new ActionHero.Process()
const packageJSON = require(path.join(__dirname, '..', '..', 'package.json'))

let api
let url

const ensureNoErrors = async () => {
  const errorMessage = await browser.findElement(by.id('error')).getText()
  if (errorMessage) { throw new Error(errorMessage) }
}

describe('browser integration tests', () => {
  beforeAll(async () => {
    api = await actionhero.start()
    url = 'http://localhost:' + api.config.servers.web.port
  })

  afterAll(async () => { await actionhero.stop() })

  describe('default index page', () => {
    beforeAll(async () => {
      await browser.get(url)
      await ensureNoErrors()
      await browser.sleep(1000)
    })

    test('loads the page', async () => {
      const title = await browser.findElement(by.tagName('h1')).getText()
      expect(title).toEqual('Your ActionHero Server is working.')
    })

    test('server status is loaded', async () => {
      const serverName = await browser.findElement(by.id('serverName')).getText()
      expect(serverName).toEqual('actionhero')
      const actionheroVersion = await browser.findElement(by.id('actionheroVersion')).getText()
      expect(actionheroVersion).toEqual(packageJSON.version)
    })

    test('documentation is loaded', async () => {
      const actionNameElements = await browser.findElements(by.tagName('h3'))
      const actionNames = []
      for (const i in actionNameElements) {
        actionNames.push(await actionNameElements[i].getText())
      }

      const expextedActions = [
        'cacheTest (v1)',
        'createChatRoom (v1)',
        'randomNumber (v1)',
        'showDocumentation (v1)',
        'sleepTest (v1)',
        'status (v1)',
        'validationTest (v1)'
      ]

      expect(actionNames).toEqual(expect.arrayContaining(expextedActions))
    })
  })

  describe('chat test page', () => {
    let sessionIDCookie

    test('I can be assigned a session on another page', async () => {
      await browser.get(url)
      await ensureNoErrors()
      sessionIDCookie = await browser.manage().getCookie('sessionID')
      expect(sessionIDCookie.value).toBeTruthy()
    })

    describe('on the chat page', () => {
      beforeAll(async () => {
        await browser.get(`${url}/chat.html`)
        browser.sleep(1000)
      })

      afterAll(async () => {
        // navigate away to close the WS connection
        await browser.get(url)
      })

      test('can connect', async () => {
        const chat = await browser.findElement(by.id('chatBox')).getText()
        expect(chat).toContain('Hello! Welcome to the actionhero api')
      })

      test('can chat', async () => {
        const chatForm = await browser.findElement(by.id('message'))
        await chatForm.sendKeys('hello world')
        const chatSumbit = await browser.findElement(by.id('submitButton'))
        await chatSumbit.click()

        browser.sleep(1000)
        const chat = await browser.findElement(by.id('chatBox')).getText()
        expect(chat).toContain('hello world')
      })

      test('has the same fingerprint', async () => {
        const thisSessionID = await browser.manage().getCookie('sessionID')
        expect(thisSessionID.value).toEqual(sessionIDCookie.value)
        const fingerprintFromWebSocket = await browser.findElement(by.id('fingerprint')).getText()
        expect(fingerprintFromWebSocket).toEqual(sessionIDCookie.value)
      })
    })
  })
})
<commit_msg>update browser tests for new styling<commit_after>/**
 * @jest-environment jest-environment-webdriver
 */

const path = require('path')
const ActionHero = require(path.join(__dirname, '..', '..', 'index.js'))
const actionhero = new ActionHero.Process()
const packageJSON = require(path.join(__dirname, '..', '..', 'package.json'))

let api
let url

const ensureNoErrors = async () => {
  const errorMessage = await browser.findElement(by.id('error')).getText()
  if (errorMessage) { throw new Error(errorMessage) }
}

describe('browser integration tests', () => {
  beforeAll(async () => {
    api = await actionhero.start()
    url = 'http://localhost:' + api.config.servers.web.port
  })

  afterAll(async () => { await actionhero.stop() })

  describe('default index page', () => {
    beforeAll(async () => {
      await browser.get(url)
      await ensureNoErrors()
      await browser.sleep(1000)
    })

    test('loads the page', async () => {
      const title = await browser.findElement(by.tagName('h1')).getText()
      expect(title).toEqual('Your Actionhero Server is working.')
    })

    test('server status is loaded', async () => {
      const serverName = await browser.findElement(by.id('serverName')).getText()
      expect(serverName).toEqual('actionhero')
      const actionheroVersion = await browser.findElement(by.id('actionheroVersion')).getText()
      expect(actionheroVersion).toEqual(packageJSON.version)
    })

    test('documentation is loaded', async () => {
      const actionNameElements = await browser.findElements(by.tagName('h3'))
      const actionNames = []
      for (const i in actionNameElements) {
        actionNames.push(await actionNameElements[i].getText())
      }

      const expextedActions = [
        'cacheTest v1',
        'createChatRoom v1',
        'randomNumber v1',
        'showDocumentation v1',
        'sleepTest v1',
        'status v1',
        'validationTest v1'
      ]

      expect(actionNames).toEqual(expect.arrayContaining(expextedActions))
    })
  })

  describe('chat test page', () => {
    let sessionIDCookie

    test('I can be assigned a session on another page', async () => {
      await browser.get(url)
      await ensureNoErrors()
      sessionIDCookie = await browser.manage().getCookie('sessionID')
      expect(sessionIDCookie.value).toBeTruthy()
    })

    describe('on the chat page', () => {
      beforeAll(async () => {
        await browser.get(`${url}/chat.html`)
        browser.sleep(1000)
      })

      afterAll(async () => {
        // navigate away to close the WS connection
        await browser.get(url)
      })

      test('can connect', async () => {
        const chat = await browser.findElement(by.id('chatBox')).getText()
        expect(chat).toContain('Hello! Welcome to the actionhero api')
      })

      test('can chat', async () => {
        const chatForm = await browser.findElement(by.id('message'))
        await chatForm.sendKeys('hello world')
        const chatSumbit = await browser.findElement(by.id('submitButton'))
        await chatSumbit.click()

        browser.sleep(1000)
        const chat = await browser.findElement(by.id('chatBox')).getText()
        expect(chat).toContain('hello world')
      })

      test('has the same fingerprint', async () => {
        const thisSessionID = await browser.manage().getCookie('sessionID')
        expect(thisSessionID.value).toEqual(sessionIDCookie.value)
        const fingerprintFromWebSocket = await browser.findElement(by.id('fingerprint')).getText()
        expect(fingerprintFromWebSocket).toEqual(sessionIDCookie.value)
      })
    })
  })
})
<|endoftext|>


<commit_before>var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var _ = require('lodash');
var util = require('./util.js');
var ejs = require('ejs');
var fs = require("fs");

module.exports = function () {

  var authRequestTemplateXMLFile = __dirname + "/../xml-templates/authnResponse-default.ejs";

  function decodeAndParseAuthenticationRequest(requestParams, cb) {
    decodeAuthenticationRequest(requestParams, function (err, xml) {
      if (err) return cb(err);
      var requestPostBinding = util.decode(xml, "utf8", "base64");

      var doc;
      try {
        doc = new dom().parseFromString(xml);
      } catch (e) {
        return cb(e);
      }
      parseAuthenticationRequest(doc, function (err, requestData) {
        if (err) return cb(err);
        if (requestParams.RelayState) {
          requestData.relayState = requestParams.RelayState;
        }
        requestData.requestPostBinding = requestPostBinding;
        cb(null, requestData);
      });
    });
  }

  function parseAuthenticationRequest(doc, cb) {
    var requestData = {};
    var request = xpath.select1("*[local-name(.)='AuthnRequest']", doc);
    if (!request) {
      return cb(new Error("No document request"));
    }

    requestData.authRequestId = request.getAttribute("ID");
    requestData.issueInstant = request.getAttribute("IssueInstant");
    requestData.acsUrl = request.getAttribute("AssertionConsumerServiceURL");

    var issuer = xpath.select1("*[local-name(.)='Issuer']", request);
    if (!issuer) {
      return cb(new Error("No document issuer"));
    }
    requestData.issuer = issuer.firstChild.data;

    requestData.nameIDPolicies = [];
    var nameIds = xpath.select("*[local-name(.)='NameIDPolicy']", request);
    if (nameIds) {
      for (var i = 0; i < nameIds.length; i++) {
        var nameId = nameIds[i];
        requestData.nameIDPolicies.push({
          allowCreate: nameId.getAttribute('AllowCreate'),
          format: nameId.getAttribute('Format')
        });
      }
    }

    cb(null, requestData);
  }

  function decodeAuthenticationRequest(requestParams, cb) {
    // TODO: might eventually want to handle signed requests (not used frequently)
    if (!requestParams.SAMLRequest) {
      return cb(new Error("Request parameters contain no SAMLRequest property"));
    }
    if (requestParams.binding === "redirect") {
      return util.inflateAndDecode(requestParams.SAMLRequest, cb);
    } else {
      cb(null, util.decode(requestParams.SAMLRequest, "base64", "utf8"));
    }
  }

  function generateAuthenticationResponse(settings, cb) {
    var ejsParams = util.generateAuthResponseParams(settings);
    var xmlTemplate = fs.readFileSync(authRequestTemplateXMLFile).toString();
    var xml = ejs.render(xmlTemplate, ejsParams);
    xml = xml.replace(/>\s*/g, '>');
    xml = xml.replace(/\s*</g, '<');

    // sign it
    var signedXml = util.signAuthenticationResponse(xml, settings.idp.privateKey, settings.idp.cert);

    // compress it
    if (settings.binding == "redirect") {
      return util.deflateAndEncode(signedXml, cb);
    } else {
      // always default to post binding
      return cb(null, util.decode(signedXml, "utf8", "base64"));
    }
  }

  return {
    decodeAndParseAuthenticationRequest: decodeAndParseAuthenticationRequest,
    decodeAuthenticationRequest: decodeAuthenticationRequest,
    generateAuthenticationResponse: generateAuthenticationResponse
  }
};
<commit_msg>Added errorHandler function with debugger - wip<commit_after>var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var _ = require('lodash');
var util = require('./util.js');
var ejs = require('ejs');
var fs = require("fs");

module.exports = function () {

  var authRequestTemplateXMLFile = __dirname + "/../xml-templates/authnResponse-default.ejs";

  function decodeAndParseAuthenticationRequest(requestParams, cb) {
    decodeAuthenticationRequest(requestParams, function (err, xml) {
      if (err) return cb(err);
      var requestPostBinding = util.decode(xml, "utf8", "base64");

      var doc;
      try {
        doc = new dom({
          errorHandler: function() {
            debugger;
          }
        }).parseFromString(xml);
      } catch (e) {
        return cb(e);
      }
      parseAuthenticationRequest(doc, function (err, requestData) {
        if (err) return cb(err);
        if (requestParams.RelayState) {
          requestData.relayState = requestParams.RelayState;
        }
        requestData.requestPostBinding = requestPostBinding;
        cb(null, requestData);
      });
    });
  }

  function parseAuthenticationRequest(doc, cb) {
    var requestData = {};
    var request = xpath.select1("*[local-name(.)='AuthnRequest']", doc);
    if (!request) {
      return cb(new Error("No document request"));
    }

    requestData.authRequestId = request.getAttribute("ID");
    requestData.issueInstant = request.getAttribute("IssueInstant");
    requestData.acsUrl = request.getAttribute("AssertionConsumerServiceURL");

    var issuer = xpath.select1("*[local-name(.)='Issuer']", request);
    if (!issuer) {
      return cb(new Error("No document issuer"));
    }
    requestData.issuer = issuer.firstChild.data;

    requestData.nameIDPolicies = [];
    var nameIds = xpath.select("*[local-name(.)='NameIDPolicy']", request);
    if (nameIds) {
      for (var i = 0; i < nameIds.length; i++) {
        var nameId = nameIds[i];
        requestData.nameIDPolicies.push({
          allowCreate: nameId.getAttribute('AllowCreate'),
          format: nameId.getAttribute('Format')
        });
      }
    }

    cb(null, requestData);
  }

  function decodeAuthenticationRequest(requestParams, cb) {
    // TODO: might eventually want to handle signed requests (not used frequently)
    if (!requestParams.SAMLRequest) {
      return cb(new Error("Request parameters contain no SAMLRequest property"));
    }
    if (requestParams.binding === "redirect") {
      return util.inflateAndDecode(requestParams.SAMLRequest, cb);
    } else {
      cb(null, util.decode(requestParams.SAMLRequest, "base64", "utf8"));
    }
  }

  function generateAuthenticationResponse(settings, cb) {
    var ejsParams = util.generateAuthResponseParams(settings);
    var xmlTemplate = fs.readFileSync(authRequestTemplateXMLFile).toString();
    var xml = ejs.render(xmlTemplate, ejsParams);
    xml = xml.replace(/>\s*/g, '>');
    xml = xml.replace(/\s*</g, '<');

    // sign it
    var signedXml = util.signAuthenticationResponse(xml, settings.idp.privateKey, settings.idp.cert);

    // compress it
    if (settings.binding == "redirect") {
      return util.deflateAndEncode(signedXml, cb);
    } else {
      // always default to post binding
      return cb(null, util.decode(signedXml, "utf8", "base64"));
    }
  }

  return {
    decodeAndParseAuthenticationRequest: decodeAndParseAuthenticationRequest,
    decodeAuthenticationRequest: decodeAuthenticationRequest,
    generateAuthenticationResponse: generateAuthenticationResponse
  }
};
<|endoftext|>


<commit_before>import template from './userLanding.html';
import formReadDialogTemplate from '../formRead/formReadDialog.html';
import comparisonFormTemplate from './requireComparisonFormDialog.html';
import enableContactMeTemplate from './enableContactMeDialog.html';

class UserLandingController {
  constructor($location, $mdDialog, $mdPanel, loomApi, lodash, moment, recruitUnitUtil, jwtHelper, globals, recruitUnitConfig) {
    "ngInject";
    console.log("in UserLandingController");
    this.moment = moment;
    this.$mdDialog = $mdDialog;
    this.recruitUnitUtil = recruitUnitUtil;
    this.loomApi = loomApi;

    this.username = globals.username;
    this.userguid = globals.userguid;
    this.roles = globals.roles;
    this.id = globals.id;
    this.myContentListArray = globals.myContentListArray;
		this.jobItemDocsArray = globals.jobItemDocsArray;
    this.myContentListPassCount = globals.myContentListPassCount;
    this.myContentListFailCount = globals.myContentListFailCount;
    this.isDeveloper = globals.isDeveloper;

    this._mdPanel = $mdPanel;
    this.openFrom = 'button';
    this.closeTo = 'button';

    var token = jwtHelper.decodeToken(recruitUnitUtil.Util.getLocalUser().token);
    //console.log(recruitUnitConfig);
    if (typeof token.userGuid != 'undefined') {
      if (recruitUnitConfig.APP_ENV != 'prod') {
				var serverUrl = recruitUnitConfig.APP_PROTOCOL + recruitUnitConfig.APP_HOST + ":" + recruitUnitConfig.APP_PORT;
				this.unitTestFormUrl = serverUrl + recruitUnitConfig.PATH_USER + token.userGuid + "/form";
			} else {
				var serverUrl = recruitUnitConfig.APP_PROTOCOL + recruitUnitConfig.APP_HOST;
				this.unitTestFormUrl = serverUrl + recruitUnitConfig.PATH_USER + token.userGuid + "/form";
      }
    }

    //check if the user has a comparison test form and is a developer. Show alert dialog if not
    //todo: might need to move this to the canActivate?
    if (this.recruitUnitUtil.Util.getLocalUser().token !== null) {
      var decodedToken = jwtHelper.decodeToken(this.recruitUnitUtil.Util.getLocalUser().token);
      if (decodedToken.isComparisonFormEnabled) {
        console.log("User has a comparison form enabled");
      } else if (decodedToken.roles.indexOf(this.recruitUnitUtil.Constants.DEVELOPER_ROLE) != -1) {
        this.showPanel();
      }
    }
  }

  $routerOnActivate(next, previous){
    this.useremail = next.params.email;
    console.log("route param email:" + this.useremail);
  }

  showFormDetailDialog($event, item){
    console.log("in showFormDetailDialog");
    console.log("item:");
    console.log(item);
    var id, isPass, isPartialPass = "";
    if (item.model == "RecruitUnitJobDescription"){
      id = item._id;
      isPass = "";
      isPartialPass = "";
		} else {
			id = item.id;
			isPass = item.isPass;
			isPartialPass = item.isPartialPass;
		}

    this.$mdDialog.show({
      controller: 'formReadController',
      controllerAs: 'formRead',
      locals: {
        'jobDetailFormId': id,
        'isItemPass': isPartialPass || isPass ? true : false,
        'payFrequencyOptions': [
          {id: "Permanent", value: "annual salary"},
          {id: "Contract", value: "daily rate"}
        ]
      },
      bindToController: true,
      template: formReadDialogTemplate,
      parent: angular.element(document.body),
      targetEvent: $event,
      clickOutsideToClose: true,
      fullscreen: false
    });
  }

  deleteItem(docId, index){
    console.log("delete id:" + docId + ",index:" + index);
    //todo: ensure the update can only change the users own document
    var controllerId = "/services/recruitunit/articles/recruitUnitContentService.controller.js";
    var jobItemModel = "/models/RecruitUnit.Job.All.js";
    var localToken = this.recruitUnitUtil.Util.getLocalUser().token;
    this.loomApi.Article.updateArticle(docId, controllerId, jobItemModel, {"published": false}, localToken).then(angular.bind(this, function (result) {
      console.log("Delete result:");
      console.log(result);
      if (result.success) {
        this.myContentListArray.splice(index, 1);
      }
    }));
  }

  formatUnixDateToNow(unixTime){
    return this.moment.unix(unixTime/1000).from();
  }

  viewItem(id){
    //console.log("view id:" + id);
    $location.path("/user/" + this.useremail + "/form/" + id);
  }

  searchUser(searchJson){
    var comparisonRulesDocId = "";
    var localToken = this.recruitUnitUtil.Util.getLocalUser().token;

    this.loomApi.Article.getUserTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
      console.log("getUserTestResults:");
      console.log(listMyTestContentResult);
      if (typeof listMyTestContentResult !== 'undefined') {
        this.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
        this.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
        this.myContentListFailCount = listMyTestContentResult.length - this.myContentListPassCount;

        return true; //return canActivate state once results are available
      }
    }));
  }

  navigateToUserRules(useremail){
    console.log("navigateToUserRules [" + useremail + "]");
    console.log("redirect to path [" + this.recruitUnitUtil.Constants.PATH_USER + useremail + this.recruitUnitUtil.Constants.PATH_COMPARISONRULESFORM + "]");
    this.recruitUnitUtil.Util.redirectUserToPath(this.recruitUnitUtil.Constants.PATH_USER + useremail + this.recruitUnitUtil.Constants.PATH_COMPARISONRULESFORM);
  }

  getDeveloperEmailAddress(docId, isDeveloper, displayDevEmail){
    if (!isDeveloper && displayDevEmail) {
      var localToken = this.recruitUnitUtil.Util.getLocalUser().token;

      return this.loomApi.User.getDevEmailFromDocId(docId, localToken).then(angular.bind(this, function (result) {
        return typeof result.email !== "undefined" ? result.email : result.message;
      }));
    }
  }

  showPanel(){
    console.log("in showPanel");
    var panelPosition = this._mdPanel.newPanelPosition()
      .absolute()
      .center();
    var panelAnimation = this._mdPanel.newPanelAnimation();
    panelAnimation.openFrom({top: 0, left: 0});
    panelAnimation.closeTo({top: document.documentElement.clientHeight, left: 0});
    panelAnimation.withAnimation(this._mdPanel.animation.SCALE);

    var config = {
      attachTo: angular.element(document.body),
      controller: 'genericDialogController',
      controllerAs: 'requireComparisonFormDialog',
      locals: {
        'useremail': this.recruitUnitUtil.Util.getLocalUser().email,
        'navigateToUserRules': this.navigateToUserRules
      },
      position: panelPosition,
      animation: panelAnimation,
      template: comparisonFormTemplate,
      hasBackdrop: true,
      panelClass: 'generic-dialog',
      zIndex: 150,
      clickOutsideToClose: true,
      escapeToClose: true,
      focusOnOpen: true
    }

    this._mdPanel.open(config);
  }

  toggleContactMeDialog($event, id, displayDevEmail, repeatScope){
    console.log("in toggleContactMeDialog");

    var panelPosition = this._mdPanel.newPanelPosition()
      .absolute()
      .center();
    var panelAnimation = this._mdPanel.newPanelAnimation();
    panelAnimation.openFrom({top: 0, left: 0});
    panelAnimation.closeTo({top: document.documentElement.clientHeight, left: 0});
    panelAnimation.withAnimation(this._mdPanel.animation.SCALE);

    var config = {
      attachTo: angular.element(document.body),
      controller: 'genericDialogController',
      controllerAs: 'contactMeDialog',
      locals: {
        'docId': id,
        'isDisplayDevEmail': displayDevEmail,
        'isDeveloper': this.isDeveloper,
        'developerEmailAddress': this.getDeveloperEmailAddress(id, this.isDeveloper, displayDevEmail),
        itemScope: repeatScope
      },
      bindToController: true,
      position: panelPosition,
      animation: panelAnimation,
      template: enableContactMeTemplate,
      hasBackdrop: true,
      panelClass: 'generic-dialog',
      zIndex: 150,
      clickOutsideToClose: true,
      escapeToClose: true,
      focusOnOpen: true
    }

    this._mdPanel.open(config);
  }

}

export default {
  controller: UserLandingController,
  controllerAs: 'userLanding',
  template: template,
  $canActivate: function($nextInstruction, recruitUnitUtil, jwtHelper, loomApi, globals, lodash, recruitUnitConfig){
    "ngInject";
    console.log("in UserLandingController canActivate");
    //console.log(recruitUnitConfig);

    if (recruitUnitUtil.Util.isLocalUserAvailable()) {
      var token = jwtHelper.decodeToken(recruitUnitUtil.Util.getLocalUser().token);
      var tokenUsername = token.username;
      var tokenRoles = token.roles;
      var requestedUsername = $nextInstruction.params.email;

      var serverUrl = recruitUnitUtil.Constants.APP_PROTOCOL + recruitUnitUtil.Constants.APP_HOST + ":" + recruitUnitUtil.Constants.APP_PORT;

      return recruitUnitUtil.Util.isUserAuthenticated(tokenUsername, recruitUnitUtil.Util.getLocalUser().token).then(angular.bind(this, function (result) {
        if (result == false) {
          recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_HOME);// todo: get path from constant;
          recruitUnitUtil.Util.deleteUserAuth();
        } else if (tokenUsername != requestedUsername) {
          recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_USER + tokenUsername);
        } else if (result.success) {
          //set user details returned from the jwt
          globals.roles = result.data.roles;
          globals.id = result.data.id;
          globals.username = result.data.displayName;
          globals.userGuid = result.data.userGuid;
          globals.userFormUrl = serverUrl + recruitUnitUtil.Constants.PATH_USER + globals.userGuid + "/form";

          if (tokenRoles.indexOf("recruiter") != -1){
            globals.isDeveloper = false;
            // var searchJson = {
            //   "authorEmail": tokenUsername
            // };
            var localToken = recruitUnitUtil.Util.getLocalUser().token;

            var selector = {
							"selector": {
								"model": "RecruitUnitJobDescription",
								"authorEmail": tokenUsername
							}
						};
            //ToDo: these 2 api calls can be consolidated into a single call
						return loomApi.Article.find(selector, localToken).then(angular.bind(this, function (result) {
							  console.log("find results:");
							  console.log(result.docs);
                if (typeof result.docs !== 'undefined' && result.docs.length > 0) {
                      globals.jobItemDocsArray = lodash.sortBy(result.docs, 'createdDate').reverse();

                      //parse recruiter JobDescription results and find matching developer JobItems
                      return result.docs.length; //return canActivate state once results are available
                } else {
                  return true;
                }
            })).then(angular.bind(this, function (result) {
              if (result > 0) {
								return loomApi.Article.getUserTestResults(null, localToken).then(angular.bind(this, function (listMyTestContentResult) {
									console.log("getUserTestResults:");
									if (typeof listMyTestContentResult !== 'undefined') {
										globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
										//globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
										//globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

										return true; //return canActivate state once results are available
									}
								}));
							}
              return result;
            }));

						// No longer require user test results as we'll be returning list from search results.
            // return loomApi.Article.getUserTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
            //   console.log("getUserTestResults:");
            //   console.log(listMyTestContentResult);
            //   if (typeof listMyTestContentResult !== 'undefined') {
            //     globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
            //     globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
            //     globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;
						//
            //     return true; //return canActivate state once results are available
            //   }
            // }));
            return true;
          } else if (tokenRoles.indexOf("developer") != -1){
            globals.isDeveloper = true;

            var searchJson = {
              "submitTo": globals.userGuid
            };

            var localToken = recruitUnitUtil.Util.getLocalUser().token;
            return loomApi.Article.getUserComparisonTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
              console.log("getUserComparisonTestResults:");
              console.log(listMyTestContentResult);
              if (typeof listMyTestContentResult !== 'undefined') {
								globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
                //globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
                //globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

                return listMyTestContentResult.length;
              }
            })).then(angular.bind(this, function (result) {
              console.log(result);
							if (result > 0) {
								return loomApi.Article.getUserComparisonTestDocs(localToken).then(angular.bind(this, function (listMyJobComparisonTestResult) {
									console.log("getUserComparisonTestDocs:");
									console.log(listMyJobComparisonTestResult);
									if (typeof listMyJobComparisonTestResult !== 'undefined') {
										globals.jobItemDocsArray = lodash.sortBy(listMyJobComparisonTestResult, 'document.createdDate').reverse();
										//globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
										//globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

										return true; //return canActivate state once results are available
									}
								}));
							}
							return result;
						}));
          }
        }

      }));
    } else {
      recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_HOME);
    }
  }
}

<commit_msg>fixing error when no comparison test results return<commit_after>import template from './userLanding.html';
import formReadDialogTemplate from '../formRead/formReadDialog.html';
import comparisonFormTemplate from './requireComparisonFormDialog.html';
import enableContactMeTemplate from './enableContactMeDialog.html';

class UserLandingController {
  constructor($location, $mdDialog, $mdPanel, loomApi, lodash, moment, recruitUnitUtil, jwtHelper, globals, recruitUnitConfig) {
    "ngInject";
    console.log("in UserLandingController");
    this.moment = moment;
    this.$mdDialog = $mdDialog;
    this.recruitUnitUtil = recruitUnitUtil;
    this.loomApi = loomApi;

    this.username = globals.username;
    this.userguid = globals.userguid;
    this.roles = globals.roles;
    this.id = globals.id;
    this.myContentListArray = globals.myContentListArray;
		this.jobItemDocsArray = globals.jobItemDocsArray;
    this.myContentListPassCount = globals.myContentListPassCount;
    this.myContentListFailCount = globals.myContentListFailCount;
    this.isDeveloper = globals.isDeveloper;

    this._mdPanel = $mdPanel;
    this.openFrom = 'button';
    this.closeTo = 'button';

    var token = jwtHelper.decodeToken(recruitUnitUtil.Util.getLocalUser().token);
    //console.log(recruitUnitConfig);
    if (typeof token.userGuid != 'undefined') {
      if (recruitUnitConfig.APP_ENV != 'prod') {
				var serverUrl = recruitUnitConfig.APP_PROTOCOL + recruitUnitConfig.APP_HOST + ":" + recruitUnitConfig.APP_PORT;
				this.unitTestFormUrl = serverUrl + recruitUnitConfig.PATH_USER + token.userGuid + "/form";
			} else {
				var serverUrl = recruitUnitConfig.APP_PROTOCOL + recruitUnitConfig.APP_HOST;
				this.unitTestFormUrl = serverUrl + recruitUnitConfig.PATH_USER + token.userGuid + "/form";
      }
    }

    //check if the user has a comparison test form and is a developer. Show alert dialog if not
    //todo: might need to move this to the canActivate?
    if (this.recruitUnitUtil.Util.getLocalUser().token !== null) {
      var decodedToken = jwtHelper.decodeToken(this.recruitUnitUtil.Util.getLocalUser().token);
      if (decodedToken.isComparisonFormEnabled) {
        console.log("User has a comparison form enabled");
      } else if (decodedToken.roles.indexOf(this.recruitUnitUtil.Constants.DEVELOPER_ROLE) != -1) {
        this.showPanel();
      }
    }
  }

  $routerOnActivate(next, previous){
    this.useremail = next.params.email;
    console.log("route param email:" + this.useremail);
  }

  showFormDetailDialog($event, item){
    console.log("in showFormDetailDialog");
    console.log("item:");
    console.log(item);
    var id, isPass, isPartialPass = "";
    if (item.model == "RecruitUnitJobDescription"){
      id = item._id;
      isPass = "";
      isPartialPass = "";
		} else {
			id = item.id;
			isPass = item.isPass;
			isPartialPass = item.isPartialPass;
		}

    this.$mdDialog.show({
      controller: 'formReadController',
      controllerAs: 'formRead',
      locals: {
        'jobDetailFormId': id,
        'isItemPass': isPartialPass || isPass ? true : false,
        'payFrequencyOptions': [
          {id: "Permanent", value: "annual salary"},
          {id: "Contract", value: "daily rate"}
        ]
      },
      bindToController: true,
      template: formReadDialogTemplate,
      parent: angular.element(document.body),
      targetEvent: $event,
      clickOutsideToClose: true,
      fullscreen: false
    });
  }

  deleteItem(docId, index){
    console.log("delete id:" + docId + ",index:" + index);
    //todo: ensure the update can only change the users own document
    var controllerId = "/services/recruitunit/articles/recruitUnitContentService.controller.js";
    var jobItemModel = "/models/RecruitUnit.Job.All.js";
    var localToken = this.recruitUnitUtil.Util.getLocalUser().token;
    this.loomApi.Article.updateArticle(docId, controllerId, jobItemModel, {"published": false}, localToken).then(angular.bind(this, function (result) {
      console.log("Delete result:");
      console.log(result);
      if (result.success) {
        this.myContentListArray.splice(index, 1);
      }
    }));
  }

  formatUnixDateToNow(unixTime){
    return this.moment.unix(unixTime/1000).from();
  }

  viewItem(id){
    //console.log("view id:" + id);
    $location.path("/user/" + this.useremail + "/form/" + id);
  }

  searchUser(searchJson){
    var comparisonRulesDocId = "";
    var localToken = this.recruitUnitUtil.Util.getLocalUser().token;

    this.loomApi.Article.getUserTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
      console.log("getUserTestResults:");
      console.log(listMyTestContentResult);
      if (typeof listMyTestContentResult !== 'undefined') {
        this.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
        this.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
        this.myContentListFailCount = listMyTestContentResult.length - this.myContentListPassCount;

        return true; //return canActivate state once results are available
      }
    }));
  }

  navigateToUserRules(useremail){
    console.log("navigateToUserRules [" + useremail + "]");
    console.log("redirect to path [" + this.recruitUnitUtil.Constants.PATH_USER + useremail + this.recruitUnitUtil.Constants.PATH_COMPARISONRULESFORM + "]");
    this.recruitUnitUtil.Util.redirectUserToPath(this.recruitUnitUtil.Constants.PATH_USER + useremail + this.recruitUnitUtil.Constants.PATH_COMPARISONRULESFORM);
  }

  getDeveloperEmailAddress(docId, isDeveloper, displayDevEmail){
    if (!isDeveloper && displayDevEmail) {
      var localToken = this.recruitUnitUtil.Util.getLocalUser().token;

      return this.loomApi.User.getDevEmailFromDocId(docId, localToken).then(angular.bind(this, function (result) {
        return typeof result.email !== "undefined" ? result.email : result.message;
      }));
    }
  }

  showPanel(){
    console.log("in showPanel");
    var panelPosition = this._mdPanel.newPanelPosition()
      .absolute()
      .center();
    var panelAnimation = this._mdPanel.newPanelAnimation();
    panelAnimation.openFrom({top: 0, left: 0});
    panelAnimation.closeTo({top: document.documentElement.clientHeight, left: 0});
    panelAnimation.withAnimation(this._mdPanel.animation.SCALE);

    var config = {
      attachTo: angular.element(document.body),
      controller: 'genericDialogController',
      controllerAs: 'requireComparisonFormDialog',
      locals: {
        'useremail': this.recruitUnitUtil.Util.getLocalUser().email,
        'navigateToUserRules': this.navigateToUserRules
      },
      position: panelPosition,
      animation: panelAnimation,
      template: comparisonFormTemplate,
      hasBackdrop: true,
      panelClass: 'generic-dialog',
      zIndex: 150,
      clickOutsideToClose: true,
      escapeToClose: true,
      focusOnOpen: true
    }

    this._mdPanel.open(config);
  }

  toggleContactMeDialog($event, id, displayDevEmail, repeatScope){
    console.log("in toggleContactMeDialog");

    var panelPosition = this._mdPanel.newPanelPosition()
      .absolute()
      .center();
    var panelAnimation = this._mdPanel.newPanelAnimation();
    panelAnimation.openFrom({top: 0, left: 0});
    panelAnimation.closeTo({top: document.documentElement.clientHeight, left: 0});
    panelAnimation.withAnimation(this._mdPanel.animation.SCALE);

    var config = {
      attachTo: angular.element(document.body),
      controller: 'genericDialogController',
      controllerAs: 'contactMeDialog',
      locals: {
        'docId': id,
        'isDisplayDevEmail': displayDevEmail,
        'isDeveloper': this.isDeveloper,
        'developerEmailAddress': this.getDeveloperEmailAddress(id, this.isDeveloper, displayDevEmail),
        itemScope: repeatScope
      },
      bindToController: true,
      position: panelPosition,
      animation: panelAnimation,
      template: enableContactMeTemplate,
      hasBackdrop: true,
      panelClass: 'generic-dialog',
      zIndex: 150,
      clickOutsideToClose: true,
      escapeToClose: true,
      focusOnOpen: true
    }

    this._mdPanel.open(config);
  }

}

export default {
  controller: UserLandingController,
  controllerAs: 'userLanding',
  template: template,
  $canActivate: function($nextInstruction, recruitUnitUtil, jwtHelper, loomApi, globals, lodash, recruitUnitConfig){
    "ngInject";
    console.log("in UserLandingController canActivate");
    //console.log(recruitUnitConfig);

    if (recruitUnitUtil.Util.isLocalUserAvailable()) {
      var token = jwtHelper.decodeToken(recruitUnitUtil.Util.getLocalUser().token);
      var tokenUsername = token.username;
      var tokenRoles = token.roles;
      var requestedUsername = $nextInstruction.params.email;

      var serverUrl = recruitUnitUtil.Constants.APP_PROTOCOL + recruitUnitUtil.Constants.APP_HOST + ":" + recruitUnitUtil.Constants.APP_PORT;

      return recruitUnitUtil.Util.isUserAuthenticated(tokenUsername, recruitUnitUtil.Util.getLocalUser().token).then(angular.bind(this, function (result) {
        if (result == false) {
          recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_HOME);// todo: get path from constant;
          recruitUnitUtil.Util.deleteUserAuth();
        } else if (tokenUsername != requestedUsername) {
          recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_USER + tokenUsername);
        } else if (result.success) {
          //set user details returned from the jwt
          globals.roles = result.data.roles;
          globals.id = result.data.id;
          globals.username = result.data.displayName;
          globals.userGuid = result.data.userGuid;
          globals.userFormUrl = serverUrl + recruitUnitUtil.Constants.PATH_USER + globals.userGuid + "/form";

          if (tokenRoles.indexOf("recruiter") != -1){
            globals.isDeveloper = false;
            // var searchJson = {
            //   "authorEmail": tokenUsername
            // };
            var localToken = recruitUnitUtil.Util.getLocalUser().token;

            var selector = {
							"selector": {
								"model": "RecruitUnitJobDescription",
								"authorEmail": tokenUsername
							}
						};
            //ToDo: these 2 api calls can be consolidated into a single call
						return loomApi.Article.find(selector, localToken).then(angular.bind(this, function (result) {
							  console.log("find results:");
							  console.log(result.docs);
                if (typeof result.docs !== 'undefined' && result.docs.length > 0) {
                      globals.jobItemDocsArray = lodash.sortBy(result.docs, 'createdDate').reverse();

                      //parse recruiter JobDescription results and find matching developer JobItems
                      return result.docs.length; //return canActivate state once results are available
                } else {
                  return true;
                }
            })).then(angular.bind(this, function (result) {
              if (result > 0) {
								return loomApi.Article.getUserTestResults(null, localToken).then(angular.bind(this, function (listMyTestContentResult) {
									console.log("getUserTestResults:");
									if (typeof listMyTestContentResult !== 'undefined') {
										globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
										//globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
										//globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

										return true; //return canActivate state once results are available
									}
								}));
							}
              return result;
            }));

						// No longer require user test results as we'll be returning list from search results.
            // return loomApi.Article.getUserTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
            //   console.log("getUserTestResults:");
            //   console.log(listMyTestContentResult);
            //   if (typeof listMyTestContentResult !== 'undefined') {
            //     globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
            //     globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
            //     globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;
						//
            //     return true; //return canActivate state once results are available
            //   }
            // }));
            return true;
          } else if (tokenRoles.indexOf("developer") != -1){
            globals.isDeveloper = true;

            var searchJson = {
              "submitTo": globals.userGuid
            };

            var localToken = recruitUnitUtil.Util.getLocalUser().token;
            return loomApi.Article.getUserComparisonTestResults(searchJson, localToken).then(angular.bind(this, function (listMyTestContentResult) {
              console.log("getUserComparisonTestResults:");
              console.log(listMyTestContentResult);
              if (typeof listMyTestContentResult !== 'undefined') {
								globals.myContentListArray = lodash.sortBy(listMyTestContentResult, 'document.createdDate').reverse();
                //globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
                //globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

                return listMyTestContentResult.length;
              }
            })).then(angular.bind(this, function (result) {
              console.log(result);
							if (result > 0) {
								return loomApi.Article.getUserComparisonTestDocs(localToken).then(angular.bind(this, function (listMyJobComparisonTestResult) {
									console.log("getUserComparisonTestDocs:");
									console.log(listMyJobComparisonTestResult);
									if (typeof listMyJobComparisonTestResult !== 'undefined') {
										globals.jobItemDocsArray = lodash.sortBy(listMyJobComparisonTestResult, 'document.createdDate').reverse();
										//globals.myContentListPassCount = lodash.filter(listMyTestContentResult, {'testResult': {'isPass': true}}).length + lodash.filter(listMyTestContentResult, {'testResult': {'isPartialPass': true}}).length;
										//globals.myContentListFailCount = listMyTestContentResult.length - globals.myContentListPassCount;

										return true; //return canActivate state once results are available
									}
								}));
							} else { //no comparison test results
								return true;
							}
						}));
          }
        }

      }));
    } else {
      recruitUnitUtil.Util.redirectUserToPath(recruitUnitUtil.Constants.PATH_HOME);
    }
  }
}

<|endoftext|>


<commit_before>var Initializer = require("../initializer");

Initializer.add('startup', 'stex.initialize-app', ['stex.autoload', 'stex.express', 'stex.request-domains'], function(stex) {
  stex.appInitializer(stex);
  stex.app.use("/", stex.router);
});
<commit_msg>Properly link request-logging initializer<commit_after>var Initializer = require("../initializer");

Initializer.add('startup', 'stex.initialize-app', ['stex.autoload', 'stex.express', 'stex.request-domains', 'stex.request-logging'], function(stex) {
  stex.appInitializer(stex);
  stex.app.use("/", stex.router);
});
<|endoftext|>


<commit_before>"use strict";

const IBase = require("./IBase");
const IPermissions = require("./IPermissions");
const Utils = require("../core/Utils");
const Role = require("../models/Role");

const Constants = require("../Constants");
const PermissionSpecs = Constants.PermissionSpecs;

const rest = require("../networking/rest");

class IRole extends IBase {
  constructor(discordie, roleId, guildId) {
    super(Role, (key) => {
      const guild = this._discordie._guilds.get(this._guildId);
      if (!guild) return null;
      const role = guild.roles.get(this._roleId);
      if (!role) return null;
      return role[key];
    });
    this._discordie = discordie;
    this._roleId = roleId;
    this._guildId = guildId;
    this._permissions = new IPermissions(
      this.getRaw().permissions,
      PermissionSpecs.Role
    );
    Utils.privatify(this);

    this._setValueOverride("permissions", (v) => this._permissions);

    Object.freeze(this);
  }
  reload() {
    const raw = this.getRaw();
    if (!raw) return;
    this._permissions.raw = raw.permissions;
  }
  commit(name, color, hoist) {
    if (!name || this.id == this._guildId) name = this.name;
    if (hoist === undefined || hoist === null) hoist = this.hoist;
    if (color === undefined || color === null) color = this.color;

    return new Promise((rs, rj) => {
      rest(this._discordie).guilds.roles.patchRole(
        this._guildId, this.id,
        name, this.permissions.raw, color, hoist
      )
      .then(() => rs(this))
      .catch(err => {
        this.reload();
        return rj(err);
      });
    });
  }
  delete() {
    return rest(this._discordie)
      .guilds.roles.deleteRole(this._guildId, this.id);
  }
}

module.exports = IRole;
<commit_msg>Disallow color/hoist edits on @everyone role<commit_after>"use strict";

const IBase = require("./IBase");
const IPermissions = require("./IPermissions");
const Utils = require("../core/Utils");
const Role = require("../models/Role");

const Constants = require("../Constants");
const PermissionSpecs = Constants.PermissionSpecs;

const rest = require("../networking/rest");

class IRole extends IBase {
  constructor(discordie, roleId, guildId) {
    super(Role, (key) => {
      const guild = this._discordie._guilds.get(this._guildId);
      if (!guild) return null;
      const role = guild.roles.get(this._roleId);
      if (!role) return null;
      return role[key];
    });
    this._discordie = discordie;
    this._roleId = roleId;
    this._guildId = guildId;
    this._permissions = new IPermissions(
      this.getRaw().permissions,
      PermissionSpecs.Role
    );
    Utils.privatify(this);

    this._setValueOverride("permissions", (v) => this._permissions);

    Object.freeze(this);
  }
  reload() {
    const raw = this.getRaw();
    if (!raw) return;
    this._permissions.raw = raw.permissions;
  }
  commit(name, color, hoist) {
    const isEveryone = (this.id == this._guildId);
    if (!name || isEveryone) name = this.name;
    if (hoist === undefined || hoist === null || isEveryone) hoist = this.hoist;
    if (color === undefined || color === null || isEveryone) color = this.color;

    return new Promise((rs, rj) => {
      rest(this._discordie).guilds.roles.patchRole(
        this._guildId, this.id,
        name, this.permissions.raw, color, hoist
      )
      .then(() => rs(this))
      .catch(err => {
        this.reload();
        return rj(err);
      });
    });
  }
  delete() {
    return rest(this._discordie)
      .guilds.roles.deleteRole(this._guildId, this.id);
  }
}

module.exports = IRole;
<|endoftext|>