<commit_before>(function() {
  /**
   *  Wrapper over the old save action that displays autosave status.
   *
   * @param {sync.actions.AbstractAction} saveAction the old save action.
   * @param {sync.api.Editor} editor the current editor.
   * @param {number} inteval the autosave interval.
   *
   * @constructor
   */
  var SaveWrapperAction = function(editor, saveAction, saveInterval) {
    sync.actions.AbstractAction.call('');
    this.statusMarker = document.querySelector('.modified-indicator');
    this.saveAction = saveAction;
    this.editor = editor;
    this.interval = saveInterval;
    // the last state class added.
    this.lastClassAdded = null;

    goog.events.listen(this.statusMarker, goog.events.EventType.CLICK,
      goog.bind(this.statusMarkerClicked, this));

    // autosave at given interval every time the editor becomes dirty.
    goog.events.listen(this.editor, sync.api.Editor.EventTypes.DIRTY_STATUS_CHANGED, goog.bind(function(e) {
      if (e.isDirty) {
        this.setStatus('dirty');
        setTimeout(goog.bind(function() {
          if (this.editor.isDirty()) {
            this.setStatus('saving');

            sync.rest.callAsync(RESTDocumentManager.autosave, {id: this.editor.docId})
              .then(goog.bind(function(e) {
                this.editor.setDirty(false);
                this.setStatus('clean');
              }, this))
              .thenCatch(goog.bind(function(e) {
                this.setStatus('error');
              }, this))
          }
        }, this), this.interval * 1000); // transform in miliseconds.
      }
    }, this));
  };
  goog.inherits(SaveWrapperAction, sync.actions.AbstractAction);

  /** @override */
  SaveWrapperAction.prototype.actionPerformed = function(opt_callback) {
    this.setStatus('clean');
    this.saveAction.actionPerformed(opt_callback);
  };

  /** @override */
  SaveWrapperAction.prototype.getDisplayName = function() {
    return this.saveAction.getDisplayName();
  };

  /** @override */
  SaveWrapperAction.prototype.renderLargeIcon = function() {
    var icon = this.saveAction.renderLargeIcon();
    this.setStatus('clean');
    return icon;
  };

  /** @override */
  SaveWrapperAction.prototype.getDescription = function() {
    return this.saveAction.getDescription();
  };

  /** @override */
  SaveWrapperAction.prototype.isEnabled = function() {
    return this.saveAction.isEnabled();
  };

  /**
   * Marks the autosave status
   *
   * @param {string} newStatus the new status to set.
   */
  SaveWrapperAction.prototype.setStatus = function(newStatus) {
    if (this.interval <= 5) {
      if(this.lastStatus == newStatus) {
        return;
      }
      this.lastStatus = newStatus;
      switch (newStatus) {
        case 'clean':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-clean';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - saved';
          this.statusMarker.title = 'All changes were saved.';
          break;

        case 'error':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-error';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - save failed';
          this.statusMarker.title = 'Failed saving your changes.';
          this.showRetryDialog();

          break;
        case 'dirty':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-dirty';

          break;
        case 'saving':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-saving';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - saving...';
          this.statusMarker.title = 'Saving your changes.';

          break;
      }
    } else {
      // for larger intervals we give feedback just for saving.
      if (! this.spinner) {
        var saveButton = document.querySelector('*[name="Author/Save"]');
        if (saveButton) {
          this.spinner = new sync.view.Spinner(saveButton, 0, 'oxy-spinner-dark-background');
        }
      }
      switch (newStatus) {
        case 'saving':
          this.spinner && this.spinner.show();
          break;
        default:
          this.spinner && this.spinner.hide();
      }
    }
  };

  /**
   * Handler for click events on status marker.
   */
  SaveWrapperAction.prototype.statusMarkerClicked = function() {
    if(this.lastStatus == 'error') {
      this.showRetryDialog();
    }
  };

  /**
   * Show a dialog to alow the user to retry the save.
   */
  SaveWrapperAction.prototype.showRetryDialog = function() {
    if (! this.failedDialog) {
      this.failedDialog = workspace.createDialog();
      this.failedDialog.setButtonConfiguration([{key: 'retry', caption: 'Retry'}, {key: 'cancel', caption: 'Cancel'}]);
      this.failedDialog.setTitle('Autosave failed');
      this.failedDialog.getElement().innerHTML =
        'The automatic save has failed.
' +
        'Retry saving the document to prevent losing your current changes.
' +
        'If retry fails, try the Save as or Download actions to save your changes.';

      var callback = function() {};
      // call Save as action on click
      var saveAs = this.failedDialog.getElement().querySelector('.autosave-failed-save-as');
      goog.events.listen(saveAs, goog.events.EventType.CLICK, goog.bind(function() {
        var saveAsAction = this.editor.getActionsManager().getActionById('Author/SaveAs');
        this.failedDialog.hide();
        saveAsAction.actionPerformed(callback);
      }, this));

      // call download action on click
      var download = this.failedDialog.getElement().querySelector('.autosave-failed-download');
      goog.events.listen(download, goog.events.EventType.CLICK, goog.bind(function() {
        var downloadAction = this.editor.getActionsManager().getActionById('Author/Download');
        this.failedDialog.hide();
        downloadAction.actionPerformed(callback);
      }, this));
    }

    this.failedDialog.onSelect(goog.bind(function(key) {
      if (key == 'retry') {
        this.saveAction.actionPerformed(function() {
        });
      }
    }, this));
    this.failedDialog.show();
  };

  /** BEFORE_EDITOR_LOADED */
  goog.events.listen(workspace, sync.api.Workspace.EventType.BEFORE_EDITOR_LOADED, function(e) {
    var autoSaveInterval = parseInt(sync.options.PluginsOptions.getClientOption('webdav_autosave_interval'));
    if (e.options.url.match(/^webdav-https?:/) && autoSaveInterval > 0) {
      /** ACTIONS_LOADED */
      goog.events.listenOnce(e.editor, sync.api.Editor.EventTypes.ACTIONS_LOADED, function(e) {
        var toolbarActions = e.actionsConfiguration.toolbars[0].children;
        if (autoSaveInterval <= 5) {
          // hide the save action for small intervals.
          var i;
          for (i = 0; i < toolbarActions.length; i ++) {
            if (toolbarActions[i].id == 'Author/Save') {
              toolbarActions.splice(i, 1);
            }
          }
        }
        // override the save action from the toolbar
        var editor = e.target;
        var actionsManager = editor.getActionsManager();
        var saveAction = actionsManager.getActionById('Author/Save');
        var autoSaveAction = new SaveWrapperAction(editor, saveAction, autoSaveInterval);
        actionsManager.registerAction('Author/Save', autoSaveAction);
      });
    }
  });
})();<commit_msg>WA-849 Fixed the autosave when being in text mode. We now update the author model before saving.<commit_after>(function() {
  /**
   *  Wrapper over the old save action that displays autosave status.
   *
   * @param {sync.api.Editor} editor the current editor.
   * @param {number} interval the autosave interval.
   *
   * @constructor
   */
  var SaveWrapperAction = function(editor, saveInterval) {
    sync.actions.AbstractAction.call('');
    this.statusMarker = document.querySelector('.modified-indicator');
    this.saveAction = editor.getActionsManager().getActionById('Author/Save');
    this.editor = editor;
    this.interval = saveInterval;
    // the last state class added.
    this.lastClassAdded = null;

    goog.events.listen(this.statusMarker, goog.events.EventType.CLICK,
      goog.bind(this.statusMarkerClicked, this));

    // autosave at given interval every time the editor becomes dirty.
    goog.events.listen(this.editor, sync.api.Editor.EventTypes.DIRTY_STATUS_CHANGED, goog.bind(function(e) {
      if (e.isDirty) {
        this.setStatus('dirty');
        setTimeout(goog.bind(function() {
          if (this.editor.isDirty()) {
            this.setStatus('saving');
            if(this.editor.isTextMode) {
              // autosave after we synchronize the author to the text.
              this.editor.syncToAuthorMode()
                .then(goog.bind(this.autosave, this));
            } else {
              this.autosave();
            }
          }
        }, this), this.interval * 1000); // transform in miliseconds.
      }
    }, this));
  };
  goog.inherits(SaveWrapperAction, sync.actions.AbstractAction);

  /**
   * Calls the rest autosave service.
   */
  SaveWrapperAction.prototype.autosave = function() {
    sync.rest.callAsync(RESTDocumentManager.autosave, {id: this.editor.docId})
      .then(goog.bind(function(e) {
        this.editor.setDirty(false);
        this.setStatus('clean');
      }, this))
      .thenCatch(goog.bind(function(e) {
        this.setStatus('error');
      }, this))
  };

  /** @override */
  SaveWrapperAction.prototype.actionPerformed = function(opt_callback) {
    this.setStatus('clean');
    this.saveAction.actionPerformed(opt_callback);
  };

  /** @override */
  SaveWrapperAction.prototype.getDisplayName = function() {
    return this.saveAction.getDisplayName();
  };

  /** @override */
  SaveWrapperAction.prototype.renderLargeIcon = function() {
    var icon = this.saveAction.renderLargeIcon();
    this.setStatus('clean');
    return icon;
  };

  /** @override */
  SaveWrapperAction.prototype.getDescription = function() {
    return this.saveAction.getDescription();
  };

  /** @override */
  SaveWrapperAction.prototype.isEnabled = function() {
    return this.saveAction.isEnabled();
  };

  /**
   * Marks the autosave status
   *
   * @param {string} newStatus the new status to set.
   */
  SaveWrapperAction.prototype.setStatus = function(newStatus) {
    if (this.interval <= 5) {
      if(this.lastStatus == newStatus) {
        return;
      }
      this.lastStatus = newStatus;
      switch (newStatus) {
        case 'clean':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-clean';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - saved';
          this.statusMarker.title = 'All changes were saved.';
          break;

        case 'error':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-error';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - save failed';
          this.statusMarker.title = 'Failed saving your changes.';
          this.showRetryDialog();

          break;
        case 'dirty':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-dirty';

          break;
        case 'saving':
          goog.dom.classlist.remove(this.statusMarker, this.lastClassAdded);
          this.lastClassAdded = 'autosave-status-saving';
          goog.dom.classlist.add(this.statusMarker, this.lastClassAdded);
          this.statusMarker.innerHTML = ' - saving...';
          this.statusMarker.title = 'Saving your changes.';

          break;
      }
    } else {
      // for larger intervals we give feedback just for saving.
      if (! this.spinner) {
        var saveButton = document.querySelector('*[name="Author/Save"]');
        if (saveButton) {
          this.spinner = new sync.view.Spinner(saveButton, 0, 'oxy-spinner-dark-background');
        }
      }
      switch (newStatus) {
        case 'saving':
          this.spinner && this.spinner.show();
          break;
        default:
          this.spinner && this.spinner.hide();
      }
    }
  };

  /**
   * Handler for click events on status marker.
   */
  SaveWrapperAction.prototype.statusMarkerClicked = function() {
    if(this.lastStatus == 'error') {
      this.showRetryDialog();
    }
  };

  /**
   * Show a dialog to alow the user to retry the save.
   */
  SaveWrapperAction.prototype.showRetryDialog = function() {
    if (! this.failedDialog) {
      this.failedDialog = workspace.createDialog();
      this.failedDialog.setButtonConfiguration([{key: 'retry', caption: 'Retry'}, {key: 'cancel', caption: 'Cancel'}]);
      this.failedDialog.setTitle('Autosave failed');
      this.failedDialog.getElement().innerHTML =
        'The automatic save has failed.
' +
        'Retry saving the document to prevent losing your current changes.
' +
        'If retry fails, try the Save as or Download actions to save your changes.';

      var callback = function() {};
      // call Save as action on click
      var saveAs = this.failedDialog.getElement().querySelector('.autosave-failed-save-as');
      goog.events.listen(saveAs, goog.events.EventType.CLICK, goog.bind(function() {
        var saveAsAction = this.editor.getActionsManager().getActionById('Author/SaveAs');
        this.failedDialog.hide();
        saveAsAction.actionPerformed(callback);
      }, this));

      // call download action on click
      var download = this.failedDialog.getElement().querySelector('.autosave-failed-download');
      goog.events.listen(download, goog.events.EventType.CLICK, goog.bind(function() {
        var downloadAction = this.editor.getActionsManager().getActionById('Author/Download');
        this.failedDialog.hide();
        downloadAction.actionPerformed(callback);
      }, this));
    }

    this.failedDialog.onSelect(goog.bind(function(key) {
      if (key == 'retry') {
        this.saveAction.actionPerformed(function() {
        });
      }
    }, this));
    this.failedDialog.show();
  };

  /** BEFORE_EDITOR_LOADED */
  goog.events.listen(workspace, sync.api.Workspace.EventType.BEFORE_EDITOR_LOADED, function(e) {
    var autoSaveInterval = parseInt(sync.options.PluginsOptions.getClientOption('webdav_autosave_interval'));
    if (e.options.url.match(/^webdav-https?:/) && autoSaveInterval > 0) {
      /** ACTIONS_LOADED */
      goog.events.listenOnce(e.editor, sync.api.Editor.EventTypes.ACTIONS_LOADED, function(e) {
        var toolbarActions = e.actionsConfiguration.toolbars[0].children;
        if (autoSaveInterval <= 5) {
          // hide the save action for small intervals.
          var i;
          for (i = 0; i < toolbarActions.length; i ++) {
            if (toolbarActions[i].id == 'Author/Save') {
              toolbarActions.splice(i, 1);
            }
          }
        }
        // override the save action from the toolbar
        var editor = e.target;
        var autoSaveAction = new SaveWrapperAction(editor, autoSaveInterval);
        editor.getActionsManager()
          .registerAction('Author/Save', autoSaveAction);
      });
    }
  });
})();<|endoftext|>


