<commit_before>/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/**
 * @requires OpenLayers/Handler.js
 */

/**
 * Class: OpenLayers.Handler.Drag
 * The drag handler is used to deal with sequences of browser events related
 *     to dragging.  The handler is used by controls that want to know when
 *     a drag sequence begins, when a drag is happening, and when it has
 *     finished.
 *
 * Controls that use the drag handler typically construct it with callbacks
 *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
 *     when the drag begins, with each move, and when the drag is done.  In
 *     addition, controls can have callbacks keyed to 'up' and 'out' if they
 *     care to differentiate between the types of events that correspond with
 *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
 *     the 'down' and 'up' callbacks will be called, but not the 'done'
 *     callback.
 *
 * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
 *
 * Inherits from:
 *  - <OpenLayers.Handler>
 */
OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {

    /**
     * Property: started
     * {Boolean} When a mousedown event is received, we want to record it, but
     *     not set 'dragging' until the mouse moves after starting.
     */
    started: false,

    /**
     * Property: stopDown
     * {Boolean} Stop propagation of mousedown events from getting to listeners
     *     on the same element.  Default is true.
     */
    stopDown: true,

    /**
     * Property: dragging
     * {Boolean}
     */
    dragging: false,

    /**
     * Property: last
     * {<OpenLayers.Pixel>} The last pixel location of the drag.
     */
    last: null,

    /**
     * Property: start
     * {<OpenLayers.Pixel>} The first pixel location of the drag.
     */
    start: null,

    /**
     * Property: oldOnselectstart
     * {Function}
     */
    oldOnselectstart: null,

    /**
     * Constructor: OpenLayers.Handler.Drag
     * Returns OpenLayers.Handler.Drag
     *
     * Parameters:
     * control - {<OpenLayers.Control>} The control that is making use of
     *     this handler.  If a handler is being used without a control, the
     *     handlers setMap method must be overridden to deal properly with
     *     the map.
     * callbacks - {Object} An object containing a single function to be
     *     called when the drag operation is finished. The callback should
     *     expect to recieve a single argument, the pixel location of the event.
     *     Callbacks for 'move' and 'done' are supported. You can also speficy
     *     callbacks for 'down', 'up', and 'out' to respond to those events.
     * options - {Object}
     */
    initialize: function(control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },

    /**
     * The four methods below (down, move, up, and out) are used by subclasses
     *     to do their own processing related to these mouse events.
     */

    /**
     * Method: down
     * This method is called during the handling of the mouse down event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse down event
     */
    down: function(evt) {
    },

    /**
     * Method: move
     * This method is called during the handling of the mouse move event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse move event
     *
     */
    move: function(evt) {
    },

    /**
     * Method: up
     * This method is called during the handling of the mouse up event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse up event
     */
    up: function(evt) {
    },

    /**
     * Method: out
     * This method is called during the handling of the mouse out event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse out event
     */
    out: function(evt) {
    },

    /**
     * The methods below are part of the magic of event handling.  Because
     *     they are named like browser events, they are registered as listeners
     *     for the events they represent.
     */

    /**
     * Method: mousedown
     * Handle mousedown events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mousedown: function (evt) {
        var propagate = true;
        this.dragging = false;
        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
            this.started = true;
            this.start = evt.xy;
            this.last = evt.xy;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "move";
            this.down(evt);
            this.callback("down", [evt.xy]);
            OpenLayers.Event.stop(evt);

            if(!this.oldOnselectstart) {
                this.oldOnselectstart = document.onselectstart;
                document.onselectstart = function() {return false;};
            }

            propagate = !this.stopDown;
        } else {
            this.started = false;
            this.start = null;
            this.last = null;
        }
        return propagate;
    },

    /**
     * Method: mousemove
     * Handle mousemove events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mousemove: function (evt) {
        if (this.started) {
            if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
                this.dragging = true;
                this.move(evt);
                this.callback("move", [evt.xy]);
                if(!this.oldOnselectstart) {
                    this.oldOnselectstart = document.onselectstart;
                    document.onselectstart = function() {return false;};
                }
                this.last = evt.xy;
            }
        }
        return true;
    },

    /**
     * Method: mouseup
     * Handle mouseup events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mouseup: function (evt) {
        if (this.started) {
            var dragged = (this.start != this.last);
            this.started = false;
            this.dragging = false;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "";
            this.up(evt);
            this.callback("up", [evt.xy]);
            if(dragged) {
                this.callback("done", [evt.xy]);
            }
            document.onselectstart = this.oldOnselectstart;
        }
        return true;
    },

    /**
     * Method: mouseout
     * Handle mouseout events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mouseout: function (evt) {
        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
            var dragged = (this.start != this.last);
            this.started = false;
            this.dragging = false;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "";
            this.out(evt);
            this.callback("out", []);
            if(dragged) {
                this.callback("done", [evt.xy]);
            }
            if(document.onselectstart) {
                document.onselectstart = this.oldOnselectstart;
            }
        }
        return true;
    },

    /**
     * Method: click
     * The drag handler captures the click event.  If something else registers
     *     for clicks on the same element, its listener will not be called
     *     after a drag.
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    click: function (evt) {
        // let the click event propagate only if the mouse moved
        return (this.start == this.last);
    },

    /**
     * Method: activate
     * Activate the handler.
     *
     * Returns:
     * {Boolean} The handler was successfully activated.
     */
    activate: function() {
        var activated = false;
        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.dragging = false;
            activated = true;
        }
        return activated;
    },

    /**
     * Method: deactivate
     * Deactivate the handler.
     *
     * Returns:
     * {Boolean} The handler was successfully deactivated.
     */
    deactivate: function() {
        var deactivated = false;
        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.started = false;
            this.dragging = false;
            this.start = null;
            this.last = null;
            deactivated = true;
        }
        return deactivated;
    },

    CLASS_NAME: "OpenLayers.Handler.Drag"
});
<commit_msg>Because onselectstart is null by default, our logic to set it and  replace it was somewhat lacking. This changes the behavior such that  using hte drag handler does not totally disable text selection in IE.  Patch from Bart, r=me (Closes #906)<commit_after>/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/**
 * @requires OpenLayers/Handler.js
 */

/**
 * Class: OpenLayers.Handler.Drag
 * The drag handler is used to deal with sequences of browser events related
 *     to dragging.  The handler is used by controls that want to know when
 *     a drag sequence begins, when a drag is happening, and when it has
 *     finished.
 *
 * Controls that use the drag handler typically construct it with callbacks
 *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
 *     when the drag begins, with each move, and when the drag is done.  In
 *     addition, controls can have callbacks keyed to 'up' and 'out' if they
 *     care to differentiate between the types of events that correspond with
 *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
 *     the 'down' and 'up' callbacks will be called, but not the 'done'
 *     callback.
 *
 * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
 *
 * Inherits from:
 *  - <OpenLayers.Handler>
 */
OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {

    /**
     * Property: started
     * {Boolean} When a mousedown event is received, we want to record it, but
     *     not set 'dragging' until the mouse moves after starting.
     */
    started: false,

    /**
     * Property: stopDown
     * {Boolean} Stop propagation of mousedown events from getting to listeners
     *     on the same element.  Default is true.
     */
    stopDown: true,

    /**
     * Property: dragging
     * {Boolean}
     */
    dragging: false,

    /**
     * Property: last
     * {<OpenLayers.Pixel>} The last pixel location of the drag.
     */
    last: null,

    /**
     * Property: start
     * {<OpenLayers.Pixel>} The first pixel location of the drag.
     */
    start: null,

    /**
     * Property: oldOnselectstart
     * {Function}
     */
    oldOnselectstart: null,

    /**
     * Constructor: OpenLayers.Handler.Drag
     * Returns OpenLayers.Handler.Drag
     *
     * Parameters:
     * control - {<OpenLayers.Control>} The control that is making use of
     *     this handler.  If a handler is being used without a control, the
     *     handlers setMap method must be overridden to deal properly with
     *     the map.
     * callbacks - {Object} An object containing a single function to be
     *     called when the drag operation is finished. The callback should
     *     expect to recieve a single argument, the pixel location of the event.
     *     Callbacks for 'move' and 'done' are supported. You can also speficy
     *     callbacks for 'down', 'up', and 'out' to respond to those events.
     * options - {Object}
     */
    initialize: function(control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },

    /**
     * The four methods below (down, move, up, and out) are used by subclasses
     *     to do their own processing related to these mouse events.
     */

    /**
     * Method: down
     * This method is called during the handling of the mouse down event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse down event
     */
    down: function(evt) {
    },

    /**
     * Method: move
     * This method is called during the handling of the mouse move event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse move event
     *
     */
    move: function(evt) {
    },

    /**
     * Method: up
     * This method is called during the handling of the mouse up event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse up event
     */
    up: function(evt) {
    },

    /**
     * Method: out
     * This method is called during the handling of the mouse out event.
     *     Subclasses can do their own processing here.
     *
     * Parameters:
     * evt - {Event} The mouse out event
     */
    out: function(evt) {
    },

    /**
     * The methods below are part of the magic of event handling.  Because
     *     they are named like browser events, they are registered as listeners
     *     for the events they represent.
     */

    /**
     * Method: mousedown
     * Handle mousedown events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mousedown: function (evt) {
        var propagate = true;
        this.dragging = false;
        if (this.checkModifiers(evt) && OpenLayers.Event.isLeftClick(evt)) {
            this.started = true;
            this.start = evt.xy;
            this.last = evt.xy;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "move";
            this.down(evt);
            this.callback("down", [evt.xy]);
            OpenLayers.Event.stop(evt);

            if(!this.oldOnselectstart) {
                this.oldOnselectstart = (document.onselectstart) ? document.onselectstart : function() { return true; };
                document.onselectstart = function() {return false;};
            }

            propagate = !this.stopDown;
        } else {
            this.started = false;
            this.start = null;
            this.last = null;
        }
        return propagate;
    },

    /**
     * Method: mousemove
     * Handle mousemove events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mousemove: function (evt) {
        if (this.started) {
            if(evt.xy.x != this.last.x || evt.xy.y != this.last.y) {
                this.dragging = true;
                this.move(evt);
                this.callback("move", [evt.xy]);
                if(!this.oldOnselectstart) {
                    this.oldOnselectstart = document.onselectstart;
                    document.onselectstart = function() {return false;};
                }
                this.last = evt.xy;
            }
        }
        return true;
    },

    /**
     * Method: mouseup
     * Handle mouseup events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mouseup: function (evt) {
        if (this.started) {
            var dragged = (this.start != this.last);
            this.started = false;
            this.dragging = false;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "";
            this.up(evt);
            this.callback("up", [evt.xy]);
            if(dragged) {
                this.callback("done", [evt.xy]);
            }
            document.onselectstart = this.oldOnselectstart;
        }
        return true;
    },

    /**
     * Method: mouseout
     * Handle mouseout events
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    mouseout: function (evt) {
        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
            var dragged = (this.start != this.last);
            this.started = false;
            this.dragging = false;
            // TBD replace with CSS classes
            this.map.div.style.cursor = "";
            this.out(evt);
            this.callback("out", []);
            if(dragged) {
                this.callback("done", [evt.xy]);
            }
            if(document.onselectstart) {
                document.onselectstart = this.oldOnselectstart;
            }
        }
        return true;
    },

    /**
     * Method: click
     * The drag handler captures the click event.  If something else registers
     *     for clicks on the same element, its listener will not be called
     *     after a drag.
     *
     * Parameters:
     * evt - {Event}
     *
     * Returns:
     * {Boolean} Let the event propagate.
     */
    click: function (evt) {
        // let the click event propagate only if the mouse moved
        return (this.start == this.last);
    },

    /**
     * Method: activate
     * Activate the handler.
     *
     * Returns:
     * {Boolean} The handler was successfully activated.
     */
    activate: function() {
        var activated = false;
        if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.dragging = false;
            activated = true;
        }
        return activated;
    },

    /**
     * Method: deactivate
     * Deactivate the handler.
     *
     * Returns:
     * {Boolean} The handler was successfully deactivated.
     */
    deactivate: function() {
        var deactivated = false;
        if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.started = false;
            this.dragging = false;
            this.start = null;
            this.last = null;
            deactivated = true;
        }
        return deactivated;
    },

    CLASS_NAME: "OpenLayers.Handler.Drag"
});
<|endoftext|>


