<commit_before>// \todo aggregate vars into single 'world' var?
var mapDinos = {};
var map;
var ajaxRequest;
var plotlist;
var plotlayers=[];
var serverTimeDiff = 0;

function ajax(path, f) {
	var xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
		if (this.readyState == 4 && this.status == 200) {
		  f(this.responseText)
		}
	};
	xhttp.open("GET", path, true);
	xhttp.send();
}

// queryLatlon does an API query on the given latlon, and calls f with the data
function queryLatLon(lat, lon, f) {
	ajax("/query/" + lat + "/" + lon, function(data) {
		f(JSON.parse(data));
	})
}

function addDinosaursToMap(dinosaurs, map) {
	console.log('addDinosaursToMap ' + dinosaurs.length);
	var dinosaursLen = dinosaurs.length;
	for (var i = 0; i < dinosaursLen; i++) {
		addDinosaurToMap(dinosaurs[i], map);
	}
}

// durationStr returns the hours, minutes, and seconds left in the milliseconds integer `duration`, in the format '4h20m3s'
function durationShortStr(duration) {
	var ds = duration / 1000;
	var h = Math.floor(ds / 60 / 60);
	var m = Math.floor(ds / 60 % 60);
	var s = Math.floor(ds % 60);
	var str = "";
	if(h > 0) {
		str += h + "h";
	}
	if(m > 0) {
		str += m + "m";
	}
	str += s + "s";
	return str;
}

function getDinoPopupStr(dino) {
	var dinoExpireTime = Date.parse(dino.Expiration);
	var dinoExpireFromNowMs = dinoExpireTime - Date.now() - serverTimeDiff;
	return "" + dino.ID + " " + dino.Dinosaur.Name + "" + "
" +
		"Time left: " + durationShortStr(dinoExpireFromNowMs);
}

function addDinosaurToMap(dino, map) {
  console.log('addDinosaurToMap ' + dino.ID);
	if(mapDinos.hasOwnProperty(dino.ID)) {
    console.log('addDinosaurToMap HAS ' + dino.ID);
		return
	}
  console.log('addDinosaurToMap NEW ' + dino.ID);

	console.log("adding " + dino.ID + " specie" + dino.Dinosaur.Name + " expiration " + dino.Expiration);

	mapDinos[dino.ID] = dino; // TODO change to store a bool or something, if dino isn't needed
	var name = dino.Dinosaur.Name;
	// \todo add image path to specie endpoint?
	var imagePath = "/images/" + name.toLowerCase() + ".png";

	// \todo add image dimensions to specie endpoint?
	// \todo store specie icons, for speed?
	var dinoIcon = L.icon({
		dinosaur: dino,
		iconUrl: imagePath,
    //				 shadowUrl: 'leaf-shadow.png',
		iconSize:     [64, 64], // size of the icon
    //				 shadowSize:   [5, 64], // size of the shadow
		iconAnchor:   [32, 32], // point of the icon which will correspond to marker's location
    //				 shadowAnchor: [4, 62],  // the same for the shadow
		popupAnchor:  [-5, -5] // point from which the popup should open relative to the iconAnchor
	});

	var dinoExpireTime = Date.parse(dino.Expiration);
	var dinoExpireFromNowMs = dinoExpireTime - Date.now() - serverTimeDiff;

  console.log('addDinosaurToMap dinoExpireFromNowMs ' + dinoExpireFromNowMs);
  console.log('addDinosaurToMap dinoExpireTime ' + dinoExpireTime);
  console.log('addDinosaurToMap now.getTime() ' + serverTimeDiff);
  console.log('addDinosaurToMap serverTimeDiff ' + serverTimeDiff);
	if(dinoExpireFromNowMs > 0) {
		var marker = L.marker([dino.Latitude, dino.Longitude], {icon: dinoIcon});
		var popupStr = "" + dino.ID + " " + dino.Dinosaur.Name + "" + "
" +
		    "Time left: " + durationShortStr(dinoExpireFromNowMs);
		marker.bindPopup(popupStr).openPopup();
		marker.getPopup().Dinosaur = dino;
		marker.addTo(map);

		window.setTimeout(function() {
		  map.removeLayer(marker);
		  delete mapDinos[dino.ID];
		  console.log("removing " + dino.ID);
		}, dinoExpireFromNowMs);
	}
}

function getDinosaursHere() {
	var latLng = map.getCenter();
	var lat = latLng.lat;
	var lon = latLng.lng;
	queryLatLon(lat, lon, function(dinosaurs) {
		addDinosaursToMap(dinosaurs, map);
	})
}

var popupInterval;
function initmap() {
	map = new L.Map('mapid');

	// create the tile layer with correct attribution
	var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
	var osmAttrib='Map data © OpenStreetMap contributors';
	var osm = new L.TileLayer(osmUrl, {attribution: osmAttrib});

	var defaultLat = 39.7392;
	var defaultLon = -104.9903;
	var defaultZoom = 15

	map.setView(new L.LatLng(defaultLat, defaultLon), defaultZoom);
	map.addLayer(osm);

	map.locate({setView: true, maxZoom: 18});

  map.on('move', getDinosaursHere);
	map.on('moveend', function() {
		console.log("map moved - calling getDinosaursHere()");
		getDinosaursHere();
	});
	map.on('popupopen', function(popupEvent) {
		var popup = popupEvent.popup;
		var dino = popup.Dinosaur;
		if(dino) {
			popup.setContent(getDinoPopupStr(dino));
			console.log('setting interval ' + dino.ID)
			popup.UpdateInterval = window.setInterval(function() {
				popup.setContent(getDinoPopupStr(dino));
			}, 1000);
		}
	})
	map.on('popupclose', function(popupEvent) {
		var popup = popupEvent.popup;
		if(popup.UpdateInterval) {
			clearInterval(popup.UpdateInterval)
			console.log('clearing interval ' + popup.Dinosaur.ID)
		}
	})
  getDinosaursHere();

}

function setLocation() {
	if (navigator.geolocation) {
		navigator.geolocation.getCurrentPosition(setPosition);
	}
}
function setPosition(position) {
	console.log('' + position.coords.latitude + ' ' + position.coords.longitude);
	map.setView(new L.LatLng(position.coords.latitude, position.coords.longitude), 18);
}

function getServerTimeDiff() {
	var startTime = Date.now();
	ajax("/now", function(timeStr) {
		var endTime = Date.now();
		var latency = endTime.getTime() - startTime.getTime();
		var serverTime = Date.parse(timeStr);
		// we subtract (latency / 2), because that's presumably how long it took to get back to us after the server created the timestamp
		serverTimeDiff = Date.now() - serverTime -  (latency / 2);
	})
}

var dinosaurGetIntervalMs = 3000;

function init() {
  getServerTimeDiff();
	initmap();
	setLocation();
	window.setInterval(getDinosaursHere, dinosaurGetIntervalMs);
}

init();
<commit_msg>Move index.js globals to top, remove unused<commit_after>// \todo aggregate vars into single 'world' var?
var mapDinos = {};
var map;
var serverTimeDiff = 0;
var popupInterval;
var dinosaurGetIntervalMs = 3000;

function ajax(path, f) {
	var xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
		if (this.readyState == 4 && this.status == 200) {
		  f(this.responseText)
		}
	};
	xhttp.open("GET", path, true);
	xhttp.send();
}

// queryLatlon does an API query on the given latlon, and calls f with the data
function queryLatLon(lat, lon, f) {
	ajax("/query/" + lat + "/" + lon, function(data) {
		f(JSON.parse(data));
	})
}

function addDinosaursToMap(dinosaurs, map) {
	console.log('addDinosaursToMap ' + dinosaurs.length);
	var dinosaursLen = dinosaurs.length;
	for (var i = 0; i < dinosaursLen; i++) {
		addDinosaurToMap(dinosaurs[i], map);
	}
}

// durationStr returns the hours, minutes, and seconds left in the milliseconds integer `duration`, in the format '4h20m3s'
function durationShortStr(duration) {
	var ds = duration / 1000;
	var h = Math.floor(ds / 60 / 60);
	var m = Math.floor(ds / 60 % 60);
	var s = Math.floor(ds % 60);
	var str = "";
	if(h > 0) {
		str += h + "h";
	}
	if(m > 0) {
		str += m + "m";
	}
	str += s + "s";
	return str;
}

function getDinoPopupStr(dino) {
	var dinoExpireTime = Date.parse(dino.Expiration);
	var dinoExpireFromNowMs = dinoExpireTime - Date.now() - serverTimeDiff;
	return "" + dino.ID + " " + dino.Dinosaur.Name + "" + "
" +
		"Time left: " + durationShortStr(dinoExpireFromNowMs);
}

function addDinosaurToMap(dino, map) {
  console.log('addDinosaurToMap ' + dino.ID);
	if(mapDinos.hasOwnProperty(dino.ID)) {
    console.log('addDinosaurToMap HAS ' + dino.ID);
		return
	}
  console.log('addDinosaurToMap NEW ' + dino.ID);

	console.log("adding " + dino.ID + " specie" + dino.Dinosaur.Name + " expiration " + dino.Expiration);

	mapDinos[dino.ID] = dino; // TODO change to store a bool or something, if dino isn't needed
	var name = dino.Dinosaur.Name;
	// \todo add image path to specie endpoint?
	var imagePath = "/images/" + name.toLowerCase() + ".png";

	// \todo add image dimensions to specie endpoint?
	// \todo store specie icons, for speed?
	var dinoIcon = L.icon({
		dinosaur: dino,
		iconUrl: imagePath,
    //				 shadowUrl: 'leaf-shadow.png',
		iconSize:     [64, 64], // size of the icon
    //				 shadowSize:   [5, 64], // size of the shadow
		iconAnchor:   [32, 32], // point of the icon which will correspond to marker's location
    //				 shadowAnchor: [4, 62],  // the same for the shadow
		popupAnchor:  [-5, -5] // point from which the popup should open relative to the iconAnchor
	});

	var dinoExpireTime = Date.parse(dino.Expiration);
	var dinoExpireFromNowMs = dinoExpireTime - Date.now() - serverTimeDiff;

  console.log('addDinosaurToMap dinoExpireFromNowMs ' + dinoExpireFromNowMs);
  console.log('addDinosaurToMap dinoExpireTime ' + dinoExpireTime);
  console.log('addDinosaurToMap now.getTime() ' + serverTimeDiff);
  console.log('addDinosaurToMap serverTimeDiff ' + serverTimeDiff);
	if(dinoExpireFromNowMs > 0) {
		var marker = L.marker([dino.Latitude, dino.Longitude], {icon: dinoIcon});
		var popupStr = "" + dino.ID + " " + dino.Dinosaur.Name + "" + "
" +
		    "Time left: " + durationShortStr(dinoExpireFromNowMs);
		marker.bindPopup(popupStr).openPopup();
		marker.getPopup().Dinosaur = dino;
		marker.addTo(map);

		window.setTimeout(function() {
		  map.removeLayer(marker);
		  delete mapDinos[dino.ID];
		  console.log("removing " + dino.ID);
		}, dinoExpireFromNowMs);
	}
}

function getDinosaursHere() {
	var latLng = map.getCenter();
	var lat = latLng.lat;
	var lon = latLng.lng;
	queryLatLon(lat, lon, function(dinosaurs) {
		addDinosaursToMap(dinosaurs, map);
	})
}

