<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

/*
 * 'sdcadm post-setup underlay-nics'
 */

var p = console.log;
var util = require('util');

var assert = require('assert-plus');
var vasync = require('vasync');

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

//---- internal support stuff

function UnderlayNics() {}
UnderlayNics.prototype.name = 'underlay-nics';

UnderlayNics.prototype.help = (
    'Provisions underlay NICs on the provided underlay network for the\n' +
    'given Compute Node(s).'
);

UnderlayNics.prototype.execute = function (opts, cb) {
    assert.object(opts, 'opts');
    assert.object(opts.sdcadm, 'opts.sdcadm');
    assert.object(opts.log, 'opts.log');
    assert.func(opts.progress, 'opts.progress');
    assert.string(opts.net_uuid, 'opts.net_uuid');
    assert.optionalArrayOfString(opts.cns, 'opts.cns');

    var progress = opts.progress;
    var sdcadm = opts.sdcadm;
    var log = opts.log;

    var MIN_VALID_NAPI_VERSION = '20150312';
    var img;
    var cns = [];
    var cns2Update = [];
    // Used by sdcadm history:
    var changes = [];
    var hist;
    // Given we may have errors for some CNs, and not from some others, we
    // need to store errors and report at end:
    var errs = [];

    vasync.pipeline({funcs: [
        function getNapiVmImgs(_, next) {
            sdcadm.getImgsForSvcVms({
                svc: 'napi'
            }, function (err, obj) {
                if (err) {
                    return next(err);
                }
                img = obj.imgs[0];
                return next();
            });
        },

        function checkMinNapiVersion(_, next) {
            progress('Checking for minimum NAPI version');
            var splitVersion = img.version.split('-');
            var validNapi = false;

            if (splitVersion[0] === 'master') {
                validNapi = splitVersion[1].substr(0, 8) >=
                    MIN_VALID_NAPI_VERSION;
            } else if (splitVersion[0] === 'release') {
                validNapi = splitVersion[1] >= MIN_VALID_NAPI_VERSION;
            }

            if (!validNapi) {
                return next(new errors.ValidationError(new Error('Datacenter ' +
                    'does not have the minimum NAPI version needed for adding' +
                    ' underlay nics to CNs. ' +
                    'Please try again after upgrading NAPI')));
            }

            return next();
        },

        function validateNetwork(_, next) {
            progress('Verifying the provided network exists');
            sdcadm.napi.getNetwork(opts.net_uuid, function (err, net) {
                if (err) {
                    if (err.statusCode === 404) {
                        return next(new errors.UsageError('The provided ' +
                                    'network UUID cannot be found in NAPI'));
                    } else {
                        return next(new errors.SDCClientError(err, 'napi'));
                    }
                }
                return next();
            });
        },

        function validateServers(_, next) {
            progress('Verifying the provided Server(s) UUID(s)');
            sdcadm.cnapi.listServers({
                setup: true
            }, function (err, recs) {
                if (err) {
                    return next(new errors.SDCClientError(err, 'cnapi'));
                }
                var hostnames = {};
                recs.forEach(function (r) {
                    hostnames[r.hostname] = r.uuid;
                });
                // Translate hostnames to UUIDs when possible:
                opts.cns = opts.cns.map(function (cn) {
                    if (Object.keys(hostnames).indexOf(cn) !== -1) {
                        return hostnames[cn];
                    } else {
                        return cn;
                    }
                });

                var uuids = recs.map(function (r) {
                    return (r.uuid);
                });

                // Print a message if any of the provided CNs is not valid
                opts.cns.forEach(function (cn) {
                    if (uuids.indexOf(cn) === -1) {
                        progress('Skipping invalid CN ' + cn);
                    } else {
                        cns.push(cn);
                    }
                });
                next();
            });
        },

        function filterServersWithUnderlayNicOnTheSameNetwork(_, next) {
            function checkServerNics(cn, nextCn) {
                sdcadm.napi.listNics({
                    owner_uuid: sdcadm.config.ufds_admin_uuid,
                    belongs_to_uuid: cn,
                    belongs_to_type: 'server',
                    network_uuid: opts.net_uuid
                }, function (err, nics) {
                    if (err) {
                        errs.push(new errors.SDCClientError(err, 'napi'));
                        nextCn(err);
                    } else {
                        if (!nics.length) {
                            cns2Update.push(cn);
                            return nextCn();
                        }
                        var hasUnderlayNic = false;
                        nics.forEach(function (nic) {
                            if (nic.underlay) {
                                progress('Skipping CN %s (already has an ' +
                                        'underlay nic in network %s)',
                                        cn, opts.net_uuid);
                                hasUnderlayNic = true;
                            }
                        });
                        if (!hasUnderlayNic) {
                            cns2Update.push(cn);
                        }
                        return nextCn();
                    }
                });
            }
            var existsQueue = vasync.queue(checkServerNics, 5);
            existsQueue.once('end', next);
            existsQueue.push(cns);
            existsQueue.close();
        },

        function saveChangesToHistory(_, next) {
            if (!cns2Update.length) {
                return next();
            }
            changes.push({
                service: {
                    name: 'napi'
                },
                type: 'underlay-nics',
                servers: cns,
                network: opts.net_uuid
            });

            sdcadm.history.saveHistory({
                changes: changes
            }, function (err, hst) {
                if (err) {
                    return next(err);
                }
                hist = hst;
                return next();
            });
        },

        function provisionNics(_, next) {
            if (!cns2Update.length) {
                return next();
            }
            function provisionUnderlayNic(cn, nextNic) {
                var route = util.format('/networks/%s/nics', opts.net_uuid);
                sdcadm.napi.post(route, {
                    owner_uuid: sdcadm.config.ufds_admin_uuid,
                    belongs_to_uuid: cn,
                    belongs_to_type: 'server',
                    underlay: true
                }, function (err, res) {
                    if (err) {
                        errs.push(new errors.SDCClientError(err, 'napi'));
                        return nextNic(err);
                    }
                    progress('Underlay NIC created for CN %s', cn);
                    nextNic();
                });
            }

            var nicsQueue = vasync.queue(provisionUnderlayNic, 5);
            nicsQueue.once('end', next);
            nicsQueue.push(cns);
            nicsQueue.close();
        }
    ]}, function (err) {
        if (errs.length) {
            err = new errors.MultiError(errs);
        }

        if (!hist) {
            log.debug('History not set for underlay-nics');
            return cb(err);
        }

        if (!cns2Update.length) {
            progress('All the provided CNs already had an underlay NIC' +
                    ' on the provided network');
            return cb(err);
        }

        if (err) {
            hist.error = err;
        }

        sdcadm.history.updateHistory(hist, function (err2) {
            if (err) {
                return cb(err);
            } else if (err2) {
                return cb(err2);
            } else {
                return cb();
            }
        });
    });
};

//---- CLI

function do_underlay_nics(subcmd, opts, args, cb) {
    if (opts.help) {
        this.do_help('help', {}, [subcmd], cb);
        return;
    }

    var net_uuid = args.shift();
    if (net_uuid === 'help') {
        return cb(new errors.UsageError(
            'Please use `sdcadm post-setup help underlay-nics` instead'));
    }
    var cns = args.length ? args : null;

    if (!net_uuid || !cns) {
        return cb(new errors.UsageError(
            'must specify network uuid and at least one server'));
    }

    var proc = new UnderlayNics();
    proc.execute({
        sdcadm: this.sdcadm,
        log: this.log.child({
            postSetup: 'underlay-nics'
        }, true),
        progress: this.progress,
        net_uuid: net_uuid,
        cns: cns
    }, cb);
}

do_underlay_nics.help = (
    UnderlayNics.prototype.help + '\n' +
    '\n' +
    'Usage:\n' +
    '     {{name}} underlay-nics NETWORK_UUID SERVER1 [SERVER2...]\n' +
    '\n' +
    'Note that this command can be re-run as many times as needed and it \n' +
    'will automatically take care of do not provision two underlay nics \n' +
    'into the same network for any CN.\n' +
    '\n' +
    '{{options}}'
);

do_underlay_nics.options = [
    {
        names: ['help', 'h'],
        type: 'bool',
        help: 'Show this help.'
    }
];

//---- exports

module.exports = {
    do_underlay_nics: do_underlay_nics
};
<commit_msg>TOOLS-1031: post-setup underlay-nics verifies that fabric underlay network is assigned to an actual nic<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

/*
 * 'sdcadm post-setup underlay-nics'
 */

var p = console.log;
var util = require('util');

var assert = require('assert-plus');
var vasync = require('vasync');

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

//---- internal support stuff

function UnderlayNics() {}
UnderlayNics.prototype.name = 'underlay-nics';

UnderlayNics.prototype.help = (
    'Provisions underlay NICs on the provided underlay network for the\n' +
    'given Compute Node(s).'
);

