<commit_before>'use strict';
var util = require('util');
var _ = require('lodash');
var async = require('async');
var schedule = require('node-schedule');
var mongoose = require('mongoose');
var VirtualDevice = require('./../virtual-device').VirtualDevice;
var logger = require('../mlogger/mlogger.js');
var ONE_HOUR_MS = 1000 * 60 * 60;
var ONE_DAY_MS = ONE_HOUR_MS * 24;
var REPORT_DATA_TYPE = ["dailyReport"];//["dailyReport", "monthlyReport", "yearlyReport", "allReport"];
var DEVICE_DATA_SCHEMA = {
    "uuid": mongoose.SchemaTypes.String,
    "userId": mongoose.SchemaTypes.String,
    "type": mongoose.SchemaTypes.String,
    "timestamp": mongoose.SchemaTypes.Date,
    "offset": mongoose.SchemaTypes.Number,
    "data": mongoose.SchemaTypes.Mixed
};
var OPERATION_SCHEMAS = {
    statistics: {
        "type": "object",
        "properties": {
            "dataType": {"type": "string"},
            "timestamp": {
                "type": "object",
                "properties": {
                    "$gte": {"type": "string"},
                    "$lt": {"type": "string"}
                }
            }
        }
    }
};

var dataMap = function () {
    emit(this.uuid, this);
};
var dailyDataReduce = function (key, values) {
    var record = JSON.parse(JSON.stringify(values[0]));
    var len = values.length;
    delete record.__v;
    delete record._id;
    if ("050608070001" === record.type) {
        var sumTemp = 0;
        values.forEach(function (item) {
            sumTemp += item.data.dis_temp;
        });
        record.data.dis_temp = sumTemp / len;
    }
    else if ("040B08040004" === record.type) {
        var sumPower = 0;
        values.forEach(function (item) {
            sumPower += item.data.power;
        });
        record.data.power = sumPower / len;
        if (len > 1) {
            record.data.energyUsed = values[len - 1].data.energyUsed - values[0].data.energyUsed;
            record.data.energySaved = values[len - 1].data.energySaved - values[0].data.energySaved;
        }
    }
    else if ("040B01000001" === record.type) {
        var sumEffectiveVolt = 0;
        var sumEffectiveCurrent = 0;
        values.forEach(function (item) {
            if (16 === item.data.direct) {
                sumEffectiveVolt += item.data.effectiveVolt;
                sumEffectiveCurrent += item.data.effectiveCurrent;
            }
            else if (17 === item.data.direct) {
                sumEffectiveVolt -= item.data.effectiveVolt;
                sumEffectiveCurrent -= item.data.effectiveCurrent;
            }
        });
        record.data.power = (sumEffectiveVolt / len) * (sumEffectiveCurrent / len) / 100;
        delete record.data.effectiveVolt;
        delete record.data.effectiveCurrent;
        delete record.data.direct;
        if (len > 1) {
            record.data.toGrid = values[len - 1].data.toGrid - values[0].data.toGrid;
            record.data.toUser = values[len - 1].data.toUser - values[0].data.toUser;
        }
    }
    else if ("040B01000005" === record.type) {
        var sumPacToGrid = 0;
        var sumPacToUser = 0;
        values.forEach(function (item) {
            sumPacToGrid += item.data.pacToGrid;
            sumPacToUser += item.data.pacToUser;
        });
        record.data.pacToGrid = sumPacToGrid / len;
        record.data.pacToUser = sumPacToUser / len;
        if (len > 1) {
            record.data.eDisChargeTotal = values[len - 1].data.eDisChargeTotal - values[0].data.eDisChargeTotal;
            record.data.eChargeTotal = values[len - 1].data.eChargeTotal - values[0].data.eChargeTotal;
            record.data.eToGridTotal = values[len - 1].data.eToGridTotal - values[0].data.eToGridTotal;
        }
    }
    else if ("040B01000004" === record.type) {
        var sumCurrentPower = 0;
        values.forEach(function (item) {
            sumCurrentPower += item.data.currentPower;
        });
        record.data.currentPower = sumCurrentPower / len;
        if (len > 1) {
            record.data.totalEnergy = values[len - 1].data.totalEnergy - values[0].data.totalEnergy;
        }
    }
    record.debug_={
        len:len,
        values:values
    };
    return record;
};
var dataReduce = function (key, values) {
    var record = JSON.parse(JSON.stringify(values[0]));
    var len = values.length;
    delete record.__v;
    delete record._id;
    if ("050608070001" === record.type) {
        var sumTemp = 0;
        values.forEach(function (item) {
            sumTemp += item.data.dis_temp
        });
        record.data.dis_temp = sumTemp / len;
    }
    else if ("040B08040004" === record.type) {
        var sumPower = 0;
        var sumEnergyUsed = 0;
        var sumEnergySaved = 0;
        values.forEach(function (item) {
            sumPower += item.data.power;
            sumEnergyUsed += item.data.energyUsed;
            sumEnergySaved += item.data.energySaved
        });
        record.data.power = sumPower / len;
        record.data.energyUsed = sumEnergyUsed;
        record.data.energySaved = sumEnergySaved;
    }
    else if ("040B01000001" === record.type) {
        var sumCurPower = 0;
        var sumToGrid = 0;
        var sumToUser = 0;
        values.forEach(function (item) {
            sumToGrid += item.data.toGrid;
            sumToUser += item.data.toUser;
            sumCurPower += item.data.power;
        });
        record.data.power = sumCurPower / len;
        record.data.toGrid = sumToGrid;
        record.data.toUser = sumToUser;
    }
    else if ("040B01000005" === record.type) {
        var sumPacToGrid = 0;
        var sumPacToUser = 0;
        var sumEDisChargeTotal = 0;
        var sumEChargeTotal = 0;
        var sumEToGridTotal = 0;
        values.forEach(function (item) {
            sumPacToGrid += item.data.pacToGrid;
            sumPacToUser += item.data.pacToUser;
            sumEDisChargeTotal += item.data.eDisChargeTotal;
            sumEChargeTotal += item.data.eChargeTotal;
            sumEToGridTotal += item.data.eToGridTotal;
        });
        record.data.pacToGrid = sumPacToGrid / len;
        record.data.pacToUser = sumPacToUser / len;
        record.data.eDisChargeTotal = sumEDisChargeTotal;
        record.data.eChargeTotal = sumEChargeTotal;
        record.data.eToGridTotal = sumEToGridTotal;
    }
    else if ("040B01000004" === record.type) {
        var sumCurrentPower = 0;
        var sumTotalEnergy = 0;
        values.forEach(function (item) {
            sumCurrentPower += item.data.currentPower;
            sumTotalEnergy += item.data.totalEnergy;
        });
        record.data.currentPower = sumCurrentPower / len;
        record.data.totalEnergy = sumTotalEnergy;
    }

    return record;
};
/**
 * @constructor
 * */
function dataStatistics(conx, uuid, token, configurator) {
    this.db = null;
    this.scheduleJob = null;
    this.dataModel = null;
    this.dailyDataModel = null;
    this.monthlyDataModel = null;
    this.yearlyDataModel = null;
    this.allDataModel = null;
    this.statisticsAll = function () {
        var self = this;
        async.mapSeries(REPORT_DATA_TYPE, function (dataType, callback) {
            var curTime = Date.now();
            var curDate = new Date(curTime);
            var startTime = null;
            var endTime = null;
            if ("dailyReport" === dataType) {
                endTime = curTime - curTime % ONE_HOUR_MS;
                startTime = endTime - ONE_HOUR_MS;
            }
            else if ("monthlyReport" === dataType) {
                startTime = curDate.setHours(0, 0, 0, 0);
                endTime = startTime + ONE_DAY_MS;
            }
            else if ("yearlyReport" === dataType) {
                var curMoth = curDate.getMonth();
                curDate.setHours(0, 0, 0, 0);
                startTime = curDate.setMonth(curMoth, 1);
                endTime = curDate.setMonth(curMoth + 1, 1);
            }
            else if ("allReport" === dataType) {
                var curYear = curDate.getFullYear();
                curDate.setHours(0, 0, 0, 0);
                curDate.setMonth(0, 1);
                startTime = curDate.setFullYear(curYear);
                endTime = curDate.setFullYear(curYear + 1);
            }
            self.statistics({
                "dataType": dataType,
                "timestamp": {
                    "$gte": new Date(startTime).toISOString(),
                    "$lt": new Date(endTime).toISOString()
                }
            }, function (resp) {
                callback(null, resp.data);
            });
        }, function (error, results) {
            logger.debug("**************************************************");
            logger.debug(results);
        });
    };
    VirtualDevice.call(this, conx, uuid, token, configurator);
}
util.inherits(dataStatistics, VirtualDevice);

/**
 * 设备管理器初始化，将系统已经添加的设备实例化并挂载到Meshblu网络
 * */
