@mhubcloud/tms_core 3.3.0-beta.2 → 3.3.0-beta.3
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 +96 -8
- package/package.json +2 -2
- package/publishers/TableStorage.js +2 -2
- package/src/defaultPublisher.ts +1 -2
- package/src/index.ts +3 -0
- package/src/publishers/googleAds.ts +96 -0
- package/src/utils/tokens/googleAdsToken.ts +66 -0
- package/src/utils/utils.ts +7 -10
- package/tests/AffiliatePort.js +1 -1
- package/tests/CJ.js +1 -1
- package/tests/Facebook.js +1 -1
- package/tests/GoogleAds.js +127 -0
- package/tests/Piano.js +1 -1
package/dist/index.js
CHANGED
|
@@ -57,10 +57,7 @@ var crypto = __toESM(require("crypto"));
|
|
|
57
57
|
var appInsights = __toESM(require("applicationinsights"));
|
|
58
58
|
var aiClient = null;
|
|
59
59
|
if (process.env.APPLICATIONINSIGHTS_CONNECTION_STRING || process.env.APPINSIGHTS_INSTRUMENTATIONKEY) {
|
|
60
|
-
|
|
61
|
-
appInsights.setup().setAutoCollectDependencies(false).setAutoCollectConsole(true, false).setSendLiveMetrics(false).setAutoCollectRequests(false).setAutoCollectPerformance(false).setUseDiskRetryCaching(false);
|
|
62
|
-
appInsights.defaultClient.config.samplingPercentage = parseFloat(samplingLevel);
|
|
63
|
-
appInsights.start();
|
|
60
|
+
appInsights.setup().setAutoCollectDependencies(false).setAutoCollectConsole(true, false).setSendLiveMetrics(false).setAutoCollectRequests(false).setAutoCollectPerformance(false).setUseDiskRetryCaching(false).start();
|
|
64
61
|
aiClient = appInsights.defaultClient;
|
|
65
62
|
}
|
|
66
63
|
var EventPublisherBase = class {
|
|
@@ -96,8 +93,7 @@ function logEvent(publisher2, event, region) {
|
|
|
96
93
|
const properties = {
|
|
97
94
|
publisher: publisher2,
|
|
98
95
|
event,
|
|
99
|
-
region
|
|
100
|
-
sampling: process.env.SamplingLevel
|
|
96
|
+
region
|
|
101
97
|
};
|
|
102
98
|
logMessage(1, "Event dispatched to platform", properties);
|
|
103
99
|
}
|
|
@@ -303,8 +299,7 @@ async function defaultPublisher(azureFunctionContext, serviceBusMessage, source,
|
|
|
303
299
|
async function processMessage(message, log2, platformPublisher, trigger) {
|
|
304
300
|
const logProperties = {
|
|
305
301
|
source: platformPublisher.source,
|
|
306
|
-
data: JSON.stringify(message)
|
|
307
|
-
sampling: process.env.Sampling
|
|
302
|
+
data: JSON.stringify(message)
|
|
308
303
|
};
|
|
309
304
|
const publishers = trigger(message, log2);
|
|
310
305
|
if (!publishers.length) {
|
|
@@ -482,6 +477,97 @@ var facebook = {
|
|
|
482
477
|
}
|
|
483
478
|
};
|
|
484
479
|
|
|
480
|
+
// src/utils/tokens/googleAdsToken.ts
|
|
481
|
+
var tokenCache = null;
|
|
482
|
+
var tokenPromise = null;
|
|
483
|
+
async function fetchGoogleAdsToken(config) {
|
|
484
|
+
if (tokenCache && tokenCache.createdAt + 3500 * 1e3 > Date.now()) {
|
|
485
|
+
return tokenCache;
|
|
486
|
+
}
|
|
487
|
+
if (!tokenPromise) {
|
|
488
|
+
tokenPromise = (async () => {
|
|
489
|
+
const tokenResponse = await fetch(`https://oauth2.googleapis.com/token`, {
|
|
490
|
+
method: "POST",
|
|
491
|
+
headers: {
|
|
492
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
493
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
494
|
+
},
|
|
495
|
+
body: new URLSearchParams({
|
|
496
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
497
|
+
client_id: config.client.id,
|
|
498
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
499
|
+
client_secret: config.client.secret,
|
|
500
|
+
refreshToken: config.refreshToken,
|
|
501
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
502
|
+
grant_type: "refresh_token"
|
|
503
|
+
})
|
|
504
|
+
});
|
|
505
|
+
const parsedTokenResponse = await tokenResponse.json();
|
|
506
|
+
const createdAt = Date.now();
|
|
507
|
+
tokenCache = { token: parsedTokenResponse.access_token, createdAt };
|
|
508
|
+
tokenPromise = null;
|
|
509
|
+
return tokenCache;
|
|
510
|
+
})();
|
|
511
|
+
}
|
|
512
|
+
return tokenPromise;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/publishers/googleAds.ts
|
|
516
|
+
var googleAds = {
|
|
517
|
+
source: "GoogleAds",
|
|
518
|
+
/**
|
|
519
|
+
*Publishes data to Google Ads
|
|
520
|
+
* @param log Azure Function context.log
|
|
521
|
+
* @param config Publisher configuration
|
|
522
|
+
* @param data Data in publisher-friendly format to be published
|
|
523
|
+
* @param event Event for which the function is triggered
|
|
524
|
+
*/
|
|
525
|
+
async publish(log2, config, data, event) {
|
|
526
|
+
if (!data.gclid && !data.wbraid && !data.gbraid && !data.user_identifiers) {
|
|
527
|
+
logMessage(1, `Excluded ${event.event} due to lack of identifiers`);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const tokenData = await fetchGoogleAdsToken(config);
|
|
531
|
+
data.conversionAction = `customers/${config.customerId}/conversionActions/${config.conversionId}`;
|
|
532
|
+
const body = {
|
|
533
|
+
conversions: [data],
|
|
534
|
+
partialFailure: true,
|
|
535
|
+
validateOnly: false,
|
|
536
|
+
debugEnabled: false
|
|
537
|
+
};
|
|
538
|
+
const requestOptions = {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: {
|
|
541
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
542
|
+
Authorization: `Bearer ${tokenData.token}`,
|
|
543
|
+
"developer-token": config.client.developerToken
|
|
544
|
+
},
|
|
545
|
+
body: JSON.stringify(body)
|
|
546
|
+
};
|
|
547
|
+
const baseUrl = `https://googleads.googleapis.com/v17/customers/${config.customerId}:uploadClickConversions`;
|
|
548
|
+
const response = await fetch(baseUrl, requestOptions);
|
|
549
|
+
const responseJson = await response.json();
|
|
550
|
+
const knownErrors = [
|
|
551
|
+
"The imported event has a conversion_date_time that precedes the click. Make sure your conversion_date_time is correct and try again., at conversions[0].conversion_date_time",
|
|
552
|
+
"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., at conversions[0].gclid",
|
|
553
|
+
"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., at conversions[0].gclid",
|
|
554
|
+
"The imported event includes an order ID that was previously recorded, so the event was not processed., at conversions[0].order_id",
|
|
555
|
+
"The imported event could not be attributed to a click. This may be because the event did not come from a Google Ads campaign., at conversions[0].gclid",
|
|
556
|
+
"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., at conversions[0].conversion_action"
|
|
557
|
+
];
|
|
558
|
+
if (responseJson.partialFailureError) {
|
|
559
|
+
logMessage(1, responseJson.partialFailureError.message);
|
|
560
|
+
if (!knownErrors.includes(responseJson.partialFailureError.message)) {
|
|
561
|
+
throw new Error(responseJson.partialFailureError.message);
|
|
562
|
+
}
|
|
563
|
+
} else if (responseJson.error) {
|
|
564
|
+
throw new Error(responseJson.error.message);
|
|
565
|
+
} else {
|
|
566
|
+
logEvent(this.source, event.event, event.region);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
|
|
485
571
|
// src/publishers/googleAnalytics4.ts
|
|
486
572
|
var googleAnalytics4 = {
|
|
487
573
|
source: "GoogleAnalytics4",
|
|
@@ -648,6 +734,8 @@ var publisher = {
|
|
|
648
734
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
649
735
|
CJ: cj,
|
|
650
736
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
737
|
+
GoogleAds: googleAds,
|
|
738
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
651
739
|
GoogleAnalytics4: googleAnalytics4,
|
|
652
740
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
653
741
|
UniversalAnalytics: universalAnalytics,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mhubcloud/tms_core",
|
|
3
|
-
"version": "3.3.0-beta.
|
|
3
|
+
"version": "3.3.0-beta.3",
|
|
4
4
|
"description": "Core function for mHub Cloud TMS publishers",
|
|
5
5
|
"homepage": "https://mhubcloud.com/",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"test": "mocha ./tests/*.js --local-settings=./local.settings.json --timeout 100000",
|
|
42
42
|
"dev": "ts-node index.ts",
|
|
43
|
-
"build": "tsup ./src/index.ts --format cjs --target
|
|
43
|
+
"build": "tsup ./src/index.ts --format cjs --target node20"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -4,8 +4,8 @@ const SOURCE = "f_TableStorage";
|
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
/**
|
|
7
|
-
* Processed data
|
|
8
|
-
* @param {Object} data The data
|
|
7
|
+
* Processed data coming from EventHub to data for further processing
|
|
8
|
+
* @param {Object} data The data coming from EventHub
|
|
9
9
|
* @returns {Object} Data for further processing
|
|
10
10
|
*/
|
|
11
11
|
parse: function parse(data, context) {
|
package/src/defaultPublisher.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { TypeSafeObject } from './types/UtilTypes';
|
|
|
8
8
|
import * as utils from './utils/utils';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Implements the Azure Function, called for set of events received via
|
|
11
|
+
* Implements the Azure Function, called for set of events received via Service Bus
|
|
12
12
|
* @deprecated Udelat novou metodu, ktera bude spravne a bude inicializovat classu
|
|
13
13
|
* @param azureFunctionContext Azure Function Context
|
|
14
14
|
* @param serviceBusMessage Message received from Service Bus
|
|
@@ -75,7 +75,6 @@ async function processMessage(
|
|
|
75
75
|
const logProperties: Telemetry['properties'] = {
|
|
76
76
|
source: platformPublisher.source,
|
|
77
77
|
data: JSON.stringify(message),
|
|
78
|
-
sampling: process.env.Sampling,
|
|
79
78
|
};
|
|
80
79
|
const publishers = trigger(message, log);
|
|
81
80
|
if (!publishers.length) {
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { defaultPublisher } from './defaultPublisher';
|
|
|
2
2
|
import { affiliatePort } from './publishers/affiliatePort';
|
|
3
3
|
import { cj } from './publishers/cj';
|
|
4
4
|
import { facebook } from './publishers/facebook';
|
|
5
|
+
import { googleAds } from './publishers/googleAds';
|
|
5
6
|
import { googleAnalytics4 } from './publishers/googleAnalytics4';
|
|
6
7
|
import { piano } from './publishers/piano';
|
|
7
8
|
import { universalAnalytics } from './publishers/universalAnalytics';
|
|
@@ -14,6 +15,8 @@ export const publisher = {
|
|
|
14
15
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
15
16
|
CJ: cj,
|
|
16
17
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
18
|
+
GoogleAds: googleAds,
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
17
20
|
GoogleAnalytics4: googleAnalytics4,
|
|
18
21
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
19
22
|
UniversalAnalytics: universalAnalytics,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as utils from './../utils/utils';
|
|
2
|
+
import {
|
|
3
|
+
PlatformPublisher,
|
|
4
|
+
PlatformRequestOptions,
|
|
5
|
+
} from '../types/PlatformPublisher';
|
|
6
|
+
import {
|
|
7
|
+
fetchGoogleAdsToken,
|
|
8
|
+
GoogleAdsConfig,
|
|
9
|
+
} from '../utils/tokens/googleAdsToken';
|
|
10
|
+
|
|
11
|
+
interface GoogleAdsData {
|
|
12
|
+
gclid?: string;
|
|
13
|
+
wbraid?: string;
|
|
14
|
+
gbraid?: string;
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
16
|
+
user_identifiers: Record<string, unknown>[];
|
|
17
|
+
conversionAction?: string;
|
|
18
|
+
[x: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const googleAds: PlatformPublisher = {
|
|
22
|
+
source: 'GoogleAds',
|
|
23
|
+
/**
|
|
24
|
+
*Publishes data to Google Ads
|
|
25
|
+
* @param log Azure Function context.log
|
|
26
|
+
* @param config Publisher configuration
|
|
27
|
+
* @param data Data in publisher-friendly format to be published
|
|
28
|
+
* @param event Event for which the function is triggered
|
|
29
|
+
*/
|
|
30
|
+
async publish(log, config, data, event) {
|
|
31
|
+
if (
|
|
32
|
+
!(data as GoogleAdsData).gclid &&
|
|
33
|
+
!(data as GoogleAdsData).wbraid &&
|
|
34
|
+
!(data as GoogleAdsData).gbraid &&
|
|
35
|
+
!(data as GoogleAdsData).user_identifiers
|
|
36
|
+
) {
|
|
37
|
+
utils.logMessage(1, `Excluded ${event.event} due to lack of identifiers`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const tokenData = await fetchGoogleAdsToken(config);
|
|
42
|
+
|
|
43
|
+
data.conversionAction = `customers/${config.customerId}/conversionActions/${config.conversionId}`;
|
|
44
|
+
|
|
45
|
+
const body = {
|
|
46
|
+
conversions: [data],
|
|
47
|
+
partialFailure: true,
|
|
48
|
+
validateOnly: false,
|
|
49
|
+
debugEnabled: false,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const requestOptions = {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
56
|
+
Authorization: `Bearer ${tokenData.token}`,
|
|
57
|
+
'developer-token': config.client.developerToken,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const baseUrl = `https://googleads.googleapis.com/v17/customers/${config.customerId}:uploadClickConversions`;
|
|
63
|
+
|
|
64
|
+
const response = await fetch(baseUrl, requestOptions);
|
|
65
|
+
const responseJson: GoogleAdsResponse = await response.json();
|
|
66
|
+
|
|
67
|
+
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., at conversions[0].conversion_date_time',
|
|
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., at conversions[0].gclid",
|
|
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., at conversions[0].gclid",
|
|
71
|
+
'The imported event includes an order ID that was previously recorded, so the event was not processed., at conversions[0].order_id',
|
|
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., at conversions[0].gclid',
|
|
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., at conversions[0].conversion_action",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
if (responseJson.partialFailureError) {
|
|
77
|
+
utils.logMessage(1, responseJson.partialFailureError.message);
|
|
78
|
+
if (!knownErrors.includes(responseJson.partialFailureError.message)) {
|
|
79
|
+
throw new Error(responseJson.partialFailureError.message);
|
|
80
|
+
}
|
|
81
|
+
} else if (responseJson.error) {
|
|
82
|
+
throw new Error(responseJson.error.message);
|
|
83
|
+
} else {
|
|
84
|
+
utils.logEvent(this.source, event.event, event.region);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
interface GoogleAdsResponse {
|
|
90
|
+
partialFailureError?: {
|
|
91
|
+
message: string;
|
|
92
|
+
};
|
|
93
|
+
error?: {
|
|
94
|
+
message: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -16,8 +16,6 @@ if (
|
|
|
16
16
|
process.env.APPLICATIONINSIGHTS_CONNECTION_STRING ||
|
|
17
17
|
process.env.APPINSIGHTS_INSTRUMENTATIONKEY
|
|
18
18
|
) {
|
|
19
|
-
const samplingLevel = process.env.SamplingLevel ?? '12.5';
|
|
20
|
-
|
|
21
19
|
appInsights
|
|
22
20
|
.setup() // Set environment variable APPLICATIONINSIGHTS_CONNECTION_STRING, so setup() can be called w/o parameters
|
|
23
21
|
.setAutoCollectDependencies(false)
|
|
@@ -25,10 +23,8 @@ if (
|
|
|
25
23
|
.setSendLiveMetrics(false)
|
|
26
24
|
.setAutoCollectRequests(false)
|
|
27
25
|
.setAutoCollectPerformance(false)
|
|
28
|
-
.setUseDiskRetryCaching(false)
|
|
29
|
-
|
|
30
|
-
parseFloat(samplingLevel);
|
|
31
|
-
appInsights.start();
|
|
26
|
+
.setUseDiskRetryCaching(false)
|
|
27
|
+
.start();
|
|
32
28
|
aiClient = appInsights.defaultClient;
|
|
33
29
|
}
|
|
34
30
|
|
|
@@ -82,10 +78,9 @@ export function logEvent(
|
|
|
82
78
|
region: string,
|
|
83
79
|
): void {
|
|
84
80
|
const properties: Telemetry['properties'] = {
|
|
85
|
-
publisher,
|
|
86
|
-
event,
|
|
87
|
-
region,
|
|
88
|
-
sampling: process.env.SamplingLevel,
|
|
81
|
+
publisher: publisher,
|
|
82
|
+
event: event,
|
|
83
|
+
region: region,
|
|
89
84
|
};
|
|
90
85
|
logMessage(1, 'Event dispatched to platform', properties);
|
|
91
86
|
}
|
|
@@ -343,7 +338,9 @@ export async function handlePlatformRequest({
|
|
|
343
338
|
rawPlatformResponse,
|
|
344
339
|
event.event,
|
|
345
340
|
);
|
|
341
|
+
|
|
346
342
|
logEvent(source, event.event, event.region);
|
|
343
|
+
|
|
347
344
|
return processedPlatformResponse;
|
|
348
345
|
} else {
|
|
349
346
|
logEvent(source, event.event, event.region);
|
package/tests/AffiliatePort.js
CHANGED
|
@@ -109,7 +109,7 @@ class AffiliatePortPurchasePublisher extends utils.EventPublisherBase {
|
|
|
109
109
|
/**
|
|
110
110
|
* Maps `purchase` event data to AffiliatePort payload
|
|
111
111
|
* @param {Object} data `purchase` event data
|
|
112
|
-
* @returns {Object} Data mapped from
|
|
112
|
+
* @returns {Object} Data mapped from incoming Service Bus message to platform payload
|
|
113
113
|
*/
|
|
114
114
|
map(data) {
|
|
115
115
|
return {
|
package/tests/CJ.js
CHANGED
|
@@ -131,7 +131,7 @@ class CJPurchasePublisher extends utils.EventPublisherBase {
|
|
|
131
131
|
/**
|
|
132
132
|
* Converts input data to publishing payload data understood by the publisher
|
|
133
133
|
* @param {Object} data `purchase` event data
|
|
134
|
-
* @returns {Object} Data mapped from
|
|
134
|
+
* @returns {Object} Data mapped from incoming Service Bus message to platform payload
|
|
135
135
|
*/
|
|
136
136
|
map(data, log) {
|
|
137
137
|
const ret = {
|
package/tests/Facebook.js
CHANGED
|
@@ -86,7 +86,7 @@ class FBPurchasePublisher extends utils.EventPublisherBase {
|
|
|
86
86
|
/**
|
|
87
87
|
* Maps `purchase` event data to Facebook payload
|
|
88
88
|
* @param {Object} data `purchase` event data
|
|
89
|
-
* @returns {Object} Data mapped from
|
|
89
|
+
* @returns {Object} Data mapped from incoming Service Bus message to platform payload
|
|
90
90
|
*/
|
|
91
91
|
map(data) {
|
|
92
92
|
return {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const publisher = require('../dist/index.js').publisher.GoogleAds;
|
|
2
|
+
const defaultPublisher = require('../dist/index.js').DefaultPublisher;
|
|
3
|
+
const utils = require('../dist/index.js').utils;
|
|
4
|
+
const testUtils = require('../dist/index.js').testUtils;
|
|
5
|
+
const localSettings = require('../local.settings.json');
|
|
6
|
+
process.env = { ...process.env, ...localSettings };
|
|
7
|
+
|
|
8
|
+
describe('test GoogleAds publisher', function () {
|
|
9
|
+
it('purchase', async function () {
|
|
10
|
+
const serviceBusMessage = {
|
|
11
|
+
currencyCode: 'CZK',
|
|
12
|
+
event: {
|
|
13
|
+
region: 'MAIN',
|
|
14
|
+
id: '115169112-4870.1657003407533',
|
|
15
|
+
code: 'purchase',
|
|
16
|
+
},
|
|
17
|
+
session: {
|
|
18
|
+
userAgent:
|
|
19
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
|
|
20
|
+
ip: '89.177.126.237',
|
|
21
|
+
referrer: 'https://crossmasters.com/',
|
|
22
|
+
},
|
|
23
|
+
referrer: 'https://crossmasters.com/',
|
|
24
|
+
permissions: [],
|
|
25
|
+
identity: {
|
|
26
|
+
piano: {
|
|
27
|
+
id: '1657003393413.319297501',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
publisher: {
|
|
31
|
+
code: 'mhubcloud.com',
|
|
32
|
+
tm: 'GTM-N7K9L4X',
|
|
33
|
+
tmv: '494',
|
|
34
|
+
},
|
|
35
|
+
$schema: '/mhub/purchase',
|
|
36
|
+
id: '20162351',
|
|
37
|
+
revenue: 440,
|
|
38
|
+
products: [
|
|
39
|
+
{
|
|
40
|
+
id: 'Car',
|
|
41
|
+
price: 440,
|
|
42
|
+
quantity: 1,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
enqueuedAt: 1657003409370,
|
|
46
|
+
ads: [
|
|
47
|
+
{
|
|
48
|
+
adPlatform: 'googleads',
|
|
49
|
+
adCodes: {
|
|
50
|
+
gclid: 'mhubcloud',
|
|
51
|
+
},
|
|
52
|
+
visitAt: 1727173492000,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
await test(serviceBusMessage);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Test function
|
|
62
|
+
* @param [object] object obsahující nastavení testovací parametry
|
|
63
|
+
*/
|
|
64
|
+
async function test(serviceBusMessage) {
|
|
65
|
+
await defaultPublisher(
|
|
66
|
+
testUtils.context,
|
|
67
|
+
serviceBusMessage,
|
|
68
|
+
'GoogleAds',
|
|
69
|
+
publisher,
|
|
70
|
+
trigger,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function trigger(data) {
|
|
75
|
+
const publishers = [];
|
|
76
|
+
let publisher;
|
|
77
|
+
|
|
78
|
+
// Dummy config, replace with your own
|
|
79
|
+
const config = {
|
|
80
|
+
client: {
|
|
81
|
+
id: process.env.Publisher_GoogleAds_ClientID,
|
|
82
|
+
secret: process.env.Publisher_GoogleAds_ClientSecret,
|
|
83
|
+
developerToken: process.env.Publisher_GoogleAds_DeveloperToken,
|
|
84
|
+
},
|
|
85
|
+
refreshToken:
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
87
|
+
process.env['Publisher_GoogleAds_RefreshToken_' + data.event.region],
|
|
88
|
+
customerId:
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
90
|
+
process.env['Publisher_GoogleAds_CustomerID_' + data.event.region],
|
|
91
|
+
conversionId: process.env.Publisher_GoogleAds_ConversionID_purchase_MAIN,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
switch (data.event?.code) {
|
|
95
|
+
case 'purchase':
|
|
96
|
+
publisher = new GoogleAdsPurchasePublisher(config);
|
|
97
|
+
publishers.push(publisher);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
return publishers;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class GoogleAdsPurchasePublisher extends utils.EventPublisherBase {
|
|
104
|
+
/**
|
|
105
|
+
* Maps `purchase` event data to GoogleAds payload
|
|
106
|
+
* @param {Object} data `purchase` event data
|
|
107
|
+
* @returns {Object} Data mapped from incoming Service Bus message to platform payload
|
|
108
|
+
*/
|
|
109
|
+
map(data) {
|
|
110
|
+
const dateTime = new Date(data.enqueuedAt);
|
|
111
|
+
const dateString =
|
|
112
|
+
dateTime.toISOString().replace('T', ' ').split('.')[0] + '+00:00';
|
|
113
|
+
const gclid = utils.extractAdCode(data, 'googleads', 'gclid')?.code;
|
|
114
|
+
const wbraid = utils.extractAdCode(data, 'googleads', 'wbraid')?.code;
|
|
115
|
+
const gbraid = utils.extractAdCode(data, 'googleads', 'gbraid')?.code;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
gclid: gclid,
|
|
119
|
+
wbraid: gclid || gbraid ? undefined : wbraid,
|
|
120
|
+
gbraid: gclid || wbraid ? undefined : gbraid,
|
|
121
|
+
orderId: data.event.id,
|
|
122
|
+
conversionDateTime: dateString,
|
|
123
|
+
currencyCode: data.currencyCode, // e.g. EUR
|
|
124
|
+
conversionValue: data.value ? data.value : 0.01, // e.g. 142.52
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
package/tests/Piano.js
CHANGED
|
@@ -80,7 +80,7 @@ class PianoPurchasePublisher extends utils.EventPublisherBase {
|
|
|
80
80
|
/**
|
|
81
81
|
* Converts input data to publishing payload data understood by the publisher
|
|
82
82
|
* @param {Object} data `purchase` event data
|
|
83
|
-
* @returns {Object} Data mapped from
|
|
83
|
+
* @returns {Object} Data mapped from incoming Service Bus message to platform payload
|
|
84
84
|
*/
|
|
85
85
|
map(data) {
|
|
86
86
|
return {
|