activator-oce-exporter
Version:
Extract Activator binder and convert it to valid OCE mono pacakge
451 lines (386 loc) • 11 kB
JavaScript
import { FusionLogger } from './services/fusion-logger';
import { analyticsData } from './static/analytics.json';
import { veevaMonitoring } from '../config.json';
import { FusionStore } from './services/fusion-store';
/*
StackRunner:
input: params = {handler: function(result, id)},
publicMethods:
push({method: function(result), id: string}),
*/
const CONFIG = {
types: {
slide: 'slide',
popup: 'popup',
pdf: 'pdf',
custom: 'custom',
insight: 'insight',
},
};
const setConfig = (config, params) => Object.keys(params).map((key) => {
config[key] = params[key];
return config[key];
});
const triggerEvent = (name, data) => {
const event = new CustomEvent(name, { detail: data });
document.dispatchEvent(event);
};
const errorHandler = (errorMessage) => {
FusionLogger.error(errorMessage, 'monitoring');
};
class StackRunner {
constructor(params = {}) {
this.config = {
handler: null,
};
this.stack = [];
this.isStackRunningProceed = false;
setConfig(this.config, params);
}
push(data) {
if (data) {
this.stack.push(data);
}
if (!this.isStackRunningProceed) {
this.run();
}
}
checkStack() {
const lastIndexItem = this.stack.length - 1;
const FIRST_ITEM_INDEX = 0;
// if we have stack of the same data, we get only last value
if (!!this.stack.length && this.stack[FIRST_ITEM_INDEX].id === this.stack[lastIndexItem].id) {
this.stack.splice(0, lastIndexItem);
}
}
resultHandler(result) {
if (this.config.handler) {
this.config.handler(result, this.stack[0]);
}
this.stack.shift();
this.run();
}
run() {
this.isStackRunningProceed = true;
this.checkStack();
const method = this.stack.length > 0 ? this.stack[0].method : null;
if (method) {
method(this.resultHandler.bind(this));
} else {
this.isStackRunningProceed = false;
}
}
}
class ClickStream {
constructor(data) {
this.setId();
this.setDefaultFields(data);
this.setCustomFields(data);
}
setDefaultFields(data) {
const {
trackId, type, description, answer,
} = data;
this.Track_Element_Id_vod__c = trackId;
this.Track_Element_Type_vod__c = type;
this.Track_Element_Description_vod__c = description;
this.Answer_vod__c = answer;
}
setCustomFields(data) {
const customFieldsConfig = {
[CONFIG.types.popup]: 'setDuration',
[CONFIG.types.slide]: 'setDuration',
[CONFIG.types.insight]: 'setInsight',
};
const func = customFieldsConfig[data.type];
if (func) {
this.constructor.prototype[func](data);
}
}
setDuration(data) {
this.Usage_Duration_vod__c = data.ticks;
}
setId(value = '') {
this.ID = value;
}
setInsight() {
this.CLM_Insight__c = 1;
}
setAnswer(data) {
this.Answer_vod__c = data.answer || '';
}
}
/*
MonitoringItem:
input: data = {id, type ....} , params = {isTotalTicks:boolean}
publicMethods:
enter() - run on view enter
exit() - run on view exit
presentationExit() - run on presentation exit in veeva(used in 'com.veeva.clm.createRecordsOnExit' func)
submit() - used for submitting data
*/
class MonitoringItem {
constructor(data, params = {}) {
this.config = {
isTotalTicks: false,
};
this.CLICKSTREAM_OBJECT_NAME = 'Call_Clickstream_vod__c';
this.TICKS_PER_MS = 10000;
this.MIN_VISIT_TIME_MS = 1000;
setConfig(this.config, params);
this.init(data);
}
init(data) {
this.itemData = MonitoringItem.initData(data);
this.clickStream = new ClickStream(this.itemData);
}
static initData(data) {
const {
itemId, id, type, description, answer,
} = data;
return {
id: itemId,
trackId: id,
description,
type,
answer,
startTime: new Date(),
endTime: new Date(),
duration: new Date(),
ticks: 0,
totalTicks: 0,
};
}
createQueryData() {
const { id, type, description } = this.itemData;
return {
id,
type,
description,
method: this.getQueryMethod(),
};
}
getQueryMethod() {
const { ID, ...objToSubmit } = this.clickStream;
if (ID) {
return result => com.veeva.clm.updateRecord(this.CLICKSTREAM_OBJECT_NAME, ID, objToSubmit, result);
}
return result => com.veeva.clm.createRecord(this.CLICKSTREAM_OBJECT_NAME, objToSubmit, result);
}
setStartTime(time = new Date()) {
this.itemData.startTime = time;
}
calcDuration() {
return this.itemData.endTime - this.itemData.startTime;
}
getDurationTime(storedTime = 0) {
return this.calcDuration() + storedTime;
}
calcTicks() {
return parseInt(this.itemData.duration * this.TICKS_PER_MS, 10);
}
setClickStreamId(result) {
const { ID } = result[this.CLICKSTREAM_OBJECT_NAME];
if (!this.clickStream.ID) {
this.clickStream.setId(ID);
}
}
setSpentTime(time) {
this.itemData.endTime = new Date();
this.itemData.duration = this.getDurationTime(time);
this.itemData.ticks = this.calcTicks();
this.itemData.totalTicks += this.itemData.ticks;
this.clickStream.setDuration(this.getTicks());
}
updateDataAnswer(data) {
const currentAnswer = data.answer;
if (this.itemData.answer !== currentAnswer) {
this.itemData.answer = currentAnswer;
this.clickStream.setAnswer(this.itemData);
}
}
getTicks() {
const { totalTicks, ticks } = this.itemData;
const time = this.config.isTotalTicks ? totalTicks : ticks;
return { ticks: time };
}
isItemVisited() {
return this.itemData.duration > this.MIN_VISIT_TIME_MS;
}
enter(time) {
this.setStartTime(time);
}
exit(time) {
this.setSpentTime(time);
if (this.isItemVisited()) {
this.submit();
}
}
exitPresentation() {
this.setSpentTime();
return this.createQueryOnExit();
}
submit() {
triggerEvent('monitoring:query-ready', this.createQueryData());
}
createQueryOnExit() {
const { ID, ...objToSubmit } = this.clickStream;
if (this.clickStream.ID) {
return com.veeva.clm.formatUpdateRecords([this.CLICKSTREAM_OBJECT_NAME], [ID], [objToSubmit]);
}
return com.veeva.clm.formatCreateRecords([this.CLICKSTREAM_OBJECT_NAME], [objToSubmit]);
}
}
export class VeevaMonitoring {
constructor(params = {}) {
this.INPUT_DATA = {
id: 'id',
type: Object.values(CONFIG.types),
description: 'description',
answer: 'answer',
};
this.config = {
isDebugMode: true,
};
this.analyticData = analyticsData;
this.monitoringItems = {};
this.keyMessageName = '';
this.stack = new StackRunner({ handler: VeevaMonitoring.queryHandler });
setConfig(this.config, params);
this.initListeners();
}
initListeners() {
document.addEventListener('monitoring:query-completed', this.setSubmittedClickStreamId.bind(this));
document.addEventListener('monitoring:query-ready', this.pushToStack.bind(this));
document.addEventListener('VeevaDataReceived', this.trackKeyMessageEnter.bind(this));
}
trackKeyMessageEnter() {
const { name } = FusionStore.keyMessage;
this.keyMessageName = name;
this.trackSlideEnter({ id: this.keyMessageName });
this.setVeevaExtends();
}
setVeevaExtends() {
if (window.com) {
window.com.veeva.clm.createRecordsOnExit = () => this.presentationExit({ id: this.keyMessageName });
}
}
setSubmittedClickStreamId(data) {
this.doItemAction(VeevaMonitoring.setItemClickStreamId, data.detail);
}
trackSlideEnter(data) {
this.doItemAction(VeevaMonitoring.trackItemEnter, data);
}
trackSlideExit(data) {
this.doItemAction(VeevaMonitoring.trackItemExit, data);
}
trackCustomData(data) {
this.doItemAction(VeevaMonitoring.trackItemCustomData, data);
}
static queryHandler(result, item) {
const { id, type, description } = item;
if (result.success === false) {
errorHandler(result.message);
} else {
triggerEvent('monitoring:query-completed', {
id, type, description, result,
});
}
}
getMonitoringItem(data) {
const itemData = this.createItemData(data);
let result = null;
if (itemData) {
result = this.monitoringItems[itemData.itemId] || (this.monitoringItems[itemData.itemId] = new MonitoringItem(itemData));
}
return result;
}
doItemAction(action, data) {
const item = this.getMonitoringItem(data);
if (item) {
action.call(this, item, data);
}
}
static setItemClickStreamId(item, data) {
item.setClickStreamId(data.result);
}
static trackItemEnter(item, data, time) {
item.enter(time);
}
static trackItemExit(item) {
item.exit();
}
static trackItemCustomData(item, data) {
item.updateDataAnswer(data);
item.submit();
}
formatInputData(data) {
const obj = {};
Object.keys(data)
.filter(key => this.INPUT_DATA[key])
.forEach((key) => {
obj[key] = data[key];
return obj;
});
return obj;
}
isTypeExist(type) {
return this.INPUT_DATA.type.includes(type);
}
isValidFields(data) {
const { id, type, description } = data;
const isValidType = type && this.isTypeExist(type);
const isValidFields = id && description;
return isValidType && isValidFields;
}
isWrongInputData(data) {
const errorMessage = `wrong monitoring data: ${JSON.stringify(data)}`;
const isWrongData = !data || !this.isValidFields(data);
if (isWrongData) {
errorHandler(errorMessage);
}
return isWrongData;
}
createItemData(data) {
const fileData = this.getFileItem(data);
let inputData = null;
if (!this.isWrongInputData(fileData)) {
inputData = this.formatInputData(fileData);
inputData.itemId = data.id;
}
return inputData;
}
static filterCondition(itemId, keyId) {
return itemId === keyId;
}
getFileItem(data) {
const item = Object.keys(this.analyticData)
.find(key => VeevaMonitoring.filterCondition(data.id, key));
return this.analyticData[item];
}
isExcluded(id) {
return veevaMonitoring.excludedTypes.includes(this.monitoringItems[id].itemData.type);
}
isSubmitEnable(slideId) {
return window.com && !this.isExcluded(slideId);
}
pushToStack(queryData) {
const data = queryData.detail;
if (this.config.isDebugMode) {
FusionLogger.log(`ClickStream to submit: ${this.monitoringItems[data.id].clickStream}`, 'monitoring');
}
if (this.isSubmitEnable(data.id)) {
this.stack.push(data);
}
}
presentationExit(data) {
const monitoringItem = this.getMonitoringItem(data);
const slideId = monitoringItem.itemData.id;
if (this.isSubmitEnable(slideId)) {
return monitoringItem.exitPresentation();
}
return null;
}
}
const monitoring = new VeevaMonitoring();
export { monitoring };