UnderlayNics.prototype.execute = function (opts, cb) {
    assert.object(opts, 'opts');
    assert.object(opts.sdcadm, 'opts.sdcadm');
    assert.object(opts.log, 'opts.log');
    assert.func(opts.progress, 'opts.progress');
    assert.string(opts.net_uuid, 'opts.net_uuid');
    assert.optionalArrayOfString(opts.cns, 'opts.cns');

    var progress = opts.progress;
    var sdcadm = opts.sdcadm;
    var log = opts.log;

    var MIN_VALID_NAPI_VERSION = '20150312';
    var img;
    var cns = [];
    var cns2Update = [];
    // Used by sdcadm history:
    var changes = [];
    var hist;
    // Given we may have errors for some CNs, and not from some others, we
    // need to store errors and report at end:
    var errs = [];

    vasync.pipeline({funcs: [
        function getNapiVmImgs(_, next) {
            sdcadm.getImgsForSvcVms({
                svc: 'napi'
            }, function (err, obj) {
                if (err) {
                    return next(err);
                }
                img = obj.imgs[0];
                return next();
            });
        },

        function checkMinNapiVersion(_, next) {
            progress('Checking for minimum NAPI version');
            var splitVersion = img.version.split('-');
            var validNapi = false;

            if (splitVersion[0] === 'master') {
                validNapi = splitVersion[1].substr(0, 8) >=
                    MIN_VALID_NAPI_VERSION;
            } else if (splitVersion[0] === 'release') {
                validNapi = splitVersion[1] >= MIN_VALID_NAPI_VERSION;
            }

            if (!validNapi) {
                return next(new errors.ValidationError(new Error('Datacenter ' +
                    'does not have the minimum NAPI version needed for adding' +
                    ' underlay nics to CNs. ' +
                    'Please try again after upgrading NAPI')));
            }

            return next();
        },

        function validateNetwork(_, next) {
            progress('Verifying the provided network exists');
            sdcadm.napi.getNetwork(opts.net_uuid, function (err, net) {
                if (err) {
                    if (err.statusCode === 404) {
                        return next(new errors.UsageError('The provided ' +
                                    'network UUID cannot be found in NAPI'));
                    } else {
                        return next(new errors.SDCClientError(err, 'napi'));
                    }
                }
                return next();
            });
        },

        function validateServers(_, next) {
            progress('Verifying the provided Server(s) UUID(s)');
            sdcadm.cnapi.listServers({
                setup: true
            }, function (err, recs) {
                if (err) {
                    return next(new errors.SDCClientError(err, 'cnapi'));
                }
                var hostnames = {};
                recs.forEach(function (r) {
                    hostnames[r.hostname] = r.uuid;
                });
                // Translate hostnames to UUIDs when possible:
                opts.cns = opts.cns.map(function (cn) {
                    if (Object.keys(hostnames).indexOf(cn) !== -1) {
                        return hostnames[cn];
                    } else {
                        return cn;
                    }
                });

                var uuids = recs.map(function (r) {
                    return (r.uuid);
                });

                // Print a message if any of the provided CNs is not valid
                opts.cns.forEach(function (cn) {
                    if (uuids.indexOf(cn) === -1) {
                        progress('Skipping invalid CN ' + cn);
                    } else {
                        cns.push(cn);
                    }
                });
                next();
            });
        },

        function filterServersWithUnderlayNicOnTheSameNetwork(_, next) {
            function checkServerNics(cn, nextCn) {
                sdcadm.napi.listNics({
                    owner_uuid: sdcadm.config.ufds_admin_uuid,
                    belongs_to_uuid: cn,
                    belongs_to_type: 'server',
                    network_uuid: opts.net_uuid
                }, function (err, nics) {
                    if (err) {
                        errs.push(new errors.SDCClientError(err, 'napi'));
                        nextCn(err);
                    } else {
                        if (!nics.length) {
                            cns2Update.push(cn);
                            return nextCn();
                        }
                        var hasUnderlayNic = false;
                        nics.forEach(function (nic) {
                            if (nic.underlay) {
                                progress('Skipping CN %s (already has an ' +
                                        'underlay nic in network %s)',
                                        cn, opts.net_uuid);
                                hasUnderlayNic = true;
                            }
                        });
                        if (!hasUnderlayNic) {
                            cns2Update.push(cn);
                        }
                        return nextCn();
                    }
                });
            }
            var existsQueue = vasync.queue(checkServerNics, 5);
            existsQueue.once('end', next);
            existsQueue.push(cns);
            existsQueue.close();
        },

        // CNs must have the configured underlay network tag assigned to an
        // actual NIC in order to be able to add underlay-nic for the CN:
        function filterServersWithoutUnderlayNicTag(_, next) {
            var underlayTag = sdcadm.sdc.metadata.fabric_cfg.sdc_underlay_tag;
            var cnsToSkip = [];
            var theCns = cns2Update;
            cns2Update = [];

            function checkServerTags(cn, nextCn) {
                sdcadm.napi.listNics({
                    owner_uuid: sdcadm.config.ufds_admin_uuid,
                    belongs_to_uuid: cn,
                    belongs_to_type: 'server'
                }, function (err, nics) {
                    if (err) {
                        errs.push(new errors.SDCClientError(err, 'napi'));
                        cnsToSkip.push(cn);
                        nextCn(err);
                    } else {
                        if (!nics.length) {
                            cnsToSkip.push(cn);
                            return nextCn();
                        }
                        var hasUnderlayTag = false;
                        nics.forEach(function (nic) {
                            if (hasUnderlayTag) {
                                return;
                            }
                            var nicTagsProvided = nic.nic_tags_provided;
                            if (nicTagsProvided &&
                                nicTagsProvided.indexOf(underlayTag) !== -1) {
                                hasUnderlayTag = true;
                            }
                        });
                        if (hasUnderlayTag) {
                            cns2Update.push(cn);
                        } else {
                            cnsToSkip.push(cn);
                        }
                        return nextCn();
                    }
                });
            }

            var existsQueue = vasync.queue(checkServerTags, 5);
            existsQueue.once('end', function () {
                if (cnsToSkip.length) {
                    progress('The following CNs do not have the configured');
                    progress('underlay network tag assigned to any NIC and');
                    progress('will be skipped:');
                    progress(cnsToSkip.join(','));
                }
                next();
            });
            existsQueue.push(theCns);
            existsQueue.close();
        },

        function saveChangesToHistory(_, next) {
            if (!cns2Update.length) {
                return next();
            }
            changes.push({
                service: {
                    name: 'napi'
                },
                type: 'underlay-nics',
                servers: cns,
                network: opts.net_uuid
            });

            sdcadm.history.saveHistory({
                changes: changes
            }, function (err, hst) {
                if (err) {
                    return next(err);
                }
                hist = hst;
                return next();
            });
        },

        function provisionNics(_, next) {
            if (!cns2Update.length) {
                return next();
            }
            function provisionUnderlayNic(cn, nextNic) {
                var route = util.format('/networks/%s/nics', opts.net_uuid);
                sdcadm.napi.post(route, {
                    owner_uuid: sdcadm.config.ufds_admin_uuid,
                    belongs_to_uuid: cn,
                    belongs_to_type: 'server',
                    underlay: true
                }, function (err, res) {
                    if (err) {
                        errs.push(new errors.SDCClientError(err, 'napi'));
                        return nextNic(err);
                    }
                    progress('Underlay NIC created for CN %s', cn);
                    nextNic();
                });
            }

            var nicsQueue = vasync.queue(provisionUnderlayNic, 5);
            nicsQueue.once('end', next);
            nicsQueue.push(cns);
            nicsQueue.close();
        }
    ]}, function (err) {
        if (errs.length) {
            err = new errors.MultiError(errs);
        }

        if (!hist) {
            log.debug('History not set for underlay-nics');
            return cb(err);
        }

        if (!cns2Update.length) {
            progress('All the provided CNs already had an underlay NIC' +
                    ' on the provided network');
            return cb(err);
        }

        if (err) {
            hist.error = err;
        }

        sdcadm.history.updateHistory(hist, function (err2) {
            if (err) {
                return cb(err);
            } else if (err2) {
                return cb(err2);
            } else {
                return cb();
            }
        });
    });
};

//---- CLI

function do_underlay_nics(subcmd, opts, args, cb) {
    if (opts.help) {
        this.do_help('help', {}, [subcmd], cb);
        return;
    }

    var net_uuid = args.shift();
    if (net_uuid === 'help') {
        return cb(new errors.UsageError(
            'Please use `sdcadm post-setup help underlay-nics` instead'));
    }
    var cns = args.length ? args : null;

    if (!net_uuid || !cns) {
        return cb(new errors.UsageError(
            'must specify network uuid and at least one server'));
    }

    var proc = new UnderlayNics();
    proc.execute({
        sdcadm: this.sdcadm,
        log: this.log.child({
            postSetup: 'underlay-nics'
        }, true),
        progress: this.progress,
        net_uuid: net_uuid,
        cns: cns
    }, cb);
}

do_underlay_nics.help = (
    UnderlayNics.prototype.help + '\n' +
    '\n' +
    'Usage:\n' +
    '     {{name}} underlay-nics NETWORK_UUID SERVER1 [SERVER2...]\n' +
    '\n' +
    'Note that this command can be re-run as many times as needed and it \n' +
    'will automatically take care of do not provision two underlay nics \n' +
    'into the same network for any CN.\n' +
    '\n' +
    '{{options}}'
);

do_underlay_nics.options = [
    {
        names: ['help', 'h'],
        type: 'bool',
        help: 'Show this help.'
    }
];

//---- exports

module.exports = {
    do_underlay_nics: do_underlay_nics
};
<|endoftext|>


<commit_before>function graph(title, json, textcolour, boxcolour, plotly_modebar, datatype) {
  $('#chart').height($(window).height())

  var extras = getExtras(datatype)
  var points = getPoints(json['values'], extras)

  if (typeof(points['y'][0]) == 'object') {
    var data = multiplePoints(points)
  } else {
    var data = singlePoint(points)
  }

  var layout = getLayout(title, textcolour, boxcolour)

  Plotly.newPlot(
    'chart',
    data,
    layout,
    { displayModeBar: JSON.parse(plotly_modebar) }
  );
}

