@mhubcloud/tms_core 1.3.0 → 1.6.0

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/index.js CHANGED
@@ -1,11 +1,14 @@
1
- const utils = require('lib/utils');
2
- const DefaultPublisher = require('lib/DefaultPublisher');
3
- const Piano = require('publishers/Piano');
1
+ const utils = require('./lib/utils');
2
+ const DefaultPublisher = require('./lib/DefaultPublisher');
4
3
 
5
- export const {
4
+ module.exports = {
6
5
  utils : utils,
7
6
  DefaultPublisher : DefaultPublisher,
8
7
  publisher : {
9
- Piano : Piano
8
+ Piano : require('./publishers/Piano'),
9
+ Facebook : require('./publishers/Facebook'),
10
+ GoogleAnalytics4 : require('./publishers/GoogleAnalytics4'),
11
+ CJ : require('./publishers/CJ'),
12
+ UniversalAnalytics : require('./publishers/UniversalAnalytics')
10
13
  }
11
14
  }
@@ -12,7 +12,7 @@ const utils = require("./utils");
12
12
  */
13
13
  module.exports = async function DefaultPublisher(context, eventHubMessages, source, publisher, trigger) {
14
14
  context.log(`JavaScript eventhub trigger function called for message array [${eventHubMessages.length}]`);
15
- global.console.log = context.log.warn;
15
+ global.console.log = context.log.warn || context.log;
16
16
 
17
17
  let ret = context.test ? [] : undefined;
18
18
  let mi = 0;
package/lib/utils.js CHANGED
@@ -223,6 +223,10 @@ module.exports = {
223
223
  logException: function logException(exception) {
224
224
  console.log(exception);
225
225
  aiClient?.trackException({ exception: exception });
226
+ //raise exception if it is running under mocha test
227
+ if(typeof global.it === 'function'){
228
+ throw(exception);
229
+ }
226
230
  },
227
231
 
228
232
  /**
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@mhubcloud/tms_core",
3
- "version": "1.3.0",
3
+ "version": "1.6.0",
4
4
  "description": "Core function for mHub Cloud TMS publishers",
5
5
  "homepage": "https://mhubcloud.com/",
6
6
  "license": "Apache 2.0",
7
- "email" : "info@crossmasters.com",
8
- "author" : "Jan Brzobohatý",
7
+ "email": "info@crossmasters.com",
8
+ "author": "Jan Brzobohatý",
9
9
  "main": "index.js",
10
10
  "keywords": [
11
11
  "mHub Cloud",
@@ -15,9 +15,15 @@
15
15
  "server side measurement",
16
16
  "conversion API"
17
17
  ],
18
+ "scripts": {
19
+ "test": "mocha ./tests/*.js"
20
+ },
18
21
  "dependencies": {
19
22
  "ajv": "^8.8.2",
20
23
  "applicationinsights": "^2.2.0",
21
24
  "node-fetch": "^2.6.7"
22
- }
23
- }
25
+ },
26
+ "devDependencies": {
27
+ "mocha": "^10.0.0"
28
+ }
29
+ }
@@ -0,0 +1,66 @@
1
+ const fetch = require('node-fetch');
2
+ const utils = require("../lib/utils");
3
+ const SOURCE = "f_CJAffiliate";
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
+ * @returns {Object} Data for further processing
10
+ */
11
+ parse: function parse(data, context) {
12
+ //ConsumerBase.cs:ConsumeOneEventAsync
13
+ data.enqueuedAt = context.test ? 1600000000000 : Date.now();
14
+ return data;
15
+ },
16
+
17
+ /**
18
+ * Publishes data to Facebook
19
+ * @param {{cid: string|number, type: string|number}} config Publisher configuration
20
+ * @param {Object} data Data in publisher-friendly format to be published
21
+ * @returns {*} Data returned by platform, if possible JSON-deserialized (otherwise as string)
22
+ * @param {string} event for which is it triggered
23
+ */
24
+ publish: async function publish(config, data, event) {
25
+ if(!data.cjevent){
26
+ console.log(`CJ - ${event} - skipped because CJEVENT is missing`);
27
+ return;
28
+ }
29
+
30
+ data = utils.removeUndefined(data);
31
+ const baseUrl = "https://www.kdukvh.com/u";
32
+ let dt = "?cid=" + encodeURI(config.cid);
33
+ dt += "&type=" + encodeURI(config.type);
34
+ for (let prp in data) {
35
+ if (data.hasOwnProperty(prp) && data[prp] !== undefined) {
36
+ dt += "&" + encodeURI(prp) + "=" + encodeURI(data[prp] === null ? "" : data[prp].toString());
37
+ }
38
+ }
39
+ const reqOpts = { method: "get", headers: {} /*Needed by test call*/ };
40
+ const resp = await fetch(baseUrl + dt, reqOpts);
41
+
42
+ if (process.env.PUBLISHER_TESTURL) {
43
+ try {
44
+ utils.prepareTestRequestHeaders(reqOpts.headers, baseUrl + dt, reqOpts, resp);
45
+ await fetch(process.env.PUBLISHER_TESTURL + dt, reqOpts);
46
+ } catch { }
47
+ }
48
+
49
+ await utils.handleResponse(resp, event);
50
+
51
+ console.log(`CJ - ${event} - sent`);
52
+ },
53
+
54
+ /**
55
+ * Validates an object against given schema
56
+ * @param {Object} data An object to validate.
57
+ * @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
58
+ * @returns {boolean} Validation result, true if successful, false if unsuccessfull (in which case more info is written to console)
59
+ */
60
+ validate: function validate(data, schema) {
61
+ if (!schema) throw new Error("Schema parameters not provided");
62
+ if (schema.indexOf("/") >= 0) throw new Error("Schema must be provided as schema name, not schema path");
63
+ //More validation done by utils.validate
64
+ return utils.validate(data, SOURCE, schema);
65
+ }
66
+ };
@@ -0,0 +1,62 @@
1
+ const fetch = require('node-fetch');
2
+ const utils = require("../lib/utils");
3
+ const SOURCE = "f_Facebook";
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
+ * @returns {Object} Data for further processing
10
+ */
11
+ parse: function parse(data, context) {
12
+ //ConsumerBase.cs:ConsumeOneEventAsync
13
+ data.enqueuedAt = context.test ? 1600000000000 : Date.now();
14
+ return data;
15
+ },
16
+
17
+ /**
18
+ * Publishes data to Facebook
19
+ * @param {{id: string, access_token: string}} config Publisher configuration
20
+ * @param {Object} data Data in publisher-friendly format to be published
21
+ * @param {string} event for which is it triggered
22
+ */
23
+ publish: async function publish(config, data, event) {
24
+ data = utils.removeUndefined(data);
25
+ const reqOpts = {
26
+ method: "post",
27
+ body: JSON.stringify({ data: [data] }),
28
+ headers: {
29
+ 'Content-Type': 'application/json'
30
+ }
31
+ };
32
+ //https://graph.facebook.com/v11.0/<id>/events
33
+ const baseUrl = "https://graph.facebook.com/v11.0/" + encodeURIComponent(config.id) + "/events";
34
+ const query = "?access_token=" + encodeURI(config.access_token);
35
+ const url = baseUrl + query;
36
+ const resp = await fetch(url, reqOpts);
37
+
38
+ if (process.env.PUBLISHER_TESTURL) {
39
+ try {
40
+ utils.prepareTestRequestHeaders(reqOpts.headers, url, reqOpts, resp);
41
+ await fetch(process.env.PUBLISHER_TESTURL + query, reqOpts);
42
+ } catch { }
43
+ }
44
+
45
+ await utils.handleResponse(resp, event);
46
+
47
+ console.log(`Facebook - ${event} - sent`);
48
+ },
49
+
50
+ /**
51
+ * Validates an object against given schema
52
+ * @param {Object} data An object to validate.
53
+ * @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
54
+ * @returns {boolean} Validation result, true if successful, false if unsuccessfull (in which case more info is written to console)
55
+ */
56
+ validate: function validate(data, schema) {
57
+ if (!schema) throw new Error("Schema parameters not provided");
58
+ if (schema.indexOf("/") >= 0) throw new Error("Schema must be provided as schema name, not schema path");
59
+ //More validation done by utils.validate
60
+ return utils.validate(data, SOURCE, schema);
61
+ }
62
+ };
@@ -0,0 +1,74 @@
1
+ const fetch = require('node-fetch');
2
+ const utils = require("../lib/utils");
3
+ const SOURCE = "f_GoogleAnalytics4";
4
+
5
+ //https://cloud.google.com/appengine/docs/standard/php7/integrating-with-analytics#server-side_analytics_collection
6
+
7
+ module.exports = {
8
+ /**
9
+ * Processed data comming from EventHub to data for further processing
10
+ * @param {Object} data The data comming from EventHub
11
+ * @returns {object} Data for further processing
12
+ */
13
+ parse: function parse(data, context) {
14
+ //ConsumerBase.cs:ConsumeOneEventAsync
15
+ data.enqueuedAt = context.test ? 1600000000000 : Date.now();
16
+ return data;
17
+ },
18
+
19
+ /**
20
+ * Publishes data to Google Analytics
21
+ * @param {{tid: string}} config Publisher configuration
22
+ * @param {{action: string, ti: string, [key:string]: string|number|boolean}} data Data in publisher-friendly format to be published
23
+ */
24
+ publish: async function publish(config, data, event) {
25
+ data = utils.removeUndefined(data);
26
+ data.v = 2;
27
+ data.tid = config.trackingId;
28
+ data.ep = {
29
+ anonymize_ip : true
30
+ };
31
+ let payload = "";
32
+
33
+ for (const prp in data) {
34
+ if (data.hasOwnProperty(prp)) {
35
+ if (payload) payload += "&";
36
+ payload += encodeURI(prp) + "=" + encodeURI(data[prp]);
37
+ }
38
+ }
39
+
40
+ var reqOpts = {
41
+ hostname: 'www.google-analytics.com',
42
+ port: 443,
43
+ path: '/g/collect?' + payload,
44
+ method: 'POST'
45
+ };
46
+
47
+ await utils.httpRequest(reqOpts);
48
+
49
+ if (process.env.PUBLISHER_TESTURL) {
50
+ try {
51
+ reqOpts.headers = {};
52
+ utils.prepareTestRequestHeaders(reqOpts.headers, reqOpts.hostname + reqOpts.path , reqOpts);
53
+ await fetch(process.env.PUBLISHER_TESTURL, reqOpts);
54
+ } catch(exception) {
55
+ console.log(exception);
56
+ }
57
+ }
58
+
59
+ console.log(`GoogleAnalytics4 - ${event} - sent`);
60
+ },
61
+
62
+ /**
63
+ * Validates an object against given schema
64
+ * @param {Object} data An object to validate.
65
+ * @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
66
+ * @returns {boolean} Validation result, true if successful, false if unsuccessfull (in which case more info is written to console)
67
+ */
68
+ validate: function validate(data, schema) {
69
+ if (!schema) throw new Error("Schema parameters not provided");
70
+ if (schema.indexOf("/") >= 0) throw new Error("Schema must be provided as schema name, not schema path");
71
+ //More validation done by utils.validate
72
+ return utils.validate(data, SOURCE, schema);
73
+ }
74
+ };
@@ -1,4 +1,4 @@
1
- const utils = require("../utils");
1
+ const utils = require("../lib/utils");
2
2
  const fetch = require('node-fetch');
3
3
  const SOURCE = "f_Piano";
4
4
 
@@ -15,7 +15,7 @@ module.exports = {
15
15
  },
16
16
 
17
17
  /**
18
- * Publishes data to Facebook
18
+ * Publishes data to Piano
19
19
  * @param {{collectionDomain: string, siteId: string|number}} config Publisher configuration
20
20
  * @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
21
  * @returns {*} Data returned by platform, if possible JSON-deserialized (otherwise as string)
@@ -0,0 +1,73 @@
1
+ const fetch = require('node-fetch');
2
+ const utils = require("../lib/utils");
3
+ const SOURCE = "f_UniversalAnalytics";
4
+
5
+ //https://cloud.google.com/appengine/docs/standard/php7/integrating-with-analytics#server-side_analytics_collection
6
+
7
+ module.exports = {
8
+ /**
9
+ * Processed data comming from EventHub to data for further processing
10
+ * @param {Object} data The data comming from EventHub
11
+ * @returns {object} Data for further processing
12
+ */
13
+ parse: function parse(data, context) {
14
+ //ConsumerBase.cs:ConsumeOneEventAsync
15
+ data.enqueuedAt = context.test ? 1600000000000 : Date.now();
16
+ return data;
17
+ },
18
+
19
+ /**
20
+ * Publishes data to Google Analytics
21
+ * @param {{tid: string}} config Publisher configuration
22
+ * @param {{action: string, ti: string, [key:string]: string|number|boolean}} data Data in publisher-friendly format to be published
23
+ */
24
+ publish: async function publish(config, data, event) {
25
+ data = utils.removeUndefined(data);
26
+ //https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#event
27
+ const url = "https://www.google-analytics.com/collect";
28
+ data.v = 1;
29
+ data.tid = config.trackingId;
30
+ let payload = "";
31
+
32
+ for (const prp in data) {
33
+ if (data.hasOwnProperty(prp)) {
34
+ if (payload) payload += "&";
35
+ payload += encodeURI(prp) + "=" + encodeURI(data[prp]);
36
+ }
37
+ }
38
+
39
+ const reqOpts = {
40
+ method: "post",
41
+ body: payload,
42
+ headers: {
43
+ 'Content-Type': 'application/x-www-form-urlencoded'
44
+ }
45
+ };
46
+
47
+ const resp = await fetch(url, reqOpts);
48
+
49
+ if (process.env.PUBLISHER_TESTURL) {
50
+ try {
51
+ utils.prepareTestRequestHeaders(reqOpts.headers, url, reqOpts, resp);
52
+ await fetch(process.env.PUBLISHER_TESTURL, reqOpts);
53
+ } catch { }
54
+ }
55
+
56
+ await utils.handleResponse(resp);
57
+
58
+ console.log(`Universal Analytics - ${event} - sent`);
59
+ },
60
+
61
+ /**
62
+ * Validates an object against given schema
63
+ * @param {Object} data An object to validate.
64
+ * @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
65
+ * @returns {boolean} Validation result, true if successful, false if unsuccessfull (in which case more info is written to console)
66
+ */
67
+ validate: function validate(data, schema) {
68
+ if (!schema) throw new Error("Schema parameters not provided");
69
+ if (schema.indexOf("/") >= 0) throw new Error("Schema must be provided as schema name, not schema path");
70
+ //More validation done by utils.validate
71
+ return utils.validate(data, SOURCE, schema);
72
+ }
73
+ };
package/tests/CJ.js ADDED
@@ -0,0 +1,98 @@
1
+ const publisher = require("../index.js").publisher.CJ;
2
+ const defaultPublisher = require("../index.js").DefaultPublisher;
3
+ const utils = require("../index.js").utils;
4
+ var assert = require('assert');
5
+
6
+ describe('test CJ publisher', function () {
7
+ it('purchase', async function () {
8
+ var eventHubMessages = [
9
+ {
10
+ "currencyCode": "CZK",
11
+ "event": {
12
+ "region": "MAIN",
13
+ "id": "115169112-4870.1657003407533",
14
+ "code": "purchase"
15
+ },
16
+ "session": {
17
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
18
+ "ip": "89.177.126.237",
19
+ "referrer": "https://crossmasters.com/"
20
+ },
21
+ "referrer": "https://crossmasters.com/",
22
+ "permissions": [],
23
+ "identity": {
24
+ "piano": {
25
+ "id": "1657003393413.319297501"
26
+ }
27
+ },
28
+ "publisher": {
29
+ "code": "mhubcloud.com",
30
+ "tm": "GTM-N7K9L4X",
31
+ "tmv": "494"
32
+ },
33
+ "$schema": "/mhub/purchase",
34
+ "id": "20162351",
35
+ "revenue": 440,
36
+ "products": [
37
+ {
38
+ "id": "Car",
39
+ "price": 440,
40
+ "quantity": 1
41
+ }
42
+ ],
43
+ "enqueuedAt": 1657003409370,
44
+ "ads": [{"adPlatform":"cjaffiliate","adCodes":{"cjevent":"816f5670dbf52c3dc27a7e849903c327d705ef643993fbab3"},"visitAt":1658392668000}]
45
+ }
46
+ ];
47
+ await test(eventHubMessages);
48
+ });
49
+ })
50
+
51
+ /**
52
+ * Test function
53
+ * @param [object] object obsahující nastavení testovací parametry
54
+ */
55
+ async function test(eventHubMessages) {
56
+ await defaultPublisher(console, eventHubMessages, "CJ", publisher, trigger);
57
+ }
58
+
59
+ function trigger(data) {
60
+ const publishers = [];
61
+ if (data.event.code === 'purchase') {
62
+ let publisher = CJPurchasePublisher;
63
+
64
+ publisher.register(publishers, { cid: 1, type: 1111111 });
65
+ }
66
+ return publishers;
67
+ };
68
+
69
+ class CJPurchasePublisher extends utils.EventPublisherBase {
70
+ /**
71
+ * Converts input data to publishing payload data understood by the publisher
72
+ * @param {Object} data `purchase` event data
73
+ * @returns {Object} Data mapped from incomming Event Hub message to platform payload
74
+ */
75
+ map(data) {
76
+ const ret = {
77
+ cjevent: utils.extractAdCode(data, 'cjaffiliate', 'cjevent')?.code,
78
+ cust_status: data.user?.ts?.indexOf("customer") >= 0 ? "return" : "new",
79
+ coupon: data.coupon,
80
+ discount: data.localDiscount,
81
+ method: "S2S",
82
+ currency: data.localCurrencyCode,
83
+ oid: data.id
84
+ // eventTime: "" // This parameters is ignored, as the time is determined by the CJ server
85
+ };
86
+ if (data.products instanceof Array) {
87
+ let i = 0;
88
+ for (const product of data.products) {
89
+ i++;
90
+ ret[`item${i}`] = product.id;
91
+ ret[`amt${i}`] = product.localPrice;
92
+ ret[`qty${i}`] = product.quantity;
93
+ }
94
+ }
95
+
96
+ return ret;
97
+ }
98
+ };
@@ -0,0 +1,118 @@
1
+ const publisher = require("../index.js").publisher.Facebook;
2
+ const defaultPublisher = require("../index.js").DefaultPublisher;
3
+ const utils = require("../index.js").utils;
4
+ var assert = require('assert');
5
+
6
+ describe('test Facebook publisher', function () {
7
+ it('purchase', async function () {
8
+ var eventHubMessages = [
9
+ {
10
+ "currencyCode": "CZK",
11
+ "event": {
12
+ "region": "MAIN",
13
+ "id": "115169112-4870.1657003407533",
14
+ "code": "purchase"
15
+ },
16
+ "session": {
17
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
18
+ "ip": "89.177.126.237",
19
+ "referrer": "https://crossmasters.com/"
20
+ },
21
+ "referrer": "https://crossmasters.com/",
22
+ "permissions": [],
23
+ "identity": {
24
+ "piano": {
25
+ "id": "1657003393413.319297501"
26
+ }
27
+ },
28
+ "publisher": {
29
+ "code": "mhubcloud.com",
30
+ "tm": "GTM-N7K9L4X",
31
+ "tmv": "494"
32
+ },
33
+ "$schema": "/mhub/purchase",
34
+ "id": "20162351",
35
+ "revenue": 440,
36
+ "products": [
37
+ {
38
+ "id": "Car",
39
+ "price": 440,
40
+ "quantity": 1
41
+ }
42
+ ],
43
+ "enqueuedAt": 1657003409370,
44
+ "ads": []
45
+ }
46
+ ];
47
+ await test(eventHubMessages);
48
+ });
49
+ })
50
+
51
+ /**
52
+ * Test function
53
+ * @param [object] object obsahující nastavení testovací parametry
54
+ */
55
+ async function test(eventHubMessages) {
56
+ try{
57
+ await defaultPublisher(console, eventHubMessages, "Facebook", publisher, trigger);
58
+ }catch(err){
59
+ assert.equal(err.toString().indexOf('Error: HTTP Error code: 400, event: purchase, body: {"error":{"message":"Invalid OAuth access token - Cannot parse access token","type":"OAuthException","code":190'), 0);
60
+ }
61
+ }
62
+
63
+ function trigger(data) {
64
+ const publishers = [];
65
+ let publisher;
66
+
67
+ //Dummy config, replace with your own
68
+ const config = {
69
+ id: "fffff",
70
+ access_token: "zzzz"
71
+ };
72
+
73
+ switch (data.event?.code) {
74
+ case "purchase":
75
+ publisher = FBPurchasePublisher;
76
+ publisher.register(publishers, config);
77
+ break;
78
+ }
79
+ return publishers;
80
+ };
81
+
82
+ class FBPurchasePublisher extends utils.EventPublisherBase {
83
+ /**
84
+ * Maps `purchase` event data to Facebook payload
85
+ * @param {Object} data `purchase` event data
86
+ * @returns {Object} Data mapped from incomming Event Hub message to platform payload
87
+ */
88
+ map(data) {
89
+ return {
90
+ event_name: "Purchase", // Constant
91
+ event_time: Math.floor(data.enqueuedAt / 1000), // convert to seconds
92
+ action_source: "website", // Constant
93
+ event_id: data.id, // e.g. OBJ1248035
94
+ event_source_url: data.location, // e.g. https://eshop.com/checkoutSuccess
95
+ opt_out: false, // Constant
96
+ user_data: {
97
+ client_user_agent: data.session?.userAgent, // e.g. Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit
98
+ fbc: utils.extractAdCode(data, 'facebook', 'fbclid', ad => `fb.1.${ad.visitAt}.${ad.code}`) || undefined, // e.g. fb.1.1623142020900.IwAR3CoXSdAr4nXyoVGo5H72wAiP8BrgPays0hhH1UgxnW_8D7rDhuHgjvvw0_aem_AXjjss7KoC0VSnBGvyUAkPip4VaZV6qytiIx5s8FQrAy5yzamZNRSC_xaCXEeJ8ecyphjuyQ2zbDAMoaIVayYAHxZgFH8TJsMzysJAIikY0How
99
+ fbp: data.identity?.facebook?.id, // e.g. fb.1.1609144040877.1929163682
100
+ client_ip_address: data.session?.ip, // e.g. 31.30.171.18
101
+ em: utils.encodeEmail(data.user?.email)
102
+ },
103
+ custom_data:
104
+ {
105
+ currency: data.currencyCode, // e.g. EUR
106
+ value: data.revenue, // e.g. 142.52
107
+ contents: data.products.map(pr => ({
108
+ id: pr.id, // e.g. PRDCT1248035
109
+ quantity: pr.quantity, // e.g. 1
110
+ item_price: pr.price // e.g. 109
111
+ })),
112
+ content_type: "product", // Constant
113
+ order_id: data.id, // e.g. OBJ1248035
114
+ content_category: "Purchase" // Constant
115
+ }
116
+ };
117
+ }
118
+ };
@@ -0,0 +1,91 @@
1
+ const publisher = require("../index.js").publisher.GoogleAnalytics4;
2
+ const defaultPublisher = require("../index.js").DefaultPublisher;
3
+ const utils = require("../index.js").utils;
4
+ var assert = require('assert');
5
+
6
+ describe('test GoogleAnalytics4 publisher', function () {
7
+ it('purchase', async function () {
8
+ var eventHubMessages = [
9
+ {
10
+ "currencyCode": "CZK",
11
+ "event": {
12
+ "region": "MAIN",
13
+ "id": "115169112-4870.1657003407533",
14
+ "code": "pageview"
15
+ },
16
+ "session": {
17
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
18
+ "ip": "89.177.126.237",
19
+ "referrer": "https://crossmasters.com/"
20
+ },
21
+ "referrer": "https://crossmasters.com/",
22
+ "permissions": [],
23
+ "identity": {
24
+ "piano": {
25
+ "id": "1657003393413.319297501"
26
+ }
27
+ },
28
+ "publisher": {
29
+ "code": "mhubcloud.com",
30
+ "tm": "GTM-N7K9L4X",
31
+ "tmv": "494"
32
+ },
33
+ "$schema": "/mhub/purchase",
34
+ "id": "20162351",
35
+ "revenue": 440,
36
+ "products": [
37
+ {
38
+ "id": "Car",
39
+ "price": 440,
40
+ "quantity": 1
41
+ }
42
+ ],
43
+ "enqueuedAt": 1657003409370,
44
+ "ads": []
45
+ }
46
+ ];
47
+ await test(eventHubMessages);
48
+ });
49
+ })
50
+
51
+ /**
52
+ * Test function
53
+ * @param [object] object obsahující nastavení testovací parametry
54
+ */
55
+ async function test(eventHubMessages) {
56
+ await defaultPublisher(console, eventHubMessages, "GoogleAnalytics4", publisher, trigger);
57
+ }
58
+
59
+ function trigger(data) {
60
+ const publishers = [];
61
+ const config = {trackingId: "G-xxxxxxxxxxx"};
62
+
63
+ if (data.event?.code === "pageview") {
64
+ const publisher = GAPageviewPublisher;
65
+ publisher.register(publishers, config);
66
+ }
67
+
68
+ return publishers;
69
+ };
70
+
71
+ class GAPageviewPublisher extends utils.EventPublisherBase {
72
+ /**
73
+ * Maps `pageview` event data to GA payload
74
+ */
75
+ map(data) {
76
+ return {
77
+ //mandatory for all events
78
+ cid: "456", //client id
79
+ en: "eventMhubCloud",//event name
80
+
81
+ //mandatory for pageview
82
+ dl: "/", //URL
83
+
84
+ //optional
85
+ //ul : "en-us",
86
+ //sr: "1536x864", //screan resolution
87
+ //dr: "ccc", //refferer
88
+ //dt: "xx" //page title
89
+ };
90
+ }
91
+ };
package/tests/Piano.js ADDED
@@ -0,0 +1,114 @@
1
+ const publisher = require("../index.js").publisher.Piano;
2
+ const defaultPublisher = require("../index.js").DefaultPublisher;
3
+ const utils = require("../index.js").utils;
4
+ var assert = require('assert');
5
+
6
+ describe('test Piano publisher', function () {
7
+ it('purchase', async function () {
8
+ var eventHubMessages = [
9
+ {
10
+ "currencyCode": "CZK",
11
+ "event": {
12
+ "region": "MAIN",
13
+ "id": "115169112-4870.1657003407533",
14
+ "code": "purchase"
15
+ },
16
+ "session": {
17
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
18
+ "ip": "89.177.126.237",
19
+ "referrer": "https://crossmasters.com/"
20
+ },
21
+ "referrer": "https://crossmasters.com/",
22
+ "permissions": [],
23
+ "identity": {
24
+ "piano": {
25
+ "id": "1657003393413.319297501"
26
+ }
27
+ },
28
+ "publisher": {
29
+ "code": "mhubcloud.com",
30
+ "tm": "GTM-N7K9L4X",
31
+ "tmv": "494"
32
+ },
33
+ "$schema": "/mhub/purchase",
34
+ "id": "20162351",
35
+ "revenue": 440,
36
+ "products": [
37
+ {
38
+ "id": "Car",
39
+ "price": 440,
40
+ "quantity": 1
41
+ }
42
+ ],
43
+ "enqueuedAt": 1657003409370,
44
+ "ads": []
45
+ }
46
+ ];
47
+ await test(eventHubMessages);
48
+ });
49
+ })
50
+
51
+ /**
52
+ * Test function
53
+ * @param [object] object obsahující nastavení testovací parametry
54
+ */
55
+ async function test(eventHubMessages) {
56
+ try{
57
+ await defaultPublisher(console, eventHubMessages, "Piano", publisher, trigger);
58
+ }catch(err){
59
+ assert.equal(err.toString(), "FetchError: request to https://xxxxx.xiti.com/?s=12345&idclient=1657003393413.319297501&ref=https://crossmasters.com/ failed, reason: getaddrinfo ENOTFOUND xxxxx.xiti.com");
60
+ }
61
+ }
62
+
63
+ function trigger(data) {
64
+ const publishers = [];
65
+ const config = {
66
+ collectionDomain: "xxxxx.xiti.com", //Secured collection domain
67
+ siteId: "12345" //site-id
68
+ }
69
+
70
+ if (data.event.code === "purchase") {
71
+ const publisher = PianoPurchasePublisher;
72
+ publisher.register(publishers, config);
73
+ }
74
+ return publishers;
75
+ };
76
+
77
+ class PianoPurchasePublisher extends utils.EventPublisherBase {
78
+ /**
79
+ * Converts input data to publishing payload data understood by the publisher
80
+ * @param {Object} data `purchase` event data
81
+ * @returns {Object} Data mapped from incomming Event Hub message to platform payload
82
+ */
83
+ map(data) {
84
+ return {
85
+ pianoData: { //This willbe sent to Piano as payload
86
+ events: [
87
+ {
88
+ name: "purchase",
89
+ data: {
90
+ event_collection_platform: "api",
91
+ event_collection_version: 2,
92
+ event_time_ts: Math.floor(data.enqueuedAt / 1000), // convert to seconds
93
+ event_id: data.id,
94
+ event_source_url: data.location
95
+ //https://developers.atinternet-solutions.com/piano-analytics-tagging-en/event-tagging-piano-analytics-en/events-pattern-en/#common-contextual-properties_9
96
+ //Any properties can go here (name must be [a-z][a-z_0-9]+; prohibited names m_…, visit_…, events)
97
+ //There are some specifically understood properties
98
+ //Type of property if not obvious from JSON can be forcec by prefix: 's:' string, 'n:' integer, 'f:' float, 'd:' date, 'b:' boolean, 'a:s:' array of strings, 'a:n:' array of integers, 'a:f:' array of floats
99
+ //or by suffix '_date', '_utc', '_ts' date in RFC 3339 format (YYYY-MM-DDTHH:mm:ssZ); '_timestamp' - timestamp in seconds
100
+ }
101
+ }
102
+ ]
103
+ },
104
+ aux: { //Additional data understood by Piano publisher and passed specificaly (in headers and query string)
105
+ visitorId: data.identity?.piano?.id, //Mandatory idclient=<visitor-id>
106
+ ref: data.referrer, //Mandatory ref=<previous-url>
107
+ //Optional:
108
+ userAgent: data.session?.userAgent,
109
+ referer: data.session?.referrer,
110
+ forwardedFor: data.session?.ip,
111
+ }
112
+ };
113
+ }
114
+ };
@@ -0,0 +1,94 @@
1
+ const publisher = require("../index.js").publisher.UniversalAnalytics;
2
+ const defaultPublisher = require("../index.js").DefaultPublisher;
3
+ const utils = require("../index.js").utils;
4
+ var assert = require('assert');
5
+
6
+ describe('test UniversalAnalytics publisher', function () {
7
+ it('pageview', async function () {
8
+ var eventHubMessages = [
9
+ {
10
+ "currencyCode": "CZK",
11
+ "event": {
12
+ "region": "MAIN",
13
+ "id": "115169112-4870.1657003407533",
14
+ "code": "pageview"
15
+ },
16
+ "session": {
17
+ "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36",
18
+ "ip": "89.177.126.237",
19
+ "referrer": "https://crossmasters.com/"
20
+ },
21
+ "referrer": "https://crossmasters.com/",
22
+ "permissions": [],
23
+ "identity": {
24
+ "piano": {
25
+ "id": "1657003393413.319297501"
26
+ }
27
+ },
28
+ "publisher": {
29
+ "code": "mhubcloud.com",
30
+ "tm": "GTM-N7K9L4X",
31
+ "tmv": "494"
32
+ },
33
+ "$schema": "/mhub/pageview",
34
+ "enqueuedAt": 1657003409370,
35
+ "ads": []
36
+ }
37
+ ];
38
+ await test(eventHubMessages);
39
+ });
40
+ })
41
+
42
+ /**
43
+ * Test function
44
+ * @param [object] object obsahující nastavení testovací parametry
45
+ */
46
+ async function test(eventHubMessages) {
47
+ await defaultPublisher(console, eventHubMessages, "UniversalAnalytics", publisher, trigger);
48
+ }
49
+
50
+ function trigger(data) {
51
+ const publishers = [];
52
+ const config = {trackingId: "UA-xxxxxxx-yy"};
53
+
54
+ if (data.event?.code === "pageview") {
55
+ const publisher = GAPageviewPublisher;
56
+ publisher.register(publishers, config);
57
+ }
58
+
59
+ return publishers;
60
+ };
61
+
62
+ class GAPageviewPublisher extends utils.EventPublisherBase {
63
+ /**
64
+ * Maps `pageview` event data to GA payload
65
+ */
66
+ map(data) {
67
+ /** @type {AdCodeInfo} */
68
+ let gclid = utils.extractAdCode(data, "googleads", "gclid");
69
+ /** @type {AdCodeInfo} */
70
+ let referrer = utils.extractAdCode(data, "referrerPlatform", "referrer");
71
+ /** @type {Ad} */
72
+ let utm = utils.extractAdCode(data, "googleAnalytics", "utm_source", (_, data) => data.adCodes ? data : null); //Only supports new format
73
+
74
+ //https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#event
75
+ //https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#dr
76
+ return {
77
+ //mandatory for all events
78
+ t : "pageview",
79
+ cid: "456", //TODO
80
+
81
+ //mandatory for pageview
82
+ dp: "/",
83
+
84
+ //Traffic Sources - optional
85
+ //TODO not tested
86
+ cn: utm?.adCodes?.utm_campaign,
87
+ cs: utm?.adCodes?.utm_source,
88
+ cm: utm?.adCodes?.utm_medium,
89
+ cc: utm?.adCodes?.utm_content,
90
+ gclid: gclid?.code,
91
+ dr: referrer?.code
92
+ };
93
+ }
94
+ };