<commit_before>var formidable = require("formidable"),
    http = require("http"),
    paperboy = require("paperboy"),
    path = require("path");

http.createServer(function(req, res) {
  if (req.url == "/upload" && req.method.toUpperCase() == "POST") {
    var form = new formidable.IncomingForm(),
        files = [];
    form.on("file", function(field, file) {
      files.push(file);
    });
    form.on("end", function() {
      res.writeHead(200, {"Content-Type": "application/json"});
      res.end(JSON.stringify({files: files}));
    });
    form.parse(req);
  } else if (req.url == "/jquery.iframe-transport.js") {
    paperboy.deliver(path.dirname(__dirname), req, res)
  } else {
    paperboy.deliver(__dirname, req, res)
  }
}).listen(8080, "127.0.0.1");

console.log("Server ready at http://localhost:8080/");<commit_msg>Add a bit of logging to the demo server.<commit_after>var formidable = require("formidable"),
    http = require("http"),
    paperboy = require("paperboy"),
    path = require("path");

http.createServer(function(req, res) {
  if (req.url == "/upload" && req.method.toUpperCase() == "POST") {
    var form = new formidable.IncomingForm(),
        files = [];
    form.on("field", function(name, value) {
      console.log(name + ": " + value);
    });
    form.on("file", function(name, file) {
      console.log(file);
      files.push(file);
    });
    form.on("end", function() {
      res.writeHead(200, {"Content-Type": "application/json"});
      res.end(JSON.stringify({files: files}));
    });
    form.parse(req);
  } else if (req.url == "/jquery.iframe-transport.js") {
    paperboy.deliver(path.dirname(__dirname), req, res)
  } else {
    paperboy.deliver(__dirname, req, res)
  }
}).listen(8080, "127.0.0.1");

console.log("Server ready at http://localhost:8080/");
<|endoftext|>


<commit_before>/* eslint-disable no-multiple-empty-lines */

'use strict';

const preq                   = require('preq');
const refParser              = require('json-schema-ref-parser-sync');
const assert                 = require('../../utils/assert.js');
const server                 = require('../../utils/server.js');
const URI                    = require('swagger-router').URI;
const yaml                   = require('js-yaml');
const fs                     = require('fs');
const specLib                = require('../../../lib/spec.js');
const OpenAPISchemaValidator = require('openapi-schema-validator').default;

const validator = new OpenAPISchemaValidator({ version: 3 });
const dateUtil = require('../../../lib/dateUtil');
const pad = dateUtil.pad;
const Ajv    = require('ajv');

const baseUri = `${server.config.uri}en.wikipedia.org/v1/`;
const d1 = new Date();
const d2 = new Date(Date.now() - 2 * dateUtil.ONE_DAY);
const dateStr1 = `${d1.getUTCFullYear()}/${pad(d1.getUTCMonth() + 1)}/${pad(d1.getUTCDate())}`;
const monthDayStr1 = `${pad(d1.getUTCMonth() + 1)}/${pad(d1.getUTCDate())}`;
const dateStr2 = `${d2.getUTCFullYear()}/${pad(d2.getUTCMonth() + 1)}/${pad(d2.getUTCDate())}`;

if (!server.stopHookAdded) {
    server.stopHookAdded = true;
    after(() => server.stop());
}

function staticSpecLoad() {

    const myService = server.config.conf.services[server.config.conf.services.length - 1].conf;
    const specPath = `${__dirname}/../../../${myService.spec ? myService.spec : 'spec'}`;

    return refParser.dereference(specLib.load(specPath, {}), function (err, schema) {
        if (err) {
            // this error will be detected later, so ignore it
            return { paths: {}, 'x-default-params': {} };
        } else {
            return schema;
        }
    });
}

function validateExamples(pathStr, defParams, mSpec) {

    const uri = new URI(pathStr, {}, true);

    if (!mSpec) {
        try {
            uri.expand(defParams);
            return true;
        } catch (e) {
            throw new Error(`Missing parameter for route ${pathStr} : ${e.message}`);
        }
    }

    if (!Array.isArray(mSpec)) {
        throw new Error(`Route ${pathStr} : x-amples must be an array!`);
    }

    mSpec.forEach((ex, idx) => {
        if (!ex.title) {
            throw new Error(`Route ${pathStr}, example ${idx}: title missing!`);
        }
        ex.request = ex.request || {};
        try {
            uri.expand(Object.assign({}, defParams, ex.request.params || {}));
        } catch (e) {
            throw new Error(
                `Route ${pathStr}, example ${idx} (${ex.title}): missing parameter: ${e.message}`
            );
        }
    });

    return true;

}

function constructTestCase(title, path, method, request, response) {

    return {
        title,
        request: {
            uri: server.config.uri + (path[0] === '/' ? path.substr(1) : path),
            method,
            headers: request.headers || {},
            query: request.query,
            body: request.body,
            followRedirect: false
        },
        response: {
            status: response.status || 200,
            headers: response.headers || {},
            body: response.body
        }
    };

}

