ga4-export-fixer 0.8.0-dev.1 → 0.8.0-dev.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/README.md +703 -665
- package/package.json +3 -2
- package/tables/ga4EventsEnhanced/config.js +73 -70
- package/tables/ga4EventsEnhanced/index.js +23 -4
- package/tables/ga4EventsEnhanced/validation.js +234 -209
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ga4-export-fixer",
|
|
3
|
-
"version": "0.8.0-dev.
|
|
3
|
+
"version": "0.8.0-dev.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"createTable.js"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"test": "node tests/ga4EventsEnhanced.test.js && node tests/assertions.test.js && node tests/mergeSQLConfigurations.test.js && node tests/preOperations.test.js && node tests/documentation.test.js && node tests/inputValidation.test.js && node tests/createTable.test.js && node tests/queryBuilder.test.js",
|
|
20
|
+
"test": "node tests/ga4EventsEnhanced.test.js && node tests/assertions.test.js && node tests/mergeSQLConfigurations.test.js && node tests/preOperations.test.js && node tests/documentation.test.js && node tests/inputValidation.test.js && node tests/createTable.test.js && node tests/queryBuilder.test.js && node tests/customSteps.test.js",
|
|
21
21
|
"test:summary": "node tests/testRunner.js",
|
|
22
22
|
"test:docs": "node tests/documentation.test.js",
|
|
23
23
|
"test:preops": "node tests/preOperations.test.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"test:assertions": "node tests/assertions.test.js",
|
|
28
28
|
"test:createTable": "node tests/createTable.test.js",
|
|
29
29
|
"test:queryBuilder": "node tests/queryBuilder.test.js",
|
|
30
|
+
"test:customSteps": "node tests/customSteps.test.js",
|
|
30
31
|
"test:integration": "node tests/integration/integration.test.js",
|
|
31
32
|
"release:dev": "./scripts/release-dev.sh",
|
|
32
33
|
"readme": "node scripts/updateReadme.js",
|
|
@@ -1,70 +1,73 @@
|
|
|
1
|
-
const { baseConfig } = require('../../defaultConfig.js');
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
The default configuration for the GA4 Events Enhanced table.
|
|
5
|
-
*/
|
|
6
|
-
const ga4EventsEnhancedConfig = {
|
|
7
|
-
...baseConfig,
|
|
8
|
-
sourceTable: undefined,
|
|
9
|
-
sourceTableType: 'GA4_EXPORT', // used with pre operations to detect if ga4 export specific pre operations are needed
|
|
10
|
-
// optional but recommended
|
|
11
|
-
schemaLock: undefined,
|
|
12
|
-
// only used with js tables
|
|
13
|
-
dataformTableConfig: {
|
|
14
|
-
type: 'incremental',
|
|
15
|
-
bigquery: {
|
|
16
|
-
partitionBy: 'event_date',
|
|
17
|
-
clusterBy: ['event_name', 'session_id', 'page_location', 'data_is_final'],
|
|
18
|
-
labels: {
|
|
19
|
-
'ga4_export_fixer': 'true'
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
onSchemaChange: 'EXTEND',
|
|
23
|
-
tags: ['ga4_export_fixer'],
|
|
24
|
-
},
|
|
25
|
-
// optional
|
|
26
|
-
includedExportTypes: {
|
|
27
|
-
daily: true,
|
|
28
|
-
fresh: false,
|
|
29
|
-
intraday: true,
|
|
30
|
-
},
|
|
31
|
-
timezone: 'Etc/UTC',
|
|
32
|
-
customTimestampParam: undefined,
|
|
33
|
-
dataIsFinal: {
|
|
34
|
-
detectionMethod: 'DAY_THRESHOLD', // 'EXPORT_TYPE' or 'DAY_THRESHOLD'
|
|
35
|
-
dayThreshold: 3 // only used if detectionMethod is 'DAY_THRESHOLD'
|
|
36
|
-
// according to GA4 documentation, the data up to 72 hours old is subject to possible changes
|
|
37
|
-
// in reality, there have been cases where the data has changed even after 72 hours (4 day window would have covered these)
|
|
38
|
-
},
|
|
39
|
-
// optional item list attribution - disabled by default (compute-heavy, only useful for ecommerce sites)
|
|
40
|
-
itemListAttribution: undefined,
|
|
41
|
-
// number of additional days to take in for taking into account sessions that overlap days
|
|
42
|
-
bufferDays: 1,
|
|
43
|
-
// these parameters are excluded by default because they've been made available in other columns
|
|
44
|
-
defaultExcludedEventParams: [
|
|
45
|
-
'page_location',
|
|
46
|
-
'ga_session_id',
|
|
47
|
-
//'custom_event_timestamp', // removed if customTimestampParam is used
|
|
48
|
-
],
|
|
49
|
-
excludedEventParams: [],
|
|
50
|
-
eventParamsToColumns: [
|
|
51
|
-
//{name: 'page_location', type: 'string', columnName: 'page_location2'},
|
|
52
|
-
],
|
|
53
|
-
sessionParams: [],
|
|
54
|
-
defaultExcludedEvents: [],
|
|
55
|
-
// session_start and first_visit are excluded via the excludedEvents array
|
|
56
|
-
// this allows the user to include them if needed
|
|
57
|
-
excludedEvents: [
|
|
58
|
-
'session_start',
|
|
59
|
-
'first_visit'
|
|
60
|
-
],
|
|
61
|
-
defaultExcludedColumns: [
|
|
62
|
-
'event_dimensions', // legacy column, not needed
|
|
63
|
-
'traffic_source', // renamed to user_traffic_source
|
|
64
|
-
'session_id'
|
|
65
|
-
],
|
|
66
|
-
// exclude these columns when extracting raw data from the export tables
|
|
67
|
-
excludedColumns: [],
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
const { baseConfig } = require('../../defaultConfig.js');
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The default configuration for the GA4 Events Enhanced table.
|
|
5
|
+
*/
|
|
6
|
+
const ga4EventsEnhancedConfig = {
|
|
7
|
+
...baseConfig,
|
|
8
|
+
sourceTable: undefined,
|
|
9
|
+
sourceTableType: 'GA4_EXPORT', // used with pre operations to detect if ga4 export specific pre operations are needed
|
|
10
|
+
// optional but recommended
|
|
11
|
+
schemaLock: undefined,
|
|
12
|
+
// only used with js tables
|
|
13
|
+
dataformTableConfig: {
|
|
14
|
+
type: 'incremental',
|
|
15
|
+
bigquery: {
|
|
16
|
+
partitionBy: 'event_date',
|
|
17
|
+
clusterBy: ['event_name', 'session_id', 'page_location', 'data_is_final'],
|
|
18
|
+
labels: {
|
|
19
|
+
'ga4_export_fixer': 'true'
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
onSchemaChange: 'EXTEND',
|
|
23
|
+
tags: ['ga4_export_fixer'],
|
|
24
|
+
},
|
|
25
|
+
// optional
|
|
26
|
+
includedExportTypes: {
|
|
27
|
+
daily: true,
|
|
28
|
+
fresh: false,
|
|
29
|
+
intraday: true,
|
|
30
|
+
},
|
|
31
|
+
timezone: 'Etc/UTC',
|
|
32
|
+
customTimestampParam: undefined,
|
|
33
|
+
dataIsFinal: {
|
|
34
|
+
detectionMethod: 'DAY_THRESHOLD', // 'EXPORT_TYPE' or 'DAY_THRESHOLD'
|
|
35
|
+
dayThreshold: 3 // only used if detectionMethod is 'DAY_THRESHOLD'
|
|
36
|
+
// according to GA4 documentation, the data up to 72 hours old is subject to possible changes
|
|
37
|
+
// in reality, there have been cases where the data has changed even after 72 hours (4 day window would have covered these)
|
|
38
|
+
},
|
|
39
|
+
// optional item list attribution - disabled by default (compute-heavy, only useful for ecommerce sites)
|
|
40
|
+
itemListAttribution: undefined,
|
|
41
|
+
// number of additional days to take in for taking into account sessions that overlap days
|
|
42
|
+
bufferDays: 1,
|
|
43
|
+
// these parameters are excluded by default because they've been made available in other columns
|
|
44
|
+
defaultExcludedEventParams: [
|
|
45
|
+
'page_location',
|
|
46
|
+
'ga_session_id',
|
|
47
|
+
//'custom_event_timestamp', // removed if customTimestampParam is used
|
|
48
|
+
],
|
|
49
|
+
excludedEventParams: [],
|
|
50
|
+
eventParamsToColumns: [
|
|
51
|
+
//{name: 'page_location', type: 'string', columnName: 'page_location2'},
|
|
52
|
+
],
|
|
53
|
+
sessionParams: [],
|
|
54
|
+
defaultExcludedEvents: [],
|
|
55
|
+
// session_start and first_visit are excluded via the excludedEvents array
|
|
56
|
+
// this allows the user to include them if needed
|
|
57
|
+
excludedEvents: [
|
|
58
|
+
'session_start',
|
|
59
|
+
'first_visit'
|
|
60
|
+
],
|
|
61
|
+
defaultExcludedColumns: [
|
|
62
|
+
'event_dimensions', // legacy column, not needed
|
|
63
|
+
'traffic_source', // renamed to user_traffic_source
|
|
64
|
+
'session_id'
|
|
65
|
+
],
|
|
66
|
+
// exclude these columns when extracting raw data from the export tables
|
|
67
|
+
excludedColumns: [],
|
|
68
|
+
// user-defined CTEs appended to the pipeline after enhanced_events
|
|
69
|
+
// each entry is a queryBuilder step (raw {name, query} or structured {name, select, from, ...})
|
|
70
|
+
customSteps: [],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = { ga4EventsEnhancedConfig };
|
|
@@ -324,8 +324,9 @@ ${excludedEventsSQL}`,
|
|
|
324
324
|
const itemListExcludedColumns = itemListSteps ? ['_item_list_attribution_row_id'] : [];
|
|
325
325
|
|
|
326
326
|
// Join event_data and session_data, include additional logic
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
// Named 'enhanced_events' so user-supplied customSteps can reference it as a stable handle.
|
|
328
|
+
const enhancedEventsStep = {
|
|
329
|
+
name: 'enhanced_events',
|
|
329
330
|
select: {
|
|
330
331
|
columns: {
|
|
331
332
|
// get the most important columns in the correct order
|
|
@@ -371,13 +372,31 @@ ${excludedEventsSQL}`,
|
|
|
371
372
|
where: helpers.incrementalDateFilter(mergedConfig)
|
|
372
373
|
};
|
|
373
374
|
|
|
374
|
-
const
|
|
375
|
+
const packageSteps = [
|
|
375
376
|
eventDataStep,
|
|
376
377
|
...(itemListSteps ?? []),
|
|
377
378
|
sessionDataStep,
|
|
378
|
-
|
|
379
|
+
enhancedEventsStep,
|
|
379
380
|
];
|
|
380
381
|
|
|
382
|
+
// Layer 2 validation: customSteps name must not collide with package step names.
|
|
383
|
+
// Reserved set is derived from packageSteps at runtime (single source of truth) — what
|
|
384
|
+
// is reserved depends on config (e.g. item_list_* exist only when itemListAttribution is on).
|
|
385
|
+
const customSteps = mergedConfig.customSteps ?? [];
|
|
386
|
+
if (customSteps.length > 0) {
|
|
387
|
+
const reservedNames = new Set(packageSteps.map(s => s.name));
|
|
388
|
+
for (const [i, step] of customSteps.entries()) {
|
|
389
|
+
if (reservedNames.has(step.name)) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
`config.customSteps[${i}].name '${step.name}' collides with a reserved package CTE name. ` +
|
|
392
|
+
`Reserved names (active for this config): ${[...reservedNames].join(', ')}. Choose a different name.`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const steps = [...packageSteps, ...customSteps];
|
|
399
|
+
|
|
381
400
|
return utils.queryBuilder(steps);
|
|
382
401
|
};
|
|
383
402
|
|