<commit_before>import React from 'react';
import { intlShape } from 'react-intl';
import PropTypes from 'prop-types';

import injectIntl from '../InjectIntl';
import Modal from '../Modal';
import ModalFooter from '../ModalFooter';

const propTypes = {
  buttonStyle: PropTypes.string,
  cancelButtonStyle: PropTypes.string,
  cancelLabel: PropTypes.string,
  confirmLabel: PropTypes.string,
  heading: PropTypes.string.isRequired,
  intl: intlShape.isRequired,
  message: PropTypes.oneOfType([
    PropTypes.node,
    PropTypes.arrayOf(PropTypes.node),
  ]),
  onCancel: PropTypes.func.isRequired,
  onConfirm: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
};

const defaultProps = {
  buttonStyle: 'primary',
  cancelButtonStyle: 'default',
};

const ConfirmationModal = (props) => {
  const testId = props.heading.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
  const cancelLabel = props.cancelLabel || props.intl.formatMessage({ id: 'stripes-components.cancel' });
  const confirmLabel = props.confirmLabel || props.intl.formatMessage({ id: 'stripes-components.submit' });

  const footer = (
    <ModalFooter
      secondaryButton={{
        label: cancelLabel,
        onClick: props.onCancel,
        id: `clickable-${testId}-confirmation-cancel`,
        buttonStyle: props.cancelButtonStyle,
      }}
      primaryButton={{
        label: confirmLabel,
        onClick: props.onConfirm,
        id: `clickable-${testId}-confirmation-confirm`,
        buttonStyle: props.buttonStyle,
      }}
    />
  );

  return (
    <Modal
      open={props.open}
      id={`${testId}-confirmation-modal`}
      label={props.heading}
      scope="module"
      size="small"
      footer={footer}
    >
      {props.message}
    </Modal>
  );
};

ConfirmationModal.propTypes = propTypes;
ConfirmationModal.defaultProps = defaultProps;

export default injectIntl(ConfirmationModal);
<commit_msg>Changed testId to be based on id passed as a prop or default to an auto generated ID.<commit_after>import React from 'react';
import { intlShape } from 'react-intl';
import PropTypes from 'prop-types';
import uniqueId from 'lodash/uniqueId';

import injectIntl from '../InjectIntl';
import Modal from '../Modal';
import ModalFooter from '../ModalFooter';

const propTypes = {
  buttonStyle: PropTypes.string,
  cancelButtonStyle: PropTypes.string,
  cancelLabel: PropTypes.string,
  confirmLabel: PropTypes.string,
  heading: PropTypes.string.isRequired,
  id: PropTypes.string,
  intl: intlShape.isRequired,
  message: PropTypes.oneOfType([
    PropTypes.node,
    PropTypes.arrayOf(PropTypes.node),
  ]),
  onCancel: PropTypes.func.isRequired,
  onConfirm: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
};

const defaultProps = {
  buttonStyle: 'primary',
  cancelButtonStyle: 'default',
};

const ConfirmationModal = (props) => {
  const testId = props.id || uniqueId('confirmation-');
  const cancelLabel = props.cancelLabel || props.intl.formatMessage({ id: 'stripes-components.cancel' });
  const confirmLabel = props.confirmLabel || props.intl.formatMessage({ id: 'stripes-components.submit' });

  const footer = (
    <ModalFooter
      secondaryButton={{
        label: cancelLabel,
        onClick: props.onCancel,
        id: `clickable-${testId}-confirmation-cancel`,
        buttonStyle: props.cancelButtonStyle,
      }}
      primaryButton={{
        label: confirmLabel,
        onClick: props.onConfirm,
        id: `clickable-${testId}-confirmation-confirm`,
        buttonStyle: props.buttonStyle,
      }}
    />
  );

  return (
    <Modal
      open={props.open}
      id={`${testId}-confirmation-modal`}
      label={props.heading}
      scope="module"
      size="small"
      footer={footer}
    >
      {props.message}
    </Modal>
  );
};

ConfirmationModal.propTypes = propTypes;
ConfirmationModal.defaultProps = defaultProps;

export default injectIntl(ConfirmationModal);
<|endoftext|>


<commit_before>jest.dontMock('../animar');
jest.dontMock('../constants');

var Animar, animar, testElement;

beforeEach( function() {
  Animar = require('../animar');
  animar = new Animar();
  testElement = document.createElement('div');
});

describe('_stepFrame()', function() {

  it('should add 1 to every current iteration property of every element\'s attribute\'s animation', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 0, totalIterations: 10, loop: false, wait: 0 }
    ]});
    animar.elementMap = new Map();
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').
      animations[0].currentIteration).toEqual(1);
  });

  it('should remove an animation if it has ended', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 10, totalIterations: 10, loop: false, wait: 0 }
    ]});
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations.length).toEqual(0);
  });

  it('should set the currentIteration to zero minus the delay value if the animation has ended and the loop ' +
      'property is true', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 10, currentIteration: 10, totalIterations: 10, loop: true, wait: 0 }
    ]});
    animar.elementMap = new Map();
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations[0].currentIteration).
      toEqual(-10);
  });

  it('should not remove the animation if there is waiting remaining', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 10, totalIterations: 10, loop: false, wait: 1 }
    ]});
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations.length).toEqual(1);
  });
});

describe('update()', function() {

  beforeEach(function() {
    animar._renderDOM = jest.genMockFn();
    animar._stepFrame = jest.genMockFn();
    animar._requestTick = jest.genMockFn();
  });

  it('should render the animations and step forward a frame', function() {
    animar._renderDOM.mockReturnValue(false);
    animar._update();
    expect(animar._renderDOM).toBeCalled();
    expect(animar._stepFrame).toBeCalled();
    expect(animar.ticking).toEqual(false);
    expect(animar._requestTick).not.toBeCalled();
  });

  it('should continue rendering if there are animations that haven\'t finished yet', function() {
    animar._renderDOM.mockReturnValue(true);
    animar._update();
    expect(animar._requestTick).toBeCalled();
  });

});

describe('_requestTick()', function() {
  var ram;

  beforeEach(function() {
    ram = window.requestAnimationFrame;
    window.requestAnimationFrame = jest.genMockFn();
  });

  afterEach(function() {
    window.requestAnimationFrame = ram;
  });

  it('should do nothing if animar is already ticking', function() {
    animar.ticking = true;
    animar._requestTick();
    expect(window.requestAnimationFrame).not.toBeCalled();
  });

  it('should make a requestAnimationFrame call to the _update function and set ticking to true', function() {
    animar.ticking = false;
    animar._requestTick();
    expect(window.requestAnimationFrame).toBeCalled();
    expect(animar.ticking).toEqual(true);
  });

});

describe('_calculateAnimationValue()', function() {

  it('should use the given easing function to add to the animation value', function() {
    expect(animar._calculateAnimationValue([{currentIteration: 0, easingFunction: function() {return 5;} }])).
      toEqual(5);
  });

  it('should add multiple animations together', function() {
    var mockEasingFunction = function() {return 5;};
    expect(animar._calculateAnimationValue([{currentIteration: 0, easingFunction: mockEasingFunction},
      {currentIteration: 0, easingFunction: mockEasingFunction}])).toEqual(10);
  });

  it('should consider an animation value zero if it\'s current iteration is less than zero', function() {
    expect(animar._calculateAnimationValue([{currentIteration: -5, easingFunction: function() {return 5;} }])).
      toEqual(5);
  });

  it('should return the value of totalIterations if the currentIteration is greater than it', function() {
    var easeSpy = jest.genMockFn();
    animar._calculateAnimationValue([{currentIteration: 25, totalIterations: 20, changeInValue: 0, startValue: 0, easingFunction: easeSpy}]);
    expect(easeSpy).toBeCalledWith(20,0,0,20);
  });

});

