@mhubcloud/tms_core 1.7.6 → 2.0.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.
Files changed (44) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +87 -0
  3. package/.prettierignore +3 -0
  4. package/.prettierrc +10 -0
  5. package/.vscode/extensions.json +12 -0
  6. package/.vscode/settings.json +52 -0
  7. package/dist/index.js +609 -0
  8. package/index.js +1 -0
  9. package/{lib → js/lib}/defaultPublisher.js +26 -24
  10. package/js/lib/utils.js +349 -0
  11. package/js/publishers/AffiliatePort.js +78 -0
  12. package/{publishers → js/publishers}/CJ.js +7 -5
  13. package/{publishers → js/publishers}/Facebook.js +6 -4
  14. package/{publishers → js/publishers}/GoogleAnalytics4.js +8 -6
  15. package/js/publishers/HttpPublisher.js +64 -0
  16. package/{publishers → js/publishers}/Piano.js +6 -4
  17. package/{publishers → js/publishers}/UniversalAnalytics.js +7 -5
  18. package/package.json +46 -29
  19. package/releasePlan.md +3 -0
  20. package/src/defaultPublisher.ts +132 -0
  21. package/src/index.ts +25 -0
  22. package/src/publishers/affiliatePort.ts +69 -0
  23. package/src/publishers/cj.ts +67 -0
  24. package/src/publishers/facebook.ts +49 -0
  25. package/src/publishers/googleAnalytics4.ts +60 -0
  26. package/src/publishers/piano.ts +71 -0
  27. package/src/publishers/universalAnalytics.ts +63 -0
  28. package/src/types/EventHubMessage.ts +8 -0
  29. package/src/types/EventPublisher.ts +9 -0
  30. package/src/types/PlatformPublisher.ts +19 -0
  31. package/src/types/UtilTypes.ts +1 -0
  32. package/src/types/global.d.ts +3 -0
  33. package/src/utils/utils.ts +358 -0
  34. package/tests/AffiliatePort.js +107 -85
  35. package/tests/CJ.js +144 -119
  36. package/tests/Facebook.js +124 -103
  37. package/tests/GoogleAnalytics4.js +82 -74
  38. package/tests/Piano.js +112 -99
  39. package/tests/UniversalAnalytics.js +90 -77
  40. package/tests/testUtils.js +7 -0
  41. package/tsconfig.eslint.json +43 -0
  42. package/tsconfig.json +26 -0
  43. package/lib/utils.js +0 -307
  44. package/publishers/AffiliatePort.js +0 -61