dataStatistics.prototype.init = function () {
    var self = this;
    var db = mongoose.createConnection(self.configurator.getConf("meshblu_server.db_url"));
    mongoose.Promise = global.Promise;
    db.once('error', function (error) {
        logger.error(200005, error);
    });

    db.once('open', function () {
        self.db = db;
        self.dataModel = db.model("data", DEVICE_DATA_SCHEMA);
        self.dailyDataModel = db.model("daily_data", DEVICE_DATA_SCHEMA);
        self.monthlyDataModel = db.model("monthly_data", DEVICE_DATA_SCHEMA);
        self.yearlyDataModel = db.model("yearly_data", DEVICE_DATA_SCHEMA);
        self.allDataModel = self.db.model("all_data", DEVICE_DATA_SCHEMA);
        self.statisticsAll();
       /* self.scheduleJob = schedule.scheduleJob("3 * * * *", function () {
            self.statisticsAll();
        });*/
    });
};

/**
 * 远程RPC回调函数
 * @callback onMessage~statistics
 * @param {object} response:
 * {
 *      "payload":
 *      {
 *          "code":{number},
 *          "message":{string},
 *          "data":null
 *      }
 * }
 */
/**
 * 获取分析结果
 * @param {object} message:消息体
 * @param {onMessage~statistics} peerCallback: 远程RPC回调函数
 * */
dataStatistics.prototype.statistics = function (message, peerCallback) {
    var self = this;
    var responseMessage = {retCode: 200, description: "Success.", data: {}};
    self.messageValidate(message, OPERATION_SCHEMAS.statistics, function (error) {
        if (error) {
            responseMessage = error;
            peerCallback(error);
        }
        else {
            var srcDataModel = null;
            var disDataModel = null;
            var reduceFun = dataReduce;
            if ("dailyReport" === message.dataType) {
                srcDataModel = self.dataModel;
                disDataModel = self.dailyDataModel;
                reduceFun = dailyDataReduce;
            }
            else if ("monthlyReport" === message.dataType) {
                srcDataModel = self.dailyDataModel;
                disDataModel = self.monthlyDataModel;
            }
            else if ("yearlyReport" === message.dataType) {
                srcDataModel = self.monthlyDataModel;
                disDataModel = self.yearlyDataModel;
            }
            else if ("allReport" === message.dataType) {
                srcDataModel = self.yearlyDataModel;
                disDataModel = self.allDataModel;
            }
            else {
                responseMessage.retCode = 200001;
                responseMessage.message = "invalid report data type:" + message.dataType;
                peerCallback(responseMessage);
                return;
            }
            var command = {
                query: {
                    "timestamp": {
                        "$gte": new Date(message.timestamp.$gte),
                        "$lt": new Date(message.timestamp.$lt)
                    }
                },
                map: dataMap,
                limit:6,
                reduce: reduceFun,
                verbose: true
            };
            srcDataModel.mapReduce(command, function (err, dbres, stats) {
                if (err) {
                    logger.debug(err);
                    responseMessage.retCode = 200000;
                    responseMessage.message = err.toString();
                    peerCallback(responseMessage);
                }
                else {
                    logger.debug("======================================================");
                    logger.debug(dbres);
                    async.mapSeries(dbres, function (result, callback) {
                        delete result.value.__v;
                        delete result.value._id;
                        result.value.timestamp = command.query.timestamp.$gte;
                        var query = {
                            "uuid": result.value.uuid,
                            "timestamp": result.value.timestamp
                        };
                        var update = result.value;
                        var options = {
                            new: true,
                            upsert: true
                        };
                        disDataModel.findOneAndUpdate(query, update, options, function (error, doc) {
                            if (error) {
                                logger.debug(error)
                            }
                            else {
                                //logger.debug(doc)
                            }
                            callback(null,doc);
                        });
                    }, function (error, results) {
                        responseMessage.data = {statistics:message,stats:stats, results:results};
                        peerCallback(responseMessage);
                    });
                }
            });
        }
    });
};

module.exports = {
    Service: dataStatistics,
    OperationSchemas: OPERATION_SCHEMAS
};<commit_msg>统计数据入库前去重复数据<commit_after>'use strict';
var util = require('util');
var _ = require('lodash');
var async = require('async');
var schedule = require('node-schedule');
var mongoose = require('mongoose');
var VirtualDevice = require('./../virtual-device').VirtualDevice;
var logger = require('../mlogger/mlogger.js');
var ONE_HOUR_MS = 1000 * 60 * 60;
var ONE_DAY_MS = ONE_HOUR_MS * 24;
var REPORT_DATA_TYPE = ["dailyReport"];//["dailyReport", "monthlyReport", "yearlyReport", "allReport"];
var DEVICE_DATA_SCHEMA = {
    "uuid": mongoose.SchemaTypes.String,
    "userId": mongoose.SchemaTypes.String,
    "type": mongoose.SchemaTypes.String,
    "timestamp": mongoose.SchemaTypes.Date,
    "offset": mongoose.SchemaTypes.Number,
    "data": mongoose.SchemaTypes.Mixed
};
var OPERATION_SCHEMAS = {
    statistics: {
        "type": "object",
        "properties": {
            "dataType": {"type": "string"},
            "timestamp": {
                "type": "object",
                "properties": {
                    "$gte": {"type": "string"},
                    "$lt": {"type": "string"}
                }
            }
        }
    }
};

var dataMap = function () {
    emit(this.uuid, this);
};
var dailyDataReduce = function (key, values) {
    var record = JSON.parse(JSON.stringify(values[0]));
    var len = values.length;
    delete record.__v;
    delete record._id;
    if ("050608070001" === record.type) {
        var sumTemp = 0;
        values.forEach(function (item) {
            sumTemp += item.data.dis_temp;
        });
        record.data.dis_temp = sumTemp / len;
    }
    else if ("040B08040004" === record.type) {
        var sumPower = 0;
        values.forEach(function (item) {
            sumPower += item.data.power;
        });
        record.data.power = sumPower / len;
        if (len > 1) {
            record.data.energyUsed = values[len - 1].data.energyUsed - values[0].data.energyUsed;
            record.data.energySaved = values[len - 1].data.energySaved - values[0].data.energySaved;
        }
    }
    else if ("040B01000001" === record.type) {
        var sumEffectiveVolt = 0;
        var sumEffectiveCurrent = 0;
        values.forEach(function (item) {
            if (16 === item.data.direct) {
                sumEffectiveVolt += item.data.effectiveVolt;
                sumEffectiveCurrent += item.data.effectiveCurrent;
            }
            else if (17 === item.data.direct) {
                sumEffectiveVolt -= item.data.effectiveVolt;
                sumEffectiveCurrent -= item.data.effectiveCurrent;
            }
        });
        record.data.power = (sumEffectiveVolt / len) * (sumEffectiveCurrent / len) / 100;
        delete record.data.effectiveVolt;
        delete record.data.effectiveCurrent;
        delete record.data.direct;
        if (len > 1) {
            record.data.toGrid = values[len - 1].data.toGrid - values[0].data.toGrid;
            record.data.toUser = values[len - 1].data.toUser - values[0].data.toUser;
        }
    }
    else if ("040B01000005" === record.type) {
        var sumPacToGrid = 0;
        var sumPacToUser = 0;
        values.forEach(function (item) {
            sumPacToGrid += item.data.pacToGrid;
            sumPacToUser += item.data.pacToUser;
        });
        record.data.pacToGrid = sumPacToGrid / len;
        record.data.pacToUser = sumPacToUser / len;
        if (len > 1) {
            record.data.eDisChargeTotal = values[len - 1].data.eDisChargeTotal - values[0].data.eDisChargeTotal;
            record.data.eChargeTotal = values[len - 1].data.eChargeTotal - values[0].data.eChargeTotal;
            record.data.eToGridTotal = values[len - 1].data.eToGridTotal - values[0].data.eToGridTotal;
        }
    }
    else if ("040B01000004" === record.type) {
        var sumCurrentPower = 0;
        values.forEach(function (item) {
            sumCurrentPower += item.data.currentPower;
        });
        record.data.currentPower = sumCurrentPower / len;
        if (len > 1) {
            record.data.totalEnergy = values[len - 1].data.totalEnergy - values[0].data.totalEnergy;
        }
    }
    record.debug_={
        len:len,
        values:values
    };
    return record;
};
var dataReduce = function (key, values) {
    var record = JSON.parse(JSON.stringify(values[0]));
    var len = values.length;
    delete record.__v;
    delete record._id;
    if ("050608070001" === record.type) {
        var sumTemp = 0;
        values.forEach(function (item) {
            sumTemp += item.data.dis_temp
        });
        record.data.dis_temp = sumTemp / len;
    }
    else if ("040B08040004" === record.type) {
        var sumPower = 0;
        var sumEnergyUsed = 0;
        var sumEnergySaved = 0;
        values.forEach(function (item) {
            sumPower += item.data.power;
            sumEnergyUsed += item.data.energyUsed;
            sumEnergySaved += item.data.energySaved
        });
        record.data.power = sumPower / len;
        record.data.energyUsed = sumEnergyUsed;
        record.data.energySaved = sumEnergySaved;
    }
    else if ("040B01000001" === record.type) {
        var sumCurPower = 0;
        var sumToGrid = 0;
        var sumToUser = 0;
        values.forEach(function (item) {
            sumToGrid += item.data.toGrid;
            sumToUser += item.data.toUser;
            sumCurPower += item.data.power;
        });
        record.data.power = sumCurPower / len;
        record.data.toGrid = sumToGrid;
        record.data.toUser = sumToUser;
    }
    else if ("040B01000005" === record.type) {
        var sumPacToGrid = 0;
        var sumPacToUser = 0;
        var sumEDisChargeTotal = 0;
        var sumEChargeTotal = 0;
        var sumEToGridTotal = 0;
        values.forEach(function (item) {
            sumPacToGrid += item.data.pacToGrid;
            sumPacToUser += item.data.pacToUser;
            sumEDisChargeTotal += item.data.eDisChargeTotal;
            sumEChargeTotal += item.data.eChargeTotal;
            sumEToGridTotal += item.data.eToGridTotal;
        });
        record.data.pacToGrid = sumPacToGrid / len;
        record.data.pacToUser = sumPacToUser / len;
        record.data.eDisChargeTotal = sumEDisChargeTotal;
        record.data.eChargeTotal = sumEChargeTotal;
        record.data.eToGridTotal = sumEToGridTotal;
    }
    else if ("040B01000004" === record.type) {
        var sumCurrentPower = 0;
        var sumTotalEnergy = 0;
        values.forEach(function (item) {
            sumCurrentPower += item.data.currentPower;
            sumTotalEnergy += item.data.totalEnergy;
        });
        record.data.currentPower = sumCurrentPower / len;
        record.data.totalEnergy = sumTotalEnergy;
    }

    return record;
};
/**
 * @constructor
 * */