describe('_add()', function() {

  var andMockReturn = 1,
    thenMockReturn = 2,
    loopMockReturn = 3;

  var defaultChainOptions;

  beforeEach(function() {
    animar._addAnimation = jest.genMockFn().mockReturnValue('animation result');
    animar._andChainFunction = jest.genMockFn().mockReturnValue(andMockReturn);
    animar._thenChainFunction = jest.genMockFn().mockReturnValue(thenMockReturn);
    animar._loopChainFunction = jest.genMockFn().mockReturnValue(loopMockReturn);

    defaultChainOptions = { delay: 0, currentDuration: 0, totalDuration: 0, animationList: [] };
  });

  it('should correctly handle being given multiple attributes', function() {
    animar._add(testElement, {
      translateX: 10,
      translateY: 50
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls.length).toEqual(2);
  });

  it('should handle an attributeValue being a number', function() {
    animar._add(testElement, {
      translateX: 15
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(15);
    expect(animar._addAnimation.mock.calls[0][3].start).toBeUndefined();
  });

  it('should handle an attributeValue being an array of length 2', function() {
    animar._add(testElement, {
      translateX: [12, 24]
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3].start).toEqual(12);
  });

  it('should properly pass through options', function() {
    var testEasingFunction = function() { return 42; };

    animar._add(testElement, {
      translateX: [12, 24]
    }, {
      duration: 60,
      easing: testEasingFunction,
      delay: 25,
      loop: true
    }, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3]).toEqual({
      start: 12,
      duration: 60,
      easing: testEasingFunction,
      delay: 25,
      loop: true
    });
  });

  it('should correctly push the result of _addAnimation to the chainOptions object', function() {
    animar._add(testElement, {
      translateX: 23
    }, {}, defaultChainOptions);
    expect(animar._andChainFunction.mock.calls[0][0].animationList[0]).toEqual('animation result');
  });

  it('should correctly update the currentDuration of the chainOptions object', function() {
    animar._add(testElement, {
      translateX: 23
    }, {
      duration: 60,
      delay: 20
    }, defaultChainOptions);
    expect(animar._andChainFunction.mock.calls[0][0].currentDuration).toEqual(80);
  });

  it('should return an object with all the relevant chain functions', function() {
    var result = animar._add(testElement, {
      translateX: 23
    }, {}, defaultChainOptions);
    expect(result).toEqual({
      and: andMockReturn,
      then: thenMockReturn,
      loop: loopMockReturn
    });
  });

  it('should work fine when given an undefined options parameter', function() {
    animar._add(testElement, {
      translateX: [12, 24]
    }, undefined, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3].start).toEqual(12);
  });

});

describe('_andChainFunction()', function() {

  var defaultChainOptions;

  beforeEach(function() {
    animar._add = jest.genMockFn();
    defaultChainOptions = { delay: 0, currentDuration: 0, totalDuration: 0, animationList: [] };
  });

  it('should return a function that when called, correctly calls the _add function with the correct parameters',
    function() {
      var result = animar._andChainFunction(defaultChainOptions);

      var testAttributesObject = { translateX: 10 },
        testOptionsObject = { duration: 20 };

      result(testElement, testAttributesObject, testOptionsObject);

      expect(animar._add.mock.calls[0][0]).toEqual(testElement);
      expect(animar._add.mock.calls[0][1]).toEqual(testAttributesObject);
      expect(animar._add.mock.calls[0][2]).toEqual(testOptionsObject);
      expect(animar._add.mock.calls[0][3]).toEqual(defaultChainOptions);
  });

});<commit_msg>added a unit test for _thenChainFunction()<commit_after>jest.dontMock('../animar');
jest.dontMock('../constants');

var Animar, animar, testElement;

beforeEach( function() {
  Animar = require('../animar');
  animar = new Animar();
  testElement = document.createElement('div');
});

describe('_stepFrame()', function() {

  it('should add 1 to every current iteration property of every element\'s attribute\'s animation', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 0, totalIterations: 10, loop: false, wait: 0 }
    ]});
    animar.elementMap = new Map();
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').
      animations[0].currentIteration).toEqual(1);
  });

  it('should remove an animation if it has ended', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 10, totalIterations: 10, loop: false, wait: 0 }
    ]});
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations.length).toEqual(0);
  });

  it('should set the currentIteration to zero minus the delay value if the animation has ended and the loop ' +
      'property is true', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 10, currentIteration: 10, totalIterations: 10, loop: true, wait: 0 }
    ]});
    animar.elementMap = new Map();
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations[0].currentIteration).
      toEqual(-10);
  });

  it('should not remove the animation if there is waiting remaining', function() {
    var attributeMap = new Map();
    attributeMap.set('testAttribute1', { animations: [
      { delay: 0, currentIteration: 10, totalIterations: 10, loop: false, wait: 1 }
    ]});
    animar.elementMap.set(testElement, attributeMap);
    animar._stepFrame();
    expect(animar.elementMap.get(testElement).get('testAttribute1').animations.length).toEqual(1);
  });
});

describe('update()', function() {

  beforeEach(function() {
    animar._renderDOM = jest.genMockFn();
    animar._stepFrame = jest.genMockFn();
    animar._requestTick = jest.genMockFn();
  });

  it('should render the animations and step forward a frame', function() {
    animar._renderDOM.mockReturnValue(false);
    animar._update();
    expect(animar._renderDOM).toBeCalled();
    expect(animar._stepFrame).toBeCalled();
    expect(animar.ticking).toEqual(false);
    expect(animar._requestTick).not.toBeCalled();
  });

  it('should continue rendering if there are animations that haven\'t finished yet', function() {
    animar._renderDOM.mockReturnValue(true);
    animar._update();
    expect(animar._requestTick).toBeCalled();
  });

});

describe('_requestTick()', function() {
  var ram;

  beforeEach(function() {
    ram = window.requestAnimationFrame;
    window.requestAnimationFrame = jest.genMockFn();
  });

  afterEach(function() {
    window.requestAnimationFrame = ram;
  });

  it('should do nothing if animar is already ticking', function() {
    animar.ticking = true;
    animar._requestTick();
    expect(window.requestAnimationFrame).not.toBeCalled();
  });

  it('should make a requestAnimationFrame call to the _update function and set ticking to true', function() {
    animar.ticking = false;
    animar._requestTick();
    expect(window.requestAnimationFrame).toBeCalled();
    expect(animar.ticking).toEqual(true);
  });

});

describe('_calculateAnimationValue()', function() {

  it('should use the given easing function to add to the animation value', function() {
    expect(animar._calculateAnimationValue([{currentIteration: 0, easingFunction: function() {return 5;} }])).
      toEqual(5);
  });

  it('should add multiple animations together', function() {
    var mockEasingFunction = function() {return 5;};
    expect(animar._calculateAnimationValue([{currentIteration: 0, easingFunction: mockEasingFunction},
      {currentIteration: 0, easingFunction: mockEasingFunction}])).toEqual(10);
  });

  it('should consider an animation value zero if it\'s current iteration is less than zero', function() {
    expect(animar._calculateAnimationValue([{currentIteration: -5, easingFunction: function() {return 5;} }])).
      toEqual(5);
  });

  it('should return the value of totalIterations if the currentIteration is greater than it', function() {
    var easeSpy = jest.genMockFn();
    animar._calculateAnimationValue([{currentIteration: 25, totalIterations: 20, changeInValue: 0, startValue: 0, easingFunction: easeSpy}]);
    expect(easeSpy).toBeCalledWith(20,0,0,20);
  });

});

describe('_add()', function() {

  var andMockReturn = 1,
    thenMockReturn = 2,
    loopMockReturn = 3;

  var defaultChainOptions;

  beforeEach(function() {
    animar._addAnimation = jest.genMockFn().mockReturnValue('animation result');
    animar._andChainFunction = jest.genMockFn().mockReturnValue(andMockReturn);
    animar._thenChainFunction = jest.genMockFn().mockReturnValue(thenMockReturn);
    animar._loopChainFunction = jest.genMockFn().mockReturnValue(loopMockReturn);

    defaultChainOptions = { delay: 0, currentDuration: 0, totalDuration: 0, animationList: [] };
  });

  it('should correctly handle being given multiple attributes', function() {
    animar._add(testElement, {
      translateX: 10,
      translateY: 50
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls.length).toEqual(2);
  });

  it('should handle an attributeValue being a number', function() {
    animar._add(testElement, {
      translateX: 15
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(15);
    expect(animar._addAnimation.mock.calls[0][3].start).toBeUndefined();
  });

  it('should handle an attributeValue being an array of length 2', function() {
    animar._add(testElement, {
      translateX: [12, 24]
    }, {}, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3].start).toEqual(12);
  });

  it('should properly pass through options', function() {
    var testEasingFunction = function() { return 42; };

    animar._add(testElement, {
      translateX: [12, 24]
    }, {
      duration: 60,
      easing: testEasingFunction,
      delay: 25,
      loop: true
    }, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3]).toEqual({
      start: 12,
      duration: 60,
      easing: testEasingFunction,
      delay: 25,
      loop: true
    });
  });

  it('should correctly push the result of _addAnimation to the chainOptions object', function() {
    animar._add(testElement, {
      translateX: 23
    }, {}, defaultChainOptions);
    expect(animar._andChainFunction.mock.calls[0][0].animationList[0]).toEqual('animation result');
  });

  it('should correctly update the currentDuration of the chainOptions object', function() {
    animar._add(testElement, {
      translateX: 23
    }, {
      duration: 60,
      delay: 20
    }, defaultChainOptions);
    expect(animar._andChainFunction.mock.calls[0][0].currentDuration).toEqual(80);
  });

  it('should return an object with all the relevant chain functions', function() {
    var result = animar._add(testElement, {
      translateX: 23
    }, {}, defaultChainOptions);
    expect(result).toEqual({
      and: andMockReturn,
      then: thenMockReturn,
      loop: loopMockReturn
    });
  });

  it('should work fine when given an undefined options parameter', function() {
    animar._add(testElement, {
      translateX: [12, 24]
    }, undefined, defaultChainOptions);

    expect(animar._addAnimation.mock.calls[0][0]).toEqual(testElement);
    expect(animar._addAnimation.mock.calls[0][1]).toEqual('translateX');
    expect(animar._addAnimation.mock.calls[0][2]).toEqual(24);
    expect(animar._addAnimation.mock.calls[0][3].start).toEqual(12);
  });

});

describe('_andChainFunction()', function() {

  var defaultChainOptions;

  beforeEach(function() {
    animar._add = jest.genMockFn();
    defaultChainOptions = { delay: 0, currentDuration: 0, totalDuration: 0, animationList: [] };
  });

  it('should return a function that when called, correctly calls the _add function with the correct parameters',
    function() {
      var result = animar._andChainFunction(defaultChainOptions);

      var testAttributesObject = { translateX: 10 },
        testOptionsObject = { duration: 20 };

      result(testElement, testAttributesObject, testOptionsObject);

      expect(animar._add.mock.calls[0][0]).toEqual(testElement);
      expect(animar._add.mock.calls[0][1]).toEqual(testAttributesObject);
      expect(animar._add.mock.calls[0][2]).toEqual(testOptionsObject);
      expect(animar._add.mock.calls[0][3]).toEqual(defaultChainOptions);
  });

});

describe('_thenChainFunction()', function() {
  var defaultChainOptions, resultingChainOptions;

  beforeEach(function() {
    animar._add = jest.genMockFn();
    defaultChainOptions = { delay: 0, currentDuration: 25, totalDuration: 50, animationList: [] };
    resultingChainOptions = { delay: 75, currentDuration: 0, totalDuration: 75, animationList: [] };
  });

  it('should return a function that when called, correctly calls the _add function with the right parameters & ' +
  'correctly transformed chainOptions', function() {
    var result = animar._thenChainFunction(defaultChainOptions);

    var testAttributesObject = { translateX: 10 },
      testOptionsObject = { duration: 20 };

    result(testElement, testAttributesObject, testOptionsObject);

    expect(animar._add.mock.calls[0][0]).toEqual(testElement);
    expect(animar._add.mock.calls[0][1]).toEqual(testAttributesObject);
    expect(animar._add.mock.calls[0][2]).toEqual(testOptionsObject);
    expect(animar._add.mock.calls[0][3]).toEqual(resultingChainOptions);
  });

});<|endoftext|>


<commit_before>const _ = require('underscore');
const debug = require('debug')('windshaft:cartodb:error-middleware');

module.exports = function errorMiddleware (/* options */) {
    return function error (err, req, res, next) {
        // jshint unused:false
        // jshint maxcomplexity:9
        var allErrors = Array.isArray(err) ? err : [err];

        allErrors = populateTimeoutErrors(allErrors);

        const label = err.label || 'UNKNOWN';
        err = allErrors[0] || new Error(label);
        allErrors[0] = err;

        var statusCode = findStatusCode(err);

        if (err.message === 'Tile does not exist' && res.locals.format === 'mvt') {
            statusCode = 204;
        }

        logErrors(allErrors, statusCode, res);
        debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);

        // If a callback was requested, force status to 200
        if (req.query && req.query.callback) {
            statusCode = 200;
        }

        var errorResponseBody = {
            errors: allErrors.map(errorMessage),
            errors_with_context: allErrors.map(errorMessageWithContext)
        };

        res.status(statusCode);

        if (req.query && req.query.callback) {
            res.jsonp(errorResponseBody);
        } else {
            res.json(errorResponseBody);
        }
    };
};

