<commit_before>/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of fiware-iotagent-lib
 *
 * fiware-iotagent-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * fiware-iotagent-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with fiware-iotagent-lib.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::<EMAIL>
 *
 * Modified by: <NAME> - <NAME>
 * Modified by: <NAME> - ATOS Research & Innovation
 */

/* eslint-disable no-prototype-builtins */
/* eslint-disable prefer-spread */
/* eslint-disable prefer-rest-params */
/* eslint-disable consistent-return */

const async = require('async');
const apply = async.apply;
const intoTrans = require('../common/domain').intoTrans;
const groupService = require('../groups/groupService');
const errors = require('../../errors');
const logger = require('logops');
const config = require('../../commonConfig');
const registrationUtils = require('./registrationUtils');
const subscriptions = require('../ngsi/subscriptionService');
const _ = require('underscore');
const context = {
    op: 'IoTAgentNGSI.DeviceService'
};

let deviceHandler;

/**
 * Loads the correct device handler based on the current config.
 */

function init() {
    switch (config.ngsiVersion()) {
        case 'ld':
            deviceHandler = require('./devices-NGSI-LD');
            break;
        case 'v2':
            deviceHandler = require('./devices-NGSI-v2');
            break;
        case 'mixed':
            deviceHandler = require('./devices-NGSI-mixed');
            break;
        default:
            deviceHandler = require('./devices-NGSI-v1');
    }
}

/**
 * Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
 * rest of the updateContext operations to be performed using an UPDATE action instead of an APPEND one.
 *
 * @param {Object} deviceData       Object containing all the deviceData needed to send the registration.
 * @param {Object} newDevice        Device object that will be stored in the database.
 */
function createInitialEntity(deviceData, newDevice, callback) {
    deviceHandler.createInitialEntity(deviceData, newDevice, callback);
}

/**
 * If the object_id or the name of the attribute is missing, complete it with the other piece of data.
 *
 * @param {Object} attribute            Device attribute
 * @return {*}                          Completed attribute
 */
function setDefaultAttributeIds(attribute) {
    /* jshint camelcase: false */

    if (!attribute.object_id && attribute.name) {
        attribute.object_id = attribute.name;
    }

    if (!attribute.name && attribute.object_id) {
        attribute.name = attribute.object_id;
    }

    return attribute;
}

/**
 * Merge array of attributes coming from the device with another one coming from a configuration. The latter will
 * complete the information in the former if the attribute in the configuration:
 * - does not have a conflicting object_id with any attribute of the device.
 * - does not have a conflicting name with any other attribute in the device
 *
 * @param {Array} original              List of attributes of the device.
 * @param {Array} newArray              List of attributes of the configuration.
 * @return {Array}                      Merge of the attributes of the device and those of the configuration.
 */
function mergeArrays(original, newArray) {
    /* jshint camelcase: false */
    const originalKeys = _.pluck(original, 'object_id');
    const newKeys = _.pluck(newArray, 'object_id');
    const addedKeys = _.difference(newKeys, originalKeys);
    const differenceArray = newArray.filter(function (item) {
        return item.object_id && addedKeys.indexOf(item.object_id) >= 0;
    });
    const originalNames = _.pluck(original, 'name');
    const newNames = _.pluck(newArray, 'name');
    const addedNames = _.difference(newNames, originalNames);
    const differenceNamesArray = newArray.filter(function (item) {
        return addedNames.indexOf(item.name) >= 0 && (!item.object_id || newKeys.indexOf(item.object_id) < 0);
    });

    return original.concat(differenceArray).concat(differenceNamesArray);
}

/**
 * Complete the information of the device with the information in the configuration group (with precedence of the
 * device). The first argument indicates what fields would be merged.
 *
 * @param {Object} fields               Fields that will be merged.
 * @param {Object} deviceData           Device data.
 * @param {Object} configuration        Configuration data.
 */
