ga4-export-fixer 0.6.1 → 0.6.2-dev.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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ The goal of the package is to **speed up development** when building data models
20
20
  - [Usage](#usage)
21
21
  - [Create GA4 Events Enhanced Table](#create-ga4-events-enhanced-table)
22
22
  - [Configuration Object](#configuration-object)
23
+ - [Assertions](#assertions)
23
24
  - [Creating Incremental Downstream Tables from ga4_events_enhanced](#creating-incremental-downstream-tables-from-ga4_events_enhanced)
24
25
  - [Helpers](#helpers)
25
26
  - [License](#license)
@@ -411,6 +412,7 @@ The boundary between fresh and intraday is timestamp-based because the fresh exp
411
412
  | `preOperations.incrementalEndOverride` | string (SQL date) | `undefined` | Override the incremental end date to re-process a specific range |
412
413
  | `preOperations.numberOfDaysToProcess` | integer | `undefined` | Limit each run to N days of data. When set, the end date becomes `start + N - 1` (capped at `current_date()`). When `undefined`, `dateRangeEnd` is used as-is. `incrementalEndOverride` takes priority |
413
414
 
415
+ Date fields (`dateRangeStart`, `dateRangeEnd`, etc.) accept string dates in `YYYYMMDD` or `YYYY-MM-DD` format, or BigQuery SQL expressions (e.g. `'current_date()'`, `'date(2026, 1, 1)'`).
414
416
 
415
417
  <a id="eventParamsToColumns"></a>
416
418
 
@@ -443,7 +445,64 @@ itemListAttribution: { lookbackType: 'TIME', lookbackTimeMs: 86400000 }
443
445
 
444
446
  > **Note:** This feature adds a compute-heavy CTE with a window function over unnested items. Only enable it if you need item list attribution for ecommerce analysis.
445
447
 
446
- Date fields (`dateRangeStart`, `dateRangeEnd`, etc.) accept string dates in `YYYYMMDD` or `YYYY-MM-DD` format, or BigQuery SQL expressions (e.g. `'current_date()'`, `'date(2026, 1, 1)'`).
448
+ ### Assertions
449
+
450
+ The package includes built-in data quality assertions that can be automatically created alongside the enhanced events table. Pass Dataform's `assert` function as the third argument to `createTable`:
451
+
452
+ ```javascript
453
+ ga4EventsEnhanced.createTable(publish, config, { assert });
454
+ ```
455
+
456
+ This creates the table along with the default-enabled assertions, using the same configuration:
457
+
458
+ | Assertion | Name | Enabled by default | Description |
459
+ | --------- | ---- | ------------------ | ----------- |
460
+ | `dailyQuality` | `{tableName}_daily_quality` | Yes | Compares session count, event count, and item revenue per day between the enhanced table and raw export. Detects missing days, count mismatches, and non-final data inflation |
461
+ | `itemRevenue` | `{tableName}_item_revenue` | No (opt-in) | Reconciles item_revenue at the (event_date, item_id) grain between the enhanced table and raw export |
462
+
463
+ Assertions inherit the table's schema and tags from `dataformTableConfig`. Each assertion queries the last 5 days of data.
464
+
465
+ #### Selective Assertions
466
+
467
+ Enable opt-in assertions by setting them to `true`, or disable default-enabled ones by setting them to `false`:
468
+
469
+ ```javascript
470
+ ga4EventsEnhanced.createTable(publish, config, {
471
+ assert,
472
+ assertions: { dailyQuality: true, itemRevenue: true },
473
+ });
474
+ ```
475
+
476
+ #### Assertion Config Overrides
477
+
478
+ Override the assertion's Dataform configuration (name, schema, tags):
479
+
480
+ ```javascript
481
+ ga4EventsEnhanced.createTable(publish, config, {
482
+ assert,
483
+ assertions: {
484
+ dailyQuality: { tags: ['data_quality', 'ga4_export_fixer'] },
485
+ },
486
+ });
487
+ ```
488
+
489
+ #### Standalone Assertions (SQLX Deployment)
490
+
491
+ For SQLX deployments or when you need full control, assertions can also be used as standalone SQL generators:
492
+
493
+ ```javascript
494
+ const { ga4EventsEnhanced } = require('ga4-export-fixer');
495
+
496
+ assert('daily_quality_check', {
497
+ schema: 'analytics_123456789',
498
+ tags: ['ga4_export_fixer'],
499
+ }).query(ctx => {
500
+ return ga4EventsEnhanced.assertions.dailyQuality(
501
+ ctx.ref('ga4_events_enhanced_123456789'),
502
+ { ...config, sourceTable: ctx.ref(config.sourceTable) }
503
+ );
504
+ });
505
+ ```
447
506
 
448
507
  ### Creating Incremental Downstream Tables from ga4_events_enhanced
449
508
 
package/createTable.js CHANGED
@@ -17,9 +17,15 @@ const preOperations = require('./preOperations.js');
17
17
  * @param {Function} tableModule.generateSql - (mergedConfig) => string.
18
18
  * @param {Function} tableModule.getColumnDescriptions - (mergedConfig) => Dataform columns object.
19
19
  * @param {Function} tableModule.getTableDescription - (mergedConfig) => string.
20
+ * @param {Object} [tableModule.assertions] - Optional assertion definitions keyed by name.
21
+ * Each value: { generate: (tableRef, mergedConfig) => string, defaultName: string }.
22
+ * @param {Object} [options] - Optional Dataform runtime options.
23
+ * @param {Function} [options.assert] - Dataform assert() function. When provided, creates assertions for the table.
24
+ * @param {Object} [options.assertions] - Per-assertion overrides. Set a key to false to disable,
25
+ * or to an object to override assertion Dataform config (name, schema, tags).
20
26
  * @returns {Object} The Dataform publish() object for the table.
21
27
  */
22
- const createTable = (dataformPublish, userConfig, tableModule) => {
28
+ const createTable = (dataformPublish, userConfig, tableModule, options) => {
23
29
  const mergedConfig = utils.mergeSQLConfigurations(tableModule.defaultConfig, userConfig);
24
30
  tableModule.validate(mergedConfig, { skipDataformContextFields: true });
25
31
 
@@ -48,11 +54,39 @@ const createTable = (dataformPublish, userConfig, tableModule) => {
48
54
  }
49
55
 
50
56
  // Create the table using Dataform publish()
51
- return dataformPublish(dataformTableConfig.name, dataformTableConfig).preOps(ctx => {
57
+ const tableResult = dataformPublish(dataformTableConfig.name, dataformTableConfig).preOps(ctx => {
52
58
  return preOperations.setPreOperations(utils.setDataformContext(ctx, mergedConfig));
53
59
  }).query(ctx => {
54
60
  return tableModule.generateSql(utils.setDataformContext(ctx, mergedConfig));
55
61
  });
62
+
63
+ // Create assertions when options.assert is provided and the table module defines assertions
64
+ if (options?.assert && tableModule.assertions) {
65
+ const tableName = dataformTableConfig.name;
66
+
67
+ for (const [key, assertionDef] of Object.entries(tableModule.assertions)) {
68
+ const assertionOption = options.assertions?.[key];
69
+ if (assertionOption === false) continue;
70
+ if (assertionOption === undefined && assertionDef.enabledByDefault === false) continue;
71
+
72
+ const assertionName = `${tableName}_${assertionDef.defaultName}`;
73
+ const assertionDataformConfig = {
74
+ schema: dataformTableConfig.schema,
75
+ tags: dataformTableConfig.tags || [],
76
+ ...(typeof assertionOption === 'object' ? assertionOption : {}),
77
+ };
78
+
79
+ options.assert(assertionDataformConfig.name || assertionName, assertionDataformConfig).query(ctx => {
80
+ const resolvedConfig = { ...mergedConfig };
81
+ if (utils.isDataformTableReferenceObject(resolvedConfig.sourceTable)) {
82
+ resolvedConfig.sourceTable = ctx.ref(resolvedConfig.sourceTable);
83
+ }
84
+ return assertionDef.generate(ctx.ref(tableName), resolvedConfig);
85
+ });
86
+ }
87
+ }
88
+
89
+ return tableResult;
56
90
  };
57
91
 
58
92
  module.exports = { createTable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ga4-export-fixer",
3
- "version": "0.6.1",
3
+ "version": "0.6.2-dev.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -160,4 +160,4 @@ const generateDailyQualityAssertionSql = (tableRef, config) => {
160
160
  return _generateDailyQualityAssertionSql(tableRef, mergedConfig);
161
161
  };
162
162
 
163
- module.exports = { generateDailyQualityAssertionSql };
163
+ module.exports = { generateDailyQualityAssertionSql, _generateDailyQualityAssertionSql };
@@ -1,7 +1,11 @@
1
- const { generateItemRevenueAssertionSql } = require('./itemRevenue.js');
2
- const { generateDailyQualityAssertionSql } = require('./dailyQuality.js');
1
+ const { generateItemRevenueAssertionSql, _generateItemRevenueAssertionSql } = require('./itemRevenue.js');
2
+ const { generateDailyQualityAssertionSql, _generateDailyQualityAssertionSql } = require('./dailyQuality.js');
3
3
 
4
4
  module.exports = {
5
5
  itemRevenue: generateItemRevenueAssertionSql,
6
6
  dailyQuality: generateDailyQualityAssertionSql,
7
+ _internal: {
8
+ dailyQuality: { generate: _generateDailyQualityAssertionSql, defaultName: 'daily_quality' },
9
+ itemRevenue: { generate: _generateItemRevenueAssertionSql, defaultName: 'item_revenue', enabledByDefault: false },
10
+ },
7
11
  };
@@ -146,4 +146,4 @@ const generateItemRevenueAssertionSql = (tableRef, config) => {
146
146
  return _generateItemRevenueAssertionSql(tableRef, mergedConfig);
147
147
  };
148
148
 
149
- module.exports = { generateItemRevenueAssertionSql };
149
+ module.exports = { generateItemRevenueAssertionSql, _generateItemRevenueAssertionSql };
@@ -410,10 +410,11 @@ const tableModule = {
410
410
  generateSql: _generateEnhancedEventsSQL,
411
411
  getColumnDescriptions: (config) => documentation.getColumnDescriptions(config, columnMetadata),
412
412
  getTableDescription: (config) => documentation.buildTableDescription(config, getTableDescriptionSections(config)),
413
+ assertions: assertions._internal,
413
414
  };
414
415
 
415
- const createEnhancedEventsTable = (dataformPublish, config) => {
416
- return createTable(dataformPublish, config, tableModule);
416
+ const createEnhancedEventsTable = (dataformPublish, config, options) => {
417
+ return createTable(dataformPublish, config, tableModule, options);
417
418
  };
418
419
 
419
420
  // Exported wrapper: merge config, validate, then delegate to preOperations module