<commit_before>'use strict';

angular.module('ludwig').factory('AcceptanceTestsService', function($q, $http, $filter, PossibleValuesService, config) {
    var droits = {};

    PossibleValuesService.get().then(function(result) {
        droits = _.indexBy(result.data, 'id');
    });

    var statusMapping = {
        'accepted-exact': 'ok',
        'accepted-2pct': 'ok',
        'accepted-10pct': 'near',
        'rejected': 'ko'
    };

    var handleResult = function(result, test, deferred) {
        test.lastExecution = angular.copy(result.data);
        test.currentStatus = test.lastExecution.status;

        formatValues(test);

        if (deferred) {
            if (test.status === 'ko') {
                deferred.reject();
            } else {
                deferred.resolve();
            }
        }
    };

    var displayValue = function(value) {
        if (_.isBoolean(value)) {
            return value ? 'Oui' : 'Non';
        }

        if (_.isNumber(value)) {
            return '' + value + ' €';
        }

        return '';
    };

    var htmlDescription = function(text) {
        if (!text) {
            return '';
        }

        var result = $filter('linky')(text);
        result = result.replace(/
/g, '
');

        return result;
    };

    var formatValues = function(test) {
        if (test._updated) {
            var updatedAt = moment(test._updated);
            test.updatedAt = updatedAt.format('DD/MM/YYYY à HH:mm');
        }
        if (test._created) {
            var createdAt = moment(test._created);
            test.createdAt = createdAt.format('DD/MM/YYYY à HH:mm');
        }

        test.status = statusMapping[test.currentStatus];
        test.htmlDescription = htmlDescription(test.description);

        if (test.lastExecution) {
            test.expectedResults = test.lastExecution.results;
        }

        test.expectedResults.forEach(function (expectedResult) {
            expectedResult.displayLabel = droits[expectedResult.code].shortLabel;
            expectedResult.displayStatus = expectedResult.status ? statusMapping[expectedResult.status] : 'unknown';
            expectedResult.displayExpected = displayValue(expectedResult.expectedValue);
            expectedResult.displayResult = displayValue(expectedResult.result);
        });
    };

    var orderTestsByKeywords = function(tests) {
        return _.sortBy(tests, function(test) {
            return test.keywords.join() + test.name;
        });
    };

    return {
        getKeywords: function() {
            return $http.get(config.baseApiPath + '/acceptance-tests/keywords').then(function(result) {
                return result.data;
            });
        },

        getOne: function(id) {
            return $http.get(config.baseApiPath + '/acceptance-tests/' + id).then(function(result) {
                var tests = [result.data];
                _.map(tests, formatValues);
                return tests;
            });
        },

        get: function(filters, isPublic) {
            return $http.get(config.baseApiPath + (isPublic ? '/public' : '') + '/acceptance-tests', { params: filters }).then(function(result) {
                var tests = result.data;
                _.map(tests, formatValues);
                return orderTestsByKeywords(tests);
            });
        },

        simulate: function(test) {
            return $http.post(config.baseApiPath + '/acceptance-tests/' + test._id + '/simulation');
        },

        launchTest: function(test) {
            test.running = true;
            delete test.status;
            test.expectedResults.forEach(function(expectedResult) {
                delete expectedResult.status;
                delete expectedResult.result;
            });

            var deferred = $q.defer();

            var promise = $http.post(config.baseApiPath + '/acceptance-tests/' + test._id + '/executions', {});
            promise.then(function(result) {
                return handleResult(result, test, deferred);
            }, function() {
                test.status = 'ko';
                test.expectedResults.forEach(function(droit) {
                    droit.status = 'ko';
                });
                deferred.reject();
            });

            deferred.promise.finally(function() {
                test.running = false;
            });

            return deferred.promise;
        }
    };
});
<commit_msg>Add resilience from tests outside from possibleValues<commit_after>'use strict';

angular.module('ludwig').factory('AcceptanceTestsService', function($q, $http, $filter, PossibleValuesService, config) {
    var droits = {};

    PossibleValuesService.get().then(function(result) {
        droits = _.indexBy(result.data, 'id');
    });

    var statusMapping = {
        'accepted-exact': 'ok',
        'accepted-2pct': 'ok',
        'accepted-10pct': 'near',
        'rejected': 'ko'
    };

    var handleResult = function(result, test, deferred) {
        test.lastExecution = angular.copy(result.data);
        test.currentStatus = test.lastExecution.status;

        formatValues(test);

        if (deferred) {
            if (test.status === 'ko') {
                deferred.reject();
            } else {
                deferred.resolve();
            }
        }
    };

    var displayValue = function(value) {
        if (_.isBoolean(value)) {
            return value ? 'Oui' : 'Non';
        }

        if (_.isNumber(value)) {
            return '' + value + ' €';
        }

        return '';
    };

    var htmlDescription = function(text) {
        if (!text) {
            return '';
        }

        var result = $filter('linky')(text);
        result = result.replace(/
/g, '
');

        return result;
    };

    var formatValues = function(test) {
        if (test._updated) {
            var updatedAt = moment(test._updated);
            test.updatedAt = updatedAt.format('DD/MM/YYYY à HH:mm');
        }
        if (test._created) {
            var createdAt = moment(test._created);
            test.createdAt = createdAt.format('DD/MM/YYYY à HH:mm');
        }

        test.status = statusMapping[test.currentStatus];
        test.htmlDescription = htmlDescription(test.description);

        if (test.lastExecution) {
            test.expectedResults = test.lastExecution.results;
        }

        test.expectedResults.forEach(function (expectedResult) {
            expectedResult.displayLabel = (droits[expectedResult.code] ? droits[expectedResult.code].shortLabel : 'Valeur inconnue');
            expectedResult.displayStatus = expectedResult.status ? statusMapping[expectedResult.status] : 'unknown';
            expectedResult.displayExpected = displayValue(expectedResult.expectedValue);
            expectedResult.displayResult = displayValue(expectedResult.result);
        });
    };

    var orderTestsByKeywords = function(tests) {
        return _.sortBy(tests, function(test) {
            return test.keywords.join() + test.name;
        });
    };

    return {
        getKeywords: function() {
            return $http.get(config.baseApiPath + '/acceptance-tests/keywords').then(function(result) {
                return result.data;
            });
        },

        getOne: function(id) {
            return $http.get(config.baseApiPath + '/acceptance-tests/' + id).then(function(result) {
                var tests = [result.data];
                _.map(tests, formatValues);
                return tests;
            });
        },

        get: function(filters, isPublic) {
            return $http.get(config.baseApiPath + (isPublic ? '/public' : '') + '/acceptance-tests', { params: filters }).then(function(result) {
                var tests = result.data;
                _.map(tests, formatValues);
                return orderTestsByKeywords(tests);
            });
        },

        simulate: function(test) {
            return $http.post(config.baseApiPath + '/acceptance-tests/' + test._id + '/simulation');
        },

        launchTest: function(test) {
            test.running = true;
            delete test.status;
            test.expectedResults.forEach(function(expectedResult) {
                delete expectedResult.status;
                delete expectedResult.result;
            });

            var deferred = $q.defer();

            var promise = $http.post(config.baseApiPath + '/acceptance-tests/' + test._id + '/executions', {});
            promise.then(function(result) {
                return handleResult(result, test, deferred);
            }, function() {
                test.status = 'ko';
                test.expectedResults.forEach(function(droit) {
                    droit.status = 'ko';
                });
                deferred.reject();
            });

            deferred.promise.finally(function() {
                test.running = false;
            });

            return deferred.promise;
        }
    };
});
<|endoftext|>


<commit_before>Package.describe({
	name: 'rocketchat:i18n',
	version: '0.0.1',
	summary: 'RocketChat i18n',
	git: ''
});

Package.onUse(function(api) {
	api.use('templating', 'client');

	var fs = Npm.require('fs');
	fs.readdirSync('packages/rocketchat-i18n/i18n').forEach(function(filename) {
		if (filename.indexOf('.json') > -1 && fs.statSync('packages/rocketchat-i18n/i18n/' + filename).size > 16) {
			api.addFiles('i18n/' + filename);
		}
	});

	api.use('tap:i18n');
});
<commit_msg>set tap:i18n version in i18n package to install the expected version when the package is used in other projects<commit_after>Package.describe({
	name: 'rocketchat:i18n',
	version: '0.0.1',
	summary: 'RocketChat i18n',
	git: ''
});

Package.onUse(function(api) {
	api.use('templating', 'client');

	var fs = Npm.require('fs');
	fs.readdirSync('packages/rocketchat-i18n/i18n').forEach(function(filename) {
		if (filename.indexOf('.json') > -1 && fs.statSync('packages/rocketchat-i18n/i18n/' + filename).size > 16) {
			api.addFiles('i18n/' + filename);
		}
	});

	api.use('tap:i18n@1.8.2');
});
<|endoftext|>


<commit_before>'use strict';
const _ = require('lodash');
const rp = require('request-promise');
const LocalRunner = require('./LocalRunner.js');
const DockerRunner = require('./DockerRunner.js');
const endpointToUrl = require('./common.js').endpointToUrl;
const WAIT_TIMEOUT = 300; // seconds

function sleep(ms) {
  return new Promise(resolve => setTimeout(() => resolve(), ms));
}

class InstanceManager {
  constructor(name) {
    this.instances = [];

    if (process.env.RESILIENCE_ARANGO_BASEPATH) {
      this.runner = new LocalRunner(process.env.RESILIENCE_ARANGO_BASEPATH);
    } else if (process.env.RESILIENCE_DOCKER_IMAGE) {
      this.runner = new DockerRunner(process.env.RESILIENCE_DOCKER_IMAGE);
    }

    if (process.env.ARANGO_STORAGE_ENGINE) {
      this.storageEngine = process.env.ARANGO_STORAGE_ENGINE;
      if (this.storageEngine !== 'rocksdb') {
        this.storageEngine = 'mmfiles';
      }
    } else {
      this.storageEngine = 'mmfiles';
    }

    if (!this.runner) {
      throw new Error(
        'Must specify RESILIENCE_ARANGO_BASEPATH (source root dir including a "build" folder containing compiled binaries or RESILIENCE_DOCKER_IMAGE to test a docker container'
      );
    }
    this.currentLog = '';
    this.agentCounter = 0;
    this.coordinatorCounter = 0;
    this.dbServerCounter = 0;
  }

  startArango(name, endpoint, role, args) {
    args.push('--server.authentication=false');
    //args.push('--log.level=v8=debug')
    //args.push('--log.level=communication=debug');
    args.push('--server.storage-engine=' + this.storageEngine);

    if (process.env.ARANGO_EXTRA_ARGS) {
      args = args.concat(process.env.ARANGO_EXTRA_ARGS.split(' '));
    }

    let instance = {
      name,
      role,
      process: null,
      status: 'NEW',
      exitcode: null,
      endpoint,
      args,
      logFn: line => {
        if (line.trim().length > 0) {
          let logLine =
            instance.name + '(' + instance.process.pid + '): \t' + line;
          if (process.env.LOG_IMMEDIATE && process.env.LOG_IMMEDIATE == "1") {
            console.log(logLine);
          } else {
            logLine = logLine.replace(/\x1B/g, '');
            this.currentLog += logLine + '\n';
          }
        }
      }
    };
    return this.runner.firstStart(instance);
  }

  startDbServer(name) {
    this.dbServerCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=PRIMARY',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'primary', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  getAgencyEndpoint() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    })[0].endpoint;
  }

  startCoordinator(name) {
    this.coordinatorCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=COORDINATOR',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'coordinator', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  replace(instance) {
    let name, role;
    switch (instance.role) {
      case 'coordinator':
        this.coordinatorCounter++;
        name = 'coordinator-' + this.coordinatorCounter;
        role = 'COORDINATOR';
        break;
      case 'primary':
        this.dbServerCounter++;
        name = 'dbServer-' + this.dbServerCounter;
        role = 'PRIMARY';
        break;
      default:
        throw new Error('Can only replace coordinators/dbServers');
    }
    if (this.instances.includes(instance)) {
      throw new Error('Instance must be destroyed before it can be replaced');
    }
    let args = [
      '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
      '--cluster.my-role=' + role,
      '--cluster.my-local-info=' + name,
      '--cluster.my-address=' + instance.endpoint
    ];
    return this.startArango(name, instance.endpoint, instance.role, args)
      .then(instance => this.waitForInstance(instance))
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  startAgency(options = {}) {
    let size = options.agencySize || 1;
    if (options.agencyWaitForSync === undefined) {
      options.agencyWaitForSync = false;
    }
    const wfs = options.agencyWaitForSync;
    let promise = Promise.resolve([]);
    let compactionStep = "200";
    let compactionKeep = "100";
    if (process.env.AGENCY_COMPACTION_STEP) {
      compactionStep = process.env.AGENCY_COMPACTION_STEP;
    }
    if (process.env.AGENCY_COMPACTION_KEEP) {
      compactionKeep = process.env.AGENCY_COMPACTION_KEEP;
    }
    for (var i = 0; i < size; i++) {
      promise = promise.then(instances => {
        return this.runner
          .createEndpoint()
          .then(endpoint => {
            let args = [
              '--agency.activate=true',
              '--agency.size=' + size,
              '--agency.pool-size=' + size,
              '--agency.wait-for-sync=true',
              '--agency.supervision=true',
              '--server.threads=16',
              '--agency.supervision-frequency=0.5',
              '--agency.supervision-grace-period=2.5',
              '--agency.compaction-step-size='+compactionStep,
              '--agency.compaction-keep-size='+compactionKeep,
              '--agency.my-address=' + endpoint,
              '--log.force-direct=true'
            ];
            if (instances.length === 0) {
              args.push('--agency.endpoint=' + endpoint);
            } else {
              args.push('--agency.endpoint=' + instances[0].endpoint);
            }
            this.agentCounter++;
            return this.startArango(
              'agency-' + this.agentCounter,
              endpoint,
              'agent',
              args
            );
          })
          .then(instance => {
            return [...instances, instance];
          });
      });
    }
    return promise.then(agents => {
      this.instances = agents;
      return agents;
    });
  }

  findPrimaryDbServer(collectionName) {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    return rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        [
          '/arango/Plan/Collections/_system',
          '/arango/Current/ServersRegistered'
        ]
      ]
    }).then(([info]) => {
      const collections = info.arango.Plan.Collections._system;
      const servers = info.arango.Current.ServersRegistered;
      for (const id of Object.keys(collections)) {
        const collection = collections[id];
        if (collection.name !== collectionName) {
          continue;
        }
        const shards = collection.shards;
        const shardsId = Object.keys(shards)[0];
        const uuid = shards[shardsId][0];
        const endpoint = servers[uuid].endpoint;
        const dbServers = this.dbServers().filter(
          instance => instance.endpoint === endpoint
        );
        if (!dbServers.length) {
          return Promise.reject(new Error(`Unknown endpoint "${endpoint}"`));
        }
        return dbServers[0];
      }
      return Promise.reject(
        new Error(`Unknown collection "${collectionName}"`)
      );
    });
  }

  startCluster(numAgents, numCoordinators, numDbServers, options = {}) {
    let agencyOptions = options.agents || {};
    _.extend(agencyOptions, {agencySize: numAgents});

    return this.startAgency(agencyOptions)
      .then(agents => {
        let promises = [Promise.resolve(agents)];

        let coordinators = Array.from(
          Array(numCoordinators).keys()
        ).map(index => {
          return this.startCoordinator('coordinator-' + (index + 1));
        });
        let dbServers = Array.from(Array(numDbServers).keys()).map(index => {
          return this.startDbServer('dbServer-' + (index + 1));
        });
        return Promise.all([].concat(coordinators, dbServers));
      })
      .then(servers => {
        return this.waitForAllInstances();
      })
      .then(() => {
        return this.getEndpoint();
      });
  }

  async waitForInstance(instance, started = Date.now()) {
    if (instance.status !== 'RUNNING') {
      throw new Error(`Instance ${instance.name} is down!`);
    }
    if (Date.now() - started > WAIT_TIMEOUT * 1000) {
      throw new Error(
        `Instance ${instance.name} is still not ready after ${WAIT_TIMEOUT} secs`
      );
    }

    let ok = false;
    try {
      await rp({
        method: 'GET',
        uri: endpointToUrl(instance.endpoint) + '/_api/version'
      });
      ok = true;
    } catch (e) {}
    if (ok) {
      return instance;
    } else {
      await sleep(100);
      return this.waitForInstance(instance, started);
    }
  }

  waitForAllInstances() {
    return Promise.all(
      this.instances.map(instance => {
        return this.waitForInstance(instance);
      })
    );
  }

  getEndpoint(instance) {
    return (instance || this.coordinators()[0]).endpoint;
  }

  getEndpointUrl(instance) {
    return endpointToUrl(this.getEndpoint(instance));
  }

  check() {
    return this.instances.every(instance => {
      return instance.status === 'RUNNING';
    });
  }

  shutdownCluster() {
    let shutdownPromise;

    let nonAgents = [].concat(this.coordinators(), this.dbServers());

    return Promise.all(nonAgents.map(this.shutdown.bind(this)))
    .then(() => {
      return Promise.all(this.agents().map(this.shutdown.bind(this)))
    });
  }

  cleanup() {
    return this.shutdownCluster()
      .then(() => {
        this.instances = [];
        this.agentCounter = 0;
        this.coordinatorCounter = 0;
        this.dbServerCounter = 0;
        return this.runner.cleanup();
      })
      .then(() => {
        let log = this.currentLog;
        this.currentLog = '';
        return log;
      });
  }

  dbServers() {
    return this.instances.filter(instance => {
      return instance.role === 'primary';
    });
  }

  coordinators() {
    return this.instances.filter(instance => {
      return instance.role === 'coordinator';
    });
  }

  agents() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    });
  }

  assignNewEndpoint(instance) {
    let index = this.instances.indexOf(instance);
    if (index === -1) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    var createNewEndpoint = () => {
      return this.runner.createEndpoint().then(endpoint => {
        if (endpoint != instance.endpoint) {
          return endpoint;
        } else {
          return createNewEndpoint();
        }
      });
    };

    return createNewEndpoint().then(endpoint => {
      [
        'server.endpoint',
        'agency.my-address',
        'cluster.my-address'
      ].filter(arg => {
        index = instance.args.indexOf('--' + arg + '=' + instance.endpoint);
        if (index !== -1) {
          instance.args[index] = '--' + arg + '=' + endpoint;
        }
      });
      return this.runner.updateEndpoint(instance, endpoint);
    });
  }

  // beware! signals are not supported on windows and it will simply do hard kills all the time
  // use shutdown to gracefully stop an instance!
  kill(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    instance.process.kill('SIGKILL');
    instance.status = 'KILLED';
    return new Promise((resolve, reject) => {
      let check = function() {
        if (instance.status !== 'EXITED') {
          setTimeout(check, 50);
        } else {
          resolve();
        }
      };
      check();
    });
  }

  shutdown(instance) {
    if (instance.status == 'EXITED') {
      return Promise.resolve(instance);
    }

    let checkDown = function() {
      return new Promise((resolve, reject) => {
        let attempts = 0;
        let maxAttempts = 1200;
        let waitInterval = 50;
        (function checkDown() {
          if (instance.status == 'EXITED') {
            resolve(instance);
          } else if (++attempts > maxAttempts) {
            reject(new Error(instance.name + ' did not stop gracefully after ' + (waitInterval * attempts) + 'ms'));
          } else {
            setTimeout(checkDown, waitInterval);
          }
        })();
      });
    }

    return rp.delete({
      url: this.getEndpointUrl(instance) + '/_admin/shutdown',
    })
    .catch(err => {
      if (err && (err.statusCode === 503 || err.error)) {
        if (err.error.code == 'ECONNREFUSED') {
          console.warn('hmmm...server ' + instance.name + ' did not respond (' + err.code + '). Assuming it is dead. Status is: ' + instance.status);
          return checkDown();
        } else if (err.error.code == 'ECONNRESET') {
          return checkDown();
        } else if (err.statusCode === 503) {
          console.warn('server ' + instance.name + ' answered 503. Assuming it is shutting down. Status is: ' + instance.status);
          return checkDown();
        }
      }
      console.error("Unhandled error", err);

      return Promise.reject(err);
    })
    .then(x => {
      return checkDown();
    })
  }

  destroy(instance) {
    let promise;
    if (this.instances.includes(instance)) {
      promise = this.shutdown(instance);
    } else {
      promise = Promise.resolve();
    }
    return promise.then(() => this.runner.destroy(instance)).then(() => {
      const i = this.instances.indexOf(instance);
      if (i !== -1) {
        this.instances = [
          ...this.instances.slice(0, i),
          ...this.instances.slice(i + 1)
        ];
      }
    });
  }

  restart(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    return this.runner.restart(instance).then(() => {
      return this.waitForInstance(instance);
    });
  }

  // this will append the logs to the test in case of a failure so
  // you get a nice combined log of what happened on the server and client
  moveServerLogs(test) {
    if (test.state === 'failed') {
      test.err.message = this.currentLog + '\n\n' + test.err.message;
    }
    this.currentLog = '';
  }

  async getFoxxmaster() {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    const [info] = await rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        ['/arango/Current/Foxxmaster', '/arango/Current/ServersRegistered']
      ]
    });
    const uuid = info.arango.Current.Foxxmaster;
    const endpoint = info.arango.Current.ServersRegistered[uuid].endpoint;
    return this.instances.find(instance => instance.endpoint === endpoint);
  }

  async restartCluster() {
    const fm = await this.getFoxxmaster();
    await this.shutdownCluster();
    await Promise.all(this.agents().map(agent => this.restart(agent)));
    await Promise.all(this.dbServers().map(dbs => this.restart(dbs)));
    this.restart(fm);
    await sleep(100);
    await Promise.all(
      this.coordinators()
        .filter(coord => coord !== fm)
        .map(coord => this.restart(coord))
    );
    await this.waitForAllInstances();
  }
}

