coveo.analytics 2.28.15 → 2.28.17

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.
@@ -658,7 +658,7 @@ const addPageViewToHistory = (pageViewValue) => __awaiter(void 0, void 0, void 0
658
658
  yield store.addElementAsync(historyElement);
659
659
  });
660
660
 
661
- const libVersion = "2.28.15" ;
661
+ const libVersion = "2.28.17" ;
662
662
 
663
663
  const getFormattedLocation = (location) => `${location.protocol}//${location.hostname}${location.pathname.indexOf('/') === 0 ? location.pathname : `/${location.pathname}`}${location.search}`;
664
664
 
@@ -1069,21 +1069,18 @@ class AnalyticsBeaconClient {
1069
1069
  constructor(opts) {
1070
1070
  this.opts = opts;
1071
1071
  }
1072
- sendEvent(eventType, payload) {
1072
+ sendEvent(eventType, originalPayload) {
1073
1073
  return __awaiter(this, void 0, void 0, function* () {
1074
1074
  if (!this.isAvailable()) {
1075
1075
  throw new Error(`navigator.sendBeacon is not supported in this browser. Consider adding a polyfill like "sendbeacon-polyfill".`);
1076
1076
  }
1077
1077
  const { baseUrl, preprocessRequest } = this.opts;
1078
- const parsedRequestData = this.encodeForEventType(eventType, payload);
1079
1078
  const paramsFragments = yield this.getQueryParamsForEventType(eventType);
1080
- const defaultOptions = {
1081
- url: `${baseUrl}/analytics/${eventType}?${paramsFragments}`,
1082
- body: new Blob([parsedRequestData], {
1083
- type: 'application/x-www-form-urlencoded',
1084
- }),
1085
- };
1086
- const { url, body } = Object.assign(Object.assign({}, defaultOptions), (preprocessRequest ? yield preprocessRequest(defaultOptions, 'analyticsBeacon') : {}));
1079
+ const { url, payload } = yield this.preProcessRequestAsPotentialJSONString(`${baseUrl}/analytics/${eventType}?${paramsFragments}`, originalPayload, preprocessRequest);
1080
+ const parsedRequestData = this.encodeForEventType(eventType, payload);
1081
+ const body = new Blob([parsedRequestData], {
1082
+ type: 'application/x-www-form-urlencoded',
1083
+ });
1087
1084
  navigator.sendBeacon(url, body);
1088
1085
  return;
1089
1086
  });
@@ -1094,6 +1091,27 @@ class AnalyticsBeaconClient {
1094
1091
  deleteHttpCookieVisitorId() {
1095
1092
  return Promise.resolve();
1096
1093
  }
1094
+ preProcessRequestAsPotentialJSONString(originalURL, originalPayload, preprocessRequest) {
1095
+ return __awaiter(this, void 0, void 0, function* () {
1096
+ let returnedUrl = originalURL;
1097
+ let returnedPayload = originalPayload;
1098
+ if (preprocessRequest) {
1099
+ const processedRequest = yield preprocessRequest({ url: originalURL, body: JSON.stringify(originalPayload) }, 'analyticsBeacon');
1100
+ const { url: processedURL, body: processedBody } = processedRequest;
1101
+ returnedUrl = processedURL || originalURL;
1102
+ try {
1103
+ returnedPayload = JSON.parse(processedBody);
1104
+ }
1105
+ catch (e) {
1106
+ console.error('Unable to process the request body as a JSON string', e);
1107
+ }
1108
+ }
1109
+ return {
1110
+ payload: returnedPayload,
1111
+ url: returnedUrl,
1112
+ };
1113
+ });
1114
+ }
1097
1115
  encodeForEventType(eventType, payload) {
1098
1116
  return this.isEventTypeLegacy(eventType)
1099
1117
  ? this.encodeForLegacyType(eventType, payload)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coveo.analytics",
3
- "version": "2.28.15",
3
+ "version": "2.28.17",
4
4
  "description": "📈 Coveo analytics client (node and browser compatible) ",
5
5
  "main": "dist/library.cjs",
6
6
  "module": "dist/browser.mjs",
@@ -67,8 +67,12 @@
67
67
  "files": [
68
68
  "modules/",
69
69
  "dist/**/*.d.ts",
70
- "dist/**/*.{js, mjs, cjs}",
71
- "dist/**/*.{js, mjs, cjs}.map",
70
+ "dist/**/*.js",
71
+ "dist/**/*.mjs",
72
+ "dist/**/*.cjs",
73
+ "dist/**/*.js.map",
74
+ "dist/**/*.mjs.map",
75
+ "dist/**/*.cjs.map",
72
76
  "src/**/*.ts",
73
77
  "react-native/",
74
78
  "LICENSE"
@@ -1,6 +1,6 @@
1
1
  import {AnalyticsBeaconClient} from './analyticsBeaconClient';
2
2
  import {EventType} from '../events';
3
- import {AnalyticsClientOrigin, IAnalyticsRequestOptions} from './analyticsRequestClient';
3
+ import {AnalyticsClientOrigin, IAnalyticsRequestOptions, PreprocessAnalyticsRequest} from './analyticsRequestClient';
4
4
 
5
5
  describe('AnalyticsBeaconClient', () => {
6
6
  const baseUrl = 'https://bloup.com';
@@ -99,33 +99,94 @@ describe('AnalyticsBeaconClient', () => {
99
99
  );
100
100
  });
101
101
 
102
- it('should preprocess the request with the preprocessRequest', async () => {
103
- let clientOrigin: AnalyticsClientOrigin;
104
- const processedRequest: IAnalyticsRequestOptions = {
105
- url: 'https://www.myownanalytics.com/endpoint',
106
- body: JSON.stringify({
107
- test: 'custom',
108
- }),
109
- };
110
- const client = new AnalyticsBeaconClient({
111
- baseUrl,
112
- token,
113
- visitorIdProvider: {
114
- getCurrentVisitorId: () => {
115
- return Promise.resolve(currentVisitorId);
102
+ describe('allows to preprocessRequest', () => {
103
+ const setupClient = (preprocessRequest: PreprocessAnalyticsRequest) => {
104
+ return new AnalyticsBeaconClient({
105
+ baseUrl,
106
+ token,
107
+ visitorIdProvider: {
108
+ getCurrentVisitorId: () => {
109
+ return Promise.resolve(currentVisitorId);
110
+ },
111
+ setCurrentVisitorId: () => {},
116
112
  },
117
- setCurrentVisitorId: () => {},
118
- },
119
- preprocessRequest: (_request, type) => {
113
+ preprocessRequest,
114
+ });
115
+ };
116
+
117
+ it('to modify the origin and the body of the request', async () => {
118
+ let clientOrigin: AnalyticsClientOrigin;
119
+ const processedRequest: IAnalyticsRequestOptions = {
120
+ url: 'https://www.myownanalytics.com/endpoint',
121
+ body: JSON.stringify({
122
+ test: 'custom',
123
+ }),
124
+ };
125
+ const client = setupClient((_request, type) => {
120
126
  clientOrigin = type;
121
127
  return processedRequest;
122
- },
128
+ });
129
+
130
+ await client.sendEvent(EventType.collect, {});
131
+
132
+ expect(clientOrigin!).toBe('analyticsBeacon');
133
+ expect(sendBeaconMock).toHaveBeenCalledWith(processedRequest.url, expect.anything());
134
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain('test=custom');
123
135
  });
124
136
 
125
- await client.sendEvent(EventType.collect, {});
137
+ it('to modify the request body as a JSON string for a collect event', async () => {
138
+ const client = setupClient((request) => {
139
+ const bodyShouldBeAvailableAsJSONString = JSON.parse(request.body as string);
140
+ expect(bodyShouldBeAvailableAsJSONString).toEqual({foo: 'bar'});
141
+ bodyShouldBeAvailableAsJSONString.foo = 'baz';
142
+ request.body = JSON.stringify(bodyShouldBeAvailableAsJSONString);
143
+ return request;
144
+ });
145
+
146
+ await client.sendEvent(EventType.collect, {foo: 'bar'});
147
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(`foo=baz`);
148
+ });
149
+
150
+ it('to modify the request body as a JSON string for a click event', async () => {
151
+ const client = setupClient((request) => {
152
+ const bodyParsedAsJSON = JSON.parse(request.body as string);
153
+ expect(bodyParsedAsJSON).toEqual({actionCause: 'foo'});
154
+ bodyParsedAsJSON.actionCause = 'bar';
155
+ request.body = JSON.stringify(bodyParsedAsJSON);
156
+ return request;
157
+ });
158
+
159
+ await client.sendEvent(EventType.click, {actionCause: 'foo'});
160
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
161
+ `clickEvent=${encodeURIComponent(`{"actionCause":"bar"}`)}`
162
+ );
163
+ });
126
164
 
127
- expect(clientOrigin!).toBe('analyticsBeacon');
128
- expect(sendBeaconMock).toHaveBeenCalledWith(processedRequest.url, processedRequest.body);
165
+ it('to augment the request body as a JSON string for a click event', async () => {
166
+ const client = setupClient((request) => {
167
+ const bodyParsedAsJSON = JSON.parse(request.body as string);
168
+ bodyParsedAsJSON.aNewProperty = 'bar';
169
+ request.body = JSON.stringify(bodyParsedAsJSON);
170
+ return request;
171
+ });
172
+
173
+ await client.sendEvent(EventType.click, {actionCause: 'foo'});
174
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
175
+ `clickEvent=${encodeURIComponent(`{"actionCause":"foo","aNewProperty":"bar"}`)}`
176
+ );
177
+ });
178
+
179
+ it('should keep original request body if preprocessRequest returns an invalid JSON string', async () => {
180
+ const client = setupClient((request) => {
181
+ request.body = 'invalid JSON string';
182
+ return request;
183
+ });
184
+
185
+ await client.sendEvent(EventType.click, {actionCause: 'bar'});
186
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
187
+ `clickEvent=${encodeURIComponent(`{"actionCause":"bar"}`)}`
188
+ );
189
+ });
129
190
  });
130
191
 
131
192
  const getSendBeaconFirstCallBlobArgument = async () => {
@@ -1,10 +1,10 @@
1
- import {AnalyticsRequestClient, IAnalyticsRequestOptions, IAnalyticsClientOptions} from './analyticsRequestClient';
1
+ import {AnalyticsRequestClient, IAnalyticsClientOptions, PreprocessAnalyticsRequest} from './analyticsRequestClient';
2
2
  import {EventType, IRequestPayload} from '../events';
3
3
 
4
4
  export class AnalyticsBeaconClient implements AnalyticsRequestClient {
5
5
  constructor(private opts: IAnalyticsClientOptions) {}
6
6
 
7
- public async sendEvent(eventType: EventType, payload: IRequestPayload): Promise<void> {
7
+ public async sendEvent(eventType: EventType, originalPayload: IRequestPayload): Promise<void> {
8
8
  if (!this.isAvailable()) {
9
9
  throw new Error(
10
10
  `navigator.sendBeacon is not supported in this browser. Consider adding a polyfill like "sendbeacon-polyfill".`
@@ -12,18 +12,19 @@ export class AnalyticsBeaconClient implements AnalyticsRequestClient {
12
12
  }
13
13
 
14
14
  const {baseUrl, preprocessRequest} = this.opts;
15
- const parsedRequestData = this.encodeForEventType(eventType, payload);
15
+
16
16
  const paramsFragments = await this.getQueryParamsForEventType(eventType);
17
- const defaultOptions: IAnalyticsRequestOptions = {
18
- url: `${baseUrl}/analytics/${eventType}?${paramsFragments}`,
19
- body: new Blob([parsedRequestData], {
20
- type: 'application/x-www-form-urlencoded',
21
- }),
22
- };
23
- const {url, body}: IAnalyticsRequestOptions = {
24
- ...defaultOptions,
25
- ...(preprocessRequest ? await preprocessRequest(defaultOptions, 'analyticsBeacon') : {}),
26
- };
17
+
18
+ const {url, payload} = await this.preProcessRequestAsPotentialJSONString(
19
+ `${baseUrl}/analytics/${eventType}?${paramsFragments}`,
20
+ originalPayload,
21
+ preprocessRequest
22
+ );
23
+
24
+ const parsedRequestData = this.encodeForEventType(eventType, payload);
25
+ const body = new Blob([parsedRequestData], {
26
+ type: 'application/x-www-form-urlencoded',
27
+ });
27
28
 
28
29
  navigator.sendBeacon(url, body as any); // https://github.com/microsoft/TypeScript/issues/38715
29
30
  return;
@@ -37,6 +38,34 @@ export class AnalyticsBeaconClient implements AnalyticsRequestClient {
37
38
  return Promise.resolve();
38
39
  }
39
40
 
41
+ private async preProcessRequestAsPotentialJSONString(
42
+ originalURL: string,
43
+ originalPayload: IRequestPayload,
44
+ preprocessRequest?: PreprocessAnalyticsRequest
45
+ ): Promise<{url: string; payload: IRequestPayload}> {
46
+ let returnedUrl = originalURL;
47
+ let returnedPayload = originalPayload;
48
+
49
+ if (preprocessRequest) {
50
+ const processedRequest = await preprocessRequest(
51
+ {url: originalURL, body: JSON.stringify(originalPayload)},
52
+ 'analyticsBeacon'
53
+ );
54
+ const {url: processedURL, body: processedBody} = processedRequest;
55
+ returnedUrl = processedURL || originalURL;
56
+ try {
57
+ returnedPayload = JSON.parse(processedBody as string);
58
+ } catch (e) {
59
+ console.error('Unable to process the request body as a JSON string', e);
60
+ }
61
+ }
62
+
63
+ return {
64
+ payload: returnedPayload,
65
+ url: returnedUrl,
66
+ };
67
+ }
68
+
40
69
  private encodeForEventType(eventType: EventType, payload: IRequestPayload): string {
41
70
  return this.isEventTypeLegacy(eventType)
42
71
  ? this.encodeForLegacyType(eventType, payload)