@@ -0,0 +1,64 @@
1
+ const fetch = require('node-fetch');
2
+ const utils = require("../lib/utils");
3
+ const SOURCE = "f_HttpPublisher";
4
+
5
+ module.exports = {
6
+ /**
7
+ * Processed data comming from EventHub to data for further processing
8
+ * @param {Object} data The data comming from EventHub
9
+ * @param {FunctionContext} test Azure Function context.test
10
+ * @returns {Object} Data for further processing
11
+ */
12
+ parse: function parse(data, test) {
13
+ //ConsumerBase.cs:ConsumeOneEventAsync
14
+ data.enqueuedAt = test ? 1600000000000 : Date.now();
15
+ return data;
16
+ },
17
+
18
+ /**
19
+ * Publishes data to provided URL
20
+ * @param {FunctionContext} log Azure Function context.log.warn
21
+ * @param {string} event Required: event for which is it triggered
22
+ * @param {string} url Required: Request URL with query parameters
23
+ * @param {string} platform Required: Name of the plaform to which request is sent
24
+ * @param {string} method Required: Request method - GET|POST|PUT...
25
+ * @param {Object} headers Optional: Object of request headers
26
+ * @param {Object} body Optional: Non-strigifed request body object
27
+ *
28
+ */
29
+ publish: async function publish(log, event, platform, url, method, headers = {}, body = {}) {
30
+ const reqOpts = {
31
+ method: method,
32
+ headers: headers
33
+ };
34
+ if(method == "POST"){
35
+ reqOpts.body = JSON.stringify(body);
36
+ }
37
+
38
+ const resp = await fetch(url, reqOpts);
39
+
40
+ if (process.env.PUBLISHER_TESTURL) {
41
+ try {
42
+ utils.prepareTestRequestHeaders(reqOpts.headers, url, reqOpts, resp);
43
+ await fetch(process.env.PUBLISHER_TESTURL, reqOpts);
44
+ } catch { }
45
+ }
46
+
47
+ await utils.handleResponse(resp, event);
48
+
49
+ log(`${platform} - ${event} - sent`);
50
+ },
51
+
52
+ /**
53
+ * Validates an object against given schema
54
+ * @param {Object} data An object to validate.
55
+ * @param {string} schema Name of the schema. That's name of a file with `.schema.json` extension in `customizable/schemas` subfolder of current publisher folder
56
+ * @returns {boolean} Validation result, true if successful, false if unsuccessfull (in which case more info is written to console)
57
+ */
58
+ validate: function validate(data, schema) {
59
+ if (!schema) throw new Error("Schema parameters not provided");
60
+ if (schema.indexOf("/") >= 0) throw new Error("Schema must be provided as schema name, not schema path");
61
+ //More validation done by utils.validate
62
+ return utils.validate(data, SOURCE, schema);
63
+ }
64
+ };
@@ -6,22 +6,24 @@ module.exports = {
6
6
  /**
7
7
  * Processed data comming from EventHub to data for further processing
8
8
  * @param {Object} data The data comming from EventHub
9
+ * @param {FunctionContext} test Azure Function context.test
9
10
  * @returns {Object} Data for further processing
10
11
  */
11
- parse: function parse(data, context) {
12
+ parse: function parse(data, test) {
12
13
  //ConsumerBase.cs:ConsumeOneEventAsync
13
- data.enqueuedAt = context.test ? 1600000000000 : Date.now();
14
+ data.enqueuedAt = test ? 1600000000000 : Date.now();
14
15
  return data;
15
16
  },
16
17
 
17
18
  /**
18
19
  * Publishes data to Piano
20
+ * @param {FunctionContext} log Azure Function context.log.warn
19
21
  * @param {{collectionDomain: string, siteId: string|number}} config Publisher configuration
20
22
  * @param {{pianoData: {events: {name: string, data: {[key: string]: any}}[]}, aux: {visitorId: string, ref: string, userAgent?: string, referer?: string, forwardedFor?: string}}} data Data in publisher-friendly format to be published
21
23
  * @returns {*} Data returned by platform, if possible JSON-deserialized (otherwise as string)
22
24
  * @param {string} event for which is it triggered
23
25
  */
24
- publish: async function publish(config, data, event) {
26
+ publish: async function publish(log, config, data, event) {
25
27
  data = utils.removeUndefined(data);
26
28
  let dt = "";
27
29
  //https://developers.atinternet-solutions.com/piano-analytics-tagging-en/event-tagging-piano-analytics-en/events-pattern-en/#post-request_5
@@ -48,7 +50,7 @@ module.exports = {
48
50
 
49
51
  await utils.handleResponse(resp, event);
50
52
 
51
- console.log(`Piano Analytics - ${event} - sent`);
53
+ log(`Piano Analytics - ${event} - sent`);
52
54
  },
53
55
 
54
56
  /**
@@ -8,20 +8,22 @@ module.exports = {
8
8
  /**
9
9
  * Processed data comming from EventHub to data for further processing
10
10
  * @param {Object} data The data comming from EventHub
11
- * @returns {object} Data for further processing
11
+ * @param {FunctionContext} test Azure Function context.test
12
+ * @returns {Object} Data for further processing
12
13
  */
13
- parse: function parse(data, context) {
14
+ parse: function parse(data, test) {
14
15
  //ConsumerBase.cs:ConsumeOneEventAsync
15
- data.enqueuedAt = context.test ? 1600000000000 : Date.now();
16
+ data.enqueuedAt = test ? 1600000000000 : Date.now();
16
17
  return data;
17
18
  },
18
19
 
19
20
  /**
20
21
  * Publishes data to Google Analytics
22
+ * @param {FunctionContext} log Azure Function context.log.warn
21
23
  * @param {{tid: string}} config Publisher configuration
22
24
  * @param {{action: string, ti: string, [key:string]: string|number|boolean}} data Data in publisher-friendly format to be published
23
25
  */
24
- publish: async function publish(config, data, event) {
26
+ publish: async function publish(log, config, data, event) {
25
27
  data = utils.removeUndefined(data);
26
28
  //https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#event
27
29
  const url = "https://www.google-analytics.com/collect";
@@ -55,7 +57,7 @@ module.exports = {
55
57
 
56
58
  await utils.handleResponse(resp);
57
59
 
58
- console.log(`Universal Analytics - ${event} - sent`);
60
+ log(`Universal Analytics - ${event} - sent`);
59
61
  },
60
62
 
61
63
  /**
package/package.json CHANGED
@@ -1,29 +1,46 @@
1
- {
2
- "name": "@mhubcloud/tms_core",
3
- "version": "1.7.6",
4
- "description": "Core function for mHub Cloud TMS publishers",
5
- "homepage": "https://mhubcloud.com/",
6
- "license": "Apache 2.0",
7
- "email": "info@crossmasters.com",
8
- "author": "Jan Brzobohatý",
9
- "main": "index.js",
10
- "keywords": [
11
- "mHub Cloud",
12
- "TMS",
13
- "tag management system",
14
- "backend measurement",
15
- "server side measurement",
16
- "conversion API"
17
- ],
18
- "scripts": {
19
- "test": "mocha ./tests/*.js --timeout 10000 "
20
- },
21
- "dependencies": {
22
- "ajv": "^8.8.2",
23
- "applicationinsights": "^2.2.0",
24
- "node-fetch": "^2.6.7"
25
- },
26
- "devDependencies": {
27
- "mocha": "^10.0.0"
28
- }
29
- }
1
+ {
2
+ "name": "@mhubcloud/tms_core",
3
+ "version": "2.0.0-beta.1",
4
+ "description": "Core function for mHub Cloud TMS publishers",
5
+ "homepage": "https://mhubcloud.com/",
6
+ "license": "Apache 2.0",
7
+ "email": "info@crossmasters.com",
8
+ "author": "Jan Brzobohatý",
9
+ "main": "dist/index.js",
10
+ "keywords": [
11
+ "mHub Cloud",
12
+ "TMS",
13
+ "tag management system",
14
+ "backend measurement",
15
+ "server side measurement",
16
+ "conversion API"
17
+ ],
18
+ "dependencies": {
19
+ "ajv": "^8.12.0",
20
+ "applicationinsights": "^2.6.0",
21
+ "node-fetch": "^2.6.11"
22
+ },
23
+ "devDependencies": {
24
+ "@azure/functions": "^3.5.1",
25
+ "@tsconfig/recommended": "^1.0.2",
26
+ "@types/node": "^18.16.14",
27
+ "@types/node-fetch": "^2.6.4",
28
+ "@typescript-eslint/eslint-plugin": "^5.59.7",
29
+ "@typescript-eslint/parser": "^5.59.7",
30
+ "eslint": "^8.41.0",
31
+ "eslint-config-prettier": "^8.8.0",
32
+ "eslint-import-resolver-typescript": "^3.5.5",
33
+ "eslint-plugin-import": "^2.27.5",
34
+ "eslint-plugin-prettier": "^4.2.1",
35
+ "mocha": "^10.2.0",
36
+ "prettier": "^2.8.8",
37
+ "ts-node": "^10.9.1",
38
+ "tsup": "^6.7.0",
39
+ "typescript": "^4.9.5"
40
+ },
41
+ "scripts": {
42
+ "dev": "ts-node index.ts",
43
+ "build": "tsup ./src/index.ts --format cjs --target node18",
44
+ "test": "mocha ./tests/*.js --timeout 10000 "
45
+ }
46
+ }
package/releasePlan.md ADDED
@@ -0,0 +1,3 @@
1
+ To test after release:
2
+ - Zkontrolovat event_time posílaný do FB, že chodí ve správném formátu a že obsahuje správný údaj.
3
+ - Když použiju utils.log tak to do appinsights zaloguje dvakrát? Máme totiž podezření, že ano.
@@ -0,0 +1,132 @@
1
+ import { Context, Logger } from '@azure/functions';
2
+ import { Telemetry } from 'applicationinsights/out/Declarations/Contracts';
3
+ import { EventHubMessage } from './types/EventHubMessage';
4
+ import { EventPublisher } from './types/EventPublisher';
5
+ import { PlatformPublisher } from './types/PlatformPublisher';
6
+ import { TypeSafeObject } from './types/UtilTypes';
7
+ import * as utils from './utils/utils';
8
+
9
+ /**
10
+ * Implements the Azure Function, called for set of events received via Event Hub
11
+ * @deprecated Udelat novou metodu, ktera bude spravne a bude inicializovat classu
12
+ * @param azureFunctionContext Azure Function Context
13
+ * @param eventHubMessages Array of messages received from Event Hub
14
+ * @param source Name of the publisher (it's folder name in which it's implemented)
15
+ * @param platformPublisher Publisher implementation instance (from <publisher>/publisher.js)
16
+ * @param trigger
17
+ */
18
+ export async function defaultPublisher(
19
+ azureFunctionContext: Context,
20
+ eventHubMessages: EventHubMessage[],
21
+ source: string,
22
+ platformPublisher: PlatformPublisher,
23
+ trigger: (a: unknown) => EventPublisher[],
24
+ ) {
25
+ const processMessagePromises = [];
26
+ for (const message of eventHubMessages) {
27
+ processMessagePromises.push(
28
+ processMessage(
29
+ message,
30
+ azureFunctionContext.log,
31
+ platformPublisher,
32
+ trigger,
33
+ ),
34
+ );
35
+ }
36
+ const results = await Promise.allSettled(processMessagePromises);
37
+ // ? musi nahoru probublat, ze aspon neco selhalo a to je to, co bude triggerovat nasledujici error. Error pro konkretniho publishera musi byt primo v processMessage v loopu. Hodnotu pro typ publishera vezmeme z platformBublisher.SOURCE
38
+ for (const result of results) {
39
+ if (result.status === 'rejected') {
40
+ utils.logException(
41
+ azureFunctionContext.log,
42
+ new Error(result.reason as Error['message']),
43
+ );
44
+ }
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Processes single message from EventHub
50
+ * @param message A message received from EventHub
51
+ * @param log Azure Function context.log
52
+ * @param source Name of the publisher (it's folder name in which it's implemented)
53
+ * @param platfromPublisher Publisher implementation instance (from <publisher>/publisher.js)
54
+ * @param trigger Publisher-specific trigger function (from <publisher>/customizable/trigger.js)
55
+ * @returns Returns false if at least one publisher failed or in case of other failed requirements
56
+ */
57
+ async function processMessage(
58
+ message: EventHubMessage,
59
+ log: Logger,
60
+ platfromPublisher: PlatformPublisher,
61
+ trigger: (a: object) => EventPublisher[],
62
+ ) {
63
+ // TODO Je potreba otestovat, ze se to nerozbije, kdyz jsme prestali prepisovat enqueuedAt na Date.now()
64
+ const logProperties: Telemetry['properties'] = {
65
+ source: platfromPublisher.source,
66
+ data: JSON.stringify(message),
67
+ };
68
+ const publishers = trigger(message);
69
+ if (!publishers.length) {
70
+ utils.log(
71
+ log,
72
+ 1,
73
+ 'Message excluded by filter (no publishers determined)',
74
+ logProperties,
75
+ );
76
+ return false;
77
+ }
78
+ let publishedAny = false;
79
+ let failedToPublishAll = false;
80
+ for (const eventPublisher of publishers) {
81
+ const mapped = await eventPublisher.map(message);
82
+
83
+ const res = await publishMessage(
84
+ log,
85
+ mapped,
86
+ eventPublisher.config,
87
+ platfromPublisher,
88
+ message.event.code + (message.event.name ? '/' + message.event.name : ''),
89
+ );
90
+ if (res) {
91
+ publishedAny = true;
92
+ } else {
93
+ utils.logException(
94
+ log,
95
+ new Error(
96
+ `The message has not been published. Source: ${platfromPublisher.source}`,
97
+ ),
98
+ );
99
+ failedToPublishAll = true;
100
+ }
101
+ }
102
+ if (!publishedAny) {
103
+ utils.logException(
104
+ log,
105
+ new Error(
106
+ 'The message has not been published to any publisher configuration',
107
+ ),
108
+ );
109
+ return false;
110
+ }
111
+ // Returns false
112
+ return !failedToPublishAll;
113
+ }
114
+
115
+ /**
116
+ * Publishes a message to configured publisher
117
+ * @param log Azure Function context.log
118
+ * @param mapped Mapped message data
119
+ * @param config Platform-specific platform publisher configuration
120
+ * @param platformPublisher Publisher implementation instance (from <publisher>/publisher.js)
121
+ * @param event Event for which logic was triggered
122
+ */
123
+ async function publishMessage(
124
+ log: Logger,
125
+ mapped: TypeSafeObject,
126
+ config: TypeSafeObject,
127
+ platformPublisher: PlatformPublisher,
128
+ event: string,
129
+ ) {
130
+ await platformPublisher.publish(log, config, mapped, event);
131
+ return true;
132
+ }
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { defaultPublisher } from './defaultPublisher';
2
+ import { affiliatePort } from './publishers/affiliatePort';
3
+ import { cj } from './publishers/cj';
4
+ import { facebook } from './publishers/facebook';
5
+ import { googleAnalytics4 } from './publishers/googleAnalytics4';
6
+ import { piano } from './publishers/piano';
7
+ import { universalAnalytics } from './publishers/universalAnalytics';
8
+ import * as utils from './utils/utils';
9
+
10
+ export const publisher = {
11
+ // eslint-disable-next-line @typescript-eslint/naming-convention
12
+ Facebook: facebook,
13
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14
+ CJ: cj,
15
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16
+ GoogleAnalytics4: googleAnalytics4,
17
+ // eslint-disable-next-line @typescript-eslint/naming-convention
18
+ UniversalAnalytics: universalAnalytics,
19
+ // eslint-disable-next-line @typescript-eslint/naming-convention
20
+ Piano: piano,
21
+ // eslint-disable-next-line @typescript-eslint/naming-convention
22
+ AffiliatePort: affiliatePort,
23
+ };
24
+
25
+ export { utils, defaultPublisher as DefaultPublisher };
@@ -0,0 +1,69 @@
1
+ import * as utils from './../utils/utils';
2
+ import {
3
+ PlatformPublisher,
4
+ PlatformRequestOptions,
5
+ } from '../types/PlatformPublisher';
6
+
7
+ interface Product {
8
+ orderId: string;
9
+ quantity: number;
10
+ price: number;
11
+ category: string;
12
+ masterType: string;
13
+ }
14
+
15
+ interface AffiliatePortData {
16
+ products: Product[];
17
+ orderId: string;
18
+ transactionID: string;
19
+ [x: string]: unknown;
20
+ }
21
+
22
+ export const affiliatePort: PlatformPublisher = {
23
+ source: 'AffiliatePort',
24
+ /**
25
+ * Publishes data to AffiliatePort
26
+ * @param log zure Function context.log
27
+ * @param config Publisher configuration
28
+ * @param data Data in publisher-friendly format to be published
29
+ * @param event Event for which the function is triggered
30
+ */
31
+ async publish(log, config, data, event) {
32
+ if (
33
+ (data as AffiliatePortData).products instanceof Array &&
34
+ (typeof config.offerId === 'string' || typeof config.offerId === 'number')
35
+ ) {
36
+ const affiliatePortData = utils.removeUndefined(
37
+ data,
38
+ ) as AffiliatePortData;
39
+
40
+ const requestOptions: PlatformRequestOptions = {
41
+ method: 'GET',
42
+ };
43
+
44
+ const baseUrl = 'https://tracking.affiliateclub.cz/aff_lsr';
45
+
46
+ for (const product of affiliatePortData.products) {
47
+ // https://tracking.affiliateclub.cz/aff_lsr?offer_id=OFFER_ID&adv_sub=TID_SUB_ID&amount=AMOUNT&transaction_id=TRANSACTION_ID&adv_sub2=CATEGORY&adv_sub3=MASTER_TYPE
48
+ const query = `?offer_id=${encodeURIComponent(
49
+ config.offerId,
50
+ )}&adv_sub=TID_${affiliatePortData.orderId}&amount=${
51
+ product.quantity * product.price
52
+ }&transaction_id=${affiliatePortData.transactionID}&adv_sub2=${
53
+ product.category
54
+ }&adv_sub3=${product.masterType}`;
55
+
56
+ await utils.handlePlatformRequest({
57
+ source: this.source,
58
+ baseUrl,
59
+ query,
60
+ requestOptions,
61
+ event,
62
+ logger: log,
63
+ });
64
+ }
65
+ } else {
66
+ throw new Error('Invalid config for platform ' + this.source);
67
+ }
68
+ },
69
+ };
@@ -0,0 +1,67 @@
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
+ interface CjData {
9
+ cjevent?: unknown;
10
+ [x: string]: unknown;
11
+ }
12
+
13
+ export const cj: PlatformPublisher = {
14
+ source: 'CJAffiliate',
15
+ /**
16
+ * Publishes data to CJ
17
+ * @param log Azure Function context.log
18
+ * @param config Publisher configuration
19
+ * @param data Data in publisher-friendly format to be published
20
+ * @param event Event for which the function is triggered
21
+ */
22
+ async publish(log, config, data, event) {
23
+ if (!(data as CjData).cjevent) {
24
+ utils.log(log, 1, `CJ - ${event} - skipped because CJEVENT is missing`);
25
+ // ? Tady se returnoval false, ale nemyslim, ze to neco pouzivalo
26
+ } else {
27
+ if (typeof config.cid === 'string' && typeof config.type === 'string') {
28
+ const cjData = utils.removeUndefined(data) as TypeSafeObject;
29
+ let query = '?cid=' + encodeURI(config.cid);
30
+ query += '&type=' + encodeURI(config.type);
31
+ for (const property in data) {
32
+ if (
33
+ Object.prototype.hasOwnProperty.call(cjData, property) &&
34
+ cjData[property] !== undefined
35
+ ) {
36
+ query +=
37
+ '&' +
38
+ encodeURI(property) +
39
+ '=' +
40
+ encodeURI(
41
+ cjData[property] === null
42
+ ? ''
43
+ : // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
44
+ (cjData[property]?.toString() as string),
45
+ );
46
+ }
47
+ }
48
+ const baseUrl = 'https://www.kdukvh.com/u';
49
+
50
+ const requestOptions: PlatformRequestOptions = {
51
+ method: 'GET',
52
+ };
53
+
54
+ await utils.handlePlatformRequest({
55
+ source: this.source,
56
+ baseUrl,
57
+ query,
58
+ requestOptions,
59
+ event,
60
+ logger: log,
61
+ });
62
+ } else {
63
+ throw new Error('Invalid config for platform ' + this.source);
64
+ }
65
+ }
66
+ },
67
+ };
@@ -0,0 +1,49 @@
1
+ import * as utils from './../utils/utils';
2
+ import {
3
+ PlatformPublisher,
4
+ PlatformRequestOptions,
5
+ } from '../types/PlatformPublisher';
6
+
7
+ export const facebook: PlatformPublisher = {
8
+ source: 'Faceboook',
9
+ /**
10
+ *Publishes data to Facebook
11
+ * @param log Azure Function context.log
12
+ * @param config Publisher configuration
13
+ * @param data Data in publisher-friendly format to be published
14
+ * @param event Event for which the function is triggered
15
+ */
16
+ async publish(log, config, data, event) {
17
+ if (
18
+ typeof config.id === 'string' &&
19
+ typeof config.access_token === 'string'
20
+ ) {
21
+ const requestOptions: PlatformRequestOptions = {
22
+ method: 'POST',
23
+ body: JSON.stringify({ data: [utils.removeUndefined(data)] }),
24
+ headers: {
25
+ // eslint-disable-next-line @typescript-eslint/naming-convention
26
+ 'Content-Type': 'application/json',
27
+ },
28
+ };
29
+
30
+ // https://graph.facebook.com/v11.0/<id>/events
31
+ const baseUrl = `https://graph.facebook.com/v11.0/${encodeURIComponent(
32
+ config.id,
33
+ )}/events`;
34
+
35
+ const query = `?access_token=${encodeURI(config.access_token)}`;
36
+
37
+ await utils.handlePlatformRequest({
38
+ source: this.source,
39
+ baseUrl,
40
+ query,
41
+ requestOptions,
42
+ event,
43
+ logger: log,
44
+ });
45
+ } else {
46
+ throw new Error('Invalid config for platform ' + this.source);
47
+ }
48
+ },
49
+ };
@@ -0,0 +1,60 @@
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
+ };