module.exports = InstanceManager;
<commit_msg>windows is slow<commit_after>'use strict';
const _ = require('lodash');
const rp = require('request-promise');
const LocalRunner = require('./LocalRunner.js');
const DockerRunner = require('./DockerRunner.js');
const endpointToUrl = require('./common.js').endpointToUrl;
const WAIT_TIMEOUT = 400; // seconds

function sleep(ms) {
  return new Promise(resolve => setTimeout(() => resolve(), ms));
}

class InstanceManager {
  constructor(name) {
    this.instances = [];

    if (process.env.RESILIENCE_ARANGO_BASEPATH) {
      this.runner = new LocalRunner(process.env.RESILIENCE_ARANGO_BASEPATH);
    } else if (process.env.RESILIENCE_DOCKER_IMAGE) {
      this.runner = new DockerRunner(process.env.RESILIENCE_DOCKER_IMAGE);
    }

    if (process.env.ARANGO_STORAGE_ENGINE) {
      this.storageEngine = process.env.ARANGO_STORAGE_ENGINE;
      if (this.storageEngine !== 'rocksdb') {
        this.storageEngine = 'mmfiles';
      }
    } else {
      this.storageEngine = 'mmfiles';
    }

    if (!this.runner) {
      throw new Error(
        'Must specify RESILIENCE_ARANGO_BASEPATH (source root dir including a "build" folder containing compiled binaries or RESILIENCE_DOCKER_IMAGE to test a docker container'
      );
    }
    this.currentLog = '';
    this.agentCounter = 0;
    this.coordinatorCounter = 0;
    this.dbServerCounter = 0;
  }

