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

define(function (require) {
    'use strict';

    return function (options) {
        function getTitle(options) {
            if(options.dialogTitle && MetkaJS.L10N.containsText(options.dialogTitle, options.type)) {
                return MetkaJS.L10N.localize(options.dialogTitle, options.type);
            } else if(options.dialogTitles
                    && options.dialogTitles[options.containerKey]
                    && MetkaJS.L10N.containsText(options.dialogTitles[options.containerKey], options.type)) {
                return MetkaJS.L10N.localize(options.dialogTitles[options.containerKey], options.type);
            } else if(options.title) {
                return MetkaJS.L10N.get(options.title);
            } else {
                return MetkaJS.L10N.localize(options, 'title');
            }
        }

        function refreshMetka() {
            if(!content) {
                return true;
            }
            if (!options.buttons) {
                options.buttons = [];
            } else {
                if (typeof options.buttons === 'string') {
                    options.buttons = [options.buttons];
                }
            }

            options.buttons.forEach(function(button, i, buttons) {
                if(typeof button === 'string') {
                    buttons[i] = {
                        type: button
                    }
                }
            });

            var $body = $('
');
            if (options.content && options.content.length > 0) {
                require('./container').call($body, options);
            } else if(options.body) {
                $body.append(options.body);
            }

            var $header = $('
')
                .append((options.disableClose ? null : '<button type="button" class="close" data-dismiss="modal">×</button>'))
                .append($('
')
                    .text(getTitle(options)));
            content
                .empty()
                .toggleClass('modal-lg', !!options.large)
                .append($('
')
                    .append($header)
                    .append($body)
                    .append((options.disableFooter ? null : $('
')
                        .append((options.buttons || []).map(function (buttonOptions) {
                            $.extend(true, buttonOptions, {
                                modalTarget: options.modalTarget
                            });
                            return require('./button')(options)(buttonOptions)
                                .if(!buttonOptions.preventDismiss, function () {
                                    // although some bootstrap features are accessible via .data method, this wont work
                                    // this.data('dismiss', 'modal');

                                    // default behaviour dismisses modal
                                    this.attr('data-dismiss', 'modal');
                                    this.addClass('focused');  // Issue #425
                                });
                        })))));

            if (options.translatableCurrentLang) {
                $header.append(require('./languageRadioInputGroup')(options, 'dialog-translation-lang', options.translatableCurrentLang));
            }
            return false;
        }

        var content = null;
        if(!options.$events) {
            options.$events = require('./events')();
        }

        options.modalTarget = require('./autoId')("M");
        options.$events.register('refresh.metka', refreshMetka);
        var $modal = $('
');
        content = $('
');
        options.$events.trigger('refresh.metka');
        $modal.append(content);
        // Issue #425
        var focusedButton = $modal.find('.focused');
        setTimeout(function() {
            $(focusedButton).focus();
        }, 500);
        /*$modal.on('show.bs.modal', function() {
            options.$events.trigger('refresh.metka');
        });*/
        var modalSettings = {
            backdrop: 'static',
            keyboard: options.keyboard
        };
        $modal.modal(modalSettings).on('hidden.bs.modal', function () {
            $(this).remove();

            // workaround to keep scrolling enabled for nested modals

            var $body = $('body');
            var $otherModal = $body.children('.modal');

            // if there are other modals on screen
            if ($otherModal.length) {
                // get instance of Bootstrap Modal object
                var bsModal = $otherModal.data('bs.modal');

                // do same things that Bootstrap does internally, when dialog is opened
                $body.addClass('modal-open');
                bsModal.setScrollbar();
            }
        });

        return $modal;
    };
});
<commit_msg>Issue #452<commit_after>/**************************************************************************************
 * Copyright (c) 2013-2015, Finnish Social Science Data Archive/University of Tampere *
 *                                                                                *
 * All rights reserved.                                                               *
 *                                                                                *
 * Redistribution and use in source and binary forms, with or without modification,   *
 * are permitted provided that the following conditions are met:                      *
 * 1. Redistributions of source code must retain the above copyright notice, this     *
 *    list of conditions and the following disclaimer.                                *
 * 2. 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.                          *
 * 3. Neither the name of the copyright holder nor the names of its contributors      *
 *    may be used to endorse or promote products derived from this software           *
 *    without specific prior written permission.                                      *
 *                                                                                *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND    *
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED      *
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE             *
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR   *
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES     *
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;       *
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON     *
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            *
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS      *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                       *
 **************************************************************************************/

define(function (require) {
    'use strict';

    return function (options) {
        function getTitle(options) {
            if(options.dialogTitle && MetkaJS.L10N.containsText(options.dialogTitle, options.type)) {
                return MetkaJS.L10N.localize(options.dialogTitle, options.type);
            } else if(options.dialogTitles
                    && options.dialogTitles[options.containerKey]
                    && MetkaJS.L10N.containsText(options.dialogTitles[options.containerKey], options.type)) {
                return MetkaJS.L10N.localize(options.dialogTitles[options.containerKey], options.type);
            } else if(options.title) {
                return MetkaJS.L10N.get(options.title);
            } else {
                return MetkaJS.L10N.localize(options, 'title');
            }
        }

        function refreshMetka() {
            if(!content) {
                return true;
            }
            if (!options.buttons) {
                options.buttons = [];
            } else {
                if (typeof options.buttons === 'string') {
                    options.buttons = [options.buttons];
                }
            }

            options.buttons.forEach(function(button, i, buttons) {
                if(typeof button === 'string') {
                    buttons[i] = {
                        type: button
                    }
                }
            });

            var $body = $('
');
            if (options.content && options.content.length > 0) {
                require('./container').call($body, options);
            } else if(options.body) {
                $body.append(options.body);
            }

            var $header = $('
')
                .append((options.disableClose ? null : '<button type="button" class="close" data-dismiss="modal">×</button>'))
                .append($('
')
                    .text(getTitle(options)));

            // Issue #452
            var txtBox = $body.find('.form-control:first');
            setTimeout(function() {
                $(txtBox).focus();
            }, 600);

            content
                .empty()
                .toggleClass('modal-lg', !!options.large)
                .append($('
')
                    .append($header)
                    .append($body)
                    .append((options.disableFooter ? null : $('
')
                        .append((options.buttons || []).map(function (buttonOptions) {
                            $.extend(true, buttonOptions, {
                                modalTarget: options.modalTarget
                            });
                            return require('./button')(options)(buttonOptions)
                                .if(!buttonOptions.preventDismiss, function () {
                                    // although some bootstrap features are accessible via .data method, this wont work
                                    // this.data('dismiss', 'modal');

                                    // default behaviour dismisses modal
                                    this.attr('data-dismiss', 'modal');
                                    this.addClass('focused');  // Issue #425
                                });
                        })))));

            if (options.translatableCurrentLang) {
                $header.append(require('./languageRadioInputGroup')(options, 'dialog-translation-lang', options.translatableCurrentLang));
            }
            return false;
        }

        var content = null;
        if(!options.$events) {
            options.$events = require('./events')();
        }

        options.modalTarget = require('./autoId')("M");
        options.$events.register('refresh.metka', refreshMetka);
        var $modal = $('
');
        content = $('
');
        options.$events.trigger('refresh.metka');
        $modal.append(content);
        // Issue #425
        var focusedButton = $modal.find('.focused');
        setTimeout(function() {
            $(focusedButton).focus();
        }, 500);
        /*$modal.on('show.bs.modal', function() {
            options.$events.trigger('refresh.metka');
        });*/
        var modalSettings = {
            backdrop: 'static',
            keyboard: options.keyboard
        };
        $modal.modal(modalSettings).on('hidden.bs.modal', function () {
            $(this).remove();

            // workaround to keep scrolling enabled for nested modals

            var $body = $('body');
            var $otherModal = $body.children('.modal');

            // if there are other modals on screen
            if ($otherModal.length) {
                // get instance of Bootstrap Modal object
                var bsModal = $otherModal.data('bs.modal');

                // do same things that Bootstrap does internally, when dialog is opened
                $body.addClass('modal-open');
                bsModal.setScrollbar();
            }
        });

        return $modal;
    };
});
<|endoftext|>


<commit_before>/*eslint no-process-exit:0 */

import * as ConfigController from './controllers/ConfigController';
import * as DisclosureController from './controllers/DisclosureController';
import express from 'express';
import bodyParser from 'body-parser';

export function run() {
  let app = express();
  app.disable('x-powered-by');
  try {
    let extensions = require('research-extensions');
    extensions.express(app);
  } catch (e) {
    // not found, move on
  }
  app.use(express.static('client'));
  app.use(bodyParser.json());
  ConfigController.init(app);
  DisclosureController.init(app);

  let server = app.listen(8090);
  console.log('Listening on port 8090');

  process.on('uncaughtException', function(err) {
    console.error('Uncaught exception: ' + err);
    console.log('waiting for pending connections to clear');
    server.close(() => {
      console.log('shutting down');
      process.exit(1);
    });
  });
}

<commit_msg>Getting port number from config if available<commit_after>import * as ConfigController from './controllers/ConfigController';
import * as DisclosureController from './controllers/DisclosureController';
import express from 'express';
import bodyParser from 'body-parser';

export function run() {
  let app = express();
  app.disable('x-powered-by');
  let config;
  try {
    let extensions = require('research-extensions');
    extensions.express(app);
    config = extensions.config;
  } catch (e) {
    // not found, move on
  }
  app.use(express.static('client'));
  app.use(bodyParser.json());
  ConfigController.init(app);
  DisclosureController.init(app);

  let portNumber = config ? config.port : 8090;
  let server = app.listen(portNumber);
  console.log('Listening on port ' + portNumber);

  process.on('uncaughtException', function(err) {
    console.error('Uncaught exception: ' + err);
    console.log('waiting for pending connections to clear');
    server.close(() => {
      console.log('shutting down');
      process.exit(1); //eslint-disable-line no-process-exit
    });
  });
}

<|endoftext|>


<commit_before>// A very simple key-value store. Keys and values can be any string.
//
// Override default port 3000 with PORT=1234 environment variable
//
// Set value with HTTP POST:
// curl -v -H "Content-Type: application/json" -X POST -d  '{"id":"testpi", "value":"200"}' http://myhost:3000/setValue
//
// Get Value with HTTP GET (or directly from browser):
// curl http://myhost:3000/getValue?id=testpi
//

var express = require('express');
var bodyParser = require('body-parser');
var port = process.env.PORT || 3000;
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

var array = [];
// Persist storage
var storage = require('node-persist');
// flag for logging storage actions for debug
var logging = true;
// storage folder
var storageBaseDir = __dirname +'/storage';
//you must first call storage.init or storage.initSync
var metrics = storage.create({logging: true, dir: storageBaseDir + '/metrics'});
metrics.initSync();
var monitor = storage.create({logging: true, dir: storageBaseDir + '/monitor'});
monitor.initSync();

// set value in metrics storage
function setValue(req, res){
	var id = req.body.id;
    var value = req.body.value;
    if (id && value) {
      //array[id] = value;
      metrics.setItem(id, value);
      res.send(id + ': ' + value);
    } else {
      res.status(400).send('must post "id" and "value"');
    }
};

// get a value from metrics storage
function getValue(req, res) {
    if(req.param("id")) {
        res.send({"id" : req.param("id"),
                "value" : metrics.getItem(req.param("id"))});
    }
}

// save in monitor storage last time a request was received by a client
function setMonitor(req, res, next) {
	id = req.headers['x-client-id'];
	if(id){
		value = {hostname: req.hostname, url : req.originalUrl, body : req.body, headers : req.hearders, tStamp: new Date() };
		monitor.setItem(id, value);
	}
	next();
}

// retrieve all monitor data from monitor storage
function getMonitor(req, res){
	result = {};
	monitor.forEach(function(key,value){
		result[key] = value;
	});
	res.status(200).send(result);
}

app.post('/setValue', setValue);

app.get('/getValue', setMonitor, getValue);

app.get('/getMonitor', getMonitor );

app.use(express.static('static'));

var server = app.listen(port, function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Metrics in your face listening at http://%s:%s', host, port);
});
<commit_msg>using hostname as identifier and saving the metric ID along UTC last time<commit_after>// A very simple key-value store. Keys and values can be any string.
//
// Override default port 3000 with PORT=1234 environment variable
//
// Set value with HTTP POST:
// curl -v -H "Content-Type: application/json" -X POST -d  '{"id":"testpi", "value":"200"}' http://myhost:3000/setValue
//
// Get Value with HTTP GET (or directly from browser):
// curl http://myhost:3000/getValue?id=testpi
//

var express = require('express');
var bodyParser = require('body-parser');
var port = process.env.PORT || 3000;
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

var array = [];
// Persist storage
var storage = require('node-persist');
// flag for logging storage actions for debug
var logging = true;
// storage folder
var storageBaseDir = __dirname +'/storage';
//you must first call storage.init or storage.initSync
var metrics = storage.create({logging: true, dir: storageBaseDir + '/metrics'});
metrics.initSync();
var monitor = storage.create({logging: true, dir: storageBaseDir + '/monitor'});
monitor.initSync();

// set value in metrics storage
function setValue(req, res){
	var id = req.body.id;
    var value = req.body.value;
    if (id && value) {
      //array[id] = value;
      metrics.setItem(id, value);
      res.send(id + ': ' + value);
    } else {
      res.status(400).send('must post "id" and "value"');
    }
};

// get a value from metrics storage
function getValue(req, res) {
    if(req.param("id")) {
        res.send({"id" : req.param("id"),
                "value" : metrics.getItem(req.param("id"))});
    }
}

// save in monitor storage last time (in UTC) a request was received by a client and the metric
// using a JSON like {"metric":"1","tStamp":"2016-01-02T19:30:20.234Z"}
function setMonitor(req, res, next) {
	hostname = req.headers['hostname'];
	id = req.param("id");
	if(id && hostname){
		monitor.setItem(hostname, {metric : id , tStamp: new Date() } );
	}
	next();
}

// retrieve all monitor data from monitor storage
function getMonitor(req, res){
	result = {};
	monitor.forEach(function(key,value){
		result[key] = value;
	});
	res.status(200).send(result);
}

app.post('/setValue', setValue);

app.get('/getValue', setMonitor, getValue);

app.get('/getMonitor', getMonitor );

app.use(express.static('static'));

var server = app.listen(port, function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Metrics in your face listening at http://%s:%s', host, port);
});
<|endoftext|>


<commit_before>import { MobileJsonWireProtocol, errors } from '../mjsonwp';
import os from 'os';
import commands from './commands';
import helpers from './helpers';
import log from './logger';
import { desiredCapabilityConstraints } from './desired-caps';
import { validateCaps } from './capabilities';
import B from 'bluebird';
import _ from 'lodash';
import { util } from 'appium-support';

const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;

const EVENT_SESSION_INIT = 'newSessionRequested';
const EVENT_SESSION_START = 'newSessionStarted';
const EVENT_SESSION_QUIT_START = 'quitSessionRequested';
const EVENT_SESSION_QUIT_DONE = 'quitSessionFinished';

class BaseDriver extends MobileJsonWireProtocol {

  constructor (opts = {}, shouldValidateCaps = true) {
    super();

    // setup state
    this.sessionId = null;
    this.opts = opts;
    this.caps = null;
    this.helpers = helpers;

    // timeout initialization
    this.newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
    this.implicitWaitMs = 0;

    this._constraints = _.cloneDeep(desiredCapabilityConstraints);
    this.locatorStrategies = [];
    this.webLocatorStrategies = [];

    // use a custom tmp dir to avoid losing data and app when computer is
    // restarted
    this.opts.tmpDir = this.opts.tmpDir ||
                       process.env.APPIUM_TMP_DIR ||
                       os.tmpdir();

    // base-driver internals
    this.curCommand = new B((r) => { r(); }); // see note in execute
    this.curCommandCancellable = new B((r) => { r(); }); // see note in execute
    this.shutdownUnexpectedly = false;
    this.noCommandTimer = null;
    this.shouldValidateCaps = shouldValidateCaps;
    // settings should be instantiated by implementing drivers
    this.settings = null;
    this.resetOnUnexpectedShutdown();

    // keeping track of initial opts
    this.initialOpts = _.cloneDeep(this.opts);

    // allow subclasses to have internal drivers
    this.managedDrivers = [];

    // store event timings
    this._eventHistory = {
      commands: [] // commands get a special place
    };
  }

  /*
   * make eventHistory a property and return a cloned object so a consumer can't
   * inadvertently change data outside of logEvent
   */
  get eventHistory () {
    return _.cloneDeep(this._eventHistory);
  }

  /*
   * API method for driver developers to log timings for important events
   */
  logEvent (eventName) {
    if (eventName === "commands") {
      throw new Error("Cannot log commands directly");
    }
    if (typeof eventName !== "string") {
      throw new Error(`Invalid eventName ${eventName}`);
    }
    if (!this._eventHistory[eventName]) {
      this._eventHistory[eventName] = [];
    }
    let ts = Date.now();
    let logTime = (new Date(ts)).toTimeString();
    this._eventHistory[eventName].push(ts);
    log.debug(`Event '${eventName}' logged at ${ts} (${logTime})`);
  }

  /*
   * Overridden in appium driver, but here so that individual drivers can be
   * tested with clients that poll
   */
  async getStatus () {
    return {};
  }

  /*
   * Initialize a new onUnexpectedShutdown promise, cancelling existing one.
   */
  resetOnUnexpectedShutdown () {
    if (this.onUnexpectedShutdown && !this.onUnexpectedShutdown.isFulfilled()) {
      this.onUnexpectedShutdown.cancel();
    }
    this.onUnexpectedShutdown = new B((resolve, reject) => {
      this.unexpectedShutdownDeferred  = {resolve, reject};
    }).cancellable();
    // noop handler to avoid warning.
    this.onUnexpectedShutdown.catch(() => {});
  }

  // we only want subclasses to ever extend the contraints
  set desiredCapConstraints (constraints) {
    this._constraints = Object.assign(this._constraints, constraints);
  }

  get desiredCapConstraints () {
    return this._constraints;
  }

  // method required by MJSONWP in order to determine whether it should
  // respond with an invalid session response
  sessionExists (sessionId) {
    if (!sessionId) return false;
    return sessionId === this.sessionId;
  }

  // method required by MJSONWP in order to determine if the command should
  // be proxied directly to the driver
  driverForSession (/*sessionId*/) {
    return this;
  }

  logExtraCaps (caps) {
    let extraCaps = _.difference(_.keys(caps),
                                 _.keys(this._constraints));
    if (extraCaps.length) {
      log.warn(`The following capabilities were provided, but are not ` +
               `recognized by appium: ${extraCaps.join(', ')}.`);
    }
  }

  validateDesiredCaps (caps) {
    if (!this.shouldValidateCaps) {
      return true;
    }

    try {
      validateCaps(caps, this._constraints);
    } catch (e) {
      log.errorAndThrow(new errors.SessionNotCreatedError(`The desiredCapabilities object was not valid for the ` +
                    `following reason(s): ${e.message}`));
    }

    this.logExtraCaps(caps);

    return true;
  }

  // This is the main command handler for the driver. It wraps command
  // execution with timeout logic, checking that we have a valid session,
  // and ensuring that we execute commands one at a time. This method is called
  // by MJSONWP's express router.
  async executeCommand (cmd, ...args) {
    // get start time for this command, and log in special cases
    let startTime = Date.now();
    if (cmd === 'createSession') {
      this.logEvent(EVENT_SESSION_INIT);
    } else if (cmd === 'deleteSession') {
      this.logEvent(EVENT_SESSION_QUIT_START);
    }

    // if we had a command timer running, clear it now that we're starting
    // a new command and so don't want to time out
    this.clearNewCommandTimeout();

    // if we don't have this command, it must not be implemented
    if (!this[cmd]) {
      throw new errors.NotYetImplementedError();
    }

    // What we're doing here is pretty clever. this.curCommand is always
    // a promise representing the command currently being executed by the
    // driver, or the last command executed by the driver (it starts off as
    // essentially a pre-resolved promise). When a command comes in, we tack it
    // to the end of this.curCommand, essentially saying we want to execute it
    // whenever this.curCommand is done. We call this new promise nextCommand,
    // and its resolution is what we ultimately will return to whomever called
    // us. Meanwhile, we reset this.curCommand to _be_ nextCommand (but
    // ignoring any rejections), so that if another command comes into the
    // server, it gets tacked on to the end of nextCommand. Thus we create
    // a chain of promises that acts as a queue with single concurrency.
    let nextCommand = this.curCommand.then(() => {
      // if we unexpectedly shut down, we need to reject every command in
      // the queue before we actually try to run it
      if (this.shutdownUnexpectedly) {
        return B.reject(new errors.NoSuchDriverError('The driver was unexpectedly shut down!'));
      }
      // We also need to turn the command into a cancellable promise so if we
      // have an unexpected shutdown event, for example, we can cancel it from
      // outside, rejecting the current command immediately
      this.curCommandCancellable = new B((r) => { r(); }).then(() => {
        return this[cmd](...args);
      }).cancellable();
      return this.curCommandCancellable;
    });
    this.curCommand = nextCommand.catch(() => {});
    let res = await nextCommand;

    // if we have set a new command timeout (which is the default), start a
    // timer once we've finished executing this command. If we don't clear
    // the timer (which is done when a new command comes in), we will trigger
    // automatic session deletion in this.onCommandTimeout. Of course we don't
    // want to trigger the timer when the user is shutting down the session
    // intentionally
    if (cmd !== 'deleteSession') {
      // reseting existing timeout
      this.startNewCommandTimeout();
    }

    // log timing information about this command
    let endTime = Date.now();
    this._eventHistory.commands.push({cmd, startTime, endTime});
    if (cmd === 'createSession') {
      this.logEvent(EVENT_SESSION_START);
    } else if (cmd === 'deleteSession') {
      this.logEvent(EVENT_SESSION_QUIT_DONE);
    }

    return res;
  }

  async startUnexpectedShutdown (err = new errors.NoSuchDriverError('The driver was unexpectedly shut down!')) {
    this.unexpectedShutdownDeferred.reject(err); // allow others to listen for this
    this.curCommandCancellable.cancel(err);
    this.shutdownUnexpectedly = true;
    await this.deleteSession(this.sessionId);
    this.shutdownUnexpectedly = false;
  }

  validateLocatorStrategy (strategy, webContext = false) {
    let validStrategies = this.locatorStrategies;
    log.debug(`Valid locator strategies for this request: ${validStrategies.join(', ')}`);

    if (webContext) {
      validStrategies = validStrategies.concat(this.webLocatorStrategies);
    }

    if (!_.includes(validStrategies, strategy)) {
      throw new errors.InvalidSelectorError(`Locator Strategy '${strategy}' is not supported for this session`);
    }
  }

  /*
   * Restart the session with the original caps,
   * preserving the timeout config.
   */
  async reset () {
    log.debug('Resetting app mid-session');
    log.debug('Running generic full reset');

    // preserving state
    let currentConfig = {};
    for (let property of ['implicitWaitMs', 'newCommandTimeoutMs', 'sessionId', 'resetOnUnexpectedShutdown']) {
      currentConfig[property] = this[property];
    }

    // We also need to preserve the unexpected shutdown, and make sure it is not cancelled during reset.
    this.resetOnUnexpectedShutdown = () => {};

    try {
      await this.deleteSession(this.sessionId);
      log.debug('Restarting app');
      await this.createSession(this.caps);
    } finally {
      // always restore state.
      for (let [key, value] of _.toPairs(currentConfig)) {
        this[key] = value;
      }
    }
    this.clearNewCommandTimeout();
  }

  async getSwipeOptions (gestures, touchCount = 1) {
    let startX =  this.helpers.getCoordDefault(gestures[0].options.x),
        startY = this.helpers.getCoordDefault(gestures[0].options.y),
        endX = this.helpers.getCoordDefault(gestures[2].options.x),
        endY = this.helpers.getCoordDefault(gestures[2].options.y),
        duration = this.helpers.getSwipeTouchDuration(gestures[1]),
        element = gestures[0].options.element,
        destElement = gestures[2].options.element || gestures[0].options.element;

    // there's no destination element handling in bootstrap and since it applies to all platforms, we handle it here
    if (util.hasValue(destElement)) {
      let locResult = await this.getLocationInView(destElement);
      let sizeResult = await this.getSize(destElement);
      let offsetX = (Math.abs(endX) < 1 && Math.abs(endX) > 0) ? sizeResult.width * endX : endX;
      let offsetY = (Math.abs(endY) < 1 && Math.abs(endY) > 0) ? sizeResult.height * endY : endY;
      endX = locResult.x + offsetX;
      endY = locResult.y + offsetY;
      // if the target element was provided, the coordinates for the destination need to be relative to it.
      if (util.hasValue(element)) {
        let firstElLocation = await this.getLocationInView(element);
        endX -= firstElLocation.x;
        endY -= firstElLocation.y;
      }
    }
    // clients are responsible to use these options correctly
    return {startX, startY, endX, endY, duration, touchCount, element};
  }

  proxyActive (/* sessionId */) {
    return false;
  }

  getProxyAvoidList (/* sessionId */) {
    return [];
  }

  canProxy (/* sessionId */) {
    return false;
  }

  addManagedDriver (driver) {
    this.managedDrivers.push(driver);
  }

  getManagedDrivers () {
    return this.managedDrivers;
  }
}

for (let [cmd, fn] of _.toPairs(commands)) {
  BaseDriver.prototype[cmd] = fn;
}

export default BaseDriver;
<commit_msg>exchange deleteSession and curCommandCancellable (#126)<commit_after>import { MobileJsonWireProtocol, errors } from '../mjsonwp';
import os from 'os';
import commands from './commands';
import helpers from './helpers';
import log from './logger';
import { desiredCapabilityConstraints } from './desired-caps';
import { validateCaps } from './capabilities';
import B from 'bluebird';
import _ from 'lodash';
import { util } from 'appium-support';

const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;

const EVENT_SESSION_INIT = 'newSessionRequested';
const EVENT_SESSION_START = 'newSessionStarted';
const EVENT_SESSION_QUIT_START = 'quitSessionRequested';
const EVENT_SESSION_QUIT_DONE = 'quitSessionFinished';

class BaseDriver extends MobileJsonWireProtocol {

  constructor (opts = {}, shouldValidateCaps = true) {
    super();

    // setup state
    this.sessionId = null;
    this.opts = opts;
    this.caps = null;
    this.helpers = helpers;

    // timeout initialization
    this.newCommandTimeoutMs = NEW_COMMAND_TIMEOUT_MS;
    this.implicitWaitMs = 0;

    this._constraints = _.cloneDeep(desiredCapabilityConstraints);
    this.locatorStrategies = [];
    this.webLocatorStrategies = [];

    // use a custom tmp dir to avoid losing data and app when computer is
    // restarted
    this.opts.tmpDir = this.opts.tmpDir ||
                       process.env.APPIUM_TMP_DIR ||
                       os.tmpdir();

    // base-driver internals
    this.curCommand = new B((r) => { r(); }); // see note in execute
    this.curCommandCancellable = new B((r) => { r(); }); // see note in execute
    this.shutdownUnexpectedly = false;
    this.noCommandTimer = null;
    this.shouldValidateCaps = shouldValidateCaps;
    // settings should be instantiated by implementing drivers
    this.settings = null;
    this.resetOnUnexpectedShutdown();

    // keeping track of initial opts
    this.initialOpts = _.cloneDeep(this.opts);

    // allow subclasses to have internal drivers
    this.managedDrivers = [];

    // store event timings
    this._eventHistory = {
      commands: [] // commands get a special place
    };
  }

  /*
   * make eventHistory a property and return a cloned object so a consumer can't
   * inadvertently change data outside of logEvent
   */
  get eventHistory () {
    return _.cloneDeep(this._eventHistory);
  }

  /*
   * API method for driver developers to log timings for important events
   */
  logEvent (eventName) {
    if (eventName === "commands") {
      throw new Error("Cannot log commands directly");
    }
    if (typeof eventName !== "string") {
      throw new Error(`Invalid eventName ${eventName}`);
    }
    if (!this._eventHistory[eventName]) {
      this._eventHistory[eventName] = [];
    }
    let ts = Date.now();
    let logTime = (new Date(ts)).toTimeString();
    this._eventHistory[eventName].push(ts);
    log.debug(`Event '${eventName}' logged at ${ts} (${logTime})`);
  }

  /*
   * Overridden in appium driver, but here so that individual drivers can be
   * tested with clients that poll
   */
  async getStatus () {
    return {};
  }

  /*
   * Initialize a new onUnexpectedShutdown promise, cancelling existing one.
   */
  resetOnUnexpectedShutdown () {
    if (this.onUnexpectedShutdown && !this.onUnexpectedShutdown.isFulfilled()) {
      this.onUnexpectedShutdown.cancel();
    }
    this.onUnexpectedShutdown = new B((resolve, reject) => {
      this.unexpectedShutdownDeferred  = {resolve, reject};
    }).cancellable();
    // noop handler to avoid warning.
    this.onUnexpectedShutdown.catch(() => {});
  }

  // we only want subclasses to ever extend the contraints
  set desiredCapConstraints (constraints) {
    this._constraints = Object.assign(this._constraints, constraints);
  }

  get desiredCapConstraints () {
    return this._constraints;
  }

  // method required by MJSONWP in order to determine whether it should
  // respond with an invalid session response
  sessionExists (sessionId) {
    if (!sessionId) return false;
    return sessionId === this.sessionId;
  }

  // method required by MJSONWP in order to determine if the command should
  // be proxied directly to the driver
  driverForSession (/*sessionId*/) {
    return this;
  }

  logExtraCaps (caps) {
    let extraCaps = _.difference(_.keys(caps),
                                 _.keys(this._constraints));
    if (extraCaps.length) {
      log.warn(`The following capabilities were provided, but are not ` +
               `recognized by appium: ${extraCaps.join(', ')}.`);
    }
  }

  validateDesiredCaps (caps) {
    if (!this.shouldValidateCaps) {
      return true;
    }

    try {
      validateCaps(caps, this._constraints);
    } catch (e) {
      log.errorAndThrow(new errors.SessionNotCreatedError(`The desiredCapabilities object was not valid for the ` +
                    `following reason(s): ${e.message}`));
    }

    this.logExtraCaps(caps);

    return true;
  }

  // This is the main command handler for the driver. It wraps command
  // execution with timeout logic, checking that we have a valid session,
  // and ensuring that we execute commands one at a time. This method is called
  // by MJSONWP's express router.
  async executeCommand (cmd, ...args) {
    // get start time for this command, and log in special cases
    let startTime = Date.now();
    if (cmd === 'createSession') {
      this.logEvent(EVENT_SESSION_INIT);
    } else if (cmd === 'deleteSession') {
      this.logEvent(EVENT_SESSION_QUIT_START);
    }

    // if we had a command timer running, clear it now that we're starting
    // a new command and so don't want to time out
    this.clearNewCommandTimeout();

    // if we don't have this command, it must not be implemented
    if (!this[cmd]) {
      throw new errors.NotYetImplementedError();
    }

    // What we're doing here is pretty clever. this.curCommand is always
    // a promise representing the command currently being executed by the
    // driver, or the last command executed by the driver (it starts off as
    // essentially a pre-resolved promise). When a command comes in, we tack it
    // to the end of this.curCommand, essentially saying we want to execute it
    // whenever this.curCommand is done. We call this new promise nextCommand,
    // and its resolution is what we ultimately will return to whomever called
    // us. Meanwhile, we reset this.curCommand to _be_ nextCommand (but
    // ignoring any rejections), so that if another command comes into the
    // server, it gets tacked on to the end of nextCommand. Thus we create
    // a chain of promises that acts as a queue with single concurrency.
    let nextCommand = this.curCommand.then(() => {
      // if we unexpectedly shut down, we need to reject every command in
      // the queue before we actually try to run it
      if (this.shutdownUnexpectedly) {
        return B.reject(new errors.NoSuchDriverError('The driver was unexpectedly shut down!'));
      }
      // We also need to turn the command into a cancellable promise so if we
      // have an unexpected shutdown event, for example, we can cancel it from
      // outside, rejecting the current command immediately
      this.curCommandCancellable = new B((r) => { r(); }).then(() => {
        return this[cmd](...args);
      }).cancellable();
      return this.curCommandCancellable;
    });
    this.curCommand = nextCommand.catch(() => {});
    let res = await nextCommand;

    // if we have set a new command timeout (which is the default), start a
    // timer once we've finished executing this command. If we don't clear
    // the timer (which is done when a new command comes in), we will trigger
    // automatic session deletion in this.onCommandTimeout. Of course we don't
    // want to trigger the timer when the user is shutting down the session
    // intentionally
    if (cmd !== 'deleteSession') {
      // reseting existing timeout
      this.startNewCommandTimeout();
    }

    // log timing information about this command
    let endTime = Date.now();
    this._eventHistory.commands.push({cmd, startTime, endTime});
    if (cmd === 'createSession') {
      this.logEvent(EVENT_SESSION_START);
    } else if (cmd === 'deleteSession') {
      this.logEvent(EVENT_SESSION_QUIT_DONE);
    }

    return res;
  }

  async startUnexpectedShutdown (err = new errors.NoSuchDriverError('The driver was unexpectedly shut down!')) {
    this.unexpectedShutdownDeferred.reject(err); // allow others to listen for this
    this.shutdownUnexpectedly = true;
    await this.deleteSession(this.sessionId);
    this.shutdownUnexpectedly = false;
    this.curCommandCancellable.cancel(err);
  }

  validateLocatorStrategy (strategy, webContext = false) {
    let validStrategies = this.locatorStrategies;
    log.debug(`Valid locator strategies for this request: ${validStrategies.join(', ')}`);

    if (webContext) {
      validStrategies = validStrategies.concat(this.webLocatorStrategies);
    }

    if (!_.includes(validStrategies, strategy)) {
      throw new errors.InvalidSelectorError(`Locator Strategy '${strategy}' is not supported for this session`);
    }
  }

  /*
   * Restart the session with the original caps,
   * preserving the timeout config.
   */
  async reset () {
    log.debug('Resetting app mid-session');
    log.debug('Running generic full reset');

    // preserving state
    let currentConfig = {};
    for (let property of ['implicitWaitMs', 'newCommandTimeoutMs', 'sessionId', 'resetOnUnexpectedShutdown']) {
      currentConfig[property] = this[property];
    }

    // We also need to preserve the unexpected shutdown, and make sure it is not cancelled during reset.
    this.resetOnUnexpectedShutdown = () => {};

    try {
      await this.deleteSession(this.sessionId);
      log.debug('Restarting app');
      await this.createSession(this.caps);
    } finally {
      // always restore state.
      for (let [key, value] of _.toPairs(currentConfig)) {
        this[key] = value;
      }
    }
    this.clearNewCommandTimeout();
  }

  async getSwipeOptions (gestures, touchCount = 1) {
    let startX =  this.helpers.getCoordDefault(gestures[0].options.x),
        startY = this.helpers.getCoordDefault(gestures[0].options.y),
        endX = this.helpers.getCoordDefault(gestures[2].options.x),
        endY = this.helpers.getCoordDefault(gestures[2].options.y),
        duration = this.helpers.getSwipeTouchDuration(gestures[1]),
        element = gestures[0].options.element,
        destElement = gestures[2].options.element || gestures[0].options.element;

    // there's no destination element handling in bootstrap and since it applies to all platforms, we handle it here
    if (util.hasValue(destElement)) {
      let locResult = await this.getLocationInView(destElement);
      let sizeResult = await this.getSize(destElement);
      let offsetX = (Math.abs(endX) < 1 && Math.abs(endX) > 0) ? sizeResult.width * endX : endX;
      let offsetY = (Math.abs(endY) < 1 && Math.abs(endY) > 0) ? sizeResult.height * endY : endY;
      endX = locResult.x + offsetX;
      endY = locResult.y + offsetY;
      // if the target element was provided, the coordinates for the destination need to be relative to it.
      if (util.hasValue(element)) {
        let firstElLocation = await this.getLocationInView(element);
        endX -= firstElLocation.x;
        endY -= firstElLocation.y;
      }
    }
    // clients are responsible to use these options correctly
    return {startX, startY, endX, endY, duration, touchCount, element};
  }

  proxyActive (/* sessionId */) {
    return false;
  }

  getProxyAvoidList (/* sessionId */) {
    return [];
  }

  canProxy (/* sessionId */) {
    return false;
  }

  addManagedDriver (driver) {
    this.managedDrivers.push(driver);
  }

  getManagedDrivers () {
    return this.managedDrivers;
  }
}

for (let [cmd, fn] of _.toPairs(commands)) {
  BaseDriver.prototype[cmd] = fn;
}

export default BaseDriver;
<|endoftext|>


<commit_before>import React from 'react';
import Typography from '@material-ui/core/Typography';

export default class extends React.Component {
    getDescriptionJson = () => {
        let div = document.createElement('div');
        div.innerHTML = this.props.appContext.state.child.desc_en;
        let table = div.querySelector('table');

        function s(selector) {
            let elements = table.querySelectorAll(selector);
            switch(elements.length) {
                case 0:
                    return false;
                case 1:
                    return elements[0].innerHTML;
                default:
                    let result = [];
                    for (let i = 0; i < elements.length; i++) {
                        result.push(elements[i].innerHTML);
                    }
                    return result;
            }
        }

        function labelValue(parentClass, childClass) {
            let result = {
                label: s(parentClass + ' .desc_label' + childClass),
                value: s(parentClass + ' .desc_value' + childClass),
            };
            return (result.label === false && result.value === false) ? false:result;
        }

        function labelValues(parentClass) {
            let result = {
                label: s(parentClass + ' ' + parentClass.replace('.', '#') + '_intro'),
                value: s(parentClass + ' ' + parentClass.replace('.', '#') + '_list li'),
            };
            return (result.label === false && result.value === false) ? false:result;
        }

        return {
            program_type: s('.program_type'),
            birthday_estimate: s('.birthday_estimate'),
            live_with: s('#live_with'),
            family: {
                father_alive: labelValue('.father', '.is_alive'),
                father_job: labelValue('.father_job', '.job'),
                mother_alive: labelValue('.mother', '.is_alive'),
                mother_job: labelValue('.mother_job', '.job'),
                brothers: labelValue('', '.brothers'),
                sisters: labelValue('', '.sisters'),
            },
            school: {
                school_attending: s('#school_attending'),
                school_performance: labelValue('', '.school_performance'),
                school_subject: labelValue('', '.school_subject'),
                vocational_training: labelValue('', '.vocational_training'),
            },
            house_duties: labelValues('.house_duties'),
            church_activities: labelValues('.church_activities'),
            hobbies: labelValues('.hobbies'),
            handicap: labelValues('.handicap'),
        };
    };

    render() {
        let description_json = this.getDescriptionJson();
        return (
            <Typography variant="caption" style={{marginTop: '8px'}}>
                {JSON.stringify(description_json)}
            </Typography>
        )
    }
}<commit_msg>CO-1935 Make ChildDescription view<commit_after>import React from 'react';
import Typography from '@material-ui/core/Typography';

export default class extends React.Component {
    getDescriptionJson = () => {
        let div = document.createElement('div');
        div.innerHTML = this.props.appContext.state.child.desc_en;
        let table = div.querySelector('table');

        function s(selector) {
            let elements = table.querySelectorAll(selector);
            switch(elements.length) {
                case 0:
                    return false;
                case 1:
                    return elements[0].innerHTML;
                default:
                    let result = [];
                    for (let i = 0; i < elements.length; i++) {
                        result.push(elements[i].innerHTML);
                    }
                    return result.join(", ");
            }
        }

        function labelValue(parentClass, childClass) {
            let result = {
                label: s(parentClass + ' .desc_label' + childClass),
                value: s(parentClass + ' .desc_value' + childClass),
            };
            return (result.label === false && result.value === false) ? false:result;
        }

        function labelValues(parentClass) {
            let result = {
                label: s(parentClass + ' ' + parentClass.replace('.', '#') + '_intro'),
                value: s(parentClass + ' ' + parentClass.replace('.', '#') + '_list li'),
            };
            return (result.label === false && result.value === false) ? false:result;
        }

        return {
            program_type: s('.program_type'),
            birthday_estimate: s('.birthday_estimate'),
            live_with: s('#live_with'),
            family: {
                // father_alive: labelValue('.father', '.is_alive'),
                father_job: labelValue('.father_job', '.job'),
                // mother_alive: labelValue('.mother', '.is_alive'),
                mother_job: labelValue('.mother_job', '.job'),
                brothers: labelValue('', '.brothers'),
                sisters: labelValue('', '.sisters'),
            },
            school: {
                school_attending: s('#school_attending'),
                school_performance: labelValue('', '.school_performance'),
                school_subject: labelValue('', '.school_subject'),
                vocational_training: labelValue('', '.vocational_training'),
            },
            house_duties: labelValues('.house_duties'),
            church_activities: labelValues('.church_activities'),
            hobbies: labelValues('.hobbies'),
            handicap: labelValues('.handicap'),
        };
    };

    render() {
        let description_json = this.getDescriptionJson();
        return (

                {Object.keys(description_json).map(item => <ChildDescriptionItem key={item} label={item} value={description_json[item]}/>)}

        )
    }
}

class ChildDescriptionItem extends React.Component {
    render() {
        const {value} = this.props;
        if (!value) {
            return "";
        }
        if (typeof value === 'object') {
            let keys = Object.keys(value);
            if (keys.length === 2 && value['label'] !== 'undefined') {
                return <Typography variant="caption" gutterBottom>{value.label + " " + value.value}</Typography>;
            }
            return (

                    {keys.map(item => <ChildDescriptionItem key={item} label={item} value={value[item]}/>)}

            )
        }
        return <Typography variant="caption" gutterBottom>{value}</Typography>;
    }
}<|endoftext|>


<commit_before>'use strict';

var callable    = require('es5-ext/object/valid-callable')
  , d           = require('d/d')
  , ee          = require('event-emitter/lib/core')
  , getIterator = require('es6-iterator/get')
  , forOf       = require('es6-iterator/for-of')
  , getSetData  = require('./_get-set-data')
  , Iterator    = require('./_iterator')

  , isArray = Array.isArray
  , call = Function.prototype.call, defineProperty = Object.defineProperty
  , Set, values;

module.exports = Set = function (/*iterable, comparator*/) {
	var iterable = arguments[0], comparator = arguments[1];
	if (!(this instanceof Set)) return new Set(iterable, comparator);
	if (this.__setData__ !== undefined) {
		throw new TypeError(this + " cannot be reinitialized");
	}
	if (iterable != null) {
		if (!isArray(iterable)) iterable = getIterator(iterable);
	}
	if (comparator != null) {
		if (comparator !== 'is') throw new RangeError("Unknown comparator");
	}
	defineProperty(this, '__setData__', d('', getSetData(comparator)));
	if (!iterable) return;
	forOf(iterable, function (value) { this.add(value); }, this);
};

ee(Object.defineProperties(Set.prototype, {
	add: d(function (value) {
		if (this.has(value)) return this;
		this.emit('_add', this.__setData__.push(value) - 1);
		return this;
	}),
	clear: d(function () {
		this.__setData__.clear();
		this.emit('_clear');
	}),
	delete: d(function (value) {
		var index = this.__setData__.eIndexOf(value);
		if (index === -1) return false;
		this.__setData__.splice(index, 1);
		this.emit('_delete', index);
		return true;
	}),
	entries: d(function () { return new Iterator(this, 'key+value'); }),
	forEach: d(function (cb/*, thisArg*/) {
		var thisArg = arguments[1], iterator, result;
		callable(cb);
		iterator = this.values();
		result = iterator.next();
		while (!result.done) {
			call.call(cb, thisArg, result.value, result.value, this);
			result = iterator.next();
		}
	}),
	has: d(function (value) {
		return (this.__setData__.eIndexOf(value) !== -1);
	}),
	keys: d(values = function () { return new Iterator(this); }),
	size: d.gs(function () { return this.__setData__.length; }),
	values: d(values),
	'@@iterator': d(values),
	toString: d(function () { return '[object Set]'; })
}));
<commit_msg>Be more friendly for extensions<commit_after>'use strict';

var callable    = require('es5-ext/object/valid-callable')
  , d           = require('d/d')
  , ee          = require('event-emitter/lib/core')
  , getIterator = require('es6-iterator/get')
  , forOf       = require('es6-iterator/for-of')
  , getSetData  = require('./_get-set-data')
  , Iterator    = require('./_iterator')

  , isArray = Array.isArray
  , call = Function.prototype.call, defineProperty = Object.defineProperty
  , Set, getValues;

module.exports = Set = function (/*iterable, comparator*/) {
	var iterable = arguments[0], comparator = arguments[1];
	if (!(this instanceof Set)) return new Set(iterable, comparator);
	if (this.__setData__ !== undefined) {
		throw new TypeError(this + " cannot be reinitialized");
	}
	if (iterable != null) {
		if (!isArray(iterable)) iterable = getIterator(iterable);
	}
	if (comparator != null) {
		if (comparator !== 'is') throw new RangeError("Unknown comparator");
	}
	defineProperty(this, '__setData__', d('', getSetData(comparator)));
	if (!iterable) return;
	forOf(iterable, function (value) { this.add(value); }, this);
};

ee(Object.defineProperties(Set.prototype, {
	add: d(function (value) {
		if (this.has(value)) return this;
		this.emit('_add', this.__setData__.push(value) - 1);
		return this;
	}),
	clear: d(function () {
		this.__setData__.clear();
		this.emit('_clear');
	}),
	delete: d(function (value) {
		var index = this.__setData__.eIndexOf(value);
		if (index === -1) return false;
		this.__setData__.splice(index, 1);
		this.emit('_delete', index);
		return true;
	}),
	entries: d(function () { return new Iterator(this, 'key+value'); }),
	forEach: d(function (cb/*, thisArg*/) {
		var thisArg = arguments[1], iterator, result;
		callable(cb);
		iterator = this.values();
		result = iterator.next();
		while (!result.done) {
			call.call(cb, thisArg, result.value, result.value, this);
			result = iterator.next();
		}
	}),
	has: d(function (value) {
		return (this.__setData__.eIndexOf(value) !== -1);
	}),
	keys: d(getValues = function () { return this.values(); }),
	size: d.gs(function () { return this.__setData__.length; }),
	values: d(function () { return new Iterator(this); }),
	'@@iterator': d(getValues),
	toString: d(function () { return '[object Set]'; })
}));
<|endoftext|>


<commit_before>'use strict';

var mbaasApi = require('fh-mbaas-api')
  , express = require('express')
  , app = express()
  , mbaasExpress = mbaasApi.mbaasExpress()
  , config = require('./config')
  , middle = require('./middleware')
  , mediator = require('fh-wfm-mediator/mediator')
  ;

var securableEndpoints = [];

app.set('port', config.get('PORT'));
app.set('base url', config.get('IP'));
app.use(middle.cors);

app.use('/sys', mbaasExpress.sys(securableEndpoints));
app.use('/mbaas', mbaasExpress.mbaas);

app.use(mbaasExpress.fhmiddleware());

// app specific router
var router = express.Router();
app.use('/api', router);

// setup the wfm routes
require('wfm-workorder/router')(mediator, app);
require('wfm-workflow/router')(mediator, app);

// app modules
require('./app/workorder')(mediator);
require('./app/workflow')(mediator);

// error handling
app.use(mbaasExpress.errorHandler());

module.exports = exports = app;
<commit_msg>Used the updated fh-prefixed wfm module names<commit_after>'use strict';

var mbaasApi = require('fh-mbaas-api')
  , express = require('express')
  , app = express()
  , mbaasExpress = mbaasApi.mbaasExpress()
  , config = require('./config')
  , middle = require('./middleware')
  , mediator = require('fh-wfm-mediator/mediator')
  ;

var securableEndpoints = [];

app.set('port', config.get('PORT'));
app.set('base url', config.get('IP'));
app.use(middle.cors);

app.use('/sys', mbaasExpress.sys(securableEndpoints));
app.use('/mbaas', mbaasExpress.mbaas);

app.use(mbaasExpress.fhmiddleware());

// app specific router
var router = express.Router();
app.use('/api', router);

// setup the wfm routes
require('fh-wfm-workorder/router')(mediator, app);
require('fh-wfm-workflow/router')(mediator, app);

// app modules
require('./app/workorder')(mediator);
require('./app/workflow')(mediator);

// error handling
app.use(mbaasExpress.errorHandler());

module.exports = exports = app;
<|endoftext|>


<commit_before>'use strict'

var ObjectId = require("mongodb").ObjectId;
require('mongodb-toolkit');
var DLModels = require('dl-models');
var map = DLModels.map;
var PurchaseOrder = DLModels.purchasing.PurchaseOrder;
var generateCode = require('../../utils/code-generator');
var BaseManager = require('../base-manager');

module.exports = class PurchaseOrderManager extends BaseManager {
    constructor(db, user) {
        super(db, user);
        this.moduleId = '';
        this.year = (new Date()).getFullYear().toString().substring(2, 4);
        this.collection = this.db.use(map.purchasing.collection.PurchaseOrder);
    }

    _validate(purchaseOrder) {
        var errors = {};
        return new Promise((resolve, reject) => {
            var valid = purchaseOrder;

            if (!valid.buyerId || valid.buyerId.toString() == '')
                errors["buyer"] = "Nama Buyer tidak boleh kosong";

            if (!valid.unit || valid.unit == '')
                errors["unit"] = "Nama unit yang mengajukan tidak boleh kosong";

            if (!valid.categoryId || valid.categoryId.toString() == '')
                errors["category"] = "Kategori tidak boleh kosong";

            // if (!valid.expectedDeliveryDate || valid.expectedDeliveryDate == '')
            //     errors["expectedDeliveryDate"] = "Tanggal rencana kirim tidak boleh kosong";

            // if (!valid.actualDeliveryDate || valid.actualDeliveryDate == '')
            //     errors["actualDeliveryDate"] = "Tanggal kirim tidak boleh kosong";

            if (valid.items.length > 0) {
                var itemErrors = [];
                for (var item of valid.items) {
                    var itemError = {};

                    // if (!item.dealQuantity || item.dealQuantity == 0 || item.dealQuantity == '')
                    //     itemError["dealQuantity"] = "Jumlah kesepakatan tidak boleh kosong";
                    // if (!item.dealUom || item.dealUom == 0 || item.dealUom == '')
                    //     itemError["dealUom"] = "Jumlah kesepakatan tidak boleh kosong";
                    if (!item.defaultQuantity || item.defaultQuantity == 0 || item.defaultQuantity == '')
                        itemError["defaultQuantity"] = "Jumlah default tidak boleh kosong";
                    itemErrors.push(itemError);
                }
                for (var itemError of itemErrors) {
                    for (var prop in itemError) {
                        errors.items = itemErrors;
                        break;
                    }
                    if (errors.items)
                        break;
                }

            }
            else {
                errors["items"] = "Harus ada minimal 1 barang";
            }

            for (var prop in errors) {
                var ValidationError = require('../../validation-error');
                reject(new ValidationError('data does not pass validation', errors));
            }

            if (!valid.stamp)
                valid = new PurchaseOrder(valid);

            valid.stamp(this.user.username, 'manager');
            resolve(valid);
        });
    }

    _getQuery(paging) {
        var filter = {
            _deleted: false
        };

        var query = paging.keyword ? {
            '$and': [filter]
        } : filter;

        if (paging.keyword) {
            var regex = new RegExp(paging.keyword, "i");

            var filterRefPONo = {
                'refNo': {
                    '$regex': regex
                }
            };
            var filterPONo = {
                'no': {
                    '$regex': regex
                }
            };
            var filterBuyerName = {
                'buyer.name': {
                    '$regex': regex
                }
            };

            var $or = {
                '$or': [filterRefPONo, filterPONo, filterBuyerName]
            };

            query['$and'].push($or);
        }
        return query;
    }

    _getQueryNoPurchaseOrderExternal(_paging) {
        var filter = {
            _deleted: false,
            // purchaseOrderExternalId: {}
        };

        var query = _paging.keyword ? {
            '$and': [filter]
        } : filter;

        if (_paging.keyword) {
            var regex = new RegExp(_paging.keyword, "i");
            var filterRefPONo = {
                'refNo': {
                    '$regex': regex
                }
            };
            var filterPONo = {
                'no': {
                    '$regex': regex
                }
            };

            var $or = {
                '$or': [filterRefPONo, filterPONo]
            };

            query['$and'].push($or);
        }

        return query;
    }

    readNoPurchaseOrderExternal(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

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

            var query = this._getQueryNoPurchaseOrderExternal(_paging);

            this.collection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(PurchaseOrders => {
                    resolve(PurchaseOrders);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    create(purchaseOrder) {
        purchaseOrder = new PurchaseOrder(purchaseOrder);

        return new Promise((resolve, reject) => {
            purchaseOrder.no = `${this.moduleId}${this.year}${generateCode()}`;
            this._validate(purchaseOrder)
                .then(validPurchaseOrderc => {
                    this.collection.insert(validPurchaseOrderc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        });
                })
                .catch(e => {
                    reject(e);
                })

        });
    }

    split(purchaseOrder) {
        purchaseOrder = new PurchaseOrder(purchaseOrder);
        return new Promise((resolve, reject) => {
            purchaseOrder.no = `${this.moduleId}${this.year}${generateCode()}`;
            this._validate(purchaseOrder)
                .then(validPurchaseOrder => {
                    this.create(validPurchaseOrder)
                        .then(id => {
                            this._getByPR(validPurchaseOrder.purchaseRequest.no).then(po => {
                                for (var item of validPurchaseOrder.items) {
                                    for (var product of po.items) {
                                        if (item.product.code == product.product.code) {
                                            product.dealQuantity = product.dealQuantity - item.dealQuantity
                                            product.defaultQuantity = product.defaultQuantity - item.defaultQuantity

                                            break;
                                        }
                                    }
                                }
                                this.update(po)
                                    .then(results => {
                                        resolve(id);
                                    })
                            })
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        });
                })
                .catch(e => {
                    reject(e);
                })

        });
    }

    _getByPR(_purchaseRequestNo) {
        return new Promise((resolve, reject) => {
            if (_purchaseRequestNo === '')
                resolve(null);
            var query = {
                "purchaseRequest.no": _purchaseRequestNo,
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(module => {
                    resolve(module);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }
}<commit_msg>fix validation<commit_after>'use strict'

var ObjectId = require("mongodb").ObjectId;
require('mongodb-toolkit');
var DLModels = require('dl-models');
var map = DLModels.map;
var PurchaseOrder = DLModels.purchasing.PurchaseOrder;
var generateCode = require('../../utils/code-generator');
var BaseManager = require('../base-manager');

module.exports = class PurchaseOrderManager extends BaseManager {
    constructor(db, user) {
        super(db, user);
        this.moduleId = '';
        this.year = (new Date()).getFullYear().toString().substring(2, 4);
        this.collection = this.db.use(map.purchasing.collection.PurchaseOrder);
    }

    _validate(purchaseOrder) {
        var errors = {};
        return new Promise((resolve, reject) => {
            var valid = purchaseOrder;

            // if (!valid.buyerId || valid.buyerId.toString() == '')
            //     errors["buyer"] = "Nama Buyer tidak boleh kosong";

            if (!valid.purchaseRequest.unit)
                errors["purchaseRequest.unit"] = "Nama unit yang mengajukan tidak boleh kosong";

            if (!valid.purchaseRequest.category)
                errors["purchaseRequest.category"] = "Kategori tidak boleh kosong";

            if (!valid.purchaseRequest.no)
                errors["purchaseRequest.no"] = "No. PR yang mengajukan tidak boleh kosong";

            if (!valid.purchaseRequest.date)
                errors["purchaseRequest.date"] = "Tanggal PR tidak boleh kosong";

            if (!valid.purchaseRequest.expectedDeliveryDate)
                errors["purchaseRequest.expectedDeliveryDate"] = "Tanggal terima PR tidak boleh kosong";

            // if (!valid.expectedDeliveryDate || valid.expectedDeliveryDate == '')
            //     errors["expectedDeliveryDate"] = "Tanggal rencana kirim tidak boleh kosong";

            // if (!valid.actualDeliveryDate || valid.actualDeliveryDate == '')
            //     errors["actualDeliveryDate"] = "Tanggal kirim tidak boleh kosong";

            if (valid.items.length > 0) {
                var itemErrors = [];
                for (var item of valid.items) {
                    var itemError = {};

                    // if (!item.dealQuantity || item.dealQuantity == 0 || item.dealQuantity == '')
                    //     itemError["dealQuantity"] = "Jumlah kesepakatan tidak boleh kosong";
                    // if (!item.dealUom || item.dealUom == 0 || item.dealUom == '')
                    //     itemError["dealUom"] = "Jumlah kesepakatan tidak boleh kosong";
                    if (!item.defaultQuantity || item.defaultQuantity == 0 || item.defaultQuantity == '')
                        itemError["defaultQuantity"] = "Jumlah default tidak boleh kosong";
                    itemErrors.push(itemError);
                }
                for (var itemError of itemErrors) {
                    for (var prop in itemError) {
                        errors.items = itemErrors;
                        break;
                    }
                    if (errors.items)
                        break;
                }

            }
            else {
                errors["items"] = "Harus ada minimal 1 barang";
            }

            for (var prop in errors) {
                var ValidationError = require('../../validation-error');
                reject(new ValidationError('data does not pass validation', errors));
            }

            if(valid.purchaseRequest)
            {
                valid.refNo = valid.purchaseRequest.no;
                valid.unit = valid.purchaseRequest.unit;
                valid.unitId = valid.purchaseRequest.unit._id;
                valid.category = valid.purchaseRequest.category;
                valid.categoryId = valid.purchaseRequest.category._id;
                valid.date = valid.purchaseRequest.date;
                valid.expectedDeliveryDate = valid.purchaseRequest.expectedDeliveryDate;
            }
            if (!valid.stamp)
                valid = new PurchaseOrder(valid);

            valid.stamp(this.user.username, 'manager');
            resolve(valid);
        });
    }

    _getQuery(paging) {
        var filter = {
            _deleted: false
        };

        var query = paging.keyword ? {
            '$and': [filter]
        } : filter;

        if (paging.keyword) {
            var regex = new RegExp(paging.keyword, "i");

            var filterRefPONo = {
                'refNo': {
                    '$regex': regex
                }
            };
            var filterPONo = {
                'no': {
                    '$regex': regex
                }
            };
            var filterBuyerName = {
                'buyer.name': {
                    '$regex': regex
                }
            };

            var $or = {
                '$or': [filterRefPONo, filterPONo, filterBuyerName]
            };

            query['$and'].push($or);
        }
        return query;
    }

    _getQueryNoPurchaseOrderExternal(_paging) {
        var filter = {
            _deleted: false,
            // purchaseOrderExternalId: {}
        };

        var query = _paging.keyword ? {
            '$and': [filter]
        } : filter;

        if (_paging.keyword) {
            var regex = new RegExp(_paging.keyword, "i");
            var filterRefPONo = {
                'refNo': {
                    '$regex': regex
                }
            };
            var filterPONo = {
                'no': {
                    '$regex': regex
                }
            };

            var $or = {
                '$or': [filterRefPONo, filterPONo]
            };

            query['$and'].push($or);
        }

        return query;
    }

    readNoPurchaseOrderExternal(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

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

            var query = this._getQueryNoPurchaseOrderExternal(_paging);

            this.collection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(PurchaseOrders => {
                    resolve(PurchaseOrders);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    create(purchaseOrder) {
        purchaseOrder = new PurchaseOrder(purchaseOrder);

        return new Promise((resolve, reject) => {
            purchaseOrder.no = `${this.moduleId}${this.year}${generateCode()}`;
            this._validate(purchaseOrder)
                .then(validPurchaseOrderc => {
                    this.collection.insert(validPurchaseOrderc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        });
                })
                .catch(e => {
                    reject(e);
                })

        });
    }

    split(purchaseOrder) {
        purchaseOrder = new PurchaseOrder(purchaseOrder);
        return new Promise((resolve, reject) => {
            purchaseOrder.no = `${this.moduleId}${this.year}${generateCode()}`;
            this._validate(purchaseOrder)
                .then(validPurchaseOrder => {
                    this.create(validPurchaseOrder)
                        .then(id => {
                            this._getByPR(validPurchaseOrder.purchaseRequest.no).then(po => {
                                for (var item of validPurchaseOrder.items) {
                                    for (var product of po.items) {
                                        if (item.product.code == product.product.code) {
                                            product.dealQuantity = product.dealQuantity - item.dealQuantity
                                            product.defaultQuantity = product.defaultQuantity - item.defaultQuantity

                                            break;
                                        }
                                    }
                                }
                                this.update(po)
                                    .then(results => {
                                        resolve(id);
                                    })
                            })
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        });
                })
                .catch(e => {
                    reject(e);
                })

        });
    }

    _getByPR(_purchaseRequestNo) {
        return new Promise((resolve, reject) => {
            if (_purchaseRequestNo === '')
                resolve(null);
            var query = {
                "purchaseRequest.no": _purchaseRequestNo,
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(module => {
                    resolve(module);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }
}<|endoftext|>


<commit_before>const express = require('express');
const session = require('express-session');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const logger = require('./logging');
const auth = require('./auth');
const db = require('./models/postgresql');

// Initialize express
const app = express();

// Set up session store using database connection
const store = new SequelizeStore({ db: db.sequelize });

// Set up session store
app.use(session({
  secret: 'super secret',
  store,
  resave: true,
  saveUninitialized: true,
}));

store.sync();

if (process.env.PRODUCTION) {
  // Register dist path for static files in prod
  const staticDir = './dist';
  logger.info(`Serving staticfiles from ${staticDir}`);
  app.use('/assets/', express.static(staticDir));
  app.get('*', (req, res) =>
    res.sendFile('index.html', { root: staticDir }));
}

module.exports = async () => auth(app);
<commit_msg>Make sure to not route `/log(in|out)`  to index.html<commit_after>const express = require('express');
const session = require('express-session');
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const logger = require('./logging');
const auth = require('./auth');
const db = require('./models/postgresql');

// Initialize express
const app = express();

// Set up session store using database connection
const store = new SequelizeStore({ db: db.sequelize });

// Set up session store
app.use(session({
  secret: 'super secret',
  store,
  resave: true,
  saveUninitialized: true,
}));

store.sync();

if (process.env.PRODUCTION) {
  // Register dist path for static files in prod
  const staticDir = './dist';
  logger.info(`Serving staticfiles from ${staticDir}`);
  app.use('/assets/', express.static(staticDir));
  app.get(/^(?!\/log(in|out))/, (req, res) =>
    res.sendFile('index.html', { root: staticDir }));
}

module.exports = async () => auth(app);
<|endoftext|>


<commit_before>'use strict';

let logger = require('../log.js');
let expressWinston = require('express-winston');
let addRequestId = require('express-request-id')();
let uuidV1 = require('uuid/v1');
let moment = require('moment');

let bodyParser = require('body-parser');
let cookieSession = require('cookie-session');
let express = require('express');
let path = require('path');

let passport = require('passport');
let OAuth2Strategy = require('passport-oauth2').Strategy;
let request = require('request');

let helmet = require('helmet');
let csurf = require('csurf');
let compression = require('compression');

let index = require('../routes/index');
let disclaimer = require('../routes/disclaimer');
let search = require('../routes/search');
let subject = require('../routes/subject');

let content = require('../data/content.js');
let config = require('../server/config');

const version = moment.now().toString();
const production = process.env.NODE_ENV === 'production';

//  Express Configuration
let app = express();

// Configure Express for running behind proxies - https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', true);

// HACK: Azure doesn't support X-Forwarded-Proto so we add it manually
// http://stackoverflow.com/a/18455265/173062
app.use(function(req, res, next) {
    if (req.headers['x-arr-ssl'] && !req.headers['x-forwarded-proto']) {
        req.headers['x-forwarded-proto'] = 'https';
    }
    return next();
});

// Secure code best practice - see:
// 1. https://expressjs.com/en/advanced/best-practice-security.html,
// 2. https://www.npmjs.com/package/helmet
app.use(helmet());

// Automatically log every request with user details, a unique session id, and a unique request id
app.use(addRequestId);
function requestLogger() {
    return expressWinston.logger({
        winstonInstance: logger,
        meta: true,
        dynamicMeta: function(req, res) {
            return {
                userId: req.user ? req.user.id : null,
                userEmail: req.user ? req.user.email : null,
                requestId: req.id,
                sessionTag: req.user ? req.user.sessionTag : null
            };
        },
        colorize: true,
        requestWhitelist: ['url', 'method', 'originalUrl', 'query', 'body']
    });
}

// SSO configuration
let testMode = process.env.NODE_ENV === 'test' ? 'true' : 'false';
let ssoConfig = config.sso;

app.use(cookieSession({
    name: 'session',
    keys: [config.sessionSecret],
    maxAge: 60 * 60 * 1000,
    secure: config.https,
    httpOnly: true,
    signed: true,
    overwrite: true,
    sameSite: true
}));

if (testMode === 'true') {
    logger.info('Authentication disabled - using default test user profile');
    app.use(dummyUserProfile);
} else {
    logger.info('Authentication enabled');
    enableSSO();
}

// View Engine Configuration
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view engine', 'jade');

// Server Configuration
app.set('port', process.env.PORT || 3000);

// Request Processing Configuration
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// Resource Delivery Configuration
app.use(compression());

if (production) {
    app.locals.version = version;
} else {
    app.use(function(req, res, next) {
        res.locals.version = moment.now().toString();
        return next();
    });
}

//  Static Resources Configuration
let cacheControl = {maxAge: config.staticResourceCacheDuration * 1000};

let publicResourcePaths = [
    '../public',
    '../govuk_modules/govuk_template/assets',
    '../govuk_modules/govuk_frontend_toolkit'
];

let iconResourcePaths = [
    '../govuk_modules/govuk_frontend_toolkit/images'
];

publicResourcePaths.forEach((dir) => {
    app.use('/public', express.static(path.join(__dirname, dir), cacheControl));
});

iconResourcePaths.forEach((dir) => {
    app.use('/public/images/icons', express.static(path.join(__dirname, dir), cacheControl));
});

// GovUK Template Configuration
/* jshint ignore:start */
app.locals.asset_path = '/public/';
/* jshint ignore:end */

// Don't cache dynamic resources
app.use(helmet.noCache());

// CSRF protection
if (testMode !== 'true') {
    app.use(csurf());
}

// Express Routing Configuration
app.use(requestLogger());
app.use('/', index);
app.use('/disclaimer/', disclaimer);
if (testMode !== 'true') {
    app.use(authRequired);
    app.use(addTemplateVariables);
}
app.use('/search/', search);
app.use('/subject/', subject);

// Error Handler
app.use(function(req, res, next) {
    let error = new Error('Not Found');
    error.status = 404;
    res.render('notfound', {nav: true, content: content.view.notfound});
});

app.use(logErrors);
app.use(clientErrors);

function logErrors(error, req, res, next) {
    logger.error('Unhandled error: ' + error.stack);
    next(error);
}

function clientErrors(error, req, res, next) {
    res.locals.error = error;
    res.locals.stack = production ? null : error.stack;
    res.locals.message = production ? 'Something went wrong. The error has been logged. Please try again' : error.message;

    res.status(error.status || 500);

    res.render('error', {nav: true, content: content.view.error});
}

//  SSO utility methods
function authRequired(req, res, next) {
    if (!req.user) {
        logger.info('Authorisation required - redirecting to login');
        return res.redirect('/login');
    }
    if (!req.user.disclaimer) {
        logger.info('Disclaimer required - redirecting to disclaimer');
        return res.redirect('/disclaimer');
    }
    res.locals.nav = true;
    next();
}

function addTemplateVariables(req, res, next) {
    res.locals.profile = req.user;
    next();
}

function dummyUserProfile(req, res, next) {
    req.user = {
        'email': 'test@test.com',
        'firstName': 'Test',
        'lastName': 'Tester',
        'profileLink': '/profile',
        'logoutLink': '/logout'
    };
    res.locals.profile = req.user;
    next();
}

function enableSSO() {

    app.use(passport.initialize());
    app.use(passport.session());

    passport.use(new OAuth2Strategy({
            authorizationURL: ssoConfig.TOKEN_HOST + ssoConfig.AUTHORIZE_PATH,
            tokenURL: ssoConfig.TOKEN_HOST + ssoConfig.TOKEN_PATH,
            clientID: ssoConfig.CLIENT_ID,
            clientSecret: ssoConfig.CLIENT_SECRET,
            proxy: true // trust upstream proxy
        },
        function(accessToken, refreshToken, profile, cb) {
            logger.info('Passport authentication invoked');

            let options = {
                uri: ssoConfig.TOKEN_HOST + ssoConfig.USER_DETAILS_PATH,
                qs: {access_token: accessToken},
                json: true
            };
            request(options, function(error, response, userDetails) {
                if (!error && response.statusCode === 200) {
                    logger.info('User authentication success');
                    return cb(null, userFor(userDetails));
                } else {
                    logger.error('Authentication failure:' + error);
                    return cb(error);
                }
            });
        })
    );

    function userFor(userDetails) {
        return {
            id: userDetails.id,
            email: userDetails.email,
            firstName: userDetails.first_name,
            lastName: userDetails.last_name,
            profileLink: userDetails.links.profile,
            logoutLink: userDetails.links.logout,
            sessionTag: uuidV1()
        };
    }

    passport.serializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

}

module.exports = app;
<commit_msg>Fix session config<commit_after>'use strict';

let logger = require('../log.js');
let expressWinston = require('express-winston');
let addRequestId = require('express-request-id')();
let uuidV1 = require('uuid/v1');
let moment = require('moment');

let bodyParser = require('body-parser');
let cookieSession = require('cookie-session');
let express = require('express');
let path = require('path');

let passport = require('passport');
let OAuth2Strategy = require('passport-oauth2').Strategy;
let request = require('request');

let helmet = require('helmet');
let csurf = require('csurf');
let compression = require('compression');

let index = require('../routes/index');
let disclaimer = require('../routes/disclaimer');
let search = require('../routes/search');
let subject = require('../routes/subject');

let content = require('../data/content.js');
let config = require('../server/config');

const version = moment.now().toString();
const production = process.env.NODE_ENV === 'production';

//  Express Configuration
let app = express();

// Configure Express for running behind proxies - https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', true);

// HACK: Azure doesn't support X-Forwarded-Proto so we add it manually
// http://stackoverflow.com/a/18455265/173062
app.use(function(req, res, next) {
    if (req.headers['x-arr-ssl'] && !req.headers['x-forwarded-proto']) {
        req.headers['x-forwarded-proto'] = 'https';
    }
    return next();
});

// Secure code best practice - see:
// 1. https://expressjs.com/en/advanced/best-practice-security.html,
// 2. https://www.npmjs.com/package/helmet
app.use(helmet());

// Automatically log every request with user details, a unique session id, and a unique request id
app.use(addRequestId);
function requestLogger() {
    return expressWinston.logger({
        winstonInstance: logger,
        meta: true,
        dynamicMeta: function(req, res) {
            return {
                userId: req.user ? req.user.id : null,
                userEmail: req.user ? req.user.email : null,
                requestId: req.id,
                sessionTag: req.user ? req.user.sessionTag : null
            };
        },
        colorize: true,
        requestWhitelist: ['url', 'method', 'originalUrl', 'query', 'body']
    });
}

// SSO configuration
let testMode = process.env.NODE_ENV === 'test' ? 'true' : 'false';
let ssoConfig = config.sso;

app.use(cookieSession({
    name: 'session',
    keys: [config.sessionSecret],
    maxAge: 60 * 60 * 1000,
    secure: config.https,
    httpOnly: true,
    signed: true,
    overwrite: true,
    sameSite: 'lax'
}));

if (testMode === 'true') {
    logger.info('Authentication disabled - using default test user profile');
    app.use(dummyUserProfile);
} else {
    logger.info('Authentication enabled');
    enableSSO();
}

// View Engine Configuration
app.set('views', path.join(__dirname, '..', 'views'));
app.set('view engine', 'jade');

// Server Configuration
app.set('port', process.env.PORT || 3000);

// Request Processing Configuration
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// Resource Delivery Configuration
app.use(compression());

if (production) {
    app.locals.version = version;
} else {
    app.use(function(req, res, next) {
        res.locals.version = moment.now().toString();
        return next();
    });
}

//  Static Resources Configuration
let cacheControl = {maxAge: config.staticResourceCacheDuration * 1000};

let publicResourcePaths = [
    '../public',
    '../govuk_modules/govuk_template/assets',
    '../govuk_modules/govuk_frontend_toolkit'
];

let iconResourcePaths = [
    '../govuk_modules/govuk_frontend_toolkit/images'
];

publicResourcePaths.forEach((dir) => {
    app.use('/public', express.static(path.join(__dirname, dir), cacheControl));
});

iconResourcePaths.forEach((dir) => {
    app.use('/public/images/icons', express.static(path.join(__dirname, dir), cacheControl));
});

// GovUK Template Configuration
/* jshint ignore:start */
app.locals.asset_path = '/public/';
/* jshint ignore:end */

// Don't cache dynamic resources
app.use(helmet.noCache());

// CSRF protection
if (testMode !== 'true') {
    app.use(csurf());
}

// Express Routing Configuration
app.use(requestLogger());
app.use('/', index);
app.use('/disclaimer/', disclaimer);
if (testMode !== 'true') {
    app.use(authRequired);
    app.use(addTemplateVariables);
}
app.use('/search/', search);
app.use('/subject/', subject);

// Error Handler
app.use(function(req, res, next) {
    let error = new Error('Not Found');
    error.status = 404;
    res.render('notfound', {nav: true, content: content.view.notfound});
});

app.use(logErrors);
app.use(clientErrors);

function logErrors(error, req, res, next) {
    logger.error('Unhandled error: ' + error.stack);
    next(error);
}

function clientErrors(error, req, res, next) {
    res.locals.error = error;
    res.locals.stack = production ? null : error.stack;
    res.locals.message = production ? 'Something went wrong. The error has been logged. Please try again' : error.message;

    res.status(error.status || 500);

    res.render('error', {nav: true, content: content.view.error});
}

//  SSO utility methods
function authRequired(req, res, next) {
    if (!req.user) {
        logger.info('Authorisation required - redirecting to login');
        return res.redirect('/login');
    }
    if (!req.user.disclaimer) {
        logger.info('Disclaimer required - redirecting to disclaimer');
        return res.redirect('/disclaimer');
    }
    res.locals.nav = true;
    next();
}

function addTemplateVariables(req, res, next) {
    res.locals.profile = req.user;
    next();
}

function dummyUserProfile(req, res, next) {
    req.user = {
        'email': 'test@test.com',
        'firstName': 'Test',
        'lastName': 'Tester',
        'profileLink': '/profile',
        'logoutLink': '/logout'
    };
    res.locals.profile = req.user;
    next();
}

function enableSSO() {

    app.use(passport.initialize());
    app.use(passport.session());

    passport.use(new OAuth2Strategy({
            authorizationURL: ssoConfig.TOKEN_HOST + ssoConfig.AUTHORIZE_PATH,
            tokenURL: ssoConfig.TOKEN_HOST + ssoConfig.TOKEN_PATH,
            clientID: ssoConfig.CLIENT_ID,
            clientSecret: ssoConfig.CLIENT_SECRET,
            proxy: true // trust upstream proxy
        },
        function(accessToken, refreshToken, profile, cb) {
            logger.info('Passport authentication invoked');

            let options = {
                uri: ssoConfig.TOKEN_HOST + ssoConfig.USER_DETAILS_PATH,
                qs: {access_token: accessToken},
                json: true
            };
            request(options, function(error, response, userDetails) {
                if (!error && response.statusCode === 200) {
                    logger.info('User authentication success');
                    return cb(null, userFor(userDetails));
                } else {
                    logger.error('Authentication failure:' + error);
                    return cb(error);
                }
            });
        })
    );

    function userFor(userDetails) {
        return {
            id: userDetails.id,
            email: userDetails.email,
            firstName: userDetails.first_name,
            lastName: userDetails.last_name,
            profileLink: userDetails.links.profile,
            logoutLink: userDetails.links.logout,
            sessionTag: uuidV1()
        };
    }

    passport.serializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        // Not used but required for Passport
        done(null, user);
    });

}

module.exports = app;
<|endoftext|>