function multiplePoints(points) {
  var data = []
  var yKeys = Object.keys(points['y'][0])
  var yCount = yKeys.length

  for (y = 0; y < yCount; y++) {
    var vals = []

    for (i = 0; i < points['y'].length; i++) {
      vals.push(points['y'][i][yKeys[y]])
    }

    data.push({
      x: points['x'],
      y: vals,
      name: yKeys[y],
      type: 'scatter',
      line: {
        shape: 'spline',
        width: 2,
        smoothing: 1.3,
      },
    })
  }

  return data
}

function singlePoint(points, textcolour) {
  return [{
    x: points['x'],
    y: points['y'],
    text: points['text'],
    hoverinfo: 'text',
    type: 'scatter',
    line: {
      shape: 'spline',
      width: 2,
      smoothing: 1.3,
      color: textcolour
    },
  }]
}

function getLayout(title, textcolour, boxcolour) {
  return {
    paper_bgcolor: boxcolour,
    plot_bgcolor: boxcolour,
    xaxis: {
      gridcolor: '#D3D3D3',
      type: 'datetime',
      tickangle: 315,
      tickfont: {
        color: textcolour
      }
    },
    yaxis: {
      rangemode: 'tozero',
      gridcolor: '#D3D3D3',
      tickangle: 315,
      tickfont: {
        color: textcolour
      }
    },
    margin: {
      l: 30, r: 30, t: 60, b: 80
    }
  }
}
<commit_msg>Pass text colour through to line plot<commit_after>function graph(title, json, textcolour, boxcolour, plotly_modebar, datatype) {
  $('#chart').height($(window).height())

  var extras = getExtras(datatype)
  var points = getPoints(json['values'], extras)

  if (typeof(points['y'][0]) == 'object') {
    var data = multiplePoints(points)
  } else {
    var data = singlePoint(points, textcolour)
  }

  var layout = getLayout(title, textcolour, boxcolour)

  Plotly.newPlot(
    'chart',
    data,
    layout,
    { displayModeBar: JSON.parse(plotly_modebar) }
  );
}

function multiplePoints(points) {
  var data = []
  var yKeys = Object.keys(points['y'][0])
  var yCount = yKeys.length

  for (y = 0; y < yCount; y++) {
    var vals = []

    for (i = 0; i < points['y'].length; i++) {
      vals.push(points['y'][i][yKeys[y]])
    }

    data.push({
      x: points['x'],
      y: vals,
      name: yKeys[y],
      type: 'scatter',
      line: {
        shape: 'spline',
        width: 2,
        smoothing: 1.3
      }
    })
  }

  return data
}

function singlePoint(points, textcolour) {

  return [{
    x: points['x'],
    y: points['y'],
    text: points['text'],
    hoverinfo: 'text',
    type: 'scatter',
    line: {
      shape: 'spline',
      width: 2,
      smoothing: 1.3,
      color: textcolour
    },
  }]
}

function getLayout(title, textcolour, boxcolour) {
  return {
    paper_bgcolor: boxcolour,
    plot_bgcolor: boxcolour,
    xaxis: {
      gridcolor: '#D3D3D3',
      type: 'datetime',
      tickangle: 315,
      tickfont: {
        color: textcolour
      }
    },
    yaxis: {
      rangemode: 'tozero',
      gridcolor: '#D3D3D3',
      tickangle: 315,
      tickfont: {
        color: textcolour
      }
    },
    margin: {
      l: 30, r: 30, t: 60, b: 80
    }
  }
}
<|endoftext|>


<commit_before>import FieldNode from './fieldNode.js';
import ReducerNode from './reducerNode.js';
import deepClone from '../lib/deepClone';

export default class CollectionNode {
    constructor(collection, body = {}, linkName = null) {
        if (collection && !_.isObject(body)) {
            throw new Meteor.Error('invalid-body', 'Every collection link should have its body defined as an object.');
        }

        this.body = deepClone(body);
        this.linkName = linkName;
        this.collection = collection;

        this.nodes = [];
        this.props = {};
        this.parent = null;
        this.linker = null;
        this.linkStorageField = null;
        this.scheduledForDeletion = false;
        this.reducers = [];
        this.results = [];
    }

    get collectionNodes() {
        return _.filter(this.nodes, n => n instanceof CollectionNode)
    }

    get fieldNodes() {
        return _.filter(this.nodes, n => n instanceof FieldNode);
    }

    get reducerNodes() {
        return _.filter(this.nodes, n => n instanceof ReducerNode);
    }

    /**
     * @param node
     * @param linker
     */
    add(node, linker) {
        node.parent = this;

        if (linker) {
            node.linker = linker;
            node.linkStorageField = linker.linkStorageField;
            node.isMeta = linker.isMeta();
            node.isVirtual = linker.isVirtual();
            node.isOneResult = linker.isOneResult();
            // we clean the storage in 2 scenarios, if it is virtual then we need t o
            node.shouldCleanStorage = (node.isVirtual)
                ? !node.hasField(node.linkStorageField)
                : !this.hasField(node.linkStorageField)
            ;
        }

        this.nodes.push(node);
    }

    /**
     * @param _node
     */
    remove(_node) {
        this.nodes = _.filter(this.nodes, node => _node !== node);
    }

    /**
     * @param filters
     * @param options
     */
    applyFields(filters, options) {
        let hasAddedAnyField = false;

        _.each(this.fieldNodes, n => {
            hasAddedAnyField = true;
            n.applyFields(options.fields)
        });

        // it will only get here if it has collectionNodes children
        _.each(this.collectionNodes, (collectionNode) => {
            let linker = collectionNode.linker;

            if (linker && !linker.isVirtual()) {
                options.fields[linker.linkStorageField] = 1;
                hasAddedAnyField = true;
            }
        });

        // if he selected filters, we should automatically add those fields
        _.each(filters, (value, field) => {
            // special handling for the $meta filter and conditional operators
            if (!_.contains(['$or', '$nor', '$not', '$and', '$meta'], field)) {
                // if the field or the parent of the field already exists, don't add it
                if(!_.has(options.fields,field.split('.')[0])){
                    hasAddedAnyField = true;
                    options.fields[field] = 1;
                }
            }
        });

        if (!hasAddedAnyField) {
            options.fields = {_id: 1};
        }
    }

    /**
     * @param fieldName
     * @returns {boolean}
     */
    hasField(fieldName) {
        return !!_.find(this.fieldNodes, fieldNode => {
            return fieldNode.name == fieldName
        })
    }

    /**
     * @param fieldName
     * @returns {FieldNode}
     */
    getField(fieldName) {
        return _.find(this.fieldNodes, fieldNode => {
            return fieldNode.name == fieldName
        })
    }

    /**
     * @param name
     * @returns {boolean}
     */
    hasCollectionNode(name) {
        return !!_.find(this.collectionNodes, node => {
            return node.linkName == name
        })
    }

    /**
     * @param name
     * @returns {boolean}
     */
    hasReducerNode(name) {
        return !!_.find(this.reducerNodes, node => {
            return node.name == name
        })
    }

    /**
     * @param name
     * @returns {ReducerNode}
     */
    getReducerNode(name) {
        return _.find(this.reducerNodes, node => {
            return node.name == name
        })
    }

    /**
     * @param name
     * @returns {CollectionNode}
     */
    getCollectionNode(name) {
        return _.find(this.collectionNodes, node => {
            return node.linkName == name
        })
    }

    /**
     * @returns {*}
     */
    getName() {
        return this.linkName
            ? this.linkName
            : (this.collection ? this.collection._name : 'N/A');
    }
}
<commit_msg>fix issue adding fields from $filters - added spaces<commit_after>import FieldNode from './fieldNode.js';
import ReducerNode from './reducerNode.js';
import deepClone from '../lib/deepClone';

export default class CollectionNode {
    constructor(collection, body = {}, linkName = null) {
        if (collection && !_.isObject(body)) {
            throw new Meteor.Error('invalid-body', 'Every collection link should have its body defined as an object.');
        }

        this.body = deepClone(body);
        this.linkName = linkName;
        this.collection = collection;

        this.nodes = [];
        this.props = {};
        this.parent = null;
        this.linker = null;
        this.linkStorageField = null;
        this.scheduledForDeletion = false;
        this.reducers = [];
        this.results = [];
    }

    get collectionNodes() {
        return _.filter(this.nodes, n => n instanceof CollectionNode)
    }

    get fieldNodes() {
        return _.filter(this.nodes, n => n instanceof FieldNode);
    }

    get reducerNodes() {
        return _.filter(this.nodes, n => n instanceof ReducerNode);
    }

    /**
     * @param node
     * @param linker
     */
    add(node, linker) {
        node.parent = this;

        if (linker) {
            node.linker = linker;
            node.linkStorageField = linker.linkStorageField;
            node.isMeta = linker.isMeta();
            node.isVirtual = linker.isVirtual();
            node.isOneResult = linker.isOneResult();
            // we clean the storage in 2 scenarios, if it is virtual then we need t o
            node.shouldCleanStorage = (node.isVirtual)
                ? !node.hasField(node.linkStorageField)
                : !this.hasField(node.linkStorageField)
            ;
        }

        this.nodes.push(node);
    }

    /**
     * @param _node
     */
    remove(_node) {
        this.nodes = _.filter(this.nodes, node => _node !== node);
    }