function dataStatistics(conx, uuid, token, configurator) {
    this.db = null;
    this.scheduleJob = null;
    this.dataModel = null;
    this.dailyDataModel = null;
    this.monthlyDataModel = null;
    this.yearlyDataModel = null;
    this.allDataModel = null;
    this.statisticsAll = function () {
        var self = this;
        async.mapSeries(REPORT_DATA_TYPE, function (dataType, callback) {
            var curTime = Date.now();
            var curDate = new Date(curTime);
            var startTime = null;
            var endTime = null;
            if ("dailyReport" === dataType) {
                endTime = curTime - curTime % ONE_HOUR_MS;
                startTime = endTime - ONE_HOUR_MS;
            }
            else if ("monthlyReport" === dataType) {
                startTime = curDate.setHours(0, 0, 0, 0);
                endTime = startTime + ONE_DAY_MS;
            }
            else if ("yearlyReport" === dataType) {
                var curMoth = curDate.getMonth();
                curDate.setHours(0, 0, 0, 0);
                startTime = curDate.setMonth(curMoth, 1);
                endTime = curDate.setMonth(curMoth + 1, 1);
            }
            else if ("allReport" === dataType) {
                var curYear = curDate.getFullYear();
                curDate.setHours(0, 0, 0, 0);
                curDate.setMonth(0, 1);
                startTime = curDate.setFullYear(curYear);
                endTime = curDate.setFullYear(curYear + 1);
            }
            self.statistics({
                "dataType": dataType,
                "timestamp": {
                    "$gte": new Date(startTime).toISOString(),
                    "$lt": new Date(endTime).toISOString()
                }
            }, function (resp) {
                callback(null, resp.data);
            });
        }, function (error, results) {
            logger.debug("**************************************************");
            logger.debug(results);
        });
    };
    VirtualDevice.call(this, conx, uuid, token, configurator);
}
util.inherits(dataStatistics, VirtualDevice);

/**
 * 设备管理器初始化，将系统已经添加的设备实例化并挂载到Meshblu网络
 * */
dataStatistics.prototype.init = function () {
    var self = this;
    var db = mongoose.createConnection(self.configurator.getConf("meshblu_server.db_url"));
    mongoose.Promise = global.Promise;
    db.once('error', function (error) {
        logger.error(200005, error);
    });

    db.once('open', function () {
        self.db = db;
        self.dataModel = db.model("data", DEVICE_DATA_SCHEMA);
        self.dailyDataModel = db.model("daily_data", DEVICE_DATA_SCHEMA);
        self.monthlyDataModel = db.model("monthly_data", DEVICE_DATA_SCHEMA);
        self.yearlyDataModel = db.model("yearly_data", DEVICE_DATA_SCHEMA);
        self.allDataModel = self.db.model("all_data", DEVICE_DATA_SCHEMA);
        self.statisticsAll();
       /* self.scheduleJob = schedule.scheduleJob("3 * * * *", function () {
            self.statisticsAll();
        });*/
    });
};

/**
 * 远程RPC回调函数
 * @callback onMessage~statistics
 * @param {object} response:
 * {
 *      "payload":
 *      {
 *          "code":{number},
 *          "message":{string},
 *          "data":null
 *      }
 * }
 */
/**
 * 获取分析结果
 * @param {object} message:消息体
 * @param {onMessage~statistics} peerCallback: 远程RPC回调函数
 * */
dataStatistics.prototype.statistics = function (message, peerCallback) {
    var self = this;
    var responseMessage = {retCode: 200, description: "Success.", data: {}};
    self.messageValidate(message, OPERATION_SCHEMAS.statistics, function (error) {
        if (error) {
            responseMessage = error;
            peerCallback(error);
        }
        else {
            var srcDataModel = null;
            var disDataModel = null;
            var reduceFun = dataReduce;
            if ("dailyReport" === message.dataType) {
                srcDataModel = self.dataModel;
                disDataModel = self.dailyDataModel;
                reduceFun = dailyDataReduce;
            }
            else if ("monthlyReport" === message.dataType) {
                srcDataModel = self.dailyDataModel;
                disDataModel = self.monthlyDataModel;
            }
            else if ("yearlyReport" === message.dataType) {
                srcDataModel = self.monthlyDataModel;
                disDataModel = self.yearlyDataModel;
            }
            else if ("allReport" === message.dataType) {
                srcDataModel = self.yearlyDataModel;
                disDataModel = self.allDataModel;
            }
            else {
                responseMessage.retCode = 200001;
                responseMessage.message = "invalid report data type:" + message.dataType;
                peerCallback(responseMessage);
                return;
            }
            var command = {
                query: {
                    "timestamp": {
                        "$gte": new Date(message.timestamp.$gte),
                        "$lt": new Date(message.timestamp.$lt)
                    }
                },
                map: dataMap,
                limit:12,
                reduce: reduceFun,
                verbose: true
            };
            srcDataModel.mapReduce(command, function (err, dbres, stats) {
                if (err) {
                    logger.debug(err);
                    responseMessage.retCode = 200000;
                    responseMessage.message = err.toString();
                    peerCallback(responseMessage);
                }
                else {
                    logger.debug("======================================================");
                    logger.debug(dbres);
                    async.mapSeries(dbres, function (result, callback) {
                        delete result.value.__v;
                        delete result.value._id;
                        result.value.timestamp = command.query.timestamp.$gte;
                        var query = {
                            "uuid": result.value.uuid,
                            "timestamp": result.value.timestamp
                        };
                        var update = result.value;
                        var options = {
                            new: true,
                            upsert: true
                        };
                        disDataModel.findOneAndUpdate(query, update, options, function (error, doc) {
                            if (error) {
                                logger.debug(error)
                            }
                            else {
                                //logger.debug(doc)
                            }
                            callback(null,doc);
                        });
                    }, function (error, results) {
                        responseMessage.data = {statistics:message,stats:stats, results:results};
                        peerCallback(responseMessage);
                    });
                }
            });
        }
    });
};

module.exports = {
    Service: dataStatistics,
    OperationSchemas: OPERATION_SCHEMAS
};<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";
let nextIdent = 0;
class CommonsChunkPlugin {
	constructor(options) {
		if(arguments.length > 1) {
			throw new Error(`Deprecation notice: CommonsChunkPlugin now only takes a single argument. Either an options
object *or* the name of the chunk.
Example: if your old code looked like this:
	new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js')
You would change it to:
	new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' })
The available options are:
	name: string
	names: string[]
	filename: string
	minChunks: number
	chunks: string[]
	children: boolean
	async: boolean
	minSize: number`);
		}

		const normalizedOptions = this.normalizeOptions(options);

		this.chunkNames = normalizedOptions.chunkNames;
		this.filenameTemplate = this.filenameTemplate;
		this.minChunks = normalizedOptions.minChunks;
		this.selectedChunks = normalizedOptions.selectedChunks;
		this.async = normalizedOptions.async;
		this.minSize = normalizedOptions.minSize;
		this.ident = normalizedOptions.ident;
	}

	normalizeOptions(options) {
		if(Array.isArray(options)) {
			return {
				chunkNames: options,
			};
		}

		if(typeof options === "string") {
			return {
				chunkNames: [options],
			};
		}

		// if "children" is set, set selectedChunks to false instead of specified chunks
		// TODO: why? :P
		const selectedChunks = options.children ? false : options.chunks;
		const chunkNames = options.name ? [options.name] : options.names;
		return {
			chunkNames: chunkNames,
			filenameTemplate: options.filename,
			minChunks: options.minChunks,
			selectedChunks: selectedChunks,
			async: options.async,
			minSize: options.minSize,
			ident: __filename + (nextIdent++),
		};
	}

	getCommonChunks(allChunks, compilation) {
		const asyncOrNoSelectedChunk = this.selectedChunks === false || this.async;

		// we have specified chunk names
		if(this.chunkNames) {
			// map chunks by chunkName for quick access
			const optimizedChunkMap = allChunks.reduce((map, chunk) => {
				map.set(chunk.name, chunk);
				return map;
			}, new Map());

			// Ensure we have a chunk per specified chunk name.
			// Reuse existing chunks if possible
			return this.chunkNames.map(chunkName => {
				if(optimizedChunkMap.has(chunkName)) {
					return optimizedChunkMap.get(chunkName);
				}
				// add the filtered chunks to the compilation
				return compilation.addChunk(chunkName);
			});
		}

		// we dont have named chunks specified, so we just take all of them
		if(asyncOrNoSelectedChunk) {
			return allChunks;
		}

		// that is not supposed to happen, lets throw
		throw new Error("Invalid chunkNames argument");
	}