function constructTests(paths, defParams) {

    const ret = [];

    Object.keys(paths).forEach((pathStr) => {
        Object.keys(paths[pathStr]).forEach((method) => {
            const p = paths[pathStr][method];
            if ({}.hasOwnProperty.call(p, 'x-monitor') && !p['x-monitor']) {
                return;
            }
            const uri = new URI(pathStr, {}, true);
            if (!p['x-amples']) {
                ret.push(constructTestCase(
                    pathStr,
                    uri.toString({ params: defParams }),
                    method,
                    {},
                    {}
                ));
                return;
            }
            p['x-amples'].forEach((ex) => {
                ex.request = ex.request || {};
                ret.push(constructTestCase(
                    ex.title,
                    uri.toString({
                        params: Object.assign({},
                        defParams,
                        ex.request.params || {})
                    }),
                    method,
                    ex.request,
                    ex.response || {}
                ));
            });
        });
    });

    return ret;

}

function cmp(result, expected, errMsg) {

    if (expected === null || expected === undefined) {
        // nothing to expect, so we can return
        return true;
    }
    if (result === null || result === undefined) {
        result = '';
    }

    if (expected.constructor === Object) {
        Object.keys(expected).forEach((key) => {
            const val = expected[key];
            assert.deepEqual({}.hasOwnProperty.call(result, key), true,
                `Body field ${key} not found in response!`);
            cmp(result[key], val, `${key} body field mismatch!`);
        });
        return true;
    } else if (expected.constructor === Array) {
        if (result.constructor !== Array) {
            assert.deepEqual(result, expected, errMsg);
            return true;
        }
        // only one item in expected - compare them all
        if (expected.length === 1 && result.length > 1) {
            result.forEach((item) => {
                cmp(item, expected[0], errMsg);
            });
            return true;
        }
        // more than one item expected, check them one by one
        if (expected.length !== result.length) {
            assert.deepEqual(result, expected, errMsg);
            return true;
        }
        expected.forEach((item, idx) => {
            cmp(result[idx], item, errMsg);
        });
        return true;
    }

    if (expected.length > 1 && expected[0] === '/' && expected[expected.length - 1] === '/') {
        if ((new RegExp(expected.slice(1, -1))).test(result)) {
            return true;
        }
    } else if (expected.length === 0 && result.length === 0) {
        return true;
    } else if (result === expected || result.startsWith(expected)) {
        return true;
    }

    assert.deepEqual(result, expected, errMsg);
    return true;

}

function validateTestResponse(testCase, res) {

    const expRes = testCase.response;

    // check the status
    assert.status(res, expRes.status);
    // check the headers
    Object.keys(expRes.headers).forEach((key) => {
        const val = expRes.headers[key];
        assert.deepEqual({}.hasOwnProperty.call(res.headers, key), true,
            `Header ${key} not found in response!`);
        cmp(res.headers[key], val, `${key} header mismatch!`);
    });
    // check the body
    if (!expRes.body) {
        return true;
    }
    res.body = res.body || '';
    if (Buffer.isBuffer(res.body)) {
        res.body = res.body.toString();
    }
    if (expRes.body.constructor !== res.body.constructor) {
        if (expRes.body.constructor === String) {
            res.body = JSON.stringify(res.body);
        } else {
            res.body = JSON.parse(res.body);
        }
    }
    // check that the body type is the same
    if (expRes.body.constructor !== res.body.constructor) {
        throw new Error(
            `Expected body type ${expRes.body.constructor} but got ${res.body.constructor}`
        );
    }

    // compare the bodies
    cmp(res.body, expRes.body, 'Body mismatch!');

    return true;

}

