<commit_before>/**
 * Created by Serhii Maksymchuk from Ubrainians for imCode
 * 04.09.17
 */
Imcms.define(
    "imcms-window-builder",
    ["imcms-window-components-builder", "imcms-window-keys-controller", "jquery"],
    function (windowComponents, windowKeysController, $) {

        function setBodyScrollingRule(overflowValue) {
            $("body").css("overflow", overflowValue);
        }

        function enableBackgroundPageScrolling(pageOverflow, scrollTop) {
            setBodyScrollingRule(pageOverflow);
            $(window).scrollTop(scrollTop);
        }

        function getScrollTopAndDisable() {
            var $window = $(window);
            var prevScrollTop = $window.scrollTop();
            $window.scrollTop(0);

            return prevScrollTop;
        }

        var WindowBuilder = function (opts) {
            this.factory = opts.factory;
            this.loadDataStrategy = opts.loadDataStrategy;
            this.clearDataStrategy = opts.clearDataStrategy;

            this.onEscKeyPressed = (opts.onEscKeyPressed === "close")
                ? this.closeWindow.bind(this)
                : opts.onEscKeyPressed;

            this.onEnterKeyPressed = opts.onEnterKeyPressed;
            this.disableKeyBindings = opts.disableKeyBindings;
            this.$editor = undefined;
        };

        WindowBuilder.prototype = {
            _scrollTop: 0,
            _pageOverflow: "auto",
            buildWindowWithShadow: function (windowInitData) {
                this.shadowBuilder = this.shadowBuilder || new WindowBuilder({
                    disableKeyBindings: true,
                    factory: function () {
                        return $("<div>", {"class": "imcms-modal-layout"});
                    }
                });

                this.buildWindow.apply(this, arguments);
                this.shadowBuilder.buildWindow();
            },
            buildWindow: function (windowInitData) {
                try {
                    this._scrollTop = getScrollTopAndDisable();
                    this._pageOverflow = $("body").css("overflow") || "auto";
                    setBodyScrollingRule("hidden");

                    if (!this.$editor) {
                        this.$editor = this.factory.apply(null, arguments).appendTo("body");
                    }

                    this.loadDataStrategy && this.loadDataStrategy.applyAsync(arguments);
                    this.$editor.css("display", "block");

                    this.disableKeyBindings || windowKeysController.registerWindow(
                        this.onEscKeyPressed, this.onEnterKeyPressed
                    );
                } catch (e) {
                    console.error(e);
                    alert("Error in window builder! Stacktrace in console.");

                    this.$editor && this.$editor.remove();
                    this.shadowBuilder && this.shadowBuilder.closeWindow();
                    this.disableKeyBindings || windowKeysController.unRegister();
                }
            },
            closeWindow: function () {
                try {
                    this.disableKeyBindings || windowKeysController.unRegister();
                    enableBackgroundPageScrolling(this._pageOverflow, this._scrollTop);

                    this._scrollTop = 0;
                    this.$editor && this.$editor.css("display", "none");

                    if (this.shadowBuilder) {
                        setTimeout(this.shadowBuilder.closeWindow.bind(this.shadowBuilder));
                    }

                    if (this.clearDataStrategy && this.clearDataStrategy.call) {
                        try {
                            this.clearDataStrategy.call();

                        } catch (e) {
                            console.error(e);
                        }
                    }
                } catch (e) {
                    console.error(e);
                    alert("Error in window builder! Stacktrace in console.");

                    this.$editor && this.$editor.remove();
                    this.shadowBuilder && this.shadowBuilder.closeWindow();
                }
            },
            /**
             * Builds head with specified title and close button
             * @param {string} title
             * @param {function} onCloseClick
             * @returns {*} head as jQuery element
             */
            buildHead: function (title, onCloseClick) {
                onCloseClick = onCloseClick || this.closeWindow.bind(this);
                return windowComponents.buildHead(title, onCloseClick);
            },
            /**
             * Builds head with specified title
             * @param {string} title
             * @returns {*} head as jQuery element
             */
            buildNonClosableHead: function (title) {
                return windowComponents.buildNonClosableHead(title);
            },
            /**
             * Builds footer with specified buttons as it's block elements
             * @param {[]} buttons - array of jQuery elements
             * @returns {*} footer as jQuery element
             */
            buildFooter: function (buttons) {
                return windowComponents.buildFooter.apply(windowComponents, arguments);
            }
        };

        return WindowBuilder;
    }
);
<commit_msg>IMCMS-350 - Image Editor improvements: - Fixed page scroll restoring when closing editors.<commit_after>/**
 * Created by Serhii Maksymchuk from Ubrainians for imCode
 * 04.09.17
 */