	getUsedChunks(compilation, allChunks, commonChunk, commonChunks, currentIndex, selectedChunks, isAsync) {
		const asyncOrNoSelectedChunk = selectedChunks === false || isAsync;

		if(Array.isArray(selectedChunks)) {
			return allChunks.filter(chunk => {
				const notCommmonChunk = chunk !== commonChunk;
				const isSelectedChunk = selectedChunks.indexOf(chunk.name) > -1;
				return notCommmonChunk && isSelectedChunk;
			});
		}

		if(asyncOrNoSelectedChunk) {
			// nothing to do here
			if(!commonChunk.chunks) {
				return [];
			}

			return commonChunk.chunks.filter((chunk) => {
				// we can only move modules from this chunk if the "commonChunk" is the only parent
				return isAsync || chunk.parents.length === 1;
			});
		}

		// this is an entry point - bad
		if(commonChunk.parents.length > 0) {
			compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
			return;
		}

		return allChunks.filter((chunk) => {
			const found = commonChunks.indexOf(chunk);
			if(found >= currentIndex) return false;
			return chunk.hasRuntime();
		});
	}

	apply(compiler) {
		const filenameTemplate = this.filenameTemplate;
		const minChunks = this.minChunks;
		const selectedChunks = this.selectedChunks;
		const asyncOption = this.async;
		const minSize = this.minSize;
		const ident = this.ident;
		compiler.plugin("this-compilation", (compilation) => {
			compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => {
				// only optimize once
				if(compilation[ident]) return;
				compilation[ident] = true;

				const commonChunks = this.formCommonChunks(chunks, compilation);

				commonChunks.forEach((commonChunk, idx) => {
					const usedChunks = this.getUsedChunks(compilation, chunks, commonChunk, commonChunks, idx, this.selectedChunks, this.async);
					// bail as this is an erronous state
					if(!usedChunks) {
						return;
					}
					let asyncChunk;
					if(asyncOption) {
						asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined);
						asyncChunk.chunkReason = "async commons chunk";
						asyncChunk.extraAsync = true;
						asyncChunk.addParent(commonChunk);
						commonChunk.addChunk(asyncChunk);
						commonChunk = asyncChunk;
					}
					const reallyUsedModules = [];
					if(minChunks !== Infinity) {
						const commonModulesCount = [];
						const commonModules = [];
						usedChunks.forEach((chunk) => {
							chunk.modules.forEach((module) => {
								const idx = commonModules.indexOf(module);
								if(idx < 0) {
									commonModules.push(module);
									commonModulesCount.push(1);
								} else {
									commonModulesCount[idx]++;
								}
							});
						});
						const _minChunks = (minChunks || Math.max(2, usedChunks.length));
						commonModulesCount.forEach((count, idx) => {
							const module = commonModules[idx];
							if(typeof minChunks === "function") {
								if(!minChunks(module, count))
									return;
							} else if(count < _minChunks) {
								return;
							}
							if(module.chunkCondition && !module.chunkCondition(commonChunk))
								return;
							reallyUsedModules.push(module);
						});
					}
					if(minSize) {
						const size = reallyUsedModules.reduce((a, b) => {
							return a + b.size();
						}, 0);
						if(size < minSize)
							return;
					}
					const reallyUsedChunks = new Set();
					reallyUsedModules.forEach((module) => {
						usedChunks.forEach((chunk) => {
							if(module.removeChunk(chunk)) {
								reallyUsedChunks.add(chunk);
							}
						});
						commonChunk.addModule(module);
						module.addChunk(commonChunk);
					});
					if(asyncOption) {
						for(let chunk of reallyUsedChunks) {
							if(chunk.isInitial()) continue;
							chunk.blocks.forEach((block) => {
								block.chunks.unshift(commonChunk);
								commonChunk.addBlock(block);
							});
						}
						asyncChunk.origins = Array.from(reallyUsedChunks).map((chunk) => {
							return chunk.origins.map((origin) => {
								const newOrigin = Object.create(origin);
								newOrigin.reasons = (origin.reasons || []).slice();
								newOrigin.reasons.push("async commons");
								return newOrigin;
							});
						}).reduce((arr, a) => {
							arr.push.apply(arr, a);
							return arr;
						}, []);
					} else {
						usedChunks.forEach((chunk) => {
							chunk.parents = [commonChunk];
							chunk.entrypoints.forEach((ep) => {
								ep.insertChunk(commonChunk, chunk);
							});
							commonChunk.addChunk(chunk);
						});
					}
					if(filenameTemplate)
						commonChunk.filenameTemplate = filenameTemplate;
				});
				return true;
			});
		});
	}
}

module.exports = CommonsChunkPlugin;
<commit_msg>extract creation of async chunk<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";
let nextIdent = 0;
class CommonsChunkPlugin {
	constructor(options) {
		if(arguments.length > 1) {
			throw new Error(`Deprecation notice: CommonsChunkPlugin now only takes a single argument. Either an options
object *or* the name of the chunk.
Example: if your old code looked like this:
	new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js')
You would change it to:
	new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' })
The available options are:
	name: string
	names: string[]
	filename: string
	minChunks: number
	chunks: string[]
	children: boolean
	async: boolean
	minSize: number`);
		}

		const normalizedOptions = this.normalizeOptions(options);

		this.chunkNames = normalizedOptions.chunkNames;
		this.filenameTemplate = this.filenameTemplate;
		this.minChunks = normalizedOptions.minChunks;
		this.selectedChunks = normalizedOptions.selectedChunks;
		this.async = normalizedOptions.async;
		this.minSize = normalizedOptions.minSize;
		this.ident = normalizedOptions.ident;
	}

	normalizeOptions(options) {
		if(Array.isArray(options)) {
			return {
				chunkNames: options,
			};
		}

		if(typeof options === "string") {
			return {
				chunkNames: [options],
			};
		}

		// if "children" is set, set selectedChunks to false instead of specified chunks
		// TODO: why? :P
		const selectedChunks = options.children ? false : options.chunks;
		const chunkNames = options.name ? [options.name] : options.names;
		return {
			chunkNames: chunkNames,
			filenameTemplate: options.filename,
			minChunks: options.minChunks,
			selectedChunks: selectedChunks,
			async: options.async,
			minSize: options.minSize,
			ident: __filename + (nextIdent++),
		};
	}

	getCommonChunks(allChunks, compilation) {
		const asyncOrNoSelectedChunk = this.selectedChunks === false || this.async;

		// we have specified chunk names
		if(this.chunkNames) {
			// map chunks by chunkName for quick access
			const optimizedChunkMap = allChunks.reduce((map, chunk) => {
				map.set(chunk.name, chunk);
				return map;
			}, new Map());

			// Ensure we have a chunk per specified chunk name.
			// Reuse existing chunks if possible
			return this.chunkNames.map(chunkName => {
				if(optimizedChunkMap.has(chunkName)) {
					return optimizedChunkMap.get(chunkName);
				}
				// add the filtered chunks to the compilation
				return compilation.addChunk(chunkName);
			});
		}

		// we dont have named chunks specified, so we just take all of them
		if(asyncOrNoSelectedChunk) {
			return allChunks;
		}

		// that is not supposed to happen, lets throw
		throw new Error("Invalid chunkNames argument");
	}

