@adobe/spacecat-shared-rum-api-client 2.9.10 → 2.11.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.11.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.10.0...@adobe/spacecat-shared-rum-api-client-v2.11.0) (2024-11-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * opportunity & suggestion model (+electrodb) ([#447](https://github.com/adobe/spacecat-shared/issues/447)) ([91cf931](https://github.com/adobe/spacecat-shared/commit/91cf931facbc7f13a6fe6eebe71f2948a4ec007e))
7
+
8
+ # [@adobe/spacecat-shared-rum-api-client-v2.10.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.9.10...@adobe/spacecat-shared-rum-api-client-v2.10.0) (2024-11-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * form vitals ([#429](https://github.com/adobe/spacecat-shared/issues/429)) ([1b0f92f](https://github.com/adobe/spacecat-shared/commit/1b0f92f682070ffc7db379602d94fa04cb442b10))
14
+
1
15
  # [@adobe/spacecat-shared-rum-api-client-v2.9.10](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.9.9...@adobe/spacecat-shared-rum-api-client-v2.9.10) (2024-11-11)
2
16
 
3
17
 
package/README.md CHANGED
@@ -298,6 +298,48 @@ Calculates the amount of non-inorganic (earned and owned) traffic and the click-
298
298
 
299
299
  ```
300
300
 
301
+ ### form-vitals
302
+
303
+ Collects form vitals for a specified domain within a given time interval. Identifies whether each URL has embedded forms and counts form views/submission/engagement. This data can infer opportunities, such as URLs with low CTR and limited form engagement, URLs with high page views but fewer form submissions etc.
304
+ An example response:
305
+
306
+ ```json
307
+ [
308
+ {
309
+ "url": "https://business.adobe.com/",
310
+ "formsubmit": {},
311
+ "formview": {
312
+ "desktop:mac": 800,
313
+ "desktop:windows": 1900,
314
+ "mobile:ios": 100,
315
+ "mobile:android": 300
316
+ },
317
+ "formengagement": {
318
+ "desktop:windows": 100
319
+ },
320
+ "pageview": {
321
+ "desktop:mac": 800,
322
+ "desktop:windows": 1900,
323
+ "mobile:ios": 100,
324
+ "mobile:android": 300
325
+ }
326
+ },
327
+ {
328
+ "url": "https://business.adobe.com/se/resources/main.html",
329
+ "formsubmit": {
330
+ "desktop:windows": 100
331
+ },
332
+ "formview": {},
333
+ "formengagement": {
334
+ "desktop:windows": 100
335
+ },
336
+ "pageview": {
337
+ "desktop:windows": 100
338
+ }
339
+ }
340
+ ]
341
+ ```
342
+
301
343
  ## Linting
302
344
  Lint the codebase using:
303
345
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-rum-api-client",
3
- "version": "2.9.10",
3
+ "version": "2.11.0",
4
4
  "description": "Shared modules of the Spacecat Services - Rum API client",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,94 @@
1
+ /*
2
+ * Copyright 2024 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ const FORM_SOURCE = ['.form', '.marketo', '.marketo-form'];
14
+ const BOT = 'bot';
15
+ const CHECKPOINT_MAPPING = { formsubmit: 'formsubmit', viewblock: 'formview', click: 'formengagement' };
16
+
17
+ function initializeResult(url, pageViews) {
18
+ return {
19
+ url,
20
+ formsubmit: {},
21
+ formview: {},
22
+ formengagement: {},
23
+ pageview: pageViews[url],
24
+ };
25
+ }
26
+
27
+ function collectFormVitals(bundles, pageViews) {
28
+ const results = {};
29
+
30
+ // Helper functions to identify event types
31
+ const isFormViewEvent = ({ checkpoint, source }) => checkpoint === 'viewblock' && FORM_SOURCE.includes(source);
32
+ const isFormClickEvent = ({ checkpoint, source }) => checkpoint === 'click' && source && /\bform\b/.test(source.toLowerCase());
33
+ const isFormSubmitEvent = ({ checkpoint }) => checkpoint === 'formsubmit';
34
+
35
+ for (const bundle of bundles) {
36
+ const {
37
+ url, userAgent, weight, events,
38
+ } = bundle;
39
+
40
+ if (userAgent && !userAgent.startsWith(BOT)) {
41
+ // Track if each condition has been processed for this event
42
+ const processedCheckpoints = {
43
+ formsubmit: false,
44
+ click: false,
45
+ };
46
+
47
+ // Process each event within the bundle
48
+ for (const event of events) {
49
+ const { checkpoint, source } = event;
50
+
51
+ // Only process the checkpoint once per event
52
+ if (!processedCheckpoints[checkpoint]) {
53
+ if (isFormViewEvent({ checkpoint, source })
54
+ || isFormSubmitEvent({ checkpoint })
55
+ || isFormClickEvent({ checkpoint, source })) {
56
+ results[url] = results[url] || initializeResult(url, pageViews);
57
+ const key = CHECKPOINT_MAPPING[checkpoint];
58
+ const res = results[url];
59
+ res[key] = {
60
+ ...res[key],
61
+ [userAgent]: (res[key][userAgent] || 0) + weight,
62
+ };
63
+ // Mark this checkpoint as processed - click and formsubmit as processed
64
+ if (checkpoint === 'click' || checkpoint === 'formsubmit') {
65
+ processedCheckpoints[checkpoint] = true;
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return results;
73
+ }
74
+
75
+ function pageviewsByUrlAndUserAgent(bundles) {
76
+ return bundles.reduce((acc, cur) => {
77
+ const { userAgent } = cur;
78
+ if (!userAgent || userAgent.startsWith(BOT)) return acc;
79
+ acc[cur.url] = acc[cur.url] || {};
80
+ acc[cur.url][userAgent] = (acc[cur.url][userAgent] || 0) + cur.weight;
81
+ return acc;
82
+ }, {});
83
+ }
84
+
85
+ function handler(bundles) {
86
+ const pageViews = pageviewsByUrlAndUserAgent(bundles);
87
+ const formVitals = collectFormVitals(bundles, pageViews);
88
+ return Object.values(formVitals);
89
+ }
90
+
91
+ export default {
92
+ handler,
93
+ checkpoints: ['viewblock', 'formsubmit', 'click'],
94
+ };
package/src/index.js CHANGED
@@ -12,6 +12,7 @@
12
12
  import { fetchBundles } from './common/rum-bundler-client.js';
13
13
  import notfound from './functions/404.js';
14
14
  import cwv from './functions/cwv.js';
15
+ import formVitals from './functions/form-vitals.js';
15
16
  import experiment from './functions/experiment.js';
16
17
  import trafficAcquisition from './functions/traffic-acquisition.js';
17
18
  import variant from './functions/variant.js';
@@ -22,6 +23,7 @@ import highOrganicLowCtr from './functions/opportunities/high-organic-low-ctr.js
22
23
  const HANDLERS = {
23
24
  404: notfound,
24
25
  cwv,
26
+ 'form-vitals': formVitals,
25
27
  experiment,
26
28
  'traffic-acquisition': trafficAcquisition,
27
29
  variant,