<commit_before>var normalizeYAxis = false;

// Requested data
var reqFilter = "";
var reqCorrelationQuery = null;

// Data currently being displayed
var curRecords = null;
var curOverlay = null;

var inFlightRequest = null;

// http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
// fix this to output decimals correctly
function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

function readBtsfRecord(dataBuf, offset, H) {
  var dv = new DataView(dataBuf);
  var N = dv.getUint32(offset+4*0, true);
  var L = dv.getUint32(offset+4*1, true);

  var C = null;
  if(H > 8) {
    C = dv.getFloat32(offset+4*2, true);
  }

  var decoder = new TextDecoder();
  var strView = new DataView(dataBuf, offset+H, L);
  var str = decoder.decode(strView);

  var data = [];
  var datOffset = offset+H+L;
  for(var i = 0; i < N; i++) {
    var T = dv.getInt32(datOffset+i*8, true);
    var D = dv.getFloat32(datOffset+i*8+4, true);
    data.push({t: T, v: D});
  }

  return {size: H+L+N*8, record: {name: str, data: data, corr: C}};
}

function readBtsfFile(dataBuf) {
  var dv = new DataView(dataBuf);
  var V = dv.getUint32(0, true);
  var F = dv.getUint32(4*1, true);
  var H = dv.getUint32(4*2, true);
  var R = dv.getUint32(4*3, true);
  console.log("loaded file",[V,F,H,R]);

  var offset = F;
  var records = [];
  for(var i = 0; i < R; i++) {
    var res = readBtsfRecord(dataBuf, offset, H);
    offset += res.size;
    records.push(res.record);
  }
  return records;
}

function serializeBtsfRecord(record) {
  var encoder = new TextEncoder("utf-8");
  var nameBuf = encoder.encode(record.name);

  var length = 4*4+4*2+nameBuf.length+record.data.length*8;
  var dataBuf = new ArrayBuffer(length);
  var dv = new DataView(dataBuf);

  // header
  dv.setUint32(4*0, 1, true);
  dv.setUint32(4*1, 4*4, true);
  dv.setUint32(4*2, 4*2, true);
  dv.setUint32(4*3, 1, true);

  // record
  dv.setUint32(16+4*0, record.data.length, true);
  dv.setUint32(16+4*1, nameBuf.length, true);
  for (var i = 0; i < nameBuf.length; i++) {
    dv.setUint8(6*4+i, nameBuf[i]);
  }
  for (var i = 0; i < record.data.length; i++) {
    dv.setInt32(6*4+nameBuf.length+i*8, record.data[i].t, true);
    dv.setFloat32(6*4+nameBuf.length+i*8+4, record.data[i].v, true);
  }

  return dataBuf;
}

function maybeTrim(name, len) {
  if(name.length > len-3) {
    return name.slice(0,len-3)+"...";
  } else {
    return name;
  }
}

function drawGraphLine(ctx,w,h,minT,maxT,data,trace) {
  var maxV = _.max(data, function(p) { return p.v; }).v;
  var minV = _.min(data, function(p) { return p.v; }).v;

  ctx.lineWidth = 1.0;
  ctx.beginPath();
  ctx.moveTo(0,h);
  // TODO: don't render way more points than there are horizontal pixels in the graph
  for(var i = 0; i < data.length; i++) {
    var x = (data[i].t-minT)/(maxT-minT)*w;
    var yFrac;
    if(normalizeYAxis) {
      yFrac = (data[i].v-minV)/(maxV-minV);
    } else {
      yFrac = (data[i].v)/(maxV); // TODO: account for possible presence of negative numbers
    }
    var y = yFrac*(h-5)+2;
    if(i == 0) ctx.moveTo(x,h-y);
    ctx.lineTo(x,h-y);
  }
  ctx.stroke();

  if(trace !== null) {
    var traceT = trace.x/w*(maxT-minT)+minT;
    var closestPt = _.min(data, function(p) { return Math.abs(p.t - traceT); });

    var x = (closestPt.t-minT)/(maxT-minT)*w;
    ctx.strokeStyle = "#EF5350";
    ctx.lineWidth = 2.0;
    ctx.beginPath();
    ctx.moveTo(x,h);
    ctx.lineTo(x,0);
    ctx.stroke();

    ctx.fillStyle = "#000";
    var textX = x+8;
    var date = new Date(closestPt.t*1000);
    var dateText = date.getFullYear() + "/" + (date.getMonth()+1);
    var valText = numberWithCommas(closestPt.v);

    var dateStyle = "10px sans-serif";
    ctx.font = dateStyle;
    var textW = ctx.measureText(dateText).width;

    ctx.font = "15px sans-serif";
    textW = Math.max(textW, ctx.measureText(valText).width);
    if(textX+textW > w) {
      textX = x - textW - 8;
    }
    ctx.fillText(valText, textX, trace.y);
    ctx.fillStyle = "#78909C"
    ctx.font = dateStyle;
    ctx.fillText(dateText, textX, trace.y-15);
  }
}

function drawGraph(canvasEl, data, trace) {
  var ctx = canvasEl.getContext("2d");
  ctx.fillStyle = "white";
  ctx.fillRect(0,0,canvasEl.width,canvasEl.height);

  var maxT = _.max(data, function(p) { return p.t; }).t;
  var minT = _.min(data, function(p) { return p.t; }).t;
  if(curOverlay !== null) {
    maxT = Math.max(maxT,_.max(curOverlay, function(p) { return p.t; }).t);
    minT = Math.min(minT,_.min(curOverlay, function(p) { return p.t; }).t);
    ctx.strokeStyle = "grey";
    drawGraphLine(ctx,canvasEl.width,canvasEl.height,minT,maxT, curOverlay, null);
  }
  ctx.strokeStyle = "#2196F3";
  drawGraphLine(ctx,canvasEl.width,canvasEl.height,minT,maxT, data, trace);
}

function displayRecords(records, maxRecords) {
  var numToDisplay = Math.min(maxRecords, records.length);
  setNumberOfGraphs(numToDisplay);

  for(var i = 0; i < numToDisplay; i++) {
    var label = document.getElementById("label-"+i);
    label.innerText = maybeTrim(records[i].name,60);
    label.title = records[i].name;

    var corrEl = document.getElementById("corr-"+i);
    if(records[i].corr !== null) {
      corrEl.style.display = "initial";
      corrEl.innerText = "r = " + records[i].corr.toFixed(3);
    } else {
      corrEl.style.display = "none";
    }

    var link = document.getElementById("btn-"+i);
    // needed because JS closures interact weirdly with loops
    (function(){
      var record = records[i];
      link.onclick = function() {
        findCorrelations(record);
      }
    })();

    var canvasEl = document.getElementById("canv-"+i);
    drawGraph(canvasEl, records[i].data, null);
  }
}

// sets up the DOM with the right number of graph boxes
function setNumberOfGraphs(n) {
  var graphsDiv = document.getElementById("graphs");
  var numPresent = graphsDiv.children.length;
  var delta = n - numPresent;

  if(delta > 0) {
    for(var i = 0; i < delta; i++) {
      var graphDiv = document.createElement("div");
      graphDiv.className = "graph";

      var label = document.createElement("h4");
      label.id = "label-"+(numPresent+i);
      graphDiv.appendChild(label);

      var canvas = document.createElement("canvas");
      canvas.id = "canv-"+(numPresent+i);
      canvas.height = 160;
      canvas.width = 300;
      graphDiv.appendChild(canvas);

      var correlate = document.createElement("img");
      correlate.id = "btn-"+(numPresent+i);
      correlate.src = "graph-icon.svg";
      graphDiv.appendChild(correlate);

      var correlation = document.createElement("span");
      correlation.innerText = "r = 0.8";
      correlation.className = "correlation"
      correlation.id = "corr-"+(numPresent+i);
      graphDiv.appendChild(correlation);

      (function(){
        var curI = (numPresent+i);
        var curCanvas = canvas;
        canvas.addEventListener('mousemove', function(evt) {
          drawGraph(curCanvas, curRecords[curI].data, {x: evt.offsetX, y: evt.offsetY});
        }, false);
        canvas.addEventListener('mouseout', function(evt) {
          drawGraph(curCanvas, curRecords[curI].data, null);
        }, false);
      })();

      graphsDiv.appendChild(graphDiv);
    }
  } else if(delta < 0) {
    for(var i = 0; i < -delta; i++) {
      graphsDiv.children[numPresent-1-i].remove();
    }
  }
}

function redisplay() {
  normalizeYAxis = !document.getElementById("zeroYAxis").checked;
  if(reqCorrelationQuery !== null) {
    document.getElementById("clearCorrButton").style.display = "inline";
  } else {
    document.getElementById("clearCorrButton").style.display = "none";
  }
  displayRecords(curRecords, 100);
}

// ==== Changing requested data

function findCorrelations(record) {
  var dataBuf = serializeBtsfRecord(record);
  reqCorrelationQuery = dataBuf;
  curOverlay = record.data;

  reqFilter = "";
  document.getElementById('filter-box').value = "";
  document.getElementById('zeroYAxis').checked = false;

  updateFromServer();
}

function filterGraphs() {
  var filter = document.getElementById("filter-box").value;
  if(filter === reqFilter) return;
  reqFilter = filter;
  updateFromServer();
}

function clearCorr() {
  reqCorrelationQuery = null;
  curOverlay = null;
  updateFromServer();
}

// ==== Fetching requested data

function handleNewData(oEvent) {
  var arrayBuffer = oEvent.target.response; // Note: not oReq.responseText
  if (arrayBuffer) {
    curRecords = readBtsfFile(arrayBuffer);
    redisplay();
  } else {
    console.log("Couldn't fetch file " + url);
  }

  // TODO: maybe instead of cancelling in flight requests when new ones are sent out
  // perhaps we can just always prefer results from requests that were *sent* more recently
  // even if they arrive earlier due to weird networking things.
  inFlightRequest = null;
}

function fetchCorrelationData(corrBuffer, filter) {
  if(inFlightRequest !== null) inFlightRequest.abort();
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "/find?"+encodeURIComponent(filter), true);
  xhr.responseType = "arraybuffer";
  xhr.onload = handleNewData;
  xhr.send(new DataView(corrBuffer));
  inFlightRequest = xhr;
}

function fetchRawData(filter) {
  if(inFlightRequest !== null) inFlightRequest.abort();
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/raw?"+encodeURIComponent(filter), true);
  xhr.responseType = "arraybuffer";
  xhr.onload = handleNewData;
  xhr.send(null);
  inFlightRequest = xhr;
}

function updateFromServer() {
  if(reqCorrelationQuery !== null) {
    fetchCorrelationData(reqCorrelationQuery, reqFilter);
  } else {
    fetchRawData(reqFilter);
  }
}

function init() {
  updateFromServer();
  document.getElementById("filter-box").focus();
}
<commit_msg>Nicer number formatting<commit_after>var normalizeYAxis = false;

// Requested data
var reqFilter = "";
var reqCorrelationQuery = null;

// Data currently being displayed
var curRecords = null;
var curOverlay = null;

var inFlightRequest = null;