	getUsedChunks(compilation, allChunks, commonChunk, commonChunks, currentIndex, selectedChunks, isAsync) {
		const asyncOrNoSelectedChunk = selectedChunks === false || isAsync;

		if(Array.isArray(selectedChunks)) {
			return allChunks.filter(chunk => {
				const notCommmonChunk = chunk !== commonChunk;
				const isSelectedChunk = selectedChunks.indexOf(chunk.name) > -1;
				return notCommmonChunk && isSelectedChunk;
			});
		}

		if(asyncOrNoSelectedChunk) {
			// nothing to do here
			if(!commonChunk.chunks) {
				return [];
			}

			return commonChunk.chunks.filter((chunk) => {
				// we can only move modules from this chunk if the "commonChunk" is the only parent
				return isAsync || chunk.parents.length === 1;
			});
		}

		// this is an entry point - bad
		if(commonChunk.parents.length > 0) {
			compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + commonChunk.name + ")"));
			return;
		}

		return allChunks.filter((chunk) => {
			const found = commonChunks.indexOf(chunk);
			if(found >= currentIndex) return false;
			return chunk.hasRuntime();
		});
	}

	createAsyncChunk(compilation, asyncOption, commonChunk) {
		const asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined);
		asyncChunk.chunkReason = "async commons chunk";
		asyncChunk.extraAsync = true;
		asyncChunk.addParent(commonChunk);
		return asyncChunk;
	}

	apply(compiler) {
		const filenameTemplate = this.filenameTemplate;
		const minChunks = this.minChunks;
		const selectedChunks = this.selectedChunks;
		const asyncOption = this.async;
		const minSize = this.minSize;
		const ident = this.ident;
		compiler.plugin("this-compilation", (compilation) => {
			compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => {
				// only optimize once
				if(compilation[ident]) return;
				compilation[ident] = true;

				const commonChunks = this.formCommonChunks(chunks, compilation);

				commonChunks.forEach((commonChunk, idx) => {
					const usedChunks = this.getUsedChunks(compilation, chunks, commonChunk, commonChunks, idx, this.selectedChunks, this.async);
					// bail as this is an erronous state
					if(!usedChunks) {
						return;
					}

					let asyncChunk;
					if(asyncOption) {
						asyncChunk = this.createAsyncChunk(compilation, this.async, commonChunk);
						commonChunk.addChunk(asyncChunk);
						commonChunk = asyncChunk;
					}
					const reallyUsedModules = [];
					if(minChunks !== Infinity) {
						const commonModulesCount = [];
						const commonModules = [];
						usedChunks.forEach((chunk) => {
							chunk.modules.forEach((module) => {
								const idx = commonModules.indexOf(module);
								if(idx < 0) {
									commonModules.push(module);
									commonModulesCount.push(1);
								} else {
									commonModulesCount[idx]++;
								}
							});
						});
						const _minChunks = (minChunks || Math.max(2, usedChunks.length));
						commonModulesCount.forEach((count, idx) => {
							const module = commonModules[idx];
							if(typeof minChunks === "function") {
								if(!minChunks(module, count))
									return;
							} else if(count < _minChunks) {
								return;
							}
							if(module.chunkCondition && !module.chunkCondition(commonChunk))
								return;
							reallyUsedModules.push(module);
						});
					}
					if(minSize) {
						const size = reallyUsedModules.reduce((a, b) => {
							return a + b.size();
						}, 0);
						if(size < minSize)
							return;
					}
					const reallyUsedChunks = new Set();
					reallyUsedModules.forEach((module) => {
						usedChunks.forEach((chunk) => {
							if(module.removeChunk(chunk)) {
								reallyUsedChunks.add(chunk);
							}
						});
						commonChunk.addModule(module);
						module.addChunk(commonChunk);
					});
					if(asyncOption) {
						for(let chunk of reallyUsedChunks) {
							if(chunk.isInitial()) continue;
							chunk.blocks.forEach((block) => {
								block.chunks.unshift(commonChunk);
								commonChunk.addBlock(block);
							});
						}
						asyncChunk.origins = Array.from(reallyUsedChunks).map((chunk) => {
							return chunk.origins.map((origin) => {
								const newOrigin = Object.create(origin);
								newOrigin.reasons = (origin.reasons || []).slice();
								newOrigin.reasons.push("async commons");
								return newOrigin;
							});
						}).reduce((arr, a) => {
							arr.push.apply(arr, a);
							return arr;
						}, []);
					} else {
						usedChunks.forEach((chunk) => {
							chunk.parents = [commonChunk];
							chunk.entrypoints.forEach((ep) => {
								ep.insertChunk(commonChunk, chunk);
							});
							commonChunk.addChunk(chunk);
						});
					}
					if(filenameTemplate)
						commonChunk.filenameTemplate = filenameTemplate;
				});
				return true;
			});
		});
	}
}

module.exports = CommonsChunkPlugin;
<|endoftext|>


<commit_before>/**
 @module orbit-common
 */

/**
 Exception thrown when an operation is not allowed.

 @class OperationNotAllowed
 @namespace OC
 @param {Object} description
 @constructor
 */
var OperationNotAllowed = function(description) {
  this.description = description;
};

OperationNotAllowed.prototype = {
  constructor: OperationNotAllowed
};

/**
 Exception thrown when a record can not be found.

 @class RecordNotFoundException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var RecordNotFoundException = function(type, record) {
  this.type = type;
  this.record = record;
};

RecordNotFoundException.prototype = {
  constructor: RecordNotFoundException
};

/**
 Exception thrown when a record can not be found.

 @class LinkNotFoundException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var LinkNotFoundException = function(type, record, key) {
  this.type = type;
  this.record = record;
  this.key = key;
};

LinkNotFoundException.prototype = {
  constructor: LinkNotFoundException
};

/**
 Exception thrown when a record already exists.

 @class RecordAlreadyExistsException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var RecordAlreadyExistsException = function(type, record) {
  this.type = type;
  this.record = record;
};

RecordAlreadyExistsException.prototype = {
  constructor: RecordAlreadyExistsException
};

export { OperationNotAllowed, RecordNotFoundException, LinkNotFoundException, RecordAlreadyExistsException };
<commit_msg>OC Exceptions: Use Orbit.Exception<commit_after>
import { Exception } from 'orbit/lib/exceptions';
/**
 @module orbit-common
 */

/**
 Exception thrown when an operation is not allowed.

 @class OperationNotAllowed
 @namespace OC
 @param {Object} description
 @constructor
 */
var OperationNotAllowed = Exception.extend({
  name: 'OC.OperationNotAllowed',
});

const _RecordException = Exception.extend({
  init: function(type, record, key) {
    this.type = type;
    this.record = record;
    var message = type + '/' + record;

    if (key) {
      this.key = key;
      message += '/' + key;
    }
    this._super(message);
  },
});

/**
 Exception thrown when a record can not be found.

 @class RecordNotFoundException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var RecordNotFoundException = _RecordException.extend({
  name: 'OC.RecordNotFoundException',
});

/**
 Exception thrown when a record can not be found.

 @class LinkNotFoundException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var LinkNotFoundException = _RecordException.extend({
  name: 'OC.LinkNotFoundException',
});

/**
 Exception thrown when a record already exists.

 @class RecordAlreadyExistsException
 @namespace OC
 @param {String} type
 @param {Object} record
 @constructor
 */
var RecordAlreadyExistsException = _RecordException.extend({
  name: 'OC.RecordAlreadyExistsException',
});

export { OperationNotAllowed, RecordNotFoundException, LinkNotFoundException, RecordAlreadyExistsException };
<|endoftext|>


<commit_before>/* eslint-disable camelcase */
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ServiceProvidersText, {
  ServiceProvidersTextCreateAcct,
} from 'platform/user/authentication/components/ServiceProvidersText';

const serviceProviders = ['Login.gov', 'ID.me', 'DS Logon', 'My HealtheVet'];
const mockStore = configureMockStore();

const getServiceProvidersTextData = ({
  propsIsBold = false,
  featureToggle = true,
}) => {
  const props = {
    isBold: propsIsBold,
  };
  const store = mockStore({
    featureToggles: {
      login_gov_disabled: featureToggle,
    },
  });
  const wrapper = mount(
    <Provider store={store}>
      <ServiceProvidersText {...props} />
    </Provider>,
  );
  return {
    props,
    store,
    wrapper,
  };
};

describe('ServiceProvidersText', () => {
  it('should display 3 CSPS if loginGovDisabled flag is enabled', () => {
    const { wrapper } = getServiceProvidersTextData({});
    expect(
      serviceProviders
        .filter(csp => csp !== 'Login.gov')
        .includes(wrapper.text()),
    );
    wrapper.unmount();
  });
  it('should display 4 CSPS if loginGovDisabled flag is disabled', () => {
    const { wrapper } = getServiceProvidersTextData({
      featureToggle: false,
    });
    expect(serviceProviders.includes(wrapper.text()));
    wrapper.unmount();
  });
  it('should display bold if `isBold` is truthy', () => {
    const { wrapper } = getServiceProvidersTextData({
      propsIsBold: true,
      featureToggle: false,
    });
    expect(wrapper.find('strong').length).to.eql(4);
    wrapper.unmount();
  });
  it('should display normal if `isBold` is falsy', () => {
    const { wrapper } = getServiceProvidersTextData({
      propsIsBold: false,
    });
    expect(wrapper.find('strong').exists()).to.be.false;
    wrapper.unmount();
  });
});

const getServiceProvidersTextCreateAcctData = ({
  propsIsFormBased = false,
  propsHasExtraTodo = false,
  featureToggle = true,
}) => {
  const props = {
    isFormBased: propsIsFormBased,
    hasExtraTodo: propsHasExtraTodo,
  };
  const store = mockStore({
    featureToggles: {
      login_gov_disabled: featureToggle,
    },
  });
  const wrapper = mount(
    <Provider store={store}>
      <ServiceProvidersTextCreateAcct {...props} />
    </Provider>,
  );
  return {
    wrapper,
  };
};

