ga4-export-fixer 0.8.0 → 0.9.0-dev.10
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 +129 -8
- package/documentation.js +272 -223
- package/helpers/ga4Transforms.js +315 -262
- package/package.json +8 -5
- package/tables/ga4EventsEnhanced/config.js +4 -0
- package/tables/ga4EventsEnhanced/index.js +216 -100
- package/tables/ga4EventsEnhanced/validation.js +99 -4
- package/utils.js +163 -26
package/documentation.js
CHANGED
|
@@ -1,223 +1,272 @@
|
|
|
1
|
-
const constants = require('./constants');
|
|
2
|
-
const { version } = require('./package.json');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Composes a multi-section column description string from individual sections.
|
|
6
|
-
* Sections with null/undefined/empty values are omitted.
|
|
7
|
-
* Sections are separated by line breaks for readability.
|
|
8
|
-
*
|
|
9
|
-
* @param {Object} sections - { base, lineage, typicalUse, config }
|
|
10
|
-
* @returns {string} Composed description
|
|
11
|
-
*/
|
|
12
|
-
const composeDescription = (sections) => {
|
|
13
|
-
const parts = [];
|
|
14
|
-
|
|
15
|
-
if (sections.base) {
|
|
16
|
-
parts.push(sections.base);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (sections.lineage) {
|
|
20
|
-
parts.push(`Lineage: ${sections.lineage}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (sections.typicalUse) {
|
|
24
|
-
parts.push(`Typical use: ${sections.typicalUse}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (sections.config) {
|
|
28
|
-
parts.push(`Config: ${sections.config}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return parts.join('\n\n');
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Returns a formatted lineage text string for a column, or null if no lineage data exists.
|
|
36
|
-
*
|
|
37
|
-
* @param {string} columnName - The column name to look up.
|
|
38
|
-
* @param {Object} columnLineage - The lineage data object mapping column names to { source, note }.
|
|
39
|
-
* @returns {string|null} Formatted lineage string, e.g. "Derived -- Concatenation of ..."
|
|
40
|
-
*/
|
|
41
|
-
const getLineageText = (columnName, columnLineage) => {
|
|
42
|
-
const entry = columnLineage[columnName];
|
|
43
|
-
if (!entry) return null;
|
|
44
|
-
|
|
45
|
-
const sourceLabels = {
|
|
46
|
-
'ga4_export': 'Standard GA4 export field',
|
|
47
|
-
'ga4_export_modified': 'GA4 export field (modified)',
|
|
48
|
-
'derived': 'Derived',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const label = sourceLabels[entry.source] || entry.source;
|
|
52
|
-
return entry.note ? `${label} -- ${entry.note}` : label;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Builds a map of config-specific notes for columns based on the provided configuration.
|
|
57
|
-
* Extracts the configuration-dependent description suffixes into a { columnName: "note" } map.
|
|
58
|
-
*
|
|
59
|
-
* @param {Object} config - The merged configuration object.
|
|
60
|
-
* @returns {Object} Map of column names to config note strings.
|
|
61
|
-
*/
|
|
62
|
-
const buildConfigNotes = (config) => {
|
|
63
|
-
const notes = {};
|
|
64
|
-
|
|
65
|
-
if (!config) return notes;
|
|
66
|
-
|
|
67
|
-
const append = (key, text) => {
|
|
68
|
-
notes[key] = notes[key] ? `${notes[key]}. ${text}` : text;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// timezone
|
|
72
|
-
if (config.timezone) {
|
|
73
|
-
append('event_datetime', `Timezone: ${config.timezone}`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// customTimestampParam
|
|
77
|
-
if (config.customTimestampParam) {
|
|
78
|
-
append('event_datetime', `Custom timestamp parameter: '${config.customTimestampParam}'`);
|
|
79
|
-
append('event_custom_timestamp', `Source parameter: '${config.customTimestampParam}'`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// data_is_final
|
|
83
|
-
if (config.dataIsFinal) {
|
|
84
|
-
const method = config.dataIsFinal.detectionMethod;
|
|
85
|
-
if (method === 'DAY_THRESHOLD') {
|
|
86
|
-
append('data_is_final', `Detection method: DAY_THRESHOLD (${config.dataIsFinal.dayThreshold} days)`);
|
|
87
|
-
} else {
|
|
88
|
-
append('data_is_final', `Detection method: EXPORT_TYPE`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// excludedEvents
|
|
93
|
-
if (config.excludedEvents && config.excludedEvents.length > 0) {
|
|
94
|
-
append('event_name', `Excluded events: ${config.excludedEvents.join(', ')}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// excludedEventParams
|
|
98
|
-
if (config.excludedEventParams && config.excludedEventParams.length > 0) {
|
|
99
|
-
append('event_params', `Excluded parameters: ${config.excludedEventParams.join(', ')}`);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// sessionParams
|
|
103
|
-
if (config.sessionParams && config.sessionParams.length > 0) {
|
|
104
|
-
append('session_params', `Configured parameters: ${config.sessionParams.join(', ')}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// includedExportTypes
|
|
108
|
-
if (config.includedExportTypes) {
|
|
109
|
-
const types = Object.entries(config.includedExportTypes)
|
|
110
|
-
.filter(([, enabled]) => enabled)
|
|
111
|
-
.map(([type]) => type);
|
|
112
|
-
if (types.length > 0) {
|
|
113
|
-
append('export_type', `Included export types: ${types.join(', ')}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return notes;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Returns a deep copy of the column descriptions, enriched with
|
|
122
|
-
* lineage, typical use, and configuration-specific sections composed into
|
|
123
|
-
* multi-section descriptions.
|
|
124
|
-
*
|
|
125
|
-
* @param {Object} config - The merged configuration object.
|
|
126
|
-
* @param {Object} columnMetadata - Column metadata provided by the table module.
|
|
127
|
-
* @param {Object} columnMetadata.descriptions - Column descriptions (Dataform ITableConfig columns format).
|
|
128
|
-
* @param {Object} columnMetadata.lineage - Column lineage data mapping column names to { source, note }.
|
|
129
|
-
* @param {Object} columnMetadata.typicalUse - Column typical use mapping column names to description strings.
|
|
130
|
-
* @returns {Object} Column descriptions object in Dataform ITableConfig columns format.
|
|
131
|
-
*/
|
|
132
|
-
const getColumnDescriptions = (config, columnMetadata) => {
|
|
133
|
-
const descriptions = JSON.parse(JSON.stringify(columnMetadata.descriptions));
|
|
134
|
-
|
|
135
|
-
const configNotes = buildConfigNotes(config);
|
|
136
|
-
|
|
137
|
-
// Compose multi-section descriptions for each top-level column
|
|
138
|
-
for (const key of Object.keys(descriptions)) {
|
|
139
|
-
const isStruct = typeof descriptions[key] === 'object' && descriptions[key].description;
|
|
140
|
-
const baseDesc = isStruct ? descriptions[key].description : (typeof descriptions[key] === 'string' ? descriptions[key] : null);
|
|
141
|
-
|
|
142
|
-
if (!baseDesc) continue;
|
|
143
|
-
|
|
144
|
-
const composed = composeDescription({
|
|
145
|
-
base: baseDesc,
|
|
146
|
-
lineage: getLineageText(key, columnMetadata.lineage),
|
|
147
|
-
typicalUse: columnMetadata.typicalUse[key] || null,
|
|
148
|
-
config: configNotes[key] || null,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (isStruct) {
|
|
152
|
-
descriptions[key].description = composed;
|
|
153
|
-
} else {
|
|
154
|
-
descriptions[key] = composed;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Add descriptions for dynamically promoted event parameter columns
|
|
159
|
-
if (config && config.eventParamsToColumns && config.eventParamsToColumns.length > 0) {
|
|
160
|
-
config.eventParamsToColumns.forEach(p => {
|
|
161
|
-
const columnName = p.columnName || p.name;
|
|
162
|
-
const type = p.type ? ` (${p.type})` : ' (any data type)';
|
|
163
|
-
descriptions[columnName] = composeDescription({
|
|
164
|
-
base: `Promoted from event parameter '${p.name}'${type}`,
|
|
165
|
-
lineage: `Derived -- Promoted from the event_params array`,
|
|
166
|
-
typicalUse: 'Promoted event parameter available as a top-level column for direct filtering and aggregation',
|
|
167
|
-
config: null,
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
1
|
+
const constants = require('./constants');
|
|
2
|
+
const { version } = require('./package.json');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Composes a multi-section column description string from individual sections.
|
|
6
|
+
* Sections with null/undefined/empty values are omitted.
|
|
7
|
+
* Sections are separated by line breaks for readability.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} sections - { base, lineage, typicalUse, config }
|
|
10
|
+
* @returns {string} Composed description
|
|
11
|
+
*/
|
|
12
|
+
const composeDescription = (sections) => {
|
|
13
|
+
const parts = [];
|
|
14
|
+
|
|
15
|
+
if (sections.base) {
|
|
16
|
+
parts.push(sections.base);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (sections.lineage) {
|
|
20
|
+
parts.push(`Lineage: ${sections.lineage}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (sections.typicalUse) {
|
|
24
|
+
parts.push(`Typical use: ${sections.typicalUse}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (sections.config) {
|
|
28
|
+
parts.push(`Config: ${sections.config}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return parts.join('\n\n');
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns a formatted lineage text string for a column, or null if no lineage data exists.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} columnName - The column name to look up.
|
|
38
|
+
* @param {Object} columnLineage - The lineage data object mapping column names to { source, note }.
|
|
39
|
+
* @returns {string|null} Formatted lineage string, e.g. "Derived -- Concatenation of ..."
|
|
40
|
+
*/
|
|
41
|
+
const getLineageText = (columnName, columnLineage) => {
|
|
42
|
+
const entry = columnLineage[columnName];
|
|
43
|
+
if (!entry) return null;
|
|
44
|
+
|
|
45
|
+
const sourceLabels = {
|
|
46
|
+
'ga4_export': 'Standard GA4 export field',
|
|
47
|
+
'ga4_export_modified': 'GA4 export field (modified)',
|
|
48
|
+
'derived': 'Derived',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const label = sourceLabels[entry.source] || entry.source;
|
|
52
|
+
return entry.note ? `${label} -- ${entry.note}` : label;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Builds a map of config-specific notes for columns based on the provided configuration.
|
|
57
|
+
* Extracts the configuration-dependent description suffixes into a { columnName: "note" } map.
|
|
58
|
+
*
|
|
59
|
+
* @param {Object} config - The merged configuration object.
|
|
60
|
+
* @returns {Object} Map of column names to config note strings.
|
|
61
|
+
*/
|
|
62
|
+
const buildConfigNotes = (config) => {
|
|
63
|
+
const notes = {};
|
|
64
|
+
|
|
65
|
+
if (!config) return notes;
|
|
66
|
+
|
|
67
|
+
const append = (key, text) => {
|
|
68
|
+
notes[key] = notes[key] ? `${notes[key]}. ${text}` : text;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// timezone
|
|
72
|
+
if (config.timezone) {
|
|
73
|
+
append('event_datetime', `Timezone: ${config.timezone}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// customTimestampParam
|
|
77
|
+
if (config.customTimestampParam) {
|
|
78
|
+
append('event_datetime', `Custom timestamp parameter: '${config.customTimestampParam}'`);
|
|
79
|
+
append('event_custom_timestamp', `Source parameter: '${config.customTimestampParam}'`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// data_is_final
|
|
83
|
+
if (config.dataIsFinal) {
|
|
84
|
+
const method = config.dataIsFinal.detectionMethod;
|
|
85
|
+
if (method === 'DAY_THRESHOLD') {
|
|
86
|
+
append('data_is_final', `Detection method: DAY_THRESHOLD (${config.dataIsFinal.dayThreshold} days)`);
|
|
87
|
+
} else {
|
|
88
|
+
append('data_is_final', `Detection method: EXPORT_TYPE`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// excludedEvents
|
|
93
|
+
if (config.excludedEvents && config.excludedEvents.length > 0) {
|
|
94
|
+
append('event_name', `Excluded events: ${config.excludedEvents.join(', ')}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// excludedEventParams
|
|
98
|
+
if (config.excludedEventParams && config.excludedEventParams.length > 0) {
|
|
99
|
+
append('event_params', `Excluded parameters: ${config.excludedEventParams.join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// sessionParams
|
|
103
|
+
if (config.sessionParams && config.sessionParams.length > 0) {
|
|
104
|
+
append('session_params', `Configured parameters: ${config.sessionParams.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// includedExportTypes
|
|
108
|
+
if (config.includedExportTypes) {
|
|
109
|
+
const types = Object.entries(config.includedExportTypes)
|
|
110
|
+
.filter(([, enabled]) => enabled)
|
|
111
|
+
.map(([type]) => type);
|
|
112
|
+
if (types.length > 0) {
|
|
113
|
+
append('export_type', `Included export types: ${types.join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return notes;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns a deep copy of the column descriptions, enriched with
|
|
122
|
+
* lineage, typical use, and configuration-specific sections composed into
|
|
123
|
+
* multi-section descriptions.
|
|
124
|
+
*
|
|
125
|
+
* @param {Object} config - The merged configuration object.
|
|
126
|
+
* @param {Object} columnMetadata - Column metadata provided by the table module.
|
|
127
|
+
* @param {Object} columnMetadata.descriptions - Column descriptions (Dataform ITableConfig columns format).
|
|
128
|
+
* @param {Object} columnMetadata.lineage - Column lineage data mapping column names to { source, note }.
|
|
129
|
+
* @param {Object} columnMetadata.typicalUse - Column typical use mapping column names to description strings.
|
|
130
|
+
* @returns {Object} Column descriptions object in Dataform ITableConfig columns format.
|
|
131
|
+
*/
|
|
132
|
+
const getColumnDescriptions = (config, columnMetadata) => {
|
|
133
|
+
const descriptions = JSON.parse(JSON.stringify(columnMetadata.descriptions));
|
|
134
|
+
|
|
135
|
+
const configNotes = buildConfigNotes(config);
|
|
136
|
+
|
|
137
|
+
// Compose multi-section descriptions for each top-level column
|
|
138
|
+
for (const key of Object.keys(descriptions)) {
|
|
139
|
+
const isStruct = typeof descriptions[key] === 'object' && descriptions[key].description;
|
|
140
|
+
const baseDesc = isStruct ? descriptions[key].description : (typeof descriptions[key] === 'string' ? descriptions[key] : null);
|
|
141
|
+
|
|
142
|
+
if (!baseDesc) continue;
|
|
143
|
+
|
|
144
|
+
const composed = composeDescription({
|
|
145
|
+
base: baseDesc,
|
|
146
|
+
lineage: getLineageText(key, columnMetadata.lineage),
|
|
147
|
+
typicalUse: columnMetadata.typicalUse[key] || null,
|
|
148
|
+
config: configNotes[key] || null,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (isStruct) {
|
|
152
|
+
descriptions[key].description = composed;
|
|
153
|
+
} else {
|
|
154
|
+
descriptions[key] = composed;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Add descriptions for dynamically promoted event parameter columns
|
|
159
|
+
if (config && config.eventParamsToColumns && config.eventParamsToColumns.length > 0) {
|
|
160
|
+
config.eventParamsToColumns.forEach(p => {
|
|
161
|
+
const columnName = p.columnName || p.name;
|
|
162
|
+
const type = p.type ? ` (${p.type})` : ' (any data type)';
|
|
163
|
+
descriptions[columnName] = composeDescription({
|
|
164
|
+
base: `Promoted from event parameter '${p.name}'${type}`,
|
|
165
|
+
lineage: `Derived -- Promoted from the event_params array`,
|
|
166
|
+
typicalUse: 'Promoted event parameter available as a top-level column for direct filtering and aggregation',
|
|
167
|
+
config: null,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Add descriptions for columns added or replaced by data enrichments.
|
|
173
|
+
// Item-level enrichments are not yet supported and throw at SQL gen time — skip here.
|
|
174
|
+
if (config && Array.isArray(config.enrichments) && config.enrichments.length > 0) {
|
|
175
|
+
config.enrichments.forEach(e => {
|
|
176
|
+
const level = e.level ?? 'event';
|
|
177
|
+
if (level !== 'event') return;
|
|
178
|
+
const joinKeys = Array.isArray(e.joinKey) ? e.joinKey : [e.joinKey];
|
|
179
|
+
const joinKeyText = joinKeys.join(', ');
|
|
180
|
+
const sourceText = renderEnrichmentSource(e.source);
|
|
181
|
+
for (const c of e.columns) {
|
|
182
|
+
const existing = descriptions[c];
|
|
183
|
+
const existingText = typeof existing === 'string'
|
|
184
|
+
? existing
|
|
185
|
+
: (existing && typeof existing === 'object' && existing.description)
|
|
186
|
+
? existing.description
|
|
187
|
+
: null;
|
|
188
|
+
const newDesc = existingText
|
|
189
|
+
? `Coalesced by enrichment '${e.name}' (joined on ${joinKeyText} from ${sourceText}; falls back to original on missed JOIN). Original: ${existingText}`
|
|
190
|
+
: `Added by enrichment '${e.name}' (joined on ${joinKeyText} from ${sourceText}).`;
|
|
191
|
+
// If the original was a struct-shaped entry, preserve the structure but replace the description.
|
|
192
|
+
// Otherwise, set as a plain string.
|
|
193
|
+
if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
|
|
194
|
+
descriptions[c] = { ...existing, description: newDesc };
|
|
195
|
+
} else {
|
|
196
|
+
descriptions[c] = newDesc;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return descriptions;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Renders an enrichment source for inclusion in column descriptions.
|
|
207
|
+
*
|
|
208
|
+
* - Backtick-quoted string: passed through as-is.
|
|
209
|
+
* - Dataform table reference object: rendered as `<dataset>.<name>` (project not available
|
|
210
|
+
* at description-generation time; resolved later via ctx.ref()).
|
|
211
|
+
*
|
|
212
|
+
* @param {string|Object} source - The enrichment's source field.
|
|
213
|
+
* @returns {string} Backtick-quoted source identifier for display.
|
|
214
|
+
*/
|
|
215
|
+
const renderEnrichmentSource = (source) => {
|
|
216
|
+
if (typeof source === 'string') return source;
|
|
217
|
+
if (source && typeof source === 'object') {
|
|
218
|
+
const dataset = source.dataset || source.schema;
|
|
219
|
+
if (dataset && source.name) return '`' + dataset + '.' + source.name + '`';
|
|
220
|
+
}
|
|
221
|
+
return String(source);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Checks whether a column (or its parent struct) is excluded by the config.
|
|
226
|
+
*
|
|
227
|
+
* @param {string[]} dependsOn - Column names this entry depends on.
|
|
228
|
+
* @param {string[]} excludedColumns - Combined excluded columns from config.
|
|
229
|
+
* @returns {boolean} True if ALL dependsOn columns are excluded.
|
|
230
|
+
*/
|
|
231
|
+
const isExcluded = (dependsOn, excludedColumns) => {
|
|
232
|
+
if (!dependsOn || dependsOn.length === 0) return false;
|
|
233
|
+
return dependsOn.every(col => excludedColumns.includes(col));
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Builds the full table description by combining table-specific sections
|
|
238
|
+
* with shared sections (package attribution, config JSON dump).
|
|
239
|
+
*
|
|
240
|
+
* @param {Object} config - The merged configuration object.
|
|
241
|
+
* @param {string[]} tableSections - Table-specific description sections (provided by the table module).
|
|
242
|
+
* @returns {string} The composed table description.
|
|
243
|
+
*/
|
|
244
|
+
const buildTableDescription = (config, tableSections) => {
|
|
245
|
+
const sections = [...tableSections];
|
|
246
|
+
|
|
247
|
+
// Package Attribution
|
|
248
|
+
sections.push(`${constants.TABLE_DESCRIPTION_SUFFIX} Version: ${version}\n${constants.TABLE_DESCRIPTION_DOCUMENTATION_LINK}`);
|
|
249
|
+
|
|
250
|
+
// Config JSON dump
|
|
251
|
+
const configForDump = Object.fromEntries(
|
|
252
|
+
Object.entries(config).filter(([key]) => !key.startsWith('default'))
|
|
253
|
+
);
|
|
254
|
+
// Strip description and columns from dataformTableConfig to avoid circular reference and bloat
|
|
255
|
+
if (configForDump.dataformTableConfig) {
|
|
256
|
+
const { description, columns, ...rest } = configForDump.dataformTableConfig;
|
|
257
|
+
configForDump.dataformTableConfig = rest;
|
|
258
|
+
}
|
|
259
|
+
const configJson = JSON.stringify(configForDump, null, 2);
|
|
260
|
+
sections.push(`The last full table refresh was done using this configuration:\n${configJson}`);
|
|
261
|
+
|
|
262
|
+
return sections.join('\n\n');
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
module.exports = {
|
|
266
|
+
getColumnDescriptions,
|
|
267
|
+
buildTableDescription,
|
|
268
|
+
composeDescription,
|
|
269
|
+
getLineageText,
|
|
270
|
+
buildConfigNotes,
|
|
271
|
+
isExcluded,
|
|
272
|
+
};
|