Imcms.define(
    "imcms-window-builder",
    ["imcms-window-components-builder", "imcms-window-keys-controller", "jquery"],
    function (windowComponents, windowKeysController, $) {

        function setBodyScrollingRule(overflowValue) {
            $("body").css("overflow", overflowValue);
        }

        function enableBackgroundPageScrolling(pageOverflow, scrollTop) {
            setBodyScrollingRule(pageOverflow);
            $(window).scrollTop(scrollTop);
        }

        function getScrollTopAndDisable() {
            var $window = $(window);
            var prevScrollTop = $window.scrollTop();
            $window.scrollTop(0);

            return prevScrollTop;
        }

        var WindowBuilder = function (opts) {
            this.factory = opts.factory;
            this.loadDataStrategy = opts.loadDataStrategy;
            this.clearDataStrategy = opts.clearDataStrategy;

            this.onEscKeyPressed = (opts.onEscKeyPressed === "close")
                ? this.closeWindow.bind(this)
                : opts.onEscKeyPressed;

            this.onEnterKeyPressed = opts.onEnterKeyPressed;
            this.disableKeyBindings = opts.disableKeyBindings;
            this.$editor = undefined;
        };

        WindowBuilder.prototype = {
            _scrollTop: 0,
            _pageOverflow: "auto",
            buildWindowWithShadow: function (windowInitData) {
                this.shadowBuilder = this.shadowBuilder || new WindowBuilder({
                    disableKeyBindings: true,
                    factory: function () {
                        return $("<div>", {"class": "imcms-modal-layout"});
                    }
                });

                this.buildWindow.apply(this, arguments);
                this.shadowBuilder.buildWindow();
            },
            buildWindow: function (windowInitData) {
                try {
                    this._scrollTop = getScrollTopAndDisable();
                    this._pageOverflow = $("body").css("overflow") || "auto";
                    setBodyScrollingRule("hidden");

                    if (!this.$editor) {
                        this.$editor = this.factory.apply(null, arguments).appendTo("body");
                    }

                    this.loadDataStrategy && this.loadDataStrategy.applyAsync(arguments);
                    this.$editor.css("display", "block");

                    this.disableKeyBindings || windowKeysController.registerWindow(
                        this.onEscKeyPressed, this.onEnterKeyPressed
                    );
                } catch (e) {
                    console.error(e);
                    alert("Error in window builder! Stacktrace in console.");

                    this.$editor && this.$editor.remove();
                    this.shadowBuilder && this.shadowBuilder.closeWindow();
                    this.disableKeyBindings || windowKeysController.unRegister();
                }
            },
            closeWindow: function (opts) {
                try {
                    this.disableKeyBindings || windowKeysController.unRegister();

                    if (!opts || !opts.skipScrollFix) {
                        enableBackgroundPageScrolling(this._pageOverflow, this._scrollTop);
                        this._scrollTop = 0;
                    }

                    this.$editor && this.$editor.css("display", "none");

                    if (this.shadowBuilder) {
                        setTimeout(this.shadowBuilder.closeWindow.bind(this.shadowBuilder, {skipScrollFix: true}));
                    }

                    if (this.clearDataStrategy && this.clearDataStrategy.call) {
                        try {
                            this.clearDataStrategy.call();

                        } catch (e) {
                            console.error(e);
                        }
                    }
                } catch (e) {
                    console.error(e);
                    alert("Error in window builder! Stacktrace in console.");

                    this.$editor && this.$editor.remove();
                    this.shadowBuilder && this.shadowBuilder.closeWindow();
                }
            },
            /**
             * Builds head with specified title and close button
             * @param {string} title
             * @param {function} onCloseClick
             * @returns {*} head as jQuery element
             */
            buildHead: function (title, onCloseClick) {
                onCloseClick = onCloseClick || this.closeWindow.bind(this);
                return windowComponents.buildHead(title, onCloseClick);
            },
            /**
             * Builds head with specified title
             * @param {string} title
             * @returns {*} head as jQuery element
             */
            buildNonClosableHead: function (title) {
                return windowComponents.buildNonClosableHead(title);
            },
            /**
             * Builds footer with specified buttons as it's block elements
             * @param {[]} buttons - array of jQuery elements
             * @returns {*} footer as jQuery element
             */
            buildFooter: function (buttons) {
                return windowComponents.buildFooter.apply(windowComponents, arguments);
            }
        };

        return WindowBuilder;
    }
);
<|endoftext|>


<commit_before>'use strict';
/**
 * `rawlist` type prompt
 */

const _ = {
  extend: require('lodash/extend'),
  isNumber: require('lodash/isNumber'),
  findIndex: require('lodash/findIndex'),
};
const chalk = require('chalk');
const { map, takeUntil } = require('rxjs/operators');
const Base = require('./base');
const Separator = require('../objects/separator');
const observe = require('../utils/events');
const Paginator = require('../utils/paginator');
const incrementListIndex = require('../utils/incrementListIndex');

class RawListPrompt extends Base {
  constructor(questions, rl, answers) {
    super(questions, rl, answers);

    if (!this.opt.choices) {
      this.throwParamError('choices');
    }

    this.opt.validChoices = this.opt.choices.filter(Separator.exclude);

    this.selected = 0;
    this.rawDefault = 0;

    _.extend(this.opt, {
      validate(val) {
        return val != null;
      },
    });

    const def = this.opt.default;
    if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
      this.selected = def;
      this.rawDefault = def;
    } else if (!_.isNumber(def) && def != null) {
      const index = _.findIndex(
        this.opt.choices.realChoices,
        ({ value }) => value === def
      );
      const safeIndex = Math.max(index, 0);
      this.selected = safeIndex;
      this.rawDefault = safeIndex;
    }

    // Make sure no default is set (so it won't be printed)
    this.opt.default = null;