function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuration, callback) {
    logger.debug(context, 'deviceData before merge with conf: %j', deviceData);
    for (let i = 0; i < fields.length; i++) {
        const confField = fields[i] === 'active' ? 'attributes' : fields[i];
        if (deviceData && deviceData[fields[i]] && ['active', 'lazy', 'commands'].indexOf(fields[i]) >= 0) {
            deviceData[fields[i]] = deviceData[fields[i]].map(setDefaultAttributeIds);
        } else if (deviceData && deviceData[fields[i]] && ['internalAttributes'].indexOf(fields[i]) >= 0) {
            if (!(deviceData[fields[i]] instanceof Array)) {
                deviceData[fields[i]] = [deviceData[fields[i]]];
            }
        }

        if (configuration && configuration[confField] && ['attributes', 'lazy', 'commands'].indexOf(confField) >= 0) {
            configuration[confField] = configuration[confField].map(setDefaultAttributeIds);
        } else if (configuration && configuration[confField] && ['internalAttributes'].indexOf(confField) >= 0) {
            if (!(configuration[confField] instanceof Array)) {
                configuration[confField] = [configuration[confField]];
            }
        }

        if (deviceData[fields[i]] && configuration && configuration[confField]) {
            deviceData[fields[i]] = mergeArrays(deviceData[fields[i]], configuration[confField]);
        } else if (
            !deviceData[fields[i]] &&
            configuration &&
            confField in configuration &&
            configuration[confField] !== undefined
        ) {
            deviceData[fields[i]] = configuration[confField];
        } else if (!deviceData[fields[i]] && (!configuration || !configuration[confField])) {
            deviceData[fields[i]] = defaults[i];
        }
    }

    if (configuration && configuration.cbHost) {
        deviceData.cbHost = configuration.cbHost;
    }
    if (configuration && configuration.ngsiVersion) {
        deviceData.ngsiVersion = configuration.ngsiVersion;
    }
    if (configuration && configuration.autoprovision && deviceData.autoprovision === undefined) {
        deviceData.autoprovision = configuration.autoprovision;
    }
    if (configuration && configuration.expressionLanguage && deviceData.expressionLanguage === undefined) {
        deviceData.expressionLanguage = configuration.expressionLanguage;
    }
    if (configuration && configuration.explicitAttrs && deviceData.explicitAttrs === undefined) {
        deviceData.explicitAttrs = configuration.explicitAttrs;
    }

    logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
    callback(null, deviceData);
}

/**
 * Find the configuration group belonging to a given device, with a different criteria depending on whether the
 * agent is in single configuration mode or node.
 *
 * @param {Object} deviceObj        Device data.
 */