// http://stackoverflow.com/questions/149055/how-can-i-format-numbers-as-money-in-javascript
Number.prototype.formatNice = function(c, d, t){
  var n = this,
      c = isNaN(c = Math.abs(c)) ? 2 : c,
      d = d == undefined ? "." : d,
      t = t == undefined ? "," : t,
      s = n < 0 ? "-" : "",
      i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
      j = (j = i.length) > 3 ? j % 3 : 0;
     return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

function readBtsfRecord(dataBuf, offset, H) {
  var dv = new DataView(dataBuf);
  var N = dv.getUint32(offset+4*0, true);
  var L = dv.getUint32(offset+4*1, true);

  var C = null;
  if(H > 8) {
    C = dv.getFloat32(offset+4*2, true);
  }

  var decoder = new TextDecoder();
  var strView = new DataView(dataBuf, offset+H, L);
  var str = decoder.decode(strView);

  var data = [];
  var datOffset = offset+H+L;
  for(var i = 0; i < N; i++) {
    var T = dv.getInt32(datOffset+i*8, true);
    var D = dv.getFloat32(datOffset+i*8+4, true);
    data.push({t: T, v: D});
  }

  return {size: H+L+N*8, record: {name: str, data: data, corr: C}};
}

function readBtsfFile(dataBuf) {
  var dv = new DataView(dataBuf);
  var V = dv.getUint32(0, true);
  var F = dv.getUint32(4*1, true);
  var H = dv.getUint32(4*2, true);
  var R = dv.getUint32(4*3, true);
  console.log("loaded file",[V,F,H,R]);

  var offset = F;
  var records = [];
  for(var i = 0; i < R; i++) {
    var res = readBtsfRecord(dataBuf, offset, H);
    offset += res.size;
    records.push(res.record);
  }
  return records;
}

function serializeBtsfRecord(record) {
  var encoder = new TextEncoder("utf-8");
  var nameBuf = encoder.encode(record.name);

  var length = 4*4+4*2+nameBuf.length+record.data.length*8;
  var dataBuf = new ArrayBuffer(length);
  var dv = new DataView(dataBuf);

  // header
  dv.setUint32(4*0, 1, true);
  dv.setUint32(4*1, 4*4, true);
  dv.setUint32(4*2, 4*2, true);
  dv.setUint32(4*3, 1, true);

  // record
  dv.setUint32(16+4*0, record.data.length, true);
  dv.setUint32(16+4*1, nameBuf.length, true);
  for (var i = 0; i < nameBuf.length; i++) {
    dv.setUint8(6*4+i, nameBuf[i]);
  }
  for (var i = 0; i < record.data.length; i++) {
    dv.setInt32(6*4+nameBuf.length+i*8, record.data[i].t, true);
    dv.setFloat32(6*4+nameBuf.length+i*8+4, record.data[i].v, true);
  }

  return dataBuf;
}

function maybeTrim(name, len) {
  if(name.length > len-3) {
    return name.slice(0,len-3)+"...";
  } else {
    return name;
  }
}

function drawGraphLine(ctx,w,h,minT,maxT,data,trace) {
  var maxV = _.max(data, function(p) { return p.v; }).v;
  var minV = _.min(data, function(p) { return p.v; }).v;

  ctx.lineWidth = 1.0;
  ctx.beginPath();
  ctx.moveTo(0,h);
  // TODO: don't render way more points than there are horizontal pixels in the graph
  for(var i = 0; i < data.length; i++) {
    var x = (data[i].t-minT)/(maxT-minT)*w;
    var yFrac;
    if(normalizeYAxis) {
      yFrac = (data[i].v-minV)/(maxV-minV);
    } else {
      yFrac = (data[i].v)/(maxV); // TODO: account for possible presence of negative numbers
    }
    var y = yFrac*(h-5)+2;
    if(i == 0) ctx.moveTo(x,h-y);
    ctx.lineTo(x,h-y);
  }
  ctx.stroke();

  if(trace !== null) {
    var traceT = trace.x/w*(maxT-minT)+minT;
    var closestPt = _.min(data, function(p) { return Math.abs(p.t - traceT); });

    var x = (closestPt.t-minT)/(maxT-minT)*w;
    ctx.strokeStyle = "#EF5350";
    ctx.lineWidth = 2.0;
    ctx.beginPath();
    ctx.moveTo(x,h);
    ctx.lineTo(x,0);
    ctx.stroke();

    ctx.fillStyle = "#000";
    var textX = x+8;
    var date = new Date(closestPt.t*1000);
    var dateText = date.getFullYear() + "/" + (date.getMonth()+1);
    var valText = (closestPt.v).formatNice(2);

    var dateStyle = "10px sans-serif";
    ctx.font = dateStyle;
    var textW = ctx.measureText(dateText).width;

    ctx.font = "15px sans-serif";
    textW = Math.max(textW, ctx.measureText(valText).width);
    if(textX+textW > w) {
      textX = x - textW - 8;
    }
    ctx.fillText(valText, textX, trace.y);
    ctx.fillStyle = "#78909C"
    ctx.font = dateStyle;
    ctx.fillText(dateText, textX, trace.y-15);
  }
}

function drawGraph(canvasEl, data, trace) {
  var ctx = canvasEl.getContext("2d");
  ctx.fillStyle = "white";
  ctx.fillRect(0,0,canvasEl.width,canvasEl.height);

  var maxT = _.max(data, function(p) { return p.t; }).t;
  var minT = _.min(data, function(p) { return p.t; }).t;
  if(curOverlay !== null) {
    maxT = Math.max(maxT,_.max(curOverlay, function(p) { return p.t; }).t);
    minT = Math.min(minT,_.min(curOverlay, function(p) { return p.t; }).t);
    ctx.strokeStyle = "grey";
    drawGraphLine(ctx,canvasEl.width,canvasEl.height,minT,maxT, curOverlay, null);
  }
  ctx.strokeStyle = "#2196F3";
  drawGraphLine(ctx,canvasEl.width,canvasEl.height,minT,maxT, data, trace);
}

function displayRecords(records, maxRecords) {
  var numToDisplay = Math.min(maxRecords, records.length);
  setNumberOfGraphs(numToDisplay);

  for(var i = 0; i < numToDisplay; i++) {
    var label = document.getElementById("label-"+i);
    label.innerText = maybeTrim(records[i].name,60);
    label.title = records[i].name;

    var corrEl = document.getElementById("corr-"+i);
    if(records[i].corr !== null) {
      corrEl.style.display = "initial";
      corrEl.innerText = "r = " + records[i].corr.toFixed(3);
    } else {
      corrEl.style.display = "none";
    }

    var link = document.getElementById("btn-"+i);
    // needed because JS closures interact weirdly with loops
    (function(){
      var record = records[i];
      link.onclick = function() {
        findCorrelations(record);
      }
    })();

    var canvasEl = document.getElementById("canv-"+i);
    drawGraph(canvasEl, records[i].data, null);
  }
}

// sets up the DOM with the right number of graph boxes
function setNumberOfGraphs(n) {
  var graphsDiv = document.getElementById("graphs");
  var numPresent = graphsDiv.children.length;
  var delta = n - numPresent;

  if(delta > 0) {
    for(var i = 0; i < delta; i++) {
      var graphDiv = document.createElement("div");
      graphDiv.className = "graph";

      var label = document.createElement("h4");
      label.id = "label-"+(numPresent+i);
      graphDiv.appendChild(label);

      var canvas = document.createElement("canvas");
      canvas.id = "canv-"+(numPresent+i);
      canvas.height = 160;
      canvas.width = 300;
      graphDiv.appendChild(canvas);

      var correlate = document.createElement("img");
      correlate.id = "btn-"+(numPresent+i);
      correlate.src = "graph-icon.svg";
      graphDiv.appendChild(correlate);

      var correlation = document.createElement("span");
      correlation.innerText = "r = 0.8";
      correlation.className = "correlation"
      correlation.id = "corr-"+(numPresent+i);
      graphDiv.appendChild(correlation);

      (function(){
        var curI = (numPresent+i);
        var curCanvas = canvas;
        canvas.addEventListener('mousemove', function(evt) {
          drawGraph(curCanvas, curRecords[curI].data, {x: evt.offsetX, y: evt.offsetY});
        }, false);
        canvas.addEventListener('mouseout', function(evt) {
          drawGraph(curCanvas, curRecords[curI].data, null);
        }, false);
      })();

      graphsDiv.appendChild(graphDiv);
    }
  } else if(delta < 0) {
    for(var i = 0; i < -delta; i++) {
      graphsDiv.children[numPresent-1-i].remove();
    }
  }
}

function redisplay() {
  normalizeYAxis = !document.getElementById("zeroYAxis").checked;
  if(reqCorrelationQuery !== null) {
    document.getElementById("clearCorrButton").style.display = "inline";
  } else {
    document.getElementById("clearCorrButton").style.display = "none";
  }
  displayRecords(curRecords, 100);
}

// ==== Changing requested data

function findCorrelations(record) {
  var dataBuf = serializeBtsfRecord(record);
  reqCorrelationQuery = dataBuf;
  curOverlay = record.data;

  reqFilter = "";
  document.getElementById('filter-box').value = "";
  document.getElementById('zeroYAxis').checked = false;

  updateFromServer();
}

function filterGraphs() {
  var filter = document.getElementById("filter-box").value;
  if(filter === reqFilter) return;
  reqFilter = filter;
  updateFromServer();
}

function clearCorr() {
  reqCorrelationQuery = null;
  curOverlay = null;
  updateFromServer();
}

// ==== Fetching requested data

function handleNewData(oEvent) {
  var arrayBuffer = oEvent.target.response; // Note: not oReq.responseText
  if (arrayBuffer) {
    curRecords = readBtsfFile(arrayBuffer);
    redisplay();
  } else {
    console.log("Couldn't fetch file " + url);
  }

  // TODO: maybe instead of cancelling in flight requests when new ones are sent out
  // perhaps we can just always prefer results from requests that were *sent* more recently
  // even if they arrive earlier due to weird networking things.
  inFlightRequest = null;
}

function fetchCorrelationData(corrBuffer, filter) {
  if(inFlightRequest !== null) inFlightRequest.abort();
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "/find?"+encodeURIComponent(filter), true);
  xhr.responseType = "arraybuffer";
  xhr.onload = handleNewData;
  xhr.send(new DataView(corrBuffer));
  inFlightRequest = xhr;
}

function fetchRawData(filter) {
  if(inFlightRequest !== null) inFlightRequest.abort();
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/raw?"+encodeURIComponent(filter), true);
  xhr.responseType = "arraybuffer";
  xhr.onload = handleNewData;
  xhr.send(null);
  inFlightRequest = xhr;
}

function updateFromServer() {
  if(reqCorrelationQuery !== null) {
    fetchCorrelationData(reqCorrelationQuery, reqFilter);
  } else {
    fetchRawData(reqFilter);
  }
}

function init() {
  updateFromServer();
  document.getElementById("filter-box").focus();
}
<|endoftext|>


<commit_before>const fs = require("fs");
const webdriver = require("selenium-webdriver");
const driver = new webdriver.Builder().forBrowser("chrome").build();