    const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop;
    this.paginator = new Paginator(undefined, { isInfinite: shouldLoop });
  }

  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.getCurrentValue.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.normalizedUpKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onUpKey.bind(this));
    events.normalizedDownKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onDownKey.bind(this));
    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));
    // Init the prompt
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {RawListPrompt} self
   */

  render(error) {
    // Render question
    let message = this.getQuestion();
    let bottomContent = '';

    if (this.status === 'answered') {
      message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
    } else {
      const choicesStr = renderChoices(this.opt.choices, this.selected);
      message +=
        '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
      message += '\n  Answer: ';
    }
    message += this.rl.line;

    if (error) {
      bottomContent = '\n' + chalk.red('>> ') + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  getCurrentValue(index) {
    if (index == null) {
      index = this.rawDefault;
    } else if (index === '') {
      index = this.selected;
    } else {
      index -= 1;
    }

    const choice = this.opt.choices.getChoice(index);
    return choice ? choice.value : null;
  }

  onEnd(state) {
    this.status = 'answered';
    this.answer = state.value;

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError() {
    this.render('Please enter a valid index');
  }

  /**
   * When user press a key
   */

  onKeypress() {
    const index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;

    if (this.opt.choices.getChoice(index)) {
      this.selected = index;
    } else {
      this.selected = undefined;
    }
    this.render();
  }

  /**
   * When user press up key
   */

  onUpKey() {
    this.onArrowKey('up');
  }

  /**
   * When user press down key
   */

  onDownKey() {
    this.onArrowKey('down');
  }

  /**
   * When user press up or down key
   * @param {String} type Arrow type: up or down
   */

  onArrowKey(type) {
    this.selected = incrementListIndex(this.selected, type, this.opt);
    this.rl.line = String(this.selected + 1);
  }
}

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */

function renderChoices(choices, pointer) {
  let output = '';
  let separatorOffset = 0;

  choices.forEach((choice, i) => {
    output += '\n  ';

    if (choice.type === 'separator') {
      separatorOffset++;
      output += ' ' + choice;
      return;
    }

    const index = i - separatorOffset;
    let display = index + 1 + ') ' + choice.name;
    if (index === pointer) {
      display = chalk.cyan(display);
    }

    output += display;
  });

  return output;
}

module.exports = RawListPrompt;
<commit_msg>fix of #1042 rawlist throw (#1045)<commit_after>'use strict';
/**
 * `rawlist` type prompt
 */

const _ = {
  extend: require('lodash/extend'),
  isNumber: require('lodash/isNumber'),
  findIndex: require('lodash/findIndex'),
};
const chalk = require('chalk');
const { map, takeUntil } = require('rxjs/operators');
const Base = require('./base');
const Separator = require('../objects/separator');
const observe = require('../utils/events');
const Paginator = require('../utils/paginator');
const incrementListIndex = require('../utils/incrementListIndex');

class RawListPrompt extends Base {
  constructor(questions, rl, answers) {
    super(questions, rl, answers);

    if (!this.opt.choices) {
      this.throwParamError('choices');
    }

    this.opt.validChoices = this.opt.choices.filter(Separator.exclude);

    this.selected = 0;
    this.rawDefault = 0;

    _.extend(this.opt, {
      validate(val) {
        return val != null;
      },
    });

    const def = this.opt.default;
    if (_.isNumber(def) && def >= 0 && def < this.opt.choices.realLength) {
      this.selected = def;
      this.rawDefault = def;
    } else if (!_.isNumber(def) && def != null) {
      const index = _.findIndex(
        this.opt.choices.realChoices,
        ({ value }) => value === def
      );
      const safeIndex = Math.max(index, 0);
      this.selected = safeIndex;
      this.rawDefault = safeIndex;
    }

    // Make sure no default is set (so it won't be printed)
    this.opt.default = null;

    const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop;
    this.paginator = new Paginator(undefined, { isInfinite: shouldLoop });
  }

  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.getCurrentValue.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.normalizedUpKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onUpKey.bind(this));
    events.normalizedDownKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onDownKey.bind(this));
    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));
    // Init the prompt
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {RawListPrompt} self
   */

  render(error) {
    // Render question
    let message = this.getQuestion();
    let bottomContent = '';

    if (this.status === 'answered') {
      message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
    } else {
      const choicesStr = renderChoices(this.opt.choices, this.selected);
      message +=
        '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
      message += '\n  Answer: ';
    }
    message += this.rl.line;

    if (error) {
      bottomContent = '\n' + chalk.red('>> ') + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  getCurrentValue(index) {
    if (index == null) {
      index = this.rawDefault;
    } else if (index === '') {
      this.selected = this.selected ?? -1;
      index = this.selected;
    } else {
      index -= 1;
    }

    const choice = this.opt.choices.getChoice(index);
    return choice ? choice.value : null;
  }

  onEnd(state) {
    this.status = 'answered';
    this.answer = state.value;

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError() {
    this.render('Please enter a valid index');
  }

  /**
   * When user press a key
   */

  onKeypress() {
    const index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;

    if (this.opt.choices.getChoice(index)) {
      this.selected = index;
    } else {
      this.selected = undefined;
    }
    this.render();
  }

  /**
   * When user press up key
   */

  onUpKey() {
    this.onArrowKey('up');
  }

  /**
   * When user press down key
   */

  onDownKey() {
    this.onArrowKey('down');
  }

  /**
   * When user press up or down key
   * @param {String} type Arrow type: up or down
   */

  onArrowKey(type) {
    this.selected = incrementListIndex(this.selected, type, this.opt);
    this.rl.line = String(this.selected + 1);
  }
}

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */

function renderChoices(choices, pointer) {
  let output = '';
  let separatorOffset = 0;

  choices.forEach((choice, i) => {
    output += '\n  ';

    if (choice.type === 'separator') {
      separatorOffset++;
      output += ' ' + choice;
      return;
    }

    const index = i - separatorOffset;
    let display = index + 1 + ') ' + choice.name;
    if (index === pointer) {
      display = chalk.cyan(display);
    }

    output += display;
  });

  return output;
}

module.exports = RawListPrompt;
<|endoftext|>


<commit_before>/*jshint strict: false */

// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoView
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2013 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Daniel H. Larkin
// / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var arangosh = require('@arangodb/arangosh');

// //////////////////////////////////////////////////////////////////////////////
// / @brief constructor
// //////////////////////////////////////////////////////////////////////////////

function ArangoView (database, data) {
  this._database = database;
  this._dbName = database._name();
  this._dbPrefix = '/_db/' + encodeURIComponent(database._name());

  if (typeof data === 'string') {
    this._id = null;
    this._name = data;
    this._type = null;
  }
  else if (data !== undefined) {
    this._id = data.id;
    this._name = data.name;
    this._type = data.type;
  } else {
    this._id = null;
    this._name = null;
    this._type = null;
  }
}

exports.ArangoView = ArangoView;

var ArangoError = require('@arangodb').ArangoError;

// //////////////////////////////////////////////////////////////////////////////
// / @brief append the waitForSync parameter to a URL
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._appendSyncParameter = function (url, waitForSync) {
  if (waitForSync) {
    if (url.indexOf('?') === -1) {
      url += '?';
    }else {
      url += '&';
    }
    url += 'waitForSync=true';
  }
  return url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief append some boolean parameter to a URL
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._appendBoolParameter = function (url, name, val) {
  if (url.indexOf('?') === -1) {
    url += '?';
  }else {
    url += '&';
  }
  url += name + (val ? '=true' : '=false');
  return url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief prefix a URL with the database name of the view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._prefixurl = function (url) {
  if (url.substr(0, 5) === '/_db/') {
    return url;
  }

  if (url[0] === '/') {
    return this._dbPrefix + url;
  }
  return this._dbPrefix + '/' + url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the base url for view usage
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._baseurl = function (suffix) {
  var url = this._database._viewurl(this.name());

  if (suffix) {
    url += '/' + suffix;
  }

  return this._prefixurl(url);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief converts into an array
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.toArray = function () {
  return this.all().toArray();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief print the help for ArangoView
// //////////////////////////////////////////////////////////////////////////////

var helpArangoView = arangosh.createHelpHeadline('ArangoView help') +
  'ArangoView constructor:                                             ' + '\n' +
  ' > view = db._view("myview");                                       ' + '\n' +
  ' > col = db._createView("myview", "type", {[properties]});          ' + '\n' +
  '                                                                          ' + '\n' +
  'Administration Functions:                                                 ' + '\n' +
  '  name()                                view name                   ' + '\n' +
  '  type()                                type of the view            ' + '\n' +
  '  properties()                          show view properties        ' + '\n' +
  '  drop()                                delete a view               ' + '\n' +
  '  _help()                               this help                         ' + '\n' +
  '                                                                          ' + '\n' +
  'Attributes:                                                               ' + '\n' +
  '  _database                             database object                   ' + '\n' +
  '  _id                                   view identifier             ';

ArangoView.prototype._help = function () {
  internal.print(helpArangoView);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets the name of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.name = function () {
  if (this._name === null) {
    this.refresh();
  }

  return this._name;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets the type of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.type = function () {
  if (this._type === null) {
    this.refresh();
  }

  return this._type;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets or sets the properties of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.properties = function (properties, partialUpdate) {
  var requestResult;

  if (properties === undefined) {
    requestResult = this._database._connection.GET(this._baseurl('properties'));
  } else if (partialUpdate === undefined || partialUpdate === true) {
    requestResult = this._database._connection.PATCH(
      this._baseurl('properties'), JSON.stringify(properties)
    );
  } else {
    requestResult = this._database._connection.PUT(
      this._baseurl('properties'), JSON.stringify(properties)
    );
  }

  arangosh.checkRequestResult(requestResult);

  return requestResult;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief drops a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.drop = function () {
  var requestResult = this._database._connection.DELETE(this._baseurl());

  if (requestResult !== null
    && requestResult.error === true
    && requestResult.errorNum !== internal.errors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code) {
    // check error in case we got anything else but "view not found"
    arangosh.checkRequestResult(requestResult);
  } else {
    this._database._unregisterView(this._name);
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief renames a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.rename = function (name) {
  var body = { name: name };
  var requestResult = this._database._connection.PUT(this._baseurl('rename'), JSON.stringify(body));

  arangosh.checkRequestResult(requestResult);

  this._database._renameView(this._name, name);
  this._name = name;
};
<commit_msg>restrict attributes (#6066)<commit_after>/*jshint strict: false */

// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoView
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2013 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Daniel H. Larkin
// / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var arangosh = require('@arangodb/arangosh');

// //////////////////////////////////////////////////////////////////////////////
// / @brief constructor
// //////////////////////////////////////////////////////////////////////////////

function ArangoView (database, data) {
  this._database = database;
  this._dbName = database._name();
  this._dbPrefix = '/_db/' + encodeURIComponent(database._name());

  if (typeof data === 'string') {
    this._id = null;
    this._name = data;
    this._type = null;
  }
  else if (data !== undefined) {
    this._id = data.id;
    this._name = data.name;
    this._type = data.type;
  } else {
    this._id = null;
    this._name = null;
    this._type = null;
  }
}

exports.ArangoView = ArangoView;

var ArangoError = require('@arangodb').ArangoError;

// //////////////////////////////////////////////////////////////////////////////
// / @brief append the waitForSync parameter to a URL
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._appendSyncParameter = function (url, waitForSync) {
  if (waitForSync) {
    if (url.indexOf('?') === -1) {
      url += '?';
    }else {
      url += '&';
    }
    url += 'waitForSync=true';
  }
  return url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief append some boolean parameter to a URL
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._appendBoolParameter = function (url, name, val) {
  if (url.indexOf('?') === -1) {
    url += '?';
  }else {
    url += '&';
  }
  url += name + (val ? '=true' : '=false');
  return url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief prefix a URL with the database name of the view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._prefixurl = function (url) {
  if (url.substr(0, 5) === '/_db/') {
    return url;
  }

  if (url[0] === '/') {
    return this._dbPrefix + url;
  }
  return this._dbPrefix + '/' + url;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the base url for view usage
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype._baseurl = function (suffix) {
  var url = this._database._viewurl(this.name());

  if (suffix) {
    url += '/' + suffix;
  }

  return this._prefixurl(url);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief converts into an array
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.toArray = function () {
  return this.all().toArray();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief print the help for ArangoView
// //////////////////////////////////////////////////////////////////////////////

var helpArangoView = arangosh.createHelpHeadline('ArangoView help') +
  'ArangoView constructor:                                             ' + '\n' +
  ' > view = db._view("myview");                                       ' + '\n' +
  ' > col = db._createView("myview", "type", {[properties]});          ' + '\n' +
  '                                                                          ' + '\n' +
  'Administration Functions:                                                 ' + '\n' +
  '  name()                                view name                   ' + '\n' +
  '  type()                                type of the view            ' + '\n' +
  '  properties()                          show view properties        ' + '\n' +
  '  drop()                                delete a view               ' + '\n' +
  '  _help()                               this help                         ' + '\n' +
  '                                                                          ' + '\n' +
  'Attributes:                                                               ' + '\n' +
  '  _database                             database object                   ' + '\n' +
  '  _id                                   view identifier             ';

ArangoView.prototype._help = function () {
  internal.print(helpArangoView);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets the name of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.name = function () {
  if (this._name === null) {
    this.refresh();
  }

  return this._name;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets the type of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.type = function () {
  if (this._type === null) {
    this.refresh();
  }

  return this._type;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief gets or sets the properties of a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.properties = function (properties, partialUpdate) {
  var requestResult;

  if (properties === undefined) {
    requestResult = this._database._connection.GET(this._baseurl('properties'));
  } else if (partialUpdate === undefined || partialUpdate === true) {
    requestResult = this._database._connection.PATCH(
      this._baseurl('properties'), JSON.stringify(properties)
    );
  } else {
    requestResult = this._database._connection.PUT(
      this._baseurl('properties'), JSON.stringify(properties)
    );
  }

  arangosh.checkRequestResult(requestResult);

  const attributes = {
    'commit': true,
    'links': true,
    'locale': true
  };

  var result = {};
  for (a in attributes) {
    if (attributes.hasOwnProperty(a) &&
      requestResult.hasOwnProperty(a) &&
      requestResult[a] !== undefined) {
      result[a] = requestResult[a];
    }
  }

  return result;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief drops a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.drop = function () {
  var requestResult = this._database._connection.DELETE(this._baseurl());

  if (requestResult !== null
    && requestResult.error === true
    && requestResult.errorNum !== internal.errors.ERROR_ARANGO_DATA_SOURCE_NOT_FOUND.code) {
    // check error in case we got anything else but "view not found"
    arangosh.checkRequestResult(requestResult);
  } else {
    this._database._unregisterView(this._name);
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief renames a view
// //////////////////////////////////////////////////////////////////////////////

ArangoView.prototype.rename = function (name) {
  var body = { name: name };
  var requestResult = this._database._connection.PUT(this._baseurl('rename'), JSON.stringify(body));

  arangosh.checkRequestResult(requestResult);

  this._database._renameView(this._name, name);
  this._name = name;
};
<|endoftext|>


<commit_before>import React from "react";
import { renderTree } from "@times-components/markup-forest";
import coreRenderers from "@times-components/markup";
import props from "./key-facts-text-prop-types";
import { Text, KeyFactTextLink, BulletContainer, Bullet } from "./styles";

const getTitle = data => {
  if (data.children.length === 1) return data.children[0].attributes.value;

  const linkText = data.children.map(child => child.attributes.value);
  const title = linkText.join(" ");
  return title.length > 0 ? title : " ";
};

const KeyFactsText = ({ listIndex, keyFactItem, fireAnalyticsEvent }) => (
  <BulletContainer key={`key-facts-${listIndex}`}>
    <Bullet />
    <Text>
      {keyFactItem.children.map((data, listItemIndex) =>
        renderTree(
          data,
          {
            ...coreRenderers,
            link(key, attributes, renderedChildren) {
              const { href: url } = attributes;
              const title = getTitle(data);
              const newURL = `${window.location.href}/${url}`;

              return (
                <KeyFactTextLink
                  key={key}
                  onClick={() => {
                    if (fireAnalyticsEvent) {
                      fireAnalyticsEvent({
                        action: "Clicked",
                        attrs: {
                          event_navigation_name:
                            "in-article component clicked : key moments",
                          event_navigation_browsing_method: "click",
                          article_parent_name: title
                        }
                      });
                    }
                  }}
                  href={newURL}
                >
                  {renderedChildren}
                </KeyFactTextLink>
              );
            }
          },
          `key-facts-${listIndex}-${listItemIndex}`
        )
      )}
    </Text>
  </BulletContainer>
);

KeyFactsText.propTypes = props;

export default KeyFactsText;
export { getTitle };
<commit_msg>fix(TDP-1577): key facts anchor links (#2894)<commit_after>import React from "react";
import { renderTree } from "@times-components/markup-forest";
import coreRenderers from "@times-components/markup";
import props from "./key-facts-text-prop-types";
import { Text, KeyFactTextLink, BulletContainer, Bullet } from "./styles";

const getTitle = data => {
  if (data.children.length === 1) return data.children[0].attributes.value;

  const linkText = data.children.map(child => child.attributes.value);
  const title = linkText.join(" ");
  return title.length > 0 ? title : " ";
};

const KeyFactsText = ({ listIndex, keyFactItem, fireAnalyticsEvent }) => (
  <BulletContainer key={`key-facts-${listIndex}`}>
    <Bullet />
    <Text>
      {keyFactItem.children.map((data, listItemIndex) =>
        renderTree(
          data,
          {
            ...coreRenderers,
            link(key, attributes, renderedChildren) {
              const { href: url } = attributes;
              const title = getTitle(data);
              const fullUrl =
                typeof window !== "undefined"
                  ? `${window.location.href}/${url}`
                  : url;

              return (
                <KeyFactTextLink
                  key={key}
                  onClick={() => {
                    if (fireAnalyticsEvent) {
                      fireAnalyticsEvent({
                        action: "Clicked",
                        attrs: {
                          event_navigation_name:
                            "in-article component clicked : key moments",
                          event_navigation_browsing_method: "click",
                          article_parent_name: title
                        }
                      });
                    }
                  }}
                  href={fullUrl}
                >
                  {renderedChildren}
                </KeyFactTextLink>
              );
            }
          },
          `key-facts-${listIndex}-${listItemIndex}`
        )
      )}
    </Text>
  </BulletContainer>
);

KeyFactsText.propTypes = props;

export default KeyFactsText;
export { getTitle };
<|endoftext|>


<commit_before>/**
 * Host.js is a wrapper object for details about a host machine in the cluster. A host may have one or more services
 * running on it, and we can collect and display host information like CPU usage, free disk, etc...
 */
define(["knockout", "jquery", "bootbox", "app", "build", "chart"], function(ko, $, bootbox, app, builder, Chart) {

   return Host; // not using new means this returns a constructor function (ie class)

   // Host Model
   function Host(hostname) {
      var self = this;

      self.services = ko.observableArray([]); // services running on this host

      self.hostname = hostname;
      self.cores = ko.observable(0);
      self.disk = ko.observable(0);
      self.memory = ko.observable(0);
      self.load = ko.observable(0);

      self.loadChart = new Chart('host-chart-load-' + hostname, self.load);
      self.diskChart = new Chart('host-chart-disk-' + hostname, self.disk);

      self.loadChart.series.maxY = 2;
      self.diskChart.series.maxY = 1024 * 1024 * 1024;

      // start collecting details
      updateHostDetails();
      updateHostStats();

      // polls a service from this host for host details
      function updateHostDetails() {
         var s = getAvailableService();
         if (s) {
            app.server.sendTo("HostInfo", {}, s.entityId, function(result) {
               if (!result.isError()) {
                  self.cores(result.numCores);
               }
            });
         } else {
            setTimeout(updateHostDetails, 1000);
         }
      }

      // polls a service from this host for host details
      function updateHostStats() {
         var s = getAvailableService();
         if (s) {
            app.server.sendTo("HostStats", {}, s.entityId, function(result) {
               if (!result.isError()) {
                  // TODO: update model & charts
                  self.load(result.load.toFixed(1));
                  self.disk(result.disk);
               }
               setTimeout(updateHostStats, 5000);
               self.loadChart.updatePlot(60000, self.load());
               self.diskChart.updatePlot(60000, self.disk());
            });
         } else {
            setTimeout(updateHostStats, 1000);
         }
      }

      // returns any available service running on this host
      function getAvailableService() {
         for (var i = 0; i < self.services().length; i++) {
            var s = self.services()[i];
            if (!s.isGone()) {
               return s;
            }
         }
         return null;
      }

      self.findService = function(entityId) {
         for (var i = 0; i < self.services().length; i++) {
            if (self.services()[i].entityId == entityId) {
               return self.services()[i];
            }
         }
         return null;
      }

      self.addService = function(s) {
         var old = self.findService(s.entityId);
         if (old) {
            self.services.remove(old);
         }
         self.services.push(s);
         self.services.sort(compareServices);
      }

      self.removeService = function(s) {
         self.services.remove(s);
      }

      function compareServices(a, b) {
         return (a.entityId - b.entityId);
      }

      self.diskLabel = ko.pureComputed(function() {
         var d = self.disk() / (1024 * 1024); // mb
         if (d > 10000) {
            return (d / 1024).toFixed(1) + " gb";
         }
         return d.toFixed(1) + " mb";
      });

      self.onClearAllErrors = function() {
         for (var i = 0; i < self.services().length; i++) {
            self.services()[i].clearErrors();
         }
      };
   }
});<commit_msg>round robin the host health calls in admin<commit_after>/**
 * Host.js is a wrapper object for details about a host machine in the cluster. A host may have one or more services
 * running on it, and we can collect and display host information like CPU usage, free disk, etc...
 */
define(["knockout", "jquery", "bootbox", "app", "build", "chart"], function(ko, $, bootbox, app, builder, Chart) {

   return Host; // not using new means this returns a constructor function (ie class)

   // Host Model
   function Host(hostname) {
      var self = this;

      self.services = ko.observableArray([]); // services running on this host

      self.hostname = hostname;
      self.cores = ko.observable(0);
      self.disk = ko.observable(0);
      self.memory = ko.observable(0);
      self.load = ko.observable(0);

      self.loadChart = new Chart('host-chart-load-' + hostname, self.load);
      self.diskChart = new Chart('host-chart-disk-' + hostname, self.disk);

      self.loadChart.series.maxY = 2;
      self.diskChart.series.maxY = 1024 * 1024 * 1024;

      // start collecting details
      updateHostDetails();
      updateHostStats();

      // polls a service from this host for host details
      function updateHostDetails() {
         var s = getAvailableService();
         if (s) {
            app.server.sendTo("HostInfo", {}, s.entityId, function(result) {
               if (!result.isError()) {
                  self.cores(result.numCores);
               }
            });
         } else {
            setTimeout(updateHostDetails, 1000);
         }
      }

      // polls a service from this host for host details
      function updateHostStats() {
         var s = getAvailableService();
         if (s) {
            app.server.sendTo("HostStats", {}, s.entityId, function(result) {
               if (!result.isError()) {
                  // TODO: update model & charts
                  self.load(result.load.toFixed(1));
                  self.disk(result.disk);
               }
               setTimeout(updateHostStats, 5000);
               self.loadChart.updatePlot(60000, self.load());
               self.diskChart.updatePlot(60000, self.disk());
            });
         } else {
            setTimeout(updateHostStats, 1000);
         }
      }

      var iter = 0;
      // returns any available service running on this host
      function getAvailableService() {
         for (var i = 0; i < self.services().length; i++) {
            var s = self.services()[(i + iter++) % self.services().length];
            if (!s.isGone()) {
               return s;
            }
         }
         return null;
      }

      self.findService = function(entityId) {
         for (var i = 0; i < self.services().length; i++) {
            if (self.services()[i].entityId == entityId) {
               return self.services()[i];
            }
         }
         return null;
      }

      self.addService = function(s) {
         var old = self.findService(s.entityId);
         if (old) {
            self.services.remove(old);
         }
         self.services.push(s);
         self.services.sort(compareServices);
      }

      self.removeService = function(s) {
         self.services.remove(s);
      }

      function compareServices(a, b) {
         return (a.entityId - b.entityId);
      }

      self.diskLabel = ko.pureComputed(function() {
         var d = self.disk() / (1024 * 1024); // mb
         if (d > 10000) {
            return (d / 1024).toFixed(1) + " gb";
         }
         return d.toFixed(1) + " mb";
      });

      self.onClearAllErrors = function() {
         for (var i = 0; i < self.services().length; i++) {
            self.services()[i].clearErrors();
         }
      };
   }
});<|endoftext|>


<commit_before>/* global ArangoServerState */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / @brief Replication management
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var ERRORS = internal.errors;
var endpointToURL = require('@arangodb/cluster').endpointToURL;
var request;
if (ArangoServerState.role() === 'PRIMARY') {
  request = require('@arangodb/request').clusterRequest;
} else {
  request = require('@arangodb/request').request;
}

var logger = { };
var applier = { };

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the replication logger state
// //////////////////////////////////////////////////////////////////////////////

logger.state = function () {
  return internal.getStateReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the tick ranges provided by the replication logger
// //////////////////////////////////////////////////////////////////////////////

logger.tickRanges = function () {
  return internal.tickRangesReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the first tick that can be provided by the replication logger
// //////////////////////////////////////////////////////////////////////////////

logger.firstTick = function () {
  return internal.firstTickReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief starts the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.start = function (initialTick, barrierId) {
  if (initialTick === undefined) {
    return internal.startReplicationApplier();
  }

  return internal.startReplicationApplier(initialTick, barrierId);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief shuts down the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.shutdown = applier.stop = function () {
  return internal.shutdownReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the replication applier state
// //////////////////////////////////////////////////////////////////////////////

applier.state = function () {
  return internal.getStateReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief stop the applier and "forget" all configuration
// //////////////////////////////////////////////////////////////////////////////

applier.forget = function () {
  return internal.forgetStateReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief returns the configuration of the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.properties = function (config) {
  if (config === undefined) {
    return internal.configureReplicationApplier();
  }

  return internal.configureReplicationApplier(config);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief performs a one-time synchronization with a remote endpoint
// //////////////////////////////////////////////////////////////////////////////

function sync (config) {
  return internal.synchronizeReplication(config);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief performs a one-time synchronization with a remote endpoint
// //////////////////////////////////////////////////////////////////////////////

function syncCollection (collection, config) {
  config = config || { };
  config.restrictType = 'include';
  config.restrictCollections = [ collection ];
  config.includeSystem = true;
  if (!config.hasOwnProperty('verbose')) {
    config.verbose = false;
  }

  return internal.synchronizeReplication(config);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief sets up the replication (all-in-one function for initial
// / synchronization and continuous replication)
// //////////////////////////////////////////////////////////////////////////////

function setupReplication (config) {
  config = config || { };
  if (!config.hasOwnProperty('autoStart')) {
    config.autoStart = true;
  }
  if (!config.hasOwnProperty('includeSystem')) {
    config.includeSystem = true;
  }
  if (!config.hasOwnProperty('verbose')) {
    config.verbose = false;
  }
  config.keepBarrier = true;

  try {
    // stop previous instance
    applier.stop();
  } catch (err) {}
  // remove existing configuration
  applier.forget();

  // run initial sync
  var result = internal.synchronizeReplication(config);

  // store applier configuration
  applier.properties(config);

  applier.start(result.lastLogTick, result.barrierId);
  return applier.state();
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief returns the server's id
// //////////////////////////////////////////////////////////////////////////////

function serverId () {
  return internal.serverId();
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief finishes off a synchronization of a single collection
// / `collection` is the name of a collection on the other server that should
// / have been synchronized using syncCollection (incremental or not)
// / recently. `from` is the tick as a string of that synchronization.
// / `config` can contain the following attributes:
// /   - "endpoint": endpoint of the other server
// / If one ensures that there is a read transaction ongoing on that collection
// / on the other server, then this function is guaranteed to bring the two
// / collections in perfect sync, until that read transaction is aborted.
// //////////////////////////////////////////////////////////////////////////////

var mType = {
  REPLICATION_COLLECTION_CREATE: 2000,
  REPLICATION_COLLECTION_DROP: 2001,
  REPLICATION_COLLECTION_RENAME: 2002,
  REPLICATION_COLLECTION_CHANGE: 2003,

  REPLICATION_INDEX_CREATE: 2100,
  REPLICATION_INDEX_DROP: 2101,

  REPLICATION_TRANSACTION_START: 2200,
  REPLICATION_TRANSACTION_COMMIT: 2201,
  REPLICATION_TRANSACTION_ABORT: 2202,

  REPLICATION_MARKER_DOCUMENT: 2300,
  REPLICATION_MARKER_EDGE: 2301,
  REPLICATION_MARKER_REMOVE: 2302
};

function syncCollectionFinalize (database, collname, from, config) {
  var url = endpointToURL(config.endpoint) + '/_db/' + database +
    '/_api/replication/logger-follow?collection=' + collname + '&from=';

  var coll = require('internal').db[collname];

  var transactions = {};

  function apply (entry) {
    var todo;

    function tryPostpone (entry) {
      if (entry.tid !== '0') {
        todo = transactions[entry.tid];
        if (Array.isArray(todo)) {
          entry.tid = '0';
          todo.push(entry);
          return true;
        }
      }
      return false;
    }

    if (entry.type === mType.REPLICATION_MARKER_DOCUMENT) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.insert(entry.data, {isRestore: true});
        return;
      } catch (err) {
        console.debug('syncCollectionFinalize: insert1', entry, JSON.stringify(err));
      }
      try {
        coll.replace(entry.data._key, entry.data, {isRestore: true});
      } catch (errx) {
        console.error('syncCollectionFinalize: replace1', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_MARKER_EDGE) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.insert(entry.data, {isRestore: true});
        return;
      } catch (err) {
        console.debug('syncCollectionFinalize: insert2', entry, JSON.stringify(err));
      }
      try {
        coll.replace(entry.data._key, entry.data, {isRestore: true});
      } catch (errx) {
        console.error('syncCollectionFinalize: replace2', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_MARKER_REMOVE) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.remove(entry.data._key);
      } catch (errx) {
        console.error('syncCollectionFinalize: remove', entry, JSON.stringify(errx));
        if (errx.errorNum !== ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
          throw errx;
        }
        // We swallow the NOT FOUND error here. It is possible that
        // a follower tries to synchronize to a leader with whom it
        // is already in sync. In that case there could have been a
        // synchronously replicated removal operation that has happened
        // whilst we were resynchronizing the shard. In this case, the
        // removal would have happened already.
      }
    } else if (entry.type === mType.REPLICATION_TRANSACTION_START) {
      transactions[entry.tid] = [];
    } else if (entry.type === mType.REPLICATION_TRANSACTION_COMMIT) {
      todo = transactions[entry.tid];
      if (Array.isArray(todo)) {
        for (var i = 0; i < todo.length; i++) {
          apply(todo[i]);
        }
      }
      delete transactions[entry.tid];
    } else if (entry.type === mType.REPLICATION_TRANSACTION_ABORT) {
      delete transactions[entry.tid];
    } else if (entry.type === mType.REPLICATION_INDEX_CREATE) {
      try {
        coll.ensureIndex(entry.data);
      } catch(errx) {
        console.error('syncCollectionFinalize: ensureIndex', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_INDEX_DROP) {
      try {
        coll.dropIndex(entry.data.id);
      } catch(errx) {
        console.error('syncCollectionFinalize: dropIndex', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_COLLECTION_CHANGE) {
      try {
        coll.properties(entry.data);
      } catch(errx) {
        console.error('syncCollectionFinalize: properties', entry, JSON.stringify(errx));
        throw errx;
      }
    } else {
      // all else, including dropping and creating the collection
      // throw 'Found collection drop, create or rename marker.';
      console.warn('Found collection drop, create or rename marker:', entry);
    }
  }

  while (true) {
    var chunk = request(url + from);
    if (chunk.statusCode !== 200 &&
      chunk.statusCode !== 204) {
      return {error: true, errorMessage: 'could not contact leader',
      response: chunk};
    }
    if (chunk.statusCode === 204 || chunk.body.length === 0) {
      break; // all done
    }

    var l = chunk.body.split('\n');
    l.pop();
    try {
      l = l.map(JSON.parse);
    } catch (err) {
      return {error: true, errorMessage: 'could not parse body',
              response: chunk, exception: err};
    }

    console.debug('Applying chunk:', l);
    try {
      for (var i = 0; i < l.length; i++) {
        apply(l[i]);
      }
    } catch (err2) {
      return {error: true, errorMessage: 'could not replicate body ops',
              response: chunk, exception: err2};
    }
    if (chunk.headers['x-arango-replication-checkmore'] !== 'true') {
      break; // all done
    }

    from = chunk.headers['x-arango-replication-lastincluded'];
  }
  return {error: false};
}

exports.logger = logger;
exports.applier = applier;
exports.sync = sync;
exports.syncCollection = syncCollection;
exports.setupReplication = setupReplication;
exports.serverId = serverId;
exports.syncCollectionFinalize = syncCollectionFinalize;
<commit_msg>Avoid log spam.<commit_after>/* global ArangoServerState */
'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / @brief Replication management
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2012 triagens GmbH, Cologne, Germany
// /
// / 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.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Copyright 2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var ERRORS = internal.errors;
var endpointToURL = require('@arangodb/cluster').endpointToURL;
var request;
if (ArangoServerState.role() === 'PRIMARY') {
  request = require('@arangodb/request').clusterRequest;
} else {
  request = require('@arangodb/request').request;
}

var logger = { };
var applier = { };

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the replication logger state
// //////////////////////////////////////////////////////////////////////////////

logger.state = function () {
  return internal.getStateReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the tick ranges provided by the replication logger
// //////////////////////////////////////////////////////////////////////////////

logger.tickRanges = function () {
  return internal.tickRangesReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the first tick that can be provided by the replication logger
// //////////////////////////////////////////////////////////////////////////////

logger.firstTick = function () {
  return internal.firstTickReplicationLogger();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief starts the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.start = function (initialTick, barrierId) {
  if (initialTick === undefined) {
    return internal.startReplicationApplier();
  }

  return internal.startReplicationApplier(initialTick, barrierId);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief shuts down the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.shutdown = applier.stop = function () {
  return internal.shutdownReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief return the replication applier state
// //////////////////////////////////////////////////////////////////////////////

applier.state = function () {
  return internal.getStateReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief stop the applier and "forget" all configuration
// //////////////////////////////////////////////////////////////////////////////

applier.forget = function () {
  return internal.forgetStateReplicationApplier();
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief returns the configuration of the replication applier
// //////////////////////////////////////////////////////////////////////////////

applier.properties = function (config) {
  if (config === undefined) {
    return internal.configureReplicationApplier();
  }

  return internal.configureReplicationApplier(config);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief performs a one-time synchronization with a remote endpoint
// //////////////////////////////////////////////////////////////////////////////

function sync (config) {
  return internal.synchronizeReplication(config);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief performs a one-time synchronization with a remote endpoint
// //////////////////////////////////////////////////////////////////////////////

function syncCollection (collection, config) {
  config = config || { };
  config.restrictType = 'include';
  config.restrictCollections = [ collection ];
  config.includeSystem = true;
  if (!config.hasOwnProperty('verbose')) {
    config.verbose = false;
  }

  return internal.synchronizeReplication(config);
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief sets up the replication (all-in-one function for initial
// / synchronization and continuous replication)
// //////////////////////////////////////////////////////////////////////////////

function setupReplication (config) {
  config = config || { };
  if (!config.hasOwnProperty('autoStart')) {
    config.autoStart = true;
  }
  if (!config.hasOwnProperty('includeSystem')) {
    config.includeSystem = true;
  }
  if (!config.hasOwnProperty('verbose')) {
    config.verbose = false;
  }
  config.keepBarrier = true;

  try {
    // stop previous instance
    applier.stop();
  } catch (err) {}
  // remove existing configuration
  applier.forget();

  // run initial sync
  var result = internal.synchronizeReplication(config);

  // store applier configuration
  applier.properties(config);

  applier.start(result.lastLogTick, result.barrierId);
  return applier.state();
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief returns the server's id
// //////////////////////////////////////////////////////////////////////////////

function serverId () {
  return internal.serverId();
}

// //////////////////////////////////////////////////////////////////////////////
// / @brief finishes off a synchronization of a single collection
// / `collection` is the name of a collection on the other server that should
// / have been synchronized using syncCollection (incremental or not)
// / recently. `from` is the tick as a string of that synchronization.
// / `config` can contain the following attributes:
// /   - "endpoint": endpoint of the other server
// / If one ensures that there is a read transaction ongoing on that collection
// / on the other server, then this function is guaranteed to bring the two
// / collections in perfect sync, until that read transaction is aborted.
// //////////////////////////////////////////////////////////////////////////////

var mType = {
  REPLICATION_COLLECTION_CREATE: 2000,
  REPLICATION_COLLECTION_DROP: 2001,
  REPLICATION_COLLECTION_RENAME: 2002,
  REPLICATION_COLLECTION_CHANGE: 2003,

  REPLICATION_INDEX_CREATE: 2100,
  REPLICATION_INDEX_DROP: 2101,

  REPLICATION_TRANSACTION_START: 2200,
  REPLICATION_TRANSACTION_COMMIT: 2201,
  REPLICATION_TRANSACTION_ABORT: 2202,

  REPLICATION_MARKER_DOCUMENT: 2300,
  REPLICATION_MARKER_EDGE: 2301,
  REPLICATION_MARKER_REMOVE: 2302
};

function syncCollectionFinalize (database, collname, from, config) {
  var url = endpointToURL(config.endpoint) + '/_db/' + database +
    '/_api/replication/logger-follow?collection=' + collname + '&from=';

  var coll = require('internal').db[collname];

  var transactions = {};

  function apply (entry) {
    var todo;

    function tryPostpone (entry) {
      if (entry.tid !== '0') {
        todo = transactions[entry.tid];
        if (Array.isArray(todo)) {
          entry.tid = '0';
          todo.push(entry);
          return true;
        }
      }
      return false;
    }

    if (entry.type === mType.REPLICATION_MARKER_DOCUMENT) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.insert(entry.data, {isRestore: true});
        return;
      } catch (err) {
        console.debug('syncCollectionFinalize: insert1', entry, JSON.stringify(err));
      }
      try {
        coll.replace(entry.data._key, entry.data, {isRestore: true});
      } catch (errx) {
        console.error('syncCollectionFinalize: replace1', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_MARKER_EDGE) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.insert(entry.data, {isRestore: true});
        return;
      } catch (err) {
        console.debug('syncCollectionFinalize: insert2', entry, JSON.stringify(err));
      }
      try {
        coll.replace(entry.data._key, entry.data, {isRestore: true});
      } catch (errx) {
        console.error('syncCollectionFinalize: replace2', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_MARKER_REMOVE) {
      if (tryPostpone(entry)) {
        return;
      }
      try {
        coll.remove(entry.data._key);
      } catch (errx) {
        console.error('syncCollectionFinalize: remove', entry, JSON.stringify(errx));
        if (errx.errorNum !== ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
          throw errx;
        }
        // We swallow the NOT FOUND error here. It is possible that
        // a follower tries to synchronize to a leader with whom it
        // is already in sync. In that case there could have been a
        // synchronously replicated removal operation that has happened
        // whilst we were resynchronizing the shard. In this case, the
        // removal would have happened already.
      }
    } else if (entry.type === mType.REPLICATION_TRANSACTION_START) {
      transactions[entry.tid] = [];
    } else if (entry.type === mType.REPLICATION_TRANSACTION_COMMIT) {
      todo = transactions[entry.tid];
      if (Array.isArray(todo)) {
        for (var i = 0; i < todo.length; i++) {
          apply(todo[i]);
        }
      }
      delete transactions[entry.tid];
    } else if (entry.type === mType.REPLICATION_TRANSACTION_ABORT) {
      delete transactions[entry.tid];
    } else if (entry.type === mType.REPLICATION_INDEX_CREATE) {
      try {
        coll.ensureIndex(entry.data);
      } catch(errx) {
        console.error('syncCollectionFinalize: ensureIndex', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_INDEX_DROP) {
      try {
        coll.dropIndex(entry.data.id);
      } catch(errx) {
        console.error('syncCollectionFinalize: dropIndex', entry, JSON.stringify(errx));
        throw errx;
      }
    } else if (entry.type === mType.REPLICATION_COLLECTION_CHANGE) {
      try {
        coll.properties(entry.data);
      } catch(errx) {
        console.error('syncCollectionFinalize: properties', entry, JSON.stringify(errx));
        throw errx;
      }
    } else {
      // all else, including dropping and creating the collection
      // throw 'Found collection drop, create or rename marker.';
      console.debug('Found collection drop, create or rename marker:', entry);
    }
  }

  while (true) {
    var chunk = request(url + from);
    if (chunk.statusCode !== 200 &&
      chunk.statusCode !== 204) {
      return {error: true, errorMessage: 'could not contact leader',
      response: chunk};
    }
    if (chunk.statusCode === 204 || chunk.body.length === 0) {
      break; // all done
    }

    var l = chunk.body.split('\n');
    l.pop();
    try {
      l = l.map(JSON.parse);
    } catch (err) {
      return {error: true, errorMessage: 'could not parse body',
              response: chunk, exception: err};
    }

    console.debug('Applying chunk:', l);
    try {
      for (var i = 0; i < l.length; i++) {
        apply(l[i]);
      }
    } catch (err2) {
      return {error: true, errorMessage: 'could not replicate body ops',
              response: chunk, exception: err2};
    }
    if (chunk.headers['x-arango-replication-checkmore'] !== 'true') {
      break; // all done
    }

    from = chunk.headers['x-arango-replication-lastincluded'];
  }
  return {error: false};
}

exports.logger = logger;
exports.applier = applier;
exports.sync = sync;
exports.syncCollection = syncCollection;
exports.setupReplication = setupReplication;
exports.serverId = serverId;
exports.syncCollectionFinalize = syncCollectionFinalize;
<|endoftext|>