function initmap() {
	map = new L.Map('mapid');

	// create the tile layer with correct attribution
	var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
	var osmAttrib='Map data © OpenStreetMap contributors';
	var osm = new L.TileLayer(osmUrl, {attribution: osmAttrib});

	var defaultLat = 39.7392;
	var defaultLon = -104.9903;
	var defaultZoom = 15

	map.setView(new L.LatLng(defaultLat, defaultLon), defaultZoom);
	map.addLayer(osm);

	map.locate({setView: true, maxZoom: 18});

  map.on('move', getDinosaursHere);
	map.on('moveend', function() {
		console.log("map moved - calling getDinosaursHere()");
		getDinosaursHere();
	});
	map.on('popupopen', function(popupEvent) {
		var popup = popupEvent.popup;
		var dino = popup.Dinosaur;
		if(dino) {
			popup.setContent(getDinoPopupStr(dino));
			console.log('setting interval ' + dino.ID)
			popup.UpdateInterval = window.setInterval(function() {
				popup.setContent(getDinoPopupStr(dino));
			}, 1000);
		}
	})
	map.on('popupclose', function(popupEvent) {
		var popup = popupEvent.popup;
		if(popup.UpdateInterval) {
			clearInterval(popup.UpdateInterval)
			console.log('clearing interval ' + popup.Dinosaur.ID)
		}
	})
  getDinosaursHere();

}

function setLocation() {
	if (navigator.geolocation) {
		navigator.geolocation.getCurrentPosition(setPosition);
	}
}
function setPosition(position) {
	console.log('' + position.coords.latitude + ' ' + position.coords.longitude);
	map.setView(new L.LatLng(position.coords.latitude, position.coords.longitude), 18);
}

function getServerTimeDiff() {
	var startTime = Date.now();
	ajax("/now", function(timeStr) {
		var endTime = Date.now();
		var latency = endTime.getTime() - startTime.getTime();
		var serverTime = Date.parse(timeStr);
		// we subtract (latency / 2), because that's presumably how long it took to get back to us after the server created the timestamp
		serverTimeDiff = Date.now() - serverTime -  (latency / 2);
	})
}

function init() {
  getServerTimeDiff();
	initmap();
	setLocation();
	window.setInterval(getDinosaursHere, dinosaurGetIntervalMs);
}

init();
<|endoftext|>


<commit_before>var path = require('path')
  , fs = require('fs')

var mMapHandler;

var MapHandler = function () {
  var self = this;

  var mAppMap = {}
    , mTestMap = {};

  self.handleMap = function(map){
    switch (map){
      case 'app' :
        if (mAppMap==null) {
          mAppMap = self.appMap();
        }
        return mAppMap;
        break;
      case 'test' :
      if (mTestMap==null) {
          mTestMap = self.testMap();
        }
        return mTestMap;
        break;
      default:
        return null;
    }
  }

  self.appMap = function(){
    var mMap = {};
    var root = path.dirname(require.main.filename);
    createMap(path.join(root, 'app'), function(map){
      mMap['app'] = map;
    })
    createMap(path.join(root, 'config'), function(map){
      mMap['config'] = map;
    })
    mMap['log']=path.join(root, 'log');
    mMap['map']=path.join(root, 'map');
    return mMap;
  }

  self.testMap = function(){
    return 'ey';
  }

}

var createMap = function(directory, callback){
  var schema = {};
  var dirMap = fs.readdirSync(directory);
  dirMap.forEach(function(dir,i,a) {
    var dirPath = path.join(directory, dir);
    var stats = fs.lstatSync(dirPath);
    if (stats.isDirectory()) {
      createMap(dirPath, function(structure){
        schema[dir]=structure;
      });
    } else {
      var splits = dir.split('.');
      var extension = splits[splits.length - 1];
      if(extension=='js'){
        var name = dir.split('.')[0];
        schema[name]=dirPath;
      }
    }
  });
  callback(schema);
}

/*var appMap = function(){
  var mMap = {};
  var root = path.dirname(require.main.filename);
  createMap(path.join(root, 'app'), function(map){
    mMap['app'] = map;
  })
  createMap(path.join(root, 'config'), function(map){
    mMap['config'] = map;
  })
  mMap['log']=path.join(root, 'log');
  mMap['map']=path.join(root, 'map');
  myAppMap = mMap;
  return mMap;
}*/

var dirMap = function(root){
  var mMap = {};
  if(!root || root==null || !root.trim().length>0) {
    return false
  }
  else {
    createMap(root, function(map){
      mMap = map;
    })
  }
  return mMap;
}

//module.exports.dirMap = function(root) { return dirMap(root); }

//module.exports.appMap = function() { return appMap(); }

module.exports = {

  dirMap : function(root) {
    return dirMap(root);
  },

  appMap : function() {
    if (mMapHandler==null) {
      mMapHandler = new MapHandler();
    }
    return mMapHandler.handleMap('app');
  },

  testMap : function() {
    if (mMapHandler==null) {
      mMapHandler = new MapHandler();
    }
    return mMapHandler.handleMap('test');
  }

}<commit_msg>release 1.0.6<commit_after>var path = require('path')
  , fs = require('fs')

var mMapHandler;

var MapHandler = function () {
  var self = this;

  var mAppMap = {}
    , mTestMap = {};

  self.handleMap = function(map, callback){
    switch (map){
      case 'app' :
        if (mAppMap==null) {
          self.appMap(function(mMap){
            myAppMap = mMap;
            callback(myAppMap);
          });
        }
        else {
          callback(myAppMap);
        }
        break;
      case 'test' :
        if (mTestMap==null) {
          self.testMap(function(mMap){
            mTestMap = mMap;
            callback(mTestMap);
          });
        }
        else {
          callback(mTestMap);
        }
        break;
      default:
        return null;
    }
  }

  self.appMap = function(callback){
    var mMap = {};
    var root = path.dirname(require.main.filename);
    createMap(path.join(root, 'app'), function(map){
      mMap['app'] = map;
    })
    createMap(path.join(root, 'config'), function(map){
      mMap['config'] = map;
    })
    mMap['log']=path.join(root, 'log');
    mMap['map']=path.join(root, 'map');
    callback(mMap);
  }

  self.testMap = function(callback){
    callback('ey');
  }

}

var createMap = function(directory, callback){
  var schema = {};
  var dirMap = fs.readdirSync(directory);
  dirMap.forEach(function(dir,i,a) {
    var dirPath = path.join(directory, dir);
    var stats = fs.lstatSync(dirPath);
    if (stats.isDirectory()) {
      createMap(dirPath, function(structure){
        schema[dir]=structure;
      });
    } else {
      var splits = dir.split('.');
      var extension = splits[splits.length - 1];
      if(extension=='js'){
        var name = dir.split('.')[0];
        schema[name]=dirPath;
      }
    }
  });
  callback(schema);
}

/*var appMap = function(){
  var mMap = {};
  var root = path.dirname(require.main.filename);
  createMap(path.join(root, 'app'), function(map){
    mMap['app'] = map;
  })
  createMap(path.join(root, 'config'), function(map){
    mMap['config'] = map;
  })
  mMap['log']=path.join(root, 'log');
  mMap['map']=path.join(root, 'map');
  myAppMap = mMap;
  return mMap;
}*/

var dirMap = function(root){
  var mMap = {};
  if(!root || root==null || !root.trim().length>0) {
    return false
  }
  else {
    createMap(root, function(map){
      mMap = map;
    })
  }
  return mMap;
}

//module.exports.dirMap = function(root) { return dirMap(root); }

//module.exports.appMap = function() { return appMap(); }

module.exports = {

  dirMap : function(root) {
    return dirMap(root);
  },

  appMap : function() {
    if (mMapHandler==null) {
      mMapHandler = new MapHandler();
    }
    mMapHandler.handleMap('app', function(map){
      return map;
    });
  },

  testMap : function() {
    if (mMapHandler==null) {
      mMapHandler = new MapHandler();
    }
    return mMapHandler.handleMap('test', function(map){
      return map;
    });
  }

}<|endoftext|>


<commit_before>'use strict';

let dns = require('dns');
let ping = require('ping');
let Promise = require('bluebird');
let pad = require('pad');

let domains = require('./domains');

function pingDomain(domain) {
    return new Promise((resolve, reject) => {
        dns.lookup(domain, (error, ip, family) => {
            if (error) { return reject(error); }
            ping.sys.probe(ip, (isAlive) => {
                console.log(pad(ip, 15)+' - '+domain+' - '+(isAlive ? 'up' : 'maybe down'));
                return resolve();
            });
        });
    });
}

Promise.map(domains, (domain) => {
        return pingDomain(domain);
    })
    .then(() => {
        console.log('Done');
    });
<commit_msg>Moved isAlive to middle because OCD<commit_after>'use strict';

let dns = require('dns');
let ping = require('ping');
let Promise = require('bluebird');
let pad = require('pad');

let domains = require('./domains');

function pingDomain(domain) {
    return new Promise((resolve, reject) => {
        dns.lookup(domain, (error, ip, family) => {
            if (error) { return reject(error); }
            ping.sys.probe(ip, (isAlive) => {
                console.log(pad(ip, 15)+' - '+pad((isAlive ? 'up' : 'maybe down'), 10)+' - '+domain);
                return resolve();
            });
        });
    });
}

Promise.map(domains, (domain) => {
        return pingDomain(domain);
    })
    .then(() => {
        console.log('Done');
    });
<|endoftext|>


<commit_before>import {SpeechRecognition} from './js/SpeechRecognition'

if(Promise === undefined){
    throw new Error('Promises are not supported in this browser. Use this polyfill: https://www.npmjs.com/package/es6-promise');
}
// polyfill mediaDevices.getUserMedia
navigator.mediaDevices = navigator.mediaDevices || {};
navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || (function(){
    // returns a getUserMedia function
    var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    return function(constraints){
        if(!getUserMedia){
            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
        }

        return new Promise(function(resolve, reject){
            getUserMedia.call(navigator, constraints, resolve, reject);
        });
    }
})();

// polyfill speechRecognition
(function(speechRecognition){
    window.SpeechRecognition = window.SpeechRecognition || SpeechRecognition
})(window.SpeechRecognition || window.webkitSpeechRecognition);<commit_msg>changed reference of Speech Recognition class<commit_after>import {SpeechRecognition} from './js/src/SpeechRecognition'

if(Promise === undefined){
    throw new Error('Promises are not supported in this browser. Use this polyfill: https://www.npmjs.com/package/es6-promise');
}
// polyfill mediaDevices.getUserMedia
navigator.mediaDevices = navigator.mediaDevices || {};
navigator.mediaDevices.getUserMedia = navigator.mediaDevices.getUserMedia || (function(){
    // returns a getUserMedia function
    var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    return function(constraints){
        if(!getUserMedia){
            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
        }

        return new Promise(function(resolve, reject){
            getUserMedia.call(navigator, constraints, resolve, reject);
        });
    }
})();

// polyfill speechRecognition
(function(speechRecognition){
    window.SpeechRecognition = window.SpeechRecognition || SpeechRecognition
})(window.SpeechRecognition || window.webkitSpeechRecognition);<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */

var Client = require('mongodb').MongoClient,
uri = require('mongodb-uri'),
thunky = require('thunky'),
zlib = require('zlib'), noop = function () {};

/**
 * Export `MongoStore`.
 */

exports = module.exports = {
  create : function (args) {
    return MongoStore(args);
  }
};

/**
 * MongoStore constructor.
 *
 * @param {Object} options
 * @api public
 */

function MongoStore(args) {

  if (!(this instanceof MongoStore))
    return new MongoStore(args);

  var store = this;
  var conn = (args.uri) ? args.uri : args;
  var options = (args.options) ? args.options : {};
  store.MongoOptions = options;
  store.name = 'mongodb';

  if ('object' === typeof conn) {
    if ('function' !== typeof conn.collection) {
      MongoOptions = conn.options;
      if (Object.keys(MongoOptions).length === 0) {
        conn = null;
      } else if (MongoOptions.client) {
        store.client = MongoOptions.client
      } else {
        store.MongoOptions.database = store.MongoOptions.database || store.MongoOptions.db;
        store.MongoOptions.hosts = store.MongoOptions.hosts || [{
              port : store.MongoOptions.port || 27017,
              host : store.MongoOptions.host || '127.0.0.1'
            }
          ];
        store.MongoOptions.hosts = store.MongoOptions.hosts || 3600;
        conn = uri.format(store.MongoOptions);
      }
    } else {
      store.client = conn;
    }
  }
  conn = conn || 'mongodb://127.0.0.1:27017';
  store.coll = store.MongoOptions.collection || 'cacheman';
  store.compression = store.MongoOptions.compression || false;

  if ('string' === typeof conn) {
    Client.connect(conn, store.MongoOptions, function getDb(err, db) {
      store.client = db;
    });
  }

}