  startArango(name, endpoint, role, args) {
    args.push('--server.authentication=false');
    //args.push('--log.level=v8=debug')
    //args.push('--log.level=communication=debug');
    args.push('--server.storage-engine=' + this.storageEngine);

    if (process.env.ARANGO_EXTRA_ARGS) {
      args = args.concat(process.env.ARANGO_EXTRA_ARGS.split(' '));
    }

    let instance = {
      name,
      role,
      process: null,
      status: 'NEW',
      exitcode: null,
      endpoint,
      args,
      logFn: line => {
        if (line.trim().length > 0) {
          let logLine =
            instance.name + '(' + instance.process.pid + '): \t' + line;
          if (process.env.LOG_IMMEDIATE && process.env.LOG_IMMEDIATE == "1") {
            console.log(logLine);
          } else {
            logLine = logLine.replace(/\x1B/g, '');
            this.currentLog += logLine + '\n';
          }
        }
      }
    };
    return this.runner.firstStart(instance);
  }

  startDbServer(name) {
    this.dbServerCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=PRIMARY',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'primary', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  getAgencyEndpoint() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    })[0].endpoint;
  }

  startCoordinator(name) {
    this.coordinatorCounter++;
    return this.runner
      .createEndpoint()
      .then(endpoint => {
        let args = [
          '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
          '--cluster.my-role=COORDINATOR',
          '--cluster.my-local-info=' + name,
          '--cluster.my-address=' + endpoint
        ];
        return this.startArango(name, endpoint, 'coordinator', args);
      })
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  replace(instance) {
    let name, role;
    switch (instance.role) {
      case 'coordinator':
        this.coordinatorCounter++;
        name = 'coordinator-' + this.coordinatorCounter;
        role = 'COORDINATOR';
        break;
      case 'primary':
        this.dbServerCounter++;
        name = 'dbServer-' + this.dbServerCounter;
        role = 'PRIMARY';
        break;
      default:
        throw new Error('Can only replace coordinators/dbServers');
    }
    if (this.instances.includes(instance)) {
      throw new Error('Instance must be destroyed before it can be replaced');
    }
    let args = [
      '--cluster.agency-endpoint=' + this.getAgencyEndpoint(),
      '--cluster.my-role=' + role,
      '--cluster.my-local-info=' + name,
      '--cluster.my-address=' + instance.endpoint
    ];
    return this.startArango(name, instance.endpoint, instance.role, args)
      .then(instance => this.waitForInstance(instance))
      .then(instance => {
        this.instances.push(instance);
        return instance;
      });
  }

  startAgency(options = {}) {
    let size = options.agencySize || 1;
    if (options.agencyWaitForSync === undefined) {
      options.agencyWaitForSync = false;
    }
    const wfs = options.agencyWaitForSync;
    let promise = Promise.resolve([]);
    let compactionStep = "200";
    let compactionKeep = "100";
    if (process.env.AGENCY_COMPACTION_STEP) {
      compactionStep = process.env.AGENCY_COMPACTION_STEP;
    }
    if (process.env.AGENCY_COMPACTION_KEEP) {
      compactionKeep = process.env.AGENCY_COMPACTION_KEEP;
    }
    for (var i = 0; i < size; i++) {
      promise = promise.then(instances => {
        return this.runner
          .createEndpoint()
          .then(endpoint => {
            let args = [
              '--agency.activate=true',
              '--agency.size=' + size,
              '--agency.pool-size=' + size,
              '--agency.wait-for-sync=true',
              '--agency.supervision=true',
              '--server.threads=16',
              '--agency.supervision-frequency=0.5',
              '--agency.supervision-grace-period=2.5',
              '--agency.compaction-step-size='+compactionStep,
              '--agency.compaction-keep-size='+compactionKeep,
              '--agency.my-address=' + endpoint,
              '--log.force-direct=true'
            ];
            if (instances.length === 0) {
              args.push('--agency.endpoint=' + endpoint);
            } else {
              args.push('--agency.endpoint=' + instances[0].endpoint);
            }
            this.agentCounter++;
            return this.startArango(
              'agency-' + this.agentCounter,
              endpoint,
              'agent',
              args
            );
          })
          .then(instance => {
            return [...instances, instance];
          });
      });
    }
    return promise.then(agents => {
      this.instances = agents;
      return agents;
    });
  }

  findPrimaryDbServer(collectionName) {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    return rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        [
          '/arango/Plan/Collections/_system',
          '/arango/Current/ServersRegistered'
        ]
      ]
    }).then(([info]) => {
      const collections = info.arango.Plan.Collections._system;
      const servers = info.arango.Current.ServersRegistered;
      for (const id of Object.keys(collections)) {
        const collection = collections[id];
        if (collection.name !== collectionName) {
          continue;
        }
        const shards = collection.shards;
        const shardsId = Object.keys(shards)[0];
        const uuid = shards[shardsId][0];
        const endpoint = servers[uuid].endpoint;
        const dbServers = this.dbServers().filter(
          instance => instance.endpoint === endpoint
        );
        if (!dbServers.length) {
          return Promise.reject(new Error(`Unknown endpoint "${endpoint}"`));
        }
        return dbServers[0];
      }
      return Promise.reject(
        new Error(`Unknown collection "${collectionName}"`)
      );
    });
  }

  startCluster(numAgents, numCoordinators, numDbServers, options = {}) {
    let agencyOptions = options.agents || {};
    _.extend(agencyOptions, {agencySize: numAgents});

    return this.startAgency(agencyOptions)
      .then(agents => {
        let promises = [Promise.resolve(agents)];

        let coordinators = Array.from(
          Array(numCoordinators).keys()
        ).map(index => {
          return this.startCoordinator('coordinator-' + (index + 1));
        });
        let dbServers = Array.from(Array(numDbServers).keys()).map(index => {
          return this.startDbServer('dbServer-' + (index + 1));
        });
        return Promise.all([].concat(coordinators, dbServers));
      })
      .then(servers => {
        return this.waitForAllInstances();
      })
      .then(() => {
        return this.getEndpoint();
      });
  }

  async waitForInstance(instance, started = Date.now()) {
    if (instance.status !== 'RUNNING') {
      throw new Error(`Instance ${instance.name} is down!`);
    }
    if (Date.now() - started > WAIT_TIMEOUT * 1000) {
      throw new Error(
        `Instance ${instance.name} is still not ready after ${WAIT_TIMEOUT} secs`
      );
    }

    let ok = false;
    try {
      await rp({
        method: 'GET',
        uri: endpointToUrl(instance.endpoint) + '/_api/version'
      });
      ok = true;
    } catch (e) {}
    if (ok) {
      return instance;
    } else {
      await sleep(100);
      return this.waitForInstance(instance, started);
    }
  }

  waitForAllInstances() {
    return Promise.all(
      this.instances.map(instance => {
        return this.waitForInstance(instance);
      })
    );
  }

  getEndpoint(instance) {
    return (instance || this.coordinators()[0]).endpoint;
  }

  getEndpointUrl(instance) {
    return endpointToUrl(this.getEndpoint(instance));
  }

  check() {
    return this.instances.every(instance => {
      return instance.status === 'RUNNING';
    });
  }

  shutdownCluster() {
    let shutdownPromise;

    let nonAgents = [].concat(this.coordinators(), this.dbServers());

    return Promise.all(nonAgents.map(this.shutdown.bind(this)))
    .then(() => {
      return Promise.all(this.agents().map(this.shutdown.bind(this)))
    });
  }

  cleanup() {
    return this.shutdownCluster()
      .then(() => {
        this.instances = [];
        this.agentCounter = 0;
        this.coordinatorCounter = 0;
        this.dbServerCounter = 0;
        return this.runner.cleanup();
      })
      .then(() => {
        let log = this.currentLog;
        this.currentLog = '';
        return log;
      });
  }

  dbServers() {
    return this.instances.filter(instance => {
      return instance.role === 'primary';
    });
  }

  coordinators() {
    return this.instances.filter(instance => {
      return instance.role === 'coordinator';
    });
  }

  agents() {
    return this.instances.filter(instance => {
      return instance.role === 'agent';
    });
  }

  assignNewEndpoint(instance) {
    let index = this.instances.indexOf(instance);
    if (index === -1) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    var createNewEndpoint = () => {
      return this.runner.createEndpoint().then(endpoint => {
        if (endpoint != instance.endpoint) {
          return endpoint;
        } else {
          return createNewEndpoint();
        }
      });
    };

    return createNewEndpoint().then(endpoint => {
      [
        'server.endpoint',
        'agency.my-address',
        'cluster.my-address'
      ].filter(arg => {
        index = instance.args.indexOf('--' + arg + '=' + instance.endpoint);
        if (index !== -1) {
          instance.args[index] = '--' + arg + '=' + endpoint;
        }
      });
      return this.runner.updateEndpoint(instance, endpoint);
    });
  }

  // beware! signals are not supported on windows and it will simply do hard kills all the time
  // use shutdown to gracefully stop an instance!
  kill(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    instance.process.kill('SIGKILL');
    instance.status = 'KILLED';
    return new Promise((resolve, reject) => {
      let check = function() {
        if (instance.status !== 'EXITED') {
          setTimeout(check, 50);
        } else {
          resolve();
        }
      };
      check();
    });
  }

  shutdown(instance) {
    if (instance.status == 'EXITED') {
      return Promise.resolve(instance);
    }

    let checkDown = function() {
      return new Promise((resolve, reject) => {
        let attempts = 0;
        let maxAttempts = 1200;
        let waitInterval = 50;
        (function checkDown() {
          if (instance.status == 'EXITED') {
            resolve(instance);
          } else if (++attempts > maxAttempts) {
            reject(new Error(instance.name + ' did not stop gracefully after ' + (waitInterval * attempts) + 'ms'));
          } else {
            setTimeout(checkDown, waitInterval);
          }
        })();
      });
    }

    return rp.delete({
      url: this.getEndpointUrl(instance) + '/_admin/shutdown',
    })
    .catch(err => {
      if (err && (err.statusCode === 503 || err.error)) {
        if (err.error.code == 'ECONNREFUSED') {
          console.warn('hmmm...server ' + instance.name + ' did not respond (' + err.code + '). Assuming it is dead. Status is: ' + instance.status);
          return checkDown();
        } else if (err.error.code == 'ECONNRESET') {
          return checkDown();
        } else if (err.statusCode === 503) {
          console.warn('server ' + instance.name + ' answered 503. Assuming it is shutting down. Status is: ' + instance.status);
          return checkDown();
        }
      }
      console.error("Unhandled error", err);

      return Promise.reject(err);
    })
    .then(x => {
      return checkDown();
    })
  }

  destroy(instance) {
    let promise;
    if (this.instances.includes(instance)) {
      promise = this.shutdown(instance);
    } else {
      promise = Promise.resolve();
    }
    return promise.then(() => this.runner.destroy(instance)).then(() => {
      const i = this.instances.indexOf(instance);
      if (i !== -1) {
        this.instances = [
          ...this.instances.slice(0, i),
          ...this.instances.slice(i + 1)
        ];
      }
    });
  }

  restart(instance) {
    if (!this.instances.includes(instance)) {
      throw new Error("Couldn't find instance " + instance.name);
    }

    return this.runner.restart(instance).then(() => {
      return this.waitForInstance(instance);
    });
  }

  // this will append the logs to the test in case of a failure so
  // you get a nice combined log of what happened on the server and client
  moveServerLogs(test) {
    if (test.state === 'failed') {
      test.err.message = this.currentLog + '\n\n' + test.err.message;
    }
    this.currentLog = '';
  }

  async getFoxxmaster() {
    const baseUrl = endpointToUrl(this.getAgencyEndpoint());
    const [info] = await rp({
      method: 'POST',
      uri: baseUrl + '/_api/agency/read',
      json: true,
      body: [
        ['/arango/Current/Foxxmaster', '/arango/Current/ServersRegistered']
      ]
    });
    const uuid = info.arango.Current.Foxxmaster;
    const endpoint = info.arango.Current.ServersRegistered[uuid].endpoint;
    return this.instances.find(instance => instance.endpoint === endpoint);
  }

  async restartCluster() {
    const fm = await this.getFoxxmaster();
    await this.shutdownCluster();
    await Promise.all(this.agents().map(agent => this.restart(agent)));
    await Promise.all(this.dbServers().map(dbs => this.restart(dbs)));
    this.restart(fm);
    await sleep(100);
    await Promise.all(
      this.coordinators()
        .filter(coord => coord !== fm)
        .map(coord => this.restart(coord))
    );
    await this.waitForAllInstances();
  }
}

