@mhubcloud/tms_core 3.6.0 → 3.7.0-beta.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 +236 -25
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -43,7 +43,8 @@ var broker_exports = {};
|
|
|
43
43
|
__export(broker_exports, {
|
|
44
44
|
dispatchEvent: () => dispatchEvent,
|
|
45
45
|
logToCosmosDb: () => logToCosmosDb,
|
|
46
|
-
resolveEvent: () => resolveEvent
|
|
46
|
+
resolveEvent: () => resolveEvent,
|
|
47
|
+
scheduleEvent: () => scheduleEvent
|
|
47
48
|
});
|
|
48
49
|
var import_cosmos = require("@azure/cosmos");
|
|
49
50
|
var import_service_bus = require("@azure/service-bus");
|
|
@@ -105,7 +106,19 @@ function logMessage(level, message, properties) {
|
|
|
105
106
|
function logEvent(publisher2, event, region, status, payload, id) {
|
|
106
107
|
let message = "";
|
|
107
108
|
if (status || status === 0) {
|
|
108
|
-
|
|
109
|
+
if (status >= 200 && status < 300) {
|
|
110
|
+
aiClient?.trackMetric({
|
|
111
|
+
name: `Dispatched/${publisher2}-${event}-${region}`,
|
|
112
|
+
value: 1
|
|
113
|
+
});
|
|
114
|
+
message = "Event dispatched to platform";
|
|
115
|
+
} else {
|
|
116
|
+
aiClient?.trackMetric({
|
|
117
|
+
name: `Failed/${publisher2}-${event}-${region}`,
|
|
118
|
+
value: 1
|
|
119
|
+
});
|
|
120
|
+
message = "Event dispatch failed";
|
|
121
|
+
}
|
|
109
122
|
} else {
|
|
110
123
|
message = "Event dispatched to platform";
|
|
111
124
|
}
|
|
@@ -275,7 +288,7 @@ async function handlePlatformRequest({
|
|
|
275
288
|
event.event,
|
|
276
289
|
event.region,
|
|
277
290
|
0,
|
|
278
|
-
requestOptions.
|
|
291
|
+
requestOptions.method === "POST" ? JSON.stringify(requestOptions.body) : query,
|
|
279
292
|
event.id
|
|
280
293
|
);
|
|
281
294
|
throw e;
|
|
@@ -294,7 +307,7 @@ async function handlePlatformRequest({
|
|
|
294
307
|
event.event,
|
|
295
308
|
event.region,
|
|
296
309
|
rawPlatformResponse.status,
|
|
297
|
-
requestOptions.
|
|
310
|
+
requestOptions.method === "POST" ? JSON.stringify(requestOptions.body) : query,
|
|
298
311
|
event.id
|
|
299
312
|
);
|
|
300
313
|
if (!rawPlatformResponse.ok) {
|
|
@@ -377,7 +390,15 @@ function resolveEvent(event, publisherConfig) {
|
|
|
377
390
|
}
|
|
378
391
|
match = matchEvent(event, publisherEvent);
|
|
379
392
|
if (match) {
|
|
380
|
-
|
|
393
|
+
if (typeof publisherEvent === "object" && publisherEvent.schedule) {
|
|
394
|
+
publishers.push({
|
|
395
|
+
queueName: publisher2,
|
|
396
|
+
schedule: publisherEvent.schedule
|
|
397
|
+
});
|
|
398
|
+
} else {
|
|
399
|
+
publishers.push(publisher2);
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
381
402
|
}
|
|
382
403
|
}
|
|
383
404
|
}
|
|
@@ -390,16 +411,17 @@ function resolveEvent(event, publisherConfig) {
|
|
|
390
411
|
}
|
|
391
412
|
function matchEvent(event, publisherEvent) {
|
|
392
413
|
if (typeof publisherEvent === "string") {
|
|
393
|
-
const regex = new RegExp(publisherEvent);
|
|
414
|
+
const regex = new RegExp(`^(?:${publisherEvent})$`);
|
|
394
415
|
const match = regex.test(event.code);
|
|
395
416
|
return match;
|
|
396
417
|
} else if (typeof publisherEvent === "object") {
|
|
418
|
+
const eventParams = publisherEvent.event;
|
|
397
419
|
let paramMatch = true;
|
|
398
|
-
for (const key of Object.keys(
|
|
399
|
-
const publisherEventParam =
|
|
420
|
+
for (const key of Object.keys(eventParams)) {
|
|
421
|
+
const publisherEventParam = eventParams[key];
|
|
400
422
|
const eventParam = event[key];
|
|
401
423
|
if (typeof publisherEventParam === "string" && typeof eventParam === "string") {
|
|
402
|
-
const regex = new RegExp(publisherEventParam);
|
|
424
|
+
const regex = new RegExp(`^(?:${publisherEventParam})$`);
|
|
403
425
|
paramMatch = regex.test(eventParam);
|
|
404
426
|
if (!paramMatch) {
|
|
405
427
|
return false;
|
|
@@ -424,17 +446,37 @@ async function dispatchEvent(message, queues) {
|
|
|
424
446
|
}
|
|
425
447
|
await Promise.all(
|
|
426
448
|
queues.map(async (queue) => {
|
|
449
|
+
let queueName;
|
|
427
450
|
try {
|
|
428
451
|
let sender;
|
|
429
|
-
|
|
430
|
-
|
|
452
|
+
let serviceBusMessage;
|
|
453
|
+
if (typeof queue === "string") {
|
|
454
|
+
queueName = queue;
|
|
455
|
+
serviceBusMessage = { body: message };
|
|
456
|
+
} else if (typeof queue === "object") {
|
|
457
|
+
queueName = queue.queueName;
|
|
458
|
+
serviceBusMessage = {
|
|
459
|
+
body: message,
|
|
460
|
+
scheduledEnqueueTimeUtc: parseScheduleOptions(queue.schedule)
|
|
461
|
+
};
|
|
431
462
|
} else {
|
|
432
|
-
|
|
433
|
-
|
|
463
|
+
console.log(`Invalid queue name format`);
|
|
464
|
+
}
|
|
465
|
+
if (queueName && serviceBusMessage) {
|
|
466
|
+
if (senders[queueName]) {
|
|
467
|
+
sender = senders[queueName];
|
|
468
|
+
} else {
|
|
469
|
+
senders[queueName] = serviceBusClient.createSender(queueName);
|
|
470
|
+
sender = senders[queueName];
|
|
471
|
+
}
|
|
472
|
+
if (sender) {
|
|
473
|
+
await sender.sendMessages(serviceBusMessage);
|
|
474
|
+
}
|
|
434
475
|
}
|
|
435
|
-
await sender.sendMessages({ body: message });
|
|
436
476
|
} catch (e) {
|
|
437
|
-
console.log(
|
|
477
|
+
console.log(
|
|
478
|
+
`Sending to queue ${queueName ?? "unknown"} failed. Cause: ${e instanceof Error ? e.message : ""}`
|
|
479
|
+
);
|
|
438
480
|
}
|
|
439
481
|
})
|
|
440
482
|
);
|
|
@@ -458,6 +500,71 @@ async function logToCosmosDb(message) {
|
|
|
458
500
|
logException(e);
|
|
459
501
|
}
|
|
460
502
|
}
|
|
503
|
+
function parseScheduleOptions(options) {
|
|
504
|
+
let dispatchTime;
|
|
505
|
+
const now = /* @__PURE__ */ new Date();
|
|
506
|
+
if ("timestamp" in options) {
|
|
507
|
+
dispatchTime = new Date(options.timestamp);
|
|
508
|
+
} else if ("scheduleHour" in options) {
|
|
509
|
+
dispatchTime = new Date(
|
|
510
|
+
now.getFullYear(),
|
|
511
|
+
now.getMonth(),
|
|
512
|
+
now.getDate() + (options.dayOffset ?? 0),
|
|
513
|
+
options.scheduleHour,
|
|
514
|
+
options.scheduleMinute ?? 0,
|
|
515
|
+
0,
|
|
516
|
+
0
|
|
517
|
+
);
|
|
518
|
+
} else if ("scheduleInterval" in options && "startHour" in options) {
|
|
519
|
+
const hour = now.getHours();
|
|
520
|
+
const nextHour = Math.ceil(hour / options.scheduleInterval) * options.scheduleInterval;
|
|
521
|
+
dispatchTime = new Date(
|
|
522
|
+
now.getFullYear(),
|
|
523
|
+
now.getMonth(),
|
|
524
|
+
now.getDate() + (options.dayOffset ?? 0),
|
|
525
|
+
nextHour,
|
|
526
|
+
options.startMinute ?? 0,
|
|
527
|
+
0,
|
|
528
|
+
0
|
|
529
|
+
);
|
|
530
|
+
if (nextHour >= 24) {
|
|
531
|
+
dispatchTime.setDate(dispatchTime.getDate() + 1);
|
|
532
|
+
dispatchTime.setHours(options.startHour, options.startMinute);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
throw new Error("Invalid scheduling configuration");
|
|
536
|
+
}
|
|
537
|
+
return dispatchTime;
|
|
538
|
+
}
|
|
539
|
+
async function scheduleEvent(message, queue, options) {
|
|
540
|
+
if (!serviceBusClient) {
|
|
541
|
+
if (!serviceBusConnectionString) {
|
|
542
|
+
throw new Error("Service Bus connection string is missing.");
|
|
543
|
+
}
|
|
544
|
+
serviceBusClient = new import_service_bus.ServiceBusClient(serviceBusConnectionString);
|
|
545
|
+
}
|
|
546
|
+
try {
|
|
547
|
+
let sender;
|
|
548
|
+
if (senders[queue]) {
|
|
549
|
+
sender = senders[queue];
|
|
550
|
+
} else {
|
|
551
|
+
senders[queue] = serviceBusClient.createSender(queue);
|
|
552
|
+
sender = senders[queue];
|
|
553
|
+
}
|
|
554
|
+
const dispatchTime = parseScheduleOptions(options);
|
|
555
|
+
const serviceBusMessage = {
|
|
556
|
+
body: message,
|
|
557
|
+
scheduledEnqueueTimeUtc: dispatchTime
|
|
558
|
+
};
|
|
559
|
+
await sender.sendMessages(serviceBusMessage);
|
|
560
|
+
return dispatchTime;
|
|
561
|
+
} catch (e) {
|
|
562
|
+
console.log(
|
|
563
|
+
`Scheduling message to queue ${queue} failed. Cause: ${e instanceof Error ? e.message : ""}`
|
|
564
|
+
);
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
461
568
|
|
|
462
569
|
// src/defaultPublisher.ts
|
|
463
570
|
async function defaultPublisher(azureFunctionContext, serviceBusMessage, source, platformPublisher, trigger) {
|
|
@@ -839,6 +946,114 @@ var googleAnalytics4 = {
|
|
|
839
946
|
}
|
|
840
947
|
};
|
|
841
948
|
|
|
949
|
+
// src/publishers/microsoftAds.ts
|
|
950
|
+
var tokenCache2;
|
|
951
|
+
var tokenPromise2;
|
|
952
|
+
var microsoftAds = {
|
|
953
|
+
source: "MicrosoftAds",
|
|
954
|
+
/**
|
|
955
|
+
*Publishes data to Microsoft Ads
|
|
956
|
+
* @param log Azure Function context.log
|
|
957
|
+
* @param config Publisher configuration
|
|
958
|
+
* @param data Data in publisher-friendly format to be published
|
|
959
|
+
* @param event Event for which the function is triggered
|
|
960
|
+
*/
|
|
961
|
+
async publish(log2, config, data, event) {
|
|
962
|
+
if (typeof config.customerAccountId === "string" && typeof config.customerId === "string" && typeof config.developerToken === "string") {
|
|
963
|
+
if (!(data.ConversionName || data.ConversionTime || data.MicrosoftClickId)) {
|
|
964
|
+
logMessage(
|
|
965
|
+
1,
|
|
966
|
+
`MicrosoftAds: excluded ${event.event} as one or more mandatory parameters were missing`
|
|
967
|
+
);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
const { token } = await this.getToken(config);
|
|
971
|
+
const body = {
|
|
972
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
973
|
+
OfflineConversions: [data]
|
|
974
|
+
};
|
|
975
|
+
const requestOptions = {
|
|
976
|
+
method: "POST",
|
|
977
|
+
body: JSON.stringify(body),
|
|
978
|
+
headers: {
|
|
979
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
980
|
+
Authorization: `Bearer ${token}`,
|
|
981
|
+
CustomerAccountId: config.customerAccountId,
|
|
982
|
+
CustomerId: config.customerId,
|
|
983
|
+
DeveloperToken: config.developerToken,
|
|
984
|
+
"Content-Type": "application/json"
|
|
985
|
+
/* eslint-enable */
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const baseUrl = "https://campaign.api.bingads.microsoft.com/CampaignManagement/v13/OfflineConversions/Apply";
|
|
989
|
+
const response = await fetch(baseUrl, requestOptions);
|
|
990
|
+
const responseJson = await response.json();
|
|
991
|
+
let status = 0;
|
|
992
|
+
let error;
|
|
993
|
+
if (responseJson.PartialErrors || responseJson.Errors) {
|
|
994
|
+
const apiErrors = responseJson.Errors ?? responseJson.PartialErrors ?? [];
|
|
995
|
+
status = 500;
|
|
996
|
+
const message = apiErrors.reduce((acc, curr) => {
|
|
997
|
+
acc += curr.Message + " ";
|
|
998
|
+
return acc;
|
|
999
|
+
}, "Microsoft Ads: Error(s) processing event: ");
|
|
1000
|
+
error = message;
|
|
1001
|
+
} else {
|
|
1002
|
+
status = 200;
|
|
1003
|
+
}
|
|
1004
|
+
logEvent(
|
|
1005
|
+
this.source,
|
|
1006
|
+
event.event,
|
|
1007
|
+
event.region,
|
|
1008
|
+
status,
|
|
1009
|
+
requestOptions.body,
|
|
1010
|
+
event.id
|
|
1011
|
+
);
|
|
1012
|
+
if (error) {
|
|
1013
|
+
throw new Error(error);
|
|
1014
|
+
}
|
|
1015
|
+
} else {
|
|
1016
|
+
throw new Error("Invalid config for platform " + this.source);
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
async getToken(config) {
|
|
1020
|
+
if (tokenCache2 && tokenCache2.createdAt + 3500 * 1e3 > Date.now()) {
|
|
1021
|
+
return tokenCache2;
|
|
1022
|
+
}
|
|
1023
|
+
if (!tokenPromise2) {
|
|
1024
|
+
tokenPromise2 = (async () => {
|
|
1025
|
+
const tokenResponse = await fetch(
|
|
1026
|
+
`https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/token`,
|
|
1027
|
+
{
|
|
1028
|
+
method: "POST",
|
|
1029
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1030
|
+
headers: {
|
|
1031
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1032
|
+
},
|
|
1033
|
+
body: new URLSearchParams({
|
|
1034
|
+
client_id: config.client.id,
|
|
1035
|
+
client_secret: config.client.secret,
|
|
1036
|
+
refresh_token: config.refreshToken,
|
|
1037
|
+
grant_type: "refresh_token",
|
|
1038
|
+
scope: "https://ads.microsoft.com/msads.manage"
|
|
1039
|
+
})
|
|
1040
|
+
/* eslint-enable */
|
|
1041
|
+
}
|
|
1042
|
+
);
|
|
1043
|
+
if (!tokenResponse.ok) {
|
|
1044
|
+
throw new Error("Access token fetch failed");
|
|
1045
|
+
}
|
|
1046
|
+
const parsedTokenResponse = await tokenResponse.json();
|
|
1047
|
+
const createdAt = Date.now();
|
|
1048
|
+
tokenCache2 = { token: parsedTokenResponse.access_token, createdAt };
|
|
1049
|
+
tokenPromise2 = null;
|
|
1050
|
+
return tokenCache2;
|
|
1051
|
+
})();
|
|
1052
|
+
}
|
|
1053
|
+
return tokenPromise2;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
842
1057
|
// src/publishers/piano.ts
|
|
843
1058
|
var piano = {
|
|
844
1059
|
source: "Piano",
|
|
@@ -951,20 +1166,16 @@ context.log.warn = function warn(str) {
|
|
|
951
1166
|
|
|
952
1167
|
// src/index.ts
|
|
953
1168
|
var publisher = {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1169
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1170
|
+
AffiliatePort: affiliatePort,
|
|
957
1171
|
CJ: cj,
|
|
958
|
-
|
|
1172
|
+
Facebook: facebook,
|
|
959
1173
|
GoogleAds: googleAds,
|
|
960
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
961
1174
|
GoogleAnalytics4: googleAnalytics4,
|
|
962
|
-
|
|
963
|
-
UniversalAnalytics: universalAnalytics,
|
|
964
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1175
|
+
MicrosoftAds: microsoftAds,
|
|
965
1176
|
Piano: piano,
|
|
966
|
-
|
|
967
|
-
|
|
1177
|
+
UniversalAnalytics: universalAnalytics
|
|
1178
|
+
/* eslint-enable */
|
|
968
1179
|
};
|
|
969
1180
|
// Annotate the CommonJS export names for ESM import in node:
|
|
970
1181
|
0 && (module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhubcloud/tms_core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0-beta.1",
|
|
4
4
|
"description": "Core function for mHub Cloud TMS publishers",
|
|
5
5
|
"homepage": "https://mhubcloud.com/",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"conversion API"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "mocha ./tests
|
|
19
|
+
"test": "mocha ./tests/broker.js --local-settings=./local.settings.json --timeout 100000",
|
|
20
20
|
"dev": "ts-node index.ts",
|
|
21
21
|
"build": "tsup ./src/index.ts --format cjs --target node22"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@azure/cosmos": "^4.
|
|
24
|
+
"@azure/cosmos": "^4.7.0",
|
|
25
25
|
"@azure/data-tables": "^13.2.2",
|
|
26
26
|
"@azure/service-bus": "^7.9.5",
|
|
27
27
|
"ajv": "^8.12.0",
|