    /**
     * @param filters
     * @param options
     */
    applyFields(filters, options) {
        let hasAddedAnyField = false;

        _.each(this.fieldNodes, n => {
            hasAddedAnyField = true;
            n.applyFields(options.fields)
        });

        // it will only get here if it has collectionNodes children
        _.each(this.collectionNodes, (collectionNode) => {
            let linker = collectionNode.linker;

            if (linker && !linker.isVirtual()) {
                options.fields[linker.linkStorageField] = 1;
                hasAddedAnyField = true;
            }
        });

        // if he selected filters, we should automatically add those fields
        _.each(filters, (value, field) => {
            // special handling for the $meta filter and conditional operators
            if (!_.contains(['$or', '$nor', '$not', '$and', '$meta'], field)) {
                // if the field or the parent of the field already exists, don't add it
                if (!_.has(options.fields, field.split('.')[0])){
                    hasAddedAnyField = true;
                    options.fields[field] = 1;
                }
            }
        });

        if (!hasAddedAnyField) {
            options.fields = {_id: 1};
        }
    }

    /**
     * @param fieldName
     * @returns {boolean}
     */
    hasField(fieldName) {
        return !!_.find(this.fieldNodes, fieldNode => {
            return fieldNode.name == fieldName
        })
    }

    /**
     * @param fieldName
     * @returns {FieldNode}
     */
    getField(fieldName) {
        return _.find(this.fieldNodes, fieldNode => {
            return fieldNode.name == fieldName
        })
    }

    /**
     * @param name
     * @returns {boolean}
     */
    hasCollectionNode(name) {
        return !!_.find(this.collectionNodes, node => {
            return node.linkName == name
        })
    }

    /**
     * @param name
     * @returns {boolean}
     */
    hasReducerNode(name) {
        return !!_.find(this.reducerNodes, node => {
            return node.name == name
        })
    }

    /**
     * @param name
     * @returns {ReducerNode}
     */
    getReducerNode(name) {
        return _.find(this.reducerNodes, node => {
            return node.name == name
        })
    }

    /**
     * @param name
     * @returns {CollectionNode}
     */
    getCollectionNode(name) {
        return _.find(this.collectionNodes, node => {
            return node.linkName == name
        })
    }

    /**
     * @returns {*}
     */
    getName() {
        return this.linkName
            ? this.linkName
            : (this.collection ? this.collection._name : 'N/A');
    }
}
<|endoftext|>


<commit_before>'use strict';
var url = require('url');
var config = require('config');
var request = require('request-promise');
var AWS = require('aws-sdk');
var logger = require('hoist-logger');
var _ = require('lodash');
var BBPromise = require('bluebird');
var throat = require('throat')(BBPromise);

AWS.config.update({
  accessKeyId: config.get('Hoist.aws.account'),
  secretAccessKey: config.get('Hoist.aws.secret'),
  region: config.get('Hoist.aws.region')
});

//var ec2 = BBPromise.promisifyAll(new AWS.EC2());
var autoscaling = BBPromise.promisifyAll(new AWS.AutoScaling());

function getQueues() {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues';
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      return _.filter(response, function (queue) {
        return _.startsWith(queue.name, 'run_module_');
      });
    });
}

function setScaleMinAndMax(queueCount) {
  var autoScaleConfig = {
    MinSize: queueCount,
    MaxSize: queueCount * 10,
    AutoScalingGroupName: 'executors'
  };
  logger.info({
    config: autoScaleConfig
  }, 'updating autoscaling config');
  return autoscaling.updateAutoScalingGroupAsync(autoScaleConfig).then(function (data) {
    logger.info({
      data: data
    }, 'auto scaling group');
  });
}

function killConnection(connectionName) {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/connections/' + encodeURIComponent(connectionName);
  logger.info('killing connection', url.format(rabbitUrl));
  var options = {
    method: 'DELETE',
    uri: url.format(rabbitUrl),
    json: true
  };
  return request(options);
}

function rebalanceQueue(queue) {
  console.log('removeing executor from queue', queue.name);
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues/' + encodeURIComponent(queue.vhost) + '/' + encodeURIComponent(queue.name);
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      /* pick the first consumer */
      /* jshint camelcase: false */
      if (response.consumer_details && response.consumer_details.length) {
        return response.consumer_details[0];
      }

    }).then(function (consumer) {
      /* jshint camelcase: false */
      return killConnection(consumer.channel_details.connection_name);
    });
}

function rebalanceQueues(queues) {
  return BBPromise.all(_.map(queues, rebalanceQueue)).then(function () {
    return BBPromise.delay(10000);
  });
}

function rebalance() {
  //get queue stats
  console.log('getting queues');
  return getQueues().
  then(function (queues) {
    var messageCount = _.reduce(_.pluck(queues, 'messages_ready'), function (sum, n) {
      return sum + n;
    });
    console.log(messageCount);
    console.log('parsing queues');
    //ensure minimum executors is the same as number of queues
    //setting new AutoScalingParams
    return setScaleMinAndMax(queues.length, messageCount)
      .then(function () {

        var queuesThatHaveNoConsumers = _.filter(queues, function (queue) {
          return queue.consumers < 2;
        });
        logger.info({
          queues: _.pluck(queuesThatHaveNoConsumers, 'name')
        }, 'do we have any queues that need consumers?');
        var queuesWithTooFewConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers < 20 && ((queue.consumers / queue.messages_ready) < 0.001);
        });
        logger.info({
          queues: _.pluck(queuesWithTooFewConsumers, 'name')
        }, 'do we have any queues that have too few consumers?');
        var queuesWithTooManyConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers > 20 || (queue.consumers > 2 && ((queue.consumers / queue.messages_ready) > 0.002));
        });
        logger.info({
          queues: _.pluck(queuesWithTooManyConsumers, 'name')
        }, 'do we have any queues that have too many consumers?');

        if (queuesThatHaveNoConsumers.length > 0 || queuesWithTooFewConsumers.length > 0) {
          logger.info('we need to add consumers');
          if (queuesWithTooManyConsumers.length > 0) {
            logger.info({
              queues: _.pluck(queuesWithTooManyConsumers, 'name')
            }, 'we should grab executors from queues');
            return rebalanceQueues(queuesWithTooManyConsumers).then(function () {
              return false;
            });
          } else {
            var scaleUpParams = {
              PolicyName: 'increase-executors',
              /* required */
              AutoScalingGroupName: 'executors',
              HonorCooldown: true
            };
            logger.info({
              params: scaleUpParams
            }, 'increasing executors');
            return autoscaling.executePolicyAsync(scaleUpParams).then(function () {
              return true;
            });
          }
        } else if (queuesWithTooManyConsumers.length > 0) {
          logger.info('we can remove executors');
          var scaleDownParams = {
            PolicyName: 'decrease-executors',
            /* required */
            AutoScalingGroupName: 'executors',
            HonorCooldown: true
          };
          logger.info({
            params: scaleDownParams
          }, 'decreasing executors');
          return autoscaling.executePolicyAsync(scaleDownParams).then(function () {
            return true;
          });
        } else {
          logger.info('we seem to have the right number of executors');
          return true;
        }

      }).then(function (done) {
        if (!done) {
          return BBPromise.delay(10000).then(function () {
            return module.exports();
          });

        }
      });

  }).catch(function (err) {
    console.log(err);
  });

  //are there any queues that need executors (have no consumers)
  //are there queues that need to be rebalanced (have too many executors per message)
  //do we need to add more executors
  //send rebalance signal?

  //scale up?
  //scale down?
}

module.exports = rebalance;
<commit_msg>fix missing text<commit_after>'use strict';
var url = require('url');
var config = require('config');
var request = require('request-promise');
var AWS = require('aws-sdk');
var logger = require('hoist-logger');
var _ = require('lodash');
var BBPromise = require('bluebird');

AWS.config.update({
  accessKeyId: config.get('Hoist.aws.account'),
  secretAccessKey: config.get('Hoist.aws.secret'),
  region: config.get('Hoist.aws.region')
});

//var ec2 = BBPromise.promisifyAll(new AWS.EC2());
var autoscaling = BBPromise.promisifyAll(new AWS.AutoScaling());

function getQueues() {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues';
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      return _.filter(response, function (queue) {
        return _.startsWith(queue.name, 'run_module_');
      });
    });
}

function setScaleMinAndMax(queueCount) {
  var autoScaleConfig = {
    MinSize: queueCount,
    MaxSize: queueCount * 10,
    AutoScalingGroupName: 'executors'
  };
  logger.info({
    config: autoScaleConfig
  }, 'updating autoscaling config');
  return autoscaling.updateAutoScalingGroupAsync(autoScaleConfig).then(function (data) {
    logger.info({
      data: data
    }, 'auto scaling group');
  });
}

function killConnection(connectionName) {
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/connections/' + encodeURIComponent(connectionName);
  logger.info('killing connection', url.format(rabbitUrl));
  var options = {
    method: 'DELETE',
    uri: url.format(rabbitUrl),
    json: true
  };
  return request(options);
}

function rebalanceQueue(queue) {
  console.log('removeing executor from queue', queue.name);
  var rabbitUrl = url.parse(config.get('Hoist.rabbit.managementurl'));
  rabbitUrl.pathname = '/api/queues/' + encodeURIComponent(queue.vhost) + '/' + encodeURIComponent(queue.name);
  return request.get(url.format(rabbitUrl), {
      json: true
    })
    .then(function (response) {
      /* pick the first consumer */
      /* jshint camelcase: false */
      if (response.consumer_details && response.consumer_details.length) {
        return response.consumer_details[0];
      }

    }).then(function (consumer) {
      /* jshint camelcase: false */
      return killConnection(consumer.channel_details.connection_name);
    });
}

function rebalanceQueues(queues) {
  return BBPromise.all(_.map(queues, rebalanceQueue)).then(function () {
    return BBPromise.delay(10000);
  });
}