module.exports = InstanceManager;
<|endoftext|>


<commit_before>var version = require('../package.json').version;
var os = require("os");

if (process.env.CIRCLE_BUILD_NUM && !process.env.CIRCLE_TAG) {
  version += [ '', process.env.CIRCLE_BUILD_NUM, process.env.CIRCLE_BRANCH].join('.')
} else if (process.env.CIRCLECI != 'true') {
  version += [ '', os.userInfo().username, os.hostname() ].join('.')
}
module.exports = version;
<commit_msg>issue numbers in version<commit_after>var version = require('../package.json').version;
var os = require("os");

if (process.env.CIRCLE_BUILD_NUM && !process.env.CIRCLE_TAG) {
  var branch = process.env.CIRCLE_BRANCH;
  if (branch =~ /^[0-9]+/) branch = 'issue-' + branch;
  version += [ '', process.env.CIRCLE_BUILD_NUM, branch].join('.')
} else if (process.env.CIRCLECI != 'true') {
  version += [ '', os.userInfo().username, os.hostname() ].join('.')
}
module.exports = version;
<|endoftext|>


<commit_before>/*
 * Copyright 2012 Amadeus s.a.s.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var Aria = require("../../Aria");
var ariaWidgetsFormDropDownListTrait = require("./DropDownListTrait");
var ariaWidgetsControllersAutoCompleteController = require("../controllers/AutoCompleteController");
var ariaUtilsEvent = require("../../utils/Event");
var ariaUtilsFunction = require("../../utils/Function");
var ariaWidgetsFormAutoCompleteStyle = require("./AutoCompleteStyle.tpl.css");
var ariaWidgetsFormListListStyle = require("./list/ListStyle.tpl.css");
var ariaWidgetsContainerDivStyle = require("../container/DivStyle.tpl.css");
var ariaWidgetsFormDropDownTextInput = require("./DropDownTextInput");
var ariaCoreBrowser = require("../../core/Browser");

/**
 * AutoComplete widget
 */
