@mhubcloud/tms_core 3.4.0-beta.5 → 3.4.1
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/dist/index.js +22 -160
- package/package.json +1 -1
- package/src/defaultPublisher.ts +12 -6
- package/src/publishers/googleAds.ts +114 -29
- package/src/utils/utils.ts +35 -16
- package/tests/AffiliatePort.js +2 -1
- package/tests/CJ.js +2 -1
- package/tests/Facebook.js +2 -1
- package/tests/GoogleAds.js +5 -5
- package/tests/GoogleAnalytics4.js +2 -1
- package/tests/Piano.js +2 -1
- package/tests/UniversalAnalytics.js +2 -1
- package/src/utils/tokens/googleAdsToken.ts +0 -66
package/dist/index.js
CHANGED
|
@@ -31,23 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DefaultPublisher: () => defaultPublisher,
|
|
34
|
-
broker: () => broker_exports,
|
|
35
34
|
publisher: () => publisher,
|
|
36
35
|
testUtils: () => testUtils_exports,
|
|
37
36
|
utils: () => utils_exports
|
|
38
37
|
});
|
|
39
38
|
module.exports = __toCommonJS(index_exports);
|
|
40
39
|
|
|
41
|
-
// src/broker.ts
|
|
42
|
-
var broker_exports = {};
|
|
43
|
-
__export(broker_exports, {
|
|
44
|
-
dispatchEvent: () => dispatchEvent,
|
|
45
|
-
logToCosmosDb: () => logToCosmosDb,
|
|
46
|
-
resolveEvent: () => resolveEvent
|
|
47
|
-
});
|
|
48
|
-
var import_cosmos = require("@azure/cosmos");
|
|
49
|
-
var import_service_bus = require("@azure/service-bus");
|
|
50
|
-
|
|
51
40
|
// src/utils/utils.ts
|
|
52
41
|
var utils_exports = {};
|
|
53
42
|
__export(utils_exports, {
|
|
@@ -55,19 +44,19 @@ __export(utils_exports, {
|
|
|
55
44
|
encodeEmail: () => encodeEmail,
|
|
56
45
|
extractAdCode: () => extractAdCode,
|
|
57
46
|
flatten: () => flatten,
|
|
58
|
-
formatError: () => formatError,
|
|
59
47
|
handlePlatformRequest: () => handlePlatformRequest,
|
|
60
48
|
handleResponse: () => handleResponse,
|
|
61
49
|
logEvent: () => logEvent,
|
|
62
50
|
logException: () => logException,
|
|
63
51
|
logMessage: () => logMessage,
|
|
52
|
+
parseErrorMessage: () => parseErrorMessage,
|
|
64
53
|
prepareTestRequestHeaders: () => prepareTestRequestHeaders,
|
|
65
54
|
removeUndefined: () => removeUndefined
|
|
66
55
|
});
|
|
67
56
|
var crypto = __toESM(require("crypto"));
|
|
68
57
|
var appInsights = __toESM(require("applicationinsights"));
|
|
69
58
|
var aiClient = null;
|
|
70
|
-
if (process.env.APPLICATIONINSIGHTS_CONNECTION_STRING) {
|
|
59
|
+
if (process.env.APPLICATIONINSIGHTS_CONNECTION_STRING || process.env.APPINSIGHTS_INSTRUMENTATIONKEY) {
|
|
71
60
|
appInsights.setup().setAutoCollectDependencies(false).setAutoCollectConsole(true, false).setSendLiveMetrics(false).setAutoCollectRequests(false).setAutoCollectPerformance(false).setUseDiskRetryCaching(false).start();
|
|
72
61
|
aiClient = appInsights.defaultClient;
|
|
73
62
|
}
|
|
@@ -100,17 +89,15 @@ function logMessage(level, message, properties) {
|
|
|
100
89
|
properties
|
|
101
90
|
});
|
|
102
91
|
}
|
|
103
|
-
function logEvent(publisher2, event, region
|
|
92
|
+
function logEvent(publisher2, event, region) {
|
|
104
93
|
const properties = {
|
|
105
94
|
publisher: publisher2,
|
|
106
95
|
event,
|
|
107
|
-
region
|
|
108
|
-
status,
|
|
109
|
-
payload
|
|
96
|
+
region
|
|
110
97
|
};
|
|
111
98
|
logMessage(1, "Event dispatched to platform", properties);
|
|
112
99
|
}
|
|
113
|
-
function logException(exception) {
|
|
100
|
+
function logException(log2, exception) {
|
|
114
101
|
aiClient?.trackException({ exception });
|
|
115
102
|
}
|
|
116
103
|
function removeUndefined(originalValue) {
|
|
@@ -224,10 +211,7 @@ async function handlePlatformRequest({
|
|
|
224
211
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
225
212
|
headers: { ...requestOptions.headers }
|
|
226
213
|
};
|
|
227
|
-
const rawPlatformResponse = await fetch(
|
|
228
|
-
fullUrl,
|
|
229
|
-
fetchRequestOptions
|
|
230
|
-
);
|
|
214
|
+
const rawPlatformResponse = await fetch(fullUrl, fetchRequestOptions);
|
|
231
215
|
if (process.env.PUBLISHER_TESTURL) {
|
|
232
216
|
await handleTestRequest({
|
|
233
217
|
fetchRequestOptions,
|
|
@@ -241,22 +225,10 @@ async function handlePlatformRequest({
|
|
|
241
225
|
rawPlatformResponse,
|
|
242
226
|
event.event
|
|
243
227
|
);
|
|
244
|
-
logEvent(
|
|
245
|
-
source,
|
|
246
|
-
event.event,
|
|
247
|
-
event.region,
|
|
248
|
-
rawPlatformResponse.status,
|
|
249
|
-
requestOptions.body ? JSON.stringify(requestOptions.body) : void 0
|
|
250
|
-
);
|
|
228
|
+
logEvent(source, event.event, event.region);
|
|
251
229
|
return processedPlatformResponse;
|
|
252
230
|
} else {
|
|
253
|
-
logEvent(
|
|
254
|
-
source,
|
|
255
|
-
event.event,
|
|
256
|
-
event.region,
|
|
257
|
-
rawPlatformResponse.status,
|
|
258
|
-
requestOptions.body ? JSON.stringify(requestOptions.body) : void 0
|
|
259
|
-
);
|
|
231
|
+
logEvent(source, event.event, event.region);
|
|
260
232
|
}
|
|
261
233
|
}
|
|
262
234
|
async function handleTestRequest(params) {
|
|
@@ -279,115 +251,18 @@ async function handleTestRequest(params) {
|
|
|
279
251
|
} catch {
|
|
280
252
|
}
|
|
281
253
|
}
|
|
282
|
-
function
|
|
283
|
-
let
|
|
254
|
+
function parseErrorMessage(error) {
|
|
255
|
+
let errorMessage;
|
|
284
256
|
if (error instanceof Error) {
|
|
285
|
-
formattedError = error;
|
|
286
257
|
if (error.cause) {
|
|
287
|
-
|
|
258
|
+
errorMessage = `${error.message} Cause: ${error.cause.toString()}`;
|
|
288
259
|
} else {
|
|
289
|
-
|
|
260
|
+
errorMessage = error.message;
|
|
290
261
|
}
|
|
291
262
|
} else {
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
return formattedError;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// src/broker.ts
|
|
298
|
-
var serviceBusConnectionString = process.env.ServiceBusConnectionString;
|
|
299
|
-
var cosmosConnectionString = process.env.CosmosDbConnectionString;
|
|
300
|
-
var cosmosDatabaseName = process.env.CosmosDbDatabaseName;
|
|
301
|
-
var cosmosContainerName = process.env.CosmosDbEventsContainerName;
|
|
302
|
-
var serviceBusClient;
|
|
303
|
-
var cosmosClient;
|
|
304
|
-
var senders = {};
|
|
305
|
-
function resolveEvent(eventCode, publisherConfig) {
|
|
306
|
-
if (!publisherConfig) {
|
|
307
|
-
const envConfig = process.env.Broker_PublisherConfig;
|
|
308
|
-
publisherConfig = envConfig ? JSON.parse(envConfig) : {};
|
|
309
|
-
}
|
|
310
|
-
const publishers = [];
|
|
311
|
-
for (const publisher2 of Object.keys(publisherConfig)) {
|
|
312
|
-
const publisherEvents = publisherConfig[publisher2];
|
|
313
|
-
if (publisherEvents) {
|
|
314
|
-
if (publisherEvents[0] === "*") {
|
|
315
|
-
let matchesExcluded = false;
|
|
316
|
-
if (Array.isArray(publisherEvents[1]) && publisherEvents[1].length > 0) {
|
|
317
|
-
for (const event of publisherEvents[1]) {
|
|
318
|
-
if (matchesExcluded) {
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
if (typeof event === "string") {
|
|
322
|
-
const regex = new RegExp(event);
|
|
323
|
-
matchesExcluded = regex.test(eventCode);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
if (!matchesExcluded) {
|
|
328
|
-
publishers.push(publisher2);
|
|
329
|
-
}
|
|
330
|
-
} else {
|
|
331
|
-
let match = false;
|
|
332
|
-
for (const event of publisherEvents) {
|
|
333
|
-
if (typeof event === "string") {
|
|
334
|
-
const regex = new RegExp(event);
|
|
335
|
-
match = regex.test(eventCode);
|
|
336
|
-
}
|
|
337
|
-
if (match) {
|
|
338
|
-
publishers.push(publisher2);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (publishers.length === 0) {
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
347
|
-
return publishers;
|
|
348
|
-
}
|
|
349
|
-
async function dispatchEvent(message, queues) {
|
|
350
|
-
if (!serviceBusClient) {
|
|
351
|
-
if (!serviceBusConnectionString) {
|
|
352
|
-
throw new Error("Service Bus connection string is missing.");
|
|
353
|
-
}
|
|
354
|
-
serviceBusClient = new import_service_bus.ServiceBusClient(serviceBusConnectionString);
|
|
355
|
-
}
|
|
356
|
-
await Promise.all(
|
|
357
|
-
queues.map(async (queue) => {
|
|
358
|
-
try {
|
|
359
|
-
let sender;
|
|
360
|
-
if (senders[queue]) {
|
|
361
|
-
sender = senders[queue];
|
|
362
|
-
} else {
|
|
363
|
-
senders[queue] = serviceBusClient.createSender(queue);
|
|
364
|
-
sender = senders[queue];
|
|
365
|
-
}
|
|
366
|
-
await sender.sendMessages({ body: message });
|
|
367
|
-
} catch (e) {
|
|
368
|
-
console.log(`Sending to queue ${queue} failed`);
|
|
369
|
-
}
|
|
370
|
-
})
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
async function logToCosmosDb(message) {
|
|
374
|
-
if (!cosmosClient) {
|
|
375
|
-
if (!cosmosConnectionString) {
|
|
376
|
-
throw new Error("Cosmos DB connection string is missing.");
|
|
377
|
-
}
|
|
378
|
-
cosmosClient = new import_cosmos.CosmosClient(cosmosConnectionString);
|
|
379
|
-
}
|
|
380
|
-
if (!cosmosDatabaseName) {
|
|
381
|
-
throw new Error("Cosmos DB database name is missing.");
|
|
382
|
-
}
|
|
383
|
-
if (!cosmosContainerName) {
|
|
384
|
-
throw new Error("Cosmos DB events container name is missing.");
|
|
385
|
-
}
|
|
386
|
-
try {
|
|
387
|
-
await cosmosClient.database(cosmosDatabaseName).container(cosmosContainerName).items.create(message);
|
|
388
|
-
} catch (e) {
|
|
389
|
-
logException(e);
|
|
263
|
+
errorMessage = error;
|
|
390
264
|
}
|
|
265
|
+
return errorMessage;
|
|
391
266
|
}
|
|
392
267
|
|
|
393
268
|
// src/defaultPublisher.ts
|
|
@@ -408,21 +283,15 @@ async function defaultPublisher(azureFunctionContext, serviceBusMessage, source,
|
|
|
408
283
|
for (const result of results) {
|
|
409
284
|
if (result.status === "rejected") {
|
|
410
285
|
if (result.reason instanceof Error) {
|
|
411
|
-
const
|
|
412
|
-
logException(
|
|
413
|
-
|
|
414
|
-
new Error(formattedError.message)
|
|
415
|
-
);
|
|
416
|
-
throw formattedError;
|
|
286
|
+
const errorMessage = parseErrorMessage(result.reason);
|
|
287
|
+
logException(azureFunctionContext.log, new Error(errorMessage));
|
|
288
|
+
throw new Error(errorMessage);
|
|
417
289
|
} else {
|
|
418
|
-
const
|
|
290
|
+
const errorMessage = parseErrorMessage(
|
|
419
291
|
typeof result.reason === "object" ? JSON.stringify(result.reason) : result.reason
|
|
420
292
|
);
|
|
421
|
-
logException(
|
|
422
|
-
|
|
423
|
-
new Error(formattedError.message)
|
|
424
|
-
);
|
|
425
|
-
throw formattedError;
|
|
293
|
+
logException(azureFunctionContext.log, new Error(errorMessage));
|
|
294
|
+
throw new Error(errorMessage);
|
|
426
295
|
}
|
|
427
296
|
}
|
|
428
297
|
}
|
|
@@ -643,7 +512,7 @@ var googleAds = {
|
|
|
643
512
|
},
|
|
644
513
|
body: JSON.stringify(body)
|
|
645
514
|
};
|
|
646
|
-
const baseUrl = `https://googleads.googleapis.com/
|
|
515
|
+
const baseUrl = `https://googleads.googleapis.com/v20/customers/${config.customerId}:uploadClickConversions`;
|
|
647
516
|
const response = await fetch(baseUrl, requestOptions);
|
|
648
517
|
const responseJson = await response.json();
|
|
649
518
|
const knownErrors = [
|
|
@@ -663,13 +532,7 @@ var googleAds = {
|
|
|
663
532
|
} else if (responseJson.error) {
|
|
664
533
|
throw new Error(responseJson.error.message);
|
|
665
534
|
} else {
|
|
666
|
-
logEvent(
|
|
667
|
-
this.source,
|
|
668
|
-
event.event,
|
|
669
|
-
event.region,
|
|
670
|
-
response.status,
|
|
671
|
-
requestOptions.body
|
|
672
|
-
);
|
|
535
|
+
logEvent(this.source, event.event, event.region);
|
|
673
536
|
}
|
|
674
537
|
},
|
|
675
538
|
async getToken(config) {
|
|
@@ -891,7 +754,6 @@ var publisher = {
|
|
|
891
754
|
// Annotate the CommonJS export names for ESM import in node:
|
|
892
755
|
0 && (module.exports = {
|
|
893
756
|
DefaultPublisher,
|
|
894
|
-
broker,
|
|
895
757
|
publisher,
|
|
896
758
|
testUtils,
|
|
897
759
|
utils
|
package/package.json
CHANGED
package/src/defaultPublisher.ts
CHANGED
|
@@ -40,17 +40,23 @@ export async function defaultPublisher(
|
|
|
40
40
|
for (const result of results) {
|
|
41
41
|
if (result.status === 'rejected') {
|
|
42
42
|
if (result.reason instanceof Error) {
|
|
43
|
-
const
|
|
44
|
-
utils.logException(
|
|
45
|
-
|
|
43
|
+
const formattedError = utils.formatError(result.reason);
|
|
44
|
+
utils.logException(
|
|
45
|
+
azureFunctionContext.log,
|
|
46
|
+
new Error(formattedError.message),
|
|
47
|
+
);
|
|
48
|
+
throw formattedError;
|
|
46
49
|
} else {
|
|
47
|
-
const
|
|
50
|
+
const formattedError = utils.formatError(
|
|
48
51
|
typeof result.reason === 'object'
|
|
49
52
|
? JSON.stringify(result.reason)
|
|
50
53
|
: (result.reason as Error['message']),
|
|
51
54
|
);
|
|
52
|
-
utils.logException(
|
|
53
|
-
|
|
55
|
+
utils.logException(
|
|
56
|
+
azureFunctionContext.log,
|
|
57
|
+
new Error(formattedError.message),
|
|
58
|
+
);
|
|
59
|
+
throw formattedError;
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
}
|
|
@@ -1,24 +1,65 @@
|
|
|
1
|
+
import { Logger } from '@azure/functions';
|
|
1
2
|
import * as utils from './../utils/utils';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { EventToLog } from '../types/EventToLog';
|
|
4
|
+
import { TypeSafeObject } from '../types/UtilTypes';
|
|
5
|
+
|
|
6
|
+
interface GoogleAdsPlatformPublisher {
|
|
7
|
+
source: string;
|
|
8
|
+
publish: (
|
|
9
|
+
log: Logger,
|
|
10
|
+
config: GoogleAdsConfig,
|
|
11
|
+
data: TypeSafeObject,
|
|
12
|
+
event: EventToLog,
|
|
13
|
+
) => Promise<void>;
|
|
14
|
+
getToken: (config: GoogleAdsConfig) => Promise<TokenCache>;
|
|
15
|
+
}
|
|
10
16
|
|
|
11
17
|
interface GoogleAdsData {
|
|
12
18
|
gclid?: string;
|
|
13
19
|
wbraid?: string;
|
|
14
20
|
gbraid?: string;
|
|
15
21
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
16
|
-
user_identifiers
|
|
22
|
+
user_identifiers?: Record<string, unknown>[];
|
|
17
23
|
conversionAction?: string;
|
|
18
24
|
[x: string]: unknown;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
interface GoogleAdsConfig extends TypeSafeObject {
|
|
28
|
+
client: {
|
|
29
|
+
id: string;
|
|
30
|
+
secret: string;
|
|
31
|
+
developerToken: string;
|
|
32
|
+
};
|
|
33
|
+
refreshToken: string;
|
|
34
|
+
customerId: string;
|
|
35
|
+
conversionId: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GoogleAdsResponse {
|
|
39
|
+
partialFailureError?: {
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
error?: {
|
|
43
|
+
message: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface TokenCache {
|
|
48
|
+
token: string;
|
|
49
|
+
createdAt: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface TokenResponse {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
54
|
+
access_token: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type TokenPromise = Promise<TokenCache> | null;
|
|
58
|
+
|
|
59
|
+
let tokenCache: TokenCache | null;
|
|
60
|
+
let tokenPromise: TokenPromise;
|
|
61
|
+
|
|
62
|
+
export const googleAds: GoogleAdsPlatformPublisher = {
|
|
22
63
|
source: 'GoogleAds',
|
|
23
64
|
/**
|
|
24
65
|
*Publishes data to Google Ads
|
|
@@ -38,7 +79,7 @@ export const googleAds: PlatformPublisher = {
|
|
|
38
79
|
return;
|
|
39
80
|
}
|
|
40
81
|
|
|
41
|
-
const tokenData = await
|
|
82
|
+
const tokenData = await this.getToken(config);
|
|
42
83
|
|
|
43
84
|
data.conversionAction = `customers/${config.customerId}/conversionActions/${config.conversionId}`;
|
|
44
85
|
|
|
@@ -54,43 +95,87 @@ export const googleAds: PlatformPublisher = {
|
|
|
54
95
|
headers: {
|
|
55
96
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
56
97
|
Authorization: `Bearer ${tokenData.token}`,
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
57
99
|
'developer-token': config.client.developerToken,
|
|
58
100
|
},
|
|
59
101
|
body: JSON.stringify(body),
|
|
60
102
|
};
|
|
61
103
|
|
|
62
|
-
const baseUrl = `https://googleads.googleapis.com/
|
|
104
|
+
const baseUrl = `https://googleads.googleapis.com/v20/customers/${config.customerId}:uploadClickConversions`;
|
|
63
105
|
|
|
64
106
|
const response = await fetch(baseUrl, requestOptions);
|
|
65
107
|
const responseJson: GoogleAdsResponse = await response.json();
|
|
66
108
|
|
|
67
109
|
const knownErrors = [
|
|
68
|
-
'The imported event has a conversion_date_time that precedes the click. Make sure your conversion_date_time is correct and try again
|
|
69
|
-
"The click ID or call is associated with an Ads account you don't have access to. Make sure you import conversions for accounts managed by your manager account
|
|
70
|
-
"The imported event can't be recorded because its click occurred before this conversion's click-through window. Make sure you import the most recent data
|
|
71
|
-
'The imported event includes an order ID that was previously recorded, so the event was not processed
|
|
72
|
-
'The imported event could not be attributed to a click. This may be because the event did not come from a Google Ads campaign
|
|
73
|
-
"The imported event can't be recorded because its click occurred before this conversion's click-through window. Make sure you import the most recent data
|
|
110
|
+
'The imported event has a conversion_date_time that precedes the click. Make sure your conversion_date_time is correct and try again.',
|
|
111
|
+
"The click ID or call is associated with an Ads account you don't have access to. Make sure you import conversions for accounts managed by your manager account.",
|
|
112
|
+
"The imported event can't be recorded because its click occurred before this conversion's click-through window. Make sure you import the most recent data.",
|
|
113
|
+
'The imported event includes an order ID that was previously recorded, so the event was not processed.',
|
|
114
|
+
'The imported event could not be attributed to a click. This may be because the event did not come from a Google Ads campaign.',
|
|
115
|
+
"The imported event can't be recorded because its click occurred before this conversion's click-through window. Make sure you import the most recent data.",
|
|
74
116
|
];
|
|
75
117
|
|
|
76
118
|
if (responseJson.partialFailureError) {
|
|
77
119
|
utils.logMessage(1, responseJson.partialFailureError.message);
|
|
78
|
-
|
|
120
|
+
const trimmedMessage =
|
|
121
|
+
responseJson.partialFailureError.message.split('., at')[0] ?? '';
|
|
122
|
+
if (!knownErrors.includes(trimmedMessage)) {
|
|
79
123
|
throw new Error(responseJson.partialFailureError.message);
|
|
80
124
|
}
|
|
81
125
|
} else if (responseJson.error) {
|
|
82
126
|
throw new Error(responseJson.error.message);
|
|
83
127
|
} else {
|
|
84
|
-
utils.logEvent(
|
|
128
|
+
utils.logEvent(
|
|
129
|
+
this.source,
|
|
130
|
+
event.event,
|
|
131
|
+
event.region,
|
|
132
|
+
response.status,
|
|
133
|
+
requestOptions.body,
|
|
134
|
+
);
|
|
85
135
|
}
|
|
86
136
|
},
|
|
87
|
-
};
|
|
88
137
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
138
|
+
async getToken(config: GoogleAdsConfig) {
|
|
139
|
+
if (tokenCache && tokenCache.createdAt + 3500 * 1000 > Date.now()) {
|
|
140
|
+
return tokenCache;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!tokenPromise) {
|
|
144
|
+
tokenPromise = (async () => {
|
|
145
|
+
const tokenResponse = await fetch(
|
|
146
|
+
`https://oauth2.googleapis.com/token`,
|
|
147
|
+
{
|
|
148
|
+
method: 'POST',
|
|
149
|
+
headers: {
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
151
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
152
|
+
},
|
|
153
|
+
body: new URLSearchParams({
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
155
|
+
client_id: config.client.id,
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
157
|
+
client_secret: config.client.secret,
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
159
|
+
refreshToken: config.refreshToken,
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
161
|
+
grant_type: 'refresh_token',
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (!tokenResponse.ok) {
|
|
167
|
+
throw new Error('Access token fetch failed');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const parsedTokenResponse: TokenResponse = await tokenResponse.json();
|
|
171
|
+
const createdAt = Date.now();
|
|
172
|
+
|
|
173
|
+
tokenCache = { token: parsedTokenResponse.access_token, createdAt };
|
|
174
|
+
tokenPromise = null;
|
|
175
|
+
return tokenCache;
|
|
176
|
+
})();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return tokenPromise;
|
|
180
|
+
},
|
|
181
|
+
};
|
package/src/utils/utils.ts
CHANGED
|
@@ -12,10 +12,7 @@ import { TypeSafeObject } from '../types/UtilTypes';
|
|
|
12
12
|
|
|
13
13
|
let aiClient: appInsights.TelemetryClient | null = null;
|
|
14
14
|
|
|
15
|
-
if (
|
|
16
|
-
process.env.APPLICATIONINSIGHTS_CONNECTION_STRING ||
|
|
17
|
-
process.env.APPINSIGHTS_INSTRUMENTATIONKEY
|
|
18
|
-
) {
|
|
15
|
+
if (process.env.APPLICATIONINSIGHTS_CONNECTION_STRING) {
|
|
19
16
|
appInsights
|
|
20
17
|
.setup() // Set environment variable APPLICATIONINSIGHTS_CONNECTION_STRING, so setup() can be called w/o parameters
|
|
21
18
|
.setAutoCollectDependencies(false)
|
|
@@ -76,11 +73,15 @@ export function logEvent(
|
|
|
76
73
|
publisher: string,
|
|
77
74
|
event: string,
|
|
78
75
|
region: string,
|
|
76
|
+
status: number,
|
|
77
|
+
payload?: string,
|
|
79
78
|
): void {
|
|
80
79
|
const properties: Telemetry['properties'] = {
|
|
81
|
-
publisher
|
|
82
|
-
event
|
|
83
|
-
region
|
|
80
|
+
publisher,
|
|
81
|
+
event,
|
|
82
|
+
region,
|
|
83
|
+
status,
|
|
84
|
+
payload,
|
|
84
85
|
};
|
|
85
86
|
logMessage(1, 'Event dispatched to platform', properties);
|
|
86
87
|
}
|
|
@@ -322,7 +323,10 @@ export async function handlePlatformRequest({
|
|
|
322
323
|
headers: { ...requestOptions.headers },
|
|
323
324
|
};
|
|
324
325
|
|
|
325
|
-
const rawPlatformResponse = await fetch(
|
|
326
|
+
const rawPlatformResponse: Response = await fetch(
|
|
327
|
+
fullUrl,
|
|
328
|
+
fetchRequestOptions,
|
|
329
|
+
);
|
|
326
330
|
|
|
327
331
|
if (process.env.PUBLISHER_TESTURL) {
|
|
328
332
|
await handleTestRequest({
|
|
@@ -339,11 +343,23 @@ export async function handlePlatformRequest({
|
|
|
339
343
|
event.event,
|
|
340
344
|
);
|
|
341
345
|
|
|
342
|
-
logEvent(
|
|
346
|
+
logEvent(
|
|
347
|
+
source,
|
|
348
|
+
event.event,
|
|
349
|
+
event.region,
|
|
350
|
+
rawPlatformResponse.status,
|
|
351
|
+
requestOptions.body ? JSON.stringify(requestOptions.body) : undefined,
|
|
352
|
+
);
|
|
343
353
|
|
|
344
354
|
return processedPlatformResponse;
|
|
345
355
|
} else {
|
|
346
|
-
logEvent(
|
|
356
|
+
logEvent(
|
|
357
|
+
source,
|
|
358
|
+
event.event,
|
|
359
|
+
event.region,
|
|
360
|
+
rawPlatformResponse.status,
|
|
361
|
+
requestOptions.body ? JSON.stringify(requestOptions.body) : undefined,
|
|
362
|
+
);
|
|
347
363
|
}
|
|
348
364
|
}
|
|
349
365
|
|
|
@@ -377,16 +393,19 @@ async function handleTestRequest(params: HandleTestRequestParams) {
|
|
|
377
393
|
}
|
|
378
394
|
}
|
|
379
395
|
|
|
380
|
-
export function
|
|
381
|
-
let
|
|
396
|
+
export function formatError(error: Error | Error['message']) {
|
|
397
|
+
let formattedError: Error;
|
|
382
398
|
if (error instanceof Error) {
|
|
399
|
+
formattedError = error;
|
|
383
400
|
if (error.cause) {
|
|
384
|
-
|
|
401
|
+
formattedError.message = `${
|
|
402
|
+
error.message
|
|
403
|
+
} Cause: ${error.cause.toString()}`;
|
|
385
404
|
} else {
|
|
386
|
-
|
|
405
|
+
formattedError.message = error.message;
|
|
387
406
|
}
|
|
388
407
|
} else {
|
|
389
|
-
|
|
408
|
+
formattedError = new Error(error);
|
|
390
409
|
}
|
|
391
|
-
return
|
|
410
|
+
return formattedError;
|
|
392
411
|
}
|
package/tests/AffiliatePort.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.AffiliatePort;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
|
-
var assert = require('assert');
|
|
5
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
6
7
|
|
|
7
8
|
describe('test AffiliatePort publisher', function () {
|
package/tests/CJ.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.CJ;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
|
-
var assert = require('assert');
|
|
5
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
6
7
|
|
|
7
8
|
describe('test CJ publisher', function () {
|
package/tests/Facebook.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.Facebook;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
|
-
var assert = require('assert');
|
|
5
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
6
7
|
|
|
7
8
|
describe('test Facebook publisher', function () {
|
package/tests/GoogleAds.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.GoogleAds;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
5
|
-
const localSettings = require('../local.settings.json');
|
|
6
|
-
process.env = { ...process.env, ...localSettings };
|
|
7
7
|
|
|
8
8
|
describe('test GoogleAds publisher', function () {
|
|
9
9
|
it('purchase', async function () {
|
|
@@ -84,11 +84,11 @@ function trigger(data) {
|
|
|
84
84
|
},
|
|
85
85
|
refreshToken:
|
|
86
86
|
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
87
|
-
process.env
|
|
87
|
+
process.env.Publisher_GoogleAds_RefreshToken,
|
|
88
88
|
customerId:
|
|
89
89
|
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
90
|
-
process.env
|
|
91
|
-
conversionId: process.env.
|
|
90
|
+
process.env.Publisher_GoogleAds_CustomerID,
|
|
91
|
+
conversionId: process.env.Publisher_GoogleAds_ConversionID_purchase,
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
switch (data.event?.code) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.GoogleAnalytics4;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
|
-
var assert = require('assert');
|
|
5
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
6
7
|
|
|
7
8
|
describe('test GoogleAnalytics4 publisher', function () {
|
package/tests/Piano.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.Piano;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
5
|
-
var assert = require('assert');
|
|
6
7
|
|
|
7
8
|
describe('test Piano publisher', function () {
|
|
8
9
|
it('purchase', async function () {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const localSettings = require('../local.settings.json').Values;
|
|
2
|
+
process.env = { ...process.env, ...localSettings };
|
|
1
3
|
const publisher = require('../dist/index.js').publisher.UniversalAnalytics;
|
|
2
4
|
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
5
|
const utils = require('../dist/index.js').utils;
|
|
4
|
-
var assert = require('assert');
|
|
5
6
|
const testUtils = require('../dist/index.js').testUtils;
|
|
6
7
|
|
|
7
8
|
describe('test UniversalAnalytics publisher', function () {
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
let tokenCache: TokenCache | null = null;
|
|
2
|
-
let tokenPromise: Promise<TokenCache> | null = null;
|
|
3
|
-
|
|
4
|
-
export async function fetchGoogleAdsToken(
|
|
5
|
-
config: GoogleAdsConfig,
|
|
6
|
-
): Promise<TokenCache> {
|
|
7
|
-
if (tokenCache && tokenCache.createdAt + 3500 * 1000 > Date.now()) {
|
|
8
|
-
return tokenCache;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (!tokenPromise) {
|
|
12
|
-
tokenPromise = (async () => {
|
|
13
|
-
const tokenResponse = await fetch(`https://oauth2.googleapis.com/token`, {
|
|
14
|
-
method: 'POST',
|
|
15
|
-
headers: {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
17
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
18
|
-
},
|
|
19
|
-
body: new URLSearchParams({
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
21
|
-
client_id: config.client.id,
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
-
client_secret: config.client.secret,
|
|
24
|
-
refreshToken: config.refreshToken,
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
26
|
-
grant_type: 'refresh_token',
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// if (!tokenResponse.ok) {
|
|
31
|
-
// throw new Error('Access token fetch failed')
|
|
32
|
-
// }
|
|
33
|
-
|
|
34
|
-
const parsedTokenResponse: TokenResponse = await tokenResponse.json();
|
|
35
|
-
const createdAt = Date.now();
|
|
36
|
-
|
|
37
|
-
tokenCache = { token: parsedTokenResponse.access_token, createdAt };
|
|
38
|
-
tokenPromise = null;
|
|
39
|
-
return tokenCache;
|
|
40
|
-
})();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return tokenPromise;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface TokenCache {
|
|
47
|
-
token: string;
|
|
48
|
-
createdAt: number;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface TokenResponse {
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
53
|
-
access_token: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface GoogleAdsConfig {
|
|
57
|
-
client: {
|
|
58
|
-
id: string;
|
|
59
|
-
secret: string;
|
|
60
|
-
developerToken: string;
|
|
61
|
-
};
|
|
62
|
-
refreshToken: string;
|
|
63
|
-
customerId: string;
|
|
64
|
-
conversionId: string;
|
|
65
|
-
[x: string]: unknown;
|
|
66
|
-
}
|