@simplybusiness/services 0.14.0 → 0.14.2
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/CHANGELOG.md +13 -0
- package/dist/cjs/mocks/scripts-mock.js +3 -3
- package/dist/cjs/mocks/scripts-mock.js.map +1 -1
- package/dist/cjs/snowplow/Snowplow.js +9 -0
- package/dist/cjs/snowplow/Snowplow.js.map +1 -1
- package/dist/cjs/snowplow/SnowplowContext.js +5 -5
- package/dist/cjs/snowplow/SnowplowContext.js.map +1 -1
- package/dist/cjs/snowplow/contexts.js +5 -4
- package/dist/cjs/snowplow/contexts.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/isObject.js +15 -0
- package/dist/cjs/utils/isObject.js.map +1 -0
- package/dist/cjs/utils/text.js +35 -3
- package/dist/cjs/utils/text.js.map +1 -1
- package/dist/esm/mocks/scripts-mock.js +3 -3
- package/dist/esm/mocks/scripts-mock.js.map +1 -1
- package/dist/esm/snowplow/Snowplow.js +9 -0
- package/dist/esm/snowplow/Snowplow.js.map +1 -1
- package/dist/esm/snowplow/SnowplowContext.js +6 -6
- package/dist/esm/snowplow/SnowplowContext.js.map +1 -1
- package/dist/esm/snowplow/contexts.js +5 -4
- package/dist/esm/snowplow/contexts.js.map +1 -1
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/isObject.js +5 -0
- package/dist/esm/utils/isObject.js.map +1 -0
- package/dist/esm/utils/text.js +22 -0
- package/dist/esm/utils/text.js.map +1 -1
- package/dist/types/snowplow/Snowplow.d.ts +2 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/isObject.d.ts +1 -0
- package/dist/types/utils/isObject.test.d.ts +1 -0
- package/dist/types/utils/text.d.ts +2 -0
- package/package.json +1 -1
- package/src/mocks/scripts-mock.ts +3 -3
- package/src/snowplow/Snowplow.ts +11 -0
- package/src/snowplow/SnowplowContext.tsx +6 -5
- package/src/snowplow/contexts.test.ts +3 -3
- package/src/snowplow/contexts.ts +3 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/isObject.test.tsx +35 -0
- package/src/utils/isObject.tsx +8 -0
- package/src/utils/text.test.ts +60 -1
- package/src/utils/text.ts +39 -0
package/dist/cjs/utils/text.js
CHANGED
|
@@ -2,12 +2,44 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
camelToSnakeCase: function() {
|
|
13
|
+
return camelToSnakeCase;
|
|
14
|
+
},
|
|
15
|
+
snakeCase: function() {
|
|
8
16
|
return snakeCase;
|
|
17
|
+
},
|
|
18
|
+
snakeCaseKeys: function() {
|
|
19
|
+
return snakeCaseKeys;
|
|
9
20
|
}
|
|
10
21
|
});
|
|
22
|
+
const _ = require(".");
|
|
23
|
+
const snakeCaseKeys = (object)=>Object.entries(object || {}).reduce((acc, [key, value])=>{
|
|
24
|
+
const newKey = camelToSnakeCase(key);
|
|
25
|
+
if (Array.isArray(value) && value.every(_.isObject)) {
|
|
26
|
+
return {
|
|
27
|
+
...acc,
|
|
28
|
+
[newKey]: value.map((v)=>snakeCaseKeys(v))
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if ((0, _.isObject)(value)) {
|
|
32
|
+
return {
|
|
33
|
+
...acc,
|
|
34
|
+
[newKey]: snakeCaseKeys(value)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
...acc,
|
|
39
|
+
[newKey]: value
|
|
40
|
+
};
|
|
41
|
+
}, {});
|
|
11
42
|
const snakeCase = (text = "")=>text.toLowerCase().replace(/ /g, "_");
|
|
43
|
+
const camelToSnakeCase = (text)=>text.charAt(0).toLowerCase() + text.slice(1).replace(/(\[.*?\])|[A-Z]/g, (match, group)=>group ? match : `_${match.toLowerCase()}`);
|
|
12
44
|
|
|
13
45
|
//# sourceMappingURL=text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/text.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/text.ts"],"sourcesContent":["import { isObject } from \".\";\n\n// Deeply converts keys in an object to snake_case\nexport const snakeCaseKeys = (object: Record<string, unknown>) =>\n Object.entries(object || {}).reduce(\n (acc: Record<string, unknown>, [key, value]): Record<string, unknown> => {\n const newKey = camelToSnakeCase(key);\n\n if (Array.isArray(value) && value.every(isObject)) {\n return {\n ...acc,\n [newKey]: value.map(v => snakeCaseKeys(v)),\n };\n }\n\n if (isObject(value)) {\n return {\n ...acc,\n [newKey]: snakeCaseKeys(value),\n };\n }\n\n return {\n ...acc,\n [newKey]: value,\n };\n },\n {},\n );\n\nexport const snakeCase = (text = \"\"): string =>\n text.toLowerCase().replace(/ /g, \"_\");\n\nexport const camelToSnakeCase = (text: string) =>\n text.charAt(0).toLowerCase() +\n text\n .slice(1)\n .replace(/(\\[.*?\\])|[A-Z]/g, (match, group) =>\n group ? match : `_${match.toLowerCase()}`,\n );\n\n"],"names":["camelToSnakeCase","snakeCase","snakeCaseKeys","object","Object","entries","reduce","acc","key","value","newKey","Array","isArray","every","isObject","map","v","text","toLowerCase","replace","charAt","slice","match","group"],"mappings":";;;;;;;;;;;IAiCaA,gBAAgB;eAAhBA;;IAHAC,SAAS;eAATA;;IA3BAC,aAAa;eAAbA;;;kBAHY;AAGlB,MAAMA,gBAAgB,CAACC,SAC5BC,OAAOC,OAAO,CAACF,UAAU,CAAC,GAAGG,MAAM,CACjC,CAACC,KAA8B,CAACC,KAAKC,MAAM;QACzC,MAAMC,SAASV,iBAAiBQ;QAEhC,IAAIG,MAAMC,OAAO,CAACH,UAAUA,MAAMI,KAAK,CAACC,UAAQ,GAAG;YACjD,OAAO;gBACL,GAAGP,GAAG;gBACN,CAACG,OAAO,EAAED,MAAMM,GAAG,CAACC,CAAAA,IAAKd,cAAcc;YACzC;QACF;QAEA,IAAIF,IAAAA,UAAQ,EAACL,QAAQ;YACnB,OAAO;gBACL,GAAGF,GAAG;gBACN,CAACG,OAAO,EAAER,cAAcO;YAC1B;QACF;QAEA,OAAO;YACL,GAAGF,GAAG;YACN,CAACG,OAAO,EAAED;QACZ;IACF,GACA,CAAC;AAGE,MAAMR,YAAY,CAACgB,OAAO,EAAE,GACjCA,KAAKC,WAAW,GAAGC,OAAO,CAAC,MAAM;AAE5B,MAAMnB,mBAAmB,CAACiB,OAC/BA,KAAKG,MAAM,CAAC,GAAGF,WAAW,KAC1BD,KACGI,KAAK,CAAC,GACNF,OAAO,CAAC,oBAAoB,CAACG,OAAOC,QACnCA,QAAQD,QAAQ,CAAC,CAAC,EAAEA,MAAMJ,WAAW,IAAI"}
|
|
@@ -16,7 +16,7 @@ export const pageData = {
|
|
|
16
16
|
trackActivity: true,
|
|
17
17
|
trackPageView: true,
|
|
18
18
|
pageViewContext: {
|
|
19
|
-
schema: "
|
|
19
|
+
schema: "iglu:uk.co.simplybusiness/journey_context/jsonschema/1-0-0",
|
|
20
20
|
data: {
|
|
21
21
|
site: "simplybusiness_us",
|
|
22
22
|
vertical: "usa",
|
|
@@ -29,13 +29,13 @@ export const pageData = {
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
distributionChannelContext: {
|
|
32
|
-
schema: "
|
|
32
|
+
schema: "iglu:com.simplybusiness/distribution_channel_context/jsonschema/1-0-0",
|
|
33
33
|
data: {
|
|
34
34
|
service_channel_identifier: "simplybusiness_us"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
serviceChannelContext: {
|
|
38
|
-
schema: "
|
|
38
|
+
schema: "iglu:com.simplybusiness/service_channel_context/jsonschema/1-0-0",
|
|
39
39
|
data: {
|
|
40
40
|
service_channel_identifier: "simplybusiness_us"
|
|
41
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/mocks/scripts-mock.ts"],"sourcesContent":["export const pageData = {\n scripts: [\n {\n metadata: { name: \"snowplow\" },\n props: {\n uid: \"49a449d8aaa9dd58f90a623d4b9dbcae235bf92a\",\n cookieDomain: \"\",\n // TODO: Change this url to \"http://localhost:8000\" for local development\n avalancheCollector:\n \"https://snowplow-collector-staging.simplybusiness.com\",\n appId: \"us-chopin\",\n includeGAContext: true,\n eventMethod: \"post\",\n postPath: \"/com.simplybusiness/events\",\n trackActivity: true,\n trackPageView: true,\n pageViewContext: {\n schema: \"
|
|
1
|
+
{"version":3,"sources":["../../../src/mocks/scripts-mock.ts"],"sourcesContent":["export const pageData = {\n scripts: [\n {\n metadata: { name: \"snowplow\" },\n props: {\n uid: \"49a449d8aaa9dd58f90a623d4b9dbcae235bf92a\",\n cookieDomain: \"\",\n // TODO: Change this url to \"http://localhost:8000\" for local development\n avalancheCollector:\n \"https://snowplow-collector-staging.simplybusiness.com\",\n appId: \"us-chopin\",\n includeGAContext: true,\n eventMethod: \"post\",\n postPath: \"/com.simplybusiness/events\",\n trackActivity: true,\n trackPageView: true,\n pageViewContext: {\n schema: \"iglu:uk.co.simplybusiness/journey_context/jsonschema/1-0-0\",\n data: {\n site: \"simplybusiness_us\",\n vertical: \"usa\",\n super_segment: \"Unknown\",\n primary_detail: \"Lawn care services\",\n journey_name: \"usa\",\n journey_id: \"666ff79d90abbc3582e496da\",\n page_step_name: \"thank_you_ssr\",\n page_step_depth: -1,\n },\n },\n distributionChannelContext: {\n schema:\n \"iglu:com.simplybusiness/distribution_channel_context/jsonschema/1-0-0\",\n data: { service_channel_identifier: \"simplybusiness_us\" },\n },\n serviceChannelContext: {\n schema:\n \"iglu:com.simplybusiness/service_channel_context/jsonschema/1-0-0\",\n data: { service_channel_identifier: \"simplybusiness_us\" },\n },\n forceSecureTracker: true,\n },\n },\n ],\n};\n"],"names":["pageData","scripts","metadata","name","props","uid","cookieDomain","avalancheCollector","appId","includeGAContext","eventMethod","postPath","trackActivity","trackPageView","pageViewContext","schema","data","site","vertical","super_segment","primary_detail","journey_name","journey_id","page_step_name","page_step_depth","distributionChannelContext","service_channel_identifier","serviceChannelContext","forceSecureTracker"],"mappings":"AAAA,OAAO,MAAMA,WAAW;IACtBC,SAAS;QACP;YACEC,UAAU;gBAAEC,MAAM;YAAW;YAC7BC,OAAO;gBACLC,KAAK;gBACLC,cAAc;gBACd,yEAAyE;gBACzEC,oBACE;gBACFC,OAAO;gBACPC,kBAAkB;gBAClBC,aAAa;gBACbC,UAAU;gBACVC,eAAe;gBACfC,eAAe;gBACfC,iBAAiB;oBACfC,QAAQ;oBACRC,MAAM;wBACJC,MAAM;wBACNC,UAAU;wBACVC,eAAe;wBACfC,gBAAgB;wBAChBC,cAAc;wBACdC,YAAY;wBACZC,gBAAgB;wBAChBC,iBAAiB,CAAC;oBACpB;gBACF;gBACAC,4BAA4B;oBAC1BV,QACE;oBACFC,MAAM;wBAAEU,4BAA4B;oBAAoB;gBAC1D;gBACAC,uBAAuB;oBACrBZ,QACE;oBACFC,MAAM;wBAAEU,4BAA4B;oBAAoB;gBAC1D;gBACAE,oBAAoB;YACtB;QACF;KACD;AACH,EAAE"}
|
|
@@ -100,6 +100,12 @@ import { newTracker, setCookiePath, setUserId, trackPageView, trackSelfDescribin
|
|
|
100
100
|
}
|
|
101
101
|
return this;
|
|
102
102
|
}
|
|
103
|
+
static getInstance(props) {
|
|
104
|
+
if (!Snowplow.instance) {
|
|
105
|
+
Snowplow.instance = new Snowplow(props);
|
|
106
|
+
}
|
|
107
|
+
return Snowplow.instance;
|
|
108
|
+
}
|
|
103
109
|
constructor(props){
|
|
104
110
|
var _props_pageViewContext;
|
|
105
111
|
_define_property(this, "avalancheTrackerName", "sb-ava");
|
|
@@ -138,7 +144,10 @@ import { newTracker, setCookiePath, setUserId, trackPageView, trackSelfDescribin
|
|
|
138
144
|
if (uid) {
|
|
139
145
|
setUserId(uid);
|
|
140
146
|
}
|
|
147
|
+
// Create a singleton instance
|
|
148
|
+
Snowplow.instance = this;
|
|
141
149
|
}
|
|
142
150
|
}
|
|
151
|
+
_define_property(Snowplow, "instance", void 0);
|
|
143
152
|
|
|
144
153
|
//# sourceMappingURL=Snowplow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/snowplow/Snowplow.ts"],"sourcesContent":["import {\n SelfDescribingJson,\n StructuredEvent,\n TrackerConfiguration,\n newTracker,\n setCookiePath,\n setUserId,\n trackPageView,\n trackSelfDescribingEvent,\n trackStructEvent,\n} from \"@snowplow/browser-tracker\";\nimport { EventDefinition, TrackingProps } from \"./types\";\n\nexport type FrontOfficeStructuredEvent = StructuredEvent & {\n serviceChannelIdentifier: string;\n};\n\n/**\n * This class is an abstraction which wraps Snowplow\n * and exposes common methods with other services:\n * - trackEvent : sends a standard payload\n * - trackUnstructEvent : sends a payload for custom schema\n */\nexport class Snowplow {\n avalancheTrackerName = \"sb-ava\";\n\n bronzeAvalancheTrackerName = \"sb-ava-br\";\n\n pvAvalancheTrackerName = \"sb-ava-pv\";\n\n uid: unknown = \"\";\n\n trackPageView: boolean = false;\n\n contexts: SelfDescribingJson<Record<string, unknown>>[] = [];\n\n serverData: Record<string, unknown> = {};\n\n eventHandlers: Record<string, (params?: Record<string, unknown>) => void> =\n {};\n\n constructor(props?: TrackingProps) {\n if (!props) return;\n\n const {\n appId,\n cookieDomain,\n avalancheCollector,\n eventMethod,\n uid,\n postPath,\n // includeGAContext,\n // trackActivity,\n trackPageView: tpv,\n } = props;\n this.uid = uid;\n this.trackPageView = tpv;\n this.serverData = props?.pageViewContext?.data || {};\n\n // Set options\n const stateStorageStrategy = \"cookieAndLocalStorage\";\n const baseOptions: TrackerConfiguration = {\n appId,\n cookieDomain,\n eventMethod,\n stateStorageStrategy,\n postPath,\n };\n // Initialize trackers\n newTracker(this.avalancheTrackerName, avalancheCollector, baseOptions);\n\n newTracker(\n this.bronzeAvalancheTrackerName,\n avalancheCollector,\n baseOptions,\n );\n\n // Page view tracker\n newTracker(this.pvAvalancheTrackerName, avalancheCollector, {\n ...baseOptions,\n eventMethod: eventMethod === \"post\" ? \"beacon\" : eventMethod,\n });\n\n setCookiePath(\"/\");\n if (uid) {\n setUserId(uid);\n }\n }\n\n setContexts(contexts: SelfDescribingJson<Record<string, unknown>>[]) {\n this.contexts = contexts;\n // Update identity context\n const index = this.contexts?.findIndex(ctx =>\n ctx.schema?.includes(\"identity_context\"),\n );\n if (index > -1) {\n this.contexts[index].data.domain_userid = this.uid;\n }\n return this;\n }\n\n // Send a page view event\n trackView() {\n if (this.trackPageView) {\n trackPageView({ context: this.contexts });\n }\n return this;\n }\n\n // Send a structured event with contexts\n async trackEvent(event: StructuredEvent) {\n await trackStructEvent({ ...event, context: this.contexts }, [\n this.bronzeAvalancheTrackerName,\n ]);\n return this;\n }\n\n // Send a custom event with defined schema and optional contexts\n async trackUnstructEvent(event: SelfDescribingJson<Record<string, unknown>>) {\n if (!event) {\n return this;\n }\n await trackSelfDescribingEvent({ event, context: this.contexts }, [\n this.avalancheTrackerName,\n ]);\n return this;\n }\n\n addEventHandlers(eventDefinitions: EventDefinition[]) {\n // Add server context to makePayload functions\n const context = this.serverData;\n\n eventDefinitions.forEach(({ name, type, makePayload }) => {\n // Convert type into relevant function\n if (type === \"structured\") {\n this.addEventHandler(name, (params?: Record<string, unknown>) => {\n this.trackEvent(\n makePayload({\n ...params,\n context,\n }) as StructuredEvent,\n );\n });\n } else {\n this.addEventHandler(name, (params?: Record<string, unknown>) => {\n this.trackUnstructEvent(\n makePayload({\n ...params,\n context,\n }) as SelfDescribingJson<Record<string, unknown>>,\n );\n });\n }\n });\n return this;\n }\n\n private addEventHandler(\n name: string,\n handler: (params?: Record<string, unknown>) => void,\n ) {\n this.eventHandlers[name] = handler;\n return this;\n }\n\n private removeEventHandler(name: string) {\n delete this.eventHandlers[name];\n return this;\n }\n\n trigger(name: string, params?: Record<string, unknown>) {\n if (this.eventHandlers[name]) {\n this.eventHandlers[name](params);\n }\n return this;\n }\n}\n"],"names":["newTracker","setCookiePath","setUserId","trackPageView","trackSelfDescribingEvent","trackStructEvent","Snowplow","setContexts","contexts","index","findIndex","ctx","schema","includes","data","domain_userid","uid","trackView","context","trackEvent","event","bronzeAvalancheTrackerName","trackUnstructEvent","avalancheTrackerName","addEventHandlers","eventDefinitions","serverData","forEach","name","type","makePayload","addEventHandler","params","handler","eventHandlers","removeEventHandler","trigger","
|
|
1
|
+
{"version":3,"sources":["../../../src/snowplow/Snowplow.ts"],"sourcesContent":["import {\n SelfDescribingJson,\n StructuredEvent,\n TrackerConfiguration,\n newTracker,\n setCookiePath,\n setUserId,\n trackPageView,\n trackSelfDescribingEvent,\n trackStructEvent,\n} from \"@snowplow/browser-tracker\";\nimport { EventDefinition, TrackingProps } from \"./types\";\n\nexport type FrontOfficeStructuredEvent = StructuredEvent & {\n serviceChannelIdentifier: string;\n};\n\n/**\n * This class is an abstraction which wraps Snowplow\n * and exposes common methods with other services:\n * - trackEvent : sends a standard payload\n * - trackUnstructEvent : sends a payload for custom schema\n */\nexport class Snowplow {\n avalancheTrackerName = \"sb-ava\";\n\n bronzeAvalancheTrackerName = \"sb-ava-br\";\n\n pvAvalancheTrackerName = \"sb-ava-pv\";\n\n uid: unknown = \"\";\n\n trackPageView: boolean = false;\n\n contexts: SelfDescribingJson<Record<string, unknown>>[] = [];\n\n serverData: Record<string, unknown> = {};\n\n eventHandlers: Record<string, (params?: Record<string, unknown>) => void> =\n {};\n\n static instance: Snowplow | undefined;\n\n constructor(props?: TrackingProps) {\n if (!props) return;\n\n const {\n appId,\n cookieDomain,\n avalancheCollector,\n eventMethod,\n uid,\n postPath,\n // includeGAContext,\n // trackActivity,\n trackPageView: tpv,\n } = props;\n this.uid = uid;\n this.trackPageView = tpv;\n this.serverData = props?.pageViewContext?.data || {};\n\n // Set options\n const stateStorageStrategy = \"cookieAndLocalStorage\";\n const baseOptions: TrackerConfiguration = {\n appId,\n cookieDomain,\n eventMethod,\n stateStorageStrategy,\n postPath,\n };\n // Initialize trackers\n newTracker(this.avalancheTrackerName, avalancheCollector, baseOptions);\n\n newTracker(\n this.bronzeAvalancheTrackerName,\n avalancheCollector,\n baseOptions,\n );\n\n // Page view tracker\n newTracker(this.pvAvalancheTrackerName, avalancheCollector, {\n ...baseOptions,\n eventMethod: eventMethod === \"post\" ? \"beacon\" : eventMethod,\n });\n\n setCookiePath(\"/\");\n if (uid) {\n setUserId(uid);\n }\n // Create a singleton instance\n Snowplow.instance = this;\n }\n\n setContexts(contexts: SelfDescribingJson<Record<string, unknown>>[]) {\n this.contexts = contexts;\n // Update identity context\n const index = this.contexts?.findIndex(ctx =>\n ctx.schema?.includes(\"identity_context\"),\n );\n if (index > -1) {\n this.contexts[index].data.domain_userid = this.uid;\n }\n return this;\n }\n\n // Send a page view event\n trackView() {\n if (this.trackPageView) {\n trackPageView({ context: this.contexts });\n }\n return this;\n }\n\n // Send a structured event with contexts\n async trackEvent(event: StructuredEvent) {\n await trackStructEvent({ ...event, context: this.contexts }, [\n this.bronzeAvalancheTrackerName,\n ]);\n return this;\n }\n\n // Send a custom event with defined schema and optional contexts\n async trackUnstructEvent(event: SelfDescribingJson<Record<string, unknown>>) {\n if (!event) {\n return this;\n }\n await trackSelfDescribingEvent({ event, context: this.contexts }, [\n this.avalancheTrackerName,\n ]);\n return this;\n }\n\n addEventHandlers(eventDefinitions: EventDefinition[]) {\n // Add server context to makePayload functions\n const context = this.serverData;\n\n eventDefinitions.forEach(({ name, type, makePayload }) => {\n // Convert type into relevant function\n if (type === \"structured\") {\n this.addEventHandler(name, (params?: Record<string, unknown>) => {\n this.trackEvent(\n makePayload({\n ...params,\n context,\n }) as StructuredEvent,\n );\n });\n } else {\n this.addEventHandler(name, (params?: Record<string, unknown>) => {\n this.trackUnstructEvent(\n makePayload({\n ...params,\n context,\n }) as SelfDescribingJson<Record<string, unknown>>,\n );\n });\n }\n });\n return this;\n }\n\n private addEventHandler(\n name: string,\n handler: (params?: Record<string, unknown>) => void,\n ) {\n this.eventHandlers[name] = handler;\n return this;\n }\n\n private removeEventHandler(name: string) {\n delete this.eventHandlers[name];\n return this;\n }\n\n trigger(name: string, params?: Record<string, unknown>) {\n if (this.eventHandlers[name]) {\n this.eventHandlers[name](params);\n }\n return this;\n }\n\n static getInstance(props?: TrackingProps) {\n if (!Snowplow.instance) {\n Snowplow.instance = new Snowplow(props);\n }\n return Snowplow.instance;\n }\n}\n"],"names":["newTracker","setCookiePath","setUserId","trackPageView","trackSelfDescribingEvent","trackStructEvent","Snowplow","setContexts","contexts","index","findIndex","ctx","schema","includes","data","domain_userid","uid","trackView","context","trackEvent","event","bronzeAvalancheTrackerName","trackUnstructEvent","avalancheTrackerName","addEventHandlers","eventDefinitions","serverData","forEach","name","type","makePayload","addEventHandler","params","handler","eventHandlers","removeEventHandler","trigger","getInstance","props","instance","constructor","pvAvalancheTrackerName","appId","cookieDomain","avalancheCollector","eventMethod","postPath","tpv","pageViewContext","stateStorageStrategy","baseOptions"],"mappings":"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"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */ import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { getContexts } from "./contexts";
|
|
4
4
|
import { eventDefinitions } from "./event-definitions";
|
|
5
5
|
import { Snowplow } from "./Snowplow";
|
|
6
6
|
const SnowplowContext = /*#__PURE__*/ createContext(null);
|
|
7
7
|
export const SnowplowProvider = ({ scripts, children })=>{
|
|
8
8
|
const [config, _setConfig] = useState(scripts);
|
|
9
|
-
const
|
|
9
|
+
const snowplow = useRef(Snowplow.getInstance(config));
|
|
10
10
|
// Attach event handlers and set contexts
|
|
11
11
|
useEffect(()=>{
|
|
12
|
-
if (snowplow && scripts) {
|
|
12
|
+
if (snowplow.current && scripts) {
|
|
13
13
|
const contexts = getContexts(config);
|
|
14
|
-
snowplow.setContexts(contexts).addEventHandlers(eventDefinitions);
|
|
14
|
+
snowplow.current.setContexts(contexts).addEventHandlers(eventDefinitions);
|
|
15
15
|
// Send page view event
|
|
16
|
-
if (config.trackPageView) snowplow.trackView();
|
|
16
|
+
if (config.trackPageView) snowplow.current.trackView();
|
|
17
17
|
}
|
|
18
18
|
}, [
|
|
19
19
|
config,
|
|
@@ -22,7 +22,7 @@ export const SnowplowProvider = ({ scripts, children })=>{
|
|
|
22
22
|
]);
|
|
23
23
|
const value = useMemo(()=>({
|
|
24
24
|
config,
|
|
25
|
-
snowplow
|
|
25
|
+
snowplow: snowplow.current
|
|
26
26
|
}), [
|
|
27
27
|
config,
|
|
28
28
|
snowplow
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/snowplow/SnowplowContext.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport { getContexts } from \"./contexts\";\nimport { eventDefinitions } from \"./event-definitions\";\nimport { Snowplow } from \"./Snowplow\";\nimport { EventDefinition, TrackingProps } from \"./types\";\n\nexport interface SnowplowContextInterface {\n config: TrackingProps;\n snowplow?: Snowplow;\n}\n\nconst SnowplowContext = createContext<SnowplowContextInterface | null>(null);\n\ntype ProviderProps = {\n scripts: TrackingProps;\n children: ReactNode;\n};\n\nexport const SnowplowProvider = ({ scripts, children }: ProviderProps) => {\n const [config, _setConfig] = useState<TrackingProps>(scripts);\n const
|
|
1
|
+
{"version":3,"sources":["../../../src/snowplow/SnowplowContext.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { getContexts } from \"./contexts\";\nimport { eventDefinitions } from \"./event-definitions\";\nimport { Snowplow } from \"./Snowplow\";\nimport { EventDefinition, TrackingProps } from \"./types\";\n\nexport interface SnowplowContextInterface {\n config: TrackingProps;\n snowplow?: Snowplow;\n}\n\nconst SnowplowContext = createContext<SnowplowContextInterface | null>(null);\n\ntype ProviderProps = {\n scripts: TrackingProps;\n children: ReactNode;\n};\n\nexport const SnowplowProvider = ({ scripts, children }: ProviderProps) => {\n const [config, _setConfig] = useState<TrackingProps>(scripts);\n const snowplow = useRef<Snowplow>(Snowplow.getInstance(config));\n\n // Attach event handlers and set contexts\n useEffect(() => {\n if (snowplow.current && scripts) {\n const contexts = getContexts(config);\n\n snowplow.current\n .setContexts(contexts)\n .addEventHandlers(eventDefinitions as EventDefinition[]);\n // Send page view event\n if (config.trackPageView) snowplow.current.trackView();\n }\n }, [config, snowplow, scripts]);\n\n const value: SnowplowContextInterface = useMemo(\n () => ({\n config,\n snowplow: snowplow.current,\n }),\n [config, snowplow],\n );\n\n return (\n <SnowplowContext.Provider value={value}>\n {children}\n </SnowplowContext.Provider>\n );\n};\n\nexport function useSnowplowContext() {\n const context = useContext(SnowplowContext);\n\n if (!context) {\n throw new Error(\n \"useSnowplowContext must be used inside a `SnowplowProvider`\",\n );\n }\n return context;\n}\n"],"names":["createContext","useContext","useEffect","useMemo","useRef","useState","getContexts","eventDefinitions","Snowplow","SnowplowContext","SnowplowProvider","scripts","children","config","_setConfig","snowplow","getInstance","current","contexts","setContexts","addEventHandlers","trackPageView","trackView","value","Provider","useSnowplowContext","context","Error"],"mappings":"AAAA,oDAAoD;AACpD,SACEA,aAAa,EACbC,UAAU,EACVC,SAAS,EACTC,OAAO,EACPC,MAAM,EACNC,QAAQ,QAEH,QAAQ;AACf,SAASC,WAAW,QAAQ,aAAa;AACzC,SAASC,gBAAgB,QAAQ,sBAAsB;AACvD,SAASC,QAAQ,QAAQ,aAAa;AAQtC,MAAMC,gCAAkBT,cAA+C;AAOvE,OAAO,MAAMU,mBAAmB,CAAC,EAAEC,OAAO,EAAEC,QAAQ,EAAiB;IACnE,MAAM,CAACC,QAAQC,WAAW,GAAGT,SAAwBM;IACrD,MAAMI,WAAWX,OAAiBI,SAASQ,WAAW,CAACH;IAEvD,yCAAyC;IACzCX,UAAU;QACR,IAAIa,SAASE,OAAO,IAAIN,SAAS;YAC/B,MAAMO,WAAWZ,YAAYO;YAE7BE,SAASE,OAAO,CACbE,WAAW,CAACD,UACZE,gBAAgB,CAACb;YACpB,uBAAuB;YACvB,IAAIM,OAAOQ,aAAa,EAAEN,SAASE,OAAO,CAACK,SAAS;QACtD;IACF,GAAG;QAACT;QAAQE;QAAUJ;KAAQ;IAE9B,MAAMY,QAAkCpB,QACtC,IAAO,CAAA;YACLU;YACAE,UAAUA,SAASE,OAAO;QAC5B,CAAA,GACA;QAACJ;QAAQE;KAAS;IAGpB,qBACE,KAACN,gBAAgBe,QAAQ;QAACD,OAAOA;kBAC9BX;;AAGP,EAAE;AAEF,OAAO,SAASa;IACd,MAAMC,UAAUzB,WAAWQ;IAE3B,IAAI,CAACiB,SAAS;QACZ,MAAM,IAAIC,MACR;IAEJ;IACA,OAAOD;AACT"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { snakeCaseKeys } from "../utils";
|
|
1
2
|
export const getContexts = (config)=>{
|
|
2
3
|
const contexts = config && Object.entries(config).filter(([key])=>key.includes("Context") && key !== "includeGAContext").map(([_key, value])=>{
|
|
3
4
|
if (typeof value === "object") {
|
|
4
|
-
return {
|
|
5
|
+
return snakeCaseKeys({
|
|
5
6
|
...value
|
|
6
|
-
};
|
|
7
|
+
});
|
|
7
8
|
}
|
|
8
|
-
return {
|
|
9
|
+
return snakeCaseKeys({
|
|
9
10
|
data: {
|
|
10
11
|
service_channel_identifier: value
|
|
11
12
|
}
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
});
|
|
14
15
|
return contexts;
|
|
15
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/snowplow/contexts.ts"],"sourcesContent":["import { SelfDescribingJson } from \"@snowplow/browser-tracker\";\nimport { TrackingProps } from \"./types\";\n\nexport const getContexts = (\n config: TrackingProps,\n): SelfDescribingJson<Record<string, unknown>>[] => {\n const contexts =\n config &&\n Object.entries(config)\n .filter(([key]) => key.includes(\"Context\") && key !== \"includeGAContext\")\n .map(([_key, value]) => {\n if (typeof value === \"object\") {\n return { ...value };\n }\n return { data: { service_channel_identifier: value } };\n });\n\n return contexts as unknown as SelfDescribingJson<Record<string, unknown>>[];\n};\n"],"names":["getContexts","config","contexts","Object","entries","filter","key","includes","map","_key","value","data","service_channel_identifier"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/snowplow/contexts.ts"],"sourcesContent":["import { SelfDescribingJson } from \"@snowplow/browser-tracker\";\nimport { TrackingProps } from \"./types\";\nimport { snakeCaseKeys } from \"../utils\";\n\nexport const getContexts = (\n config: TrackingProps,\n): SelfDescribingJson<Record<string, unknown>>[] => {\n const contexts =\n config &&\n Object.entries(config)\n .filter(([key]) => key.includes(\"Context\") && key !== \"includeGAContext\")\n .map(([_key, value]) => {\n if (typeof value === \"object\") {\n return snakeCaseKeys(({ ...value }));\n }\n return snakeCaseKeys({ data: { service_channel_identifier: value } });\n });\n\n return contexts as unknown as SelfDescribingJson<Record<string, unknown>>[];\n};\n"],"names":["snakeCaseKeys","getContexts","config","contexts","Object","entries","filter","key","includes","map","_key","value","data","service_channel_identifier"],"mappings":"AAEA,SAASA,aAAa,QAAQ,WAAW;AAEzC,OAAO,MAAMC,cAAc,CACzBC;IAEA,MAAMC,WACJD,UACAE,OAAOC,OAAO,CAACH,QACZI,MAAM,CAAC,CAAC,CAACC,IAAI,GAAKA,IAAIC,QAAQ,CAAC,cAAcD,QAAQ,oBACrDE,GAAG,CAAC,CAAC,CAACC,MAAMC,MAAM;QACjB,IAAI,OAAOA,UAAU,UAAU;YAC7B,OAAOX,cAAe;gBAAE,GAAGW,KAAK;YAAC;QACnC;QACA,OAAOX,cAAc;YAAEY,MAAM;gBAAEC,4BAA4BF;YAAM;QAAE;IACrE;IAEJ,OAAOR;AACT,EAAE"}
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["export * from \"./text\";\n"],"names":[],"mappings":"AAAA,cAAc,SAAS"}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/index.ts"],"sourcesContent":["export * from \"./text\";\nexport * from \"./isObject\";\n"],"names":[],"mappings":"AAAA,cAAc,SAAS;AACvB,cAAc,aAAa"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/isObject.tsx"],"sourcesContent":["export function isObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) === Object.prototype\n );\n }\n"],"names":["isObject","value","Array","isArray","Object","getPrototypeOf","prototype"],"mappings":"AAAA,OAAO,SAASA,SAASC,KAAc;IACnC,OACE,OAAOA,UAAU,YACjBA,UAAU,QACV,CAACC,MAAMC,OAAO,CAACF,UACfG,OAAOC,cAAc,CAACJ,WAAWG,OAAOE,SAAS;AAErD"}
|
package/dist/esm/utils/text.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
import { isObject } from ".";
|
|
2
|
+
// Deeply converts keys in an object to snake_case
|
|
3
|
+
export const snakeCaseKeys = (object)=>Object.entries(object || {}).reduce((acc, [key, value])=>{
|
|
4
|
+
const newKey = camelToSnakeCase(key);
|
|
5
|
+
if (Array.isArray(value) && value.every(isObject)) {
|
|
6
|
+
return {
|
|
7
|
+
...acc,
|
|
8
|
+
[newKey]: value.map((v)=>snakeCaseKeys(v))
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
if (isObject(value)) {
|
|
12
|
+
return {
|
|
13
|
+
...acc,
|
|
14
|
+
[newKey]: snakeCaseKeys(value)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
...acc,
|
|
19
|
+
[newKey]: value
|
|
20
|
+
};
|
|
21
|
+
}, {});
|
|
1
22
|
export const snakeCase = (text = "")=>text.toLowerCase().replace(/ /g, "_");
|
|
23
|
+
export const camelToSnakeCase = (text)=>text.charAt(0).toLowerCase() + text.slice(1).replace(/(\[.*?\])|[A-Z]/g, (match, group)=>group ? match : `_${match.toLowerCase()}`);
|
|
2
24
|
|
|
3
25
|
//# sourceMappingURL=text.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/text.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/text.ts"],"sourcesContent":["import { isObject } from \".\";\n\n// Deeply converts keys in an object to snake_case\nexport const snakeCaseKeys = (object: Record<string, unknown>) =>\n Object.entries(object || {}).reduce(\n (acc: Record<string, unknown>, [key, value]): Record<string, unknown> => {\n const newKey = camelToSnakeCase(key);\n\n if (Array.isArray(value) && value.every(isObject)) {\n return {\n ...acc,\n [newKey]: value.map(v => snakeCaseKeys(v)),\n };\n }\n\n if (isObject(value)) {\n return {\n ...acc,\n [newKey]: snakeCaseKeys(value),\n };\n }\n\n return {\n ...acc,\n [newKey]: value,\n };\n },\n {},\n );\n\nexport const snakeCase = (text = \"\"): string =>\n text.toLowerCase().replace(/ /g, \"_\");\n\nexport const camelToSnakeCase = (text: string) =>\n text.charAt(0).toLowerCase() +\n text\n .slice(1)\n .replace(/(\\[.*?\\])|[A-Z]/g, (match, group) =>\n group ? match : `_${match.toLowerCase()}`,\n );\n\n"],"names":["isObject","snakeCaseKeys","object","Object","entries","reduce","acc","key","value","newKey","camelToSnakeCase","Array","isArray","every","map","v","snakeCase","text","toLowerCase","replace","charAt","slice","match","group"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,IAAI;AAE7B,kDAAkD;AAClD,OAAO,MAAMC,gBAAgB,CAACC,SAC5BC,OAAOC,OAAO,CAACF,UAAU,CAAC,GAAGG,MAAM,CACjC,CAACC,KAA8B,CAACC,KAAKC,MAAM;QACzC,MAAMC,SAASC,iBAAiBH;QAEhC,IAAII,MAAMC,OAAO,CAACJ,UAAUA,MAAMK,KAAK,CAACb,WAAW;YACjD,OAAO;gBACL,GAAGM,GAAG;gBACN,CAACG,OAAO,EAAED,MAAMM,GAAG,CAACC,CAAAA,IAAKd,cAAcc;YACzC;QACF;QAEA,IAAIf,SAASQ,QAAQ;YACnB,OAAO;gBACL,GAAGF,GAAG;gBACN,CAACG,OAAO,EAAER,cAAcO;YAC1B;QACF;QAEA,OAAO;YACL,GAAGF,GAAG;YACN,CAACG,OAAO,EAAED;QACZ;IACF,GACA,CAAC,GACD;AAEJ,OAAO,MAAMQ,YAAY,CAACC,OAAO,EAAE,GACjCA,KAAKC,WAAW,GAAGC,OAAO,CAAC,MAAM,KAAK;AAExC,OAAO,MAAMT,mBAAmB,CAACO,OAC/BA,KAAKG,MAAM,CAAC,GAAGF,WAAW,KAC1BD,KACGI,KAAK,CAAC,GACNF,OAAO,CAAC,oBAAoB,CAACG,OAAOC,QACnCA,QAAQD,QAAQ,CAAC,CAAC,EAAEA,MAAMJ,WAAW,IAAI,EACzC"}
|
|
@@ -18,6 +18,7 @@ export declare class Snowplow {
|
|
|
18
18
|
contexts: SelfDescribingJson<Record<string, unknown>>[];
|
|
19
19
|
serverData: Record<string, unknown>;
|
|
20
20
|
eventHandlers: Record<string, (params?: Record<string, unknown>) => void>;
|
|
21
|
+
static instance: Snowplow | undefined;
|
|
21
22
|
constructor(props?: TrackingProps);
|
|
22
23
|
setContexts(contexts: SelfDescribingJson<Record<string, unknown>>[]): this;
|
|
23
24
|
trackView(): this;
|
|
@@ -27,4 +28,5 @@ export declare class Snowplow {
|
|
|
27
28
|
private addEventHandler;
|
|
28
29
|
private removeEventHandler;
|
|
29
30
|
trigger(name: string, params?: Record<string, unknown>): this;
|
|
31
|
+
static getInstance(props?: TrackingProps): Snowplow;
|
|
30
32
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isObject(value: unknown): value is Record<string, unknown>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ export const pageData = {
|
|
|
15
15
|
trackActivity: true,
|
|
16
16
|
trackPageView: true,
|
|
17
17
|
pageViewContext: {
|
|
18
|
-
schema: "
|
|
18
|
+
schema: "iglu:uk.co.simplybusiness/journey_context/jsonschema/1-0-0",
|
|
19
19
|
data: {
|
|
20
20
|
site: "simplybusiness_us",
|
|
21
21
|
vertical: "usa",
|
|
@@ -29,12 +29,12 @@ export const pageData = {
|
|
|
29
29
|
},
|
|
30
30
|
distributionChannelContext: {
|
|
31
31
|
schema:
|
|
32
|
-
"
|
|
32
|
+
"iglu:com.simplybusiness/distribution_channel_context/jsonschema/1-0-0",
|
|
33
33
|
data: { service_channel_identifier: "simplybusiness_us" },
|
|
34
34
|
},
|
|
35
35
|
serviceChannelContext: {
|
|
36
36
|
schema:
|
|
37
|
-
"
|
|
37
|
+
"iglu:com.simplybusiness/service_channel_context/jsonschema/1-0-0",
|
|
38
38
|
data: { service_channel_identifier: "simplybusiness_us" },
|
|
39
39
|
},
|
|
40
40
|
forceSecureTracker: true,
|
package/src/snowplow/Snowplow.ts
CHANGED
|
@@ -39,6 +39,8 @@ export class Snowplow {
|
|
|
39
39
|
eventHandlers: Record<string, (params?: Record<string, unknown>) => void> =
|
|
40
40
|
{};
|
|
41
41
|
|
|
42
|
+
static instance: Snowplow | undefined;
|
|
43
|
+
|
|
42
44
|
constructor(props?: TrackingProps) {
|
|
43
45
|
if (!props) return;
|
|
44
46
|
|
|
@@ -85,6 +87,8 @@ export class Snowplow {
|
|
|
85
87
|
if (uid) {
|
|
86
88
|
setUserId(uid);
|
|
87
89
|
}
|
|
90
|
+
// Create a singleton instance
|
|
91
|
+
Snowplow.instance = this;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
setContexts(contexts: SelfDescribingJson<Record<string, unknown>>[]) {
|
|
@@ -174,4 +178,11 @@ export class Snowplow {
|
|
|
174
178
|
}
|
|
175
179
|
return this;
|
|
176
180
|
}
|
|
181
|
+
|
|
182
|
+
static getInstance(props?: TrackingProps) {
|
|
183
|
+
if (!Snowplow.instance) {
|
|
184
|
+
Snowplow.instance = new Snowplow(props);
|
|
185
|
+
}
|
|
186
|
+
return Snowplow.instance;
|
|
187
|
+
}
|
|
177
188
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
useContext,
|
|
5
5
|
useEffect,
|
|
6
6
|
useMemo,
|
|
7
|
+
useRef,
|
|
7
8
|
useState,
|
|
8
9
|
type ReactNode,
|
|
9
10
|
} from "react";
|
|
@@ -26,25 +27,25 @@ type ProviderProps = {
|
|
|
26
27
|
|
|
27
28
|
export const SnowplowProvider = ({ scripts, children }: ProviderProps) => {
|
|
28
29
|
const [config, _setConfig] = useState<TrackingProps>(scripts);
|
|
29
|
-
const
|
|
30
|
+
const snowplow = useRef<Snowplow>(Snowplow.getInstance(config));
|
|
30
31
|
|
|
31
32
|
// Attach event handlers and set contexts
|
|
32
33
|
useEffect(() => {
|
|
33
|
-
if (snowplow && scripts) {
|
|
34
|
+
if (snowplow.current && scripts) {
|
|
34
35
|
const contexts = getContexts(config);
|
|
35
36
|
|
|
36
|
-
snowplow
|
|
37
|
+
snowplow.current
|
|
37
38
|
.setContexts(contexts)
|
|
38
39
|
.addEventHandlers(eventDefinitions as EventDefinition[]);
|
|
39
40
|
// Send page view event
|
|
40
|
-
if (config.trackPageView) snowplow.trackView();
|
|
41
|
+
if (config.trackPageView) snowplow.current.trackView();
|
|
41
42
|
}
|
|
42
43
|
}, [config, snowplow, scripts]);
|
|
43
44
|
|
|
44
45
|
const value: SnowplowContextInterface = useMemo(
|
|
45
46
|
() => ({
|
|
46
47
|
config,
|
|
47
|
-
snowplow,
|
|
48
|
+
snowplow: snowplow.current,
|
|
48
49
|
}),
|
|
49
50
|
[config, snowplow],
|
|
50
51
|
);
|
|
@@ -10,7 +10,7 @@ describe("Snowplow Contexts", () => {
|
|
|
10
10
|
|
|
11
11
|
expect(contexts).toHaveLength(3);
|
|
12
12
|
expect(contexts[0]).toEqual({
|
|
13
|
-
schema: "
|
|
13
|
+
schema: "iglu:uk.co.simplybusiness/journey_context/jsonschema/1-0-0",
|
|
14
14
|
data: {
|
|
15
15
|
site: "simplybusiness_us",
|
|
16
16
|
vertical: "usa",
|
|
@@ -24,11 +24,11 @@ describe("Snowplow Contexts", () => {
|
|
|
24
24
|
});
|
|
25
25
|
expect(contexts[1]).toEqual({
|
|
26
26
|
schema:
|
|
27
|
-
"
|
|
27
|
+
"iglu:com.simplybusiness/distribution_channel_context/jsonschema/1-0-0",
|
|
28
28
|
data: { service_channel_identifier: "simplybusiness_us" },
|
|
29
29
|
});
|
|
30
30
|
expect(contexts[2]).toEqual({
|
|
31
|
-
schema: "
|
|
31
|
+
schema: "iglu:com.simplybusiness/service_channel_context/jsonschema/1-0-0",
|
|
32
32
|
data: { service_channel_identifier: "simplybusiness_us" },
|
|
33
33
|
});
|
|
34
34
|
});
|
package/src/snowplow/contexts.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SelfDescribingJson } from "@snowplow/browser-tracker";
|
|
2
2
|
import { TrackingProps } from "./types";
|
|
3
|
+
import { snakeCaseKeys } from "../utils";
|
|
3
4
|
|
|
4
5
|
export const getContexts = (
|
|
5
6
|
config: TrackingProps,
|
|
@@ -10,9 +11,9 @@ export const getContexts = (
|
|
|
10
11
|
.filter(([key]) => key.includes("Context") && key !== "includeGAContext")
|
|
11
12
|
.map(([_key, value]) => {
|
|
12
13
|
if (typeof value === "object") {
|
|
13
|
-
return { ...value };
|
|
14
|
+
return snakeCaseKeys(({ ...value }));
|
|
14
15
|
}
|
|
15
|
-
return { data: { service_channel_identifier: value } };
|
|
16
|
+
return snakeCaseKeys({ data: { service_channel_identifier: value } });
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
return contexts as unknown as SelfDescribingJson<Record<string, unknown>>[];
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isObject } from "./isObject";
|
|
2
|
+
|
|
3
|
+
describe("isObject", () => {
|
|
4
|
+
it("should return true for plain objects", () => {
|
|
5
|
+
expect(isObject({})).toBeTruthy();
|
|
6
|
+
expect(isObject({ key: "value" })).toBeTruthy();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return false for arrays", () => {
|
|
10
|
+
expect(isObject([])).toBeFalsy();
|
|
11
|
+
expect(isObject([1, 2, 3])).toBeFalsy();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return false for null", () => {
|
|
15
|
+
expect(isObject(null)).toBeFalsy();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should return false for non-object types", () => {
|
|
19
|
+
expect(isObject("string")).toBeFalsy();
|
|
20
|
+
expect(isObject(123)).toBeFalsy();
|
|
21
|
+
expect(isObject(true)).toBeFalsy();
|
|
22
|
+
expect(isObject(undefined)).toBeFalsy();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should return false for instances of classes", () => {
|
|
26
|
+
class MyClass {}
|
|
27
|
+
const instance = new MyClass();
|
|
28
|
+
expect(isObject(instance)).toBeFalsy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should return false for objects with different prototypes", () => {
|
|
32
|
+
const obj = Object.create(null);
|
|
33
|
+
expect(isObject(obj)).toBeFalsy();
|
|
34
|
+
});
|
|
35
|
+
});
|
package/src/utils/text.test.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
camelToSnakeCase,
|
|
3
|
+
snakeCase,
|
|
4
|
+
snakeCaseKeys,
|
|
5
|
+
} from "./text";
|
|
2
6
|
|
|
3
7
|
describe("text utils", () => {
|
|
4
8
|
test("snakeCase", () => {
|
|
@@ -6,4 +10,59 @@ describe("text utils", () => {
|
|
|
6
10
|
expect(snakeCase("Hello")).toBe("hello");
|
|
7
11
|
expect(snakeCase("")).toBe("");
|
|
8
12
|
});
|
|
13
|
+
|
|
14
|
+
test("camelToSnakeCase", () => {
|
|
15
|
+
expect(camelToSnakeCase("")).toBe("");
|
|
16
|
+
expect(camelToSnakeCase("helloWorld")).toBe("hello_world");
|
|
17
|
+
expect(camelToSnakeCase("coverTogglesData")).toBe("cover_toggles_data");
|
|
18
|
+
expect(camelToSnakeCase("hello")).toBe("hello");
|
|
19
|
+
expect(camelToSnakeCase("Address 1")).toBe("address 1");
|
|
20
|
+
expect(camelToSnakeCase("uk_address_lookup[Address 2]")).toBe(
|
|
21
|
+
"uk_address_lookup[Address 2]",
|
|
22
|
+
);
|
|
23
|
+
expect(
|
|
24
|
+
camelToSnakeCase("uk_correspondence_address_lookup[Customer[address_2]]"),
|
|
25
|
+
).toBe("uk_correspondence_address_lookup[Customer[address_2]]");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("snakeCaseKeys", () => {
|
|
29
|
+
expect(
|
|
30
|
+
snakeCaseKeys({
|
|
31
|
+
authToken: "1234",
|
|
32
|
+
coverTogglesData: {
|
|
33
|
+
fromValue: "f",
|
|
34
|
+
toValue: "t",
|
|
35
|
+
},
|
|
36
|
+
hello: "world",
|
|
37
|
+
}),
|
|
38
|
+
).toEqual({
|
|
39
|
+
auth_token: "1234",
|
|
40
|
+
cover_toggles_data: {
|
|
41
|
+
from_value: "f",
|
|
42
|
+
to_value: "t",
|
|
43
|
+
},
|
|
44
|
+
hello: "world",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// With Arrays
|
|
48
|
+
expect(
|
|
49
|
+
snakeCaseKeys({
|
|
50
|
+
authToken: "1234",
|
|
51
|
+
coverTogglesData: [
|
|
52
|
+
{
|
|
53
|
+
fromValue: "f",
|
|
54
|
+
toValue: "t",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
}),
|
|
58
|
+
).toEqual({
|
|
59
|
+
auth_token: "1234",
|
|
60
|
+
cover_toggles_data: [
|
|
61
|
+
{
|
|
62
|
+
from_value: "f",
|
|
63
|
+
to_value: "t",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
9
68
|
});
|