function rebalance() {
  //get queue stats
  console.log('getting queues');
  return getQueues().
  then(function (queues) {
    var messageCount = _.reduce(_.pluck(queues, 'messages_ready'), function (sum, n) {
      return sum + n;
    });
    console.log(messageCount);
    console.log('parsing queues');
    //ensure minimum executors is the same as number of queues
    //setting new AutoScalingParams
    return setScaleMinAndMax(queues.length, messageCount)
      .then(function () {

        var queuesThatHaveNoConsumers = _.filter(queues, function (queue) {
          return queue.consumers < 2;
        });
        logger.info({
          queues: _.pluck(queuesThatHaveNoConsumers, 'name')
        }, 'do we have any queues that need consumers?');
        var queuesWithTooFewConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers < 20 && ((queue.consumers / queue.messages_ready) < 0.001);
        });
        logger.info({
          queues: _.pluck(queuesWithTooFewConsumers, 'name')
        }, 'do we have any queues that have too few consumers?');
        var queuesWithTooManyConsumers = _.filter(queues, function (queue) {
          /* jshint camelcase:false */
          return queue.consumers > 20 || (queue.consumers > 2 && ((queue.consumers / queue.messages_ready) > 0.002));
        });
        logger.info({
          queues: _.pluck(queuesWithTooManyConsumers, 'name')
        }, 'do we have any queues that have too many consumers?');

        if (queuesThatHaveNoConsumers.length > 0 || queuesWithTooFewConsumers.length > 0) {
          logger.info('we need to add consumers');
          if (queuesWithTooManyConsumers.length > 0) {
            logger.info({
              queues: _.pluck(queuesWithTooManyConsumers, 'name')
            }, 'we should grab executors from queues');
            return rebalanceQueues(queuesWithTooManyConsumers).then(function () {
              return false;
            });
          } else {
            var scaleUpParams = {
              PolicyName: 'increase-executors',
              /* required */
              AutoScalingGroupName: 'executors',
              HonorCooldown: true
            };
            logger.info({
              params: scaleUpParams
            }, 'increasing executors');
            return autoscaling.executePolicyAsync(scaleUpParams).then(function () {
              return true;
            });
          }
        } else if (queuesWithTooManyConsumers.length > 0) {
          logger.info('we can remove executors');
          var scaleDownParams = {
            PolicyName: 'decrease-executors',
            /* required */
            AutoScalingGroupName: 'executors',
            HonorCooldown: true
          };
          logger.info({
            params: scaleDownParams
          }, 'decreasing executors');
          return autoscaling.executePolicyAsync(scaleDownParams).then(function () {
            return true;
          });
        } else {
          logger.info('we seem to have the right number of executors');
          return true;
        }

      }).then(function (done) {
        if (!done) {
          return BBPromise.delay(10000).then(function () {
            return module.exports();
          });

        }
      });

  }).catch(function (err) {
    console.log(err);
  });

  //are there any queues that need executors (have no consumers)
  //are there queues that need to be rebalanced (have too many executors per message)
  //do we need to add more executors
  //send rebalance signal?

  //scale up?
  //scale down?
}

module.exports = rebalance;
<|endoftext|>


<commit_before>// Copyright 2015 Esri
// Licensed under The MIT License(MIT);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://opensource.org/licenses/MIT
// 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.

/* eslint-env node */
'use strict';

const {
  parse
} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default;

// Replace indentifier
const Identifiers = {
  'require': 'eriuqer',
  'define': 'enifed'
};

// Use babel to parse/traverse/generate code.
// - replace define and require with enifed and eriuqer
// - if required, collect the external AMD modules referenced

module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmdModules) {

  const ast = parse(code);

  traverse(ast, {
    Program: {
      exit(path) {
        // This will take care of the loader.js code where define and require are define globally
        // The cool thing is that babel will rename all references as well
        // Rename at the end so we don't overlap with the CallExpression visitor
        path.scope.rename('define', Identifiers.define);
        path.scope.rename('require', Identifiers.require);
      }
    },
    CallExpression(path) {

      // Looking for:
      // - call for the global define function. If it matches then rename it and collect the external AMD references
      // - call for the global require function. It it matches then rename it
      // - eval calls: We need to process the eval code itself (used by auto-import)
      const {
        node
      } = path;

      // Collect external AMD references
      // Looking for define('foo', ['a', 'b'], function(a, b) {})
      if (amdPackages &&
        externalAmdModules &&
        t.isIdentifier(node.callee, {
          name: 'define'
        }) &&
        node.arguments.length >= 2 &&
        t.isArrayExpression(node.arguments[1])) {

        node.arguments[1].elements.forEach(function (element) {
          if (!t.isStringLiteral(element)) {
            return;
          }

          const isExternalAmd = amdPackages.some(function (amdPackage) {
            return element.value.indexOf(amdPackage + '/') === 0 || element.value === amdPackage;
          });

          if (!isExternalAmd) {
            return;
          }

          externalAmdModules.add(element.value);

        });
      }

      // Rename if it's invoking a global require function
      if (t.isIdentifier(node.callee, {
          name: 'define'
        }) && !path.scope.hasBinding('define')) {
        node.callee.name = Identifiers.define;
        return;
      }

      // Rename if it's invoking a global require function
      if (t.isIdentifier(node.callee, {
          name: 'require'
        }) && !path.scope.hasBinding('require')) {
        node.callee.name = Identifiers.require;
        return;
      }

      // auto-import injects eval expression. We need to process them as individual code
      if (t.isIdentifier(node.callee, {
          name: 'eval'
        }) && t.isStringLiteral(node.arguments[0])) {
        node.arguments[0].value = replaceRequireAndDefine(node.arguments[0].value, amdPackages, externalAmdModules);
      }
    },
    VariableDeclarator(path) {
      // This could happened in auto-import eval: var d = define;
      if (t.isIdentifier(path.node.init, {
          name: 'define'
        }) && !path.scope.hasBinding('define')) {
        path.node.init.name = Identifiers.define;
        return;
      }
      // This could happened in auto-import eval: var r = require;
      if (t.isIdentifier(path.node.init, {
          name: 'require'
        }) && !path.scope.hasBinding('require')) {
        path.node.init.name = Identifiers.require;
      }
    },
    AssignmentExpression(path) {
      // This could happened in auto-import eval: window.d = define;
      if (t.isIdentifier(path.node.right, {
          name: 'define'
        }) && !path.scope.hasBinding('define')) {
        path.node.right.name = Identifiers.define;
        return;
      }
      // This could happened in auto-import eval: window.r = require;
      if (t.isIdentifier(path.node.right, {
          name: 'require'
        }) && !path.scope.hasBinding('require')) {
        path.node.right.name = Identifiers.require;
      }
    },

  });

  return generator(ast, {
    retainLines: true,
    retainFunctionParens: true
  }, code).code;
}
<commit_msg>Fixed require/define conversion for tests<commit_after>// Copyright 2015 Esri
// Licensed under The MIT License(MIT);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//   http://opensource.org/licenses/MIT
// 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.

/* eslint-env node */
'use strict';

const {
  parse
} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default;

// Replace indentifier
const IdentifierMap = {
  'require': 'eriuqer',
  'define': 'enifed'
};

const Identifiers = Object.keys(IdentifierMap);

// Use babel to parse/traverse/generate code.
// - replace define and require with enifed and eriuqer
// - if required, collect the external AMD modules referenced

module.exports = function replaceRequireAndDefine(code, amdPackages, externalAmdModules) {

  const ast = parse(code);

  traverse(ast, {
    Program: {
      exit(path) {
        // This will take care of the loader.js code where define and require are define globally
        // The cool thing is that babel will rename all references as well
        // Rename at the end so we don't overlap with the CallExpression visitor
        Identifiers.forEach(identifier => path.scope.rename(identifier, IdentifierMap[identifier]));
      }
    },
    CallExpression(path) {

      // Looking for:
      // - call for the global define function. If it matches then rename it and collect the external AMD references
      // - call for the global require function. It it matches then rename it
      // - eval calls: We need to process the eval code itself (used by auto-import)
      const {
        node
      } = path;

      // Collect external AMD references
      // Looking for define('foo', ['a', 'b'], function(a, b) {})
      if (amdPackages &&
        externalAmdModules &&
        t.isIdentifier(node.callee, {
          name: 'define'
        }) &&
        node.arguments.length >= 2 &&
        t.isArrayExpression(node.arguments[1])) {

        node.arguments[1].elements.forEach(function (element) {
          if (!t.isStringLiteral(element)) {
            return;
          }

          const isExternalAmd = amdPackages.some(function (amdPackage) {
            return element.value.indexOf(amdPackage + '/') === 0 || element.value === amdPackage;
          });

          if (!isExternalAmd) {
            return;
          }

          externalAmdModules.add(element.value);

        });
      }

      // auto-import injects eval expression. We need to process them as individual code
      if (t.isIdentifier(node.callee, {
        name: 'eval'
      }) && t.isStringLiteral(node.arguments[0])) {
        node.arguments[0].value = replaceRequireAndDefine(node.arguments[0].value, amdPackages, externalAmdModules);
      }
    },
    Identifier(path) {
      // Only interested by our identifiers that have no bindings in the path scope
      if (!Identifiers.includes(path.node.name) || path.scope.hasBinding(path.node.name)) {
        return;
      }

      // Avoid: foo.define/require
      if (t.isMemberExpression(path.container) && path.container.property === path.node) {
        return;
      }

      // Avoid class properties/methods
      if ((t.isClassMethod(path.container) || t.isClassProperty(path.container)) && path.container.key === path.node) {
        return;
      }

      // Rename
      path.node.name = IdentifierMap[path.node.name];
    }

  });

  return generator(ast, {
    retainLines: true,
    retainFunctionParens: true
  }, code).code;
}
<|endoftext|>