describe('ServiceProvidersTextCreateAcct', () => {
  it('should render string if `isFormBased` prop is truthy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsIsFormBased: true,
    });
    expect(wrapper.text()).to.include(
      'completed this form without signing in, and you',
    );
    wrapper.unmount();
  });
  it('should NOT render string if `isFormBased` prop is falsy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsIsFormBased: false,
    });
    expect(wrapper.text()).to.not.include(
      'completed this form without signing in, and you',
    );
    wrapper.unmount();
  });
  it('should NOT render Login.gov if feature flag is enabled', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      featureToggle: true,
    });
    expect(wrapper.text()).to.not.include('Login.gov');
    wrapper.unmount();
  });
  it('should render Login.gov if feature flag is disabled', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      featureToggle: false,
    });
    expect(wrapper.text()).to.include('Login.gov');
    wrapper.unmount();
  });
  it('should render string if `hasExtraTodo` prop is truthy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsHasExtraTodo: true,
    });
    expect(wrapper.text()).to.include(
      'When you sign in or create an account, you’ll be able to:',
    );
    wrapper.unmount();
  });
  it('should NOT render string if `hasExtraTodo` prop is falsy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsHasExtraTodo: false,
    });
    expect(wrapper.text()).to.not.include(
      'When you sign in or create an account, you’ll be able to:',
    );
    wrapper.unmount();
  });
});
<commit_msg>Fix/flaky platform auth test (#20950)<commit_after>/* eslint-disable camelcase */
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ServiceProvidersText, {
  ServiceProvidersTextCreateAcct,
} from 'platform/user/authentication/components/ServiceProvidersText';

const serviceProviders = ['Login.gov', 'ID.me', 'DS Logon', 'My HealtheVet'];
const mockStore = configureMockStore();

const getServiceProvidersTextData = ({
  propsIsBold = false,
  featureToggle = true,
}) => {
  const props = {
    isBold: propsIsBold,
  };
  const store = mockStore({
    featureToggles: {
      login_gov_disabled: featureToggle,
    },
  });
  const wrapper = mount(
    <Provider store={store}>
      <ServiceProvidersText {...props} />
    </Provider>,
  );
  return {
    props,
    store,
    wrapper,
  };
};

describe('ServiceProvidersText', () => {
  it('should display 3 CSPS if loginGovDisabled flag is enabled', () => {
    const { wrapper } = getServiceProvidersTextData({});
    expect(
      serviceProviders
        .filter(csp => csp !== 'Login.gov')
        .includes(wrapper.text()),
    );
    wrapper.unmount();
  });
  it('should display 4 CSPS if loginGovDisabled flag is disabled', () => {
    const { wrapper } = getServiceProvidersTextData({
      featureToggle: false,
    });
    expect(serviceProviders.includes(wrapper.text()));
    wrapper.unmount();
  });
  it.skip('should display bold if `isBold` is truthy', () => {
    const { wrapper } = getServiceProvidersTextData({
      propsIsBold: true,
      featureToggle: false,
    });
    expect(wrapper.find('strong').length).to.eql(4);
    wrapper.unmount();
  });
  it('should display normal if `isBold` is falsy', () => {
    const { wrapper } = getServiceProvidersTextData({
      propsIsBold: false,
    });
    expect(wrapper.find('strong').exists()).to.be.false;
    wrapper.unmount();
  });
});

const getServiceProvidersTextCreateAcctData = ({
  propsIsFormBased = false,
  propsHasExtraTodo = false,
  featureToggle = true,
}) => {
  const props = {
    isFormBased: propsIsFormBased,
    hasExtraTodo: propsHasExtraTodo,
  };
  const store = mockStore({
    featureToggles: {
      login_gov_disabled: featureToggle,
    },
  });
  const wrapper = mount(
    <Provider store={store}>
      <ServiceProvidersTextCreateAcct {...props} />
    </Provider>,
  );
  return {
    wrapper,
  };
};

describe('ServiceProvidersTextCreateAcct', () => {
  it('should render string if `isFormBased` prop is truthy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsIsFormBased: true,
    });
    expect(wrapper.text()).to.include(
      'completed this form without signing in, and you',
    );
    wrapper.unmount();
  });
  it('should NOT render string if `isFormBased` prop is falsy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsIsFormBased: false,
    });
    expect(wrapper.text()).to.not.include(
      'completed this form without signing in, and you',
    );
    wrapper.unmount();
  });
  it('should NOT render Login.gov if feature flag is enabled', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      featureToggle: true,
    });
    expect(wrapper.text()).to.not.include('Login.gov');
    wrapper.unmount();
  });
  it('should render Login.gov if feature flag is disabled', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      featureToggle: false,
    });
    expect(wrapper.text()).to.include('Login.gov');
    wrapper.unmount();
  });
  it('should render string if `hasExtraTodo` prop is truthy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsHasExtraTodo: true,
    });
    expect(wrapper.text()).to.include(
      'When you sign in or create an account, you’ll be able to:',
    );
    wrapper.unmount();
  });
  it('should NOT render string if `hasExtraTodo` prop is falsy', () => {
    const { wrapper } = getServiceProvidersTextCreateAcctData({
      propsHasExtraTodo: false,
    });
    expect(wrapper.text()).to.not.include(
      'When you sign in or create an account, you’ll be able to:',
    );
    wrapper.unmount();
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var util = require('util');
var timers = require('timers');
var NullStatsd = require('uber-statsd-client/null');
var extend = require('xtend');

var BaseBackend = require('./base-backend');
var Record = require('./record');

module.exports = ReservoirBackend;

function ReservoirBackend(options) {
    if (!(this instanceof ReservoirBackend)) {
        return new ReservoirBackend(options);
    }

    var self = this;

    BaseBackend.call(self);

    self.backend = options.backend;
    assert(
        typeof self.backend === 'object' &&
        typeof self.backend.logMany === 'function',
        'options.backend must be object with `logMany` method'
    );

    self.statsd = options.statsd || NullStatsd();
    assert(
        typeof self.statsd === 'object' &&
        typeof self.statsd.gauge === 'function',
        'options.statsd must be object with `gauge` method'
    );

    self.size = options.size || 100;
    assert(
        typeof self.size === 'number' &&
        self.size >= 5 && self.size < 1000000000,
        'options.size must be number 5 >= n > 1000000000'
    );

    self.rangeRand = options.rangeRand || ReservoirBackend.rangeRand;
    assert(
        typeof self.rangeRand === 'function',
        'options.rangeRand must be function'
    );

    self.flushInterval = options.flushInterval || 50;
    assert(
        typeof self.flushInterval === 'number' &&
        self.flushInterval > 1 && self.flushInterval < 1000000,
        'options.flushInterval must be number 1 > n > 1000000'
    );

    self.timers = options.timers || timers;
    assert(
        typeof self.timers === 'object' &&
        typeof self.timers.setTimeout === 'function',
        'options.timers must be object with setTimeout function'
    );

    self.now = options.now || Date.now;
    assert(
        typeof self.now === 'function',
        'options.now must be function'
    );

    self.timer = null;
    self.count = 0;
    self.records = [];
    self.dropCount = {};
    self.logCount = {};
}

util.inherits(ReservoirBackend, BaseBackend);

ReservoirBackend.rangeRand = function rand(lo, hi) {
    return Math.floor(Math.random() * (hi - lo) + lo);
};

ReservoirBackend.prototype.setSize = function setSize(size) {
    var self = this;

    var i;
    if (size < self.size) {
        var removed = self.records.splice(size, self.size - size);

        for (i = 0; i < removed.length; i++) {
            self.countDrop(removed[i].data.level);
        }
    }

    self.size = size;
};

ReservoirBackend.prototype.setFlushInterval = function setFlushInterval(time) {
    var self = this;

    self.flushInterval = time;

    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.setupTimer();
    }
};

ReservoirBackend.prototype.flush = function flush() {
    var self = this;

    var start = self.now();

    if (self.count > self.size) {
        self.backend.log(new Record(
            'warn',
            'dropped logs',
            {
                dropCount: extend(self.dropCount),
                flushInterval: self.flushInterval,
                size: self.size
            }
        ));
    }

    var i;
    var keys = Object.keys(self.dropCount);
    for (i = 0; i < keys.length; i++) {
        self.statsd.increment('larch.dropped.' + keys[i], self.dropCount[keys[i]]);
        self.dropCount[keys[i]] = 0;
    }

    for (i = 0; i < self.records.length; i++) {
        self.countLog(self.records[i].data.level);
    }

    keys = Object.keys(self.logCount);
    for (i = 0; i < keys.length; i++) {
        self.statsd.increment('larch.logged.' + keys[i], self.logCount[keys[i]]);
        self.logCount[keys[i]] = 0;
    }

    var copy = self.records.slice(0);
    self.backend.logMany(copy, onLoggingDone);

    function onLoggingDone(err) {
        // TODO: what to do when flush fails? Generate a log message?
        if (err) {
            var count = 1;
            if (err.count) {
                count = err.count;
            }

            self.statsd.incr('larch.errors', count);
        }

        self.statsd.timing('larch.flushTime', self.now() - start);
    }

    self.records.length = 0;
    self.count = 0;
};

ReservoirBackend.prototype.log = function log(record, cb) {
    var self = this;

    self.count += 1;

    if (self.records.length < self.size) {
        self.records.push(record);
    } else {
        var probability = self.rangeRand(0, self.count);
        if (probability < self.size) {
            // record drop for the record we're evicting
            self.countDrop(self.records[probability].data.level);

            self.records[probability] = record;
        } else {
            // record drop for record we're dropping
            self.countDrop(record.data.level);
        }
    }

    if (typeof cb === 'function') {
        cb();
    }
};

ReservoirBackend.prototype.countDrop = function countDrop(level) {
    var self = this;

    if (!self.dropCount[level]) {
        self.dropCount[level] = 1;
    } else {
        self.dropCount[level] += 1;
    }
};

ReservoirBackend.prototype.countLog = function countLog(level) {
    var self = this;

    if (!self.logCount[level]) {
        self.logCount[level] = 1;
    } else {
        self.logCount[level] += 1;
    }
};

ReservoirBackend.prototype.destroy = function destroy(cb) {
    var self = this;

    if (self.records.length) {
        self.flush();
    }
    self.timers.clearTimeout(self.timer);
    self.backend.destroy(cb);
};

ReservoirBackend.prototype.bootstrap = function bootstrap(cb) {
    var self = this;

    assert(
        typeof cb === 'function',
        'bootstrap must be called with a callback'
    );

    self.setupTimer();

    self.backend.bootstrap(cb);
};

ReservoirBackend.prototype.setupTimer = function setupTimer() {
    var self = this;

    if (!self.timer) {
        self.timer = self.timers.setTimeout(onTimer, self.flushInterval);
    }

    function onTimer() {
        self.flush();

        self.timer = self.timers.setTimeout(onTimer, self.flushInterval);
    }
};
<commit_msg>larch: fix statsd by using increment method<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var util = require('util');
var timers = require('timers');
var NullStatsd = require('uber-statsd-client/null');
var extend = require('xtend');

var BaseBackend = require('./base-backend');
var Record = require('./record');

module.exports = ReservoirBackend;

function ReservoirBackend(options) {
    if (!(this instanceof ReservoirBackend)) {
        return new ReservoirBackend(options);
    }

    var self = this;

    BaseBackend.call(self);

    self.backend = options.backend;
    assert(
        typeof self.backend === 'object' &&
        typeof self.backend.logMany === 'function',
        'options.backend must be object with `logMany` method'
    );

    self.statsd = options.statsd || NullStatsd();
    assert(
        typeof self.statsd === 'object' &&
        typeof self.statsd.gauge === 'function',
        'options.statsd must be object with `gauge` method'
    );

    self.size = options.size || 100;
    assert(
        typeof self.size === 'number' &&
        self.size >= 5 && self.size < 1000000000,
        'options.size must be number 5 >= n > 1000000000'
    );

    self.rangeRand = options.rangeRand || ReservoirBackend.rangeRand;
    assert(
        typeof self.rangeRand === 'function',
        'options.rangeRand must be function'
    );

    self.flushInterval = options.flushInterval || 50;
    assert(
        typeof self.flushInterval === 'number' &&
        self.flushInterval > 1 && self.flushInterval < 1000000,
        'options.flushInterval must be number 1 > n > 1000000'
    );

    self.timers = options.timers || timers;
    assert(
        typeof self.timers === 'object' &&
        typeof self.timers.setTimeout === 'function',
        'options.timers must be object with setTimeout function'
    );

    self.now = options.now || Date.now;
    assert(
        typeof self.now === 'function',
        'options.now must be function'
    );

    self.timer = null;
    self.count = 0;
    self.records = [];
    self.dropCount = {};
    self.logCount = {};
}

util.inherits(ReservoirBackend, BaseBackend);

ReservoirBackend.rangeRand = function rand(lo, hi) {
    return Math.floor(Math.random() * (hi - lo) + lo);
};

ReservoirBackend.prototype.setSize = function setSize(size) {
    var self = this;

    var i;
    if (size < self.size) {
        var removed = self.records.splice(size, self.size - size);

        for (i = 0; i < removed.length; i++) {
            self.countDrop(removed[i].data.level);
        }
    }

    self.size = size;
};

ReservoirBackend.prototype.setFlushInterval = function setFlushInterval(time) {
    var self = this;

    self.flushInterval = time;

    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.setupTimer();
    }
};