describe('Swagger spec', function() {

    // the variable holding the spec
    let spec = staticSpecLoad();
    // default params, if given
    let defParams = spec['x-default-params'] || {};

    this.timeout(20000); // eslint-disable-line no-invalid-this

    before(() => {
        return server.start();
    });

    it('get the spec', () => {
        return preq.get(`${server.config.uri}?spec`)
        .then((res) => {
            assert.status(200);
            assert.contentType(res, 'application/json');
            assert.notDeepEqual(res.body, undefined, 'No body received!');
            assert.deepEqual({ errors: [] }, validator.validate(res.body), 'Spec must have no validation errors');
        });
    });

    it('spec validation', () => {
        if (spec['x-default-params']) {
            defParams = spec['x-default-params'];
        }
        // check the high-level attributes
        ['info', 'openapi', 'paths'].forEach((prop) => {
            assert.deepEqual(!!spec[prop], true, `No ${prop} field present!`);
        });
        // no paths - no love
        assert.deepEqual(!!Object.keys(spec.paths), true, 'No paths given in the spec!');
        // now check each path
        Object.keys(spec.paths).forEach((pathStr) => {
            assert.deepEqual(!!pathStr, true, 'A path cannot have a length of zero!');
            const path = spec.paths[pathStr];
            assert.deepEqual(!!Object.keys(path), true, `No methods defined for path: ${pathStr}`);
            Object.keys(path).forEach((method) => {
                const mSpec = path[method];
                if ({}.hasOwnProperty.call(mSpec, 'x-monitor') && !mSpec['x-monitor']) {
                    return;
                }
                validateExamples(pathStr, defParams, mSpec['x-amples']);
            });
        });
    });

    describe('validate responses against schema', () => {
        const ajv = new Ajv({});

        const assertValidSchema = (uri, schemaPath) => {
            return preq.get({ uri })
            .then((res) => {
                if (!ajv.validate(schemaPath, res.body)) {
                    throw new assert.AssertionError({ message: ajv.errorsText() });
                }
            });
        };

        const assertValidSchemaAggregated = (uri) => {
            return preq.get({ uri, query: { aggregated: true } })
            .then((res) => {
                if (res.body) {
                    throw new assert.AssertionError({ message: 'Response should be empty!' });
                }
            });
        };

        const assertBadRequest = (uri) => {
            return preq.get({ uri })
            .then((res) => {
                assert.fail('This request should fail!');
            })
            .catch((err) => {
                if (!ajv.validate('#/components/schemas/problem', err.body.internalErr)) {
                    throw new assert.AssertionError({ message: ajv.errorsText() });
                }
            });
        };

        Object.keys(spec.components.schemas).forEach((defName) => {
            ajv.addSchema(spec.components.schemas[defName], `#/components/schemas/${defName}`);
        });

        // Valid non-aggregated requests

        it('featured article response should conform to schema', () => {
            const uri = `${baseUri}page/featured/${dateStr1}`;
            return assertValidSchema(uri, '#/components/schemas/article_summary_merge_link');
        });

        // Skip for 2019-10-07 due to broken POTD template on-wiki
        it.skip('featured image response should conform to schema', () => {
            const uri = `${baseUri}media/image/featured/${dateStr1}`;
            return assertValidSchema(uri, '#/components/schemas/image');
        });

        it('most-read response should conform to schema', () => {
            const uri = `${baseUri}page/most-read/${dateStr2}`;
            return assertValidSchema(uri, '#/components/schemas/mostread');
        });

        it('news response should conform to schema', () => {
            const uri = `${baseUri}page/news`;
            return assertValidSchema(uri, '#/components/schemas/news');
        });

        it('random response should conform to schema', () => {
            const uri = `${baseUri}page/random/title`;
            return assertValidSchema(uri, '#/components/schemas/random');
        });

        it('announcements should conform to schema', () => {
            const uri = `${baseUri}feed/announcements`;
            return assertValidSchema(uri, '#/components/schemas/announcements');
        });

        it('onthisday response should conform to schema', () => {
            const uri = `${baseUri}feed/onthisday/all/${monthDayStr1}`;
            return assertValidSchema(uri, '#/components/schemas/onthisdayResponse');
        });

        it('summary response should conform to schema', () => {
            const uri = `${baseUri}page/summary/Dubai/808803658`;
            return assertValidSchema(uri, '#/components/schemas/summary');
        });

        it('metadata response should conform to schema', () => {
            const uri = `${baseUri}page/metadata/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/metadata');
        });

        it('media response should conform to schema', () => {
            const uri = `${baseUri}page/media/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/media_list_with_metadata');
        });

        it('media-list response should conform to schema', () => {
            const uri = `${baseUri}page/media-list/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/media_list');
        });

        it('references response should conform to schema', () => {
            const uri = `${baseUri}page/references/List_of_highest-grossing_Indian_films`;
            return assertValidSchema(uri, '#/components/schemas/references_response');
        });

        // Bad requests return empty response for aggregated=true

        it('featured article response should conform to schema (invalid lang, agg=true)', () => {
            const uri = `${server.config.uri}is.wikipedia.org/v1/page/featured/${dateStr1}`;
            return assertValidSchemaAggregated(uri);
        });

        it('featured image response should conform to schema (invalid date, agg=true)', () => {
            const uri = `${baseUri}media/image/featured/2004/01/01`;
            return assertValidSchemaAggregated(uri);
        });

        it('most-read response should conform to schema (invalid date, agg=true)', () => {
            const uri = `${baseUri}page/most-read/2004/01/01`;
            return assertValidSchemaAggregated(uri);
        });

        it('news response (invalid language, agg=true) should be empty', () => {
            return assertValidSchemaAggregated(`${server.config.uri}is.wikipedia.org/v1/page/news`);
        });

        // Bad requests fail without aggregated=true

        it('featured article request should fail for invalid language when !agg=true', () => {
            const uri = `${server.config.uri}is.wikipedia.org/v1/page/featured/${dateStr1}`;
            return assertBadRequest(uri);
        });

        it('featured image request should fail for invalid date when !agg=true', () => {
            const uri = `${baseUri}media/image/featured/2004/01/01`;
            return assertBadRequest(uri);
        });

        it('most-read request should fail for invalid date when !agg=true', () => {
            const uri = `${baseUri}page/most-read/2004/01/01`;
            return assertBadRequest(uri);
        });

        it('news request should fail for invalid language when !agg=true', () => {
            return assertBadRequest(`${server.config.uri}is.wikipedia.org/v1/page/news`);
        });
    });

    describe('validate spec examples', () => {

        constructTests(spec.paths, defParams).forEach((testCase) => {
            it(testCase.title, () => {
                return preq(testCase.request)
                .then((res) => {
                    validateTestResponse(testCase, res);
                }, (err) => {
                    validateTestResponse(testCase, err);
                });
            });
        });

    });
});
<commit_msg>Hygiene: Stop skipping featured-image spec test<commit_after>/* eslint-disable no-multiple-empty-lines */

'use strict';

const preq                   = require('preq');
const refParser              = require('json-schema-ref-parser-sync');
const assert                 = require('../../utils/assert.js');
const server                 = require('../../utils/server.js');
const URI                    = require('swagger-router').URI;
const yaml                   = require('js-yaml');
const fs                     = require('fs');
const specLib                = require('../../../lib/spec.js');
const OpenAPISchemaValidator = require('openapi-schema-validator').default;

const validator = new OpenAPISchemaValidator({ version: 3 });
const dateUtil = require('../../../lib/dateUtil');
const pad = dateUtil.pad;
const Ajv    = require('ajv');

const baseUri = `${server.config.uri}en.wikipedia.org/v1/`;
const d1 = new Date();
const d2 = new Date(Date.now() - 2 * dateUtil.ONE_DAY);
const dateStr1 = `${d1.getUTCFullYear()}/${pad(d1.getUTCMonth() + 1)}/${pad(d1.getUTCDate())}`;
const monthDayStr1 = `${pad(d1.getUTCMonth() + 1)}/${pad(d1.getUTCDate())}`;
const dateStr2 = `${d2.getUTCFullYear()}/${pad(d2.getUTCMonth() + 1)}/${pad(d2.getUTCDate())}`;

if (!server.stopHookAdded) {
    server.stopHookAdded = true;
    after(() => server.stop());
}

function staticSpecLoad() {

    const myService = server.config.conf.services[server.config.conf.services.length - 1].conf;
    const specPath = `${__dirname}/../../../${myService.spec ? myService.spec : 'spec'}`;

    return refParser.dereference(specLib.load(specPath, {}), function (err, schema) {
        if (err) {
            // this error will be detected later, so ignore it
            return { paths: {}, 'x-default-params': {} };
        } else {
            return schema;
        }
    });
}

function validateExamples(pathStr, defParams, mSpec) {

    const uri = new URI(pathStr, {}, true);

    if (!mSpec) {
        try {
            uri.expand(defParams);
            return true;
        } catch (e) {
            throw new Error(`Missing parameter for route ${pathStr} : ${e.message}`);
        }
    }

    if (!Array.isArray(mSpec)) {
        throw new Error(`Route ${pathStr} : x-amples must be an array!`);
    }

    mSpec.forEach((ex, idx) => {
        if (!ex.title) {
            throw new Error(`Route ${pathStr}, example ${idx}: title missing!`);
        }
        ex.request = ex.request || {};
        try {
            uri.expand(Object.assign({}, defParams, ex.request.params || {}));
        } catch (e) {
            throw new Error(
                `Route ${pathStr}, example ${idx} (${ex.title}): missing parameter: ${e.message}`
            );
        }
    });

    return true;

}

function constructTestCase(title, path, method, request, response) {

    return {
        title,
        request: {
            uri: server.config.uri + (path[0] === '/' ? path.substr(1) : path),
            method,
            headers: request.headers || {},
            query: request.query,
            body: request.body,
            followRedirect: false
        },
        response: {
            status: response.status || 200,
            headers: response.headers || {},
            body: response.body
        }
    };

}

function constructTests(paths, defParams) {

    const ret = [];

    Object.keys(paths).forEach((pathStr) => {
        Object.keys(paths[pathStr]).forEach((method) => {
            const p = paths[pathStr][method];
            if ({}.hasOwnProperty.call(p, 'x-monitor') && !p['x-monitor']) {
                return;
            }
            const uri = new URI(pathStr, {}, true);
            if (!p['x-amples']) {
                ret.push(constructTestCase(
                    pathStr,
                    uri.toString({ params: defParams }),
                    method,
                    {},
                    {}
                ));
                return;
            }
            p['x-amples'].forEach((ex) => {
                ex.request = ex.request || {};
                ret.push(constructTestCase(
                    ex.title,
                    uri.toString({
                        params: Object.assign({},
                        defParams,
                        ex.request.params || {})
                    }),
                    method,
                    ex.request,
                    ex.response || {}
                ));
            });
        });
    });

    return ret;

}

function cmp(result, expected, errMsg) {

    if (expected === null || expected === undefined) {
        // nothing to expect, so we can return
        return true;
    }
    if (result === null || result === undefined) {
        result = '';
    }

    if (expected.constructor === Object) {
        Object.keys(expected).forEach((key) => {
            const val = expected[key];
            assert.deepEqual({}.hasOwnProperty.call(result, key), true,
                `Body field ${key} not found in response!`);
            cmp(result[key], val, `${key} body field mismatch!`);
        });
        return true;
    } else if (expected.constructor === Array) {
        if (result.constructor !== Array) {
            assert.deepEqual(result, expected, errMsg);
            return true;
        }
        // only one item in expected - compare them all
        if (expected.length === 1 && result.length > 1) {
            result.forEach((item) => {
                cmp(item, expected[0], errMsg);
            });
            return true;
        }
        // more than one item expected, check them one by one
        if (expected.length !== result.length) {
            assert.deepEqual(result, expected, errMsg);
            return true;
        }
        expected.forEach((item, idx) => {
            cmp(result[idx], item, errMsg);
        });
        return true;
    }

    if (expected.length > 1 && expected[0] === '/' && expected[expected.length - 1] === '/') {
        if ((new RegExp(expected.slice(1, -1))).test(result)) {
            return true;
        }
    } else if (expected.length === 0 && result.length === 0) {
        return true;
    } else if (result === expected || result.startsWith(expected)) {
        return true;
    }

    assert.deepEqual(result, expected, errMsg);
    return true;

}

function validateTestResponse(testCase, res) {

    const expRes = testCase.response;

    // check the status
    assert.status(res, expRes.status);
    // check the headers
    Object.keys(expRes.headers).forEach((key) => {
        const val = expRes.headers[key];
        assert.deepEqual({}.hasOwnProperty.call(res.headers, key), true,
            `Header ${key} not found in response!`);
        cmp(res.headers[key], val, `${key} header mismatch!`);
    });
    // check the body
    if (!expRes.body) {
        return true;
    }
    res.body = res.body || '';
    if (Buffer.isBuffer(res.body)) {
        res.body = res.body.toString();
    }
    if (expRes.body.constructor !== res.body.constructor) {
        if (expRes.body.constructor === String) {
            res.body = JSON.stringify(res.body);
        } else {
            res.body = JSON.parse(res.body);
        }
    }
    // check that the body type is the same
    if (expRes.body.constructor !== res.body.constructor) {
        throw new Error(
            `Expected body type ${expRes.body.constructor} but got ${res.body.constructor}`
        );
    }

    // compare the bodies
    cmp(res.body, expRes.body, 'Body mismatch!');

    return true;

}

describe('Swagger spec', function() {

    // the variable holding the spec
    let spec = staticSpecLoad();
    // default params, if given
    let defParams = spec['x-default-params'] || {};

    this.timeout(20000); // eslint-disable-line no-invalid-this

    before(() => {
        return server.start();
    });

    it('get the spec', () => {
        return preq.get(`${server.config.uri}?spec`)
        .then((res) => {
            assert.status(200);
            assert.contentType(res, 'application/json');
            assert.notDeepEqual(res.body, undefined, 'No body received!');
            assert.deepEqual({ errors: [] }, validator.validate(res.body), 'Spec must have no validation errors');
        });
    });

    it('spec validation', () => {
        if (spec['x-default-params']) {
            defParams = spec['x-default-params'];
        }
        // check the high-level attributes
        ['info', 'openapi', 'paths'].forEach((prop) => {
            assert.deepEqual(!!spec[prop], true, `No ${prop} field present!`);
        });
        // no paths - no love
        assert.deepEqual(!!Object.keys(spec.paths), true, 'No paths given in the spec!');
        // now check each path
        Object.keys(spec.paths).forEach((pathStr) => {
            assert.deepEqual(!!pathStr, true, 'A path cannot have a length of zero!');
            const path = spec.paths[pathStr];
            assert.deepEqual(!!Object.keys(path), true, `No methods defined for path: ${pathStr}`);
            Object.keys(path).forEach((method) => {
                const mSpec = path[method];
                if ({}.hasOwnProperty.call(mSpec, 'x-monitor') && !mSpec['x-monitor']) {
                    return;
                }
                validateExamples(pathStr, defParams, mSpec['x-amples']);
            });
        });
    });

    describe('validate responses against schema', () => {
        const ajv = new Ajv({});

        const assertValidSchema = (uri, schemaPath) => {
            return preq.get({ uri })
            .then((res) => {
                if (!ajv.validate(schemaPath, res.body)) {
                    throw new assert.AssertionError({ message: ajv.errorsText() });
                }
            });
        };

        const assertValidSchemaAggregated = (uri) => {
            return preq.get({ uri, query: { aggregated: true } })
            .then((res) => {
                if (res.body) {
                    throw new assert.AssertionError({ message: 'Response should be empty!' });
                }
            });
        };

        const assertBadRequest = (uri) => {
            return preq.get({ uri })
            .then((res) => {
                assert.fail('This request should fail!');
            })
            .catch((err) => {
                if (!ajv.validate('#/components/schemas/problem', err.body.internalErr)) {
                    throw new assert.AssertionError({ message: ajv.errorsText() });
                }
            });
        };

        Object.keys(spec.components.schemas).forEach((defName) => {
            ajv.addSchema(spec.components.schemas[defName], `#/components/schemas/${defName}`);
        });

        // Valid non-aggregated requests

        it('featured article response should conform to schema', () => {
            const uri = `${baseUri}page/featured/${dateStr1}`;
            return assertValidSchema(uri, '#/components/schemas/article_summary_merge_link');
        });

        it('featured image response should conform to schema', () => {
            const uri = `${baseUri}media/image/featured/${dateStr1}`;
            return assertValidSchema(uri, '#/components/schemas/image');
        });

        it('most-read response should conform to schema', () => {
            const uri = `${baseUri}page/most-read/${dateStr2}`;
            return assertValidSchema(uri, '#/components/schemas/mostread');
        });

        it('news response should conform to schema', () => {
            const uri = `${baseUri}page/news`;
            return assertValidSchema(uri, '#/components/schemas/news');
        });

        it('random response should conform to schema', () => {
            const uri = `${baseUri}page/random/title`;
            return assertValidSchema(uri, '#/components/schemas/random');
        });

        it('announcements should conform to schema', () => {
            const uri = `${baseUri}feed/announcements`;
            return assertValidSchema(uri, '#/components/schemas/announcements');
        });

        it('onthisday response should conform to schema', () => {
            const uri = `${baseUri}feed/onthisday/all/${monthDayStr1}`;
            return assertValidSchema(uri, '#/components/schemas/onthisdayResponse');
        });

        it('summary response should conform to schema', () => {
            const uri = `${baseUri}page/summary/Dubai/808803658`;
            return assertValidSchema(uri, '#/components/schemas/summary');
        });

        it('metadata response should conform to schema', () => {
            const uri = `${baseUri}page/metadata/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/metadata');
        });

        it('media response should conform to schema', () => {
            const uri = `${baseUri}page/media/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/media_list_with_metadata');
        });

        it('media-list response should conform to schema', () => {
            const uri = `${baseUri}page/media-list/Hummingbird`;
            return assertValidSchema(uri, '#/components/schemas/media_list');
        });

        it('references response should conform to schema', () => {
            const uri = `${baseUri}page/references/List_of_highest-grossing_Indian_films`;
            return assertValidSchema(uri, '#/components/schemas/references_response');
        });

        // Bad requests return empty response for aggregated=true

        it('featured article response should conform to schema (invalid lang, agg=true)', () => {
            const uri = `${server.config.uri}is.wikipedia.org/v1/page/featured/${dateStr1}`;
            return assertValidSchemaAggregated(uri);
        });

        it('featured image response should conform to schema (invalid date, agg=true)', () => {
            const uri = `${baseUri}media/image/featured/2004/01/01`;
            return assertValidSchemaAggregated(uri);
        });

        it('most-read response should conform to schema (invalid date, agg=true)', () => {
            const uri = `${baseUri}page/most-read/2004/01/01`;
            return assertValidSchemaAggregated(uri);
        });

        it('news response (invalid language, agg=true) should be empty', () => {
            return assertValidSchemaAggregated(`${server.config.uri}is.wikipedia.org/v1/page/news`);
        });

        // Bad requests fail without aggregated=true

        it('featured article request should fail for invalid language when !agg=true', () => {
            const uri = `${server.config.uri}is.wikipedia.org/v1/page/featured/${dateStr1}`;
            return assertBadRequest(uri);
        });

        it('featured image request should fail for invalid date when !agg=true', () => {
            const uri = `${baseUri}media/image/featured/2004/01/01`;
            return assertBadRequest(uri);
        });

        it('most-read request should fail for invalid date when !agg=true', () => {
            const uri = `${baseUri}page/most-read/2004/01/01`;
            return assertBadRequest(uri);
        });

        it('news request should fail for invalid language when !agg=true', () => {
            return assertBadRequest(`${server.config.uri}is.wikipedia.org/v1/page/news`);
        });
    });

    describe('validate spec examples', () => {

        constructTests(spec.paths, defParams).forEach((testCase) => {
            it(testCase.title, () => {
                return preq(testCase.request)
                .then((res) => {
                    validateTestResponse(testCase, res);
                }, (err) => {
                    validateTestResponse(testCase, err);
                });
            });
        });

    });
});
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var Bluebird = require('Bluebird');
var readFile = Bluebird.promisify(fs.readFile);

