@adobe/spacecat-shared-rum-api-client 2.18.8 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.19.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.18.9...@adobe/spacecat-shared-rum-api-client-v2.19.0) (2025-01-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* introduce navigation and traffic acquisition data in form audit ([#544](https://github.com/adobe/spacecat-shared/issues/544)) ([550f917](https://github.com/adobe/spacecat-shared/commit/550f9174b21c346938a353feef347d160e194e70))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.18.9](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.18.8...@adobe/spacecat-shared-rum-api-client-v2.18.9) (2025-01-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **rum-api-client:** only consider earned traffic in hi-org-lo-ctr ([#565](https://github.com/adobe/spacecat-shared/issues/565)) ([f7a2019](https://github.com/adobe/spacecat-shared/commit/f7a20193844f40938032a4236f55241fcdefb8d4))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-rum-api-client-v2.18.8](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.18.7...@adobe/spacecat-shared-rum-api-client-v2.18.8) (2025-01-24)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { DataChunks } from '@adobe/rum-distiller';
|
|
14
|
+
import trafficAcquisition from './traffic-acquisition.js';
|
|
14
15
|
import { generateKey, DELIMITER, loadBundles } from '../utils.js';
|
|
15
16
|
|
|
16
17
|
const FORM_SOURCE = ['.form', '.marketo', '.marketo-form'];
|
|
@@ -24,6 +25,7 @@ function initializeResult(url) {
|
|
|
24
25
|
formengagement: {},
|
|
25
26
|
formbuttonclick: {},
|
|
26
27
|
pageview: {},
|
|
28
|
+
forminternalnavigation: [],
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -48,6 +50,57 @@ const metricFns = {
|
|
|
48
50
|
},
|
|
49
51
|
};
|
|
50
52
|
|
|
53
|
+
function populateFormsInternalNavigation(bundles, formVitals) {
|
|
54
|
+
const dataChunks = new DataChunks();
|
|
55
|
+
loadBundles(bundles, dataChunks);
|
|
56
|
+
dataChunks.filter = { checkpoint: ['navigate'] };
|
|
57
|
+
dataChunks.filtered.forEach((bundle) => {
|
|
58
|
+
const forminternalnavigation = bundle.events.find((e) => e.checkpoint === 'navigate');
|
|
59
|
+
if (forminternalnavigation && formVitals[bundle.url]
|
|
60
|
+
&& !formVitals[bundle.url].forminternalnavigation
|
|
61
|
+
.some((e) => e.url === forminternalnavigation.source)) {
|
|
62
|
+
formVitals[bundle.url].forminternalnavigation.push({
|
|
63
|
+
url: forminternalnavigation.source,
|
|
64
|
+
pageview: formVitals[forminternalnavigation.source]?.pageview || null,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function findFormCTAForInternalNavigation(bundles, formVitals) {
|
|
71
|
+
formVitals.forEach((item) => {
|
|
72
|
+
const { url, forminternalnavigation } = item;
|
|
73
|
+
if (forminternalnavigation && Array.isArray(forminternalnavigation)) {
|
|
74
|
+
forminternalnavigation.forEach((nav) => {
|
|
75
|
+
if (nav.url) {
|
|
76
|
+
let totalClickOnPage = 0;
|
|
77
|
+
const CTAs = new Map();
|
|
78
|
+
const clickCheckpointBundles = bundles.filter((bundle) => bundle.url === nav.url && bundle.events.find((e) => e.checkpoint === 'click'));
|
|
79
|
+
clickCheckpointBundles.forEach((bundle) => {
|
|
80
|
+
totalClickOnPage += bundle.weight;
|
|
81
|
+
const clickCheckpoint = bundle.events.find((e) => e.checkpoint === 'click' && e.target === url);
|
|
82
|
+
|
|
83
|
+
if (clickCheckpoint) {
|
|
84
|
+
const { source } = clickCheckpoint;
|
|
85
|
+
// Retrieves the existing CTA object if it exists; otherwise,
|
|
86
|
+
// initializes a new one with default values.
|
|
87
|
+
const existingCTA = CTAs.get(source) || { source, clicks: 0 };
|
|
88
|
+
existingCTA.clicks += bundle.weight;
|
|
89
|
+
CTAs.set(source, existingCTA);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Convert CTAs Map to an array and store it in the nav object
|
|
94
|
+
// eslint-disable-next-line no-param-reassign
|
|
95
|
+
nav.CTAs = Array.from(CTAs.values());
|
|
96
|
+
// eslint-disable-next-line no-param-reassign
|
|
97
|
+
nav.totalClicksOnPage = totalClickOnPage;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
51
104
|
function containsFormVitals(row) {
|
|
52
105
|
return METRICS.some((metric) => Object.keys(row[metric]).length > 0);
|
|
53
106
|
}
|
|
@@ -62,12 +115,19 @@ function handler(bundles) {
|
|
|
62
115
|
// counts metrics per each group
|
|
63
116
|
METRICS.forEach((metric) => dataChunks.addSeries(metric, metricFns[metric]));
|
|
64
117
|
|
|
118
|
+
// traffic acquisition data per url
|
|
119
|
+
const trafficByUrl = trafficAcquisition.handler(bundles);
|
|
120
|
+
const trafficByUrlMap = Object.fromEntries(
|
|
121
|
+
trafficByUrl.map(({ url, ...item }) => [url, item]),
|
|
122
|
+
);
|
|
123
|
+
|
|
65
124
|
// aggregates metrics per group (url and user agent)
|
|
66
125
|
const formVitals = dataChunks.facets.urlUserAgents.reduce((acc, { value, metrics, weight }) => {
|
|
67
126
|
const [url, userAgent] = value.split(DELIMITER);
|
|
68
127
|
|
|
69
128
|
acc[url] = acc[url] || initializeResult(url);
|
|
70
129
|
acc[url].pageview[userAgent] = weight;
|
|
130
|
+
acc[url].trafficacquisition = trafficByUrlMap[url];
|
|
71
131
|
|
|
72
132
|
METRICS.filter((metric) => metrics[metric].sum) // filter out user-agents with no form vitals
|
|
73
133
|
.forEach((metric) => {
|
|
@@ -77,11 +137,15 @@ function handler(bundles) {
|
|
|
77
137
|
return acc;
|
|
78
138
|
}, {});
|
|
79
139
|
|
|
80
|
-
|
|
81
|
-
|
|
140
|
+
// populate internal navigation data
|
|
141
|
+
populateFormsInternalNavigation(bundles, formVitals);
|
|
142
|
+
// filter out pages with no form vitals
|
|
143
|
+
const filteredFormVitals = Object.values(formVitals).filter(containsFormVitals);
|
|
144
|
+
findFormCTAForInternalNavigation(bundles, filteredFormVitals);
|
|
145
|
+
return filteredFormVitals;
|
|
82
146
|
}
|
|
83
147
|
|
|
84
148
|
export default {
|
|
85
149
|
handler,
|
|
86
|
-
checkpoints: ['viewblock', 'formsubmit', 'click'],
|
|
150
|
+
checkpoints: ['viewblock', 'formsubmit', 'click', 'navigate'],
|
|
87
151
|
};
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import trafficAcquisition from '../traffic-acquisition.js';
|
|
14
14
|
import { getCTRByUrlAndVendor, getSiteAvgCTR } from '../../common/aggregateFns.js';
|
|
15
15
|
|
|
16
|
-
const DAILY_EARNED_THRESHOLD =
|
|
16
|
+
const DAILY_EARNED_THRESHOLD = 3000;
|
|
17
17
|
const CTR_THRESHOLD_RATIO = 0.95;
|
|
18
18
|
const DAILY_PAGEVIEW_THRESHOLD = 1000;
|
|
19
19
|
const VENDORS_TO_CONSIDER = 5;
|
|
@@ -94,8 +94,8 @@ function convertToOpportunity(traffic) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function hasHighOrganicTraffic(interval, traffic) {
|
|
97
|
-
const { earned
|
|
98
|
-
return earned
|
|
97
|
+
const { earned } = traffic;
|
|
98
|
+
return earned >= DAILY_EARNED_THRESHOLD * interval;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function hasLowerCTR(ctr, siteAvgCTR) {
|