const lodash = require('lodash');
const objectHelper = require('./object-helper');
/**
*
* @static
* @requires lodash
*/
class VisitorProfileBeautifier {
/**
*
* @static
* @param {*} visitorProfile
* @param {*} cdhConfiguration
* @returns {Object} Adds an additional lot of properties with the attribute names
*/
static beautify = (visitorProfile, cdhConfiguration) => {
visitorProfile = lodash.cloneDeep(visitorProfile);
// badges and audiences are arrays, so the logic is different than the rest (which are objects)
// UPDATE 11/11/2022: recently it seems at least some profiles use an object for badges
if (Array.isArray(visitorProfile.badges)) {
visitorProfile.badges_pretty = this.renameArrayFromMap(visitorProfile.badges, cdhConfiguration.visitorAttributes, 'name');
} else {
Object.keys(visitorProfile.badges || []).forEach(id => {
visitorProfile.badges_pretty = visitorProfile.badges_pretty || [];
visitorProfile.badges_pretty.push(cdhConfiguration.visitorAttributes[id].name);
});
}
// first make sure the audiences are actually raw (varies within the trace output)
visitorProfile.audiences = this.renameArrayFromMap(visitorProfile.audiences, cdhConfiguration.reverseAudiences, 'id');
// then make sure the pretty one is pretty
visitorProfile.audiences_pretty = this.renameArrayFromMap(visitorProfile.audiences, cdhConfiguration.audiences, 'name');
const conditionalOptions = [
'dates', 'properties', 'metrics', 'property_sets', 'metric_sets', 'flags',
'funnels', 'sequences', 'property_lists', 'metric_lists', 'flag_lists', 'secondary_ids'
];
conditionalOptions.forEach((el) => {
visitorProfile = this.conditionalAddPretty(visitorProfile, cdhConfiguration, el);
});
// for funnels, also rename any attributes that were captured in the 'snapshot'
if (typeof visitorProfile.funnels_pretty === 'object') {
const funnels = Object.keys(visitorProfile.funnels_pretty);
funnels.forEach((funnelName) => {
const funnel = visitorProfile.funnels_pretty[funnelName];
const funnelSteps = Object.keys(funnel);
funnelSteps.forEach((stepName) => {
// the 'completed' key can't have a snapshot, it's just a boolean - the rest should
if (stepName !== 'completed') {
// event AND visit/visitor attributes can be captured
visitorProfile.funnels_pretty[funnelName][stepName].snapshot = this.renameObjectKeysFromMap(visitorProfile.funnels_pretty[funnelName][stepName].snapshot, cdhConfiguration.visitorAttributes, 'name');
visitorProfile.funnels_pretty[funnelName][stepName].snapshot = this.renameObjectKeysFromMap(visitorProfile.funnels_pretty[funnelName][stepName].snapshot, cdhConfiguration.eventAttributes, 'name');
}
});
});
}
// for sequences, also rename any attributes that were captured in the 'snapshot'
if (typeof visitorProfile.sequences_pretty === 'object') {
const sequences = Object.keys(visitorProfile.sequences_pretty);
sequences.forEach((sequenceName, index) => {
const sequence = visitorProfile.sequences_pretty[sequenceName];
sequence.forEach((entry, index) => {
// event AND visit/visitor attributes can be captured
visitorProfile.sequences_pretty[sequenceName][index].snapshot = this.renameObjectKeysFromMap(visitorProfile.sequences_pretty[sequenceName][index].snapshot, cdhConfiguration.visitorAttributes, 'name');
visitorProfile.sequences_pretty[sequenceName][index].snapshot = this.renameObjectKeysFromMap(visitorProfile.sequences_pretty[sequenceName][index].snapshot, cdhConfiguration.eventAttributes, 'name');
});
});
}
return objectHelper.sortKeysByName(visitorProfile);
};
/**
*
* @param {*} visitorProfile
* @param {*} keyName
* @returns {Object}
*/
static conditionalAddPretty = (visitorProfile, cdhConfiguration, keyName) => {
const prettyKey = `${keyName}_pretty`;
if (typeof visitorProfile[keyName] === 'object') {
visitorProfile[prettyKey] = this.renameObjectKeysFromMap(visitorProfile[keyName], cdhConfiguration.visitorAttributes, 'name');
}
return visitorProfile;
};
/**
*
* @param {*} inputObject
* @param {*} map
* @param {*} field
* @returns {Object}
*/
static renameObjectKeysFromMap = (inputObject, map, field) => {
const rawObject = lodash.cloneDeep(inputObject);
if (typeof rawObject !== 'object') return rawObject;
const idArray = Object.keys(rawObject);
const prettyObject = {};
idArray.forEach((id) => {
let prettyName = map[id] && map[id][field];
if (prettyName === undefined || prettyName === false) prettyName = id; // fall back to ID if missing from map
prettyObject[prettyName] = rawObject[id];
});
return prettyObject;
};
/**
*
* @param {*} inputIdArray
* @param {*} map
* @param {*} field
* @returns {Object}
*/
static renameArrayFromMap (inputIdArray, map, field) {
const idArray = lodash.cloneDeep(inputIdArray);
if (typeof idArray !== 'object' || typeof idArray.map !== 'function') return idArray;
return idArray.map((id) => {
let mapped = map[id] && map[id][field];
if (mapped === undefined || mapped === false) mapped = id; // fall back to ID if missing from map
return mapped;
});
}
}
module.exports = VisitorProfileBeautifier;