driver.get("http://localhost:8080/test/");
driver.wait(webdriver.until.titleMatches(/: Complete$/), 10000).then(() => {
    driver.executeScript("return document.documentElement.outerHTML").then((html) => {
        fs.writeFile("test-result.html", html, (err) => {
            if (err) throw err;
            driver.findElements(webdriver.By.css(".test")).then((tests) => {
                driver.findElements(webdriver.By.css(".test.pass:not(.pending)")).then((passedTests) => {
                    driver.findElements(webdriver.By.css(".test.fail")).then((failedTests) => {
                        driver.findElements(webdriver.By.css(".test.pending")).then((pendingTests) => {
                            if (!tests.length) throw "No tests are evaluated.";
                            console.log(`${passedTests.length} of ${tests.length} test${passedTests.length === 1 ? " is": "s are"} passed.`);
                            console.log(`${failedTests.length} of ${tests.length} test${failedTests.length === 1 ? " is": "s are"} failed.`);
                            console.log(`${pendingTests.length} of ${tests.length} test${pendingTests.length === 1 ? " is": "s are"} pended.`);
                            const exitCode = failedTests.length ? 1: 0;
                            driver.quit();
                            process.exit(exitCode);
                        });
                    });
                });
            });
        });
    });
}).catch((err) => {
    console.error(err);
    driver.quit();
    process.exit(1);
});
<commit_msg>Setting circleci up.<commit_after>const fs = require("fs");
const webdriver = require("selenium-webdriver");
const driver = new webdriver.Builder().forBrowser("chrome").build();

driver.get("http://localhost:8080/test/");
driver.wait(webdriver.until.titleMatches(/: Complete$/), 10000).then(() => {
    driver.executeScript("return document.documentElement.outerHTML").then((html) => {
        fs.writeFile("test-result.html", html, (err) => {
            if (err) throw err;
            driver.findElements(webdriver.By.css(".test")).then((tests) => {
                driver.findElements(webdriver.By.css(".test.pass:not(.pending)")).then((passedTests) => {
                    driver.findElements(webdriver.By.css(".test.fail")).then((failedTests) => {
                        driver.findElements(webdriver.By.css(".test.pending")).then((pendingTests) => {
                            if (!tests.length) throw "No tests are evaluated.";
                            const colorRed = "\u001b[31m";
                            const colorGreen = "\u001b[32m";
                            const colorCyan = "\u001b[36m";
                            const colorReset = "\u001b[0m";
                            let passed = passedTests.length.toString(10);
                            let failed = failedTests.length.toString(10);
                            let pending = pendingTests.length.toString(10);
                            const digits = tests.length.toString(10).length;
                            passed = " ".repeat(digits - passed.length) + passed;
                            failed = " ".repeat(digits - failed.length) + failed;
                            pending = " ".repeat(digits - pending.length) + pending;
                            console.log(`${colorGreen}${passed} of ${tests.length} test${passedTests.length === 1 ? " is": "s are"} passed.${colorReset}`);
                            console.log(`${colorRed}${failed} of ${tests.length} test${failedTests.length === 1 ? " is": "s are"} failed.${colorReset}`);
                            console.log(`${colorCyan}${pending} of ${tests.length} test${pendingTests.length === 1 ? " is": "s are"} pended.${colorReset}`);
                            const exitCode = failedTests.length ? 1: 0;
                            driver.quit();
                            process.exit(exitCode);
                        });
                    });
                });
            });
        });
    });
}).catch((err) => {
    console.error(err);
    driver.quit();
    process.exit(1);
});
<|endoftext|>


<commit_before>"use strict"
var R = require("ramda");
var fs = require("fs-extra");
var path = require("path");
var minimist = require("minimist");
var chalk = require("chalk");
var toBool = require("js-util").toBool;
var shell = require("shelljs");

// Ensure the 'lib' has been built.
if (!fs.existsSync(path.join(__dirname, "lib"))) {
  shell.exec("npm run build");
}
var server = require("./lib/server");

// Retrieve command-line arguments.
var args = process.argv.slice(2);
args = args.length > 0 ? args = minimist(args) : {};

// Set initialization paths if [--init] was passed.
if (args.init) {

  console.log(chalk.red("TODO - Turn on --prod"));

  // args.prod = true;
  args.entry = path.join(__dirname, "/lib/client/entry.js");
  args.output = path.join(__dirname, "/public/js/ui-harness.js");
}

/**
 * Look for arguments passed in at the command-line,
 * and builds the javascript using Webpack.
 *
 * Command-line arguments:
 *
 *           --entry:   Required. Path(s) to the files to build.
 *                      Comma-seperated to include multiple paths.
 *                      Example: --entry ./src/specs
 *
 *           --output:  Optional. Path to save the output to.
 *
 *           --prod:    Optional. Flag indicating if the JS should be build in production mode
 *                      meaning optimizations (like minification) are employed.
 *                      Default: true
 *
 */
if (R.is(String, args.entry)) {
  server.bundle({
    entry: args.entry.split(","),
    output: args.output,
    isProduction: toBool(args.prod)
  });
} else {
  console.log(chalk.red("No entry path was specified, for example: `--entry ./src/specs`\n"));
}
<commit_msg>Remove warning.<commit_after>"use strict"
var R = require("ramda");
var fs = require("fs-extra");
var path = require("path");
var minimist = require("minimist");
var chalk = require("chalk");
var toBool = require("js-util").toBool;
var shell = require("shelljs");

// Ensure the 'lib' has been built.
if (!fs.existsSync(path.join(__dirname, "lib"))) {
  shell.exec("npm run build");
}
var server = require("./lib/server");

// Retrieve command-line arguments.
var args = process.argv.slice(2);
args = args.length > 0 ? args = minimist(args) : {};

// Set initialization paths if [--init] was passed.
if (args.init) {
  // args.prod = true;
  args.entry = path.join(__dirname, "/lib/client/entry.js");
  args.output = path.join(__dirname, "/public/js/ui-harness.js");
}

/**
 * Look for arguments passed in at the command-line,
 * and builds the javascript using Webpack.
 *
 * Command-line arguments:
 *
 *           --entry:   Required. Path(s) to the files to build.
 *                      Comma-seperated to include multiple paths.
 *                      Example: --entry ./src/specs
 *
 *           --output:  Optional. Path to save the output to.
 *
 *           --prod:    Optional. Flag indicating if the JS should be build in production mode
 *                      meaning optimizations (like minification) are employed.
 *                      Default: true
 *
 */
if (R.is(String, args.entry)) {
  server.bundle({
    entry: args.entry.split(","),
    output: args.output,
    isProduction: toBool(args.prod)
  });
} else {
  console.log(chalk.red("No entry path was specified, for example: `--entry ./src/specs`\n"));
}
<|endoftext|>


<commit_before><commit_msg>Commit solution for combinations<commit_after><|endoftext|>


<commit_before><commit_msg>no bundled plugins while developing<commit_after><|endoftext|>