function findConfigurationGroup(deviceObj, callback) {
    function handlerGroupFind(error, group) {
        let effectiveGroup = group;

        if (!group && config.getConfig().types[deviceObj.type]) {
            effectiveGroup = config.getConfig().types[deviceObj.type];
        } else if (!group) {
            effectiveGroup = deviceObj;
        }

        callback(null, effectiveGroup);<commit_msg>check value is not boolean<commit_after>/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of fiware-iotagent-lib
 *
 * fiware-iotagent-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * fiware-iotagent-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with fiware-iotagent-lib.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::<EMAIL>
 *
 * Modified by: <NAME> - <NAME>
 * Modified by: <NAME> - ATOS Research & Innovation
 */

/* eslint-disable no-prototype-builtins */
/* eslint-disable prefer-spread */
/* eslint-disable prefer-rest-params */
/* eslint-disable consistent-return */

const async = require('async');
const apply = async.apply;
const intoTrans = require('../common/domain').intoTrans;
const groupService = require('../groups/groupService');
const errors = require('../../errors');
const logger = require('logops');
const config = require('../../commonConfig');
const registrationUtils = require('./registrationUtils');
const subscriptions = require('../ngsi/subscriptionService');
const _ = require('underscore');
const context = {
    op: 'IoTAgentNGSI.DeviceService'
};

let deviceHandler;

/**
 * Loads the correct device handler based on the current config.
 */

function init() {
    switch (config.ngsiVersion()) {
        case 'ld':
            deviceHandler = require('./devices-NGSI-LD');
            break;
        case 'v2':
            deviceHandler = require('./devices-NGSI-v2');
            break;
        case 'mixed':
            deviceHandler = require('./devices-NGSI-mixed');
            break;
        default:
            deviceHandler = require('./devices-NGSI-v1');
    }
}

/**
 * Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
 * rest of the updateContext operations to be performed using an UPDATE action instead of an APPEND one.
 *
 * @param {Object} deviceData       Object containing all the deviceData needed to send the registration.
 * @param {Object} newDevice        Device object that will be stored in the database.
 */
function createInitialEntity(deviceData, newDevice, callback) {
    deviceHandler.createInitialEntity(deviceData, newDevice, callback);
}

/**
 * If the object_id or the name of the attribute is missing, complete it with the other piece of data.
 *
 * @param {Object} attribute            Device attribute
 * @return {*}                          Completed attribute
 */
function setDefaultAttributeIds(attribute) {
    /* jshint camelcase: false */

    if (!attribute.object_id && attribute.name) {
        attribute.object_id = attribute.name;
    }

    if (!attribute.name && attribute.object_id) {
        attribute.name = attribute.object_id;
    }

    return attribute;
}

/**
 * Merge array of attributes coming from the device with another one coming from a configuration. The latter will
 * complete the information in the former if the attribute in the configuration:
 * - does not have a conflicting object_id with any attribute of the device.
 * - does not have a conflicting name with any other attribute in the device
 *
 * @param {Array} original              List of attributes of the device.
 * @param {Array} newArray              List of attributes of the configuration.
 * @return {Array}                      Merge of the attributes of the device and those of the configuration.
 */
function mergeArrays(original, newArray) {
    /* jshint camelcase: false */
    const originalKeys = _.pluck(original, 'object_id');
    const newKeys = _.pluck(newArray, 'object_id');
    const addedKeys = _.difference(newKeys, originalKeys);
    const differenceArray = newArray.filter(function (item) {
        return item.object_id && addedKeys.indexOf(item.object_id) >= 0;
    });
    const originalNames = _.pluck(original, 'name');
    const newNames = _.pluck(newArray, 'name');
    const addedNames = _.difference(newNames, originalNames);
    const differenceNamesArray = newArray.filter(function (item) {
        return addedNames.indexOf(item.name) >= 0 && (!item.object_id || newKeys.indexOf(item.object_id) < 0);
    });

    return original.concat(differenceArray).concat(differenceNamesArray);
}

/**
 * Complete the information of the device with the information in the configuration group (with precedence of the
 * device). The first argument indicates what fields would be merged.
 *
 * @param {Object} fields               Fields that will be merged.
 * @param {Object} deviceData           Device data.
 * @param {Object} configuration        Configuration data.
 */
function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuration, callback) {
    logger.debug(context, 'deviceData before merge with conf: %j', deviceData);
    for (let i = 0; i < fields.length; i++) {
        const confField = fields[i] === 'active' ? 'attributes' : fields[i];
        if (deviceData && deviceData[fields[i]] && ['active', 'lazy', 'commands'].indexOf(fields[i]) >= 0) {
            deviceData[fields[i]] = deviceData[fields[i]].map(setDefaultAttributeIds);
        } else if (deviceData && deviceData[fields[i]] && ['internalAttributes'].indexOf(fields[i]) >= 0) {
            if (!(deviceData[fields[i]] instanceof Array)) {
                deviceData[fields[i]] = [deviceData[fields[i]]];
            }
        }

        if (configuration && configuration[confField] && ['attributes', 'lazy', 'commands'].indexOf(confField) >= 0) {
            configuration[confField] = configuration[confField].map(setDefaultAttributeIds);
        } else if (configuration && configuration[confField] && ['internalAttributes'].indexOf(confField) >= 0) {
            if (!(configuration[confField] instanceof Array)) {
                configuration[confField] = [configuration[confField]];
            }
        }

        if (deviceData[fields[i]] && configuration && configuration[confField]) {
            deviceData[fields[i]] = mergeArrays(deviceData[fields[i]], configuration[confField]);
        } else if (
            !deviceData[fields[i]] &&
            configuration &&
            confField in configuration &&
            configuration[confField] !== undefined
        ) {
            deviceData[fields[i]] = configuration[confField];
        } else if (!deviceData[fields[i]] && (!configuration || !configuration[confField])) {
            deviceData[fields[i]] = defaults[i];
        }
    }

    if (configuration && configuration.cbHost) {
        deviceData.cbHost = configuration.cbHost;
    }
    if (configuration && configuration.ngsiVersion) {
        deviceData.ngsiVersion = configuration.ngsiVersion;
    }
    if (configuration && configuration.autoprovision !== undefined && deviceData.autoprovision === undefined) {
        deviceData.autoprovision = configuration.autoprovision;
    }
    if (configuration && configuration.expressionLanguage && deviceData.expressionLanguage === undefined) {
        deviceData.expressionLanguage = configuration.expressionLanguage;
    }
    if (configuration && configuration.explicitAttrs !== undefined && deviceData.explicitAttrs === undefined) {
        deviceData.explicitAttrs = configuration.explicitAttrs;
    }

    logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
    callback(null, deviceData);
}

/**
 * Find the configuration group belonging to a given device, with a different criteria depending on whether the
 * agent is in single configuration mode or node.
 *
 * @param {Object} deviceObj        Device data.
 */
function findConfigurationGroup(deviceObj, callback) {
    function handlerGroupFind(error, group) {
        let effectiveGroup = group;

        if (!group && config.getConfig().types[deviceObj.type]) {
            effectiveGroup = config.getConfig().types[deviceObj.type];
        } else if (!group) {
            effectiveGroup = deviceObj;
        }

        callback(null, effectiveGroup);
{'fraction_non_alphanumeric': 0.11259958689879021, 'fraction_numerical': 0.0018884626733549719, 'mean_word_length': 3.214374533698085, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1441575', 'n_tokens_mistral': 4377, 'n_tokens_neox': 4274, 'n_words': 1389}