ReservoirBackend.prototype.flush = function flush() {
    var self = this;

    var start = self.now();

    if (self.count > self.size) {
        self.backend.log(new Record(
            'warn',
            'dropped logs',
            {
                dropCount: extend(self.dropCount),
                flushInterval: self.flushInterval,
                size: self.size
            }
        ));
    }

    var i;
    var keys = Object.keys(self.dropCount);
    for (i = 0; i < keys.length; i++) {
        self.statsd.increment('larch.dropped.' + keys[i], self.dropCount[keys[i]]);
        self.dropCount[keys[i]] = 0;
    }

    for (i = 0; i < self.records.length; i++) {
        self.countLog(self.records[i].data.level);
    }

    keys = Object.keys(self.logCount);
    for (i = 0; i < keys.length; i++) {
        self.statsd.increment('larch.logged.' + keys[i], self.logCount[keys[i]]);
        self.logCount[keys[i]] = 0;
    }

    var copy = self.records.slice(0);
    self.backend.logMany(copy, onLoggingDone);

    function onLoggingDone(err) {
        // TODO: what to do when flush fails? Generate a log message?
        if (err) {
            var count = 1;
            if (err.count) {
                count = err.count;
            }

            self.statsd.increment('larch.errors', count);
        }

        self.statsd.timing('larch.flushTime', self.now() - start);
    }

    self.records.length = 0;
    self.count = 0;
};

ReservoirBackend.prototype.log = function log(record, cb) {
    var self = this;

    self.count += 1;

    if (self.records.length < self.size) {
        self.records.push(record);
    } else {
        var probability = self.rangeRand(0, self.count);
        if (probability < self.size) {
            // record drop for the record we're evicting
            self.countDrop(self.records[probability].data.level);

            self.records[probability] = record;
        } else {
            // record drop for record we're dropping
            self.countDrop(record.data.level);
        }
    }

    if (typeof cb === 'function') {
        cb();
    }
};

ReservoirBackend.prototype.countDrop = function countDrop(level) {
    var self = this;

    if (!self.dropCount[level]) {
        self.dropCount[level] = 1;
    } else {
        self.dropCount[level] += 1;
    }
};

ReservoirBackend.prototype.countLog = function countLog(level) {
    var self = this;

    if (!self.logCount[level]) {
        self.logCount[level] = 1;
    } else {
        self.logCount[level] += 1;
    }
};

ReservoirBackend.prototype.destroy = function destroy(cb) {
    var self = this;

    if (self.records.length) {
        self.flush();
    }
    self.timers.clearTimeout(self.timer);
    self.backend.destroy(cb);
};

ReservoirBackend.prototype.bootstrap = function bootstrap(cb) {
    var self = this;

    assert(
        typeof cb === 'function',
        'bootstrap must be called with a callback'
    );

    self.setupTimer();

    self.backend.bootstrap(cb);
};

ReservoirBackend.prototype.setupTimer = function setupTimer() {
    var self = this;

    if (!self.timer) {
        self.timer = self.timers.setTimeout(onTimer, self.flushInterval);
    }

    function onTimer() {
        self.flush();

        self.timer = self.timers.setTimeout(onTimer, self.flushInterval);
    }
};
<|endoftext|>


<commit_before>var Util           = require('util');
var EventEmitter   = require('events').EventEmitter;
var Packets        = require('../packets');
var ErrorConstants = require('../constants/errors');

module.exports = Sequence;
Util.inherits(Sequence, EventEmitter);
function Sequence(callback) {
  EventEmitter.call(this);

  this._callback         = callback;
  this._ended            = false;

  // Experimental: Long stack trace support
  this._callSite = new Error;
}

Sequence.determinePacket = function(byte) {
  switch (byte) {
    case 0x00: return Packets.OkPacket;
    case 0xfe: return Packets.EofPacket;
    case 0xff: return Packets.ErrorPacket;
  }
};

Sequence.prototype.hasErrorHandler = function() {
  return this._callback || this.listeners('error').length > 1;
};

Sequence.prototype._packetToError = function(packet) {
  var code = ErrorConstants[packet.errno] || 'UNKNOWN_CODE_PLEASE_REPORT';
  var err  = new Error(code + ': ' + packet.message);
  err.code = code;

  return err;
};

Sequence.prototype._addLongStackTrace = function(err) {
  var delimiter = '\n    --------------------\n' ;
  if (err.stack.indexOf(delimiter) > -1) {
    return;
  }

  err.stack += delimiter + this._callSite.stack.replace(/.+\n/, '');
};

Sequence.prototype.end = function(err) {
  if (this._ended) {
    return;
  }

  this._ended = true;

  if (err) {
    this._addLongStackTrace(err);
  }

  // try...finally for exception safety
  try {
    if (err) {
      this.emit('error', err);
    }
  } finally {
    try {
      if (this._callback) {
        this._callback.apply(this, arguments);
      }
    } finally {
      this.emit('end');
    }
  }
};

Sequence.prototype['OkPacket'] = function(packet) {
  this.end(null, packet);
};

Sequence.prototype['ErrorPacket'] = function(packet) {
  this.end(this._packetToError(packet));
};

// Implemented by child classes
Sequence.prototype.start = function() {};
<commit_msg>Fix memory leak<commit_after>var Util           = require('util');
var EventEmitter   = require('events').EventEmitter;
var Packets        = require('../packets');
var ErrorConstants = require('../constants/errors');

module.exports = Sequence;
Util.inherits(Sequence, EventEmitter);
function Sequence(callback) {
  EventEmitter.call(this);

  this._callback         = callback;
  this._ended            = false;

  // Experimental: Long stack trace support
  this._callSite = new Error;
}

Sequence.determinePacket = function(byte) {
  switch (byte) {
    case 0x00: return Packets.OkPacket;
    case 0xfe: return Packets.EofPacket;
    case 0xff: return Packets.ErrorPacket;
  }
};

Sequence.prototype.hasErrorHandler = function() {
  return this._callback || this.listeners('error').length > 1;
};

