@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.
@@ -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
- };
@@ -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
- }
@@ -1,5 +0,0 @@
1
- export interface EventToLog {
2
- event: string;
3
- region: string;
4
- id: string;
5
- }
@@ -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
- }
@@ -1,10 +0,0 @@
1
- export interface ServiceBusMessage {
2
- enqueuedAt: number;
3
- event: {
4
- code: string;
5
- name?: string;
6
- region: string;
7
- };
8
- messageId: string;
9
- [x: string]: unknown;
10
- }
@@ -1 +0,0 @@
1
- export type TypeSafeObject = Record<string, unknown>;
@@ -1,3 +0,0 @@
1
- declare namespace globalThis {
2
- function it(): unknown;
3
- }
@@ -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 };