/**
 * Get an entry.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.get = function get(key, options, fn) {

  if ('function' === typeof options) {
    fn = options;
    options = null;
  }
  fn = fn || noop;

  var store = this;

  store.client.collection(store.coll).findOne({
    key : key
  }, function findOne(err, data) {
    console.log(err);
    console.log(data);
    if (err)
      return fn(err);
    if (!data)
      return fn(null, null);
    if (data.expire < Date.now()) {
      store.del(key);
      return fn(null, null);
    }
    try {
      if (data.compressed)
        return decompress(data.value, fn);
      fn(null, data.value);
    } catch (err) {
      fn(err);
    }
  });
};

/**
 * Set an entry.
 *
 * @param {String} key
 * @param {Mixed} val
 * @param {Number} ttl
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.set = function set(key, val, options, fn) {

  if ('function' === typeof options) {
    fn = options;
    options = null;
  }
  fn = fn || noop;

  var store = this;
  var ttl = (options && (options.ttl || options.ttl === 0)) ? options.ttl : store.MongoOptions.ttl;

  var data,
  query = {
    key : key
  },
  options = {
    upsert : true,
    safe : true
  };

  try {
    data = {
      key : key,
      value : val,
      expire : Date.now() + ((ttl || 60) * 1000)
    };
  } catch (err) {
    return fn(err);
  }

  if (!store.compression) {
    update(data);
  } else {
    compress(data, function compressData(err, data) {
      if (err)
        return fn(err);
      update(data);
    });
  }

  function update(data) {
    store.client.collection(store.coll).update(query, data, options, function _update(err, data) {

      if (err)
        return fn(err);
      if (!data)
        return fn(null, null);
      fn(null, val);
    });
  }
};

/**
 * Delete an entry.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.del = function del(key, options, fn) {
  if (typeof options === 'function') {
    fn = options;
  }
  var store = this;
  fn = fn || noop;
  this.ready(function ready(err, db) {
    if (err)
      return fn(err);
    db.collection(store.coll).remove({
      key : key
    }, {
      safe : true
    }, fn);
  });
};

/**
 * Clear all entries for this bucket.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.reset = function reset(key, fn) {
  var store = this;

  if ('function' === typeof key) {
    fn = key;
    key = null;
  }

  fn = fn || noop;
  store.ready(function ready(err, db) {
    if (err)
      return fn(err);
    db.collection(store.coll).remove({}, {
      safe : true
    }, fn);
  });
};

MongoStore.prototype.isCacheableValue = function (value) {
  return value !== null && value !== undefined;
};

/**
 * Non-exported Helpers
 */

/**
 * Compress data value.
 *
 * @param {Object} data
 * @param {Function} fn
 * @api public
 */

function compress(data, fn) {

  // Data is not of a "compressable" type (currently only Buffer)
  if (!Buffer.isBuffer(data.value))
    return fn(null, data);

  zlib.gzip(data.value, function (err, val) {
    // If compression was successful, then use the compressed data.
    // Otherwise, save the original data.
    if (!err) {
      data.value = val;
      data.compressed = true;
    }

    fn(err, data);
  });
};

/**
 * Decompress data value.
 *
 * @param {Object} value
 * @param {Function} fn
 * @api public
 */

function decompress(value, fn) {
  var v = (value.buffer && Buffer.isBuffer(value.buffer)) ? value.buffer : value;
  zlib.gunzip(v, fn);
};
<commit_msg>Clean debug info<commit_after>'use strict';

/**
 * Module dependencies.
 */

var Client = require('mongodb').MongoClient,
uri = require('mongodb-uri'),
thunky = require('thunky'),
zlib = require('zlib'), noop = function () {};

/**
 * Export `MongoStore`.
 */

exports = module.exports = {
  create : function (args) {
    return MongoStore(args);
  }
};

/**
 * MongoStore constructor.
 *
 * @param {Object} options
 * @api public
 */

function MongoStore(args) {

  if (!(this instanceof MongoStore))
    return new MongoStore(args);

  var store = this;
  var conn = (args.uri) ? args.uri : args;
  var options = (args.options) ? args.options : {};
  store.MongoOptions = options;
  store.name = 'mongodb';

  if ('object' === typeof conn) {
    if ('function' !== typeof conn.collection) {
      MongoOptions = conn.options;
      if (Object.keys(MongoOptions).length === 0) {
        conn = null;
      } else if (MongoOptions.client) {
        store.client = MongoOptions.client
      } else {
        store.MongoOptions.database = store.MongoOptions.database || store.MongoOptions.db;
        store.MongoOptions.hosts = store.MongoOptions.hosts || [{
              port : store.MongoOptions.port || 27017,
              host : store.MongoOptions.host || '127.0.0.1'
            }
          ];
        store.MongoOptions.hosts = store.MongoOptions.hosts || 3600;
        conn = uri.format(store.MongoOptions);
      }
    } else {
      store.client = conn;
    }
  }
  conn = conn || 'mongodb://127.0.0.1:27017';
  store.coll = store.MongoOptions.collection || 'cacheman';
  store.compression = store.MongoOptions.compression || false;

  if ('string' === typeof conn) {
    Client.connect(conn, store.MongoOptions, function getDb(err, db) {
      store.client = db;
    });
  }

}

/**
 * Get an entry.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.get = function get(key, options, fn) {

  if ('function' === typeof options) {
    fn = options;
    options = null;
  }
  fn = fn || noop;

  var store = this;

  store.client.collection(store.coll).findOne({
    key : key
  }, function findOne(err, data) {
    if (err)
      return fn(err);
    if (!data)
      return fn(null, null);
    if (data.expire < Date.now()) {
      store.del(key);
      return fn(null, null);
    }
    try {
      if (data.compressed)
        return decompress(data.value, fn);
      fn(null, data.value);
    } catch (err) {
      fn(err);
    }
  });
};

/**
 * Set an entry.
 *
 * @param {String} key
 * @param {Mixed} val
 * @param {Number} ttl
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.set = function set(key, val, options, fn) {

  if ('function' === typeof options) {
    fn = options;
    options = null;
  }
  fn = fn || noop;

  var store = this;
  var ttl = (options && (options.ttl || options.ttl === 0)) ? options.ttl : store.MongoOptions.ttl;

  var data,
  query = {
    key : key
  },
  options = {
    upsert : true,
    safe : true
  };

  try {
    data = {
      key : key,
      value : val,
      expire : Date.now() + ((ttl || 60) * 1000)
    };
  } catch (err) {
    return fn(err);
  }

  if (!store.compression) {
    update(data);
  } else {
    compress(data, function compressData(err, data) {
      if (err)
        return fn(err);
      update(data);
    });
  }

  function update(data) {
    store.client.collection(store.coll).update(query, data, options, function _update(err, data) {

      if (err)
        return fn(err);
      if (!data)
        return fn(null, null);
      fn(null, val);
    });
  }
};

/**
 * Delete an entry.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.del = function del(key, options, fn) {
  if (typeof options === 'function') {
    fn = options;
  }
  var store = this;
  fn = fn || noop;
  this.ready(function ready(err, db) {
    if (err)
      return fn(err);
    db.collection(store.coll).remove({
      key : key
    }, {
      safe : true
    }, fn);
  });
};

/**
 * Clear all entries for this bucket.
 *
 * @param {String} key
 * @param {Function} fn
 * @api public
 */

MongoStore.prototype.reset = function reset(key, fn) {
  var store = this;

  if ('function' === typeof key) {
    fn = key;
    key = null;
  }

  fn = fn || noop;
  store.ready(function ready(err, db) {
    if (err)
      return fn(err);
    db.collection(store.coll).remove({}, {
      safe : true
    }, fn);
  });
};

MongoStore.prototype.isCacheableValue = function (value) {
  return value !== null && value !== undefined;
};

/**
 * Non-exported Helpers
 */

/**
 * Compress data value.
 *
 * @param {Object} data
 * @param {Function} fn
 * @api public
 */

function compress(data, fn) {

  // Data is not of a "compressable" type (currently only Buffer)
  if (!Buffer.isBuffer(data.value))
    return fn(null, data);

  zlib.gzip(data.value, function (err, val) {
    // If compression was successful, then use the compressed data.
    // Otherwise, save the original data.
    if (!err) {
      data.value = val;
      data.compressed = true;
    }

    fn(err, data);
  });
};

/**
 * Decompress data value.
 *
 * @param {Object} value
 * @param {Function} fn
 * @api public
 */

function decompress(value, fn) {
  var v = (value.buffer && Buffer.isBuffer(value.buffer)) ? value.buffer : value;
  zlib.gunzip(v, fn);
};
<|endoftext|>


<commit_before>var fs = require("fs")

var isJavascript = /\.js$/

module.exports = allFiles

function allFiles(uri, regex) {
    var stat = fs.statSync(uri)
    regex = regex || isJavascript

    if (stat.isFile()) {
        if (!regex.test(uri)) {
            return []
        }

        return [fs.readFileSync(uri)]
    }

    var files = fs.readdirSync(uri)
    return files.filter(function (uri) {
        return regex.test(uri)
    }).reduce(function (acc, uri) {
        return acc.concat(allFiles(uri))
    }, [])
}
<commit_msg>return uris<commit_after>var fs = require("fs")

var isJavascript = /\.js$/

module.exports = allFiles

function allFiles(uri, regex) {
    var stat = fs.statSync(uri)
    regex = regex || isJavascript

    if (stat.isFile()) {
        if (!regex.test(uri)) {
            return []
        }

        return [uri]
    }

    var files = fs.readdirSync(uri)
    return files.filter(function (uri) {
        return regex.test(uri)
    }).reduce(function (acc, uri) {
        return acc.concat(allFiles(uri))
    }, [])
}
<|endoftext|>


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

module.exports = {
  name: 'ember-world-flags',
  included: function(app) {
    app.import('vendor/flags16.css');
    app.import('vendor/flags32.css');
  }
};
<commit_msg>enable ember-world-flags to be used as nested addon<commit_after>/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-world-flags',
  included: function(app, parentAddon) {
    if (typeof app.import !== 'function' && app.app) {
      this.app = app = app.app;
    }
    this._super.included(app);
    var target = (parentAddon || app);

    target.import('vendor/flags16.css');
    target.import('vendor/flags32.css');
  }
};
<|endoftext|>


<commit_before>var U2 = require('uglify-js');

module.exports = function singleQuote(codeIn) {
	var code = codeIn;
	var hasReturn = code.indexOf('\r\n') !== -1;
	var hasCommandLineInfo = code.substr(0, 2) === '#!';
	var commandlineInfo = '';
	code = code.replace(/\r/g, '');
	if (hasCommandLineInfo) {
		var endOfLine = (code.indexOf('\n') + 1);
		if (!endOfLine) {
			return codeIn;
		}
		commandlineInfo = code.substr(0, endOfLine);
		code = code.substr(endOfLine);
	}
	var ast = U2.parse(code);
	// accumulate string-nodes in this array
	var stringNodes = [];
	ast.walk(new U2.TreeWalker(function (node) {
		if (node instanceof U2.AST_String) {
			stringNodes.push(node);
		}
	}));
	// now go through the nodes backwards and replace code
	for (var i = stringNodes.length; --i >= 0;) {
		var node = stringNodes[i];
		var replacement = code.substr(node.start.pos, node.end.endpos - node.start.pos);
		if (replacement.substr(0, 1) === '\'') {
			continue;
		}
		replacement = replacement.replace(/\\"/g, '"').replace(/'/g, '\\\'');
		replacement = '\'' + replacement.substr(1, replacement.length - 2) + '\'';
		code = splice_string(code, node.start.pos, node.end.endpos, replacement);
	}
	U2.parse(code); //do sanity-check so we don't mess up files
	code = commandlineInfo + code;
	code = hasReturn ? code.replace(/\n/g, '\r\n') : code;
	return code;
};