function isRenderTimeoutError (err) {
    return err.message === 'Render timed out';
}

function isDatasourceTimeoutError (err) {
    return err.message && err.message.match(/canceling statement due to statement timeout/i);
}

function isTimeoutError (err) {
    return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
}

function populateTimeoutErrors (errors) {
    return errors.map(function (error) {
        if (isRenderTimeoutError(error)) {
            error.subtype = 'render';
        }

        if (isDatasourceTimeoutError(error)) {
            error.subtype = 'datasource';
        }

        if (isTimeoutError(error)) {
            error.message = 'You are over platform\'s limits. Please contact us to know more details';
            error.type = 'limit';
            error.http_status = 429;
        }

        return error;
    });
}

function findStatusCode(err) {
    var statusCode;
    if ( err.http_status ) {
        statusCode = err.http_status;
    } else {
        statusCode = statusFromErrorMessage('' + err);
    }
    return statusCode;
}

module.exports.findStatusCode = findStatusCode;

function statusFromErrorMessage(errMsg) {
    // Find an appropriate statusCode based on message
    // jshint maxcomplexity:7
    var statusCode = 400;
    if ( -1 !== errMsg.indexOf('permission denied') ) {
        statusCode = 403;
    }
    else if ( -1 !== errMsg.indexOf('authentication failed') ) {
        statusCode = 403;
    }
    else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
        statusCode = 400;
    }
    else if ( -1 !== errMsg.indexOf('does not exist') ) {
        if ( -1 !== errMsg.indexOf(' role ') ) {
            statusCode = 403; // role 'xxx' does not exist
        } else if ( errMsg.match(/function .* does not exist/) ) {
            statusCode = 400; // invalid SQL (SQL function does not exist)
        } else {
            statusCode = 404;
        }
    }

    return statusCode;
}

function errorMessage(err) {
    // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
    var message = (_.isString(err) ? err : err.message) || 'Unknown error';

    return stripConnectionInfo(message);
}

module.exports.errorMessage = errorMessage;

function stripConnectionInfo(message) {
    // Strip connection info, if any
    return message
        // See https://github.com/CartoDB/Windshaft/issues/173
        .replace(/Connection string: '[^']*'\n\s/im, '')
        // See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
        .replace(/is the server.*encountered/im, 'encountered');
}

var ERROR_INFO_TO_EXPOSE = {
    message: true,
    layer: true,
    type: true,
    analysis: true,
    subtype: true
};

function shouldBeExposed (prop) {
    return !!ERROR_INFO_TO_EXPOSE[prop];
}

function errorMessageWithContext(err) {
    // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
    var message = (_.isString(err) ? err : err.message) || 'Unknown error';

    var error = {
        type: err.type || 'unknown',
        message: stripConnectionInfo(message),
    };

    for (var prop in err) {
        // type & message are properties from Error's prototype and will be skipped
        if (err.hasOwnProperty(prop) && shouldBeExposed(prop)) {
            error[prop] = err[prop];
        }
    }

    return error;
}

function logErrors(errors, statusCode, res) {
    let errorsCopy = errors.slice(0);

    if(!errorsCopy || !errorsCopy.length) {
        return;
    }

    const mainError = errorsCopy.shift();

    let errorsLog = {
        statusCode: statusCode || 200,
        message:    mainError.message,
        name:       mainError.name,
        label:      mainError.label,
        type:       mainError.type,
        subtype:    mainError.subtype
    };

    errorsLog.moreErrors = errorsCopy.map(error => {
        return {
            message: error.message,
            name:    error.name,
            label:   error.label,
            type:    error.type,
            subtype: error.subtype
        };
    });

    res.set('X-Tiler-Errors', JSON.stringify(errorsLog));
}
<commit_msg>removing unneeded check<commit_after>const _ = require('underscore');
const debug = require('debug')('windshaft:cartodb:error-middleware');

module.exports = function errorMiddleware (/* options */) {
    return function error (err, req, res, next) {
        // jshint unused:false
        // jshint maxcomplexity:9
        var allErrors = Array.isArray(err) ? err : [err];

        allErrors = populateTimeoutErrors(allErrors);

        const label = err.label || 'UNKNOWN';
        err = allErrors[0] || new Error(label);
        allErrors[0] = err;

        var statusCode = findStatusCode(err);

        if (err.message === 'Tile does not exist' && res.locals.format === 'mvt') {
            statusCode = 204;
        }

        logErrors(allErrors, statusCode, res);
        debug('[%s ERROR] -- %d: %s, %s', label, statusCode, err, err.stack);

        // If a callback was requested, force status to 200
        if (req.query && req.query.callback) {
            statusCode = 200;
        }

        var errorResponseBody = {
            errors: allErrors.map(errorMessage),
            errors_with_context: allErrors.map(errorMessageWithContext)
        };

        res.status(statusCode);

        if (req.query && req.query.callback) {
            res.jsonp(errorResponseBody);
        } else {
            res.json(errorResponseBody);
        }
    };
};

function isRenderTimeoutError (err) {
    return err.message === 'Render timed out';
}

function isDatasourceTimeoutError (err) {
    return err.message && err.message.match(/canceling statement due to statement timeout/i);
}

function isTimeoutError (err) {
    return isRenderTimeoutError(err) || isDatasourceTimeoutError(err);
}

function populateTimeoutErrors (errors) {
    return errors.map(function (error) {
        if (isRenderTimeoutError(error)) {
            error.subtype = 'render';
        }

        if (isDatasourceTimeoutError(error)) {
            error.subtype = 'datasource';
        }

        if (isTimeoutError(error)) {
            error.message = 'You are over platform\'s limits. Please contact us to know more details';
            error.type = 'limit';
            error.http_status = 429;
        }

        return error;
    });
}

function findStatusCode(err) {
    var statusCode;
    if ( err.http_status ) {
        statusCode = err.http_status;
    } else {
        statusCode = statusFromErrorMessage('' + err);
    }
    return statusCode;
}

module.exports.findStatusCode = findStatusCode;

function statusFromErrorMessage(errMsg) {
    // Find an appropriate statusCode based on message
    // jshint maxcomplexity:7
    var statusCode = 400;
    if ( -1 !== errMsg.indexOf('permission denied') ) {
        statusCode = 403;
    }
    else if ( -1 !== errMsg.indexOf('authentication failed') ) {
        statusCode = 403;
    }
    else if (errMsg.match(/Postgis Plugin.*[\s|\n].*column.*does not exist/)) {
        statusCode = 400;
    }
    else if ( -1 !== errMsg.indexOf('does not exist') ) {
        if ( -1 !== errMsg.indexOf(' role ') ) {
            statusCode = 403; // role 'xxx' does not exist
        } else if ( errMsg.match(/function .* does not exist/) ) {
            statusCode = 400; // invalid SQL (SQL function does not exist)
        } else {
            statusCode = 404;
        }
    }

    return statusCode;
}

function errorMessage(err) {
    // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
    var message = (_.isString(err) ? err : err.message) || 'Unknown error';

    return stripConnectionInfo(message);
}

module.exports.errorMessage = errorMessage;

function stripConnectionInfo(message) {
    // Strip connection info, if any
    return message
        // See https://github.com/CartoDB/Windshaft/issues/173
        .replace(/Connection string: '[^']*'\n\s/im, '')
        // See https://travis-ci.org/CartoDB/Windshaft/jobs/20703062#L1644
        .replace(/is the server.*encountered/im, 'encountered');
}

var ERROR_INFO_TO_EXPOSE = {
    message: true,
    layer: true,
    type: true,
    analysis: true,
    subtype: true
};

function shouldBeExposed (prop) {
    return !!ERROR_INFO_TO_EXPOSE[prop];
}

function errorMessageWithContext(err) {
    // See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
    var message = (_.isString(err) ? err : err.message) || 'Unknown error';

    var error = {
        type: err.type || 'unknown',
        message: stripConnectionInfo(message),
    };

    for (var prop in err) {
        // type & message are properties from Error's prototype and will be skipped
        if (err.hasOwnProperty(prop) && shouldBeExposed(prop)) {
            error[prop] = err[prop];
        }
    }

    return error;
}

function logErrors(errors, statusCode, res) {
    let errorsCopy = errors.slice(0);
    const mainError = errorsCopy.shift();

    let errorsLog = {
        statusCode: statusCode || 200,
        message:    mainError.message,
        name:       mainError.name,
        label:      mainError.label,
        type:       mainError.type,
        subtype:    mainError.subtype
    };

    errorsLog.moreErrors = errorsCopy.map(error => {
        return {
            message: error.message,
            name:    error.name,
            label:   error.label,
            type:    error.type,
            subtype: error.subtype
        };
    });

    res.set('X-Tiler-Errors', JSON.stringify(errorsLog));
}
<|endoftext|>


<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var fs = require('fs');
var path = require('path');
var utils = require('../../util/utils');

var isWindows = process.platform === 'win32';

// Making sure this works on node 0.6.20 as well as 0.8
fs.existsSync = fs.existsSync || path.existsSync;
path.sep = path.sep || (isWindows ? '\\' : '/');

