@mhubcloud/tms_core 3.5.1 → 3.6.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/LICENSE +202 -202
- package/dist/index.js +32 -0
- package/package.json +46 -46
- package/.eslintignore +0 -3
- package/.eslintrc.cjs +0 -87
- package/.prettierignore +0 -3
- package/.prettierrc +0 -10
- package/.vscode/extensions.json +0 -12
- package/.vscode/settings.json +0 -51
- package/index.js +0 -17
- package/releasePlan.md +0 -2
- package/src/broker.ts +0 -182
- package/src/defaultPublisher.ts +0 -144
- package/src/index.ts +0 -30
- package/src/publishers/affiliatePort.ts +0 -69
- package/src/publishers/cj.ts +0 -70
- package/src/publishers/facebook.ts +0 -93
- package/src/publishers/googleAds.ts +0 -193
- package/src/publishers/googleAnalytics4.ts +0 -60
- package/src/publishers/piano.ts +0 -71
- package/src/publishers/universalAnalytics.ts +0 -63
- package/src/types/EventPublisher.ts +0 -13
- package/src/types/EventToLog.ts +0 -5
- package/src/types/PlatformPublisher.ts +0 -20
- package/src/types/ServiceBusMessage.ts +0 -10
- package/src/types/UtilTypes.ts +0 -1
- package/src/types/global.d.ts +0 -3
- package/src/utils/testUtils.ts +0 -18
- package/src/utils/utils.ts +0 -433
- package/tests/AffiliatePort.js +0 -133
- package/tests/CJ.js +0 -161
- package/tests/Facebook.js +0 -128
- package/tests/GoogleAds.js +0 -130
- package/tests/GoogleAnalytics4.js +0 -99
- package/tests/Piano.js +0 -120
- package/tests/UniversalAnalytics.js +0 -107
- package/tsconfig.eslint.json +0 -43
- package/tsconfig.json +0 -26
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { Logger } from '@azure/functions';
|
|
2
|
-
import * as utils from './../utils/utils';
|
|
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
|
-
}
|
|
16
|
-
|
|
17
|
-
interface GoogleAdsData {
|
|
18
|
-
gclid?: string;
|
|
19
|
-
wbraid?: string;
|
|
20
|
-
gbraid?: string;
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
22
|
-
user_identifiers?: Record<string, unknown>[];
|
|
23
|
-
conversionAction?: string;
|
|
24
|
-
[x: string]: unknown;
|
|
25
|
-
}
|
|
26
|
-
|
|
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 = {
|
|
63
|
-
source: 'GoogleAds',
|
|
64
|
-
/**
|
|
65
|
-
*Publishes data to Google Ads
|
|
66
|
-
* @param log Azure Function context.log
|
|
67
|
-
* @param config Publisher configuration
|
|
68
|
-
* @param data Data in publisher-friendly format to be published
|
|
69
|
-
* @param event Event for which the function is triggered
|
|
70
|
-
*/
|
|
71
|
-
async publish(log, config, data, event) {
|
|
72
|
-
if (
|
|
73
|
-
!(data as GoogleAdsData).gclid &&
|
|
74
|
-
!(data as GoogleAdsData).wbraid &&
|
|
75
|
-
!(data as GoogleAdsData).gbraid &&
|
|
76
|
-
!(data as GoogleAdsData).user_identifiers
|
|
77
|
-
) {
|
|
78
|
-
utils.logMessage(1, `Excluded ${event.event} due to lack of identifiers`);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const tokenData = await this.getToken(config);
|
|
83
|
-
|
|
84
|
-
data.conversionAction = `customers/${config.customerId}/conversionActions/${config.conversionId}`;
|
|
85
|
-
|
|
86
|
-
const body = {
|
|
87
|
-
conversions: [data],
|
|
88
|
-
partialFailure: true,
|
|
89
|
-
validateOnly: false,
|
|
90
|
-
debugEnabled: false,
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const requestOptions = {
|
|
94
|
-
method: 'POST',
|
|
95
|
-
headers: {
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
97
|
-
Authorization: `Bearer ${tokenData.token}`,
|
|
98
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
99
|
-
'developer-token': config.client.developerToken,
|
|
100
|
-
},
|
|
101
|
-
body: JSON.stringify(body),
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const baseUrl = `https://googleads.googleapis.com/v20/customers/${config.customerId}:uploadClickConversions`;
|
|
105
|
-
|
|
106
|
-
const response = await fetch(baseUrl, requestOptions);
|
|
107
|
-
const responseJson: GoogleAdsResponse = await response.json();
|
|
108
|
-
|
|
109
|
-
const knownErrors = [
|
|
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.",
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
let status = 0;
|
|
119
|
-
let error;
|
|
120
|
-
|
|
121
|
-
if (responseJson.partialFailureError) {
|
|
122
|
-
status = 500;
|
|
123
|
-
utils.logMessage(1, responseJson.partialFailureError.message);
|
|
124
|
-
const trimmedMessage =
|
|
125
|
-
responseJson.partialFailureError.message.split('., at')[0] ?? '';
|
|
126
|
-
if (!knownErrors.includes(trimmedMessage)) {
|
|
127
|
-
error = responseJson.partialFailureError.message;
|
|
128
|
-
}
|
|
129
|
-
} else if (responseJson.error) {
|
|
130
|
-
status = 500;
|
|
131
|
-
error = responseJson.error.message;
|
|
132
|
-
} else {
|
|
133
|
-
status = 200;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
utils.logEvent(
|
|
137
|
-
this.source,
|
|
138
|
-
event.event,
|
|
139
|
-
event.region,
|
|
140
|
-
status,
|
|
141
|
-
requestOptions.body,
|
|
142
|
-
event.id,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
if (error) {
|
|
146
|
-
throw new Error(error);
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
async getToken(config: GoogleAdsConfig) {
|
|
151
|
-
if (tokenCache && tokenCache.createdAt + 3500 * 1000 > Date.now()) {
|
|
152
|
-
return tokenCache;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (!tokenPromise) {
|
|
156
|
-
tokenPromise = (async () => {
|
|
157
|
-
const tokenResponse = await fetch(
|
|
158
|
-
`https://oauth2.googleapis.com/token`,
|
|
159
|
-
{
|
|
160
|
-
method: 'POST',
|
|
161
|
-
headers: {
|
|
162
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
163
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
164
|
-
},
|
|
165
|
-
body: new URLSearchParams({
|
|
166
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
167
|
-
client_id: config.client.id,
|
|
168
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
169
|
-
client_secret: config.client.secret,
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
171
|
-
refreshToken: config.refreshToken,
|
|
172
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
173
|
-
grant_type: 'refresh_token',
|
|
174
|
-
}),
|
|
175
|
-
},
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
if (!tokenResponse.ok) {
|
|
179
|
-
throw new Error('Access token fetch failed');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const parsedTokenResponse: TokenResponse = await tokenResponse.json();
|
|
183
|
-
const createdAt = Date.now();
|
|
184
|
-
|
|
185
|
-
tokenCache = { token: parsedTokenResponse.access_token, createdAt };
|
|
186
|
-
tokenPromise = null;
|
|
187
|
-
return tokenCache;
|
|
188
|
-
})();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return tokenPromise;
|
|
192
|
-
},
|
|
193
|
-
};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import * as utils from './../utils/utils';
|
|
2
|
-
import {
|
|
3
|
-
PlatformPublisher,
|
|
4
|
-
PlatformRequestOptions,
|
|
5
|
-
} from '../types/PlatformPublisher';
|
|
6
|
-
import { TypeSafeObject } from '../types/UtilTypes';
|
|
7
|
-
|
|
8
|
-
export const googleAnalytics4: PlatformPublisher = {
|
|
9
|
-
source: 'GoogleAnalytics4',
|
|
10
|
-
/**
|
|
11
|
-
* Publishes data to Google Analytics
|
|
12
|
-
* @param log zure Function context.log
|
|
13
|
-
* @param config Publisher configuration
|
|
14
|
-
* @param data Data in publisher-friendly format to be published
|
|
15
|
-
* @param event Event for which the function is triggered
|
|
16
|
-
*/
|
|
17
|
-
async publish(log, config, data, event) {
|
|
18
|
-
if (config.trackingId) {
|
|
19
|
-
const ga4Data = utils.removeUndefined(data) as TypeSafeObject;
|
|
20
|
-
ga4Data.v = 2;
|
|
21
|
-
ga4Data.seg = 1;
|
|
22
|
-
ga4Data.gcs = 111;
|
|
23
|
-
ga4Data.tid = config.trackingId;
|
|
24
|
-
|
|
25
|
-
let payload = '';
|
|
26
|
-
|
|
27
|
-
for (const property in ga4Data) {
|
|
28
|
-
if (Object.prototype.hasOwnProperty.call(ga4Data, property)) {
|
|
29
|
-
if (payload) {
|
|
30
|
-
payload += '&';
|
|
31
|
-
}
|
|
32
|
-
payload += encodeURI(property) + '=' + encodeURI(ga4Data[property]);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const requestOptions: PlatformRequestOptions = {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: {
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
40
|
-
'User-Agent': 'mHub Cloud',
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const baseUrl = 'https://www.google-analytics.com/g/collect';
|
|
45
|
-
|
|
46
|
-
const query = `?${payload}`;
|
|
47
|
-
|
|
48
|
-
await utils.handlePlatformRequest({
|
|
49
|
-
source: this.source,
|
|
50
|
-
baseUrl,
|
|
51
|
-
query,
|
|
52
|
-
requestOptions,
|
|
53
|
-
event,
|
|
54
|
-
logger: log,
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error('Invalid config for platform ' + this.source);
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
};
|
package/src/publishers/piano.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import * as utils from './../utils/utils';
|
|
2
|
-
import {
|
|
3
|
-
PlatformPublisher,
|
|
4
|
-
PlatformRequestOptions,
|
|
5
|
-
} from '../types/PlatformPublisher';
|
|
6
|
-
|
|
7
|
-
interface Aux {
|
|
8
|
-
visitorId: string;
|
|
9
|
-
ref: string;
|
|
10
|
-
userAgent?: string;
|
|
11
|
-
referer?: string;
|
|
12
|
-
forwardedFor?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface PianoData {
|
|
16
|
-
aux: Aux;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const piano: PlatformPublisher = {
|
|
20
|
-
source: 'Piano',
|
|
21
|
-
/**
|
|
22
|
-
* Publishes data to Piano
|
|
23
|
-
* @param log zure Function context.log
|
|
24
|
-
* @param config Publisher configuration
|
|
25
|
-
* @param data Data in publisher-friendly format to be published
|
|
26
|
-
* @param event Event for which the function is triggered
|
|
27
|
-
*/
|
|
28
|
-
async publish(log, config, data, event) {
|
|
29
|
-
if (
|
|
30
|
-
typeof config.siteId === 'string' &&
|
|
31
|
-
typeof config.collectionDomain === 'string'
|
|
32
|
-
) {
|
|
33
|
-
const pianoData = utils.removeUndefined(data) as PianoData;
|
|
34
|
-
let query = '';
|
|
35
|
-
// https://developers.atinternet-solutions.com/piano-analytics-tagging-en/event-tagging-piano-analytics-en/events-pattern-en/#post-request_5
|
|
36
|
-
// https://<collection-domain>/<path>?s=<site-id>&idclient=<visitor-id>&ref=<previous-url>
|
|
37
|
-
query = '?s=' + encodeURI(config.siteId);
|
|
38
|
-
query += '&idclient=' + encodeURI(pianoData.aux.visitorId);
|
|
39
|
-
query += '&ref=' + encodeURI(pianoData.aux.ref);
|
|
40
|
-
|
|
41
|
-
const requestOptions: PlatformRequestOptions = {
|
|
42
|
-
method: 'POST',
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
requestOptions.headers = {};
|
|
46
|
-
|
|
47
|
-
if (pianoData.aux.userAgent) {
|
|
48
|
-
requestOptions.headers['User-Agent'] = pianoData.aux.userAgent;
|
|
49
|
-
}
|
|
50
|
-
if (pianoData.aux.referer) {
|
|
51
|
-
requestOptions.headers.Referer = pianoData.aux.referer;
|
|
52
|
-
}
|
|
53
|
-
if (pianoData.aux.forwardedFor) {
|
|
54
|
-
requestOptions.headers['X-Forwarded-For'] = pianoData.aux.forwardedFor;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const baseUrl = `https://${config.collectionDomain}/`;
|
|
58
|
-
|
|
59
|
-
await utils.handlePlatformRequest({
|
|
60
|
-
source: this.source,
|
|
61
|
-
baseUrl,
|
|
62
|
-
query,
|
|
63
|
-
requestOptions,
|
|
64
|
-
event,
|
|
65
|
-
logger: log,
|
|
66
|
-
});
|
|
67
|
-
} else {
|
|
68
|
-
throw new Error('Invalid config for platform ' + this.source);
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import * as utils from './../utils/utils';
|
|
2
|
-
import {
|
|
3
|
-
PlatformPublisher,
|
|
4
|
-
PlatformRequestOptions,
|
|
5
|
-
} from '../types/PlatformPublisher';
|
|
6
|
-
import { TypeSafeObject } from '../types/UtilTypes';
|
|
7
|
-
|
|
8
|
-
export const universalAnalytics: PlatformPublisher = {
|
|
9
|
-
source: 'UniversalAnalytics',
|
|
10
|
-
/**
|
|
11
|
-
* Publishes data to UniversalAnalytics
|
|
12
|
-
* @param log zure Function context.log
|
|
13
|
-
* @param config Publisher configuration
|
|
14
|
-
* @param data Data in publisher-friendly format to be published
|
|
15
|
-
* @param event Event for which the function is triggered
|
|
16
|
-
*/
|
|
17
|
-
async publish(log, config, data, event) {
|
|
18
|
-
if (config.trackingId) {
|
|
19
|
-
const uaData = utils.removeUndefined(data) as TypeSafeObject;
|
|
20
|
-
uaData.v = 1;
|
|
21
|
-
uaData.tid = config.trackingId;
|
|
22
|
-
|
|
23
|
-
let payload = '';
|
|
24
|
-
|
|
25
|
-
for (const property in uaData) {
|
|
26
|
-
if (Object.prototype.hasOwnProperty.call(uaData, property)) {
|
|
27
|
-
if (payload) {
|
|
28
|
-
payload += '&';
|
|
29
|
-
}
|
|
30
|
-
if (uaData[property]) {
|
|
31
|
-
payload +=
|
|
32
|
-
encodeURI(property) + '=' + encodeURI(uaData[property] ?? '');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const requestOptions: PlatformRequestOptions = {
|
|
38
|
-
method: 'POST',
|
|
39
|
-
body: payload,
|
|
40
|
-
headers: {
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
42
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
44
|
-
'User-Agent': 'mHub Cloud',
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
const baseUrl = 'https://www.google-analytics.com/collect';
|
|
48
|
-
|
|
49
|
-
const query = `?${payload}`;
|
|
50
|
-
|
|
51
|
-
await utils.handlePlatformRequest({
|
|
52
|
-
source: this.source,
|
|
53
|
-
baseUrl,
|
|
54
|
-
query,
|
|
55
|
-
requestOptions,
|
|
56
|
-
event,
|
|
57
|
-
logger: log,
|
|
58
|
-
});
|
|
59
|
-
} else {
|
|
60
|
-
throw new Error('Invalid config for platform ' + this.source);
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Logger } from '@azure/functions';
|
|
2
|
-
import { ServiceBusMessage } from './ServiceBusMessage';
|
|
3
|
-
|
|
4
|
-
export interface EventPublisherBaseClass {
|
|
5
|
-
config: Record<string, unknown>;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface EventPublisher extends EventPublisherBaseClass {
|
|
9
|
-
map: (
|
|
10
|
-
data: ServiceBusMessage,
|
|
11
|
-
log: Logger,
|
|
12
|
-
) => Promise<Record<string, unknown>>;
|
|
13
|
-
}
|
package/src/types/EventToLog.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { HttpMethod, Logger } from '@azure/functions';
|
|
2
|
-
import { EventToLog } from './EventToLog';
|
|
3
|
-
import { TypeSafeObject } from './UtilTypes';
|
|
4
|
-
|
|
5
|
-
export interface PlatformPublisher {
|
|
6
|
-
source: string; // ! ADDED
|
|
7
|
-
publish: (
|
|
8
|
-
log: Logger,
|
|
9
|
-
config: TypeSafeObject,
|
|
10
|
-
data: TypeSafeObject | unknown[],
|
|
11
|
-
event: EventToLog,
|
|
12
|
-
) => Promise<void>;
|
|
13
|
-
// ! REMOVED parse and validate
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface PlatformRequestOptions {
|
|
17
|
-
method: HttpMethod | undefined;
|
|
18
|
-
body?: any | undefined;
|
|
19
|
-
headers?: Record<string, string>;
|
|
20
|
-
}
|
package/src/types/UtilTypes.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type TypeSafeObject = Record<string, unknown>;
|
package/src/types/global.d.ts
DELETED
package/src/utils/testUtils.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
interface LogFunction {
|
|
2
|
-
(str: string): void;
|
|
3
|
-
warn: (str: string) => void;
|
|
4
|
-
}
|
|
5
|
-
// TODO tohle by se mělo dát spíš do nějakých obecných types asi
|
|
6
|
-
interface Context {
|
|
7
|
-
log: LogFunction;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const context: Context = {} as Context;
|
|
11
|
-
context.log = function log(str: string) {
|
|
12
|
-
console.log(str);
|
|
13
|
-
} as LogFunction;
|
|
14
|
-
context.log.warn = function warn(str: string) {
|
|
15
|
-
console.warn(str);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export { context };
|