function splice_string(str, begin, end, replacement) {
	return str.substr(0, begin) + replacement + str.substr(end);
}<commit_msg>return errorcode so we can spot the differnece between malformed JavaScript and singlequote bugs<commit_after>var U2 = require('uglify-js');

module.exports = function singleQuote(codeIn) {
	var code = codeIn;
	var hasReturn = code.indexOf('\r\n') !== -1;
	var hasCommandLineInfo = code.substr(0, 2) === '#!';
	var commandlineInfo = '';
	var ast;
	code = code.replace(/\r/g, '');
	if (hasCommandLineInfo) {
		var endOfLine = (code.indexOf('\n') + 1);
		if (!endOfLine) {
			return codeIn;
		}
		commandlineInfo = code.substr(0, endOfLine);
		code = code.substr(endOfLine);
	}
	try {
		ast = U2.parse(code);
	} catch (ex) {
		var parseError = new Error('Error parsing code');
		parseError.code = 'parse';
		parseError.inner = ex;
		throw parseError;
	}
	// accumulate string-nodes in this array
	var stringNodes = [];
	ast.walk(new U2.TreeWalker(function (node) {
		if (node instanceof U2.AST_String) {
			stringNodes.push(node);
		}
	}));
	// now go through the nodes backwards and replace code
	for (var i = stringNodes.length; --i >= 0;) {
		var node = stringNodes[i];
		var replacement = code.substr(node.start.pos, node.end.endpos - node.start.pos);
		if (replacement.substr(0, 1) === '\'') {
			continue;
		}
		replacement = replacement.replace(/\\"/g, '"').replace(/'/g, '\\\'');
		replacement = '\'' + replacement.substr(1, replacement.length - 2) + '\'';
		code = splice_string(code, node.start.pos, node.end.endpos, replacement);
	}
	try {
		U2.parse(code); //do sanity-check so we don't mess up files
	} catch (ex) {
		var parseError = new Error('Error parsing code produced by singlequote');
		parseError.code = 'singlequote';
		parseError.inner = ex;
		throw parseError;
	}
	U2.parse(code);
	code = commandlineInfo + code;
	code = hasReturn ? code.replace(/\n/g, '\r\n') : code;
	return code;
};

function splice_string(str, begin, end, replacement) {
	return str.substr(0, begin) + replacement + str.substr(end);
}<|endoftext|>


<commit_before>var argv = require('optimist')
    .usage('Converts Sparse Matrices collection (http://aws.amazon.com/datasets/2379) to npm module format.\nUsage: $0')
    .demand('s')
    .alias('s', 'src')
    .describe('s', 'Path to /MM folder from public dataset ')
    .default({'o': './out'})
    .alias('o', 'out')
    .describe('o', 'Path to outupt folder where results will be stored')
    .argv;

var readdirp = require('readdirp'),
    mkdirp = require('mkdirp'),
    mtxParser = require('ngraph.serialization/mtx'),
    fs = require('fs'),
    path = require('path');

readdirp({ root: argv.src, fileFilter: mtxFileFilter })
  .on('data', convertToNPMEntry);

function convertToNPMEntry(mtxFileEntry) {
  console.log('Processing ' + mtxFileEntry.fullPath);
  var mtxFile = fs.readFileSync(mtxFileEntry.fullPath, 'ascii'),
      graph = mtxParser.load(mtxFile),
      storedGraph = mtxParser.saveToObject(graph);

  var outDir = path.join(argv.out, mtxFileEntry.parentDir);
  mkdirp.sync(outDir);

  var savedFileName = saveJavaScriptFile(storedGraph, outDir);
  saveReadmeFile(graph.description, outDir);

  console.log('Saved to ' + savedFileName + '; Vertices: ' + graph.getNodesCount() + '; Edges: ' + graph.getLinksCount());
}

function saveJavaScriptFile(graphObject, outDir) {
  var fileContent = 'module.exports = ' + JSON.stringify(graphObject) + ';',
      saveTo = path.join(outDir, 'index.js');

  fs.writeFileSync(saveTo, fileContent);
  return saveTo;
}

function saveReadmeFile(description, outDir) {
  if (!description) { return; }

  var saveTo = path.join(outDir, 'Readme.md');

  var lines = description.split('\n'),
      content = [],
      name = '';
  for (var i = 0; i < lines.length; ++i) {
    if (!lines[i].length) {
      continue;
    }
    var firstChar = lines[i][0];
    if (firstChar === '%' || firstChar === '-') {
      // Skip header and separators
      continue;
    }
    var nameMatch = lines[i].match(/ name: (.*)/);
    if (nameMatch) {
      name = nameMatch[1];
    } else {
      content.push(lines[i]);
    }
  }

  if (name) {
    content.unshift('# ' + name);
    var thumbnail = 'http://www2.research.att.com/~yifanhu/GALLERY/GRAPHS/GIF_SMALL/' + name.replace('/', '@') + '.gif';
    content.push('![' + name + '](' + thumbnail + ')');
  }

  fs.writeFileSync(saveTo, content.join('\n\n'));
}

function mtxFileFilter(fileEntry) {
  if (path.extname(fileEntry.name) !== '.mtx') {
    return false;
  }

  var fileName = path.basename(fileEntry.name, '.mtx');
  return fileEntry.parentDir.match(new RegExp(fileName + '$'));
}
<commit_msg>Save common index.js too<commit_after>var argv = require('optimist')
    .usage('Converts Sparse Matrices collection (http://aws.amazon.com/datasets/2379) to npm module format.\nUsage: $0')
    .demand('s')
    .alias('s', 'src')
    .describe('s', 'Path to /MM folder from public dataset ')
    .default({'o': './out'})
    .alias('o', 'out')
    .describe('o', 'Path to outupt folder where results will be stored')
    .argv;

var readdirp = require('readdirp'),
    mkdirp = require('mkdirp'),
    mtxParser = require('ngraph.serialization/mtx'),
    fs = require('fs'),
    allEntries = [],
    path = require('path');

readdirp({ root: argv.src, fileFilter: mtxFileFilter })
  .on('data', convertToNPMEntry)
  .on('end', writeSummary);

function writeSummary() {
  var outDir = argv.out;
  mkdirp.sync(outDir);
  saveJavaScriptFile(allEntries, outDir);
}

function convertToNPMEntry(mtxFileEntry) {
  console.log('Processing ' + mtxFileEntry.fullPath);
  var mtxFile = fs.readFileSync(mtxFileEntry.fullPath, 'ascii'),
      graph = mtxParser.load(mtxFile),
      storedGraph = mtxParser.saveToObject(graph);

  var outDir = path.join(argv.out, mtxFileEntry.parentDir);
  mkdirp.sync(outDir);

  if (graph.description) {
    storedGraph.description = graph.description;
  }
  var savedFileName = saveJavaScriptFile(storedGraph, outDir);
  saveReadmeFile(graph.description, outDir);

  allEntries.push(mtxFileEntry.parentDir);
  console.log('Saved to ' + savedFileName + '; Vertices: ' + graph.getNodesCount() + '; Edges: ' + graph.getLinksCount());
}

function saveJavaScriptFile(graphObject, outDir) {
  var fileContent = 'module.exports = ' + JSON.stringify(graphObject) + ';',
      saveTo = path.join(outDir, 'index.js');

  fs.writeFileSync(saveTo, fileContent);
  return saveTo;
}

function saveReadmeFile(description, outDir) {
  if (!description) { return; }

  var saveTo = path.join(outDir, 'Readme.md');

  var lines = description.split('\n'),
      content = [],
      name = '';
  for (var i = 0; i < lines.length; ++i) {
    if (!lines[i].length) {
      continue;
    }
    var firstChar = lines[i][0];
    if (firstChar === '%' || firstChar === '-') {
      // Skip header and separators
      continue;
    }
    var nameMatch = lines[i].match(/ name: (.*)/);
    if (nameMatch) {
      name = nameMatch[1];
    } else {
      content.push(lines[i]);
    }
  }

  if (name) {
    content.unshift('# ' + name);
    var thumbnail = 'http://www2.research.att.com/~yifanhu/GALLERY/GRAPHS/GIF_SMALL/' + name.replace('/', '@') + '.gif';
    content.push('![' + name + '](' + thumbnail + ')');
  }

  fs.writeFileSync(saveTo, content.join('\n\n'));
}

function mtxFileFilter(fileEntry) {
  if (path.extname(fileEntry.name) !== '.mtx') {
    return false;
  }

  var fileName = path.basename(fileEntry.name, '.mtx');
  return fileEntry.parentDir.match(new RegExp(fileName + '$'));
}
<|endoftext|>


<commit_before>'use strict';

var File = require('vinyl');
var isZip = require('is-zip');
var rm = require('rimraf');
var stripDirs = require('strip-dirs');
var tempWrite = require('temp-write');
var through = require('through2');
var Zip = require('adm-zip');

/**
 * zip decompress plugin
 *
 * @param {Object} opts
 * @api public
 */

module.exports = function (opts) {
	opts = opts || {};
	opts.strip = +opts.strip || 0;

	return through.obj(function (file, enc, cb) {
		var self = this;

		if (file.isNull()) {
			cb(null, file);
			return;
		}

		if (file.isStream()) {
			cb(new Error('Streaming is not supported'));
			return;
		}

		if (!isZip(file.contents)) {
			cb(null, file);
			return;
		}

		tempWrite(file.contents, function (err, filepath) {
			var zip = new Zip(filepath);

			zip.getEntries().forEach(function (file) {
				if (!file.isDirectory) {
					self.push(new File({
						contents: file.getData(),
						path: stripDirs(file.entryName.toString(), opts.strip)
					}));
				}
			});

			rm(filepath, function (err) {
				if (err) {
					cb(err);
					return;
				}

				cb();
			});
		});
	});
};
<commit_msg>Tweaks<commit_after>'use strict';

var File = require('vinyl');
var isZip = require('is-zip');
var rm = require('rimraf');
var stripDirs = require('strip-dirs');
var tempWrite = require('temp-write');
var through = require('through2');
var Zip = require('adm-zip');

/**
 * zip decompress plugin
 *
 * @param {Object} opts
 * @api public
 */

module.exports = function (opts) {
	opts = opts || {};
	opts.strip = +opts.strip || 0;

	return through.obj(function (file, enc, cb) {
		var self = this;

		if (file.isNull()) {
			cb(null, file);
			return;
		}

		if (file.isStream()) {
			cb(new Error('Streaming is not supported'));
			return;
		}

		if (!isZip(file.contents)) {
			cb(null, file);
			return;
		}

		tempWrite(file.contents, function (err, filepath) {
			var zip = new Zip(filepath);

			zip.getEntries().forEach(function (file) {
				if (!file.isDirectory) {
					self.push(new File({
						contents: file.getData(),
						path: stripDirs(file.entryName.toString(), opts.strip)
					}));
				}
			});

			rm(filepath, cb);
		});
	});
};
<|endoftext|>


<commit_before>var fsAccess = require('fs-access')
var path = require('path')
var which = require('which')

function isJSFlags (flag) {
  return flag.indexOf('--js-flags=') === 0
}