var templatesDir = __dirname;
var log = { info: function () { } };
var confirm = function () { return false; };

var ScriptType = {
  batch: 'BATCH',
  bash: 'BASH'
};

var ProjectType = {
  wap: 'WAP',
  website: 'WEBSITE',
  node: 'NODE',
  basic: 'BASIC'
};

exports.ScriptType = ScriptType;
exports.ProjectType = ProjectType;

function ScriptGenerator(repositoryRoot, projectType, projectPath, solutionPath, sitePath, scriptType, noDotDeployment, noSolution, logger, confirmFunc) {
  argNotNull(repositoryRoot, 'repositoryRoot');
  argNotNull(projectType, 'projectType');
  argNotNull(scriptType, 'scriptType');
  argNotNull(sitePath, 'sitePath');

  scriptType = scriptType.toUpperCase();
  if (scriptType !== ScriptType.batch && scriptType !== ScriptType.bash) {
    throw new Error('Script type should be either batch or bash');
  }
  this.scriptType = scriptType;

  log = logger || log;
  confirm = confirmFunc || confirm;

  if (projectPath) {
    if (!isPathSubDir(repositoryRoot, projectPath)) {
      throw new Error('The project file path should be a sub-directory of the repository root');
    }

    var relativeProjectPath = path.relative(repositoryRoot, projectPath);
    log.info('Project file path: .' + path.sep + relativeProjectPath);
    this.projectPath = relativeProjectPath;
  }

  if (solutionPath) {
    if (!isPathSubDir(repositoryRoot, solutionPath)) {
      throw new Error('The solution file path should be the same as repository root or a sub-directory of it.');
    }

    var relativeSolutionPath = path.relative(repositoryRoot, solutionPath);
    log.info('Solution file path: .' + path.sep + relativeSolutionPath);
    this.solutionPath = relativeSolutionPath;
  }

  if (!isPathSubDir(repositoryRoot, sitePath)) {
    throw new Error('The site directory path should be the same as repository root or a sub-directory of it.');
  }

  var relativeSitePath = path.relative(repositoryRoot, sitePath);
  if (relativeSitePath) {
    relativeSitePath = path.sep + relativeSitePath;
    log.info('The site directory path: .' + relativeSitePath);
  }
  this.sitePath = relativeSitePath || '';

  projectType = projectType.toUpperCase();

  this.repositoryRoot = repositoryRoot;
  this.projectType = projectType;
  this.noDotDeployment = noDotDeployment;
  this.noSolution = noSolution;
  this.absoluteSitePath = path.join(this.repositoryRoot, this.sitePath);

  this.generators = [];
  this.generators[ProjectType.wap] = generateWapDeploymentScript;
  this.generators[ProjectType.website] = generateWebSiteDeploymentScript;
  this.generators[ProjectType.node] = generateNodeDeploymentScript;
  this.generators[ProjectType.basic] = generateBasicWebSiteDeploymentScript;
}

function generateWapDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateWapDeploymentScript(_);
}

function generateWebSiteDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateWebSiteDeploymentScript(_);
}

function generateNodeDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateNodeDeploymentScript(_);
}

function generateBasicWebSiteDeploymentScript(scriptGenerator, _) {
  if (scriptGenerator.solutionPath) {
    throw new Error('Solution path is not supported with this website type');
  }
  scriptGenerator.generateWebSiteDeploymentScript(_);
}

ScriptGenerator.prototype.generateDeploymentScript = function (_) {
  var generator = this.generators[this.projectType];
  if (!generator) {
    throw new Error('Invalid project type received: ' + this.projectType);
  }

  generator(this, _);
};

function isPathSubDir(parentPath, childPath) {
  var relativePath = path.relative(parentPath, childPath);

  // The parent path is actually the parent of the child path if the result of path.relative:
  // a. Doesn't contain '..' at the start
  // b. Doesn't equal to the child path entirely
  return relativePath.indexOf('..') !== 0 &&
    relativePath !== path.resolve(childPath);
}

ScriptGenerator.prototype.generateNodeDeploymentScript = function (_) {
  log.info('Generating deployment script for node.js Web Site');

  utils.copyIisNodeWhenServerJsPresent(log, this.absoluteSitePath, _);

  this.generateBasicDeploymentScript('node.template', _);
};

ScriptGenerator.prototype.generateWapDeploymentScript = function (_) {
  argNotNull(this.projectPath, 'projectPath');

  if (this.scriptType != ScriptType.batch) {
    throw new Error('Only batch script files are supported for .NET Web Application');
  }

  if (!this.solutionPath && !this.noSolution) {
    throw new Error('Missing solution file path (--solutionPath), to explicitly not require a solution use the flag --no-solution');
  }

  log.info('Generating deployment script for .NET Web Application');

  var msbuildArguments = '"%DEPLOYMENT_SOURCE%\\' + this.projectPath + '" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release';

  if (this.solutionPath) {
    var solutionDir = path.dirname(this.solutionPath);
    msbuildArguments += ' /p:SolutionDir="%DEPLOYMENT_SOURCE%\\' + solutionDir + '\\\\\"';
  }

  msbuildArguments += ' %SCM_BUILD_ARGS%';

  this.generateDotNetDeploymentScript('deploy.batch.aspnet.wap.template', msbuildArguments, _);
};

ScriptGenerator.prototype.generateWebSiteDeploymentScript = function (_) {
  if (this.solutionPath) {
    // Solution based website (.NET)
    log.info('Generating deployment script for .NET Web Site');

    if (this.scriptType != ScriptType.batch) {
      throw new Error('Only batch script files are supported for .NET Web Site');
    }

    var msbuildArguments = '"%DEPLOYMENT_SOURCE%\\' + fixPathSeperatorToWindows(this.solutionPath) + '" /verbosity:m /nologo %SCM_BUILD_ARGS%';
    this.generateDotNetDeploymentScript('deploy.batch.aspnet.website.template', msbuildArguments, _);
  } else {
    // Basic website
    log.info('Generating deployment script for Web Site');

    var packageJsonFilePath = path.join(this.absoluteSitePath, 'package.json');
    if (fs.existsSync(packageJsonFilePath)) {
      // On package.json, use node.template which will install npm packages
      this.generateBasicDeploymentScript('node.template', _);
    } else {
      // Otherwise it's a simple copy to deploy
      this.generateBasicDeploymentScript('basic.template', _);
    }
  }
};

ScriptGenerator.prototype.generateBasicDeploymentScript = function (templateFileName, _) {
  argNotNull(templateFileName, 'templateFileName');

  var lowerCaseScriptType = this.scriptType.toLowerCase();
  var fixedSitePath = this.scriptType === ScriptType.batch ? fixPathSeperatorToWindows(this.sitePath) : fixPathSeperatorToUnix(this.sitePath);
  var templateContent = getTemplatesContent([
    'deploy.' + lowerCaseScriptType + '.prefix.template',
    'deploy.' + lowerCaseScriptType + '.' + templateFileName,
    'deploy.' + lowerCaseScriptType + '.postfix.template'
  ]).replace(/{SitePath}/g, fixedSitePath);

  this.writeDeploymentFiles(templateContent, _);
};

ScriptGenerator.prototype.generateDotNetDeploymentScript = function (templateFileName, msbuildArguments, _) {
  argNotNull(templateFileName, 'templateFileName');

  var templateContent = getTemplatesContent([
    'deploy.batch.prefix.template',
    'deploy.batch.aspnet.template',
    templateFileName,
    'deploy.batch.postfix.template'
  ]).replace(/{MSBuildArguments}/g, msbuildArguments)
    .replace(/{SitePath}/g, fixPathSeperatorToWindows(this.sitePath));

  this.writeDeploymentFiles(templateContent, _);
};

function getTemplatesContent(fileNames) {
  var content = '';

  for (var i in fileNames) {
    content += getTemplateContent(fileNames[i]);
  }

  return content;
}

function fixPathSeperatorToWindows(pathStr) {
  return pathStr.replace(/\//g, '\\');
}

function fixPathSeperatorToUnix(pathStr) {
  return pathStr.replace(/\\/g, '/');
}

ScriptGenerator.prototype.writeDeploymentFiles = function (templateContent, _) {
  argNotNull(templateContent, 'templateContent');

  var deployScriptFileName;
  var deploymentCommand;
  if (this.scriptType == ScriptType.batch) {
    deployScriptFileName = 'deploy.cmd';
    deploymentCommand = deployScriptFileName;
  } else {
    deployScriptFileName = 'deploy.sh';
    deploymentCommand = 'bash ' + deployScriptFileName;
  }

  var deployScriptPath = path.join(this.repositoryRoot, deployScriptFileName);
  var deploymentFilePath = path.join(this.repositoryRoot, '.deployment');

  // Write the custom deployment script
  writeContentToFile(deployScriptPath, templateContent, _);

  if (!this.noDotDeployment) {
    // Write the .deployment file
    writeContentToFile(deploymentFilePath, '[config]\ncommand = ' + deploymentCommand, _);
  }

  log.info('Generated deployment script files');
};

function getTemplateContent(templateFileName) {
  return fs.readFileSync(getTemplatePath(templateFileName), 'utf8');
}

function getTemplatePath(fileName) {
  return path.join(templatesDir, fileName);
}

function writeContentToFile(path, content, _) {
  // TODO: Add check whether file exist
  if (fs.existsSync(path)) {
    if (!confirm('The file: "' + path + '" already exists\nAre you sure you want to overwrite it (y/n): ', _)) {
      // Do not overwrite the file
      return;
    }
  }

  fs.writeFile(path, content, _);
}

function argNotNull(arg, argName) {
  if (arg === null || arg === undefined) {
    throw new Error('The argument "' + argName + '" is null');
  }
}

exports.ScriptGenerator = ScriptGenerator;
<commit_msg>Change missed build args<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var fs = require('fs');
var path = require('path');
var utils = require('../../util/utils');

var isWindows = process.platform === 'win32';

// Making sure this works on node 0.6.20 as well as 0.8
fs.existsSync = fs.existsSync || path.existsSync;
path.sep = path.sep || (isWindows ? '\\' : '/');

var templatesDir = __dirname;
var log = { info: function () { } };
var confirm = function () { return false; };

var ScriptType = {
  batch: 'BATCH',
  bash: 'BASH'
};

var ProjectType = {
  wap: 'WAP',
  website: 'WEBSITE',
  node: 'NODE',
  basic: 'BASIC'
};

exports.ScriptType = ScriptType;
exports.ProjectType = ProjectType;

function ScriptGenerator(repositoryRoot, projectType, projectPath, solutionPath, sitePath, scriptType, noDotDeployment, noSolution, logger, confirmFunc) {
  argNotNull(repositoryRoot, 'repositoryRoot');
  argNotNull(projectType, 'projectType');
  argNotNull(scriptType, 'scriptType');
  argNotNull(sitePath, 'sitePath');

  scriptType = scriptType.toUpperCase();
  if (scriptType !== ScriptType.batch && scriptType !== ScriptType.bash) {
    throw new Error('Script type should be either batch or bash');
  }
  this.scriptType = scriptType;

  log = logger || log;
  confirm = confirmFunc || confirm;

  if (projectPath) {
    if (!isPathSubDir(repositoryRoot, projectPath)) {
      throw new Error('The project file path should be a sub-directory of the repository root');
    }

    var relativeProjectPath = path.relative(repositoryRoot, projectPath);
    log.info('Project file path: .' + path.sep + relativeProjectPath);
    this.projectPath = relativeProjectPath;
  }

  if (solutionPath) {
    if (!isPathSubDir(repositoryRoot, solutionPath)) {
      throw new Error('The solution file path should be the same as repository root or a sub-directory of it.');
    }

    var relativeSolutionPath = path.relative(repositoryRoot, solutionPath);
    log.info('Solution file path: .' + path.sep + relativeSolutionPath);
    this.solutionPath = relativeSolutionPath;
  }

  if (!isPathSubDir(repositoryRoot, sitePath)) {
    throw new Error('The site directory path should be the same as repository root or a sub-directory of it.');
  }

  var relativeSitePath = path.relative(repositoryRoot, sitePath);
  if (relativeSitePath) {
    relativeSitePath = path.sep + relativeSitePath;
    log.info('The site directory path: .' + relativeSitePath);
  }
  this.sitePath = relativeSitePath || '';

  projectType = projectType.toUpperCase();

  this.repositoryRoot = repositoryRoot;
  this.projectType = projectType;
  this.noDotDeployment = noDotDeployment;
  this.noSolution = noSolution;
  this.absoluteSitePath = path.join(this.repositoryRoot, this.sitePath);

  this.generators = [];
  this.generators[ProjectType.wap] = generateWapDeploymentScript;
  this.generators[ProjectType.website] = generateWebSiteDeploymentScript;
  this.generators[ProjectType.node] = generateNodeDeploymentScript;
  this.generators[ProjectType.basic] = generateBasicWebSiteDeploymentScript;
}

function generateWapDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateWapDeploymentScript(_);
}

function generateWebSiteDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateWebSiteDeploymentScript(_);
}

function generateNodeDeploymentScript(scriptGenerator, _) {
  scriptGenerator.generateNodeDeploymentScript(_);
}

function generateBasicWebSiteDeploymentScript(scriptGenerator, _) {
  if (scriptGenerator.solutionPath) {
    throw new Error('Solution path is not supported with this website type');
  }
  scriptGenerator.generateWebSiteDeploymentScript(_);
}

ScriptGenerator.prototype.generateDeploymentScript = function (_) {
  var generator = this.generators[this.projectType];
  if (!generator) {
    throw new Error('Invalid project type received: ' + this.projectType);
  }

  generator(this, _);
};

function isPathSubDir(parentPath, childPath) {
  var relativePath = path.relative(parentPath, childPath);

  // The parent path is actually the parent of the child path if the result of path.relative:
  // a. Doesn't contain '..' at the start
  // b. Doesn't equal to the child path entirely
  return relativePath.indexOf('..') !== 0 &&
    relativePath !== path.resolve(childPath);
}

ScriptGenerator.prototype.generateNodeDeploymentScript = function (_) {
  log.info('Generating deployment script for node.js Web Site');

  utils.copyIisNodeWhenServerJsPresent(log, this.absoluteSitePath, _);

  this.generateBasicDeploymentScript('node.template', _);
};

ScriptGenerator.prototype.generateWapDeploymentScript = function (_) {
  argNotNull(this.projectPath, 'projectPath');

  if (this.scriptType != ScriptType.batch) {
    throw new Error('Only batch script files are supported for .NET Web Application');
  }

  if (!this.solutionPath && !this.noSolution) {
    throw new Error('Missing solution file path (--solutionPath), to explicitly not require a solution use the flag --no-solution');
  }

  log.info('Generating deployment script for .NET Web Application');

  var msbuildArguments = '"%DEPLOYMENT_SOURCE%\\' + this.projectPath + '" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release';

  if (this.solutionPath) {
    var solutionDir = path.dirname(this.solutionPath);
    msbuildArguments += ' /p:SolutionDir="%DEPLOYMENT_SOURCE%\\' + solutionDir + '\\\\\"';
  }

  msbuildArguments += ' %SCM_BUILD_ARGS%';

  this.generateDotNetDeploymentScript('deploy.batch.aspnet.wap.template', msbuildArguments, _);
};

ScriptGenerator.prototype.generateWebSiteDeploymentScript = function (_) {
  if (this.solutionPath) {
    // Solution based website (.NET)
    log.info('Generating deployment script for .NET Web Site');

    if (this.scriptType != ScriptType.batch) {
      throw new Error('Only batch script files are supported for .NET Web Site');
    }

    var msbuildArguments = '"%DEPLOYMENT_SOURCE%\\' + fixPathSeperatorToWindows(this.solutionPath) + '" /verbosity:m /nologo %SCM_BUILD_ARGS%';
    this.generateDotNetDeploymentScript('deploy.batch.aspnet.website.template', msbuildArguments, _);
  } else {
    // Basic website
    log.info('Generating deployment script for Web Site');
    this.generateBasicDeploymentScript('basic.template', _);
  }
};

ScriptGenerator.prototype.generateBasicDeploymentScript = function (templateFileName, _) {
  argNotNull(templateFileName, 'templateFileName');

  var lowerCaseScriptType = this.scriptType.toLowerCase();
  var fixedSitePath = this.scriptType === ScriptType.batch ? fixPathSeperatorToWindows(this.sitePath) : fixPathSeperatorToUnix(this.sitePath);
  var templateContent = getTemplatesContent([
    'deploy.' + lowerCaseScriptType + '.prefix.template',
    'deploy.' + lowerCaseScriptType + '.' + templateFileName,
    'deploy.' + lowerCaseScriptType + '.postfix.template'
  ]).replace(/{SitePath}/g, fixedSitePath);

  this.writeDeploymentFiles(templateContent, _);
};

ScriptGenerator.prototype.generateDotNetDeploymentScript = function (templateFileName, msbuildArguments, _) {
  argNotNull(templateFileName, 'templateFileName');

  var templateContent = getTemplatesContent([
    'deploy.batch.prefix.template',
    'deploy.batch.aspnet.template',
    templateFileName,
    'deploy.batch.postfix.template'
  ]).replace(/{MSBuildArguments}/g, msbuildArguments)
    .replace(/{SitePath}/g, fixPathSeperatorToWindows(this.sitePath));

  this.writeDeploymentFiles(templateContent, _);
};

function getTemplatesContent(fileNames) {
  var content = '';

  for (var i in fileNames) {
    content += getTemplateContent(fileNames[i]);
  }

  return content;
}

