@adobe/spacecat-shared-rum-api-client 2.23.3 → 2.24.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 +14 -0
- package/package.json +1 -1
- package/src/functions/form-vitals.js +108 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.24.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.23.4...@adobe/spacecat-shared-rum-api-client-v2.24.0) (2025-05-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* identify iframe forms ([#715](https://github.com/adobe/spacecat-shared/issues/715)) ([d2e0a47](https://github.com/adobe/spacecat-shared/commit/d2e0a47f1020471a6f22f9d713e6bf9c03df2279))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.23.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.23.3...@adobe/spacecat-shared-rum-api-client-v2.23.4) (2025-04-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* form source proper handling in form vitals ([#711](https://github.com/adobe/spacecat-shared/issues/711)) ([e37bf0a](https://github.com/adobe/spacecat-shared/commit/e37bf0a6df28952a0e51f8689c4016a9bbff82e0))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-rum-api-client-v2.23.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.23.2...@adobe/spacecat-shared-rum-api-client-v2.23.3) (2025-04-26)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ import { DataChunks } from '@adobe/rum-distiller';
|
|
|
14
14
|
import { generateKey, DELIMITER, loadBundles } from '../utils.js';
|
|
15
15
|
|
|
16
16
|
const METRICS = ['formview', 'formengagement', 'formsubmit'];
|
|
17
|
-
const CHECKPOINTS = ['viewblock', 'click', 'fill', 'formsubmit', 'navigate'];
|
|
17
|
+
const CHECKPOINTS = ['viewblock', 'click', 'fill', 'formsubmit', 'navigate', 'viewmedia'];
|
|
18
18
|
const KEYWORDS_TO_FILTER = ['search'];
|
|
19
19
|
|
|
20
20
|
function initializeResult(url) {
|
|
@@ -41,7 +41,7 @@ function filterEvents(bundles) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const isFormRelatedEvent = ['fill', 'formsubmit'].includes(event.checkpoint)
|
|
44
|
-
|| /\bform\b
|
|
44
|
+
|| /\bform\b|aemform\w*/i.test(event.source);
|
|
45
45
|
return isFormRelatedEvent && !KEYWORDS_TO_FILTER.some((keyword) => event.source
|
|
46
46
|
&& event.source.toLowerCase().includes(keyword));
|
|
47
47
|
}),
|
|
@@ -82,6 +82,7 @@ function populateFormsInternalNavigation(bundles, formVitals) {
|
|
|
82
82
|
dataChunks.filter = { checkpoint: ['navigate'] };
|
|
83
83
|
dataChunks.filtered.forEach((bundle) => {
|
|
84
84
|
const formInternalNav = bundle.events.find((e) => e.checkpoint === 'navigate');
|
|
85
|
+
|
|
85
86
|
const formVital = findByUrl(formVitals, bundle.url);
|
|
86
87
|
if (formInternalNav && formVital
|
|
87
88
|
&& !formVital.forminternalnavigation
|
|
@@ -89,7 +90,7 @@ function populateFormsInternalNavigation(bundles, formVitals) {
|
|
|
89
90
|
const fv = findByUrl(formVitals, formInternalNav.source);
|
|
90
91
|
formVital.forminternalnavigation.push({
|
|
91
92
|
url: formInternalNav.source,
|
|
92
|
-
pageview: fv
|
|
93
|
+
...(fv && { pageview: fv.pageview }),
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
96
|
});
|
|
@@ -133,8 +134,58 @@ function containsFormVitals(row) {
|
|
|
133
134
|
return METRICS.some((metric) => Object.keys(row[metric]).length > 0);
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
function getParentPageVitalsGroupedByIFrame(bundles, dataChunks, iframeParentMap) {
|
|
138
|
+
const iframeVitals = {};
|
|
139
|
+
if (dataChunks.facets.urlUserAgents) {
|
|
140
|
+
dataChunks.facets.urlUserAgents.reduce((acc, { value, weight }) => {
|
|
141
|
+
const [url, userAgent] = value.split(DELIMITER);
|
|
142
|
+
|
|
143
|
+
let iframeSrc = null;
|
|
144
|
+
for (const iframeUrl of Object.keys(iframeParentMap)) {
|
|
145
|
+
for (const parentUrl of iframeParentMap[iframeUrl]) {
|
|
146
|
+
if (parentUrl === url) {
|
|
147
|
+
iframeSrc = iframeUrl;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (iframeSrc) {
|
|
153
|
+
acc[url] = acc[url] || { url, pageview: {}, forminternalnavigation: [] };
|
|
154
|
+
acc[url].pageview[userAgent] = acc[url].pageview[userAgent] || weight;
|
|
155
|
+
acc[url].iframeSrc = iframeSrc;
|
|
156
|
+
}
|
|
157
|
+
return acc;
|
|
158
|
+
}, iframeVitals);
|
|
159
|
+
}
|
|
160
|
+
const groupedByIframeSrc = {};
|
|
161
|
+
const parentWebVitals = {};
|
|
162
|
+
|
|
163
|
+
// select the parent page with the most views
|
|
164
|
+
for (const [url, obj] of Object.entries(iframeVitals)) {
|
|
165
|
+
const { iframeSrc } = obj;
|
|
166
|
+
const totalViews = (obj.pageview.mobile || 0) + (obj.pageview.desktop || 0);
|
|
167
|
+
if (!groupedByIframeSrc[iframeSrc] || totalViews > groupedByIframeSrc[iframeSrc].totalViews) {
|
|
168
|
+
groupedByIframeSrc[iframeSrc] = { url, totalViews };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const { url } of Object.values(groupedByIframeSrc)) {
|
|
173
|
+
parentWebVitals[url] = iframeVitals[url];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
populateFormsInternalNavigation(bundles, parentWebVitals);
|
|
177
|
+
findFormCTAForInternalNavigation(bundles, Object.values(parentWebVitals));
|
|
178
|
+
|
|
179
|
+
const iframeParentVitalsMap = {};
|
|
180
|
+
for (const vitals of Object.values(parentWebVitals)) {
|
|
181
|
+
iframeParentVitalsMap[vitals.iframeSrc] = vitals;
|
|
182
|
+
}
|
|
183
|
+
return iframeParentVitalsMap;
|
|
184
|
+
}
|
|
185
|
+
|
|
136
186
|
function handler(bundles) {
|
|
137
187
|
// Filter out search related events
|
|
188
|
+
|
|
138
189
|
const bundlesWithFilteredEvents = filterEvents(bundles);
|
|
139
190
|
|
|
140
191
|
const dataChunks = new DataChunks();
|
|
@@ -143,17 +194,26 @@ function handler(bundles) {
|
|
|
143
194
|
const formViewdataChunks = new DataChunks();
|
|
144
195
|
loadBundles(bundlesWithFilteredEvents, formViewdataChunks);
|
|
145
196
|
const formSourceMap = {};
|
|
197
|
+
const iframeParentMap = {};
|
|
146
198
|
const globalFormSourceSet = new Set();
|
|
147
|
-
formViewdataChunks.filter = { checkpoint: ['viewblock'] };
|
|
199
|
+
formViewdataChunks.filter = { checkpoint: ['viewblock', 'viewmedia'] };
|
|
148
200
|
formViewdataChunks.filtered.forEach(({ url, events }) => {
|
|
149
201
|
formSourceMap[url] = formSourceMap[url] || new Set();
|
|
150
|
-
events.forEach(({ checkpoint, source }) => {
|
|
202
|
+
events.forEach(({ checkpoint, source, target }) => {
|
|
151
203
|
if (checkpoint === 'viewblock' && source) {
|
|
152
204
|
formSourceMap[url].add(source);
|
|
153
205
|
globalFormSourceSet.add(source);
|
|
154
206
|
}
|
|
207
|
+
if (checkpoint === 'viewmedia' && target) {
|
|
208
|
+
const regex = /aemform[\w.]*\.iframe[\w.]*/;
|
|
209
|
+
if (regex.test(target)) {
|
|
210
|
+
iframeParentMap[target] = iframeParentMap[target] || new Set();
|
|
211
|
+
iframeParentMap[target].add(url);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
155
214
|
});
|
|
156
215
|
});
|
|
216
|
+
|
|
157
217
|
// traffic acquisition data per url - uncomment this when required
|
|
158
218
|
// const trafficByUrl = trafficAcquisition.handler(bundles);
|
|
159
219
|
// const trafficByUrlMap = Object.fromEntries(
|
|
@@ -176,25 +236,57 @@ function handler(bundles) {
|
|
|
176
236
|
// aggregates metrics per group (url and user agent)
|
|
177
237
|
dataChunks.facets.urlUserAgents.reduce((acc, { value, metrics, weight }) => {
|
|
178
238
|
const [url, userAgent] = value.split(DELIMITER);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
239
|
+
if (formSourceMap[url].has(source)) {
|
|
240
|
+
const key = generateKey(url, source);
|
|
241
|
+
acc[key] = acc[key] || initializeResult(url);
|
|
242
|
+
acc[key].pageview[userAgent] = acc[key].pageview[userAgent] || weight;
|
|
243
|
+
// Enable traffic acquisition for persistence by uncommenting this line
|
|
244
|
+
// acc[key].trafficacquisition = trafficByUrlMap[url];
|
|
245
|
+
acc[key].formsource = source;
|
|
246
|
+
// filter out user-agents with no form vitals
|
|
247
|
+
METRICS.filter((metric) => metrics[metric].sum)
|
|
248
|
+
.forEach((metric) => {
|
|
249
|
+
acc[key][metric][userAgent] = metrics[metric].sum;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
189
252
|
return acc;
|
|
190
253
|
}, formVitals);
|
|
191
254
|
});
|
|
255
|
+
|
|
256
|
+
const iframeParentVitalsMap = getParentPageVitalsGroupedByIFrame(
|
|
257
|
+
bundles,
|
|
258
|
+
dataChunks,
|
|
259
|
+
iframeParentMap,
|
|
260
|
+
);
|
|
261
|
+
|
|
192
262
|
// populate internal navigation data
|
|
193
263
|
populateFormsInternalNavigation(bundles, formVitals);
|
|
194
264
|
// filter out pages with no form vitals
|
|
195
265
|
const filteredFormVitals = Object.values(formVitals).filter(containsFormVitals);
|
|
196
266
|
findFormCTAForInternalNavigation(bundles, filteredFormVitals);
|
|
197
|
-
|
|
267
|
+
|
|
268
|
+
const updatedFormVitals = filteredFormVitals.map((formVital) => {
|
|
269
|
+
const formVitalCopy = { ...formVital };
|
|
270
|
+
const parentFormVital = iframeParentVitalsMap[formVital.url];
|
|
271
|
+
if (parentFormVital) {
|
|
272
|
+
const {
|
|
273
|
+
url,
|
|
274
|
+
pageview,
|
|
275
|
+
forminternalnavigation,
|
|
276
|
+
iframeSrc,
|
|
277
|
+
} = parentFormVital;
|
|
278
|
+
Object.assign(formVitalCopy, {
|
|
279
|
+
url,
|
|
280
|
+
pageview: { ...pageview },
|
|
281
|
+
forminternalnavigation,
|
|
282
|
+
|
|
283
|
+
iframeSrc,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return formVitalCopy;
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
return [...updatedFormVitals];
|
|
198
290
|
}
|
|
199
291
|
|
|
200
292
|
export default {
|