module.exports = function() {
  var sourceFile = path.join(__dirname, 'source.js');

  var lineResults = [
    1,
    undefined,
    1,
    1,
    1,
    undefined,
    0,
    undefined,
    undefined
  ];

  return readFile(sourceFile).then(function(content) {
    var report = {};

    lineResults.source = content.toString();
    report[sourceFile] = lineResults;

    return Bluebird.resolve({
      path: sourceFile,
      resultReport: report
    });
  });

};
<commit_msg>fix module name<commit_after>var fs = require('fs');
var path = require('path');
var Bluebird = require('bluebird');
var readFile = Bluebird.promisify(fs.readFile);

module.exports = function() {
  var sourceFile = path.join(__dirname, 'source.js');

  var lineResults = [
    1,
    undefined,
    1,
    1,
    1,
    undefined,
    0,
    undefined,
    undefined
  ];

  return readFile(sourceFile).then(function(content) {
    var report = {};

    lineResults.source = content.toString();
    report[sourceFile] = lineResults;

    return Bluebird.resolve({
      path: sourceFile,
      resultReport: report
    });
  });

};
<|endoftext|>


<commit_before>/**
 * @fileOverview Low level JSGI adapter implementation.
 */

var {Headers, getMimeParameter} = require('ringo/utils/http');
var {Stream} = require('io');
var {Binary, ByteString} = require('binary');
var system = require('system')

export('handleRequest', 'writeHeaders');
var log = require('ringo/logging').getLogger(module.id);

/**
 * Handle a JSGI request.
 * @param moduleId the module id. Ignored if functionObj is already a function.
 * @param functionObj the function, either as function object or function name to be
 *             imported from the module moduleId.
 * @param req the JSGI request object
 * @returns the JSGI response object
 */
function handleRequest(moduleId, functionObj, request) {
    initRequest(request);
    var app;
    if (typeof(functionObj) == 'function') {
        app = functionObj;
    } else {
        var module = require(moduleId);
        app = module[functionObj];
        var middleware = module.middleware || [];
        request.env.ringo_config = moduleId;
        app = middleware.reduceRight(middlewareWrapper, resolve(app));
    }
    if (!(typeof(app) == 'function')) {
        throw new Error('No valid JSGI app: ' + app);
    }
    var result = app(request);
    if (!result) {
        throw new Error('No valid JSGI response: ' + result);
    }
    commitResponse(request, result);
}

/**
 * Set up the I/O related properties of a jsgi environment object.
 * @param env a jsgi request object
 */
function initRequest(request) {
    var input, errors;
    if (request.hasOwnProperty('input')) {
        // already set up, probably because the original request threw a retry
        return;
    }
    Object.defineProperty(request, "input", {
        get: function() {
            if (!input)
                input = new Stream(request.env.servletRequest.getInputStream());
            return input;
        }
    });
    Object.defineProperty(request.jsgi, "errors", {
        value: system.stderr
    });
}

/**
 * Apply the return value of a JSGI application to a servlet response.
 * This is used internally by the org.ringojs.jsgi.JsgiServlet class, so
 * you won't need this unless you're implementing your own servlet
 * based JSGI connector.
 *
 * @param req the JSGI request argument
 * @param result the object returned by a JSGI application
 */
function commitResponse(req, result) {
    var request = req.env.servletRequest;
    var response = req.env.servletResponse;
    var {status, headers, body} = result;
    if (!status || !headers || !body) {
        // Check if this is an asynchronous response. If not throw an Error
        if (handleAsyncResponse(request, response, result)) {
            return;
        } else {
            throw new Error('No valid JSGI response: ' + result);
        }
    }
    // Allow application/middleware to handle request via Servlet API
    if (!response.isCommitted() && !Headers(headers).contains("X-JSGI-Skip-Response")) {
        writeResponse(response, status, headers, body);
    }
}

function writeResponse(servletResponse, status, headers, body) {
    servletResponse.setStatus(status);
    writeHeaders(servletResponse, headers);
    var charset = getMimeParameter(headers.get("Content-Type"), "charset");
    writeBody(servletResponse, body, charset);
}

function writeHeaders(servletResponse, headers) {
    for (var key in headers) {
        var values = headers[key];
        if (typeof values === "string") {
            values = values.split("\n");
        } else if (!Array.isArray(values)) {
            continue;
        }
        values.forEach(function(value) {
            servletResponse.addHeader(key, value);
        });
    }
}

function writeBody(response, body, charset) {
    if (body && typeof body.forEach == "function") {
        var output = response.getOutputStream();
        var writer = function(part) {
            if (!(part instanceof Binary)) {
                part = part.toByteString(charset);
            }
            output.write(part);
        };
        body.forEach(writer);
        if (typeof body.close == "function") {
            body.close(writer);
        }
    } else {
        throw new Error("Response body doesn't implement forEach: " + body);
    }
}