<commit_before>var model = require('../model.js');
var Rule = require('./AbstractRule.js');

/**
 * One of the less popular variants of backgammon in Bulgaria (гюлбара).
 * @constructor
 * @extends Rule
 */
function RuleAmLongBackgammon() {
    Rule.call(this);

    /**
     * Rule name, matching the class name (eg. 'RuleAmLongBackgammon')
     * @type {string}
     */
    this.name = 'RuleAmLongBackgammon';

    /**
     * Short title describing rule specifics
     * @type {string}
     */
    this.title = 'Long Backgammon';

    /**
     * Full description of rule
     * @type {string}
     */
    this.description = 'One of the less popular variants of backgammon in Armenia.';

    /**
     * Full name of country where this rule (variant) is played.
     * To list multiple countries use a pipe ('|') character as separator.
     * @type {string}
     */
    this.country = 'Armenia';

    /**
     * Two character ISO code of country where this rule (variant) is played.
     * To list multiple codes use a pipe ('|') character as separator.
     * List codes in same order as countries in the field above.
     * @type {string}
     */
    this.countryCode = 'am';

    /**
     * Descendents should list all action types that are allowed in this rule.
     * @type {MoveActionType[]}
     */
    this.allowedActions = [
        model.MoveActionType.MOVE,
        model.MoveActionType.BEAR
    ];

    /**
     * Descendents should list all action types that are allowed in this rule.
     * @type {MoveActionType[]}
     */
    this.startAllowedCombinations = [3, 4, 6];
}

RuleAmLongBackgammon.prototype = Object.create(Rule.prototype);
RuleAmLongBackgammon.prototype.constructor = RuleAmLongBackgammon;

/**
 * Reset state to initial position of pieces according to current rule.
 * @memberOf RuleAmLongBackgammon
 * @param {State} state - Board state
 */
RuleAmLongBackgammon.prototype.resetState = function(state) {
    /**
     * Move pieces to correct initial positions for both players.
     * Values in state.points are zero based and denote the .
     * the number of pieces on each position.
     * Index 0 of array is position 1 and increments to the number of maximum
     * points.
     *
     * Position: |12 13 14 15 16 17| |18 19 20 21 22 23|
     *           |                 | |              15w| <-
     *           |                 | |                 |
     *           |                 | |                 |
     *           |                 | |                 |
     *        -> |15b              | |                 |
     * Position: |11 10 09 08 07 06| |05 04 03 02 01 00|
     *
     */

    model.State.clear(state);

    this.place(state, 15, model.PieceType.WHITE, 23);

    this.place(state, 15, model.PieceType.BLACK, 11);
};

/**
 * Increment position by specified number of steps and return an incremented position
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Denormalized position
 * @param {PieceType} type - Type of piece
 * @param {number} steps - Number of steps to increment towards first home position
 * @returns {number} - Incremented position (denormalized)
 */
RuleAmLongBackgammon.prototype.incPos = function(position, type, steps) {
    var newPosition;
    if (type === model.PieceType.WHITE) {
        newPosition = position - steps;
    }
    else {
        newPosition = position - steps;
        if ((position < 12) && (newPosition < 0)) {
            newPosition = 24 + newPosition;
        }
        else if ((position >= 12) && (newPosition <= 11)) {
            newPosition = newPosition - 12;
        }
    }

    console.log('New pos:', position, newPosition, steps);

    return newPosition;
};

/**
 * Normalize position - Normalized positions start from 0 to 23 for both players,
 * where 0 is the first position in the home part of the board, 6 is the last
 * position in the home part and 23 is the furthest position - in the opponent's
 * home.
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Denormalized position (0 to 23 for white and 12 to 11 for black)
 * @param {PieceType} type - Type of piece (white/black)
 * @returns {number} - Normalized position (0 to 23 for both players)
 */
RuleAmLongBackgammon.prototype.normPos = function(position, type) {
    var normPosition = position;

    if (type === model.PieceType.BLACK) {
        if (position < 0) {
            normPosition = position;
        }
        else if (position >= 12) {
            normPosition = position - 12;
        }
        else {
            normPosition = position + 12;
        }
    }
    return normPosition;
};

/**
 * Get denormalized position - start from 0 to 23 for white player and from
 * 12 to 11 for black player.
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Normalized position (0 to 23 for both players)
 * @param {PieceType} type - Type of piece (white/black)
 * @return {number} - Denormalized position (0 to 23 for white and 12 to 11 for black)
 */
RuleAmLongBackgammon.prototype.denormPos = function(position, type) {
    var denormPosition = position;

    if (type === model.PieceType.BLACK) {
        if (position < 0) {
            denormPosition = position;
        }
        else if (position >= 12) {
            denormPosition = position - 12;
        }
        else {
            denormPosition = position + 12;
        }
    }
    return denormPosition;
};

/**
 * Check is allowd combination or not
 * @param dice
 * @returns bool
 */
RuleAmLongBackgammon.prototype.isAllowedDiceCombinations = function(dice) {
    model.Dice.isDouble(){
        return dice.values[0] === dice.values[1];
    }

    return null;
};

/**
 * Call this method after a request for moving a piece has been made.
 * Determines if the move is allowed and what actions will have to be made as
 * a result. Actions can be `move`, `place`, `hit` or `bear`.
 *
 * If move is allowed or not depends on the current state of the game. For example,
 * if the player has pieces on the bar, they will only be allowed to place pieces.
 *
 * Multiple actions can be returned, if required. Placing (or moving) a piece over
 * an opponent's blot will result in two actions: `hit` first, then `place` (or `move`).
 *
 * The list of actions returned would usually be appllied to game state and then
 * sent to client. The client's UI would play the actions (eg. with movement animation)
 * in the same order.
 *
 * @memberOf RuleAmLongBackgammon
 * @param {Match} currentGame - Match
 * @param {Piece} piece - Piece to move
 * @param {PieceType} type - Type of piece
 * @param {number} steps - Number of steps to increment towards first home position
 * @returns {MoveAction[]} - List of actions if move is allowed, empty list otherwise.
 */
RuleAmLongBackgammon.prototype.getMoveActions = function(currentGame, piece, steps) {
    var actionList = [];
    var state = currentGame.state;
    // Next, check conditions specific to this game rule and build the list of
    // actions that has to be made.

    /**
     * Create a new move action and add it to actionList. Used internally.
     *
     * @alias RuleAmLongBackgammon.getMoveActions.addAction
     * @memberof RuleAmLongBackgammon.getMoveActions
     * @method RuleAmLongBackgammon.getMoveActions.addAction
     * @param {MoveActionType} moveActionType - Type of move action (eg. move, hit, bear)
     * @param {Piece} piece - Piece to move
     * @param {number} from - Denormalized source position. If action uses only one position parameter, this one is used.
     * @param {number} to - Denormalized destination position.
     * @returns {MoveAction}
     * @see {@link getMoveActions} for more information on purpose of move actions.
     */
    function addAction(moveActionType, piece, from, to) {
        var action = new model.MoveAction();
        action.type = moveActionType;
        action.piece = piece;
        action.position = from;
        action.from = from;
        if (typeof to !== "undefined") {
            action.to = to;
        }
        actionList.push(action);
        return action;
    }

    // TODO: Catch exceptions due to disallowed move requests and pass them as error message to the client.
    try {
        var position = model.State.getPiecePos(state, piece);

        // TODO: Consider using state machine? Is it worth, can it be useful in other methods too?
        if (this.allPiecesAreHome(state, piece.type)) {
          /*
           If all pieces are in home field, the player can bear pieces
           Cases:
           - Normalized position >= 0 --> Just move the piece
           - Normalized position === -1 --> Bear piece
           - Normalized position < -1 --> Bear piece, only if there are no player pieces at higher positions

           +12-13-14-15-16-17------18-19-20-21-22-23-+
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |    O  O          |
           |                  |   | O  O  O          |
           +11-10--9--8--7--6-------5--4--3--2--1--0-+ -1

           */
            var destination = this.incPos(position, piece.type, steps);
            var normDestination = this.normPos(destination, piece.type);

            // Move the piece, unless point is blocked by opponent
            if (normDestination >= 0) {

                var destTopPiece = model.State.getTopPiece(state, destination);
                var destTopPieceType = (destTopPiece) ? destTopPiece.type : null;

                // There are no pieces at this point or the top piece is owned by player,
                // so just move piece to that position
                if ((destTopPieceType === null) || (destTopPieceType === piece.type)) {
                    addAction(
                        model.MoveActionType.MOVE, piece, position, destination
                    );
                }
            }
            // If steps are just enought to reach position -1, bear piece
            else if (normDestination === -1) {
                addAction(
                    model.MoveActionType.BEAR, piece, position
                );
            }
            // If steps move the piece beyond -1 position, the player can bear the piece,
            // only if there are no other pieces at higher positions
            else {
                var normSource = this.normPos(position, piece.type);
                if (this.countAtHigherPos(state, normSource + 1, piece.type) <= 0) {
                    addAction(
                        model.MoveActionType.BEAR, piece, position
                    );
                }
            }
        }
        else {
          /*
           If there are no pieces at bar, and at least one piece outside home,
           just move the piece.
           Input data: position=13, steps=3
           Cases:
           - Opponent has one or more pieces --> point is blocked, cannot place piece there
           - Opponent has no pieces there
                Cases:
                -piece is block last place of side and not allow opponent go to finish
                    Cases:
                    -opponent have a piece('s) which are no passed blocked side --> point is blocked, cannot place piece there
                --> place the checker at position 10
           !
           +12-13-14-15-16-17------18-19-20-21-22-23-+
           |    O             |   | X                |
           |    O             |   | X                |
           |                  |   | X                |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |       O          |
           |                  |   | O  O  O          |
           +11-10--9--8--7--6-------5--4--3--2--1--0-+ -1
           !
           */

            var destination = this.incPos(position, piece.type, steps);

            // Make sure the piece is not a second piece of Start place
            // If that second piece of start place but it is allowed combinations of Dice
            if(RuleAmLongBackgammon.isAllowedDiceCombinations(currentGame.turnDice)){

            }else{
                // Make sure that destination is within board
                if ((destination >= 0) && (destination <= 23)) {
                    var normDest = this.normPos(destination, piece.type);
                    // TODO: Make sure position is not outside board

                    var destTopPiece = model.State.getTopPiece(state, destination);
                    var destTopPieceType = (destTopPiece) ? destTopPiece.type : null;

                    // There are no pieces at this point or the top piece is owned by player
                    if ((destTopPieceType === null) || (destTopPieceType === piece.type)) {
                        addAction(
                            model.MoveActionType.MOVE, piece, position, destination
                        );
                    }
                }
            }
        }
    }
    catch (e) {
        actionList = [];
        return actionList;
    }

    return actionList;
};