module.exports = Aria.classDefinition({
    $classpath : "aria.widgets.form.AutoComplete",
    $extends : ariaWidgetsFormDropDownTextInput,
    $css : [ariaWidgetsFormAutoCompleteStyle, ariaWidgetsFormListListStyle,
            ariaWidgetsContainerDivStyle],
    /**
     * AutoComplete constructor
     * @param {aria.widgets.CfgBeans:AutoCompleteCfg} cfg the widget configuration
     * @param {aria.templates.TemplateCtxt} ctxt template context
     * @param {Number} lineNumber Line number corresponding in the .tpl file where the widget is created
     * @param {Number} controller the data controller object
     */
    $constructor : function (cfg, ctxt, lineNumber, controller) {
        var controllerInstance = controller || new ariaWidgetsControllersAutoCompleteController();

        if (!cfg.expandButton && cfg.bind) {
            delete cfg.bind.popupOpen;
        }

        this.$DropDownTextInput.constructor.call(this, cfg, ctxt, lineNumber, controllerInstance);

        if (!cfg.expandButton) {
            /**
             * Array of icon names which need to be hidden.
             * @type Array
             * @protected
             * @override
             */
            this._hideIconNames = ["dropdown"];
        }

        try {
            controllerInstance.setResourcesHandler(cfg.resourcesHandler);
        } catch (e) {
            this.$logError(this.WIDGET_AUTOCOMPLETE_INVALID_HANDLER, [cfg.resourcesHandler], e);
        }
        controllerInstance.autoFill = cfg.autoFill;
        controllerInstance.freeText = cfg.freeText;
        controllerInstance.maxlength = cfg.maxlength;
        controllerInstance.expandButton = cfg.expandButton;
        controllerInstance.selectionKeys = cfg.selectionKeys;
        controllerInstance.preselect = cfg.preselect;
        if (cfg.waiAria && cfg.waiSuggestionAriaLabelGetter) {
            controllerInstance.waiSuggestionAriaLabelGetter = ariaUtilsFunction.bind(this._waiSuggestionAriaLabelGetter, this);
        }

        /**
         * Whether the width of the popup can be smaller than the field, when configured to be so. If false, the
         * popupWidth property will be overridden when it is smaller than the field width
         * @type Boolean
         * @protected
         */
        this._freePopupWidth = false;
        this._waiSuggestionsChangedListener = null;
    },
    $destructor : function () {
        this._removeWaiSuggestionsChangedListener();

        // The dropdown might still be open when we destroy the widget, destroy it now
        if (this._dropdownPopup) {
            this._dropdownPopup.$removeListeners({
                "onBeforeClose" : this._beforeDropdownClose,
                scope : this
            });
        }

        // There might be some events
        if (this._initDone) {
            this._removeEvents();
        }

        this.$DropDownTextInput.$destructor.call(this);
    },
    $statics : {
        // ERROR MESSAGE:
        WIDGET_AUTOCOMPLETE_INVALID_HANDLER : "%1Could not create resources handler %2: dependency on this handler is missing."
    },
    $prototype : {
        /**
         * Skinnable class to use for this widget.
         * @protected
         * @type String
         */
        _skinnableClass : "AutoComplete",

        /**
         * Prototype init method called at prototype creation time Allows to store class-level objects that are shared
         * by all instances
         * @param {Object} p the prototype object being built
         */
        $init : function (p) {
            var src = ariaWidgetsFormDropDownListTrait.prototype;
            for (var key in src) {
                if (src.hasOwnProperty(key) && !p.hasOwnProperty(key)) {
                    // copy methods which are not already on this object (this avoids copying $classpath and
                    // $destructor)
                    p[key] = src[key];
                }
            }
        },

        /**
         * Internal function to render the content of the dropdown div
         * @protected
         * @param {aria.templates.MarkupWriter} out Markup writer which should receive the content of the popup.
         * @param {Object} arg Optional parameters
         */
        _renderDropdownContent : function (out, options) {
            options = options || {};
            var cfg = this._cfg;
            if (!("defaultTemplate" in options)) {
                if (cfg.suggestionsTemplate) {
                    options.defaultTemplate = cfg.suggestionsTemplate;
                } else {
                    options.defaultTemplate = this.controller.getDefaultTemplate();
                }
            }
            if (!("minWidth" in options)) {
                var inputMarkupWidth = this._inputMarkupWidth;
                // set a minimum size for the autocomplete content
                inputMarkupWidth = (inputMarkupWidth < 150) ? 150 : inputMarkupWidth;
                options.minWidth = inputMarkupWidth + this._skinObj.offsetRight;
            }
            options.maxHeight = this._cfg.popupMaxHeight || 210;
            if (cfg.waiAria) {
                options.onchange = {
                    scope: this,
                    fn: this._updateAriaActiveDescendant,
                    args: options.onchange
                };
            }
            this.$DropDownListTrait._renderDropdownContent.call(this, out, options);
        },

        /**
         * Override $DropDownTextInput._reactToControllerReport
         * @protected
         * @param {aria.widgets.controllers.reports.DropDownControllerReport} report
         * @param {Object} arg Optional parameters
         */
        _reactToControllerReport : function (report, arg) {
            // To reset focus if there is no suggestion dropdown
            if (!this.controller._resetFocus) {
                this._keepFocus = false;
            }
            this.$DropDownTextInput._reactToControllerReport.call(this, report, arg);
        },

        /**
         * DOM callback function called when the focus is taken off the input. The onBlur event is available on the
         * input that sits inside a span.
         * @param {aria.DomEvent} event Blur event
         * @protected
         */
        _dom_onblur : function (event, avoidCallback) {
            this._keepFocus = false;
            this.$DropDownTextInput._dom_onblur.call(this, event, avoidCallback);
        },

        _dom_onkeydown : function (event) {
            // On Shift+F10, when AutoComplete has no expand button enabled, we don't open the dropdown, we only close it
            if (this._isShiftF10Pressed(event) && !this._cfg.expandButton && this._dropdownPopup == null) {
                return;
            }

            this.$DropDownTextInput._dom_onkeydown.apply(this, arguments);
        },

         /**
         * Internal method to handle the onkeyup event. This is called to set the value property in the data model
         * through the setProperty method that also handles all other widgets bound to this value.
         * @protected
         */
        _dom_onkeyup : function (event) {
            this.$DropDownTextInput._dom_onkeyup.call(this, event);
            if (event.keyCode == event.KC_ENTER) {
                this.checkValue();
            }
        },

        /**
         * React to a dropdown close. If the widget is using autofill we want to select the pre-selected value in the
         * datamodel and report it to the input field also when the user clicks away from the field instead of
         * navigating through TAB or selecting an item from the dropdown. This function is called any time we close the
         * dropdown, even when typing there are no results.
         * @param {Object} event Event that triggered this callback
         * @private
         */
        _beforeDropdownClose : function (event) {
            if (this._cfg.autoFill && event.domEvent) {
                // Closing the dropdown after typing is not a domEvent
                var value = this.controller.getDataModel().value;
                if (value != null) {
                    var report = this.controller.checkDropdownValue(value);
                    this._reactToControllerReport(report);
                }
            }
        },

        /**
         * Updates the aria-activedescendant attribute on the input DOM element.
         * This method supposes that the popup is open.
         * This method is registered as the onchange callback for the list widget,
         * if accessibility is enabled. It is also called from _afterDropdownOpen.
         */
        _updateAriaActiveDescendant : function(event, cb) {
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                var listWidget = this.controller.getListWidget();
                var ariaActiveDescendant = listWidget ? listWidget.getSelectedOptionDomId() : null;
                if (ariaActiveDescendant != null) {
                    field.setAttribute("aria-activedescendant", ariaActiveDescendant);
                } else {
                    field.removeAttribute("aria-activedescendant");
                }
            }
            if (cb) {
                this.$callback(cb, event);
            }
        },

        /**
         * Callback for the event onAfterOpen raised by the popup.
         * @override
         */
        _afterDropdownOpen : function () {
            this.$DropDownListTrait._afterDropdownOpen.apply(this, arguments);
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                var listWidget = this.controller.getListWidget();
                field.setAttribute("aria-owns", listWidget.getListDomId());
                var dropDownIcon = this._getDropdownIcon();
                if (dropDownIcon) {
                    dropDownIcon.setAttribute("aria-expanded", "true");
                }
                listWidget.afterWidgetLoaded({
                    fn: this._updateAriaActiveDescendant,
                    scope: this
                });
            }
        },
        /**
         * @override
         */
        _waiSuggestionsChanged : function () {
            var self = this;
            setTimeout(function () {
                self._updateAriaActiveDescendant();
            }, 0);
            this.$DropDownListTrait._waiSuggestionsChanged.apply(this, arguments);
        },

        /**
         * Called after the dropdown is closed.
         * @override
         */
        _afterDropdownClose : function () {
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                field.removeAttribute("aria-activedescendant");
                field.removeAttribute("aria-owns");

                var dropDownIcon = this._getDropdownIcon();
                if (dropDownIcon) {
                    dropDownIcon.setAttribute("aria-expanded", "false");
                }
           }
            this.$DropDownListTrait._afterDropdownClose.apply(this, arguments);
        },

        /**
         * Override _init function to add event listeners that cannot be delegated on IE
         * @method
         * @override
         */
        _init : ariaCoreBrowser.isOldIE ? function () {
            this.$DropDownTextInput._init.call(this);

            var field = this.getTextInputField();
            ariaUtilsEvent.addListener(field, "paste", {
                fn : this._dom_onpaste,
                scope : this
            });
            ariaUtilsEvent.addListener(field, "cut", {
                fn : this._dom_oncut,
                scope : this
            });
        } : function () {
            this.$DropDownTextInput._init.call(this);
        },

        /**
         * Remove event listeners added during init. This is done for IE in order to support copy/paste
         * @method
         * @protected
         */
        _removeEvents : ariaCoreBrowser.isOldIE ? function () {
            var field = this.getTextInputField();
            ariaUtilsEvent.removeListener(field, "paste");
            ariaUtilsEvent.removeListener(field, "cut");
        } : function () {},

        /**
         * Internal method to handle the paste event. This event is converted as a Ctrl+v and handled as a keydown in
         * order to set the datamodel and open the dropdown
         * @param {aria.DomEvent} event Event object
         * @protected
         */
        _dom_onpaste : function (event) {
            this.__propagateKeyDown(event);
        },

        /**
         * Internal method to handle the cut event. This event is converted as a Ctrl+x and handled as a keydown in
         * order to set the datamodel and close the dropdown.
         * @param {aria.DomEvent} event Event object
         * @protected
         */
        _dom_oncut : function (event) {
            this.__propagateKeyDown(event);
        },

        /**
         * Convert mouse event and browser copy/paste (contextual menu event) into a keydown event that can be handled
         * by the controller
         * @param {aria.DomEvent} event Event object, in IE this is an instance of HTMLElement
         * @private
         */
        __propagateKeyDown : function (event) {
            var toBeDisposed = false;
            if (!event.$DomEvent) {
                event = new aria.DomEvent(event);
                toBeDisposed = true;
            }

            event.isSpecialKey = true;
            event.ctrlKey = true;
            event.charCode = 0;

            this._dom_onkeydown.call(this, event);

            if (toBeDisposed) {
                event.$dispose();
            }
        },
        /**
         * Internal method called when one of the model property that the widget is bound to has changed Must be
         * overridden by sub-classes defining bindable properties
         * @param {String} propertyName the property name
         * @param {Object} newValue the new value
         * @param {Object} oldValue the old property value
         * @protected
         */
        _onBoundPropertyChange : function (propertyName, newValue, oldValue) {
            if (propertyName != "popupOpen" || this._cfg.expandButton) {
                this.$DropDownTextInput._onBoundPropertyChange.apply(this, arguments);
            }
        },

        /**
         * Initialization method called by the delegate engine when the DOM is loaded
         */
        initWidget : function () {
            if (!this._cfg.expandButton && this._cfg.popupOpen) {
                this._cfg.popupOpen = false;
            }
            this.$DropDownTextInput.initWidget.call(this);
        }
    }
});
<commit_msg>fix #1810 Screen reader reading previously focused autocomplete field on tab<commit_after>/*
 * Copyright 2012 Amadeus s.a.s.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var Aria = require("../../Aria");