function writeAsync(servletResponse, jsgiResponse) {
    if (!jsgiResponse.status || !jsgiResponse.headers || !jsgiResponse.body) {
        throw new Error('No valid JSGI response: ' + jsgiResponse);
    }
    var {status, headers, body} = jsgiResponse;
    writeResponse(servletResponse, status, Headers(headers), body);
}

function handleAsyncResponse(request, response, result) {
    // support for asynchronous JSGI based on Jetty continuations
    // If result has a "suspend" method we just call it and return, letting
    // the response take care of everything
    if (typeof result.suspend === "function") {
        result.suspend();
        return true;
    }
    // As a convenient shorthand, allow apps to return the deferred as returned
    // by ringo.promise.defer()
    if (result.promise && typeof result.promise.then === "function") {
        result = result.promise;
    }
    // Only handle asynchronously if the result has a then() function
    if (typeof result.then !== "function") {
        return false;
    }

    var {ContinuationSupport, ContinuationListener} = org.eclipse.jetty.continuation;
    var continuation = ContinuationSupport.getContinuation(request);
    var handled = false;

    var onFinish = sync(function(value) {
        if (handled) return;
        log.debug("JSGI async response finished", value);
        handled = true;
        writeAsync(continuation.getServletResponse(), value);
        continuation.complete();
    }, request);

    var onError = sync(function(error) {
        if (handled) return;
        log.error("JSGI async error", error);
        var jsgiResponse = {
            status: 500,
            headers: {"Content-Type": "text/html"},
            body: ["<!DOCTYPE html>
Error

", String(error), "
"]
        };
        handled = true;
        writeAsync(continuation.getServletResponse(), jsgiResponse);
        continuation.complete();
    }, request);

    continuation.addContinuationListener(new ContinuationListener({
        onTimeout: sync(function() {
            if (handled) return;
            log.error("JSGI async timeout");
            var jsgiResponse = {
                status: 500,
                headers: {"Content-Type": "text/html"},
                body: ["<!DOCTYPE html>
Error

Request timed out
"]
            };
            handled = true;
            writeAsync(continuation.getServletResponse(), jsgiResponse);
            continuation.complete();
        }, request)
    }));

    log.debug('handling JSGI async response, calling then');
    sync(function() {
        result.then(onFinish, onError);
        // default async request timeout is 30 seconds
        continuation.setTimeout(result.timeout || 30000);
        continuation.suspend(response);
    }, request)();
    return true;
}

/**
 * Convenience function that resolves a module id or object to a
 * JSGI middleware or application function. This assumes the function is
 * exported as "middleware" or "handleRequest".
 * @param app a function, module object, module id, or an array of
 *            any of these
 * @returns the resolved middleware function
 */
function resolve(app) {
    if (typeof app == 'string') {
        var module = require(app);
        return module.middleware || module.handleRequest;
    } else if (Array.isArray(app)) {
        // allow an app or middleware item to be itself a list of middlewares
        return app.reduceRight(middlewareWrapper);
    }
    return app;
}

/**
 * Helper function for wrapping middleware stacks
 * @param inner an app or middleware module or function wrapped by outer
 * @param outer a middleware module or function wrapping inner
 * @returns the wrapped middleware function
 */
function middlewareWrapper(inner, outer) {
    return resolve(outer)(inner);
}
<commit_msg>Add support for environments to ringo/jsgi as defined by modular JSGI spec<commit_after>/**
 * @fileOverview Low level JSGI adapter implementation.
 */

var {Headers, getMimeParameter} = require('ringo/utils/http');
var {Stream} = require('io');
var {Binary, ByteString} = require('binary');
var system = require('system')

export('handleRequest', 'writeHeaders');
var log = require('ringo/logging').getLogger(module.id);

/**
 * Handle a JSGI request.
 * @param moduleId the module id. Ignored if functionObj is already a function.
 * @param functionObj the function, either as function object or function name to be
 *             imported from the module moduleId.
 * @param req the JSGI request object
 * @returns the JSGI response object
 */
function handleRequest(moduleId, functionObj, request) {
    initRequest(request);
    var app;
    if (typeof(functionObj) === 'function') {
        app = functionObj;
    } else {
        var module = require(moduleId);
        app = module[functionObj];
        var middleware = module.middleware || [];
        request.env.ringo_config = moduleId;
        app = middleware.reduceRight(middlewareWrapper, resolve(app));
    }
    // if RINGO_ENV environment variable is set and application supports
    // modular JSGI environment feature use the proper environment
    if (app && system.env.RINGO_ENV && typeof(app.env) === 'function') {
        app = app.env(system.env.RINGO_ENV);
    }
    if (typeof(app) !== 'function') {
        throw new Error('No valid JSGI app: ' + app);
    }
    var result = app(request);
    if (!result) {
        throw new Error('No valid JSGI response: ' + result);
    }
    commitResponse(request, result);
}

/**
 * Set up the I/O related properties of a jsgi environment object.
 * @param env a jsgi request object
 */
function initRequest(request) {
    var input, errors;
    if (request.hasOwnProperty('input')) {
        // already set up, probably because the original request threw a retry
        return;
    }
    Object.defineProperty(request, "input", {
        get: function() {
            if (!input)
                input = new Stream(request.env.servletRequest.getInputStream());
            return input;
        }
    });
    Object.defineProperty(request.jsgi, "errors", {
        value: system.stderr
    });
}

/**
 * Apply the return value of a JSGI application to a servlet response.
 * This is used internally by the org.ringojs.jsgi.JsgiServlet class, so
 * you won't need this unless you're implementing your own servlet
 * based JSGI connector.
 *
 * @param req the JSGI request argument
 * @param result the object returned by a JSGI application
 */
function commitResponse(req, result) {
    var request = req.env.servletRequest;
    var response = req.env.servletResponse;
    var {status, headers, body} = result;
    if (!status || !headers || !body) {
        // Check if this is an asynchronous response. If not throw an Error
        if (handleAsyncResponse(request, response, result)) {
            return;
        } else {
            throw new Error('No valid JSGI response: ' + result);
        }
    }
    // Allow application/middleware to handle request via Servlet API
    if (!response.isCommitted() && !Headers(headers).contains("X-JSGI-Skip-Response")) {
        writeResponse(response, status, headers, body);
    }
}

function writeResponse(servletResponse, status, headers, body) {
    servletResponse.setStatus(status);
    writeHeaders(servletResponse, headers);
    var charset = getMimeParameter(headers.get("Content-Type"), "charset");
    writeBody(servletResponse, body, charset);
}

function writeHeaders(servletResponse, headers) {
    for (var key in headers) {
        var values = headers[key];
        if (typeof values === "string") {
            values = values.split("\n");
        } else if (!Array.isArray(values)) {
            continue;
        }
        values.forEach(function(value) {
            servletResponse.addHeader(key, value);
        });
    }
}

function writeBody(response, body, charset) {
    if (body && typeof body.forEach == "function") {
        var output = response.getOutputStream();
        var writer = function(part) {
            if (!(part instanceof Binary)) {
                part = part.toByteString(charset);
            }
            output.write(part);
        };
        body.forEach(writer);
        if (typeof body.close == "function") {
            body.close(writer);
        }
    } else {
        throw new Error("Response body doesn't implement forEach: " + body);
    }
}

function writeAsync(servletResponse, jsgiResponse) {
    if (!jsgiResponse.status || !jsgiResponse.headers || !jsgiResponse.body) {
        throw new Error('No valid JSGI response: ' + jsgiResponse);
    }
    var {status, headers, body} = jsgiResponse;
    writeResponse(servletResponse, status, Headers(headers), body);
}

function handleAsyncResponse(request, response, result) {
    // support for asynchronous JSGI based on Jetty continuations
    // If result has a "suspend" method we just call it and return, letting
    // the response take care of everything
    if (typeof result.suspend === "function") {
        result.suspend();
        return true;
    }
    // As a convenient shorthand, allow apps to return the deferred as returned
    // by ringo.promise.defer()
    if (result.promise && typeof result.promise.then === "function") {
        result = result.promise;
    }
    // Only handle asynchronously if the result has a then() function
    if (typeof result.then !== "function") {
        return false;
    }

    var {ContinuationSupport, ContinuationListener} = org.eclipse.jetty.continuation;
    var continuation = ContinuationSupport.getContinuation(request);
    var handled = false;

    var onFinish = sync(function(value) {
        if (handled) return;
        log.debug("JSGI async response finished", value);
        handled = true;
        writeAsync(continuation.getServletResponse(), value);
        continuation.complete();
    }, request);

    var onError = sync(function(error) {
        if (handled) return;
        log.error("JSGI async error", error);
        var jsgiResponse = {
            status: 500,
            headers: {"Content-Type": "text/html"},
            body: ["<!DOCTYPE html>
Error

", String(error), "
"]
        };
        handled = true;
        writeAsync(continuation.getServletResponse(), jsgiResponse);
        continuation.complete();
    }, request);

    continuation.addContinuationListener(new ContinuationListener({
        onTimeout: sync(function() {
            if (handled) return;
            log.error("JSGI async timeout");
            var jsgiResponse = {
                status: 500,
                headers: {"Content-Type": "text/html"},
                body: ["<!DOCTYPE html>
Error

Request timed out
"]
            };
            handled = true;
            writeAsync(continuation.getServletResponse(), jsgiResponse);
            continuation.complete();
        }, request)
    }));

    log.debug('handling JSGI async response, calling then');
    sync(function() {
        result.then(onFinish, onError);
        // default async request timeout is 30 seconds
        continuation.setTimeout(result.timeout || 30000);
        continuation.suspend(response);
    }, request)();
    return true;
}

/**
 * Convenience function that resolves a module id or object to a
 * JSGI middleware or application function. This assumes the function is
 * exported as "middleware" or "handleRequest".
 * @param app a function, module object, module id, or an array of
 *            any of these
 * @returns the resolved middleware function
 */
function resolve(app) {
    if (typeof app == 'string') {
        var module = require(app);
        return module.middleware || module.handleRequest;
    } else if (Array.isArray(app)) {
        // allow an app or middleware item to be itself a list of middlewares
        return app.reduceRight(middlewareWrapper);
    }
    return app;
}

/**
 * Helper function for wrapping middleware stacks
 * @param inner an app or middleware module or function wrapped by outer
 * @param outer a middleware module or function wrapping inner
 * @returns the wrapped middleware function
 */
function middlewareWrapper(inner, outer) {
    return resolve(outer)(inner);
}
<|endoftext|>


<commit_before>const Sinon = require("sinon");
const Assert = require('assert');
const debug = require('debug');
const LambdaUtil = require('../lib/LambdaUtil');

describe("LambdaUtil", () => {

	const debugEnableState = debug.disable();
	let origHandlerDef;
	before(() => {
		// enable debugging to enforce execution of debug code paths
		debug.enable('dynatrace');
		origHandlerDef = process.env._HANDLER;
	});

	after(() => {
		debug.disable();
		debug.enable(debugEnableState);
		process.env._HANDLER = origHandlerDef;
	});

	let sandbox;
	beforeEach(() => {
		sandbox = Sinon.createSandbox();
	});

	afterEach(() => {
		sandbox.restore();
	});

	function decompose(handlerDef, expectedModuleFilePath, expectedExportPath) {
		const result = LambdaUtil.decomposeHandlerDef(handlerDef);

		Assert.strictEqual(result.moduleFilePath, expectedModuleFilePath, handlerDef);
		Assert.deepEqual(result.exportPathArray, expectedExportPath);
	}

	it("decomposeHandlerDef", () => {
		decompose("index.handler", "index", ["handler"]);
		decompose("lib/index.handler", "lib/index", ["handler"]);
		decompose("index.foo.handler", "index", ["foo", "handler"]);
		decompose("lib/index.foo.bar.handler", "lib/index", ["foo", "bar", "handler"]);
	});

	function resolveUserHandler(handlerDef) {
		it(handlerDef, () => {
			// create a proxy object which getter and a getter stub
			const proxyGetterStub = Sinon.stub();
			const proxy = new Proxy(Object.create(null), {
				get: proxyGetterStub
			});

			process.env._HANDLER = "should be preserved";

			// let the proxy object getter return the proxy object
			proxyGetterStub.returns(proxy);

			// decompose handler
			const decomposeResult = LambdaUtil.decomposeHandlerDef(handlerDef);

			// resolveUserHandlerFromDtLambdaHandler destructs exportPath in decomposeResult -> make a copy
			const exportPathCopy = decomposeResult.exportPathArray.slice();

			// stub decomposeHandlerDef and let stub return previous determined result
			const decomposeHandlerDefStub = sandbox.stub(LambdaUtil, "decomposeHandlerDef").returns(decomposeResult);

			const result = LambdaUtil.resolveUserHandlerFromDtLambdaHandler(proxy, handlerDef);

			Sinon.assert.calledOnce(decomposeHandlerDefStub);
			Sinon.assert.calledWith(decomposeHandlerDefStub, handlerDef);

			Assert.strictEqual(proxyGetterStub.callCount, exportPathCopy.length);

			// check if first proxy getter invocation was done with modulePath$topLevelExport
			Sinon.assert.calledWith(proxyGetterStub.getCall(0), proxy, `${decomposeResult.moduleFilePath}$${exportPathCopy[0]}`);

			// check nested export paths
			for (let i = 1; i < exportPathCopy.length; ++i) {
				Sinon.assert.calledWith(proxyGetterStub.getCall(i), proxy, exportPathCopy[i]);
			}

			Assert.strictEqual(result, proxy);
			Assert.strictEqual(process.env._HANDLER, "should be preserved");
		});
	}

	describe("resolveUserHandlerFromDtLambdaHandler", () => {
		resolveUserHandler("index.handler");
		resolveUserHandler("lib/index.handler");
		resolveUserHandler("index.foo.handler");
		resolveUserHandler("lib/index.foo.handler");
		resolveUserHandler("lib/index.foo.bar.handler");
	});
});
<commit_msg>unit test Windows compatibility<commit_after>const Sinon = require("sinon");
const Assert = require('assert');
const debug = require('debug');
const Path = require('path');
const LambdaUtil = require('../lib/LambdaUtil');

describe("LambdaUtil", () => {

	const debugEnableState = debug.disable();
	let origHandlerDef;
	before(() => {
		// enable debugging to enforce execution of debug code paths
		debug.enable('dynatrace');
		origHandlerDef = process.env._HANDLER;
	});

	after(() => {
		debug.disable();
		debug.enable(debugEnableState);
		process.env._HANDLER = origHandlerDef;
	});

	let sandbox;
	beforeEach(() => {
		sandbox = Sinon.createSandbox();
	});

	afterEach(() => {
		sandbox.restore();
	});

	function decompose(handlerDef, expectedModuleFilePath, expectedExportPath) {
		it(handlerDef, () => {
			const result = LambdaUtil.decomposeHandlerDef(handlerDef);

			Assert.strictEqual(result.moduleFilePath, expectedModuleFilePath, handlerDef);
			Assert.deepEqual(result.exportPathArray, expectedExportPath);
		});
	}

	describe("decomposeHandlerDef", () => {
		decompose("index.handler", "index", ["handler"]);
		decompose("lib/index.handler", Path.join("lib", "index"), ["handler"]);
		decompose("index.foo.handler", "index", ["foo", "handler"]);
		decompose("lib/index.foo.bar.handler", Path.join("lib", "index"), ["foo", "bar", "handler"]);
	});

	function resolveUserHandler(handlerDef) {
		it(handlerDef, () => {
			// create a proxy object which getter and a getter stub
			const proxyGetterStub = Sinon.stub();
			const proxy = new Proxy(Object.create(null), {
				get: proxyGetterStub
			});

			process.env._HANDLER = "should be preserved";

			// let the proxy object getter return the proxy object
			proxyGetterStub.returns(proxy);

			// decompose handler
			const decomposeResult = LambdaUtil.decomposeHandlerDef(handlerDef);

			// resolveUserHandlerFromDtLambdaHandler destructs exportPath in decomposeResult -> make a copy
			const exportPathCopy = decomposeResult.exportPathArray.slice();

			// stub decomposeHandlerDef and let stub return previous determined result
			const decomposeHandlerDefStub = sandbox.stub(LambdaUtil, "decomposeHandlerDef").returns(decomposeResult);

			const result = LambdaUtil.resolveUserHandlerFromDtLambdaHandler(proxy, handlerDef);

			Sinon.assert.calledOnce(decomposeHandlerDefStub);
			Sinon.assert.calledWith(decomposeHandlerDefStub, handlerDef);

			Assert.strictEqual(proxyGetterStub.callCount, exportPathCopy.length);

			// check if first proxy getter invocation was done with modulePath$topLevelExport
			Sinon.assert.calledWith(proxyGetterStub.getCall(0), proxy, `${decomposeResult.moduleFilePath}$${exportPathCopy[0]}`);

			// check nested export paths
			for (let i = 1; i < exportPathCopy.length; ++i) {
				Sinon.assert.calledWith(proxyGetterStub.getCall(i), proxy, exportPathCopy[i]);
			}

			Assert.strictEqual(result, proxy);
			Assert.strictEqual(process.env._HANDLER, "should be preserved");
		});
	}

	describe("resolveUserHandlerFromDtLambdaHandler", () => {
		resolveUserHandler("index.handler");
		resolveUserHandler("lib/index.handler");
		resolveUserHandler("index.foo.handler");
		resolveUserHandler("lib/index.foo.handler");
		resolveUserHandler("lib/index.foo.bar.handler");
	});
});
<|endoftext|>


<commit_before>/*
 * TODO
 * - add update animation
 */

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 900 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var now = new Date;
var interval = 2000;

var parseDate = d3.time.format("%d-%b-%y").parse;
var data = [
            [
             {date:now, value:500, id:"average"}
            ],
            [
              {date:now, value:400, id:"Node1"}
            ],
            [
             {date:now, value:400, id:"Node1"}
            ]
           ];

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

// add scale of the axes
//x.domain(d3.extent(data[0], function(d) { return d.date; }));
//x.domain([nowTimestamp-60, nowTimestamp+60]);
x.domain([new Date(+(now)-(10*1000)), new Date(+(now)+(120*1000))]);
y.domain([0, 1]);

var xAxis = d3.svg.axis()
    .scale(x)
    // add ticks (axis and vertical line)
    .tickSize(-height).tickPadding(6).ticks(5)
    .orient("bottom");

var tickFormatY = d3.format("s"); // add SI-postfix (like 2k instead of 2000)
var yAxis = d3.svg.axis()
    .scale(y)
    // add ticks (axis and vertical line)
    .tickSize(-width).tickFormat(tickFormatY).tickPadding(6).ticks(5)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.value); });

var zoom = d3.behavior.zoom().x(x)
            .scaleExtent([0.2, 5]) // allow zooming in/out five times each
            .on("zoom", draw);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

// needed for zooming and dragging
var rect = svg.append("rect").attr("width", width).attr("height", height);

// avoid data lines to overlap with axis
var svgBox = svg.append("svg").attr("width", width).attr("height", height)
                .attr("viewBox", "0 0 " + width + " " + height);

var lines = svgBox.selectAll("g").data(data);

//for each array, create a 'g' line container
var aLineContainer = lines
  .enter().append("g");

aLineContainer.append("path").attr("class", "line");

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")");