function sanitizeJSFlags (flag) {
  var test = /--js-flags=(['"])/.exec(flag)
  if (!test) {
    return flag
  }
  var escapeChar = test[1]
  var endExp = new RegExp(escapeChar + '$')
  var startExp = new RegExp('--js-flags=' + escapeChar)
  return flag.replace(startExp, '--js-flags=').replace(endExp, '')
}

var ChromeBrowser = function (baseBrowserDecorator, args) {
  baseBrowserDecorator(this)

  var flags = args.flags || []
  var userDataDir = args.chromeDataDir || this._tempDir

  this._getOptions = function (url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    flags.forEach(function (flag, i) {
      if (isJSFlags(flag)) {
        flags[i] = sanitizeJSFlags(flag)
      }
    })

    return [
      '--user-data-dir=' + userDataDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      '--disable-popup-blocking',
      '--disable-translate',
      '--disable-background-timer-throttling',
      // on macOS, disable-background-timer-throttling is not enough
      // and we need disable-renderer-backgrounding too
      // see https://github.com/karma-runner/karma-chrome-launcher/issues/123
      '--disable-renderer-backgrounding',
      '--disable-device-discovery-notifications'
    ].concat(flags, [url])
  }
}

// Return location of chrome.exe file for a given Chrome directory (available: "Chrome", "Chrome SxS").
function getChromeExe (chromeDirName) {
  // Only run these checks on win32
  if (process.platform !== 'win32') {
    return null
  }
  var windowsChromeDirectory, i, prefix
  var suffix = '\\Google\\' + chromeDirName + '\\Application\\chrome.exe'
  var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']]

  for (i = 0; i < prefixes.length; i++) {
    prefix = prefixes[i]
    try {
      windowsChromeDirectory = path.join(prefix, suffix)
      fsAccess.sync(windowsChromeDirectory)
      return windowsChromeDirectory
    } catch (e) {}
  }

  return windowsChromeDirectory
}

var ChromiumBrowser = function (baseBrowserDecorator, args) {
  baseBrowserDecorator(this)

  var flags = args.flags || []
  var userDataDir = args.chromeDataDir || this._tempDir

  this._getOptions = function (url) {
    // Chromium CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    flags.forEach(function (flag, i) {
      if (isJSFlags(flag)) {
        flags[i] = sanitizeJSFlags(flag)
      }
    })

    return [
      '--user-data-dir=' + userDataDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      '--disable-popup-blocking',
      '--disable-translate',
      '--disable-background-timer-throttling'
    ].concat(flags, [url])
  }
}

// Return location of Chromium's chrome.exe file.
function getChromiumExe (chromeDirName) {
  // Only run these checks on win32
  if (process.platform !== 'win32') {
    return null
  }
  var windowsChromiumDirectory, i, prefix
  var suffix = '\\Chromium\\Application\\chrome.exe'
  var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']]

  for (i = 0; i < prefixes.length; i++) {
    prefix = prefixes[i]
    try {
      windowsChromiumDirectory = path.join(prefix, suffix)
      fsAccess.sync(windowsChromiumDirectory)
      return windowsChromiumDirectory
    } catch (e) {}
  }

  return windowsChromiumDirectory
}

function getBin (commands) {
  // Don't run these checks on win32
  if (process.platform !== 'linux') {
    return null
  }
  var bin, i
  for (i = 0; i < commands.length; i++) {
    try {
      if (which.sync(commands[i])) {
        bin = commands[i]
        break
      }
    } catch (e) {}
  }
  return bin
}

function getChromeDarwin (defaultPath) {
  if (process.platform !== 'darwin') {
    return null
  }

  try {
    var homePath = path.join(process.env.HOME, defaultPath)
    fsAccess.sync(homePath)
    return homePath
  } catch (e) {
    return defaultPath
  }
}

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome', 'google-chrome-stable']),
    darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
    win32: getChromeExe('Chrome')
  },
  ENV_CMD: 'CHROME_BIN'
}

ChromeBrowser.$inject = ['baseBrowserDecorator', 'args']

function headlessGetOptions (url, args, parent) {
  return parent.call(this, url, args).concat(['--headless', '--disable-gpu', '--remote-debugging-port=9222'])
}

var ChromeHeadlessBrowser = function (baseBrowserDecorator, args) {
  ChromeBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return headlessGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeHeadlessBrowser.prototype = {
  name: 'ChromeHeadless',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome', 'google-chrome-stable']),
    darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
    win32: getChromeExe('Chrome')
  },
  ENV_CMD: 'CHROME_BIN'
}

ChromeHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args']

function canaryGetOptions (url, args, parent) {
  // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
  var flags = args.flags || []
  var augmentedFlags
  var customFlags = '--nocrankshaft --noopt'

  flags.forEach(function (flag) {
    if (isJSFlags(flag)) {
      augmentedFlags = sanitizeJSFlags(flag) + ' ' + customFlags
    }
  })

  return parent.call(this, url).concat([augmentedFlags || '--js-flags=' + customFlags])
}

var ChromeCanaryBrowser = function (baseBrowserDecorator, args) {
  ChromeBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return canaryGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome-canary', 'google-chrome-unstable']),
    darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'),
    win32: getChromeExe('Chrome SxS')
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
}

ChromeCanaryBrowser.$inject = ['baseBrowserDecorator', 'args']

var ChromeCanaryHeadlessBrowser = function (baseBrowserDecorator, args) {
  ChromeCanaryBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return headlessGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeCanaryHeadlessBrowser.prototype = {
  name: 'ChromeCanaryHeadless',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome-canary', 'google-chrome-unstable']),
    darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'),
    win32: getChromeExe('Chrome SxS')
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
}

ChromeCanaryHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args']

ChromiumBrowser.prototype = {
  name: 'Chromium',

  DEFAULT_CMD: {
    // Try chromium-browser before chromium to avoid conflict with the legacy
    // chromium-bsu package previously known as 'chromium' in Debian and Ubuntu.
    linux: getBin(['chromium-browser', 'chromium']),
    darwin: '/Applications/Chromium.app/Contents/MacOS/Chromium',
    win32: getChromiumExe()
  },
  ENV_CMD: 'CHROMIUM_BIN'
}

ChromiumBrowser.$inject = ['baseBrowserDecorator', 'args']

var DartiumBrowser = function () {
  ChromeBrowser.apply(this, arguments)

  var checkedFlag = '--checked'
  var dartFlags = process.env.DART_FLAGS || ''
  var flags = dartFlags.split(' ')
  if (flags.indexOf(checkedFlag) === -1) {
    flags.push(checkedFlag)
    process.env.DART_FLAGS = flags.join(' ')
  }
}

DartiumBrowser.prototype = {
  name: 'Dartium',
  DEFAULT_CMD: {},
  ENV_CMD: 'DARTIUM_BIN'
}

DartiumBrowser.$inject = ['baseBrowserDecorator', 'args']

// PUBLISH DI MODULE
module.exports = {
  'launcher:Chrome': ['type', ChromeBrowser],
  'launcher:ChromeHeadless': ['type', ChromeHeadlessBrowser],
  'launcher:ChromeCanary': ['type', ChromeCanaryBrowser],
  'launcher:ChromeCanaryHeadless': ['type', ChromeCanaryHeadlessBrowser],
  'launcher:Chromium': ['type', ChromiumBrowser],
  'launcher:Dartium': ['type', DartiumBrowser]
}

module.exports.test = {
  isJSFlags: isJSFlags,
  sanitizeJSFlags: sanitizeJSFlags,
  headlessGetOptions: headlessGetOptions,
  canaryGetOptions: canaryGetOptions
}
<commit_msg>feat: add ChromiumHeadless<commit_after>var fsAccess = require('fs-access')
var path = require('path')
var which = require('which')

function isJSFlags (flag) {
  return flag.indexOf('--js-flags=') === 0
}

function sanitizeJSFlags (flag) {
  var test = /--js-flags=(['"])/.exec(flag)
  if (!test) {
    return flag
  }
  var escapeChar = test[1]
  var endExp = new RegExp(escapeChar + '$')
  var startExp = new RegExp('--js-flags=' + escapeChar)
  return flag.replace(startExp, '--js-flags=').replace(endExp, '')
}

var ChromeBrowser = function (baseBrowserDecorator, args) {
  baseBrowserDecorator(this)

  var flags = args.flags || []
  var userDataDir = args.chromeDataDir || this._tempDir

  this._getOptions = function (url) {
    // Chrome CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    flags.forEach(function (flag, i) {
      if (isJSFlags(flag)) {
        flags[i] = sanitizeJSFlags(flag)
      }
    })

    return [
      '--user-data-dir=' + userDataDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      '--disable-popup-blocking',
      '--disable-translate',
      '--disable-background-timer-throttling',
      // on macOS, disable-background-timer-throttling is not enough
      // and we need disable-renderer-backgrounding too
      // see https://github.com/karma-runner/karma-chrome-launcher/issues/123
      '--disable-renderer-backgrounding',
      '--disable-device-discovery-notifications'
    ].concat(flags, [url])
  }
}

// Return location of chrome.exe file for a given Chrome directory (available: "Chrome", "Chrome SxS").
function getChromeExe (chromeDirName) {
  // Only run these checks on win32
  if (process.platform !== 'win32') {
    return null
  }
  var windowsChromeDirectory, i, prefix
  var suffix = '\\Google\\' + chromeDirName + '\\Application\\chrome.exe'
  var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']]

  for (i = 0; i < prefixes.length; i++) {
    prefix = prefixes[i]
    try {
      windowsChromeDirectory = path.join(prefix, suffix)
      fsAccess.sync(windowsChromeDirectory)
      return windowsChromeDirectory
    } catch (e) {}
  }

  return windowsChromeDirectory
}

var ChromiumBrowser = function (baseBrowserDecorator, args) {
  baseBrowserDecorator(this)

  var flags = args.flags || []
  var userDataDir = args.chromeDataDir || this._tempDir

  this._getOptions = function (url) {
    // Chromium CLI options
    // http://peter.sh/experiments/chromium-command-line-switches/
    flags.forEach(function (flag, i) {
      if (isJSFlags(flag)) {
        flags[i] = sanitizeJSFlags(flag)
      }
    })

    return [
      '--user-data-dir=' + userDataDir,
      '--no-default-browser-check',
      '--no-first-run',
      '--disable-default-apps',
      '--disable-popup-blocking',
      '--disable-translate',
      '--disable-background-timer-throttling'
    ].concat(flags, [url])
  }
}

// Return location of Chromium's chrome.exe file.
function getChromiumExe (chromeDirName) {
  // Only run these checks on win32
  if (process.platform !== 'win32') {
    return null
  }
  var windowsChromiumDirectory, i, prefix
  var suffix = '\\Chromium\\Application\\chrome.exe'
  var prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env['PROGRAMFILES(X86)']]

  for (i = 0; i < prefixes.length; i++) {
    prefix = prefixes[i]
    try {
      windowsChromiumDirectory = path.join(prefix, suffix)
      fsAccess.sync(windowsChromiumDirectory)
      return windowsChromiumDirectory
    } catch (e) {}
  }

  return windowsChromiumDirectory
}

function getBin (commands) {
  // Don't run these checks on win32
  if (process.platform !== 'linux') {
    return null
  }
  var bin, i
  for (i = 0; i < commands.length; i++) {
    try {
      if (which.sync(commands[i])) {
        bin = commands[i]
        break
      }
    } catch (e) {}
  }
  return bin
}

function getChromeDarwin (defaultPath) {
  if (process.platform !== 'darwin') {
    return null
  }

  try {
    var homePath = path.join(process.env.HOME, defaultPath)
    fsAccess.sync(homePath)
    return homePath
  } catch (e) {
    return defaultPath
  }
}

ChromeBrowser.prototype = {
  name: 'Chrome',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome', 'google-chrome-stable']),
    darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
    win32: getChromeExe('Chrome')
  },
  ENV_CMD: 'CHROME_BIN'
}

ChromeBrowser.$inject = ['baseBrowserDecorator', 'args']

function headlessGetOptions (url, args, parent) {
  return parent.call(this, url, args).concat(['--headless', '--disable-gpu', '--remote-debugging-port=9222'])
}