var ariaWidgetsFormDropDownListTrait = require("./DropDownListTrait");
var ariaWidgetsControllersAutoCompleteController = require("../controllers/AutoCompleteController");
var ariaUtilsEvent = require("../../utils/Event");
var ariaUtilsFunction = require("../../utils/Function");
var ariaWidgetsFormAutoCompleteStyle = require("./AutoCompleteStyle.tpl.css");
var ariaWidgetsFormListListStyle = require("./list/ListStyle.tpl.css");
var ariaWidgetsContainerDivStyle = require("../container/DivStyle.tpl.css");
var ariaWidgetsFormDropDownTextInput = require("./DropDownTextInput");
var ariaCoreBrowser = require("../../core/Browser");

/**
 * AutoComplete widget
 */
module.exports = Aria.classDefinition({
    $classpath : "aria.widgets.form.AutoComplete",
    $extends : ariaWidgetsFormDropDownTextInput,
    $css : [ariaWidgetsFormAutoCompleteStyle, ariaWidgetsFormListListStyle,
            ariaWidgetsContainerDivStyle],
    /**
     * AutoComplete constructor
     * @param {aria.widgets.CfgBeans:AutoCompleteCfg} cfg the widget configuration
     * @param {aria.templates.TemplateCtxt} ctxt template context
     * @param {Number} lineNumber Line number corresponding in the .tpl file where the widget is created
     * @param {Number} controller the data controller object
     */
    $constructor : function (cfg, ctxt, lineNumber, controller) {
        var controllerInstance = controller || new ariaWidgetsControllersAutoCompleteController();

        if (!cfg.expandButton && cfg.bind) {
            delete cfg.bind.popupOpen;
        }

        this.$DropDownTextInput.constructor.call(this, cfg, ctxt, lineNumber, controllerInstance);

        if (!cfg.expandButton) {
            /**
             * Array of icon names which need to be hidden.
             * @type Array
             * @protected
             * @override
             */
            this._hideIconNames = ["dropdown"];
        }

        try {
            controllerInstance.setResourcesHandler(cfg.resourcesHandler);
        } catch (e) {
            this.$logError(this.WIDGET_AUTOCOMPLETE_INVALID_HANDLER, [cfg.resourcesHandler], e);
        }
        controllerInstance.autoFill = cfg.autoFill;
        controllerInstance.freeText = cfg.freeText;
        controllerInstance.maxlength = cfg.maxlength;
        controllerInstance.expandButton = cfg.expandButton;
        controllerInstance.selectionKeys = cfg.selectionKeys;
        controllerInstance.preselect = cfg.preselect;
        if (cfg.waiAria && cfg.waiSuggestionAriaLabelGetter) {
            controllerInstance.waiSuggestionAriaLabelGetter = ariaUtilsFunction.bind(this._waiSuggestionAriaLabelGetter, this);
        }

        /**
         * Whether the width of the popup can be smaller than the field, when configured to be so. If false, the
         * popupWidth property will be overridden when it is smaller than the field width
         * @type Boolean
         * @protected
         */
        this._freePopupWidth = false;
        this._waiSuggestionsChangedListener = null;
    },
    $destructor : function () {
        this._removeWaiSuggestionsChangedListener();

        // The dropdown might still be open when we destroy the widget, destroy it now
        if (this._dropdownPopup) {
            this._dropdownPopup.$removeListeners({
                "onBeforeClose" : this._beforeDropdownClose,
                scope : this
            });
        }

        // There might be some events
        if (this._initDone) {
            this._removeEvents();
        }

        this.$DropDownTextInput.$destructor.call(this);
    },
    $statics : {
        // ERROR MESSAGE:
        WIDGET_AUTOCOMPLETE_INVALID_HANDLER : "%1Could not create resources handler %2: dependency on this handler is missing."
    },
    $prototype : {
        /**
         * Skinnable class to use for this widget.
         * @protected
         * @type String
         */
        _skinnableClass : "AutoComplete",

        /**
         * Prototype init method called at prototype creation time Allows to store class-level objects that are shared
         * by all instances
         * @param {Object} p the prototype object being built
         */
        $init : function (p) {
            var src = ariaWidgetsFormDropDownListTrait.prototype;
            for (var key in src) {
                if (src.hasOwnProperty(key) && !p.hasOwnProperty(key)) {
                    // copy methods which are not already on this object (this avoids copying $classpath and
                    // $destructor)
                    p[key] = src[key];
                }
            }
        },

        /**
         * Internal function to render the content of the dropdown div
         * @protected
         * @param {aria.templates.MarkupWriter} out Markup writer which should receive the content of the popup.
         * @param {Object} arg Optional parameters
         */
        _renderDropdownContent : function (out, options) {
            options = options || {};
            var cfg = this._cfg;
            if (!("defaultTemplate" in options)) {
                if (cfg.suggestionsTemplate) {
                    options.defaultTemplate = cfg.suggestionsTemplate;
                } else {
                    options.defaultTemplate = this.controller.getDefaultTemplate();
                }
            }
            if (!("minWidth" in options)) {
                var inputMarkupWidth = this._inputMarkupWidth;
                // set a minimum size for the autocomplete content
                inputMarkupWidth = (inputMarkupWidth < 150) ? 150 : inputMarkupWidth;
                options.minWidth = inputMarkupWidth + this._skinObj.offsetRight;
            }
            options.maxHeight = this._cfg.popupMaxHeight || 210;
            if (cfg.waiAria) {
                options.onchange = {
                    scope: this,
                    fn: this._updateAriaActiveDescendant,
                    args: options.onchange
                };
            }
            this.$DropDownListTrait._renderDropdownContent.call(this, out, options);
        },

        /**
         * Override $DropDownTextInput._reactToControllerReport
         * @protected
         * @param {aria.widgets.controllers.reports.DropDownControllerReport} report
         * @param {Object} arg Optional parameters
         */
        _reactToControllerReport : function (report, arg) {
            // To reset focus if there is no suggestion dropdown
            if (!this.controller._resetFocus) {
                this._keepFocus = false;
            }
            this.$DropDownTextInput._reactToControllerReport.call(this, report, arg);
        },

        /**
         * DOM callback function called when the focus is taken off the input. The onBlur event is available on the
         * input that sits inside a span.
         * @param {aria.DomEvent} event Blur event
         * @protected
         */
        _dom_onblur : function (event, avoidCallback) {
            this._keepFocus = false;
            this.$DropDownTextInput._dom_onblur.call(this, event, avoidCallback);
        },

        _dom_onkeydown : function (event) {
            // On Shift+F10, when AutoComplete has no expand button enabled, we don't open the dropdown, we only close it
            if (this._isShiftF10Pressed(event) && !this._cfg.expandButton && this._dropdownPopup == null) {
                return;
            }

            this.$DropDownTextInput._dom_onkeydown.apply(this, arguments);
        },

         /**
         * Internal method to handle the onkeyup event. This is called to set the value property in the data model
         * through the setProperty method that also handles all other widgets bound to this value.
         * @protected
         */
        _dom_onkeyup : function (event) {
            this.$DropDownTextInput._dom_onkeyup.call(this, event);
            if (event.keyCode == event.KC_ENTER) {
                this.checkValue();
            }
        },

        /**
         * React to a dropdown close. If the widget is using autofill we want to select the pre-selected value in the
         * datamodel and report it to the input field also when the user clicks away from the field instead of
         * navigating through TAB or selecting an item from the dropdown. This function is called any time we close the
         * dropdown, even when typing there are no results.
         * @param {Object} event Event that triggered this callback
         * @private
         */
        _beforeDropdownClose : function (event) {
            if (this._cfg.autoFill && event.domEvent) {
                // Closing the dropdown after typing is not a domEvent
                var value = this.controller.getDataModel().value;
                if (value != null) {
                    var report = this.controller.checkDropdownValue(value);
                    this._reactToControllerReport(report);
                }
            }
        },

        /**
         * Updates the aria-activedescendant attribute on the input DOM element.
         * This method supposes that the popup is open.
         * This method is registered as the onchange callback for the list widget,
         * if accessibility is enabled. It is also called from _afterDropdownOpen.
         */
        _updateAriaActiveDescendant : function(event, cb) {
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                var listWidget = this.controller.getListWidget();
                var ariaActiveDescendant = listWidget ? listWidget.getSelectedOptionDomId() : null;
                if (ariaActiveDescendant != null) {
                    field.setAttribute("aria-activedescendant", ariaActiveDescendant);
                } else {
                    field.removeAttribute("aria-activedescendant");
                }
            }
            if (cb) {
                this.$callback(cb, event);
            }
        },

        /**
         * Callback for the event onAfterOpen raised by the popup.
         * @override
         */
        _afterDropdownOpen : function () {
            this.$DropDownListTrait._afterDropdownOpen.apply(this, arguments);
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                var listWidget = this.controller.getListWidget();
                field.setAttribute("aria-owns", listWidget.getListDomId());
                var dropDownIcon = this._getDropdownIcon();
                if (dropDownIcon) {
                    dropDownIcon.setAttribute("aria-expanded", "true");
                }
                listWidget.afterWidgetLoaded({
                    fn: this._updateAriaActiveDescendant,
                    scope: this
                });
            }
        },
        /**
         * @override
         */
        _waiSuggestionsChanged : function () {
            var self = this;
            setTimeout(function () {
                self._updateAriaActiveDescendant();
            }, 0);
            this.$DropDownListTrait._waiSuggestionsChanged.apply(this, arguments);
        },

        /**
         * Called after the dropdown is closed.
         * @override
         */
        _afterDropdownClose : function () {
            if (this._cfg.waiAria) {
                var field = this.getTextInputField();
                field.removeAttribute("aria-owns");

                var dropDownIcon = this._getDropdownIcon();
                if (dropDownIcon) {
                    dropDownIcon.setAttribute("aria-expanded", "false");
                }
           }
            this.$DropDownListTrait._afterDropdownClose.apply(this, arguments);
        },

        /**
         * Override _init function to add event listeners that cannot be delegated on IE
         * @method
         * @override
         */
        _init : ariaCoreBrowser.isOldIE ? function () {
            this.$DropDownTextInput._init.call(this);

            var field = this.getTextInputField();
            ariaUtilsEvent.addListener(field, "paste", {
                fn : this._dom_onpaste,
                scope : this
            });
            ariaUtilsEvent.addListener(field, "cut", {
                fn : this._dom_oncut,
                scope : this
            });
        } : function () {
            this.$DropDownTextInput._init.call(this);
        },

        /**
         * Remove event listeners added during init. This is done for IE in order to support copy/paste
         * @method
         * @protected
         */
        _removeEvents : ariaCoreBrowser.isOldIE ? function () {
            var field = this.getTextInputField();
            ariaUtilsEvent.removeListener(field, "paste");
            ariaUtilsEvent.removeListener(field, "cut");
        } : function () {},

        /**
         * Internal method to handle the paste event. This event is converted as a Ctrl+v and handled as a keydown in
         * order to set the datamodel and open the dropdown
         * @param {aria.DomEvent} event Event object
         * @protected
         */
        _dom_onpaste : function (event) {
            this.__propagateKeyDown(event);
        },

        /**
         * Internal method to handle the cut event. This event is converted as a Ctrl+x and handled as a keydown in
         * order to set the datamodel and close the dropdown.
         * @param {aria.DomEvent} event Event object
         * @protected
         */
        _dom_oncut : function (event) {
            this.__propagateKeyDown(event);
        },

        /**
         * Convert mouse event and browser copy/paste (contextual menu event) into a keydown event that can be handled
         * by the controller
         * @param {aria.DomEvent} event Event object, in IE this is an instance of HTMLElement
         * @private
         */
        __propagateKeyDown : function (event) {
            var toBeDisposed = false;
            if (!event.$DomEvent) {
                event = new aria.DomEvent(event);
                toBeDisposed = true;
            }

            event.isSpecialKey = true;
            event.ctrlKey = true;
            event.charCode = 0;

            this._dom_onkeydown.call(this, event);

            if (toBeDisposed) {
                event.$dispose();
            }
        },
        /**
         * Internal method called when one of the model property that the widget is bound to has changed Must be
         * overridden by sub-classes defining bindable properties
         * @param {String} propertyName the property name
         * @param {Object} newValue the new value
         * @param {Object} oldValue the old property value
         * @protected
         */
        _onBoundPropertyChange : function (propertyName, newValue, oldValue) {
            if (propertyName != "popupOpen" || this._cfg.expandButton) {
                this.$DropDownTextInput._onBoundPropertyChange.apply(this, arguments);
            }
        },

        /**
         * Initialization method called by the delegate engine when the DOM is loaded
         */
        initWidget : function () {
            if (!this._cfg.expandButton && this._cfg.popupOpen) {
                this._cfg.popupOpen = false;
            }
            this.$DropDownTextInput.initWidget.call(this);
        }
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import Route from 'react-router/lib/Route';

import store from '../store';

import { accountGetUserInfo } from './actions';
import { LoginContainer } from './components/Login';
import { LogoutContainer } from './components/Logout';
import { ProfileContainer } from './components/Profile';
import { RegisterContainer } from './components/Register';
import { PasswordContainer } from './components/Password';
import { PasswordResetContainer } from './components/PasswordReset';
import { PasswordResetConfirmContainer } from './components/PasswordResetConfirm';
import { ActivateContainer } from './components/Activate';

function recoverAuthToken() {
  let authToken = store.getState().user.get('auth_token');

  if (!authToken) {
    authToken = window.sessionStorage.getItem('auth_token') ||
      window.localStorage.getItem('auth_token');

    if (authToken) {
      store.dispatch({
        type: 'SET_TOKEN',
        response: { auth_token: authToken },
      });
      store.dispatch(accountGetUserInfo());
    }
  }

  return authToken ? true : false;
}

export function checkAuth(nextState, replaceState) {
  if (recoverAuthToken()) {
    replaceState(null, '/dashboard/');
  }
}

export function requireAuth(nextState, replaceState) {
  if (!recoverAuthToken()) {
    replaceState({ nextPathname: nextState.location.pathname }, '/account/login/');
  }
}

export default (

    <Route path="/account/login/" component={ LoginContainer } />
    <Route path="/account/logout/" component={ LogoutContainer } onEnter={ requireAuth } />
    <Route path="/account/register/" component={ RegisterContainer } />
    <Route path="/account/profile/" component={ ProfileContainer } onEnter={ requireAuth } />
    <Route path="/account/password/" component={ PasswordContainer } onEnter={ requireAuth } />
    <Route
      path="/account/password/reset/"
      component={ PasswordResetContainer }
      onEnter={ requireAuth }
    />
    <Route
      path="/account/password/reset/confirm/:uid/:token/"
      component={ PasswordResetConfirmContainer }
      onEnter={ requireAuth }
    />
    <Route path="/account/activate/:uid/:token/" component={ ActivateContainer } />

);
<commit_msg>Remove required login from password reset component<commit_after>import React from 'react';
import Route from 'react-router/lib/Route';

import store from '../store';

import { accountGetUserInfo } from './actions';
import { LoginContainer } from './components/Login';
import { LogoutContainer } from './components/Logout';
import { ProfileContainer } from './components/Profile';
import { RegisterContainer } from './components/Register';
import { PasswordContainer } from './components/Password';
import { PasswordResetContainer } from './components/PasswordReset';
import { PasswordResetConfirmContainer } from './components/PasswordResetConfirm';
import { ActivateContainer } from './components/Activate';

function recoverAuthToken() {
  let authToken = store.getState().user.get('auth_token');

  if (!authToken) {
    authToken = window.sessionStorage.getItem('auth_token') ||
      window.localStorage.getItem('auth_token');

    if (authToken) {
      store.dispatch({
        type: 'SET_TOKEN',
        response: { auth_token: authToken },
      });
      store.dispatch(accountGetUserInfo());
    }
  }

  return authToken ? true : false;
}

export function checkAuth(nextState, replaceState) {
  if (recoverAuthToken()) {
    replaceState(null, '/dashboard/');
  }
}

export function requireAuth(nextState, replaceState) {
  if (!recoverAuthToken()) {
    replaceState({ nextPathname: nextState.location.pathname }, '/account/login/');
  }
}

export default (

    <Route path="/account/login/" component={ LoginContainer } />
    <Route path="/account/logout/" component={ LogoutContainer } onEnter={ requireAuth } />
    <Route path="/account/register/" component={ RegisterContainer } />
    <Route path="/account/profile/" component={ ProfileContainer } onEnter={ requireAuth } />
    <Route path="/account/password/" component={ PasswordContainer } onEnter={ requireAuth } />
    <Route path="/account/password/reset/" component={ PasswordResetContainer } />
    <Route
      path="/account/password/reset/confirm/:uid/:token/"
      component={ PasswordResetConfirmContainer }
      onEnter={ requireAuth }
    />
    <Route path="/account/activate/:uid/:token/" component={ ActivateContainer } />

);
<|endoftext|>