function fixPathSeperatorToWindows(pathStr) {
  return pathStr.replace(/\//g, '\\');
}

function fixPathSeperatorToUnix(pathStr) {
  return pathStr.replace(/\\/g, '/');
}

ScriptGenerator.prototype.writeDeploymentFiles = function (templateContent, _) {
  argNotNull(templateContent, 'templateContent');

  var deployScriptFileName;
  var deploymentCommand;
  if (this.scriptType == ScriptType.batch) {
    deployScriptFileName = 'deploy.cmd';
    deploymentCommand = deployScriptFileName;
  } else {
    deployScriptFileName = 'deploy.sh';
    deploymentCommand = 'bash ' + deployScriptFileName;
  }

  var deployScriptPath = path.join(this.repositoryRoot, deployScriptFileName);
  var deploymentFilePath = path.join(this.repositoryRoot, '.deployment');

  // Write the custom deployment script
  writeContentToFile(deployScriptPath, templateContent, _);

  if (!this.noDotDeployment) {
    // Write the .deployment file
    writeContentToFile(deploymentFilePath, '[config]\ncommand = ' + deploymentCommand, _);
  }

  log.info('Generated deployment script files');
};

function getTemplateContent(templateFileName) {
  return fs.readFileSync(getTemplatePath(templateFileName), 'utf8');
}

function getTemplatePath(fileName) {
  return path.join(templatesDir, fileName);
}

function writeContentToFile(path, content, _) {
  // TODO: Add check whether file exist
  if (fs.existsSync(path)) {
    if (!confirm('The file: "' + path + '" already exists\nAre you sure you want to overwrite it (y/n): ', _)) {
      // Do not overwrite the file
      return;
    }
  }

  fs.writeFile(path, content, _);
}

function argNotNull(arg, argName) {
  if (arg === null || arg === undefined) {
    throw new Error('The argument "' + argName + '" is null');
  }
}

exports.ScriptGenerator = ScriptGenerator;
<|endoftext|>


<commit_before>'use strict';

const Promise = require('bluebird');

const helpers = require('../../helpers');
const UnprocessibleEntityError = require('../../../app/exceptions/UnprocessibleEntityError');

// TODO: Move inside lib/contentful
function getCampaignId(broadcastObject) {
  const campaign = broadcastObject.fields.campaign;
  if (!campaign.fields) {
    return null;
  }
  return campaign.fields.campaignId;
}

// TODO: Move inside lib/contentful
function getTopic(broadcastObject) {
  return broadcastObject.fields.topic;
}

// TODO: Move inside lib/contentful
function replaceFieldsInMessage(fields = [], message) {
  let msg = message;
  fields.forEach((field) => {
    const key = Object.keys(field)[0];
    msg = message.replace(new RegExp(`{{${key}}}`, 'g'), field[key]);
  });
  return msg;
}

// TODO: Move inside lib/contentful
function getMessage(broadcastObject) {
  return broadcastObject.fields.message;
}

// TODO: Move inside lib/contentful
function parsePropertiesFromBroadcast(req, res) {
  return helpers.getBroadcast(req, res)
    .then((broadcast) => {
      req.topic = getTopic(broadcast);
      req.campaignId = getCampaignId(broadcast);
      req.importMessageText = replaceFieldsInMessage(req.messageFields, getMessage(broadcast));
      return broadcast;
    });
}

module.exports = function getBroadcast() {
  return (req, res, next) => {
    const asyncProperties = [];

    // We are only allowing Customer.io imports, which MUST have broadcastId. If in the future
    // we want to allow generic imports we can relax this requirement here and call next()
    if (!req.broadcastId) {
      const error = new UnprocessibleEntityError('broadcastId is a required property.');
      return helpers.sendErrorResponse(res, error);
    }

    asyncProperties.push(parsePropertiesFromBroadcast(req, res));

    // Process async parsers
    return Promise.all(asyncProperties)
      .then(() => next())
      .catch(err => helpers.sendErrorResponse(res, err));
  };
};
<commit_msg>renamed importMessageText to messageText in import-message/broadcast. This makes it easier to DRY code in the middleware.<commit_after>'use strict';

const Promise = require('bluebird');

const helpers = require('../../helpers');
const UnprocessibleEntityError = require('../../../app/exceptions/UnprocessibleEntityError');

// TODO: Move inside lib/contentful
function getCampaignId(broadcastObject) {
  const campaign = broadcastObject.fields.campaign;
  if (!campaign.fields) {
    return null;
  }
  return campaign.fields.campaignId;
}

// TODO: Move inside lib/contentful
function getTopic(broadcastObject) {
  return broadcastObject.fields.topic;
}

// TODO: Move inside lib/contentful
function replaceFieldsInMessage(fields = [], message) {
  let msg = message;
  fields.forEach((field) => {
    const key = Object.keys(field)[0];
    msg = message.replace(new RegExp(`{{${key}}}`, 'g'), field[key]);
  });
  return msg;
}

// TODO: Move inside lib/contentful
function getMessage(broadcastObject) {
  return broadcastObject.fields.message;
}

// TODO: Move inside lib/contentful
function parsePropertiesFromBroadcast(req, res) {
  return helpers.getBroadcast(req, res)
    .then((broadcast) => {
      req.topic = getTopic(broadcast);
      req.campaignId = getCampaignId(broadcast);
      req.messageText = replaceFieldsInMessage(req.messageFields, getMessage(broadcast));
      return broadcast;
    });
}

module.exports = function getBroadcast() {
  return (req, res, next) => {
    const asyncProperties = [];

    // We are only allowing Customer.io imports, which MUST have broadcastId. If in the future
    // we want to allow generic imports we can relax this requirement here and call next()
    if (!req.broadcastId) {
      const error = new UnprocessibleEntityError('broadcastId is a required property.');
      return helpers.sendErrorResponse(res, error);
    }

    asyncProperties.push(parsePropertiesFromBroadcast(req, res));

    // Process async parsers
    return Promise.all(asyncProperties)
      .then(() => next())
      .catch(err => helpers.sendErrorResponse(res, err));
  };
};
<|endoftext|>


<commit_before>// ******************************************************************
// § 5.1. Validation keywords for numeric instances
// ******************************************************************

var Errors = require('../../../errors.js');

module.exports = function(config) {
  var errors = [];
  if (config.inst % config.schema.multipleOf) {
    errors.push(new Errors.NumericValidationError(config.resolutionScope,
      config.instanceContext, 'multipleOf', config.schema.multipleOf,
      config.inst));
  }
  return errors;
};
<commit_msg>multipleOf now works with small numbers<commit_after>// ******************************************************************
// § 5.1. Validation keywords for numeric instances
// ******************************************************************

var Errors = require('../../../errors.js');

module.exports = function(config) {
  var errors = [];
  var intermediate = config.inst / config.schema.multipleOf;
  if (intermediate % 1) {
    errors.push(new Errors.NumericValidationError(config.resolutionScope,
      config.instanceContext, 'multipleOf', config.schema.multipleOf,
      config.inst));
  }
  return errors;
};
<|endoftext|>


<commit_before>// Copyright 2015, EMC, Inc.
/* jshint node: true */

'use strict';

var di = require('di');

module.exports = snmpPduPowerMetricFactory;
di.annotate(snmpPduPowerMetricFactory,
        new di.Provide('JobUtils.Metrics.Snmp.PduPowerMetric'));
di.annotate(snmpPduPowerMetricFactory, new di.Inject(
    'JobUtils.Metrics.Snmp.Base',
    'Assert',
    'Util',
    '_'
));

function snmpPduPowerMetricFactory(
    BaseMetric,
    assert,
    util,
    _
) {
    function SnmpPduPowerMetric(nodeId, host, community) {
        SnmpPduPowerMetric.super_.call(this, nodeId, host, community);
    }

    util.inherits(SnmpPduPowerMetric, BaseMetric);

    SnmpPduPowerMetric.prototype.collectMetricData = function() {
        var self = this;

        return self.identify()
        .then(function() {
            if (self.nodeType !== 'sinetica') {
                return self.updateOidDescriptionMapByType('power');
            }
        })
        .then(self.collectPowerData.bind(self))
        .then(self.calculatePowerData.bind(self));
    };

    SnmpPduPowerMetric.prototype.collectPowerData = function() {
        var self = this;

        if (this.nodeType === 'sinetica') {
            return self._collectSineticaPowerData();
        }
    };

    SnmpPduPowerMetric.prototype.calculatePowerData = function(values) {
        var self = this;

        if (this.nodeType === 'sinetica') {
            return self._calculateSineticaPowerData(values);
        }
    };

    SnmpPduPowerMetric.prototype._collectSineticaPowerData = function() {
        var self = this;
        var pduOids = [];
        var outletOids = [];
        var snmpQueryType = 'bulkwalk';

        // NOTE: Sinetica(Panduit) IPI PDU gateway cannot handle snmp commands concurrently
        // Sending snmp commands concurrently will have a chance to cause PDU no response
        pduOids = [
            //PDUs' health and status
            'HAWK-I2-MIB::pduName',
            'HAWK-I2-MIB::pduRS',
            'HAWK-I2-MIB::pduMonEn',
            'HAWK-I2-MIB::pduCommsFail',
            //PDUs' power status
            'HAWK-I2-MIB::pduRMSVoltsValue',
            'HAWK-I2-MIB::pduRMSAmpsValue',
            'HAWK-I2-MIB::pduTotalEnergyValue',
            'HAWK-I2-MIB::pduMeanKVAValue',
            'HAWK-I2-MIB::pduMeanKWattsValue',
            'HAWK-I2-MIB::pduPwrFactorValue',
            'HAWK-I2-MIB::pduPwrSupplyFreq'
                ];

        outletOids = [
            //PDU outlets' power status
            'HAWK-I2-MIB::pduOutName',
            'HAWK-I2-MIB::pduOutOn',
            'HAWK-I2-MIB::pduOutRMSAmpsValue',
            'HAWK-I2-MIB::pduOutRMSAmpsPeak',
            'HAWK-I2-MIB::pduOutMeanKVAValue',
            'HAWK-I2-MIB::pduOutKWHrValue',
            'HAWK-I2-MIB::pduOutPFactorValue'
                ];

        return self.snmptool.collectHostSnmp(pduOids, {
            snmpQueryType: snmpQueryType,
               isSequential: true
        })
        .then(function (pduOidsResult) {
            return self.snmptool.collectHostSnmp(outletOids, {
                snmpQueryType: snmpQueryType,
                   isSequential: true
            })
            .then(function (outletOidsResult) {
                return { pdusResult : pduOidsResult,
                    outletsResult: outletOidsResult };
            });
        });
    };

    SnmpPduPowerMetric.prototype._calculateSineticaPowerData = function(powerData) {
        var out = {};
        var unitsMap = {
            'pduOutName': 'null',
            'pduOutOn': 'null',
            'pduName': 'null',
            'pduRS': 'null',
            'pduMonEn': 'null',
            'pduCommsFail': 'null',
            'pduRMSVoltsValue': {
                'unit': ' Volts',
                'factor': '1'
            },
            'pduRMSAmpsValue': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduTotalEnergyValue': {
                'unit': ' kWh',
                'factor': '0.1'
            },
            'pduMeanKVAValue': {
                'unit': ' kVA',
                'factor': '0.1'
            },
            'pduMeanKWattsValue': {
                'unit': ' kW',
                'factor': '0.1'
            },
            'pduPwrFactorValue': {
                'unit': '',
                'factor': '0.01'
            },
            'pduPwrSupplyFreq': {
                'unit': ' Hz',
                'factor': '0.1'
            },
            'pduOutRMSAmpsValue': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduOutRMSAmpsPeak': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduOutMeanKVAValue': {
                'unit': ' kVA',
                'factor': '0.1'
            },
            'pduOutKWHrValue': {
                'unit': ' kWhr',
                'factor': '0.1'
            },
            'pduOutPFactorValue': {
                'unit': '',
                'factor': '0.01'
            }
        };

        //Get PDU number and initialize the output
        _.forEach(_.keys(powerData.pdusResult[0].values),function (key) {
            var pduNum = _.last(key.split('.'));

            //PDU number '99' is the virtual PDU for Aggregate
            //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
            pduNum = (pduNum === '99')? 'Aggregate' : ((+pduNum + 2) / 3);
            out['PDU_' + pduNum] = {};
        });

        /* Get PDUs Monitor information and format it, output like this:
         *  "result": {
         *      "PDU_1": {
         *          ...
         *          "pduMeanKVAValue": "0.2 kVA",
         *          ...
         *          },
         *      "PDU_2": {
         *          ...
         *          }
         *      }
         */
        _.forEach(powerData.pdusResult, function (pduPowerItems) {
            _.forEach(pduPowerItems.values,function (pduPowerItemValue, key) {
                var pduNum = _.last(key.split('.'));
                var pduPowerItem = _.last((_.first(key.split('.'))).split('::'));

                //PDU number '99' is the virtual PDU for Aggregate
                //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
                pduNum = (pduNum === '99')? 'Aggregate' : ((+pduNum + 2) / 3);

                _.forEach(out, function (value, pdu) {
                    //PDU name is 'PDU_*'
                    if (pdu.split('_')[1] === String(pduNum)) {
                        if (unitsMap[pduPowerItem] !== 'null') {
                            out[pdu][pduPowerItem] =
                    (pduPowerItemValue * unitsMap[pduPowerItem].factor) +
                    unitsMap[pduPowerItem].unit;
                        } else {
                            out[pdu][pduPowerItem] = pduPowerItemValue;
                        }
                    }
                });
            });
        });

        //Get outlet number and initialize the output
        _.forEach(_.keys(powerData.outletsResult[0].values),function (key) {
            //Don't like PDU status, outlet status data don't contain PDU number '99'
            //so don't handle PDU number '99' here like PDU status
            //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
            var pduNum = (+((key.split('.'))[1]) + 2) / 3;
            var outLetNum = _.last(key.split('.'));
            var pdu = 'PDU_' + pduNum;
            var outlet = 'outlet_' + outLetNum;

            if (!_.has(out, pdu)) {
                return;
            }

            if (!_.has(out[pdu], 'outlets')) {
                out[pdu].outlets = {};
            }

            if (_.has(out[pdu].outlets, outlet)) {
                return;
            } else {
                out[pdu].outlets[outlet] = {};
            }
        });

        /*Get PDUs' Outlets Monitor information and format it, output like this:
         *
         *  "result": {
         *      "PDU_1": {
         *          ...
         *          "outlets": {
         *              "outlet_1": {
         *                  "pduOutKWHrValue": "0 kWhr",
         *                  ...
         *                  },
         *              "outlet_2": {
         *              ...
         */
        _.forEach(powerData.outletsResult, function (outletPowerItems) {
            _.forEach(outletPowerItems.values,function (outletPowerItemValue, key) {
                //Don't like PDU status, outlet status data don't contain PDU number '99'
                //so don't handle PDU number '99' here like PDU status
                //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
                var pduNum = (+((key.split('.'))[1]) + 2) / 3;
                var outletNum = _.last(key.split('.'));
                var outletPowerItem = _.last((_.first(key.split('.'))).split('::'));

                _.forEach(out, function (value, pdu) {
                    _.forEach(out[pdu].outlets, function (value, outlet) {
                        if (pdu.split('_')[1] === String(pduNum) &&
                            outlet.split('_')[1] === String(outletNum)) {
                            if (unitsMap[outletPowerItem] !== 'null') {
                            out[pdu].outlets[outlet][outletPowerItem] =
                        (outletPowerItemValue * unitsMap[outletPowerItem].factor) +
                        unitsMap[outletPowerItem].unit;
                            } else {
                                out[pdu].outlets[outlet][outletPowerItem] = outletPowerItemValue;
                            }
                        }
                    });
                });
            });
        });

        return out;
    };

    return SnmpPduPowerMetric;
}
<commit_msg>ODR-225: pdu power pollers fail because of unknown OID description type<commit_after>// Copyright 2015, EMC, Inc.
/* jshint node: true */