svg.append("g")
    .attr("class", "y axis")
  .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Price ($)");

// show scatter points and tooltips
var formatTime = d3.time.format("%e %B");
var div = d3.select("body").append("div").attr("class", "tooltip").style("opacity", 0);
aLineContainer.selectAll(".dot")
.data( function(d, i) { return d; } )  // This is the nested data call
.enter()
  .append("circle")
  .attr("class", "dot")
  .attr("r", 3.5)
  .on("mouseover", function(d) {
            div.transition()
               .duration(100)
               .style("opacity", .9);
            div.html(formatTime(d.date) + "
"  + d.value + "
"  + d.id)
               .style("left", (d3.event.pageX) + "px")
               .style("top", (d3.event.pageY - 28) + "px");
            })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

var currentDate = now;
function update() {
  console.log("updating");

  currentDate = new Date(+(currentDate)+interval);

  var newData = [
                 {date:currentDate, value:(Math.random()+1)*200, id:"average"},
                 {date:currentDate, value:(Math.random()+1)*100, id:"min"},
                 {date:currentDate, value:(Math.random()+2)*200, id:"max"},
                ];

  var lowestXDomain      = x.domain()[0];
  var highestXDomain     = x.domain()[1];
  var currentHighestDate = d3.max(data[0], function(d) { return d.date });

  // is current highest date currently being showed?
  if (lowestXDomain <= currentHighestDate && currentHighestDate <= highestXDomain) {
    var newHighestDate = d3.max(newData, function(d) { return d.date });
    // if new highest date is out of the domain, update the domain
    if (highestXDomain < newHighestDate) {
      x.domain([new Date(+(lowestXDomain)+(interval)), newHighestDate]);
      console.log("update x domain");
//      svg.select("g.y.axis").transition().duration(300).ease("linear").call(yAxis);
    }
  }

  newData.forEach(function(d, i) {
    data[i].push(d);
  });
//  console.dir(data);

  aLineContainer
  .attr("d", line)
  .attr("transform", null)
//  .transition()
//  .duration(500)
//  .ease("linear")
//  .attr("transform", "translate(" + x(0) + ")")
//  .each("end", update);

//  d3.selectAll("path.line").data({date:parseDate("4-May-12"), value:Math.random()*500, id:"Node1"}).enter();

  // update domains
//  x.domain([parseDate("30-Apr-12"), parseDate("6-Jun-12")]);
  y.domain([0, d3.max(data.map(function(d) { return d3.max(d, function(dm) { return dm.value; }); } )) * 1.1]);
//  xAxis.scale(x);
//  yAxis.scale(y);

//  lines = svgBox.selectAll("g").data([newData]);
//  lines.enter().append("g");

  draw();
}

window.setInterval(function() {
  update();
}, interval);

draw();

function draw() {
  svg.select("g.x.axis").call(xAxis);
  svg.select("g.y.axis").transition().duration(300).ease("linear").call(yAxis);

  //  svg.select("path.area").attr("d", area);
  svg.selectAll("path.line").attr("d", line);
  aLineContainer.selectAll("circle.dot").attr("cx", line.x()).attr("cy", line.y());
//  d3.select("#footer span").text("U.S. Commercial Flights, " + x.domain().map(format).join("-"));
}<commit_msg>cleanups in the standalone chart<commit_after>/*
 * TODO
 * - add update animation
 */

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 900 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var now = new Date;
var interval = 2000;

var data = [
            [
             {date:now, value:500, id:"average"}
            ],
            [
              {date:now, value:400, id:"Node1"}
            ],
            [
             {date:now, value:400, id:"Node1"}
            ]
           ];

// add ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

// add default scale of the axes
x.domain([new Date(+(now)-(10*1000)), new Date(+(now)+(120*1000))]);
y.domain([0, 1]);

var xAxis = d3.svg.axis().scale(x)
    // add ticks (axis and vertical line)
    .tickSize(-height).tickPadding(6).ticks(5).orient("bottom");

var tickFormatY = d3.format("s"); // add SI-postfix (like 2k instead of 2000)
var yAxis = d3.svg.axis().scale(y)
    // add ticks (axis and vertical line)
    .tickSize(-width).tickFormat(tickFormatY).tickPadding(6).ticks(5).orient("left");

var line = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.value); });