Sequence.prototype._packetToError = function(packet) {
  var code = ErrorConstants[packet.errno] || 'UNKNOWN_CODE_PLEASE_REPORT';
  var err  = new Error(code + ': ' + packet.message);
  err.code = code;

  return err;
};

Sequence.prototype._addLongStackTrace = function(err) {
  var delimiter = '\n    --------------------\n' ;
  if (err.stack.indexOf(delimiter) > -1) {
    return;
  }

  err.stack += delimiter + this._callSite.stack.replace(/.+\n/, '');
};

Sequence.prototype.end = function(err) {
  if (this._ended) {
    return;
  }

  this._ended = true;

  if (err) {
    this._addLongStackTrace(err);
  }

  // Without this we are leaking memory. This problem was introduced in
  // 8189925374e7ce3819bbe88b64c7b15abac96b16. I suspect that the error object
  // causes a cyclic reference that the GC does not detect properly, but I was
  // unable to produce a standalone version of this leak. This would be a great
  // challenge for somebody interested in difficult problems : )!
  delete this._callSite;

  // try...finally for exception safety
  try {
    if (err) {
      this.emit('error', err);
    }
  } finally {
    try {
      if (this._callback) {
        this._callback.apply(this, arguments);
      }
    } finally {
      this.emit('end');
    }
  }
};

Sequence.prototype['OkPacket'] = function(packet) {
  this.end(null, packet);
};

Sequence.prototype['ErrorPacket'] = function(packet) {
  this.end(this._packetToError(packet));
};

// Implemented by child classes
Sequence.prototype.start = function() {};
<|endoftext|>


<commit_before>/*global require, exports*/
var util = require('util'),
    _ = require('underscore'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    uglifyJs = require('uglify-js'),
    uglifyAst = require('uglifyast')(uglifyJs),
    Relation = require('./Relation');

function JavaScriptInclude(config) {
    Relation.call(this, config);
}

util.inherits(JavaScriptInclude, Relation);

extendWithGettersAndSetters(JavaScriptInclude.prototype, {
    get href() {
        return this.node.args[0].value;
    },

    set href(href) {
        this.node.args[0].value = href;
    },

    inline: function () {
        throw new Error("JavaScriptInclude.inline(): Not supported");
    },

    attach: function (asset, position, adjacentRelation) {
        this.node = new uglifyJs.AST_Call({
            expression: new uglifyJs.AST_SymbolRef({
                name: 'INCLUDE'
            }),
            args: [new uglifyJs.AST_String({value: '<urlGoesHere>'})]
        });
        if (position === 'before' || position === 'after') {
            if (adjacentRelation.detachableNode instanceof uglifyJs.AST_SimpleStatement) {
                this.detachableNode = new uglifyJs.AST_SimpleStatement({body: this.node});
            } else {
                // Assume uglifyJs.AST_Seq
                this.detachableNode = this.node;
            }
            if (adjacentRelation.parentNode instanceof uglifyJs.AST_Seq) {
                this.parentNode = new uglifyJs.AST_Seq({
                    car: position === 'before' ? this.detachableNode : adjacentRelation.detachableNode,
                    cdr: position === 'before' ? adjacentRelation.detachableNode : this.detachableNode
                });
                uglifyAst.replaceDescendantNode(adjacentRelation.parentNode, adjacentRelation.detachableNode, this.parentNode);
                adjacentRelation.parentNode = this.parentNode;
            } else {
                this.parentNode = adjacentRelation.parentNode;
                var adjacentDetachableNodeIndex = this.parentNode.body.indexOf(adjacentRelation.detachableNode);
                if (adjacentDetachableNodeIndex === -1) {
                    throw new Error("JavaScriptInclude.attach: adjacentRelation.node not found");
                }
                this.parentNode.body.splice(adjacentDetachableNodeIndex + (position === 'after' ? 1 : 0), 0, this.detachableNode);
            }
        } else {
            this.parentNode = asset.parseTree;
            this.detachableNode = new uglifyJs.AST_SimpleStatement({body: this.node});
            if (position === 'first') {
                this.parentNode.body.unshift(this.detachableNode);
            } else if (position === 'last') {
                this.parentNode.body.push(this.detachableNode);
            } else {
                throw new Error("JavaScriptInclude.attach: Unsupported 'position' value: " + position);
            }
        }
        return Relation.prototype.attach.call(this, asset, position, adjacentRelation);
    },

    detach: function () {
        if (this.parentNode instanceof uglifyJs.AST_Seq) {
            if (this.parentNode.cdr instanceof uglifyJs.AST_Seq) {
                this.parentNode.car = this.parentNode.cdr.car;
                this.parentNode.cdr = this.parentNode.cdr.cdr;
            } else {
                if (this.parentNode.car === this.node) {
                    this.parentNode.car = new uglifyJs.AST_EmptyStatement();
                } else {
                    this.parentNode.cdr = new uglifyJs.AST_EmptyStatement();
                }
            }
        } else {
            var parentNodeIndex = this.parentNode.body.indexOf(this.detachableNode);
            if (parentNodeIndex === -1) {
                throw new Error("JavaScriptInclude.detach: this.detachableNode not a child of this.parentNode.");
            }
            this.parentNode.body.splice(parentNodeIndex, 1);
        }
        return Relation.prototype.detach.call(this);
    }
});

module.exports = JavaScriptInclude;
<commit_msg>Fix JavaScriptInclude.detach.<commit_after>/*global require, exports*/
var util = require('util'),
    _ = require('underscore'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    uglifyJs = require('uglify-js'),
    uglifyAst = require('uglifyast')(uglifyJs),
    Relation = require('./Relation');

function JavaScriptInclude(config) {
    Relation.call(this, config);
}

util.inherits(JavaScriptInclude, Relation);

extendWithGettersAndSetters(JavaScriptInclude.prototype, {
    get href() {
        return this.node.args[0].value;
    },

    set href(href) {
        this.node.args[0].value = href;
    },

    inline: function () {
        throw new Error("JavaScriptInclude.inline(): Not supported");
    },

    attach: function (asset, position, adjacentRelation) {
        this.node = new uglifyJs.AST_Call({
            expression: new uglifyJs.AST_SymbolRef({
                name: 'INCLUDE'
            }),
            args: [new uglifyJs.AST_String({value: '<urlGoesHere>'})]
        });
        if (position === 'before' || position === 'after') {
            if (adjacentRelation.detachableNode instanceof uglifyJs.AST_SimpleStatement) {
                this.detachableNode = new uglifyJs.AST_SimpleStatement({body: this.node});
            } else {
                // Assume uglifyJs.AST_Seq
                this.detachableNode = this.node;
            }
            if (adjacentRelation.parentNode instanceof uglifyJs.AST_Seq) {
                this.parentNode = new uglifyJs.AST_Seq({
                    car: position === 'before' ? this.detachableNode : adjacentRelation.detachableNode,
                    cdr: position === 'before' ? adjacentRelation.detachableNode : this.detachableNode
                });
                uglifyAst.replaceDescendantNode(adjacentRelation.parentNode, adjacentRelation.detachableNode, this.parentNode);
                adjacentRelation.parentNode = this.parentNode;
            } else {
                this.parentNode = adjacentRelation.parentNode;
                var adjacentDetachableNodeIndex = this.parentNode.body.indexOf(adjacentRelation.detachableNode);
                if (adjacentDetachableNodeIndex === -1) {
                    throw new Error("JavaScriptInclude.attach: adjacentRelation.node not found");
                }
                this.parentNode.body.splice(adjacentDetachableNodeIndex + (position === 'after' ? 1 : 0), 0, this.detachableNode);
            }
        } else {
            this.parentNode = asset.parseTree;
            this.detachableNode = new uglifyJs.AST_SimpleStatement({body: this.node});
            if (position === 'first') {
                this.parentNode.body.unshift(this.detachableNode);
            } else if (position === 'last') {
                this.parentNode.body.push(this.detachableNode);
            } else {
                throw new Error("JavaScriptInclude.attach: Unsupported 'position' value: " + position);
            }
        }
        return Relation.prototype.attach.call(this, asset, position, adjacentRelation);
    },

    detach: function () {
        if (this.parentNode instanceof uglifyJs.AST_Seq) {
            if (this.parentNode.cdr instanceof uglifyJs.AST_Seq) {
                this.parentNode.car = this.parentNode.cdr.car;
                this.parentNode.cdr = this.parentNode.cdr.cdr;
            } else {
                if (this.parentNode.car === this.detachableNode) {
                    this.parentNode.car = new uglifyJs.AST_EmptyStatement();
                } else if (this.parentNode.cdr === this.detachableNode) {
                    this.parentNode.cdr = new uglifyJs.AST_EmptyStatement();
                } else {
                    throw new Error('JavaScriptInclude.detach: this.detachableNode is neither equal to this.parentNode.car nor this.parentNode.cdr');
                }
            }
        } else {
            var parentNodeIndex = this.parentNode.body.indexOf(this.detachableNode);
            if (parentNodeIndex === -1) {
                throw new Error("JavaScriptInclude.detach: this.detachableNode not a child of this.parentNode.");
            }
            this.parentNode.body.splice(parentNodeIndex, 1);
        }
        this.parentNode = null;
        return Relation.prototype.detach.call(this);
    }
});

module.exports = JavaScriptInclude;
<|endoftext|>