@magicpixel/rn-mp-client-sdk 0.2.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.
Files changed (138) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +29 -0
  3. package/lib/commonjs/common/app-types.js +6 -0
  4. package/lib/commonjs/common/app-types.js.map +1 -0
  5. package/lib/commonjs/common/constants.js +51 -0
  6. package/lib/commonjs/common/constants.js.map +1 -0
  7. package/lib/commonjs/common/data-store.js +361 -0
  8. package/lib/commonjs/common/data-store.js.map +1 -0
  9. package/lib/commonjs/common/event-bus.js +42 -0
  10. package/lib/commonjs/common/event-bus.js.map +1 -0
  11. package/lib/commonjs/common/logger.js +30 -0
  12. package/lib/commonjs/common/logger.js.map +1 -0
  13. package/lib/commonjs/common/network-service.js +90 -0
  14. package/lib/commonjs/common/network-service.js.map +1 -0
  15. package/lib/commonjs/common/reporter.js +107 -0
  16. package/lib/commonjs/common/reporter.js.map +1 -0
  17. package/lib/commonjs/common/utils.js +276 -0
  18. package/lib/commonjs/common/utils.js.map +1 -0
  19. package/lib/commonjs/coverage/clover.xml +6 -0
  20. package/lib/commonjs/coverage/coverage-final.json +1 -0
  21. package/lib/commonjs/coverage/lcov-report/base.css +224 -0
  22. package/lib/commonjs/coverage/lcov-report/block-navigation.js +83 -0
  23. package/lib/commonjs/coverage/lcov-report/block-navigation.js.map +1 -0
  24. package/lib/commonjs/coverage/lcov-report/favicon.png +0 -0
  25. package/lib/commonjs/coverage/lcov-report/index.html +101 -0
  26. package/lib/commonjs/coverage/lcov-report/prettify.css +1 -0
  27. package/lib/commonjs/coverage/lcov-report/prettify.js +995 -0
  28. package/lib/commonjs/coverage/lcov-report/prettify.js.map +1 -0
  29. package/lib/commonjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  30. package/lib/commonjs/coverage/lcov-report/sorter.js +212 -0
  31. package/lib/commonjs/coverage/lcov-report/sorter.js.map +1 -0
  32. package/lib/commonjs/coverage/lcov.info +0 -0
  33. package/lib/commonjs/eedl/eedl.js +262 -0
  34. package/lib/commonjs/eedl/eedl.js.map +1 -0
  35. package/lib/commonjs/index.js +214 -0
  36. package/lib/commonjs/index.js.map +1 -0
  37. package/lib/commonjs/models/mp-client-sdk.js +33 -0
  38. package/lib/commonjs/models/mp-client-sdk.js.map +1 -0
  39. package/lib/commonjs/processors/data-element.processor.js +191 -0
  40. package/lib/commonjs/processors/data-element.processor.js.map +1 -0
  41. package/lib/commonjs/processors/qc.processor.js +111 -0
  42. package/lib/commonjs/processors/qc.processor.js.map +1 -0
  43. package/lib/commonjs/processors/tag.processor.js +432 -0
  44. package/lib/commonjs/processors/tag.processor.js.map +1 -0
  45. package/lib/commonjs/processors/trans-function.processor.js +91 -0
  46. package/lib/commonjs/processors/trans-function.processor.js.map +1 -0
  47. package/lib/commonjs/processors/visit-id.processor.js +172 -0
  48. package/lib/commonjs/processors/visit-id.processor.js.map +1 -0
  49. package/lib/module/common/app-types.js +2 -0
  50. package/lib/module/common/app-types.js.map +1 -0
  51. package/lib/module/common/constants.js +41 -0
  52. package/lib/module/common/constants.js.map +1 -0
  53. package/lib/module/common/data-store.js +346 -0
  54. package/lib/module/common/data-store.js.map +1 -0
  55. package/lib/module/common/event-bus.js +31 -0
  56. package/lib/module/common/event-bus.js.map +1 -0
  57. package/lib/module/common/logger.js +21 -0
  58. package/lib/module/common/logger.js.map +1 -0
  59. package/lib/module/common/network-service.js +73 -0
  60. package/lib/module/common/network-service.js.map +1 -0
  61. package/lib/module/common/reporter.js +88 -0
  62. package/lib/module/common/reporter.js.map +1 -0
  63. package/lib/module/common/utils.js +263 -0
  64. package/lib/module/common/utils.js.map +1 -0
  65. package/lib/module/coverage/clover.xml +6 -0
  66. package/lib/module/coverage/coverage-final.json +1 -0
  67. package/lib/module/coverage/lcov-report/base.css +224 -0
  68. package/lib/module/coverage/lcov-report/block-navigation.js +81 -0
  69. package/lib/module/coverage/lcov-report/block-navigation.js.map +1 -0
  70. package/lib/module/coverage/lcov-report/favicon.png +0 -0
  71. package/lib/module/coverage/lcov-report/index.html +101 -0
  72. package/lib/module/coverage/lcov-report/prettify.css +1 -0
  73. package/lib/module/coverage/lcov-report/prettify.js +993 -0
  74. package/lib/module/coverage/lcov-report/prettify.js.map +1 -0
  75. package/lib/module/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  76. package/lib/module/coverage/lcov-report/sorter.js +210 -0
  77. package/lib/module/coverage/lcov-report/sorter.js.map +1 -0
  78. package/lib/module/coverage/lcov.info +0 -0
  79. package/lib/module/eedl/eedl.js +246 -0
  80. package/lib/module/eedl/eedl.js.map +1 -0
  81. package/lib/module/index.js +176 -0
  82. package/lib/module/index.js.map +1 -0
  83. package/lib/module/models/mp-client-sdk.js +24 -0
  84. package/lib/module/models/mp-client-sdk.js.map +1 -0
  85. package/lib/module/processors/data-element.processor.js +176 -0
  86. package/lib/module/processors/data-element.processor.js.map +1 -0
  87. package/lib/module/processors/qc.processor.js +90 -0
  88. package/lib/module/processors/qc.processor.js.map +1 -0
  89. package/lib/module/processors/tag.processor.js +396 -0
  90. package/lib/module/processors/tag.processor.js.map +1 -0
  91. package/lib/module/processors/trans-function.processor.js +73 -0
  92. package/lib/module/processors/trans-function.processor.js.map +1 -0
  93. package/lib/module/processors/visit-id.processor.js +144 -0
  94. package/lib/module/processors/visit-id.processor.js.map +1 -0
  95. package/lib/typescript/common/app-types.d.ts +101 -0
  96. package/lib/typescript/common/constants.d.ts +21 -0
  97. package/lib/typescript/common/data-store.d.ts +81 -0
  98. package/lib/typescript/common/event-bus.d.ts +6 -0
  99. package/lib/typescript/common/logger.d.ts +5 -0
  100. package/lib/typescript/common/network-service.d.ts +8 -0
  101. package/lib/typescript/common/reporter.d.ts +12 -0
  102. package/lib/typescript/common/utils.d.ts +38 -0
  103. package/lib/typescript/eedl/eedl.d.ts +46 -0
  104. package/lib/typescript/index.d.ts +18 -0
  105. package/lib/typescript/models/mp-client-sdk.d.ts +157 -0
  106. package/lib/typescript/processors/data-element.processor.d.ts +12 -0
  107. package/lib/typescript/processors/qc.processor.d.ts +4 -0
  108. package/lib/typescript/processors/tag.processor.d.ts +27 -0
  109. package/lib/typescript/processors/trans-function.processor.d.ts +12 -0
  110. package/lib/typescript/processors/visit-id.processor.d.ts +7 -0
  111. package/package.json +170 -0
  112. package/src/common/app-types.ts +128 -0
  113. package/src/common/constants.ts +43 -0
  114. package/src/common/data-store.ts +333 -0
  115. package/src/common/event-bus.ts +35 -0
  116. package/src/common/logger.ts +19 -0
  117. package/src/common/network-service.ts +85 -0
  118. package/src/common/reporter.ts +110 -0
  119. package/src/common/utils.ts +281 -0
  120. package/src/coverage/clover.xml +6 -0
  121. package/src/coverage/coverage-final.json +1 -0
  122. package/src/coverage/lcov-report/base.css +224 -0
  123. package/src/coverage/lcov-report/block-navigation.js +87 -0
  124. package/src/coverage/lcov-report/favicon.png +0 -0
  125. package/src/coverage/lcov-report/index.html +101 -0
  126. package/src/coverage/lcov-report/prettify.css +1 -0
  127. package/src/coverage/lcov-report/prettify.js +2 -0
  128. package/src/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  129. package/src/coverage/lcov-report/sorter.js +196 -0
  130. package/src/coverage/lcov.info +0 -0
  131. package/src/eedl/eedl.ts +233 -0
  132. package/src/index.tsx +258 -0
  133. package/src/models/mp-client-sdk.ts +174 -0
  134. package/src/processors/data-element.processor.ts +249 -0
  135. package/src/processors/qc.processor.ts +115 -0
  136. package/src/processors/tag.processor.ts +527 -0
  137. package/src/processors/trans-function.processor.ts +85 -0
  138. package/src/processors/visit-id.processor.ts +164 -0