/**
 * Mark move as played
 * @memberOf RuleAmLongBackgammon
 * @abstract
 * @param {Game} game - Game
 * @param {number} move - Move (number of steps)
 * @returns {boolean} - True if piece was moved/borne/recovered
 */
RuleAmLongBackgammon.prototype.markAsPlayed = function (game, move) {
    // Once a piece is moved, consider that the turn has been started.
    // This flag is used when the player cannot play all moves.
    // Remaining moves are tranfered to other player, only if
    // the turn has been started.
    game.turnStarted = true;
    Rule.prototype.markAsPlayed.call(this, game, move);
};

module.exports = new RuleAmLongBackgammon();<commit_msg>First Commit<commit_after>var model = require('../model.js');
var Rule = require('./AbstractRule.js');

/**
 * One of the less popular variants of backgammon in Bulgaria (гюлбара).
 * @constructor
 * @extends Rule
 */
function RuleAmLongBackgammon() {
    Rule.call(this);

    /**
     * Rule name, matching the class name (eg. 'RuleAmLongBackgammon')
     * @type {string}
     */
    this.name = 'RuleAmLongBackgammon';

    /**
     * Short title describing rule specifics
     * @type {string}
     */
    this.title = 'Long Backgammon';

    /**
     * Full description of rule
     * @type {string}
     */
    this.description = 'One of the less popular variants of backgammon in Armenia.';

    /**
     * Full name of country where this rule (variant) is played.
     * To list multiple countries use a pipe ('|') character as separator.
     * @type {string}
     */
    this.country = 'Armenia';

    /**
     * Two character ISO code of country where this rule (variant) is played.
     * To list multiple codes use a pipe ('|') character as separator.
     * List codes in same order as countries in the field above.
     * @type {string}
     */
    this.countryCode = 'am';

    /**
     * Descendents should list all action types that are allowed in this rule.
     * @type {MoveActionType[]}
     */
    this.allowedActions = [
        model.MoveActionType.MOVE,
        model.MoveActionType.BEAR
    ];

    /**
     * Descendents should list all action types that are allowed in this rule.
     * @type {MoveActionType[]}
     */
    this.startAllowedCombinations = [3, 4, 6];
}

RuleAmLongBackgammon.prototype = Object.create(Rule.prototype);
RuleAmLongBackgammon.prototype.constructor = RuleAmLongBackgammon;

/**
 * Reset state to initial position of pieces according to current rule.
 * @memberOf RuleAmLongBackgammon
 * @param {State} state - Board state
 */
RuleAmLongBackgammon.prototype.resetState = function(state) {
    /**
     * Move pieces to correct initial positions for both players.
     * Values in state.points are zero based and denote the .
     * the number of pieces on each position.
     * Index 0 of array is position 1 and increments to the number of maximum
     * points.
     *
     * Position: |12 13 14 15 16 17| |18 19 20 21 22 23|
     *           |                 | |              15w| <-
     *           |                 | |                 |
     *           |                 | |                 |
     *           |                 | |                 |
     *        -> |15b              | |                 |
     * Position: |11 10 09 08 07 06| |05 04 03 02 01 00|
     *
     */

    model.State.clear(state);

    this.place(state, 15, model.PieceType.WHITE, 23);

    this.place(state, 15, model.PieceType.BLACK, 11);
};

/**
 * Increment position by specified number of steps and return an incremented position
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Denormalized position
 * @param {PieceType} type - Type of piece
 * @param {number} steps - Number of steps to increment towards first home position
 * @returns {number} - Incremented position (denormalized)
 */
RuleAmLongBackgammon.prototype.incPos = function(position, type, steps) {
    var newPosition;
    if (type === model.PieceType.WHITE) {
        newPosition = position - steps;
    }
    else {
        newPosition = position - steps;
        if ((position < 12) && (newPosition < 0)) {
            newPosition = 24 + newPosition;
        }
        else if ((position >= 12) && (newPosition <= 11)) {
            newPosition = newPosition - 12;
        }
    }

    console.log('New pos:', position, newPosition, steps);

    return newPosition;
};

/**
 * Normalize position - Normalized positions start from 0 to 23 for both players,
 * where 0 is the first position in the home part of the board, 6 is the last
 * position in the home part and 23 is the furthest position - in the opponent's
 * home.
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Denormalized position (0 to 23 for white and 12 to 11 for black)
 * @param {PieceType} type - Type of piece (white/black)
 * @returns {number} - Normalized position (0 to 23 for both players)
 */
RuleAmLongBackgammon.prototype.normPos = function(position, type) {
    var normPosition = position;

    if (type === model.PieceType.BLACK) {
        if (position < 0) {
            normPosition = position;
        }
        else if (position >= 12) {
            normPosition = position - 12;
        }
        else {
            normPosition = position + 12;
        }
    }
    return normPosition;
};

/**
 * Get denormalized position - start from 0 to 23 for white player and from
 * 12 to 11 for black player.
 * @memberOf RuleAmLongBackgammon
 * @param {number} position - Normalized position (0 to 23 for both players)
 * @param {PieceType} type - Type of piece (white/black)
 * @return {number} - Denormalized position (0 to 23 for white and 12 to 11 for black)
 */
RuleAmLongBackgammon.prototype.denormPos = function(position, type) {
    var denormPosition = position;

    if (type === model.PieceType.BLACK) {
        if (position < 0) {
            denormPosition = position;
        }
        else if (position >= 12) {
            denormPosition = position - 12;
        }
        else {
            denormPosition = position + 12;
        }
    }
    return denormPosition;
};

/**
 * Check is are allowed combination or not
 * @param dice
 * @returns bool
 */
RuleAmLongBackgammon.prototype.isAllowedDiceCombinations = function(dice) {
    if(model.Dice.isDouble()){
        return ((this.startAllowedCombinations.indexOf(dice.values[0])) > -1);
    }

    return null;
};

/**
 * Call this method after a request for moving a piece has been made.
 * Determines if the move is allowed and what actions will have to be made as
 * a result. Actions can be `move`, `place`, `hit` or `bear`.
 *
 * If move is allowed or not depends on the current state of the game. For example,
 * if the player has pieces on the bar, they will only be allowed to place pieces.
 *
 * Multiple actions can be returned, if required. Placing (or moving) a piece over
 * an opponent's blot will result in two actions: `hit` first, then `place` (or `move`).
 *
 * The list of actions returned would usually be appllied to game state and then
 * sent to client. The client's UI would play the actions (eg. with movement animation)
 * in the same order.
 *
 * @memberOf RuleAmLongBackgammon
 * @param {Match} currentGame - Match
 * @param {Piece} piece - Piece to move
 * @param {PieceType} type - Type of piece
 * @param {number} steps - Number of steps to increment towards first home position
 * @returns {MoveAction[]} - List of actions if move is allowed, empty list otherwise.
 */
