@mparticle/web-sdk 2.32.0 → 2.32.2

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/src/store.ts CHANGED
@@ -37,6 +37,7 @@ import {
37
37
  IGlobalStoreV2MinifiedKeys,
38
38
  IPersistenceMinified,
39
39
  } from './persistence.interfaces';
40
+ import { CookieSyncDates, IPixelConfiguration } from './cookieSyncManager';
40
41
 
41
42
  // This represents the runtime configuration of the SDK AFTER
42
43
  // initialization has been complete and all settings and
@@ -118,7 +119,6 @@ function createSDKConfig(config: SDKInitConfig): SDKConfig {
118
119
 
119
120
  // TODO: Placeholder Types to be filled in as we migrate more modules
120
121
  // to TypeScript
121
- export type PixelConfiguration = Dictionary;
122
122
  export type ServerSettings = Dictionary;
123
123
  export type SessionAttributes = Dictionary;
124
124
  export type IntegrationAttributes = Dictionary<Dictionary<string>>;
@@ -168,7 +168,7 @@ export interface IStore {
168
168
  identifyCalled: boolean;
169
169
  isLoggedIn: boolean;
170
170
  sideloadedKitsCount?: number;
171
- cookieSyncDates: Dictionary<number>;
171
+ cookieSyncDates: CookieSyncDates;
172
172
  integrationAttributes: IntegrationAttributes;
173
173
  requireDelay: boolean;
174
174
  isLocalStorageAvailable: boolean | null;
@@ -178,7 +178,7 @@ export interface IStore {
178
178
  kits: Dictionary<MPForwarder>;
179
179
  sideloadedKits: MPForwarder[];
180
180
  configuredForwarders: MPForwarder[];
181
- pixelConfigurations: PixelConfiguration[];
181
+ pixelConfigurations: IPixelConfiguration[];
182
182
  integrationDelayTimeoutStart: number; // UNIX Timestamp
183
183
  webviewBridgeEnabled?: boolean;
184
184
  wrapperSDKInfo: WrapperSDKInfo;
package/src/uploaders.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  type HTTPMethod = 'get' | 'post';
2
2
 
3
- export interface fetchPayload {
3
+ export interface IFetchPayload {
4
4
  method: string;
5
5
  headers: {
6
6
  Accept: string;
@@ -12,7 +12,7 @@ export interface fetchPayload {
12
12
  export abstract class AsyncUploader {
13
13
  url: string;
14
14
  public abstract upload(
15
- fetchPayload: fetchPayload,
15
+ fetchPayload: IFetchPayload,
16
16
  url?: string
17
17
  ): Promise<Response>;
18
18
 
@@ -23,7 +23,7 @@ export abstract class AsyncUploader {
23
23
 
24
24
  export class FetchUploader extends AsyncUploader {
25
25
  public async upload(
26
- fetchPayload: fetchPayload,
26
+ fetchPayload: IFetchPayload,
27
27
  _url?: string
28
28
  ): Promise<Response> {
29
29
  const url = _url || this.url;
@@ -32,7 +32,7 @@ export class FetchUploader extends AsyncUploader {
32
32
  }
33
33
 
34
34
  export class XHRUploader extends AsyncUploader {
35
- public async upload(fetchPayload: fetchPayload): Promise<Response> {
35
+ public async upload(fetchPayload: IFetchPayload): Promise<Response> {
36
36
  const response: Response = await this.makeRequest(
37
37
  this.url,
38
38
  fetchPayload.body,
package/src/utils.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { MPID } from '@mparticle/web-sdk';
1
2
  import Constants from './constants';
2
3
 
3
4
  const { Messages } = Constants;
@@ -175,6 +176,27 @@ const replaceApostrophesWithQuotes = (value: string): string =>
175
176
  const replaceQuotesWithApostrophes = (value: string): string =>
176
177
  value.replace(/\"/g, "'");
177
178
 
179
+ const replaceMPID = (value: string, mpid: MPID): string => value.replace('%%mpid%%', mpid);
180
+
181
+ const replaceAmpWithAmpersand = (value: string): string => value.replace(/&amp;/g, '&');
182
+
183
+ const createCookieSyncUrl = (
184
+ mpid: MPID,
185
+ pixelUrl: string,
186
+ redirectUrl?: string
187
+ ): string => {
188
+ const modifiedPixelUrl = replaceAmpWithAmpersand(pixelUrl);
189
+ const modifiedDirectUrl = redirectUrl
190
+ ? replaceAmpWithAmpersand(redirectUrl)
191
+ : null;
192
+
193
+ const url = replaceMPID(modifiedPixelUrl, mpid);
194
+ const redirect = modifiedDirectUrl
195
+ ? replaceMPID(modifiedDirectUrl, mpid)
196
+ : '';
197
+ return url + encodeURIComponent(redirect);
198
+ };
199
+
178
200
  // FIXME: REFACTOR for V3
179
201
  // only used in store.js to sanitize server-side formatting of
180
202
  // booleans when checking for `isDevelopmentMode`
@@ -336,6 +358,7 @@ const getHref = (): string => {
336
358
  export {
337
359
  createCookieString,
338
360
  revertCookieString,
361
+ createCookieSyncUrl,
339
362
  valueof,
340
363
  converted,
341
364
  decoded,
@@ -366,4 +389,6 @@ export {
366
389
  queryStringParser,
367
390
  getCookies,
368
391
  getHref,
392
+ replaceMPID,
393
+ replaceAmpWithAmpersand,
369
394
  };
@@ -1,89 +0,0 @@
1
- import { IAliasRequest, IAliasCallback } from "./identity.interfaces";
2
- import { MParticleWebSDK } from "./sdkRuntimeModels";
3
- import Constants from './constants';
4
- import { FetchUploader, XHRUploader } from './uploaders';
5
- import { HTTP_ACCEPTED, HTTP_OK } from "./constants";
6
-
7
-
8
- const { HTTPCodes, Messages } = Constants;
9
-
10
- interface IAliasResponseBody {
11
- message?: string
12
- }
13
-
14
- export async function sendAliasRequest (mpInstance: MParticleWebSDK, aliasRequest: IAliasRequest, aliasCallback: IAliasCallback) {
15
- const { verbose, error } = mpInstance.Logger;
16
- const { invokeAliasCallback } = mpInstance._Helpers;
17
- const { aliasUrl } = mpInstance._Store.SDKConfig;
18
- const { devToken: apiKey } = mpInstance._Store;
19
-
20
- verbose(Messages.InformationMessages.SendAliasHttp);
21
-
22
- // https://go.mparticle.com/work/SQDSDKS-6750
23
- const uploadUrl = `https://${aliasUrl}${apiKey}/Alias`;
24
- const uploader = window.fetch
25
- ? new FetchUploader(uploadUrl)
26
- : new XHRUploader(uploadUrl);
27
-
28
-
29
- // https://go.mparticle.com/work/SQDSDKS-6568
30
- const uploadPayload = {
31
- method: 'post',
32
- headers: {
33
- Accept: 'text/plain;charset=UTF-8',
34
- 'Content-Type': 'application/json',
35
- },
36
- body: JSON.stringify(aliasRequest),
37
- };
38
-
39
- try {
40
- const response = await uploader.upload(uploadPayload);
41
-
42
- let message: string;
43
- let aliasResponseBody: IAliasResponseBody;
44
-
45
- // FetchUploader returns the response as a JSON object that we have to await
46
- if (response.json) {
47
- // HTTP responses of 202, 200, and 403 do not have a response. response.json will always exist on a fetch, but can only be await-ed when the response is not empty, otherwise it will throw an error.
48
- try {
49
- aliasResponseBody = await response.json();
50
- } catch (e) {
51
- verbose('The request has no response body');
52
- }
53
- } else {
54
- // https://go.mparticle.com/work/SQDSDKS-6568
55
- // XHRUploader returns the response as a string that we need to parse
56
- const xhrResponse = response as unknown as XMLHttpRequest;
57
-
58
- aliasResponseBody = xhrResponse.responseText
59
- ? JSON.parse(xhrResponse.responseText)
60
- : '';
61
- }
62
-
63
- let errorMessage: string;
64
-
65
- switch (response.status) {
66
- case HTTP_OK:
67
- case HTTP_ACCEPTED:
68
- // https://go.mparticle.com/work/SQDSDKS-6670
69
- message =
70
- 'Successfully sent forwarding stats to mParticle Servers';
71
- break;
72
- default:
73
- // 400 has an error message, but 403 doesn't
74
- if (aliasResponseBody?.message) {
75
- errorMessage = aliasResponseBody.message;
76
- }
77
- message =
78
- 'Issue with sending Alias Request to mParticle Servers, received HTTP Code of ' +
79
- response.status;
80
- }
81
-
82
- verbose(message);
83
- invokeAliasCallback(aliasCallback, response.status, errorMessage);
84
- } catch (e) {
85
- const err = e as Error;
86
- error('Error sending alias request to mParticle servers. ' + err);
87
- invokeAliasCallback(aliasCallback, HTTPCodes.noHttpCoverage, (err.message));
88
- }
89
- };
@@ -1,197 +0,0 @@
1
- import Constants from './constants';
2
-
3
- var Messages = Constants.Messages;
4
-
5
- export default function cookieSyncManager(mpInstance) {
6
- var self = this;
7
-
8
- // Public
9
- this.attemptCookieSync = function(previousMPID, mpid, mpidIsNotInCookies) {
10
- // TODO: These should move inside the for loop
11
- var pixelConfig,
12
- lastSyncDateForModule,
13
- url,
14
- redirect,
15
- urlWithRedirect,
16
- requiresConsent;
17
-
18
- // TODO: Make this exit quicker instead of nested
19
- if (mpid && !mpInstance._Store.webviewBridgeEnabled) {
20
- mpInstance._Store.pixelConfigurations.forEach(function(
21
- pixelSettings
22
- ) {
23
- // set requiresConsent to false to start each additional pixel configuration
24
- // set to true only if filteringConsenRuleValues.values.length exists
25
- requiresConsent = false;
26
-
27
- // TODO: Replace with isEmpty
28
- if (
29
- pixelSettings.filteringConsentRuleValues &&
30
- pixelSettings.filteringConsentRuleValues.values &&
31
- pixelSettings.filteringConsentRuleValues.values.length
32
- ) {
33
- requiresConsent = true;
34
- }
35
-
36
- pixelConfig = {
37
- // Kit Module ID
38
- moduleId: pixelSettings.moduleId,
39
-
40
- // Tells you how often we should do a cookie sync (in days)
41
- frequencyCap: pixelSettings.frequencyCap,
42
-
43
- // Url for cookie sync pixel
44
- pixelUrl: self.replaceAmp(pixelSettings.pixelUrl),
45
-
46
- // TODO: Document requirements for redirectUrl
47
- redirectUrl: pixelSettings.redirectUrl
48
- ? self.replaceAmp(pixelSettings.redirectUrl)
49
- : null,
50
-
51
- // Filtering rules as defined in UI
52
- filteringConsentRuleValues:
53
- pixelSettings.filteringConsentRuleValues,
54
- };
55
-
56
- // TODO: combine replaceMPID and replaceAmp into sanitizeUrl function
57
- url = self.replaceMPID(pixelConfig.pixelUrl, mpid);
58
- redirect = pixelConfig.redirectUrl
59
- ? self.replaceMPID(pixelConfig.redirectUrl, mpid)
60
- : '';
61
- urlWithRedirect = url + encodeURIComponent(redirect);
62
-
63
- // TODO: Refactor so that Persistence is only called once
64
- // outside of the loop
65
- var persistence = mpInstance._Persistence.getPersistence();
66
-
67
- // TODO: Is there a historic reason for checking for previousMPID?
68
- // it does not appear to be passed in anywhere
69
- if (previousMPID && previousMPID !== mpid) {
70
- if (persistence && persistence[mpid]) {
71
- if (!persistence[mpid].csd) {
72
- persistence[mpid].csd = {};
73
- }
74
- self.performCookieSync(
75
- urlWithRedirect,
76
- pixelConfig.moduleId,
77
- mpid,
78
- persistence[mpid].csd,
79
- pixelConfig.filteringConsentRuleValues,
80
- mpidIsNotInCookies,
81
- requiresConsent
82
- );
83
- }
84
- return;
85
- } else {
86
- // TODO: Refactor to check for the inverse and exit early
87
- // rather than nesting
88
- if (persistence[mpid]) {
89
- if (!persistence[mpid].csd) {
90
- persistence[mpid].csd = {};
91
- }
92
- lastSyncDateForModule = persistence[mpid].csd[
93
- pixelConfig.moduleId.toString()
94
- ]
95
- ? persistence[mpid].csd[
96
- pixelConfig.moduleId.toString()
97
- ]
98
- : null;
99
-
100
- if (lastSyncDateForModule) {
101
- // Check to see if we need to refresh cookieSync
102
- if (
103
- // TODO: Turn this into a convenience method for readability?
104
- // We use similar comparisons elsewhere in the SDK,
105
- // so perhaps we can make a time comparison convenience method
106
- new Date().getTime() >
107
- new Date(lastSyncDateForModule).getTime() +
108
- pixelConfig.frequencyCap *
109
- // TODO: Turn these numbers into a constant so
110
- // we can remember what this number is for
111
- 60 *
112
- 1000 *
113
- 60 *
114
- 24
115
- ) {
116
- self.performCookieSync(
117
- urlWithRedirect,
118
- pixelConfig.moduleId,
119
- mpid,
120
- persistence[mpid].csd,
121
- pixelConfig.filteringConsentRuleValues,
122
- mpidIsNotInCookies,
123
- requiresConsent
124
- );
125
- }
126
- } else {
127
- self.performCookieSync(
128
- urlWithRedirect,
129
- pixelConfig.moduleId,
130
- mpid,
131
- persistence[mpid].csd,
132
- pixelConfig.filteringConsentRuleValues,
133
- mpidIsNotInCookies,
134
- requiresConsent
135
- );
136
- }
137
- }
138
- }
139
- });
140
- }
141
- };
142
-
143
- // Private
144
- this.replaceMPID = function(string, mpid) {
145
- return string.replace('%%mpid%%', mpid);
146
- };
147
-
148
- // Private
149
- // TODO: Rename function to replaceAmpWithAmpersand
150
- this.replaceAmp = function(string) {
151
- return string.replace(/&amp;/g, '&');
152
- };
153
-
154
- // Private
155
- this.performCookieSync = function(
156
- url,
157
- moduleId,
158
- mpid,
159
- cookieSyncDates,
160
- filteringConsentRuleValues,
161
- mpidIsNotInCookies,
162
- requiresConsent
163
- ) {
164
- // if MPID is new to cookies, we should not try to perform the cookie sync
165
- // because a cookie sync can only occur once a user either consents or doesn't
166
- // we should not check if its enabled if the user has a blank consent
167
- // TODO: We should do this check outside of this function
168
- if (requiresConsent && mpidIsNotInCookies) {
169
- return;
170
- }
171
-
172
- // TODO: Refactor so that check is made outside of the function.
173
- // Cache or store the boolean so that it only gets called once per
174
- // cookie sync attempt per module.
175
- // Currently, attemptCookieSync is called as a loop and therefore this
176
- // function polls the user object and consent multiple times.
177
- if (
178
- mpInstance._Consent.isEnabledForUserConsent(
179
- filteringConsentRuleValues,
180
- mpInstance.Identity.getCurrentUser()
181
- )
182
- ) {
183
- var img = document.createElement('img');
184
-
185
- mpInstance.Logger.verbose(Messages.InformationMessages.CookieSync);
186
- img.onload = function() {
187
- // TODO: Break this out into a convenience method so we can unit test
188
- cookieSyncDates[moduleId.toString()] = new Date().getTime();
189
- mpInstance._Persistence.saveUserCookieSyncDatesToPersistence(
190
- mpid,
191
- cookieSyncDates
192
- );
193
- };
194
- img.src = url;
195
- }
196
- };
197
- }
@@ -1,131 +0,0 @@
1
- import Constants from './constants';
2
- import { sendAliasRequest } from './aliasRequestApiClient';
3
- import { FetchUploader, XHRUploader } from './uploaders';
4
- import { CACHE_HEADER } from './identity-utils';
5
- import { parseNumber } from './utils';
6
-
7
- var HTTPCodes = Constants.HTTPCodes,
8
- Messages = Constants.Messages;
9
-
10
- const { Modify } = Constants.IdentityMethods;
11
-
12
- export default function IdentityAPIClient(mpInstance) {
13
- this.sendAliasRequest = async function(aliasRequest, callback) {
14
- await sendAliasRequest(mpInstance, aliasRequest, callback);
15
- };
16
-
17
- this.sendIdentityRequest = async function(
18
- identityApiRequest,
19
- method,
20
- callback,
21
- originalIdentityApiData,
22
- parseIdentityResponse,
23
- mpid,
24
- knownIdentities
25
- ) {
26
- const { verbose, error } = mpInstance.Logger;
27
- const { invokeCallback } = mpInstance._Helpers;
28
-
29
- verbose(Messages.InformationMessages.SendIdentityBegin);
30
- if (!identityApiRequest) {
31
- error(Messages.ErrorMessages.APIRequestEmpty);
32
- return;
33
- }
34
- verbose(Messages.InformationMessages.SendIdentityHttp);
35
-
36
- if (mpInstance._Store.identityCallInFlight) {
37
- invokeCallback(
38
- callback,
39
- HTTPCodes.activeIdentityRequest,
40
- 'There is currently an Identity request processing. Please wait for this to return before requesting again'
41
- );
42
- return;
43
- }
44
-
45
- const previousMPID = mpid || null;
46
- const uploadUrl = this.getUploadUrl(method, mpid);
47
-
48
- const uploader = window.fetch
49
- ? new FetchUploader(uploadUrl)
50
- : new XHRUploader(uploadUrl);
51
-
52
- const fetchPayload = {
53
- method: 'post',
54
- headers: {
55
- Accept: 'text/plain;charset=UTF-8',
56
- 'Content-Type': 'application/json',
57
- 'x-mp-key': mpInstance._Store.devToken,
58
- },
59
- body: JSON.stringify(identityApiRequest),
60
- };
61
-
62
- try {
63
- mpInstance._Store.identityCallInFlight = true;
64
- const response = await uploader.upload(fetchPayload);
65
-
66
- let identityResponse;
67
-
68
- if (response.json) {
69
- // https://go.mparticle.com/work/SQDSDKS-6568
70
- // FetchUploader returns the response as a JSON object that we have to await
71
- const responseBody = await response.json();
72
- identityResponse = this.getIdentityResponseFromFetch(
73
- response,
74
- responseBody
75
- );
76
- } else {
77
- identityResponse = this.getIdentityResponseFromXHR(response);
78
- }
79
-
80
- verbose(
81
- 'Received Identity Response from server: ' +
82
- JSON.stringify(identityResponse.responseText)
83
- );
84
-
85
- parseIdentityResponse(
86
- identityResponse,
87
- previousMPID,
88
- callback,
89
- originalIdentityApiData,
90
- method,
91
- knownIdentities,
92
- false
93
- );
94
- } catch (err) {
95
- mpInstance._Store.identityCallInFlight = false;
96
- invokeCallback(callback, HTTPCodes.noHttpCoverage, err);
97
- error('Error sending identity request to servers' + ' - ' + err);
98
- }
99
- };
100
-
101
- this.getUploadUrl = (method, mpid) => {
102
- const uploadServiceUrl = mpInstance._Helpers.createServiceUrl(
103
- mpInstance._Store.SDKConfig.identityUrl
104
- );
105
-
106
- const uploadUrl =
107
- method === Modify
108
- ? uploadServiceUrl + mpid + '/' + method
109
- : uploadServiceUrl + method;
110
-
111
- return uploadUrl;
112
- };
113
-
114
- this.getIdentityResponseFromFetch = (response, responseBody) => ({
115
- status: response.status,
116
- responseText: responseBody,
117
- cacheMaxAge: parseInt(response.headers.get(CACHE_HEADER)) || 0,
118
- expireTimestamp: 0,
119
- });
120
-
121
- this.getIdentityResponseFromXHR = response => ({
122
- status: response.status,
123
- responseText: response.responseText
124
- ? JSON.parse(response.responseText)
125
- : {},
126
- cacheMaxAge: parseNumber(
127
- response.getResponseHeader(CACHE_HEADER) || ''
128
- ),
129
- expireTimestamp: 0,
130
- });
131
- }