@@ -0,0 +1,115 @@
1
+ import { DataStore } from '../common/data-store';
2
+ import { Logger } from '../common/logger';
3
+ import { Utils } from '../common/utils';
4
+ import type { ClientSdkQcItem } from '../models/mp-client-sdk';
5
+ import { Constants } from '../common/constants';
6
+ import { Reporter } from '../common/reporter';
7
+
8
+ export class QcProcessor {
9
+ static processQc(
10
+ sdkQc: ClientSdkQcItem[],
11
+ eventName: string,
12
+ evtId: string
13
+ ): string[] {
14
+ const validQcList: string[] = [];
15
+ const validQcInfo: Array<{ nm: string; id: string; st: boolean }> = [];
16
+ try {
17
+ Logger.logDbg('Processing qc lists for eventId:: ', evtId);
18
+ for (const qc of sdkQc) {
19
+ let notMatching = true;
20
+ Logger.logDbg('Processing QC: ', qc.nm);
21
+
22
+ for (const qcCondition of qc.c) {
23
+ let deVal: string | number | boolean;
24
+
25
+ if (
26
+ qcCondition.param === Constants.CUST_EVT ||
27
+ qcCondition.param === Constants.MP_DL_EVT
28
+ ) {
29
+ if (!eventName) {
30
+ // this qc has an event condition but no eventName was supplied. so it is not going to match
31
+ notMatching = true;
32
+ break;
33
+ } else {
34
+ deVal = eventName;
35
+ }
36
+ } else {
37
+ deVal = DataStore.getDataElementValue(qcCondition.param);
38
+ }
39
+
40
+ if (typeof deVal === 'undefined' || deVal === null || deVal === '') {
41
+ Logger.logDbg(
42
+ 'DE ',
43
+ qcCondition.param,
44
+ 'was used, but has no value. QC will not qualify'
45
+ );
46
+ notMatching = true;
47
+ continue;
48
+ }
49
+
50
+ // check exclude conditions first
51
+ if (qcCondition.e && qcCondition.e.length > 0) {
52
+ if (
53
+ Utils.isMatch(
54
+ deVal.toString(),
55
+ qcCondition.e,
56
+ !qcCondition.e_cs,
57
+ qcCondition.e_ect
58
+ )
59
+ ) {
60
+ Logger.logDbg(
61
+ 'QC:Exc: ',
62
+ qcCondition.param,
63
+ ' did not qualify. QC: ',
64
+ qc.nm
65
+ );
66
+ notMatching = true;
67
+ continue;
68
+ } else {
69
+ notMatching = false;
70
+ }
71
+ }
72
+
73
+ if (qcCondition.i && qcCondition.i.length > 0) {
74
+ if (
75
+ !Utils.isMatch(
76
+ deVal.toString(),
77
+ qcCondition.i,
78
+ !qcCondition.i_cs,
79
+ qcCondition.i_ect
80
+ )
81
+ ) {
82
+ Logger.logDbg(
83
+ 'QC:Inc: ',
84
+ qcCondition.param,
85
+ ' did not qualify. QC: ',
86
+ qc.nm
87
+ );
88
+ notMatching = true;
89
+ break;
90
+ } else {
91
+ notMatching = false;
92
+ }
93
+ } else {
94
+ // if there is no qc, auto include
95
+ notMatching = false;
96
+ }
97
+ }
98
+
99
+ if (!notMatching) {
100
+ validQcList.push(qc.id);
101
+ validQcInfo.push({ id: qc.id, nm: qc.nm, st: true });
102
+ } else {
103
+ validQcInfo.push({ id: qc.id, nm: qc.nm, st: false });
104
+ }
105
+ }
106
+ } catch (err) {
107
+ Logger.logDbg('Error processing qc criteria: ', err);
108
+ Reporter.reportError('m:processQC', err);
109
+ }
110
+
111
+ Logger.logDbg('QC Status: ', validQcInfo);
112
+ DataStore.setValidQc(validQcList, validQcInfo);
113
+ return validQcList;
114
+ }
115
+ }
@@ -0,0 +1,527 @@
1
+ /**
2
+ * Finds all the applicable tags based on the true qualification criteria. Method does not return anything
3
+ * because it uses pass by memory reference. The last 2 paramters (allTagInfo and applicableTagInfo) are
4
+ * return variables
5
+ * @param sdkTags
6
+ * @param sdkProviders
7
+ * @param validQcList
8
+ * @param allTagInfo
9
+ * @param applicableTagInfo
10
+ */
11
+ import type {
12
+ HydrateTagInfo,
13
+ MapLike,
14
+ ReplaceMode,
15
+ TagInfoItem,
16
+ } from '../common/app-types';
17
+ import type {
18
+ ClientSdkParamItem,
19
+ ClientSdkPrItem,
20
+ ClientSdkTagItem,
21
+ } from '../models/mp-client-sdk';
22
+ import { Logger } from '../common/logger';
23
+ import { DataStore } from '../common/data-store';
24
+ import { Reporter } from '../common/reporter';
25
+ import { Constants } from '../common/constants';
26
+ import { Utils } from '../common/utils';
27
+
28
+ export class TagProcessor {
29
+ private static findApplicableTags(
30
+ sdkTags: MapLike<ClientSdkTagItem>,
31
+ sdkProviders: MapLike<ClientSdkPrItem>,
32
+ validQcList: string[],
33
+ allTagInfo: TagInfoItem[],
34
+ applicableTagInfo: TagInfoItem[]
35
+ ): void {
36
+ const applicableTags: string[] = [];
37
+ const tagKeys = Object.keys(sdkTags);
38
+ const currentEpochSeconds = Math.round(new Date().getTime() / 1000);
39
+ Logger.logDbg('Total Tag Length: ', tagKeys.length);
40
+ for (const tagId of tagKeys) {
41
+ const tag: ClientSdkTagItem | undefined = sdkTags[tagId];
42
+ if (tag) {
43
+ let isQualified = true;
44
+ for (const qc of tag.qc) {
45
+ if (validQcList.indexOf(qc) === -1) {
46
+ isQualified = false;
47
+ allTagInfo.push({
48
+ id: tagId,
49
+ nm: tag.nm,
50
+ status: false,
51
+ qc,
52
+ pr: tag.p,
53
+ prNm: sdkProviders[tag.p]?.nm || '',
54
+ });
55
+ break;
56
+ }
57
+ }
58
+ if (isQualified && applicableTags.indexOf(tagId) === -1) {
59
+ if (this.checkTagDateValidity(currentEpochSeconds, tag.st, tag.ex)) {
60
+ applicableTags.push(tagId);
61
+ allTagInfo.push({
62
+ id: tagId,
63
+ nm: tag.nm,
64
+ status: true,
65
+ qc: tag.qc,
66
+ pr: tag.p,
67
+ prNm: sdkProviders[tag.p]?.nm || '',
68
+ });
69
+ applicableTagInfo.push({
70
+ id: tagId,
71
+ nm: tag.nm,
72
+ status: true,
73
+ qc: tag.qc,
74
+ pr: tag.p,
75
+ prNm: sdkProviders[tag.p]?.nm || '',
76
+ });
77
+ } else {
78
+ allTagInfo.push({
79
+ id: tagId,
80
+ nm: tag.nm,
81
+ status: false,
82
+ qc: tag.qc,
83
+ pr: tag.p,
84
+ prNm: sdkProviders[tag.p]?.nm || '',
85
+ });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Core function which actually puts the tag on page based on the tag type (JS or Image)
94
+ * @param eventName
95
+ * @param evtId
96
+ */
97
+ static async processTags(eventName: string, evtId: string): Promise<void> {
98
+ try {
99
+ Logger.logDbg(
100
+ 'Processing tags for event: ',
101
+ eventName,
102
+ ' with id: ',
103
+ evtId
104
+ );
105
+ const allTagInfo: Array<TagInfoItem> = [];
106
+ const applicableTagInfo: Array<TagInfoItem> = [];
107
+ const prStatus = DataStore.getPrivacyCompliance();
108
+ const dataElements = DataStore.getDataElements();
109
+ const transFunctions = DataStore.getTransFunctions();
110
+ const sdkTags = DataStore.getSdkTags();
111
+ const sdkProviders = DataStore.getSdkProviders();
112
+ const validQcList = DataStore.getValidQcList();
113
+
114
+ // Find applicable tags
115
+ this.findApplicableTags(
116
+ sdkTags,
117
+ sdkProviders,
118
+ validQcList,
119
+ allTagInfo,
120
+ applicableTagInfo
121
+ );
122
+
123
+ // set expected tag count, so that the reporter knows when all tags are finished and can report asynchronously
124
+ Reporter.setExpectedTagCount(applicableTagInfo.length, evtId);
125
+
126
+ Logger.logDbg('Applicable Tags: ', applicableTagInfo);
127
+
128
+ // process tags one by one
129
+ for (const tagInfo of applicableTagInfo) {
130
+ try {
131
+ Logger.logDbg(`Processing Tag: ${tagInfo.nm} [${tagInfo.id}]`);
132
+
133
+ const tag: ClientSdkTagItem | undefined = sdkTags[tagInfo.id];
134
+
135
+ if (!tag) {
136
+ continue;
137
+ }
138
+
139
+ const provider: ClientSdkPrItem | undefined = sdkProviders[tag.p];
140
+
141
+ if (!provider) {
142
+ continue;
143
+ }
144
+
145
+ const result = this.hydrateTag(
146
+ tagInfo,
147
+ tag,
148
+ provider,
149
+ prStatus,
150
+ dataElements,
151
+ transFunctions
152
+ );
153
+
154
+ if (result.isInError) {
155
+ Logger.logDbg(
156
+ `Tag ${tagInfo.nm} not processed because of error code: ${result.errCd}`
157
+ );
158
+ if (result.errCd === 'NP') {
159
+ Reporter.reportItem(
160
+ {
161
+ st: Constants.ST_PR_EXC,
162
+ t: tagInfo.id,
163
+ p: tagInfo.pr,
164
+ tNm: tagInfo.nm,
165
+ req: result.errMsg,
166
+ },
167
+ evtId
168
+ );
169
+ } else if (result.errCd === 'PRB') {
170
+ Reporter.reportItem(
171
+ {
172
+ st: Constants.ST_PR_BL,
173
+ t: tagInfo.id,
174
+ p: tagInfo.pr,
175
+ tNm: tagInfo.nm,
176
+ req: result.errMsg,
177
+ },
178
+ evtId
179
+ );
180
+ } else if (result.errCd === 'REF') {
181
+ Reporter.reportItem(
182
+ {
183
+ st: Constants.ST_VAL_FAIL,
184
+ t: tagInfo.id,
185
+ p: tagInfo.pr,
186
+ tNm: tagInfo.nm,
187
+ req: result.errMsg,
188
+ },
189
+ evtId
190
+ );
191
+ }
192
+ continue;
193
+ }
194
+
195
+ const finalUrl = result.content;
196
+ Logger.logDbg('Final Url :: ', finalUrl);
197
+
198
+ if (provider.typ === Constants.PR_TYP_APP) {
199
+ if (provider.sTyp === Constants.PR_S_TYP_R) {
200
+ // process and fire app event
201
+ try {
202
+ const parsedData = JSON.parse(result.content as string);
203
+ Utils.triggerEvent(
204
+ parsedData.event_bus_name,
205
+ parsedData.event_bus_payload
206
+ );
207
+ Reporter.reportItem(
208
+ {
209
+ st: Constants.ST_OK,
210
+ t: tagInfo.id,
211
+ p: tagInfo.pr,
212
+ tNm: tagInfo.nm,
213
+ req: parsedData,
214
+ },
215
+ evtId
216
+ );
217
+ } catch (err) {
218
+ Logger.logError(err);
219
+ Reporter.reportItem(
220
+ {
221
+ st: Constants.ST_ERR,
222
+ t: tagInfo.id,
223
+ p: tagInfo.pr,
224
+ tNm: tagInfo.nm,
225
+ req: result.content,
226
+ },
227
+ evtId
228
+ );
229
+ }
230
+ } else {
231
+ Logger.logError('Unsupported provider type: ' + provider.sTyp);
232
+ }
233
+ }
234
+ } catch (err) {
235
+ Logger.logError(
236
+ 'Failed loading tag: ',
237
+ tagInfo.id,
238
+ ': Name: ',
239
+ tagInfo.nm,
240
+ 'with error ',
241
+ err
242
+ );
243
+ Reporter.reportItem(
244
+ {
245
+ st: Constants.ST_ERR,
246
+ t: tagInfo.id,
247
+ p: tagInfo.pr,
248
+ tNm: tagInfo.nm,
249
+ req: (err as any)?.stack,
250
+ },
251
+ evtId
252
+ );
253
+ }
254
+ }
255
+ } catch (err) {
256
+ Logger.logError('Error processing tags: ', err);
257
+ // TODO: Report as metric to client
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Takes all the placeholders in a tag and replaces them with actual values from data elements or
263
+ * actual static values. Method returns an object with errCd and replaced content and a boolean identifier
264
+ * to denote if this process failed or not
265
+ * @param tagInfo
266
+ * @param tag
267
+ * @param provider
268
+ * @param prStatus
269
+ * @param dataElements
270
+ * @param transFunctions
271
+ */
272
+ private static hydrateTag(
273
+ tagInfo: TagInfoItem,
274
+ tag: ClientSdkTagItem,
275
+ provider: ClientSdkPrItem,
276
+ prStatus: boolean,
277
+ dataElements: MapLike<any>,
278
+ transFunctions: MapLike<any>
279
+ ): HydrateTagInfo {
280
+ Logger.logDbg(`Hydrating Tag: ${tagInfo.nm} [${tagInfo.id}]`);
281
+ if (!provider) {
282
+ // serious issue. tag is in build sdk but not the appropriate provider
283
+ Logger.logError(
284
+ `Not hydrating tag: ${tagInfo.nm} [${tagInfo.id}] because appropriate provider was not found`
285
+ );
286
+ return {
287
+ isInError: true,
288
+ content: null,
289
+ errCd: 'NP',
290
+ errMsg: `Not hydrating tag: ${tagInfo.nm} [${tagInfo.id}] because appropriate provider was not found`,
291
+ }; // No Provider
292
+ }
293
+
294
+ if (provider.pr === 'B' && !prStatus) {
295
+ Logger.logDbg(
296
+ `Not hydrating provider: ${provider.nm} [${tag.p}] because it is blacklisted`
297
+ );
298
+ return {
299
+ isInError: true,
300
+ content: null,
301
+ errCd: 'PRB',
302
+ errMsg: `Not hydrating provider: ${provider.nm} [${tag.p}] because it is blacklisted`,
303
+ }; // Privacy Blocked
304
+ }
305
+
306
+ // merge both provider and client tag params into one - tag param overwrites provider params
307
+ const params: MapLike<ClientSdkParamItem | undefined> =
308
+ Utils.mergeMaps<ClientSdkParamItem>(
309
+ undefined,
310
+ provider.rParams,
311
+ tag.rParams
312
+ );
313
+
314
+ if (provider.typ === 'app' && provider.sTyp === 'r') {
315
+ // this is o app type provider. no need to find place-holders, just convert params to an object and prepare the json object
316
+ return this.resourceParamsToActualValues(
317
+ Object.values(params),
318
+ dataElements,
319
+ transFunctions
320
+ );
321
+ } else {
322
+ // make tag url
323
+ const preReplaceUrl: string = provider.url + (tag.url ? tag.url : '');
324
+ const finalUrl: string = provider.url + (tag.url ? tag.url : '');
325
+
326
+ // find placeholder values
327
+ const placeHolders: string[] = this.findPlaceHolders(preReplaceUrl);
328
+ // process tag attributes
329
+ return this.replacePlaceHolders(
330
+ finalUrl,
331
+ placeHolders,
332
+ dataElements,
333
+ transFunctions,
334
+ params,
335
+ provider.chld || tag.chld,
336
+ 'rph'
337
+ );
338
+ }
339
+ }
340
+
341
+ private static findPlaceHolders(content: string): string[] {
342
+ const placeHolders: string[] = [];
343
+ let match = Constants.PLACEHOLDER_REGEX.exec(content);
344
+ while (match !== null) {
345
+ placeHolders.push(match[2]);
346
+ match = Constants.PLACEHOLDER_REGEX.exec(content);
347
+ }
348
+ return placeHolders;
349
+ }
350
+
351
+ private static resourceParamsToActualValues(
352
+ params: ClientSdkParamItem[],
353
+ deItems: MapLike,
354
+ transFunctions: MapLike
355
+ ): HydrateTagInfo {
356
+ // resolve all param values and convert them into a temporary structure
357
+ const paramValueArray: MapLike = {};
358
+
359
+ for (const param of params) {
360
+ if (param.val_src !== 'CHILDREN') {
361
+ const deVal = this.parseParamValue(param, deItems, transFunctions);
362
+
363
+ if (param.rqd && typeof deVal === 'undefined') {
364
+ return {
365
+ isInError: true,
366
+ content: '',
367
+ errCd: 'REF',
368
+ errMsg: `Tag validation failed. Parameter ${param.nm} is marked as mandatory, but no value was found in source.`,
369
+ };
370
+ }
371
+
372
+ paramValueArray[param.fKey] = deVal;
373
+ }
374
+ }
375
+
376
+ const op = Utils.unflattenObject(paramValueArray);
377
+
378
+ return {
379
+ isInError: false,
380
+ content: JSON.stringify(op),
381
+ errCd: null,
382
+ errMsg: null,
383
+ };
384
+ }
385
+
386
+ private static parseParamValue(
387
+ paramItem: ClientSdkParamItem,
388
+ deItems: MapLike<any>,
389
+ transFunctions: MapLike<any>
390
+ ): any {
391
+ let paramValue = undefined;
392
+ if (paramItem.sv || paramItem.de || paramItem.tfId) {
393
+ if (paramItem.sv) {
394
+ paramValue = paramItem.sv;
395
+ } else if (paramItem.de) {
396
+ paramValue = deItems[paramItem.de];
397
+ } else if (paramItem.tfId) {
398
+ paramValue = transFunctions[paramItem.tfId];
399
+ }
400
+
401
+ paramValue = Utils.applyTransformationResourceParam(
402
+ paramValue,
403
+ paramItem.tf
404
+ );
405
+ return paramValue;
406
+ } else {
407
+ return undefined;
408
+ }
409
+ }
410
+
411
+ private static replacePlaceHolders(
412
+ content: string,
413
+ placeHolders: string[],
414
+ deItems: MapLike<any>,
415
+ transFunctions: MapLike<any>,
416
+ params: MapLike<ClientSdkParamItem>,
417
+ hasChildren: boolean,
418
+ replaceMode: ReplaceMode
419
+ ): HydrateTagInfo {
420
+ let isInError = false;
421
+ let errMsg;
422
+
423
+ if (!params) {
424
+ return {
425
+ isInError: false,
426
+ content,
427
+ errCd: null,
428
+ errMsg: 'No parameters where found to process in this tag',
429
+ };
430
+ }
431
+
432
+ if (hasChildren) {
433
+ // convert the flattened parameters to parent - child format. this is inside a condition
434
+ // because we may not have a lot of JSON type parameters and we can save some processing time by limiting
435
+ // when this is done
436
+ params = Utils.unFlattenResourceParams(Object.values(params));
437
+ }
438
+
439
+ for (const ph of placeHolders) {
440
+ try {
441
+ const tagParam: ClientSdkParamItem = params[ph];
442
+ if (tagParam) {
443
+ if (tagParam.children && tagParam.children.length > 0) {
444
+ const paramValue: MapLike<any> = {};
445
+ for (const childParam of tagParam.children) {
446
+ paramValue[childParam.nm] = this.parseParamValue(
447
+ childParam,
448
+ deItems,
449
+ transFunctions
450
+ );
451
+ }
452
+ content = this.replaceValues(
453
+ ph,
454
+ content,
455
+ JSON.stringify(paramValue),
456
+ replaceMode
457
+ );
458
+ } else {
459
+ const paramValue = this.parseParamValue(
460
+ tagParam,
461
+ deItems,
462
+ transFunctions
463
+ );
464
+ if (typeof paramValue === 'object') {
465
+ content = this.replaceValues(
466
+ ph,
467
+ content,
468
+ JSON.stringify(paramValue),
469
+ replaceMode
470
+ );
471
+ } else {
472
+ content = this.replaceValues(
473
+ ph,
474
+ content,
475
+ paramValue,
476
+ replaceMode
477
+ );
478
+ }
479
+ }
480
+ } else {
481
+ // TODO: Report
482
+ // place holder was found but the corresponding item was not found in tag resource params
483
+ Logger.logDbg(
484
+ 'place holder:',
485
+ ph,
486
+ ', was found but the corresponding item was not found in tag resource params'
487
+ );
488
+ }
489
+ } catch (err) {
490
+ isInError = true;
491
+ errMsg = (err as any)?.stack;
492
+ break;
493
+ }
494
+ }
495
+
496
+ return {
497
+ isInError,
498
+ content,
499
+ errCd: isInError ? 'REF' : null,
500
+ errMsg: errMsg,
501
+ };
502
+ }
503
+
504
+ private static checkTagDateValidity(
505
+ currentEpochSeconds: number,
506
+ tagStart: number,
507
+ tagEnd: number
508
+ ): boolean {
509
+ return currentEpochSeconds >= tagStart && currentEpochSeconds <= tagEnd;
510
+ }
511
+
512
+ private static replaceValues(
513
+ placeHolder: string,
514
+ content: string,
515
+ val: any,
516
+ replaceMode: ReplaceMode
517
+ ): string {
518
+ let replaceVal: string;
519
+ if (replaceMode === 'kph') {
520
+ replaceVal = val && val !== '' ? val : `{{${placeHolder}}`;
521
+ } else if (replaceMode === 'rph') {
522
+ replaceVal = val && val !== '' ? val : '';
523
+ }
524
+ content = content.split(`{{${placeHolder}}}`).join(replaceVal);
525
+ return content;
526
+ }
527
+ }