<commit_before>/*    Copyright 2019-2021 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename)

const Sensor = require('./Sensor.js').Sensor

const Promise = require('bluebird')
const extensionManager = require('./ExtensionManager.js')

const configServerKey = "ext.guardian.socketio.server";
const configRegionKey = "ext.guardian.socketio.region";
const configBizModeKey = "ext.guardian.business";
const configAdminStatusKey = "ext.guardian.socketio.adminStatus";

const rclient = require('../util/redis_manager.js').getRedisClient();

const io = require('socket.io-client');

const EncipherTool = require('../net2/EncipherTool.js');
const et = new EncipherTool();

const CloudWrapper = require('../api/lib/CloudWrapper.js');
const cw = new CloudWrapper();
const receicveMessageAsync = Promise.promisify(cw.getCloud().receiveMessage).bind(cw.getCloud());
const encryptMessageAsync = Promise.promisify(cw.getCloud().encryptMessage).bind(cw.getCloud());

const zlib = require('zlib');
const deflateAsync = Promise.promisify(zlib.deflate);

class GuardianSensor extends Sensor {
  constructor() {
    super();
    this.realtimeExpireDate = 0;
    this.realtimeRunning = 0;
  }

  async apiRun() {
    extensionManager.onGet("guardianSocketioServer", (msg) => {
      return this.getServer();
    });

    extensionManager.onSet("guardianSocketioServer", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      return this.setServer(data.server, data.region);
    });

    extensionManager.onGet("guardian.business", async (msg) => {
      return this.getBusiness();
    });

    extensionManager.onSet("guardian.business", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      await rclient.setAsync(configBizModeKey, JSON.stringify(data));
    });

    extensionManager.onGet("guardianSocketioRegion", (msg) => {
      return this.getRegion();
    });

    extensionManager.onCmd("startGuardianSocketioServer", (msg, data) => {
      return this.start();
    });

    extensionManager.onCmd("stopGuardianSocketioServer", (msg, data) => {
      return this.stop();
    });

    extensionManager.onCmd("resetGuardian", (msg, data) => {
      return this.reset();
    });

    extensionManager.onCmd("setAndStartGuardianService", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      const socketioServer = data.server;
      if (!socketioServer) {
        throw new Error("invalid guardian relay server");
      }
      const forceRestart = !this.socket || (await this.getRegion() != data.region) || (await this.getServer() != socketioServer)
      await this.setServer(socketioServer, data.region);

      forceRestart && await this.start();
    });

    const adminStatusOn = await this.isAdminStatusOn();
    if (adminStatusOn) {
      await this.start();
    }
  }

  async setServer(server, region) {
    if (server) {
      if (region) {
        await rclient.setAsync(configRegionKey, region);
      } else {
        await rclient.delAsync(configRegionKey);
      }
      return rclient.setAsync(configServerKey, server);
    } else {
      throw new Error("invalid server");
    }
  }

  async locked() {
    const business = await this.getBusiness(); // if the box belong to MSP, deny from logging to other web container or my.firewalla.com
    if (business && business.type == 'msp') {
      return true;
    }
    return false;
  }

  async getBusiness() {
    const data = await rclient.getAsync(configBizModeKey);
    if (!data) {
      return null;
    }
    try {
      return JSON.parse(data);
    } catch (err) {
      log.error(`Failed to parse data, err: ${err}`);
      return null;
    }
  }

  async getServer() {
    const value = await rclient.getAsync(configServerKey);
    return value || "";
  }

  async getRegion() {
    return rclient.getAsync(configRegionKey);
  }

  async isAdminStatusOn() {
    const status = await rclient.getAsync(configAdminStatusKey);
    return status === '1';
  }

  async adminStatusOn() {
    return rclient.setAsync(configAdminStatusKey, '1');
  }

  async adminStatusOff() {
    return rclient.setAsync(configAdminStatusKey, '0');
  }

  async start() {
    const server = await this.getServer();
    if (!server) {
      throw new Error("socketio server not set");
    }

    await this._stop();

    await this.adminStatusOn();

    const gid = await et.getGID();
    const eid = await et.getEID();

    const region = await this.getRegion();
    const socketPath = region ? `/${region}/socket.io` : '/socket.io'
    this.socket = io(server, {
      path: socketPath,
      transports: ['websocket']
    });
    if (!this.socket) {
      throw new Error("failed to init socket io");
    }

    this.socket.on('connect', () => {
      log.forceInfo(`Socket IO connection to ${server}, ${region} is connected.`);
      this.socket.emit("box_registration", {
        gid: gid,
        eid: eid
      });
    });

    this.socket.on('disconnect', (reason) => {
      log.forceInfo(`Socket IO connection to ${server}, ${region} is disconnected. reason:`, reason);
    });

    const key = `send_to_box_${gid}`;
    this.socket.on(key, (message) => {
      if (message.gid === gid) {
        this.onMessage(gid, message).catch((err) => {
          log.error(`Failed to process message from group ${gid}`, err);
        });
      }
    })

    const liveKey = `realtime_send_to_box_${gid}`;
    this.socket.on(liveKey, (message) => {
      if (message.gid === gid) {
        switch (message.action) {
          case "keepalive":
            this.setRealtimeExpirationDate();
            break;
          case "close":
            this.resetRealtimeExpirationDate();
            break;
          default:
            this.setRealtimeExpirationDate();
            this.onRealTimeMessage(gid, message).catch((err) => {
              log.error(`Failed to process message from group ${gid}`, err);
            });
        }
      }
    })
  }

  async _stop() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }

  async stop() {
    await this.adminStatusOff();
    return this._stop();
  }

  async reset() {
    await rclient.delAsync(configServerKey);
    await rclient.delAsync(configRegionKey);
    await rclient.delAsync(configBizModeKey);
    await rclient.delAsync(configAdminStatusKey);
    return this._stop();
  }

  isRealtimeValid() {
    return this.realtimeExpireDate && new Date() / 1000 < this.realtimeExpireDate;
  }

  setRealtimeExpirationDate() {
    this.realtimeExpireDate = Math.floor(new Date() / 1000) + 300; // extend for 5 mins
  }

  resetRealtimeExpirationDate() {
    this.realtimeExpireDate = 0;
  }

  async onRealTimeMessage(gid, message) {
    if (this.realtimeRunning) {
      return;
    }

    const controller = await cw.getNetBotController(gid);

    this.realtimeRunning = true;

    if (controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      decryptedMessage.obj.data.value.streaming = { id: decryptedMessage.message.obj.id };
      decryptedMessage.message.suppressLog = true; // reduce sse message

      while (this.isRealtimeValid()) {
        try {
          const response = await controller.msgHandlerAsync(gid, decryptedMessage, 'web');

          const input = Buffer.from(JSON.stringify(response), 'utf8');
          const output = await deflateAsync(input);

          const compressedResponse = JSON.stringify({
            compressed: 1,
            compressMode: 1,
            data: output.toString('base64')
          });

          const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

          try {
            if (this.socket) {
              this.socket.emit("realtime_send_from_box", {
                message: encryptedResponse,
                gid: gid
              });
            }
          } catch (err) {
            log.error('Socket IO connection error', err);
          }

          await delay(500); // self protection
        } catch (err) {
          log.error("Got error when handling request, err:", err);
          await delay(500); // self protection
          break;
        }
      }
      this.realtimeRunning = false;
    }
  }

  async onMessage(gid, message) {
    const controller = await cw.getNetBotController(gid);

    if (controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      const response = await controller.msgHandlerAsync(gid, decryptedMessage, 'web');

      const input = Buffer.from(JSON.stringify(response), 'utf8');
      const output = await deflateAsync(input);

      const compressedResponse = JSON.stringify({
        compressed: 1,
        compressMode: 1,
        data: output.toString('base64')
      });

      const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

      try {
        if (this.socket) {
          this.socket.emit("send_from_box", {
            message: encryptedResponse,
            gid: gid
          });
        }
      } catch (err) {
        log.error('Socket IO connection error', err);
      }
    }
  }
}

module.exports = GuardianSensor;
<commit_msg>cleanup legacy guardian settings<commit_after>/*    Copyright 2019-2021 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename)

const Sensor = require('./Sensor.js').Sensor

const Promise = require('bluebird')
const extensionManager = require('./ExtensionManager.js')

const configServerKey = "ext.guardian.socketio.server";
const configRegionKey = "ext.guardian.socketio.region";
const configBizModeKey = "ext.guardian.business";
const configAdminStatusKey = "ext.guardian.socketio.adminStatus";

const rclient = require('../util/redis_manager.js').getRedisClient();
const license = require('../util/license.js')
const delay = require('../util/util.js').delay;
const io = require('socket.io-client');

const EncipherTool = require('../net2/EncipherTool.js');
const et = new EncipherTool();

const CloudWrapper = require('../api/lib/CloudWrapper.js');
const cw = new CloudWrapper();
const receicveMessageAsync = Promise.promisify(cw.getCloud().receiveMessage).bind(cw.getCloud());
const encryptMessageAsync = Promise.promisify(cw.getCloud().encryptMessage).bind(cw.getCloud());

const zlib = require('zlib');
const deflateAsync = Promise.promisify(zlib.deflate);
const rp = require('request-promise');
class GuardianSensor extends Sensor {
  constructor() {
    super();
    this.realtimeExpireDate = 0;
    this.realtimeRunning = 0;
  }

  async apiRun() {
    extensionManager.onGet("guardianSocketioServer", (msg) => {
      return this.getServer();
    });

    extensionManager.onSet("guardianSocketioServer", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      return this.setServer(data.server, data.region);
    });

    extensionManager.onGet("guardian.business", async (msg) => {
      return this.getBusiness();
    });

    extensionManager.onSet("guardian.business", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      await rclient.setAsync(configBizModeKey, JSON.stringify(data));
    });

    extensionManager.onGet("guardianSocketioRegion", (msg) => {
      return this.getRegion();
    });

    extensionManager.onCmd("startGuardianSocketioServer", (msg, data) => {
      return this.start();
    });

    extensionManager.onCmd("stopGuardianSocketioServer", (msg, data) => {
      return this.stop();
    });

    extensionManager.onCmd("resetGuardian", (msg, data) => {
      return this.reset();
    });

    extensionManager.onCmd("setAndStartGuardianService", async (msg, data) => {
      if (await this.locked()) {
        throw new Error("Box had been locked");
      }
      const socketioServer = data.server;
      if (!socketioServer) {
        throw new Error("invalid guardian relay server");
      }
      const forceRestart = !this.socket || (await this.getRegion() != data.region) || (await this.getServer() != socketioServer)
      await this.setServer(socketioServer, data.region);

      forceRestart && await this.start();
    });

    const adminStatusOn = await this.isAdminStatusOn();
    if (adminStatusOn) {
      await this.start();
    }
    await this.handlLegacy();
  }
  async handlLegacy() {
    try {
      // box might be removed from msp but it was offline before
      // remove legacy settings to avoid the box been locked forever
      const region = await this.getRegion();
      const server = await this.getServer();
      const business = await this.getBusiness();
      const licenseJSON = license.getLicense();
      const licenseString = licenseJSON && licenseJSON.DATA && licenseJSON.DATA.UUID;
      if (business && business.jwtToken && server) {
        const uri = region ? `${server}/${region}/v1/binding/checkLicense/${licenseString}` : `${server}/v1/binding/checkLicense/${licenseString}`
        const options = {
          method: 'GET',
          family: 4,
          uri: uri,
          headers: {
            Authorization: `Bearer ${business.jwtToken}`,
            ContentType: 'application/json'
          },
          json: true
        }
        const result = await rp(options)
        log.info("jack test result", result)
        if (!result || result.id != business.id) {
          await this.reset();
        }
      }
    } catch (e) {
      log.warn("Check license from msp error", e)
    }
  }

  async setServer(server, region) {
    if (server) {
      if (region) {
        await rclient.setAsync(configRegionKey, region);
      } else {
        await rclient.delAsync(configRegionKey);
      }
      return rclient.setAsync(configServerKey, server);
    } else {
      throw new Error("invalid server");
    }
  }

  async locked() {
    const business = await this.getBusiness(); // if the box belong to MSP, deny from logging to other web container or my.firewalla.com
    if (business && business.type == 'msp') {
      return true;
    }
    return false;
  }

  async getBusiness() {
    const data = await rclient.getAsync(configBizModeKey);
    if (!data) {
      return null;
    }
    try {
      return JSON.parse(data);
    } catch (err) {
      log.error(`Failed to parse data, err: ${err}`);
      return null;
    }
  }

  async getServer() {
    const value = await rclient.getAsync(configServerKey);
    return value || "";
  }

  async getRegion() {
    return rclient.getAsync(configRegionKey);
  }

  async isAdminStatusOn() {
    const status = await rclient.getAsync(configAdminStatusKey);
    return status === '1';
  }

  async adminStatusOn() {
    return rclient.setAsync(configAdminStatusKey, '1');
  }

  async adminStatusOff() {
    return rclient.setAsync(configAdminStatusKey, '0');
  }

  async start() {
    const server = await this.getServer();
    if (!server) {
      throw new Error("socketio server not set");
    }

    await this._stop();

    await this.adminStatusOn();

    const gid = await et.getGID();
    const eid = await et.getEID();

    const region = await this.getRegion();
    const socketPath = region ? `/${region}/socket.io` : '/socket.io'
    this.socket = io(server, {
      path: socketPath,
      transports: ['websocket']
    });
    if (!this.socket) {
      throw new Error("failed to init socket io");
    }

    this.socket.on('connect', () => {
      log.forceInfo(`Socket IO connection to ${server}, ${region} is connected.`);
      this.socket.emit("box_registration", {
        gid: gid,
        eid: eid
      });
    });

    this.socket.on('disconnect', (reason) => {
      log.forceInfo(`Socket IO connection to ${server}, ${region} is disconnected. reason:`, reason);
    });

    const key = `send_to_box_${gid}`;
    this.socket.on(key, (message) => {
      if (message.gid === gid) {
        this.onMessage(gid, message).catch((err) => {
          log.error(`Failed to process message from group ${gid}`, err);
        });
      }
    })

    const liveKey = `realtime_send_to_box_${gid}`;
    this.socket.on(liveKey, (message) => {
      if (message.gid === gid) {
        switch (message.action) {
          case "keepalive":
            this.setRealtimeExpirationDate();
            break;
          case "close":
            this.resetRealtimeExpirationDate();
            break;
          default:
            this.setRealtimeExpirationDate();
            this.onRealTimeMessage(gid, message).catch((err) => {
              log.error(`Failed to process message from group ${gid}`, err);
            });
        }
      }
    })
  }

  async _stop() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }

  async stop() {
    await this.adminStatusOff();
    return this._stop();
  }

  async reset() {
    log.info("Reset guardian settings");
    await rclient.delAsync(configServerKey);
    await rclient.delAsync(configRegionKey);
    await rclient.delAsync(configBizModeKey);
    await rclient.delAsync(configAdminStatusKey);
    return this._stop();
  }

  isRealtimeValid() {
    return this.realtimeExpireDate && new Date() / 1000 < this.realtimeExpireDate;
  }

  setRealtimeExpirationDate() {
    this.realtimeExpireDate = Math.floor(new Date() / 1000) + 300; // extend for 5 mins
  }

  resetRealtimeExpirationDate() {
    this.realtimeExpireDate = 0;
  }

  async onRealTimeMessage(gid, message) {
    if (this.realtimeRunning) {
      return;
    }

    const controller = await cw.getNetBotController(gid);

    this.realtimeRunning = true;

    if (controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      decryptedMessage.obj.data.value.streaming = { id: decryptedMessage.message.obj.id };
      decryptedMessage.message.suppressLog = true; // reduce sse message

      while (this.isRealtimeValid()) {
        try {
          const response = await controller.msgHandlerAsync(gid, decryptedMessage, 'web');

          const input = Buffer.from(JSON.stringify(response), 'utf8');
          const output = await deflateAsync(input);

          const compressedResponse = JSON.stringify({
            compressed: 1,
            compressMode: 1,
            data: output.toString('base64')
          });

          const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

          try {
            if (this.socket) {
              this.socket.emit("realtime_send_from_box", {
                message: encryptedResponse,
                gid: gid
              });
            }
          } catch (err) {
            log.error('Socket IO connection error', err);
          }

          await delay(500); // self protection
        } catch (err) {
          log.error("Got error when handling request, err:", err);
          await delay(500); // self protection
          break;
        }
      }
      this.realtimeRunning = false;
    }
  }

  async onMessage(gid, message) {
    const controller = await cw.getNetBotController(gid);

    if (controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      const response = await controller.msgHandlerAsync(gid, decryptedMessage, 'web');

      const input = Buffer.from(JSON.stringify(response), 'utf8');
      const output = await deflateAsync(input);

      const compressedResponse = JSON.stringify({
        compressed: 1,
        compressMode: 1,
        data: output.toString('base64')
      });

      const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

      try {
        if (this.socket) {
          this.socket.emit("send_from_box", {
            message: encryptedResponse,
            gid: gid
          });
        }
      } catch (err) {
        log.error('Socket IO connection error', err);
      }
    }
  }
}

module.exports = GuardianSensor;
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename)

const Sensor = require('./Sensor.js').Sensor

const sem = require('../sensor/SensorEventManager.js').getInstance()

const Promise = require('bluebird')
const extensionManager = require('./ExtensionManager.js')

const fc = require('../net2/config.js')

const configServerKey = "ext.guardian.socketio.server";
const configAdminStatusKey = "ext.guardian.socketio.adminStatus";

const rclient = require('../util/redis_manager.js').getRedisClient();

const io = require('socket.io-client');

const EncipherTool = require('../net2/EncipherTool.js');
const et = new EncipherTool();

const CloudWrapper = require('../api/lib/CloudWrapper.js');
const cw = new CloudWrapper();
const receicveMessageAsync = Promise.promisify(cw.getCloud().receiveMessage).bind(cw.getCloud());
const encryptMessageAsync = Promise.promisify(cw.getCloud().encryptMessage).bind(cw.getCloud());

const zlib = require('zlib');
const deflateAsync = Promise.promisify(zlib.deflate);

class GuardianSensor extends Sensor {
  constructor() {
    super();
  }

  async apiRun() {
    extensionManager.onGet("guardianSocketioServer", (msg) => {
      return this.getServer();
    })

    extensionManager.onSet("guardianSocketioServer", (msg, data) => {
      return this.setServer(data.server);
    });

    extensionManager.onCmd("startGuardianSocketioServer", (msg, data) => {
      return this.start();
    });

    extensionManager.onCmd("stopGuardianSocketioServer", (msg, data) => {
      return this.stop();
    });

    extensionManager.onCmd("setAndStartGuardianService", async (msg, data) => {
      const socketioServer = data.server;
      if(!socketioServer) {
        throw new Error("invalid guardian relay server");
      }

      await guardianSensor.setServer(socketioServer);

      await guardianSensor.start();
    });

    const adminStatusOn = await this.isAdminStatusOn();
    if(adminStatusOn) {
      await this.start();
    }
  }

  async setServer(server) {
    if(server) {
      return rclient.setAsync(configServerKey, server);
    } else {
      throw new Error("invalid server");
    }
  }

  async getServer() {
    const value = await rclient.getAsync(configServerKey);
    return value || "";
  }

  async isAdminStatusOn() {
    const status = await rclient.getAsync(configAdminStatusKey);
    return status === '1';
  }

  async adminStatusOn() {
    return rclient.setAsync(configAdminStatusKey, '1');
  }

  async adminStatusOff() {
    return rclient.setAsync(configAdminStatusKey, '0');
  }

  async start() {
    const server = await this.getServer();
    if(!server) {
      throw new Error("socketio server not set");
    }

    await this._stop();

    await this.adminStatusOn();

    this.socket = io.connect(server);
    if(!this.socket) {
      throw new Error("failed to init socket io");
    }

    const gid = await et.getGID();
    const key = `send_to_box_${gid}`;
    this.socket.on(key, (message) => {
      if(message.gid === gid) {
        this.onMessage(gid, message);
      }
    })
  }

  async _stop() {
    this.socket.disconnect();
    this.socket = null;
  }

  async stop() {
    await this.adminStatusOff();
    return this._stop();
  }

  async onMessage(gid, message) {
    const controller = await cw.getNetBotController(gid);

    if(controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      const response = await controller.msgHandlerAsync(gid, decryptedMessage);

      const input = new Buffer(JSON.stringify(response), 'utf8');
      const output = await deflateAsync(input);

      const compressedResponse = JSON.stringify({
        compressed: 1,
        compressMode: 1,
        data: output.toString('base64')
      });

      const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

      this.socket.emit("send_from_box", {
        message: encryptedResponse,
        gid: gid
      });
    }
  }
}

module.exports = GuardianSensor;
<commit_msg>bug fix<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename)

const Sensor = require('./Sensor.js').Sensor

const sem = require('../sensor/SensorEventManager.js').getInstance()

const Promise = require('bluebird')
const extensionManager = require('./ExtensionManager.js')

const fc = require('../net2/config.js')

const configServerKey = "ext.guardian.socketio.server";
const configAdminStatusKey = "ext.guardian.socketio.adminStatus";

const rclient = require('../util/redis_manager.js').getRedisClient();

const io = require('socket.io-client');

const EncipherTool = require('../net2/EncipherTool.js');
const et = new EncipherTool();

const CloudWrapper = require('../api/lib/CloudWrapper.js');
const cw = new CloudWrapper();
const receicveMessageAsync = Promise.promisify(cw.getCloud().receiveMessage).bind(cw.getCloud());
const encryptMessageAsync = Promise.promisify(cw.getCloud().encryptMessage).bind(cw.getCloud());

const zlib = require('zlib');
const deflateAsync = Promise.promisify(zlib.deflate);

class GuardianSensor extends Sensor {
  constructor() {
    super();
  }

  async apiRun() {
    extensionManager.onGet("guardianSocketioServer", (msg) => {
      return this.getServer();
    })

    extensionManager.onSet("guardianSocketioServer", (msg, data) => {
      return this.setServer(data.server);
    });

    extensionManager.onCmd("startGuardianSocketioServer", (msg, data) => {
      return this.start();
    });

    extensionManager.onCmd("stopGuardianSocketioServer", (msg, data) => {
      return this.stop();
    });

    extensionManager.onCmd("setAndStartGuardianService", async (msg, data) => {
      const socketioServer = data.server;
      if(!socketioServer) {
        throw new Error("invalid guardian relay server");
      }

      await this.setServer(socketioServer);

      await this.start();
    });

    const adminStatusOn = await this.isAdminStatusOn();
    if(adminStatusOn) {
      await this.start();
    }
  }

  async setServer(server) {
    if(server) {
      return rclient.setAsync(configServerKey, server);
    } else {
      throw new Error("invalid server");
    }
  }

  async getServer() {
    const value = await rclient.getAsync(configServerKey);
    return value || "";
  }

  async isAdminStatusOn() {
    const status = await rclient.getAsync(configAdminStatusKey);
    return status === '1';
  }

  async adminStatusOn() {
    return rclient.setAsync(configAdminStatusKey, '1');
  }

  async adminStatusOff() {
    return rclient.setAsync(configAdminStatusKey, '0');
  }

  async start() {
    const server = await this.getServer();
    if(!server) {
      throw new Error("socketio server not set");
    }

    await this._stop();

    await this.adminStatusOn();

    this.socket = io.connect(server);
    if(!this.socket) {
      throw new Error("failed to init socket io");
    }

    const gid = await et.getGID();
    const key = `send_to_box_${gid}`;
    this.socket.on(key, (message) => {
      if(message.gid === gid) {
        this.onMessage(gid, message);
      }
    })
  }

  async _stop() {
    if(this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }

  async stop() {
    await this.adminStatusOff();
    return this._stop();
  }

  async onMessage(gid, message) {
    const controller = await cw.getNetBotController(gid);

    if(controller && this.socket) {
      const encryptedMessage = message.message;
      const decryptedMessage = await receicveMessageAsync(gid, encryptedMessage);
      decryptedMessage.mtype = decryptedMessage.message.mtype;
      const response = await controller.msgHandlerAsync(gid, decryptedMessage);

      const input = new Buffer(JSON.stringify(response), 'utf8');
      const output = await deflateAsync(input);

      const compressedResponse = JSON.stringify({
        compressed: 1,
        compressMode: 1,
        data: output.toString('base64')
      });

      const encryptedResponse = await encryptMessageAsync(gid, compressedResponse);

      this.socket.emit("send_from_box", {
        message: encryptedResponse,
        gid: gid
      });
    }
  }
}

module.exports = GuardianSensor;
<|endoftext|>


<commit_before>
import request from 'request';
import Webcom from 'webcom';
import uuid from 'uuid';
import minimist from 'minimist';
import Debug from 'console-debug';
import range from 'lodash/utility/range';

const args = minimist(process.argv.slice(2), {boolean: ['log']});
const host = args.server || 'https://webcom.orange.com';
const proxy = args.proxy;
const ns = uuid.v1();
const simplelogin = {};

if(!args.log) {
	// Override console warn to avoid log pollution by Webcom
	const debugConsole = new Debug({
		uncaughtExceptionCatch: false,      // Do we want to catch uncaughtExceptions?
		consoleFilter: ['LOG', 'WARN'],     // Filter these console output types
		colors: true                        // do we want pretty pony colors in our console output?
	});
	console.warn = debugConsole.warn.bind(debugConsole);
}

const admin_jwt = process.env.WEBCOM_JWT;

const createNamespace = (token, namespace = ns) => new Promise((resolve, reject) => {
	request.post({
		url: `${host}/admin/base/${namespace}`,
		formData: {token},
		proxy
	}, err => err ? reject(err) : resolve());
});

const deleteNamespace = (token, namespace = ns) => new Promise((resolve, reject) => {
	request.post({
		url: `${host}/admin/base/${namespace}`,
		formData: {
			_method: 'DELETE',
			namespace,
			token
		},
		proxy
	}, err => err ? reject(err) : resolve());
});

const adminToken = (token, namespace = ns) => new Promise((resolve, reject) => {
	request({
		url: `${host}/admin/base/${namespace}/token?token=${token}`,
		proxy
	}, (err, header, body) => err ? reject(err) : resolve(JSON.parse(body).authToken));
});

const putRules = (token, rulesToTest, namespace = ns) => new Promise((resolve, reject) => {
	request({
		url: `${host}/base/${namespace}/.settings/rules.json?auth=${token}`,
		method: 'PUT',
		json: rulesToTest,
		proxy
	}, (err, header, body) => {
		const parseError = err || (body && body.error ? new Error(body.error) : false)
		parseError ? reject(parseError) : resolve();
	});
});

const createUser = (namespace, email) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).createUser(email, 'password', (err, authData) => {
		if(err) {
			reject(err);
			return console.error(err);
		}
		simplelogin[authData.id] = {
			token: authData.token,
			uid: authData.uid,
			id: authData.id,
			email
		};
		resolve(authData);
	});
});

const deleteUser = (namespace, email) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).removeUser(email, 'password', (err) => {
		if (err) {
			reject(err);
			return console.error(err);
		}
		resolve();
	});
});

const auth = (token, namespace = ns) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).auth(token, (err, authData) => {
		if (err) {
			reject(err);
		} else {
			resolve(authData && authData.auth ? authData.auth : authData);
		}
	}, reject);
});

const update = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).update(data, (err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const push = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).push(data, (err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const set = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).set(data, (err) => {

		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const remove = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).remove((err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const read = (path) => new Promise(
	(resolve, reject) => {
		(new Webcom(`${host}/base/${ns}/${path}`, proxy)).once('value', resolve, reject);
	}
);

const logout = (path = '') => new Promise(
	(resolve, reject) => {
		(new Webcom(`${host}/base/${ns}/${path}`, proxy)).logout(err => err ? reject(err) : resolve());
	}
);

export const initNamespace = (rules, nbUser = 5) => done => {
	console.info(`\n  \u2622 ${ns}\n`);
	auth(admin_jwt, 'accounts')
		.then(() => {
			console.warn('auth success');
			return createNamespace(admin_jwt, ns)
		})
		.then(() => {
			console.warn(`namespace ${ns} created`);
			return adminToken(admin_jwt, ns)
		})
		.then((token) => {
			console.warn(`admin token for namespace ${ns} ok`);
			return putRules(token, rules);
		})
		.then(() => {
			console.warn(`rules set for namespace ${ns}`);
			const users = range(1, nbUser + 1).map(id => createUser(ns, `user${id}@reach.io`));
			return Promise.all(users);
		})
		.then(() => {
			console.warn(`users created for namespace ${ns}`);
			done()
		})
		.catch(done);
};

export const destroyNamespace = () => done => {
	const users = Object.keys(simplelogin).map(id => deleteUser(ns, simplelogin[id].email));
	Promise.all(users)
		.then(() => deleteNamespace(admin_jwt, ns))
		.then(() => done())
		.catch(done);
};

export const resetNamespace = (data) => done => {
	adminToken(admin_jwt, ns)
		.then((token) => auth(token, ns))
		.then(() => set('', data))
		.then(() => done())
		.catch(done);
};

export const plugin = (chai, utils) => {
	// Add Properties
	[
		['can', 'positivity', true],
		['cannot', 'positivity', false],
		['read', 'operation'],
		['remove', 'operation']
	].forEach(args => {
		chai.Assertion.addProperty(
			args[0],
			function(){
				utils.flag(this, args[1], args.length == 3 ? args[2] : args[0]);
			}
		);
	}, this);

	// Add chainable methods
	[
		'write',
		'push',
		'update',
		'set'
	].forEach(method => {
		chai.Assertion.addChainableMethod(
			method,
			function(d) {
				utils.flag(this, 'operation', method);
				utils.flag(this, 'operationData', d);
			},
			function() {
				utils.flag(this, 'operation', method);
				utils.flag(this, 'operationData', null);
			}
		);
	}, this);

	// Add final method
	chai.Assertion.addMethod('path', function(path, done) {
		const operationType = utils.flag(this, 'operation');
		const positivity = utils.flag(this, 'positivity');
		const newData = utils.flag(this, 'operationData');
		const user = this._obj === false ? 'Unauthenticated user' : this._obj.email;

		let method;
		switch (operationType) {
			case 'write':
				method = newData && newData instanceof Object ? update : set;
				break;
			case 'set':
				method = set;
				break;
			case 'update':
				method = update;
				break;
			case 'push':
				method = push;
				break;
			case 'remove':
				method = remove;
				break;
			case 'read':
			default:
				method = read;
				break;
		}

		(this._obj ? auth(this._obj.token) : logout(path)).then(() => {
			const errorMsg = newData ? `${operationType} '${newData instanceof Object ? JSON.stringify(newData) : newData}' to ${path}` : `${operationType} ${path}`
			if (positivity) {
				method(path, newData).then(() => done(), (err) => {
					if (err && /^PERMISSION_DENIED$/i.test(err.code)) {
						done(new Error(`${user} cannot ${errorMsg} but should be able to`));
					} else {
						done(err);
					}
				});
			} else {
				method(path, newData).then(
					() => {
						done(new Error(`${user} can ${errorMsg} but should not be able to`));
					},
					(err) => {
						if (err && /^PERMISSION_DENIED$/i.test(err.code)) {
							done();
						} else {
							done(err);
						}
					}
				);
			}
		});
	});
};

export const users = {
	unauthenticated: false,
	password: (id = 5) => simplelogin[id]
};
<commit_msg>chore: improve test error logs<commit_after>
import request from 'request';
import Webcom from 'webcom';
import uuid from 'uuid';
import minimist from 'minimist';
import Debug from 'console-debug';
import range from 'lodash/utility/range';

const args = minimist(process.argv.slice(2), {boolean: ['log']});
const host = args.server || 'https://webcom.orange.com';
const proxy = args.proxy;
const ns = uuid.v1();
const simplelogin = {};

if(!args.log) {
	// Override console warn to avoid log pollution by Webcom
	const debugConsole = new Debug({
		uncaughtExceptionCatch: false,      // Do we want to catch uncaughtExceptions?
		consoleFilter: ['LOG', 'WARN'],     // Filter these console output types
		colors: true                        // do we want pretty pony colors in our console output?
	});
	console.warn = debugConsole.warn.bind(debugConsole);
}

const admin_jwt = process.env.WEBCOM_JWT;

const createNamespace = (token, namespace = ns) => new Promise((resolve, reject) => {
	request.post({
		url: `${host}/admin/base/${namespace}`,
		formData: {token},
		proxy
	}, err => err ? reject(err) : resolve());
});

const deleteNamespace = (token, namespace = ns) => new Promise((resolve, reject) => {
	request.post({
		url: `${host}/admin/base/${namespace}`,
		formData: {
			_method: 'DELETE',
			namespace,
			token
		},
		proxy
	}, err => err ? reject(err) : resolve());
});

const adminToken = (token, namespace = ns) => new Promise((resolve, reject) => {
	request({
		url: `${host}/admin/base/${namespace}/token?token=${token}`,
		proxy
	}, (err, header, body) => err ? reject(err) : resolve(JSON.parse(body).authToken));
});

const putRules = (token, rulesToTest, namespace = ns) => new Promise((resolve, reject) => {
	request({
		url: `${host}/base/${namespace}/.settings/rules.json?auth=${token}`,
		method: 'PUT',
		json: rulesToTest,
		proxy
	}, (err, header, body) => {
		const parseError = err || (body && body.error ? new Error(body.error) : false)
		parseError ? reject(parseError) : resolve();
	});
});

const createUser = (namespace, email) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).createUser(email, 'password', (err, authData) => {
		if(err) {
			reject(err);
			return console.error(err);
		}
		simplelogin[authData.id] = {
			token: authData.token,
			uid: authData.uid,
			id: authData.id,
			email
		};
		resolve(authData);
	});
});

const deleteUser = (namespace, email) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).removeUser(email, 'password', (err) => {
		if (err) {
			reject(err);
			return console.error(err);
		}
		resolve();
	});
});

const auth = (token, namespace = ns) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${namespace}`, proxy)).auth(token, (err, authData) => {
		if (err) {
			reject(err);
		} else {
			resolve(authData && authData.auth ? authData.auth : authData);
		}
	}, reject);
});

const update = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).update(data, (err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const push = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).push(data, (err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const set = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).set(data, (err) => {

		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const remove = (path, data) => new Promise((resolve, reject) => {
	(new Webcom(`${host}/base/${ns}/${path}`, proxy)).remove((err) => {
		if (err) {
			reject(err);
		} else {
			resolve();
		}
	});
});

const read = (path) => new Promise(
	(resolve, reject) => {
		(new Webcom(`${host}/base/${ns}/${path}`, proxy)).once('value', resolve, reject);
	}
);

const logout = (path = '') => new Promise(
	(resolve, reject) => {
		(new Webcom(`${host}/base/${ns}/${path}`, proxy)).logout(err => err ? reject(err) : resolve());
	}
);

export const initNamespace = (rules, nbUser = 5) => done => {
	console.info(`\n  \u2622 ${ns}\n`);
	auth(admin_jwt, 'accounts')
		.then(() => {
			console.warn('auth success');
			return createNamespace(admin_jwt, ns)
		})
		.then(() => {
			console.warn(`namespace ${ns} created`);
			return adminToken(admin_jwt, ns)
		})
		.then((token) => {
			console.warn(`admin token for namespace ${ns} ok`);
			return putRules(token, rules);
		})
		.then(() => {
			console.warn(`rules set for namespace ${ns}`);
			const users = range(1, nbUser + 1).map(id => createUser(ns, `user${id}@reach.io`));
			return Promise.all(users);
		})
		.then(() => {
			console.warn(`users created for namespace ${ns}`);
			done()
		})
		.catch(done);
};

export const destroyNamespace = () => done => {
	const users = Object.keys(simplelogin).map(id => deleteUser(ns, simplelogin[id].email));
	Promise.all(users)
		.then(() => deleteNamespace(admin_jwt, ns))
		.then(() => done())
		.catch(done);
};

export const resetNamespace = (data) => done => {
	adminToken(admin_jwt, ns)
		.then((token) => auth(token, ns))
		.then(() => set('', data))
		.then(() => done())
		.catch(done);
};

export const plugin = (chai, utils) => {
	// Add Properties
	[
		['can', 'positivity', true],
		['cannot', 'positivity', false],
		['read', 'operation'],
		['remove', 'operation']
	].forEach(args => {
		chai.Assertion.addProperty(
			args[0],
			function(){
				utils.flag(this, args[1], args.length == 3 ? args[2] : args[0]);
			}
		);
	}, this);

	// Add chainable methods
	[
		'write',
		'push',
		'update',
		'set'
	].forEach(method => {
		chai.Assertion.addChainableMethod(
			method,
			function(d) {
				utils.flag(this, 'operation', method);
				utils.flag(this, 'operationData', d);
			},
			function() {
				utils.flag(this, 'operation', method);
				utils.flag(this, 'operationData', null);
			}
		);
	}, this);

	// Add final method
	chai.Assertion.addMethod('path', function(path, done) {
		const operationType = utils.flag(this, 'operation');
		const positivity = utils.flag(this, 'positivity');
		const newData = utils.flag(this, 'operationData');
		const user = this._obj === false ? 'Unauthenticated user' : `${this._obj.email} (${this._obj.uid})`;

		let method;
		switch (operationType) {
			case 'write':
				method = newData && newData instanceof Object ? update : set;
				break;
			case 'set':
				method = set;
				break;
			case 'update':
				method = update;
				break;
			case 'push':
				method = push;
				break;
			case 'remove':
				method = remove;
				break;
			case 'read':
			default:
				method = read;
				break;
		}

		(this._obj ? auth(this._obj.token) : logout(path)).then(() => {
			const errorMsg = newData ? `${operationType} '${newData instanceof Object ? JSON.stringify(newData) : newData}' to ${path}` : `${operationType} ${path}`
			if (positivity) {
				method(path, newData).then(() => done(), (err) => {
					if (err && /^PERMISSION_DENIED$/i.test(err.code)) {
						done(new Error(`${user} cannot ${errorMsg} but should be able to`));
					} else {
						done(err);
					}
				});
			} else {
				method(path, newData).then(
					() => {
						done(new Error(`${user} can ${errorMsg} but should not be able to`));
					},
					(err) => {
						if (err && /^PERMISSION_DENIED$/i.test(err.code)) {
							done();
						} else {
							done(err);
						}
					}
				);
			}
		});
	});
};

export const users = {
	unauthenticated: false,
	password: (id = 5) => simplelogin[id]
};
<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import RegisterUser from './reducer_registerUser'
import Login from './reducer_login'
import SearchedBooks from './reducer_search'

const rootReducer = combineReducers({
  userRegister: RegisterUser,
  userLogin: Login,
  searchedBooks: SearchedBooks
});

export default rootReducer;<commit_msg>added wishlistaction to state in combine reducers<commit_after>import { combineReducers } from 'redux';
import RegisterUser from './reducer_registerUser'
import Login from './reducer_login'
import SearchedBooks from './reducer_search'
import Wishlist from './reducer_wishlist'

const rootReducer = combineReducers({
  userRegister: RegisterUser,
  userLogin: Login,
  searchedBooks: SearchedBooks,
  wishlistAction: Wishlist
});

export default rootReducer;<|endoftext|>


<commit_before>import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'

const todoApp = combineReducers({
  todos,
  visibilityFilter
})

export default todoApp<commit_msg>Delete index.js<commit_after><|endoftext|>


<commit_before>import {
  CHANGE_EVENT_RESPONSE_PENDING,
  COMPLETE_POST_PENDING,
  APPEND_COMMENT,
  CREATE_COMMENT,
  CREATE_POST,
  FETCH_POST,
  FETCH_POSTS,
  FETCH_PERSON,
  FIND_OR_CREATE_THREAD,
  FOLLOW_POST_PENDING,
  PIN_POST_PENDING,
  REMOVE_COMMENT,
  REMOVE_POST,
  UPDATE_POST,
  UPDATE_POST_READ_TIME,
  VOTE_ON_POST_PENDING
} from '../actions'
import { compact, omit, find, some, without, map, uniq } from 'lodash'
import { get, isNull, omitBy } from 'lodash/fp'
import { addOrRemovePersonId, mergeList } from './util'

const normalize = (post) => omitBy(isNull, {
  ...post,
  children: post.children ? map(post.children, c => c.id) : null, // FIXME should be child_ids
  numComments: post.num_comments || post.numComments,
  num_comments: null,
  comments: null,
  communities: null,
  people: null
})

const normalizeUpdate = (post, params, payload) => {
  const normalized = {
    ...omit(post, 'linkPreview'),
    ...omit(params, 'imageUrl', 'imageRemoved', 'docs', 'removedDocs')
  }
  if (some(payload.children)) {
    normalized.children = map(payload.children, c => c.id)
  }
  return normalized
}

const listWithChildren = (post, payload) => {
  if (payload.children) return [post].concat(payload.children.map(normalize))
  return [post]
}

const changeEventResponse = (post, response, user) => {
  if (!user) return

  var meInResponders = find(post.responders, {id: user.id})
  var responders = without(post.responders, meInResponders)

  if (!meInResponders || meInResponders.response !== response) {
    responders.push({id: user.id, name: user.name, avatar_url: user.avatar_url, response: response})
  }
  return {...post, ...{ responders }}
}

const updatePostProps = (state, postId, props) => {
  return {
    ...state,
    [postId]: {...state[postId], ...props}
  }
}

export default function (state = {}, action) {
  const { error, type, payload, meta } = action
  if (error) return state

  let { id, personId, postId } = meta || {}
  let post = state[id]

  switch (type) {
    case FETCH_POSTS:
      return mergeList(state, payload.posts.map(normalize), 'id')
    case CREATE_POST:
    case FETCH_POST:
    case FIND_OR_CREATE_THREAD:
      return mergeList(state, listWithChildren(normalize(payload), payload), 'id')
    case UPDATE_POST:
      post = normalizeUpdate(post, meta.params, payload)
      return mergeList(state, listWithChildren(post, payload), 'id', {drop: ['linkPreview']})
    case CHANGE_EVENT_RESPONSE_PENDING:
      var { response, user } = meta
      return {...state, [id]: changeEventResponse(post, response, user)}
    case VOTE_ON_POST_PENDING:
      return addOrRemovePersonId(state, id, personId, 'voter_ids')
    case REMOVE_POST:
      return {...state, [id]: null}
    case FOLLOW_POST_PENDING:
      return addOrRemovePersonId(state, id, personId, 'follower_ids')
    case APPEND_COMMENT:
      return updatePostProps(state, id, {
        numComments: (post.numComments || 0) + 1,
        updated_at: new Date()
      })
    case CREATE_COMMENT:
      return updatePostProps(state, id, {
        follower_ids: uniq((post.follower_ids || []).concat(payload.user_id)),
        numComments: (post.numComments || 0) + 1,
        updated_at: new Date()
      })
    case REMOVE_COMMENT:
      return updatePostProps(state, postId, {
        numComments: state[meta.postId].numComments - 1
      })
    case FETCH_PERSON:
      const newPosts = compact([payload.recent_request, payload.recent_offer])
      return mergeList(state, newPosts.map(normalize), 'id')
    case PIN_POST_PENDING:
      post = state[id]
      const membership = get(['memberships', meta.slug], post)
      return {
        ...state,
        [id]: {
          ...post,
          memberships: {
            ...post.memberships,
            [meta.slug]: {...membership, pinned: !get('pinned', membership)}
          }
        }
      }
    case COMPLETE_POST_PENDING:
      return updatePostProps(state, id, {
        fulfilled_at: post.fulfilled_at ? null : new Date()
      })
    case UPDATE_POST_READ_TIME:
      return updatePostProps(state, id, {last_read_at: new Date()})
  }
  return state
}
<commit_msg>update last_read_at when making a new message<commit_after>import {
  CHANGE_EVENT_RESPONSE_PENDING,
  COMPLETE_POST_PENDING,
  APPEND_COMMENT,
  CREATE_COMMENT,
  CREATE_POST,
  FETCH_POST,
  FETCH_POSTS,
  FETCH_PERSON,
  FIND_OR_CREATE_THREAD,
  FOLLOW_POST_PENDING,
  PIN_POST_PENDING,
  REMOVE_COMMENT,
  REMOVE_POST,
  UPDATE_POST,
  UPDATE_POST_READ_TIME,
  VOTE_ON_POST_PENDING
} from '../actions'
import { compact, omit, find, some, without, map, uniq } from 'lodash'
import { get, isNull, omitBy } from 'lodash/fp'
import { addOrRemovePersonId, mergeList } from './util'

const normalize = (post) => omitBy(isNull, {
  ...post,
  children: post.children ? map(post.children, c => c.id) : null, // FIXME should be child_ids
  numComments: post.num_comments || post.numComments,
  num_comments: null,
  comments: null,
  communities: null,
  people: null
})

const normalizeUpdate = (post, params, payload) => {
  const normalized = {
    ...omit(post, 'linkPreview'),
    ...omit(params, 'imageUrl', 'imageRemoved', 'docs', 'removedDocs')
  }
  if (some(payload.children)) {
    normalized.children = map(payload.children, c => c.id)
  }
  return normalized
}

const listWithChildren = (post, payload) => {
  if (payload.children) return [post].concat(payload.children.map(normalize))
  return [post]
}

const changeEventResponse = (post, response, user) => {
  if (!user) return

  var meInResponders = find(post.responders, {id: user.id})
  var responders = without(post.responders, meInResponders)

  if (!meInResponders || meInResponders.response !== response) {
    responders.push({id: user.id, name: user.name, avatar_url: user.avatar_url, response: response})
  }
  return {...post, ...{ responders }}
}

const updatePostProps = (state, postId, props) => {
  return {
    ...state,
    [postId]: {...state[postId], ...props}
  }
}

export default function (state = {}, action) {
  const { error, type, payload, meta } = action
  if (error) return state

  let { id, personId, postId } = meta || {}
  let post = state[id]

  switch (type) {
    case FETCH_POSTS:
      return mergeList(state, payload.posts.map(normalize), 'id')
    case CREATE_POST:
    case FETCH_POST:
    case FIND_OR_CREATE_THREAD:
      return mergeList(state, listWithChildren(normalize(payload), payload), 'id')
    case UPDATE_POST:
      post = normalizeUpdate(post, meta.params, payload)
      return mergeList(state, listWithChildren(post, payload), 'id', {drop: ['linkPreview']})
    case CHANGE_EVENT_RESPONSE_PENDING:
      var { response, user } = meta
      return {...state, [id]: changeEventResponse(post, response, user)}
    case VOTE_ON_POST_PENDING:
      return addOrRemovePersonId(state, id, personId, 'voter_ids')
    case REMOVE_POST:
      return {...state, [id]: null}
    case FOLLOW_POST_PENDING:
      return addOrRemovePersonId(state, id, personId, 'follower_ids')
    case APPEND_COMMENT:
      return updatePostProps(state, id, {
        numComments: (post.numComments || 0) + 1,
        updated_at: new Date()
      })
    case CREATE_COMMENT:
      return updatePostProps(state, id, {
        follower_ids: uniq((post.follower_ids || []).concat(payload.user_id)),
        numComments: (post.numComments || 0) + 1,
        updated_at: new Date(),
        last_read_at: new Date()
      })
    case REMOVE_COMMENT:
      return updatePostProps(state, postId, {
        numComments: state[meta.postId].numComments - 1
      })
    case FETCH_PERSON:
      const newPosts = compact([payload.recent_request, payload.recent_offer])
      return mergeList(state, newPosts.map(normalize), 'id')
    case PIN_POST_PENDING:
      post = state[id]
      const membership = get(['memberships', meta.slug], post)
      return {
        ...state,
        [id]: {
          ...post,
          memberships: {
            ...post.memberships,
            [meta.slug]: {...membership, pinned: !get('pinned', membership)}
          }
        }
      }
    case COMPLETE_POST_PENDING:
      return updatePostProps(state, id, {
        fulfilled_at: post.fulfilled_at ? null : new Date()
      })
    case UPDATE_POST_READ_TIME:
      return updatePostProps(state, id, {last_read_at: new Date()})
  }
  return state
}
<|endoftext|>


<commit_before>import './babel-maybefill';

import express from 'express';
import {fetchAllRefsWithInfo} from './github-api';
import _ from 'lodash';

const d = require('debug')('serf:ref-server');
const app = express();

const yargs = require('yargs')
  .usage('serf-server owner/repo owner2/repo owner/repo3...')
  .help('h')
  .alias('p', 'port')
  .describe('p', 'The port to start the server on')
  .alias('h', 'help')
  .epilog(`
Some useful environment variables:

SERF_PORT - the port to serve on if not specified via -p, defaults to 3000.
GITHUB_TOKEN - the GitHub API token to use. Must be provided.`);

const argv = yargs.argv;

function main() {
  const validNwos = argv._;
  if (validNwos.length < 1) {
    console.error("ERROR: Supply a list of valid repositories in owner/repo format (i.e. 'rails/rails')\n");
    yargs.showHelp();
    process.exit(-1);
  }

  app.get('/info/:owner/:name', async (req, res) => {
    try {
      if (!req.params.owner || !req.params.name) {
        throw new Error("no");
      }

      let needle = `${req.params.owner}/${req.params.name}`;
      if (!_.find(validNwos, (x) => x === needle)) {
        throw new Error("no");
      }

      res.json(await fetchAllRefsWithInfo(needle));
    } catch (e) {
      d(e.message);
      d(e.stack);
      res.status(500).json({error: e.message});
    }
  });

  let port = argv.port || 3000;
  console.log(`Listening on port ${port}`);
  app.listen(port);
}

main();
<commit_msg>Twerk some yargs stuff<commit_after>import './babel-maybefill';

import express from 'express';
import {fetchAllRefsWithInfo} from './github-api';
import _ from 'lodash';

const d = require('debug')('serf:ref-server');
const app = express();

const yargs = require('yargs')
  .usage(`Usage: serf-server owner/repo owner2/repo owner/repo3...
Runs a web service to monitor GitHub commits and provide them to Serf clients`)
  .help('h')
  .alias('p', 'port')
  .describe('p', 'The port to start the server on')
  .alias('h', 'help')
  .epilog(`
Some useful environment variables:

SERF_PORT - the port to serve on if not specified via -p, defaults to 3000.
GITHUB_TOKEN - the GitHub API token to use. Must be provided.`);

const argv = yargs.argv;

function main() {
  const validNwos = argv._;
  if (validNwos.length < 1) {
    console.error("ERROR: Supply a list of valid repositories in owner/repo format (i.e. 'rails/rails')\n");
    yargs.showHelp();
    process.exit(-1);
  }

  app.get('/info/:owner/:name', async (req, res) => {
    try {
      if (!req.params.owner || !req.params.name) {
        throw new Error("no");
      }

      let needle = `${req.params.owner}/${req.params.name}`;
      if (!_.find(validNwos, (x) => x === needle)) {
        throw new Error("no");
      }

      res.json(await fetchAllRefsWithInfo(needle));
    } catch (e) {
      d(e.message);
      d(e.stack);
      res.status(500).json({error: e.message});
    }
  });

  let port = argv.port || 3000;
  console.log(`Listening on port ${port}`);
  app.listen(port);
}

main();
<|endoftext|>


<commit_before>PUBVIS = function () {
    var make_it_all = function (params) {
        filename = params.filename;
        target = params.target;

        console.log ('filename: ' + filename);
        console.log ('target: ' + target);

        fetch_bibfile ( filename );

       // var data = bib2json( fetch_bibfile( filename ) );
       // display_data( data.KEYJSON ) ;
       // display_error( data.KEYERROR ) ;

    };

    //get asynchron = stoßt weiteren verlauf an wenn daten geladen
    var fetch_bibfile = function ( filename ) {
        var result;

        $.get( filename, function( data ) {
            //console.log( "Data Loaded" );
            result = bib2json( data );

            display_data( result.json ) ;

            display_error( result.errors ) ;
        });
    }
    //return oneBigJson and errors in an object
    var bib2json = function ( bibfile ) {
        var dataArr, bigJson, errors, entry, entryAt;

        dataArr = bibfile.split("@");
        //console.log("DataArr filled ");

        bigJson = [];
        errors = { index: [], errorMessage: [] };
        for (var i = 1 ; i <= dataArr.length-1; i++) {

            entry = dataArr[i].toString();
            entryAt = "@" + entry;
            //console.log("1. entryAt: " + entryAt);

            try {

                //pars bib-entry to JSON list with one object
                jsonFormat = bibtexParse.toJSON( entryAt );
              //  console.log( "2. dataArr i: " + dataArr[i] );
              //  console.dir( jsonFormat );

            } catch (e) {
                errors.index.push( i );
                errors.errorMessage.push( e );
               // console.log ( e );
            }

            //combine lists
            bigJson = bigJson.concat (jsonFormat);

            //console.log ("entry #" + i + " added");
            //console.log ("3. entry content: " + jsonFormatString);
        };

        //console.dir( bigJson );
        console.log("ende Bib2Json");
        return { json: bigJson,
                 errors: errors };

    }

    var display_data = function ( json ) {

        //draw data
        //console.dir( json );

        //d3.select("body").style("background-color", "black");

        var get_years = function () {
            var all_years_distinct = [], all_years_double = [], amount_per_years = [], actual_year, time_span;
            var oldest_year;

            var count_value_in_array = function (array, value) {
                var counter = 0;
                console.log ("array.length: " + array.length );

                for ( var z = 0; z < array.length; z++ ) {
                    //console.log( "indexJahr: " + array[z] + " und value: " + value );

                    if ( array[z] === value ) {
                        counter++
                        //console.log ("match!" + " count: " + counter);
                    }
                }
                return counter;
            }

            //save all years into a list
            for ( var i = 0; i < json.length; i++ ) {

                if ( json[i].entryTags.year){
                    all_years_double.push( json[i].entryTags.year );
                }

            }
            //console.log( "all_years: " + all_years_double.length);

            //sort array (as JS sorts all emlements as strings, this inner function is
            //necessary to order intagers correct)
            all_years_double.sort( function ( a, b ) {
                return a - b;
            });
            //console.log ( "all_years_double" );
            //console.dir (all_years_double);

            //get first element (= oldest year) and calculate time span for length of array
            actual_year = new Date().getFullYear();
            oldest_year = parseInt(all_years_double[0], 10);
            time_span = actual_year - oldest_year;
            //console.log( "oldest year: " + all_years_double[0]);
            //console.log( "actual_year: " + actual_year );
            //console.log( "time_span: " + time_span );

            //create a new list with time span
            for ( var y = 0; y <= time_span; y++ ) {

                all_years_distinct.push( oldest_year );
                oldest_year++;

            }
            //console.log( "all_years_distinct: " );
            //console.dir( all_years_distinct );

            //iterate list with all_years_double and count their orccurance
            for ( var y = 0; y <= time_span; y++ ) {

                var amount = count_value_in_array( all_years_double, all_years_distinct[y].toString() );
                //console.log ("all_years[y]: " + all_years[y] );
                amount_per_years.push( amount );
                //console.log ("year: " +  all_years_distinct[y] + " amount: " + amount);
            }
            return { time_list: all_years_distinct,
                     amount_per_years: amount_per_years };
        }

        //lists all years counted from the oldest in the file.bib
        console.dir( get_years(json).time_list );

        //lists the amounts per year in the same order as the years are listed
        //(obj[0] = amount of oldest year in bib.file)
        console.dir( get_years(json).amount_per_years );
    }

    var display_error = function ( errors ) {

        //draw errors
        console.dir ( errors );
    }

    return { make_it_all : make_it_all };
} ();<commit_msg>expand function bib2json by adding an additional list to errors which saves the defective bib entry<commit_after>PUBVIS = function () {
    var make_it_all = function (params) {
        filename = params.filename;
        target = params.target;

        console.log ('filename: ' + filename);
        console.log ('target: ' + target);

        fetch_bibfile ( filename );

       // var data = bib2json( fetch_bibfile( filename ) );
       // display_data( data.KEYJSON ) ;
       // display_error( data.KEYERROR ) ;

    };

    //get asynchron = stoßt weiteren verlauf an wenn daten geladen
    var fetch_bibfile = function ( filename ) {
        var result;

        $.get( filename, function( data ) {
            //console.log( "Data Loaded" );
            result = bib2json( data );

            display_data( result.json ) ;

            display_error( result.errors ) ;
        });
    }
    //return oneBigJson and errors in an object
    var bib2json = function ( bibfile ) {
        var dataArr, bigJson, errors, entry, entryAt;

        dataArr = bibfile.split("@");
        //console.log("DataArr filled ");

        bigJson = [];
        errors = { index: [], errorMessage: [], errorEntry: [] };
        for (var i = 1 ; i <= dataArr.length-1; i++) {

            entry = dataArr[i].toString();
            entryAt = "@" + entry;
            //console.log("1. entryAt: " + entryAt);

            try {

                //pars bib-entry to JSON list with one object
                jsonFormat = bibtexParse.toJSON( entryAt );
              //  console.log( "2. dataArr i: " + dataArr[i] );
              //  console.dir( jsonFormat );

            } catch (e) {
                errors.index.push( i );
                errors.errorMessage.push( e );
                errors.errorEntry.push ( entryAt );
               // console.log ( e );
            }

            //combine lists
            bigJson = bigJson.concat (jsonFormat);

            //console.log ("entry #" + i + " added");
            //console.log ("3. entry content: " + jsonFormatString);
        };

        //console.dir( bigJson );
        console.log("ende Bib2Json");
        return { json: bigJson,
                 errors: errors };

    }

    var display_data = function ( json ) {

        //draw data
        //console.dir( json );

        //d3.select("body").style("background-color", "black");

        var get_years = function () {
            var all_years_distinct = [], all_years_double = [], amount_per_years = [], actual_year, time_span;
            var oldest_year;

            //@param.array = array
            //@param.value = string (example "year")
            var count_value_in_array = function (array, value) {
                var counter = 0;
                console.log ("array.length: " + array.length );

                for ( var z = 0; z < array.length; z++ ) {
                    //console.log( "indexJahr: " + array[z] + " und value: " + value );

                    if ( array[z] === value ) {
                        counter++
                        //console.log ("match!" + " count: " + counter);
                    }
                }
                return counter;
            }

            //save all years into a list
            for ( var i = 0; i < json.length; i++ ) {

                if ( json[i].entryTags.year){
                    all_years_double.push( json[i].entryTags.year );
                }

            }
            //console.log( "all_years: " + all_years_double.length);

            //sort array (as JS sorts all emlements as strings, this inner function is
            //necessary to order intagers correct
            //source: Douglas Crockford, JavaScript. The good Parts., p.80
            all_years_double.sort( function ( a, b ) {
                return a - b;
            });
            //console.log ( "all_years_double" );
            //console.dir (all_years_double);

            //get first element (= oldest year) and calculate time span for length of array
            actual_year = new Date().getFullYear();
            oldest_year = parseInt(all_years_double[0], 10);
            time_span = actual_year - oldest_year;
            //console.log( "oldest year: " + all_years_double[0]);
            //console.log( "actual_year: " + actual_year );
            //console.log( "time_span: " + time_span );

            //create a new list with time span
            for ( var y = 0; y <= time_span; y++ ) {

                all_years_distinct.push( oldest_year );
                oldest_year++;

            }
            //console.log( "all_years_distinct: " );
            //console.dir( all_years_distinct );

            //iterate list with all_years_double and count their orccurance
            for ( var y = 0; y <= time_span; y++ ) {

                var amount = count_value_in_array( all_years_double, all_years_distinct[y].toString() );
                //console.log ("all_years[y]: " + all_years[y] );
                amount_per_years.push( amount );
                //console.log ("year: " +  all_years_distinct[y] + " amount: " + amount);
            }
            return { time_list: all_years_distinct,
                     amount_per_years: amount_per_years };
        }

        //lists all years counted from the oldest in the file.bib
        console.dir( get_years(json).time_list );

        //lists the amounts per year in the same order as the years are listed
        //(obj[0] = amount of oldest year in bib.file)
        console.dir( get_years(json).amount_per_years );
    }

    var display_error = function ( errors ) {

        //draw errors
        console.dir ( errors );
    }

    return { make_it_all : make_it_all };
} ();<|endoftext|>