var ChromeHeadlessBrowser = function (baseBrowserDecorator, args) {
  ChromeBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return headlessGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeHeadlessBrowser.prototype = {
  name: 'ChromeHeadless',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome', 'google-chrome-stable']),
    darwin: getChromeDarwin('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
    win32: getChromeExe('Chrome')
  },
  ENV_CMD: 'CHROME_BIN'
}

ChromeHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args']

function canaryGetOptions (url, args, parent) {
  // disable crankshaft optimizations, as it causes lot of memory leaks (as of Chrome 23.0)
  var flags = args.flags || []
  var augmentedFlags
  var customFlags = '--nocrankshaft --noopt'

  flags.forEach(function (flag) {
    if (isJSFlags(flag)) {
      augmentedFlags = sanitizeJSFlags(flag) + ' ' + customFlags
    }
  })

  return parent.call(this, url).concat([augmentedFlags || '--js-flags=' + customFlags])
}

var ChromeCanaryBrowser = function (baseBrowserDecorator, args) {
  ChromeBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return canaryGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeCanaryBrowser.prototype = {
  name: 'ChromeCanary',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome-canary', 'google-chrome-unstable']),
    darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'),
    win32: getChromeExe('Chrome SxS')
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
}

ChromeCanaryBrowser.$inject = ['baseBrowserDecorator', 'args']

var ChromeCanaryHeadlessBrowser = function (baseBrowserDecorator, args) {
  ChromeCanaryBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return headlessGetOptions.call(this, url, args, parentOptions)
  }
}

ChromeCanaryHeadlessBrowser.prototype = {
  name: 'ChromeCanaryHeadless',

  DEFAULT_CMD: {
    linux: getBin(['google-chrome-canary', 'google-chrome-unstable']),
    darwin: getChromeDarwin('/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'),
    win32: getChromeExe('Chrome SxS')
  },
  ENV_CMD: 'CHROME_CANARY_BIN'
}

ChromeCanaryHeadlessBrowser.$inject = ['baseBrowserDecorator', 'args']

ChromiumBrowser.prototype = {
  name: 'Chromium',

  DEFAULT_CMD: {
    // Try chromium-browser before chromium to avoid conflict with the legacy
    // chromium-bsu package previously known as 'chromium' in Debian and Ubuntu.
    linux: getBin(['chromium-browser', 'chromium']),
    darwin: '/Applications/Chromium.app/Contents/MacOS/Chromium',
    win32: getChromiumExe()
  },
  ENV_CMD: 'CHROMIUM_BIN'
}

ChromiumBrowser.$inject = ['baseBrowserDecorator', 'args']

var ChromiumHeadlessBrowser = function (baseBrowserDecorator, args) {
  ChromiumBrowser.apply(this, arguments)

  var parentOptions = this._getOptions
  this._getOptions = function (url) {
    return headlessGetOptions.call(this, url, args, parentOptions)
  }
}

ChromiumHeadlessBrowser.prototype = {
  name: 'ChromiumHeadless',

  DEFAULT_CMD: {
    // Try chromium-browser before chromium to avoid conflict with the legacy
    // chromium-bsu package previously known as 'chromium' in Debian and Ubuntu.
    linux: getBin(['chromium-browser', 'chromium']),
    darwin: '/Applications/Chromium.app/Contents/MacOS/Chromium',
    win32: getChromiumExe()
  },
  ENV_CMD: 'CHROMIUM_BIN'
}

var DartiumBrowser = function () {
  ChromeBrowser.apply(this, arguments)

  var checkedFlag = '--checked'
  var dartFlags = process.env.DART_FLAGS || ''
  var flags = dartFlags.split(' ')
  if (flags.indexOf(checkedFlag) === -1) {
    flags.push(checkedFlag)
    process.env.DART_FLAGS = flags.join(' ')
  }
}

DartiumBrowser.prototype = {
  name: 'Dartium',
  DEFAULT_CMD: {},
  ENV_CMD: 'DARTIUM_BIN'
}

DartiumBrowser.$inject = ['baseBrowserDecorator', 'args']

// PUBLISH DI MODULE
module.exports = {
  'launcher:Chrome': ['type', ChromeBrowser],
  'launcher:ChromeHeadless': ['type', ChromeHeadlessBrowser],
  'launcher:ChromeCanary': ['type', ChromeCanaryBrowser],
  'launcher:ChromeCanaryHeadless': ['type', ChromeCanaryHeadlessBrowser],
  'launcher:Chromium': ['type', ChromiumBrowser],
  'launcher:ChromiumHeadless': ['type', ChromiumHeadlessBrowser],
  'launcher:Dartium': ['type', DartiumBrowser]
}

module.exports.test = {
  isJSFlags: isJSFlags,
  sanitizeJSFlags: sanitizeJSFlags,
  headlessGetOptions: headlessGetOptions,
  canaryGetOptions: canaryGetOptions
}
<|endoftext|>


<commit_before>var crypto = require('crypto')
var ndjson = require('ndjson')
var duplexify = require('duplexify')

var weapons = ['rock', 'paper', 'scissors']

module.exports = function (choice) {
  choice = choice.toLowerCase()

  if (weapons.indexOf(choice) === -1) throw new Error('Invalid choice: ' + choice)

  var nounce = crypto.randomBytes(32)
  var proof = crypto.createHmac('sha256', nounce).update(choice).digest('hex')

  var serialize = ndjson.serialize()
  var parse = ndjson.parse()

  parse.once('data', function (remoteProof) {
    serialize.end({choice: choice, nounce: nounce.toString('hex')})
    parse.once('data', function (data) {
      if (weapons.indexOf(data.choice) === -1) {
        return stream.destroy(new Error('Remote sent invalid choice: ' + data.choice))
      }
      if (crypto.createHmac('sha256', new Buffer(data.nounce, 'hex')).update(data.choice).digest('hex') !== remoteProof) {
        return stream.destroy(new Error('Remote sent invalid proof'))
      }

      if (data.choice === choice) return stream.emit('tie', choice, data.choice)

      switch (choice) {
        case 'rock': return won(data.choice === 'scissors')
        case 'paper': return won(data.choice === 'rock')
        case 'scissors': return won(data.choice === 'paper')
      }

      stream.emit('error', new Error('Unexpected error (' + data.choice + ' vs ' + choice + ')'))

      function won (yes) {
        stream.emit(yes ? 'win' : 'lose', choice, data.choice)
      }
    })
  })

  serialize.write(proof)

  var stream = duplexify(parse, serialize)
  return stream
}
<commit_msg>local and remote proof have to be different<commit_after>var crypto = require('crypto')
var ndjson = require('ndjson')
var duplexify = require('duplexify')

var weapons = ['rock', 'paper', 'scissors']

module.exports = function (choice) {
  choice = choice.toLowerCase()

  if (weapons.indexOf(choice) === -1) throw new Error('Invalid choice: ' + choice)

  var nounce = crypto.randomBytes(32)
  var proof = crypto.createHmac('sha256', nounce).update(choice).digest('hex')

  var serialize = ndjson.serialize()
  var parse = ndjson.parse()

  parse.once('data', function (remoteProof) {
    if (remoteProof === proof) return stream.destroy(new Error('Remote sent invalid proof'))
    serialize.end({choice: choice, nounce: nounce.toString('hex')})
    parse.once('data', function (data) {
      if (weapons.indexOf(data.choice) === -1) {
        return stream.destroy(new Error('Remote sent invalid choice: ' + data.choice))
      }
      if (crypto.createHmac('sha256', new Buffer(data.nounce, 'hex')).update(data.choice).digest('hex') !== remoteProof) {
        return stream.destroy(new Error('Remote sent invalid proof'))
      }

      if (data.choice === choice) return stream.emit('tie', choice, data.choice)

      switch (choice) {
        case 'rock': return won(data.choice === 'scissors')
        case 'paper': return won(data.choice === 'rock')
        case 'scissors': return won(data.choice === 'paper')
      }

      stream.emit('error', new Error('Unexpected error (' + data.choice + ' vs ' + choice + ')'))

      function won (yes) {
        stream.emit(yes ? 'win' : 'lose', choice, data.choice)
      }
    })
  })

  serialize.write(proof)

  var stream = duplexify(parse, serialize)
  return stream
}
<|endoftext|>


<commit_before>const { init, callEndpoint } = require('./lib');

// return data['{name}Response']['{name}Result'] as most APIs seem to return from the hierarchy
// before any of the useful data is buried.
const digResponseResult = (name, data) => {
    try {
        return data[`${name}Response`][`${name}Result`];
    } catch (err) {
        return data;
    }
};

/*
    returns:
    { markets, marketParticipations }
    markets = MarketplaceId, DefaultCountryCode, DomainName, Name, DefaultCurrencyCode, DefaultLanguageCode
    marketParticipations = MarketplaceId, SellerId, HasSellerSuspendedListings
*/

// TODO: upgrade to call ListMarketplaceParticipationsByNextToken when a NextToken
// response is returned.
// FURTHER TODO: how smart can we make our framework? can we handle multiple requests
// to any endpoint that returns a NextToken ?
// EVEN FURTHER TODO: can we handle rate limiting, while we're doing that, and only
// return results when we get all the data?

const getMarketplaces = async () => {
    const result = await callEndpoint('ListMarketplaceParticipations');
    const result2 = digResponseResult('ListMarketplaceParticipations', result);

    // destructure result2.ListParticipations.Participation to marketParticipationsTemp
    const { ListParticipations: { Participation: marketParticipationsTemp } } = result2;
    // destructure result2.ListMarketplaces.Marketplace to marketsTemp
    const { ListMarketplaces: { Marketplace: marketsTemp } } = result2;

    let marketDetails = {};

    // map and filter
    const markets = marketsTemp.reduce((arr, market) => {
        // market = { MarketplaceId, DefaultCountryCode, DomainName, DefaultCurrencyCode, Name }
        // A2ZV50J4W1RKNI === "sim1.stores.amazon.com", "Non-Amazon"
        // A1MQXOICRS2Z7M === "siprod.stores.amazon.ca", "SI CA Prod Marketplace"
        // A2EUQ1WTGCTBG2 === "www.amazon.ca" "Amazon.ca"
        // ATVPDKIKX0DER === "www.amazon.com" "Amazon.com"
        // Looks like "Non-Amazon" and "SI CA Prod Marketplace" are test markets? maybe?
        if (market.MarketplaceId === 'A2ZV50J4W1RKNI' || market.MarketplaceId === 'A1MQXOICRS2Z7M') {
            return arr;
        }
        arr.push(market);
        marketDetails[market.MarketplaceId] = market;
        return arr;
    }, []);

    // marketParticipation = { MarketplaceId, SellerId, HasSellerSuspendedListings }
    // map and filter
    const marketParticipations = marketParticipationsTemp.reduce((arr, participation) => {
        if (participation.MarketplaceId === 'A2ZV50J4W1RKNI' || participation.MarketplaceId === 'A1MQXOICRS2Z7M') {
            return arr;
        }
        arr.push(participation);
        marketDetails[participation.MarketplaceId] = {
            ...marketDetails[participation.MarketplaceId],
            ...participation,
        };
        return arr;
    }, []);

    return { markets, marketParticipations, marketDetails };
}

// see https://docs.developer.amazonservices.com/en_UK/orders-2013-09-01/Orders_ListOrders.html
// returns
/*
    LatestShipDate: [Array], OrderType: [Array], PurchaseDate: [Array], AmazonOrderId: [Array],
    BuyerEmail: [Array], LastUpdateDate: [Array], IsReplacementOrder: [Array], ShipServiceLevel: [Array],
    NumberOfItemsShipped: [Array], OrderStatus: [Array], SalesChannel: [Array], ShippedByAmazonTFM: [Array],
    IsBusinessOrder: [Array], LatestDeliveryDate: [Array], NumberOfItemsUnshipped: [Array],
    PaymentMethodDetails: [Array], BuyerName: [Array], EarliestDeliveryDate: [Array],
    OrderTotal: [Array], IsPremiumOrder: [Array], EarliestShipDate: [Array], MarketplaceId: [Array],
    FulfillmentChannel: [Array], PaymentMethod: [Array], ShippingAddress: [Array],
    IsPrime: [Array], ShipmentServiceLevelCategory: [Array]
*/
// TODO: if provide a NextToken then call ListOrdersByNextToken ?
// TODO: provide an option to automatically call ListOrdersByNextToken if NextToken is received?
const listOrders = async (options) => {
    const results = await callEndpoint('ListOrders', options);
    const orders = digResponseResult('ListOrders', results);
    return orders.Orders.Order;
};

/*
  // http://docs.developer.amazonservices.com/en_US/finances/Finances_Datatypes.html#FinancialEvents
  ProductAdsPaymentEventList: '',
  RentalTransactionEventList: '',
  PayWithAmazonEventList: '',
  ServiceFeeEventList: { ServiceFeeEvent: [ [Object], [Object] ] },
  ServiceProviderCreditEventList: '',
  SellerDealPaymentEventList: '',
  SellerReviewEnrollmentPaymentEventList: '',
  DebtRecoveryEventList: '',
  ShipmentEventList: { ShipmentEvent: [ [Object], [Object], [Object], [Object] ] },
  RetrochargeEventList: '',
  SAFETReimbursementEventList: '',
  GuaranteeClaimEventList: '',
  ChargebackEventList: '',
  FBALiquidationEventList: '',
  LoanServicingEventList: '',
  RefundEventList: '',
  AdjustmentEventList:
   { AdjustmentEvent:
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ] },
  PerformanceBondRefundEventList: ''
*/
const listFinancialEvents = async (options) => {
    const results = await callEndpoint('ListFinancialEvents', options);
    const financialEvents = digResponseResult('ListFinancialEvents', results);
    return financialEvents;
}

/*
[ { Condition: 'NewItem',
    SupplyDetail: '',
    TotalSupplyQuantity: '0',
    FNSKU: 'B000WFVXGI',
    InStockSupplyQuantity: '0',
    ASIN: 'B000WFVXGI',
    SellerSKU: 'ND-X5EF-Z0N1' } ]
*/
const listInventorySupply = async (options) => {
    const results = await callEndpoint('ListInventorySupply', options);
    const inventorySupply = digResponseResult('ListInventorySupply', results);
    return inventorySupply.InventorySupplyList.member;
}

/*
returns
[ { Identifiers: { MarketplaceASIN: [Object] },
    AttributeSets: { 'ns2:ItemAttributes': [Object] },
    Relationships: '',
    SalesRankings: { SalesRank: [Array] } } ]
*/
const getMatchingProductForId = async (options) => {
    let obj = {};
    if (options.IdList) {
        obj = options.IdList.reduce((prev, curr, index) => {
            prev[`IdList.Id.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.IdList;
    }
    obj = Object.assign({}, obj, options);
    const products = await callEndpoint('GetMatchingProductForId', obj);

    try {
        const productList = digResponseResult('GetMatchingProductForProductId', products);
        const ret = productList.map(p => p.Products.Product);
        return ret;
    } catch (err) {
        return products;
    }
}

/*
{ ReportType: '_GET_MERCHANT_LISTINGS_DATA_',
  ReportProcessingStatus: '_SUBMITTED_',
  EndDate: '2017-07-31T06:17:53+00:00',
  Scheduled: 'false',
  ReportRequestId: '56938017378',
  SubmittedDate: '2017-07-31T06:17:53+00:00',
  StartDate: '2017-07-31T06:17:53+00:00' }
*/
const requestReport = async (options) => {
    const result = await callEndpoint('RequestReport', options);
    const reportRequests = digResponseResult('RequestReport', result);
    return reportRequests.ReportRequestInfo;
}

// interesting note: there are tons of reports returned by this API,
// apparently Amazon auto pulls reports, and many reports pulled in the Seller Central
// interface will also show up here. Somewhere in the docs, it says that Amazon
// keeps all reports for 90 days.

/*
[ { ReportType: '_GET_MERCHANT_LISTINGS_DATA_',
    ReportProcessingStatus: '_DONE_',
    EndDate: '2017-07-31T06:09:35+00:00',
    Scheduled: 'false',
    ReportRequestId: '56937017378',
    StartedProcessingDate: '2017-07-31T06:09:39+00:00',
    SubmittedDate: '2017-07-31T06:09:35+00:00',
    StartDate: '2017-07-31T06:09:35+00:00',
    CompletedDate: '2017-07-31T06:09:46+00:00',
    GeneratedReportId: '5935233306017378' } ]
*/
const getReportRequestList = async (options = {}) => {
    let obj = {};
    if (options.ReportRequestIdList) {
        obj = options.ReportRequestIdList.reduce((prev, curr, index) => {
            prev[`ReportRequestIdList.Id.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.ReportRequestIdList;
    }
    if (options.ReportTypeList) {
        obj = options.ReportTypeList.reduce((prev, curr, index) => {
            prev[`ReportTypeList.Type.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.ReportTypeList;
    }
    if (options.ReportProcessingStatusList) {
        obj = options.ReportProcessingStatusList.reduce((prev, curr, index) => {
            prev[`ReportProcessingStatusList.Status.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.ReportProcessingStatusList;
    }

    obj = Object.assign({}, obj, options);
    const result = await callEndpoint('GetReportRequestList', obj);
    // NextToken is under result.GetReportRequestListResponse.GetReportRequestListResult
    const reportRequestList = digResponseResult('GetReportRequestList', result);
    return result.ReportRequestInfo;
}

const getReport = async (options) => {
    const result = await callEndpoint('GetReport', options);
    return result;
}

const getReportList = async (options = {}) => {
    let obj = {};
    if (options.ReportRequestIdList) {
        obj = options.ReportRequestIdList.reduce((prev, curr, index) => {
            prev[`ReportRequestIdList.Id.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.ReportRequestIdList;
    }
    if (options.ReportTypeList) {
        obj = options.ReportTypeList.reduce((prev, curr, index) => {
            prev[`ReportTypeList.Type.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.ReportTypeList;
    }
    obj = Object.assign({}, obj, options);
    const result = await callEndpoint('GetReportList', obj);
    // NextToken should be in result.GetReportListResponse.GetReportListResult
    try {
        const cache = digResponseResult('GetReportList', result);
        const ret = {
            result: cache.ReportInfo,
            nextToken: cache.HasNext && cache.NextToken,
        }
        return ret;
    } catch (err) {
        return result;
    }
}

const getReportListByNextToken = async (options) => {
    const result = await callEndpoint('GetReportListByNextToken', options);
    try {
        const cache = digResponseResult('GetReportListByNextToken', result);
        const ret = {
            result: cache.ReportInfo,
            nextToken: cache.HasNext && cache.NextToken,
        };
        return ret;
    } catch (err) {
        return result;
    }
}

const getReportListAll = async (options = {}) => {
    let reports = [];
    const reportList = await getReportList(options);
    reports = reports.concat(reportList.result);
    let nextToken = reportList.nextToken;
    while (nextToken) {
        const nextPage = await getReportListByNextToken({ NextToken: nextToken });
        nextToken = nextPage.nextToken;
        reports = reports.concat(nextPage.result);
        await sleep(2000);
    }
    return reports;
}

// TODO: should we emit events notifying of things happening inside here?
// TODO: need to test all report types with this function, because not all reports return
// the same set of data - some are not giving a ReportRequestId for some reason?!
// perhaps there was an error in requestReport() regarding this.

// TODO: _GET_FLAT_FILE_ORDERS_DATA_ seems to always result in a cancelled report
// TODO: _GET_FLAT_FILE_PENDING_ORDERS_DATA_ results in undefined reportRequestId
// TODO: undefined reportRequestId results in us downloading a large list of reports
// when we call getReportRequestList()
// TODO: need to improve the throttling mechanism, waiting 45 seconds per test minimum sucks.

// known to work if given a StartDate: _GET_FLAT_FILE_ORDERS_DATA_, _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
// known to require calling GetReportList (therefore not yet working): _GET_SELLER_FEEDBACK_DATA_
// _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
//
// unsure why not working: _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ - i may not have any data.
// may require parameters? just cancells if not given parameters.
//  _GET_CONVERGED_FLAT_FILE_SOLD_LISTINGS_DATA_
//  _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_FLAT_FILE_ALL_RDERS_DATA_BY_ORDER_DATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_
//  _GET_FLAT_FILE_PENDING_ORDERS_DATA_ - may also have failed due to not having any pending orders?
// after I got to _GET_FLAT_FILE_PENDING_ORDERS_DATA_ .. getReportRequestList started throwing out
// "undefined" as a status. wtf?
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_

const requestAndDownloadReport = async (ReportType, file, reportParams = {}) => {
    async function checkReportComplete(reportRequestId) {
        console.log(`-- checking if report is complete ${reportRequestId}`);
        while (true) {
            const report = await getReportRequestList({
                ReportRequestIdList: [ reportRequestId ],
            });
            switch (report.ReportProcessingStatus) {
                case '_IN_PROGRESS_': // fallthrough intentional
                case '_SUBMITTED_': // fallthrough intentional
                    console.log(`-- retrying report ${reportRequestId} in 45 sec`);
                    await sleep(45000); // GetReportRequestList throttles to at most 10 requests, you get 1 back every 45 seconds
                    break;
                case '_CANCELLED_':
                    console.log(`-- cancelled: ${reportRequestId}`, report);
                    return { cancelled: true };
                case '_DONE_':
                    return report;
                case '_DONE_NO_DATA_':
                    return {};
                default:
                    console.log(`-- unknown status retry in 45 sec: ${report.ReportProcessingStatus}`, report);
                    await sleep(45000);
                    break;
            }
        }
    }

    console.log(`-- requesting report ${ReportType}`);
    const request = await requestReport({
        ReportType,
        ...reportParams,
    });
    const reportRequestId = request.ReportRequestId;
    await sleep(20000); // some requests may be available quickly, check after 20 sec
    const reportCheck = await checkReportComplete(reportRequestId);
    const ReportId = reportCheck.GeneratedReportId;
    // TODO: Some reports do not provide a GeneratedReportId and we need to call GetReportList to find the identifier!
    if (!ReportId) {
        console.warn('**** No ReportId received !! This is not yet handled');
        const reportList = await getReportList({
            ReportTypeList: [ ReportType ]
        });
        console.warn('**** reportList', reportList);
        return {};
        // TODO
    }
    const report = await getReport({ ReportId });
    if (file) {
        await writeFile(file, JSON.stringify(report, null, 4));
    }
    return report;
}

module.exports = {
    init,
    callEndpoint,
    getMarketplaces,
    listOrders,
    listFinancialEvents,
    listInventorySupply,
    getMatchingProductForId,
    requestReport,
    getReport,
    getReportList,
    getReportListByNextToken,
    getReportListAll,
    getReportRequestList,
    requestAndDownloadReport,
};
<commit_msg>remove special list handling from reports functions<commit_after>const { init, callEndpoint } = require('./lib');

// return data['{name}Response']['{name}Result'] as most APIs seem to return from the hierarchy
// before any of the useful data is buried.
const digResponseResult = (name, data) => {
    try {
        return data[`${name}Response`][`${name}Result`];
    } catch (err) {
        return data;
    }
};

/*
    returns:
    { markets, marketParticipations }
    markets = MarketplaceId, DefaultCountryCode, DomainName, Name, DefaultCurrencyCode, DefaultLanguageCode
    marketParticipations = MarketplaceId, SellerId, HasSellerSuspendedListings
*/

// TODO: upgrade to call ListMarketplaceParticipationsByNextToken when a NextToken
// response is returned.
// FURTHER TODO: how smart can we make our framework? can we handle multiple requests
// to any endpoint that returns a NextToken ?
// EVEN FURTHER TODO: can we handle rate limiting, while we're doing that, and only
// return results when we get all the data?

const getMarketplaces = async () => {
    const result = await callEndpoint('ListMarketplaceParticipations');
    const result2 = digResponseResult('ListMarketplaceParticipations', result);

    // destructure result2.ListParticipations.Participation to marketParticipationsTemp
    const { ListParticipations: { Participation: marketParticipationsTemp } } = result2;
    // destructure result2.ListMarketplaces.Marketplace to marketsTemp
    const { ListMarketplaces: { Marketplace: marketsTemp } } = result2;

    let marketDetails = {};

    // map and filter
    const markets = marketsTemp.reduce((arr, market) => {
        // market = { MarketplaceId, DefaultCountryCode, DomainName, DefaultCurrencyCode, Name }
        // A2ZV50J4W1RKNI === "sim1.stores.amazon.com", "Non-Amazon"
        // A1MQXOICRS2Z7M === "siprod.stores.amazon.ca", "SI CA Prod Marketplace"
        // A2EUQ1WTGCTBG2 === "www.amazon.ca" "Amazon.ca"
        // ATVPDKIKX0DER === "www.amazon.com" "Amazon.com"
        // Looks like "Non-Amazon" and "SI CA Prod Marketplace" are test markets? maybe?
        if (market.MarketplaceId === 'A2ZV50J4W1RKNI' || market.MarketplaceId === 'A1MQXOICRS2Z7M') {
            return arr;
        }
        arr.push(market);
        marketDetails[market.MarketplaceId] = market;
        return arr;
    }, []);

    // marketParticipation = { MarketplaceId, SellerId, HasSellerSuspendedListings }
    // map and filter
    const marketParticipations = marketParticipationsTemp.reduce((arr, participation) => {
        if (participation.MarketplaceId === 'A2ZV50J4W1RKNI' || participation.MarketplaceId === 'A1MQXOICRS2Z7M') {
            return arr;
        }
        arr.push(participation);
        marketDetails[participation.MarketplaceId] = {
            ...marketDetails[participation.MarketplaceId],
            ...participation,
        };
        return arr;
    }, []);

    return { markets, marketParticipations, marketDetails };
}

// see https://docs.developer.amazonservices.com/en_UK/orders-2013-09-01/Orders_ListOrders.html
// returns
/*
    LatestShipDate: [Array], OrderType: [Array], PurchaseDate: [Array], AmazonOrderId: [Array],
    BuyerEmail: [Array], LastUpdateDate: [Array], IsReplacementOrder: [Array], ShipServiceLevel: [Array],
    NumberOfItemsShipped: [Array], OrderStatus: [Array], SalesChannel: [Array], ShippedByAmazonTFM: [Array],
    IsBusinessOrder: [Array], LatestDeliveryDate: [Array], NumberOfItemsUnshipped: [Array],
    PaymentMethodDetails: [Array], BuyerName: [Array], EarliestDeliveryDate: [Array],
    OrderTotal: [Array], IsPremiumOrder: [Array], EarliestShipDate: [Array], MarketplaceId: [Array],
    FulfillmentChannel: [Array], PaymentMethod: [Array], ShippingAddress: [Array],
    IsPrime: [Array], ShipmentServiceLevelCategory: [Array]
*/
// TODO: if provide a NextToken then call ListOrdersByNextToken ?
// TODO: provide an option to automatically call ListOrdersByNextToken if NextToken is received?
const listOrders = async (options) => {
    const results = await callEndpoint('ListOrders', options);
    const orders = digResponseResult('ListOrders', results);
    return orders.Orders.Order;
};

/*
  // http://docs.developer.amazonservices.com/en_US/finances/Finances_Datatypes.html#FinancialEvents
  ProductAdsPaymentEventList: '',
  RentalTransactionEventList: '',
  PayWithAmazonEventList: '',
  ServiceFeeEventList: { ServiceFeeEvent: [ [Object], [Object] ] },
  ServiceProviderCreditEventList: '',
  SellerDealPaymentEventList: '',
  SellerReviewEnrollmentPaymentEventList: '',
  DebtRecoveryEventList: '',
  ShipmentEventList: { ShipmentEvent: [ [Object], [Object], [Object], [Object] ] },
  RetrochargeEventList: '',
  SAFETReimbursementEventList: '',
  GuaranteeClaimEventList: '',
  ChargebackEventList: '',
  FBALiquidationEventList: '',
  LoanServicingEventList: '',
  RefundEventList: '',
  AdjustmentEventList:
   { AdjustmentEvent:
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ] },
  PerformanceBondRefundEventList: ''
*/
const listFinancialEvents = async (options) => {
    const results = await callEndpoint('ListFinancialEvents', options);
    const financialEvents = digResponseResult('ListFinancialEvents', results);
    return financialEvents;
}

/*
[ { Condition: 'NewItem',
    SupplyDetail: '',
    TotalSupplyQuantity: '0',
    FNSKU: 'B000WFVXGI',
    InStockSupplyQuantity: '0',
    ASIN: 'B000WFVXGI',
    SellerSKU: 'ND-X5EF-Z0N1' } ]
*/
const listInventorySupply = async (options) => {
    const results = await callEndpoint('ListInventorySupply', options);
    const inventorySupply = digResponseResult('ListInventorySupply', results);
    return inventorySupply.InventorySupplyList.member;
}

/*
returns
[ { Identifiers: { MarketplaceASIN: [Object] },
    AttributeSets: { 'ns2:ItemAttributes': [Object] },
    Relationships: '',
    SalesRankings: { SalesRank: [Array] } } ]
*/
const getMatchingProductForId = async (options) => {
    let obj = {};
    if (options.IdList) {
        obj = options.IdList.reduce((prev, curr, index) => {
            prev[`IdList.Id.${index+1}`] = curr;
            return prev;
        }, {})
        delete options.IdList;
    }
    obj = Object.assign({}, obj, options);
    const products = await callEndpoint('GetMatchingProductForId', obj);

    try {
        const productList = digResponseResult('GetMatchingProductForProductId', products);
        const ret = productList.map(p => p.Products.Product);
        return ret;
    } catch (err) {
        return products;
    }
}

/*
{ ReportType: '_GET_MERCHANT_LISTINGS_DATA_',
  ReportProcessingStatus: '_SUBMITTED_',
  EndDate: '2017-07-31T06:17:53+00:00',
  Scheduled: 'false',
  ReportRequestId: '56938017378',
  SubmittedDate: '2017-07-31T06:17:53+00:00',
  StartDate: '2017-07-31T06:17:53+00:00' }
*/
const requestReport = async (options) => {
    const result = await callEndpoint('RequestReport', options);
    const reportRequests = digResponseResult('RequestReport', result);
    return reportRequests.ReportRequestInfo;
}

// interesting note: there are tons of reports returned by this API,
// apparently Amazon auto pulls reports, and many reports pulled in the Seller Central
// interface will also show up here. Somewhere in the docs, it says that Amazon
// keeps all reports for 90 days.

/*
[ { ReportType: '_GET_MERCHANT_LISTINGS_DATA_',
    ReportProcessingStatus: '_DONE_',
    EndDate: '2017-07-31T06:09:35+00:00',
    Scheduled: 'false',
    ReportRequestId: '56937017378',
    StartedProcessingDate: '2017-07-31T06:09:39+00:00',
    SubmittedDate: '2017-07-31T06:09:35+00:00',
    StartDate: '2017-07-31T06:09:35+00:00',
    CompletedDate: '2017-07-31T06:09:46+00:00',
    GeneratedReportId: '5935233306017378' } ]
*/
const getReportRequestList = async (options = {}) => {
    let obj = {};
    obj = Object.assign({}, obj, options);
    const result = await callEndpoint('GetReportRequestList', obj);
    // NextToken is under result.GetReportRequestListResponse.GetReportRequestListResult
    const reportRequestList = digResponseResult('GetReportRequestList', result);
    return result.ReportRequestInfo;
}

const getReport = async (options) => {
    const result = await callEndpoint('GetReport', options);
    return result;
}

const getReportList = async (options = {}) => {
    let obj = {};
    obj = Object.assign({}, obj, options);
    const result = await callEndpoint('GetReportList', obj);
    // NextToken should be in result.GetReportListResponse.GetReportListResult
    try {
        const cache = digResponseResult('GetReportList', result);
        const ret = {
            result: cache.ReportInfo,
            nextToken: cache.HasNext && cache.NextToken,
        }
        return ret;
    } catch (err) {
        return result;
    }
}

const getReportListByNextToken = async (options) => {
    const result = await callEndpoint('GetReportListByNextToken', options);
    try {
        const cache = digResponseResult('GetReportListByNextToken', result);
        const ret = {
            result: cache.ReportInfo,
            nextToken: cache.HasNext && cache.NextToken,
        };
        return ret;
    } catch (err) {
        return result;
    }
}

const getReportListAll = async (options = {}) => {
    let reports = [];
    const reportList = await getReportList(options);
    reports = reports.concat(reportList.result);
    let nextToken = reportList.nextToken;
    while (nextToken) {
        const nextPage = await getReportListByNextToken({ NextToken: nextToken });
        nextToken = nextPage.nextToken;
        reports = reports.concat(nextPage.result);
        await sleep(2000);
    }
    return reports;
}

// TODO: should we emit events notifying of things happening inside here?
// TODO: need to test all report types with this function, because not all reports return
// the same set of data - some are not giving a ReportRequestId for some reason?!
// perhaps there was an error in requestReport() regarding this.

// TODO: _GET_FLAT_FILE_ORDERS_DATA_ seems to always result in a cancelled report
// TODO: _GET_FLAT_FILE_PENDING_ORDERS_DATA_ results in undefined reportRequestId
// TODO: undefined reportRequestId results in us downloading a large list of reports
// when we call getReportRequestList()
// TODO: need to improve the throttling mechanism, waiting 45 seconds per test minimum sucks.

// known to work if given a StartDate: _GET_FLAT_FILE_ORDERS_DATA_, _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
// known to require calling GetReportList (therefore not yet working): _GET_SELLER_FEEDBACK_DATA_
// _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
//
// unsure why not working: _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_ - i may not have any data.
// may require parameters? just cancells if not given parameters.
//  _GET_CONVERGED_FLAT_FILE_SOLD_LISTINGS_DATA_
//  _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_FLAT_FILE_ALL_RDERS_DATA_BY_ORDER_DATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_
//  _GET_FLAT_FILE_PENDING_ORDERS_DATA_ - may also have failed due to not having any pending orders?
// after I got to _GET_FLAT_FILE_PENDING_ORDERS_DATA_ .. getReportRequestList started throwing out
// "undefined" as a status. wtf?
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
//  _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_

const requestAndDownloadReport = async (ReportType, file, reportParams = {}) => {
    async function checkReportComplete(reportRequestId) {
        console.log(`-- checking if report is complete ${reportRequestId}`);
        while (true) {
            const report = await getReportRequestList({
                ReportRequestIdList: [ reportRequestId ],
            });
            switch (report.ReportProcessingStatus) {
                case '_IN_PROGRESS_': // fallthrough intentional
                case '_SUBMITTED_': // fallthrough intentional
                    console.log(`-- retrying report ${reportRequestId} in 45 sec`);
                    await sleep(45000); // GetReportRequestList throttles to at most 10 requests, you get 1 back every 45 seconds
                    break;
                case '_CANCELLED_':
                    console.log(`-- cancelled: ${reportRequestId}`, report);
                    return { cancelled: true };
                case '_DONE_':
                    return report;
                case '_DONE_NO_DATA_':
                    return {};
                default:
                    console.log(`-- unknown status retry in 45 sec: ${report.ReportProcessingStatus}`, report);
                    await sleep(45000);
                    break;
            }
        }
    }

    console.log(`-- requesting report ${ReportType}`);
    const request = await requestReport({
        ReportType,
        ...reportParams,
    });
    const reportRequestId = request.ReportRequestId;
    await sleep(20000); // some requests may be available quickly, check after 20 sec
    const reportCheck = await checkReportComplete(reportRequestId);
    const ReportId = reportCheck.GeneratedReportId;
    // TODO: Some reports do not provide a GeneratedReportId and we need to call GetReportList to find the identifier!
    if (!ReportId) {
        console.warn('**** No ReportId received !! This is not yet handled');
        const reportList = await getReportList({
            ReportTypeList: [ ReportType ]
        });
        console.warn('**** reportList', reportList);
        return {};
        // TODO
    }
    const report = await getReport({ ReportId });
    if (file) {
        await writeFile(file, JSON.stringify(report, null, 4));
    }
    return report;
}

module.exports = {
    init,
    callEndpoint,
    getMarketplaces,
    listOrders,
    listFinancialEvents,
    listInventorySupply,
    getMatchingProductForId,
    requestReport,
    getReport,
    getReportList,
    getReportListByNextToken,
    getReportListAll,
    getReportRequestList,
    requestAndDownloadReport,
};
<|endoftext|>