var zoom = d3.behavior.zoom().x(x)
            .scaleExtent([0.005, 5]) // allow zooming in/out
            .on("zoom", draw);

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

// needed for zooming and dragging
var rect = svg.append("rect").attr("width", width).attr("height", height);

// avoid data lines to overlap with axis
var svgBox = svg.append("svg").attr("width", width).attr("height", height)
                .attr("viewBox", "0 0 " + width + " " + height);

var lines = svgBox.selectAll("g").data(data);

//for each array, create a 'g' line container
var aLineContainer = lines.enter().append("g");
aLineContainer.append("path").attr("class", "line");

// add x axis to chart
svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")");

// add y axis to chart
svg.append("g")
    .attr("class", "y axis")
  .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Price ($)");

// show scatter points and tooltips
var formatTime = d3.time.format("%e %B");
var div = d3.select("body").append("div").attr("class", "tooltip").style("opacity", 0);
aLineContainer.selectAll(".dot")
.data( function(d, i) { return d; } )  // This is the nested data call
.enter()
  .append("circle")
  .attr("class", "dot")
  .attr("r", 3.5)
  .on("mouseover", function(d) {
            div.transition()
               .duration(100)
               .style("opacity", .9);
            div.html(formatTime(d.date) + "
"  + d.value + "
"  + d.id)
               .style("left", (d3.event.pageX) + "px")
               .style("top", (d3.event.pageY - 28) + "px");
            })
        .on("mouseout", function(d) {
            div.transition()
                .duration(500)
                .style("opacity", 0);
        });

var currentDate = now;
function update() {
  console.log("updating");

  currentDate = new Date(+(currentDate)+interval);

  var newData = [
                 {date:currentDate, value:(Math.random()+1)*200, id:"average"},
                 {date:currentDate, value:(Math.random()+1)*100, id:"min"},
                 {date:currentDate, value:(Math.random()+2)*200, id:"max"},
                ];

  var lowestXDomain      = x.domain()[0];
  var highestXDomain     = x.domain()[1];
  var currentHighestDate = d3.max(data[0], function(d) { return d.date });

  // is current highest date currently being showed?
  if (lowestXDomain <= currentHighestDate && currentHighestDate <= highestXDomain) {
    var newHighestDate = d3.max(newData, function(d) { return d.date });
    // if new highest date is out of the domain, update the domain
    if (highestXDomain < newHighestDate) {
      x.domain([new Date(+(lowestXDomain)+(interval)), newHighestDate]);
//      svg.select("g.y.axis").transition().duration(300).ease("linear").call(yAxis);
    }
  }

  newData.forEach(function(d, i) {
    data[i].push(d);
  });

  aLineContainer
  .attr("d", line)
  .attr("transform", null)
//  .transition()
//  .duration(500)
//  .ease("linear")
//  .attr("transform", "translate(" + x(0) + ")")
//  .each("end", update);

  // update domains
  y.domain([0, d3.max(data.map(function(d) { return d3.max(d, function(dm) { return dm.value; }); } )) * 1.1]);
//  xAxis.scale(x);
//  yAxis.scale(y);

  draw();
}

window.setInterval(function() {
  update();
}, interval);

draw();

function draw() {
  svg.select("g.x.axis").call(xAxis);
  svg.select("g.y.axis").transition().duration(300).ease("linear").call(yAxis);

  svg.selectAll("path.line").attr("d", line);
  aLineContainer.selectAll("circle.dot").attr("cx", line.x()).attr("cy", line.y());
//  d3.select("#footer span").text("U.S. Commercial Flights, " + x.domain().map(format).join("-"));
}<|endoftext|>


<commit_before>import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import actions from './actions';
import ContainersList from '../Presentational/ContainersList/ContainersList';

const mapStateToProps = state => ({
  pending: !!state.pending[actions.FETCH_CONTAINERS],
  containers: state.containers,
  infos: state.infos,
  error: state.error,
});

const mapDispatchToProps = dispatch => ({
  onRefresh: () => dispatch(actions.fetchContainers()),
  onAdd: () => dispatch(push('/containers/New')),
  onSelect: id => dispatch(push(`/containers/${id}`)),
  onLogout: () => dispatch(actions.logout()),
});

const ContainersListContainer = connect(mapStateToProps, mapDispatchToProps)(ContainersList);

/**
 * Container for handling list view.
 */
export default ContainersListContainer;
<commit_msg>Update ContainersListContainer.js<commit_after>import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import actions from './actions';
import ContainersList from '../Presentational/ContainersList/ContainersList';

const mapStateToProps = state => ({
  pending: !!state.pending[actions.FETCH_CONTAINERS],
  containers: state.containers,
  infos: state.infos,
  error: state.error,
});

const mapDispatchToProps = dispatch => ({
  onRefresh: () => dispatch(actions.info()),
  onAdd: () => dispatch(push('/containers/New')),
  onSelect: id => dispatch(push(`/containers/${id}`)),
  onLogout: () => dispatch(actions.logout()),
});

const ContainersListContainer = connect(mapStateToProps, mapDispatchToProps)(ContainersList);

/**
 * Container for handling list view.
 */
export default ContainersListContainer;
<|endoftext|>