RuleAmLongBackgammon.prototype.getMoveActions = function(currentGame, piece, steps) {
    var actionList = [];
    var state = currentGame.state;
    // Next, check conditions specific to this game rule and build the list of
    // actions that has to be made.

    /**
     * Create a new move action and add it to actionList. Used internally.
     *
     * @alias RuleAmLongBackgammon.getMoveActions.addAction
     * @memberof RuleAmLongBackgammon.getMoveActions
     * @method RuleAmLongBackgammon.getMoveActions.addAction
     * @param {MoveActionType} moveActionType - Type of move action (eg. move, hit, bear)
     * @param {Piece} piece - Piece to move
     * @param {number} from - Denormalized source position. If action uses only one position parameter, this one is used.
     * @param {number} to - Denormalized destination position.
     * @returns {MoveAction}
     * @see {@link getMoveActions} for more information on purpose of move actions.
     */
    function addAction(moveActionType, piece, from, to) {
        var action = new model.MoveAction();
        action.type = moveActionType;
        action.piece = piece;
        action.position = from;
        action.from = from;
        if (typeof to !== "undefined") {
            action.to = to;
        }
        actionList.push(action);
        return action;
    }

    // TODO: Catch exceptions due to disallowed move requests and pass them as error message to the client.
    try {
        var position = model.State.getPiecePos(state, piece);

        // TODO: Consider using state machine? Is it worth, can it be useful in other methods too?
        if (this.allPiecesAreHome(state, piece.type)) {
          /*
           If all pieces are in home field, the player can bear pieces
           Cases:
           - Normalized position >= 0 --> Just move the piece
           - Normalized position === -1 --> Bear piece
           - Normalized position < -1 --> Bear piece, only if there are no player pieces at higher positions

           +12-13-14-15-16-17------18-19-20-21-22-23-+
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |    O  O          |
           |                  |   | O  O  O          |
           +11-10--9--8--7--6-------5--4--3--2--1--0-+ -1

           */
            var destination = this.incPos(position, piece.type, steps);
            var normDestination = this.normPos(destination, piece.type);

            // Move the piece, unless point is blocked by opponent
            if (normDestination >= 0) {

                var destTopPiece = model.State.getTopPiece(state, destination);
                var destTopPieceType = (destTopPiece) ? destTopPiece.type : null;

                // There are no pieces at this point or the top piece is owned by player,
                // so just move piece to that position
                if ((destTopPieceType === null) || (destTopPieceType === piece.type)) {
                    addAction(
                        model.MoveActionType.MOVE, piece, position, destination
                    );
                }
            }
            // If steps are just enought to reach position -1, bear piece
            else if (normDestination === -1) {
                addAction(
                    model.MoveActionType.BEAR, piece, position
                );
            }
            // If steps move the piece beyond -1 position, the player can bear the piece,
            // only if there are no other pieces at higher positions
            else {
                var normSource = this.normPos(position, piece.type);
                if (this.countAtHigherPos(state, normSource + 1, piece.type) <= 0) {
                    addAction(
                        model.MoveActionType.BEAR, piece, position
                    );
                }
            }
        }
        else {
          /*
           If there are no pieces at bar, and at least one piece outside home,
           just move the piece.
           Input data: position=13, steps=3
           Cases:
           - Opponent has one or more pieces --> point is blocked, cannot place piece there
           - Opponent has no pieces there
                Cases:
                -piece is block last place of side and not allow opponent go to finish
                    Cases:
                    -opponent have a piece('s) which are no passed blocked side --> point is blocked, cannot place piece there
                --> place the checker at position 10
           !
           +12-13-14-15-16-17------18-19-20-21-22-23-+
           |    O             |   | X                |
           |    O             |   | X                |
           |                  |   | X                |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |                  |
           |                  |   |       O          |
           |                  |   | O  O  O          |
           +11-10--9--8--7--6-------5--4--3--2--1--0-+ -1
           !
           */

            var destination = this.incPos(position, piece.type, steps);

            // Make sure the piece is not a second piece of Start place
            // If that second piece of start place but it is allowed combinations of Dice
            // if(! RuleAmLongBackgammon.isAllowedDiceCombinations(currentGame.turnDice)){
            if(false){

            }else{
                // Make sure that destination is within board
                if ((destination >= 0) && (destination <= 23)) {
                    var normDest = this.normPos(destination, piece.type);
                    // TODO: Make sure position is not outside board

                    var destTopPiece = model.State.getTopPiece(state, destination);
                    var destTopPieceType = (destTopPiece) ? destTopPiece.type : null;

                    // There are no pieces at this point or the top piece is owned by player
                    if ((destTopPieceType === null) || (destTopPieceType === piece.type)) {
                        addAction(
                            model.MoveActionType.MOVE, piece, position, destination
                        );
                    }
                }
            }
        }
    }
    catch (e) {
        actionList = [];
        return actionList;
    }

    return actionList;
};

/**
 * Mark move as played
 * @memberOf RuleAmLongBackgammon
 * @abstract
 * @param {Game} game - Game
 * @param {number} move - Move (number of steps)
 * @returns {boolean} - True if piece was moved/borne/recovered
 */
RuleAmLongBackgammon.prototype.markAsPlayed = function (game, move) {
    // Once a piece is moved, consider that the turn has been started.
    // This flag is used when the player cannot play all moves.
    // Remaining moves are tranfered to other player, only if
    // the turn has been started.
    game.turnStarted = true;
    Rule.prototype.markAsPlayed.call(this, game, move);
};

module.exports = new RuleAmLongBackgammon();<|endoftext|>


<commit_before>'use strict';

/**
 * @fileoverview Limit the number of top-level suites in a single file
 * @author Alexander Afanasyev
 */

var astUtil = require('../util/ast'),
    defaultSuiteLimit = 1;

module.exports = function (context) {
    var stack = [],
        topLevelDescribes = [],
        suiteLimit = context.options[0] ? context.options[0] : defaultSuiteLimit;

    return {
        CallExpression: function (node) {
            if (astUtil.isDescribe(node)) {
                stack.push(node);
            }
        },

        'CallExpression:exit': function (node) {
            if (astUtil.isDescribe(node)) {
                if (stack.length === 1) {
                    topLevelDescribes.push(node);
                }

                stack.pop(node);
            }
        },

        'Program:exit': function () {
            if (topLevelDescribes.length > suiteLimit) {
                context.report({
                    node: topLevelDescribes[1],
                    message: 'The number of top-level suites is more than ' + suiteLimit + '.'
                });
            }
        }
    };
};
<commit_msg>Reporting appropriate suite<commit_after>'use strict';

/**
 * @fileoverview Limit the number of top-level suites in a single file
 * @author Alexander Afanasyev
 */

var astUtil = require('../util/ast'),
    defaultSuiteLimit = 1;

module.exports = function (context) {
    var stack = [],
        topLevelDescribes = [],
        suiteLimit = context.options[0] ? context.options[0] : defaultSuiteLimit;

    return {
        CallExpression: function (node) {
            if (astUtil.isDescribe(node)) {
                stack.push(node);
            }
        },

        'CallExpression:exit': function (node) {
            if (astUtil.isDescribe(node)) {
                if (stack.length === 1) {
                    topLevelDescribes.push(node);
                }

                stack.pop(node);
            }
        },

        'Program:exit': function () {
            if (topLevelDescribes.length > suiteLimit) {
                context.report({
                    node: topLevelDescribes[suiteLimit],
                    message: 'The number of top-level suites is more than ' + suiteLimit + '.'
                });
            }
        }
    };
};
<|endoftext|>


<commit_before>var _ = require('underscore');

var hoodiejs = require('../../../helpers/pack_hoodie');
var plugins = require('../../../helpers/plugin_api');

exports.register = function (plugin, options, next) {

  'use strict';

  var internals = {
    couchCfg: options.app.couch,
    mapProxyPath: function (request, callback) {
      callback(null, internals.couchCfg.url + request.url.path.substr('/_api'.length));
    },
    getHoodiePath: function (request, reply) {
      reply(hoodiejs(options.app))
      .type('application/javascript')
      .header(
        'Pragma', 'no-cache'
      );
    },
    notFound: function (request, reply) {
      reply({
        'error': 'not found'
      }).code(404);
    }
  };

  plugin.route([
    {
      method: 'GET',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'PUT',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'POST',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'DELETE',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins',
      handler: function (req, res) {
        res(plugins.metadata(options.app));
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}',
      handler: function (request, reply) {
        var metaData = _.find(plugins.metadata(options.app), function (doc) {
          return doc.name === request.params.name;
        });

        if (!metaData) {
          reply({
            'error': 'not found'
          }).code(404);
        } else {
          reply(metaData);
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}/pocket',
      handler: function (req, res) {
        res.redirect('/');
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}/pocket/{path*}',
      handler: {
        directory: {
          path: function (request) {
            return plugins.pockets(options.app)[request.params.name];
          },
          listing: false,
          index: true
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/_assets/{path*}',
      handler: {
        directory: {
          path: options.app.project_dir + '/node_modules/hoodie-server/node_modules/hoodie-pocket-uikit/app',
          listing: true,
          index: false
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_files/hoodie.js',
      handler: internals.getHoodiePath
    }
  ]);

  return next();
};
<commit_msg>[fix] Assets folder needs to point to the built version, not the source<commit_after>var _ = require('underscore');

var hoodiejs = require('../../../helpers/pack_hoodie');
var plugins = require('../../../helpers/plugin_api');

exports.register = function (plugin, options, next) {

  'use strict';

  var internals = {
    couchCfg: options.app.couch,
    mapProxyPath: function (request, callback) {
      callback(null, internals.couchCfg.url + request.url.path.substr('/_api'.length));
    },
    getHoodiePath: function (request, reply) {
      reply(hoodiejs(options.app))
      .type('application/javascript')
      .header(
        'Pragma', 'no-cache'
      );
    },
    notFound: function (request, reply) {
      reply({
        'error': 'not found'
      }).code(404);
    }
  };

  plugin.route([
    {
      method: 'GET',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'PUT',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'POST',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'DELETE',
      path: '/_api/{p*}',
      handler: {
        proxy: {
          passThrough: true,
          mapUri: internals.mapProxyPath
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins',
      handler: function (req, res) {
        res(plugins.metadata(options.app));
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}',
      handler: function (request, reply) {
        var metaData = _.find(plugins.metadata(options.app), function (doc) {
          return doc.name === request.params.name;
        });

        if (!metaData) {
          reply({
            'error': 'not found'
          }).code(404);
        } else {
          reply(metaData);
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}/pocket',
      handler: function (req, res) {
        res.redirect('/');
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/{name}/pocket/{path*}',
      handler: {
        directory: {
          path: function (request) {
            return plugins.pockets(options.app)[request.params.name];
          },
          listing: false,
          index: true
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_plugins/_assets/{path*}',
      handler: {
        directory: {
          path: options.app.project_dir + '/node_modules/hoodie-server/node_modules/hoodie-pocket-uikit/dist',
          listing: true,
          index: false
        }
      }
    },
    {
      method: 'GET',
      path: '/_api/_files/hoodie.js',
      handler: internals.getHoodiePath
    }
  ]);

  return next();
};
<|endoftext|>