'use strict';

var di = require('di');

module.exports = snmpPduPowerMetricFactory;
di.annotate(snmpPduPowerMetricFactory,
        new di.Provide('JobUtils.Metrics.Snmp.PduPowerMetric'));
di.annotate(snmpPduPowerMetricFactory, new di.Inject(
    'JobUtils.Metrics.Snmp.Base',
    'Assert',
    'Util',
    '_'
));

function snmpPduPowerMetricFactory(
    BaseMetric,
    assert,
    util,
    _
) {
    function SnmpPduPowerMetric(nodeId, host, community) {
        SnmpPduPowerMetric.super_.call(this, nodeId, host, community);
    }

    util.inherits(SnmpPduPowerMetric, BaseMetric);

    SnmpPduPowerMetric.prototype.collectMetricData = function() {
        var self = this;

        return self.identify()
        .then(self.collectPowerData.bind(self))
        .then(self.calculatePowerData.bind(self));
    };

    SnmpPduPowerMetric.prototype.collectPowerData = function() {
        var self = this;

        if (this.nodeType === 'sinetica') {
            return self._collectSineticaPowerData();
        }
    };

    SnmpPduPowerMetric.prototype.calculatePowerData = function(values) {
        var self = this;

        if (this.nodeType === 'sinetica') {
            return self._calculateSineticaPowerData(values);
        }
    };

    SnmpPduPowerMetric.prototype._collectSineticaPowerData = function() {
        var self = this;
        var pduOids = [];
        var outletOids = [];
        var snmpQueryType = 'bulkwalk';

        // NOTE: Sinetica(Panduit) IPI PDU gateway cannot handle snmp commands concurrently
        // Sending snmp commands concurrently will have a chance to cause PDU no response
        pduOids = [
            //PDUs' health and status
            'HAWK-I2-MIB::pduName',
            'HAWK-I2-MIB::pduRS',
            'HAWK-I2-MIB::pduMonEn',
            'HAWK-I2-MIB::pduCommsFail',
            //PDUs' power status
            'HAWK-I2-MIB::pduRMSVoltsValue',
            'HAWK-I2-MIB::pduRMSAmpsValue',
            'HAWK-I2-MIB::pduTotalEnergyValue',
            'HAWK-I2-MIB::pduMeanKVAValue',
            'HAWK-I2-MIB::pduMeanKWattsValue',
            'HAWK-I2-MIB::pduPwrFactorValue',
            'HAWK-I2-MIB::pduPwrSupplyFreq'
                ];

        outletOids = [
            //PDU outlets' power status
            'HAWK-I2-MIB::pduOutName',
            'HAWK-I2-MIB::pduOutOn',
            'HAWK-I2-MIB::pduOutRMSAmpsValue',
            'HAWK-I2-MIB::pduOutRMSAmpsPeak',
            'HAWK-I2-MIB::pduOutMeanKVAValue',
            'HAWK-I2-MIB::pduOutKWHrValue',
            'HAWK-I2-MIB::pduOutPFactorValue'
                ];

        return self.snmptool.collectHostSnmp(pduOids, {
            snmpQueryType: snmpQueryType,
               isSequential: true
        })
        .then(function (pduOidsResult) {
            return self.snmptool.collectHostSnmp(outletOids, {
                snmpQueryType: snmpQueryType,
                   isSequential: true
            })
            .then(function (outletOidsResult) {
                return { pdusResult : pduOidsResult,
                    outletsResult: outletOidsResult };
            });
        });
    };

    SnmpPduPowerMetric.prototype._calculateSineticaPowerData = function(powerData) {
        var out = {};
        var unitsMap = {
            'pduOutName': 'null',
            'pduOutOn': 'null',
            'pduName': 'null',
            'pduRS': 'null',
            'pduMonEn': 'null',
            'pduCommsFail': 'null',
            'pduRMSVoltsValue': {
                'unit': ' Volts',
                'factor': '1'
            },
            'pduRMSAmpsValue': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduTotalEnergyValue': {
                'unit': ' kWh',
                'factor': '0.1'
            },
            'pduMeanKVAValue': {
                'unit': ' kVA',
                'factor': '0.1'
            },
            'pduMeanKWattsValue': {
                'unit': ' kW',
                'factor': '0.1'
            },
            'pduPwrFactorValue': {
                'unit': '',
                'factor': '0.01'
            },
            'pduPwrSupplyFreq': {
                'unit': ' Hz',
                'factor': '0.1'
            },
            'pduOutRMSAmpsValue': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduOutRMSAmpsPeak': {
                'unit': ' Amps',
                'factor': '0.1'
            },
            'pduOutMeanKVAValue': {
                'unit': ' kVA',
                'factor': '0.1'
            },
            'pduOutKWHrValue': {
                'unit': ' kWhr',
                'factor': '0.1'
            },
            'pduOutPFactorValue': {
                'unit': '',
                'factor': '0.01'
            }
        };

        //Get PDU number and initialize the output
        _.forEach(_.keys(powerData.pdusResult[0].values),function (key) {
            var pduNum = _.last(key.split('.'));

            //PDU number '99' is the virtual PDU for Aggregate
            //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
            pduNum = (pduNum === '99')? 'Aggregate' : ((+pduNum + 2) / 3);
            out['PDU_' + pduNum] = {};
        });

        /* Get PDUs Monitor information and format it, output like this:
         *  "result": {
         *      "PDU_1": {
         *          ...
         *          "pduMeanKVAValue": "0.2 kVA",
         *          ...
         *          },
         *      "PDU_2": {
         *          ...
         *          }
         *      }
         */
        _.forEach(powerData.pdusResult, function (pduPowerItems) {
            _.forEach(pduPowerItems.values,function (pduPowerItemValue, key) {
                var pduNum = _.last(key.split('.'));
                var pduPowerItem = _.last((_.first(key.split('.'))).split('::'));

                //PDU number '99' is the virtual PDU for Aggregate
                //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
                pduNum = (pduNum === '99')? 'Aggregate' : ((+pduNum + 2) / 3);

                _.forEach(out, function (value, pdu) {
                    //PDU name is 'PDU_*'
                    if (pdu.split('_')[1] === String(pduNum)) {
                        if (unitsMap[pduPowerItem] !== 'null') {
                            out[pdu][pduPowerItem] =
                    (pduPowerItemValue * unitsMap[pduPowerItem].factor) +
                    unitsMap[pduPowerItem].unit;
                        } else {
                            out[pdu][pduPowerItem] = pduPowerItemValue;
                        }
                    }
                });
            });
        });

        //Get outlet number and initialize the output
        _.forEach(_.keys(powerData.outletsResult[0].values),function (key) {
            //Don't like PDU status, outlet status data don't contain PDU number '99'
            //so don't handle PDU number '99' here like PDU status
            //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
            var pduNum = (+((key.split('.'))[1]) + 2) / 3;
            var outLetNum = _.last(key.split('.'));
            var pdu = 'PDU_' + pduNum;
            var outlet = 'outlet_' + outLetNum;

            if (!_.has(out, pdu)) {
                return;
            }

            if (!_.has(out[pdu], 'outlets')) {
                out[pdu].outlets = {};
            }

            if (_.has(out[pdu].outlets, outlet)) {
                return;
            } else {
                out[pdu].outlets[outlet] = {};
            }
        });

        /*Get PDUs' Outlets Monitor information and format it, output like this:
         *
         *  "result": {
         *      "PDU_1": {
         *          ...
         *          "outlets": {
         *              "outlet_1": {
         *                  "pduOutKWHrValue": "0 kWhr",
         *                  ...
         *                  },
         *              "outlet_2": {
         *              ...
         */
        _.forEach(powerData.outletsResult, function (outletPowerItems) {
            _.forEach(outletPowerItems.values,function (outletPowerItemValue, key) {
                //Don't like PDU status, outlet status data don't contain PDU number '99'
                //so don't handle PDU number '99' here like PDU status
                //Sinetica PDU SNMP display PDU number is 1,4,7,..., convert to 1,2,3,...
                var pduNum = (+((key.split('.'))[1]) + 2) / 3;
                var outletNum = _.last(key.split('.'));
                var outletPowerItem = _.last((_.first(key.split('.'))).split('::'));

                _.forEach(out, function (value, pdu) {
                    _.forEach(out[pdu].outlets, function (value, outlet) {
                        if (pdu.split('_')[1] === String(pduNum) &&
                            outlet.split('_')[1] === String(outletNum)) {
                            if (unitsMap[outletPowerItem] !== 'null') {
                            out[pdu].outlets[outlet][outletPowerItem] =
                        (outletPowerItemValue * unitsMap[outletPowerItem].factor) +
                        unitsMap[outletPowerItem].unit;
                            } else {
                                out[pdu].outlets[outlet][outletPowerItem] = outletPowerItemValue;
                            }
                        }
                    });
                });
            });
        });

        return out;
    };

    return SnmpPduPowerMetric;
}
<|endoftext|>