@engine9/schemas 1.9.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/.eslintignore +1 -0
- package/.prettierrc +7 -0
- package/AGENTS.md +10 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/channels/email/README.md +83 -0
- package/channels/email/index.js +22 -0
- package/channels/email/search.js +86 -0
- package/channels/email/segments.js +81 -0
- package/eslint.config.js +21 -0
- package/event/README.md +79 -0
- package/event/index.js +17 -0
- package/event/schema.js +111 -0
- package/event/search.js +51 -0
- package/message/index.js +8 -0
- package/message/schema.js +130 -0
- package/message/ui.console.json5 +233 -0
- package/package.json +26 -0
- package/person/index.js +100 -0
- package/person/metrics.js +17 -0
- package/person/schema.js +47 -0
- package/person/transforms/inbound/normalize_field_names.js +16 -0
- package/person/transforms/inbound/upsert_tables.js +21 -0
- package/person/transforms/outbound/appendName.js +32 -0
- package/person/transforms/simpleMap.js +34 -0
- package/person/ui.console.json5 +165 -0
- package/person/upsert.test.js +72 -0
- package/person_address/index.js +30 -0
- package/person_address/normalize.js +232 -0
- package/person_address/normalize.test.js +69 -0
- package/person_address/schema.js +49 -0
- package/person_address/transforms/inbound/normalize.js +23 -0
- package/person_address/transforms/inbound/upsert_tables.js +51 -0
- package/person_address/transforms/outbound/appendAddress.js +87 -0
- package/person_address/ui.console.json5 +31 -0
- package/person_custom/index.js +24 -0
- package/person_custom/transforms/appendRemoteId.js +31 -0
- package/person_custom/transforms/inbound/upsert_tables.js +31 -0
- package/person_email/README.md +82 -0
- package/person_email/index.js +35 -0
- package/person_email/schema.js +49 -0
- package/person_email/search.js +61 -0
- package/person_email/segments.js +16 -0
- package/person_email/transforms/inbound/extract_identifiers.js +33 -0
- package/person_email/transforms/inbound/upsert_tables.js +142 -0
- package/person_email/transforms/outbound/appendEmail.js +32 -0
- package/person_email/transforms/outbound/appendEmailHash.js +20 -0
- package/person_email/transforms/outbound/tables.js +14 -0
- package/person_email/ui.console.json5 +58 -0
- package/person_email/upsert.test.js +126 -0
- package/person_hash/README.md +70 -0
- package/person_hash/hash.js +103 -0
- package/person_hash/hash.test.js +165 -0
- package/person_hash/index.js +37 -0
- package/person_hash/schema.js +54 -0
- package/person_hash/search.js +86 -0
- package/person_hash/transforms/inbound/extract_identifiers.js +70 -0
- package/person_hash/transforms/inbound/upsert_tables.js +105 -0
- package/person_hash/transforms/outbound/appendEmailHash.js +42 -0
- package/person_hash/transforms/outbound/appendPhoneHash.js +42 -0
- package/person_hash/ui.console.json5 +80 -0
- package/person_phone/README.md +79 -0
- package/person_phone/index.js +33 -0
- package/person_phone/schema.js +58 -0
- package/person_phone/search.js +79 -0
- package/person_phone/segments.js +26 -0
- package/person_phone/transforms/inbound/extract_identifiers.js +59 -0
- package/person_phone/transforms/inbound/upsert_tables.js +119 -0
- package/person_phone/transforms/outbound/appendPhoneHash.js +37 -0
- package/person_phone/ui.console.json5 +31 -0
- package/person_phone/upsert.test.js +50 -0
- package/person_remote/index.js +58 -0
- package/person_remote/metrics.js +17 -0
- package/person_remote/schema.js +21 -0
- package/person_remote/transforms/inbound/extract_identifiers.js +34 -0
- package/person_remote/transforms/inbound/upsert_tables.js +92 -0
- package/person_remote/transforms/outbound/appendRemotePersonId.js +59 -0
- package/plugin/index.js +13 -0
- package/plugin/schema.js +70 -0
- package/plugin/settings.js +19 -0
- package/plugin/ui.console.json5 +79 -0
- package/report/index.js +7 -0
- package/report/ui.console.json5 +21 -0
- package/segment/index.js +21 -0
- package/segment/schema.js +81 -0
- package/segment/search.js +64 -0
- package/segment/transforms/inbound/upsert.js +44 -0
- package/segment/ui.console.json5 +142 -0
- package/segment_stats/index.js +16 -0
- package/segment_stats/schema.js +30 -0
- package/source_code/index.js +16 -0
- package/source_code/metrics.js +28 -0
- package/source_code/schema.js +29 -0
- package/source_code/ui.console.json5 +31 -0
- package/stacks/limited-pii/index.js +8 -0
- package/stacks/limited-pii/stack.json +22 -0
- package/stacks/stacks.test.js +27 -0
- package/stacks/standard/index.js +7 -0
- package/stacks/standard/stack.json +20 -0
- package/tag/index.js +13 -0
- package/tag/schema.js +23 -0
- package/task/index.js +13 -0
- package/task/schema.js +54 -0
- package/task/ui.console.json5 +60 -0
- package/timeline/index.js +18 -0
- package/timeline/schema.js +18 -0
- package/timeline/transforms/inbound/upsert_tables.js +22 -0
- package/timeline/ui.console.json5 +31 -0
- package/transaction/contact_details/index.js +11 -0
- package/transaction/contact_details/schema.js +22 -0
- package/transaction/core/README.md +79 -0
- package/transaction/core/attribute.test.js +231 -0
- package/transaction/core/index.js +31 -0
- package/transaction/core/metrics.js +20 -0
- package/transaction/core/schema.js +45 -0
- package/transaction/core/search.js +87 -0
- package/transaction/core/segments.js +14 -0
- package/transaction/core/transforms/appendTransactionSummary.js +101 -0
- package/transaction/core/transforms/attribute.js +327 -0
- package/transaction/core/transforms/inbound/upsert_tables.js +57 -0
- package/utilities/limited-pii/index.js +14 -0
- package/utilities/limited-pii/settings.js +12 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { mergeIntoQueue, uuidIsValid } from '@engine9/input-tools';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
Batch stand-in for TransactionBase.attribute.
|
|
5
|
+
|
|
6
|
+
Runs after the transaction upsert when the inbound weaver includes it.
|
|
7
|
+
A first pass usually finds no override rows. Run it again to re-attribute
|
|
8
|
+
once transaction_metadata_override has a row for the remote transaction.
|
|
9
|
+
|
|
10
|
+
A message matches when its source code equals the transaction source code
|
|
11
|
+
(or transaction_source_code_override) and publish_date is earlier than the
|
|
12
|
+
transaction timestamp plus 29 hours. When several messages match, the latest
|
|
13
|
+
publish_date wins.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const PUBLISH_GRACE_MS = 29 * 60 * 60 * 1000;
|
|
17
|
+
|
|
18
|
+
function sqlQuote(value) {
|
|
19
|
+
return String(value).replace(/'/g, "''");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const bindings = ({ pluginId } = {}) => {
|
|
23
|
+
if (!pluginId) {
|
|
24
|
+
throw new Error('pluginId is required for attribute lookups');
|
|
25
|
+
}
|
|
26
|
+
if (!uuidIsValid(pluginId)) {
|
|
27
|
+
throw new Error(`pluginId must be a uuid for attribute lookups, got ${pluginId}`);
|
|
28
|
+
}
|
|
29
|
+
const safePluginId = sqlQuote(pluginId);
|
|
30
|
+
return {
|
|
31
|
+
// remote_entry_id = remote_transaction_id, transaction_bot_id = plugin.remote_plugin_id
|
|
32
|
+
overrides: {
|
|
33
|
+
path: 'sql.query',
|
|
34
|
+
options: {
|
|
35
|
+
table: 'transaction_metadata_override',
|
|
36
|
+
columns: [
|
|
37
|
+
'remote_transaction_id',
|
|
38
|
+
'transaction_bot_id',
|
|
39
|
+
'transaction_source_code_override',
|
|
40
|
+
'override_message_id',
|
|
41
|
+
'ignore_reason',
|
|
42
|
+
{ eql: 'source_code_dictionary.source_code_id', name: 'dictionary_source_code_id' },
|
|
43
|
+
{ eql: 'message_source_code.source_code', name: 'matched_source_code' },
|
|
44
|
+
{ eql: 'message_source_code.message_id', name: 'matched_message_id' },
|
|
45
|
+
{ eql: 'message_source_code.publish_date', name: 'matched_publish_date' },
|
|
46
|
+
{ eql: 'global_message.id', name: 'matched_message_uuid' },
|
|
47
|
+
{ eql: 'global_message.publish_date', name: 'matched_message_publish_date' },
|
|
48
|
+
{ eql: 'global_message.do_not_attribute', name: 'matched_do_not_attribute' }
|
|
49
|
+
],
|
|
50
|
+
lookup: [{ personIdField: 'remote_entry_id', tableLookupField: 'remote_transaction_id' }],
|
|
51
|
+
joins: [
|
|
52
|
+
{
|
|
53
|
+
table: 'plugin',
|
|
54
|
+
join_eql: 'transaction_metadata_override.transaction_bot_id=plugin.remote_plugin_id'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
table: 'source_code_dictionary',
|
|
58
|
+
type: 'left',
|
|
59
|
+
join_eql:
|
|
60
|
+
'transaction_metadata_override.transaction_source_code_override=source_code_dictionary.source_code'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
table: 'message_source_code',
|
|
64
|
+
type: 'left',
|
|
65
|
+
join_eql: 'transaction_metadata_override.transaction_source_code_override=message_source_code.source_code'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
table: 'global_message',
|
|
69
|
+
type: 'left',
|
|
70
|
+
join_eql: 'message_source_code.message_id=global_message.message_id'
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
conditions: [{ eql: `plugin.id='${safePluginId}'` }]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
messageSourceCodes: {
|
|
77
|
+
path: 'sql.query',
|
|
78
|
+
options: {
|
|
79
|
+
table: 'message_source_code',
|
|
80
|
+
columns: [
|
|
81
|
+
'source_code',
|
|
82
|
+
'message_id',
|
|
83
|
+
'publish_date',
|
|
84
|
+
{ eql: 'global_message.id', name: 'message_uuid' },
|
|
85
|
+
{ eql: 'global_message.publish_date', name: 'message_publish_date' },
|
|
86
|
+
{ eql: 'global_message.do_not_attribute', name: 'do_not_attribute' }
|
|
87
|
+
],
|
|
88
|
+
lookup: [{ personIdField: 'source_code', tableLookupField: 'source_code' }],
|
|
89
|
+
joins: [
|
|
90
|
+
{
|
|
91
|
+
table: 'global_message',
|
|
92
|
+
type: 'left',
|
|
93
|
+
join_eql: 'message_source_code.message_id=global_message.message_id'
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
// remote_input_id is only on input, so the plugin join does not make `id` ambiguous
|
|
99
|
+
pluginInputs: {
|
|
100
|
+
path: 'sql.query',
|
|
101
|
+
options: {
|
|
102
|
+
table: 'input',
|
|
103
|
+
columns: [
|
|
104
|
+
{ eql: 'input.id', name: 'input_id' },
|
|
105
|
+
'remote_input_id',
|
|
106
|
+
{ eql: 'plugin.remote_plugin_id', name: 'remote_plugin_id' }
|
|
107
|
+
],
|
|
108
|
+
lookup: [{ personIdField: 'remote_input_id', tableLookupField: 'remote_input_id' }],
|
|
109
|
+
joins: [
|
|
110
|
+
{
|
|
111
|
+
table: 'plugin',
|
|
112
|
+
join_eql: 'input.plugin_id=plugin.id'
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
conditions: [{ eql: `plugin.id='${safePluginId}'` }]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
tablesToUpsert: { path: 'sql.tables.upsert' }
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const type = 'upsert';
|
|
123
|
+
|
|
124
|
+
function blankToNull(value) {
|
|
125
|
+
if (value == null) return null;
|
|
126
|
+
const text = String(value).trim();
|
|
127
|
+
return text === '' ? null : text;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function positiveInt(value) {
|
|
131
|
+
const n = parseInt(value, 10);
|
|
132
|
+
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function integerMessageId(value) {
|
|
136
|
+
if (value == null || value === '') return null;
|
|
137
|
+
if (typeof value === 'string' && uuidIsValid(value)) return null;
|
|
138
|
+
const n = Number(value);
|
|
139
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function publishIsBefore(publishDate, ts) {
|
|
143
|
+
if (publishDate == null || publishDate === '' || ts == null || ts === '') return false;
|
|
144
|
+
const publishMs = new Date(publishDate).getTime();
|
|
145
|
+
const tsMs = new Date(ts).getTime();
|
|
146
|
+
if (Number.isNaN(publishMs) || Number.isNaN(tsMs)) return false;
|
|
147
|
+
return publishMs < tsMs + PUBLISH_GRACE_MS;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function candidateFromMessageSource(row) {
|
|
151
|
+
const sourceCode = blankToNull(row?.source_code);
|
|
152
|
+
if (!sourceCode) return null;
|
|
153
|
+
const messageId = integerMessageId(row.message_id);
|
|
154
|
+
const messageUuid = uuidIsValid(row.message_uuid) ? row.message_uuid : null;
|
|
155
|
+
if (messageId == null && !messageUuid) return null;
|
|
156
|
+
return {
|
|
157
|
+
source_code: sourceCode,
|
|
158
|
+
message_id: messageId,
|
|
159
|
+
message_uuid: messageUuid,
|
|
160
|
+
publish_date: row.message_publish_date || row.publish_date || null,
|
|
161
|
+
do_not_attribute: row.do_not_attribute
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function candidateFromOverride(row) {
|
|
166
|
+
const sourceCode = blankToNull(row?.matched_source_code);
|
|
167
|
+
if (!sourceCode) return null;
|
|
168
|
+
return candidateFromMessageSource({
|
|
169
|
+
source_code: sourceCode,
|
|
170
|
+
message_id: row.matched_message_id,
|
|
171
|
+
message_uuid: row.matched_message_uuid,
|
|
172
|
+
publish_date: row.matched_publish_date,
|
|
173
|
+
message_publish_date: row.matched_message_publish_date,
|
|
174
|
+
do_not_attribute: row.matched_do_not_attribute
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function chooseMessage(candidates, ts, { respectDoNotAttribute }) {
|
|
179
|
+
let best = null;
|
|
180
|
+
let bestPublish = null;
|
|
181
|
+
for (const candidate of candidates) {
|
|
182
|
+
if (!candidate) continue;
|
|
183
|
+
if (respectDoNotAttribute && Number(candidate.do_not_attribute) === 1) continue;
|
|
184
|
+
if (!publishIsBefore(candidate.publish_date, ts)) continue;
|
|
185
|
+
const publishMs = new Date(candidate.publish_date).getTime();
|
|
186
|
+
const messageKey = String(candidate.message_id ?? candidate.message_uuid ?? '');
|
|
187
|
+
const bestKey = best ? String(best.message_id ?? best.message_uuid ?? '') : '';
|
|
188
|
+
if (!best || publishMs > bestPublish || (publishMs === bestPublish && messageKey < bestKey)) {
|
|
189
|
+
best = candidate;
|
|
190
|
+
bestPublish = publishMs;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return best;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function remotePluginIdFor(row, pluginInputs, overrideBotId) {
|
|
197
|
+
const remoteInputId = blankToNull(row.remote_input_id);
|
|
198
|
+
if (remoteInputId) {
|
|
199
|
+
const match = pluginInputs.find((p) => String(p.remote_input_id ?? '') === remoteInputId);
|
|
200
|
+
if (match?.remote_plugin_id) return String(match.remote_plugin_id);
|
|
201
|
+
}
|
|
202
|
+
const inputId = row.input_id;
|
|
203
|
+
if (inputId) {
|
|
204
|
+
const match = pluginInputs.find((p) => (p.input_id ?? p.id) === inputId && p.remote_plugin_id);
|
|
205
|
+
if (match?.remote_plugin_id) return String(match.remote_plugin_id);
|
|
206
|
+
}
|
|
207
|
+
return overrideBotId ? String(overrideBotId) : null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function mergeAttribution(existing, incoming) {
|
|
211
|
+
return { ...existing, ...incoming };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function mergeTransactionUpdate(existing, incoming) {
|
|
215
|
+
return { ...existing, ...incoming, id: existing.id ?? incoming.id };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
Upsert already queued this batch object: write the attribution columns onto
|
|
220
|
+
that object. A later attribute pass queues a narrow transaction update by id.
|
|
221
|
+
*/
|
|
222
|
+
function applyTransactionAttribution(tablesToUpsert, row, fields) {
|
|
223
|
+
Object.assign(row, fields);
|
|
224
|
+
tablesToUpsert.transaction = tablesToUpsert.transaction || [];
|
|
225
|
+
const queued = tablesToUpsert.transaction.find((item) => item === row || (row.id != null && item.id === row.id));
|
|
226
|
+
if (queued) {
|
|
227
|
+
if (queued !== row) Object.assign(queued, fields);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (row.id == null || row.id === '') {
|
|
231
|
+
throw new Error('attribute requires a transaction id, or a transaction row already queued by upsert');
|
|
232
|
+
}
|
|
233
|
+
mergeIntoQueue(tablesToUpsert.transaction, { id: row.id, ...fields }, {
|
|
234
|
+
keyFields: ['id'],
|
|
235
|
+
merge: mergeTransactionUpdate,
|
|
236
|
+
label: 'transaction'
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export async function transform({
|
|
241
|
+
batch,
|
|
242
|
+
tablesToUpsert,
|
|
243
|
+
overrides = [],
|
|
244
|
+
messageSourceCodes = [],
|
|
245
|
+
pluginInputs = []
|
|
246
|
+
}) {
|
|
247
|
+
if (!batch?.length) return;
|
|
248
|
+
tablesToUpsert.attribution = tablesToUpsert.attribution || [];
|
|
249
|
+
|
|
250
|
+
const overridesByRemote = new Map();
|
|
251
|
+
for (const row of overrides) {
|
|
252
|
+
const remoteId = blankToNull(row.remote_transaction_id);
|
|
253
|
+
if (!remoteId) continue;
|
|
254
|
+
if (!overridesByRemote.has(remoteId)) overridesByRemote.set(remoteId, []);
|
|
255
|
+
overridesByRemote.get(remoteId).push(row);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const messagesByCode = new Map();
|
|
259
|
+
for (const row of messageSourceCodes) {
|
|
260
|
+
const candidate = candidateFromMessageSource(row);
|
|
261
|
+
if (!candidate) continue;
|
|
262
|
+
if (!messagesByCode.has(candidate.source_code)) messagesByCode.set(candidate.source_code, []);
|
|
263
|
+
messagesByCode.get(candidate.source_code).push(candidate);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
batch.forEach((row) => {
|
|
267
|
+
const remoteId = blankToNull(row.remote_entry_id);
|
|
268
|
+
const overrideRows = remoteId ? overridesByRemote.get(remoteId) || [] : [];
|
|
269
|
+
const override = overrideRows[0] || null;
|
|
270
|
+
const overrideCode = blankToNull(override?.transaction_source_code_override);
|
|
271
|
+
// An override row with a blank code does not fall back to the transaction
|
|
272
|
+
// source code. The legacy column is NOT NULL default '', and attribute()
|
|
273
|
+
// treats any non-null override as the only code to match.
|
|
274
|
+
const usingOverride = override != null;
|
|
275
|
+
const effectiveCode = usingOverride ? overrideCode : blankToNull(row.source_code);
|
|
276
|
+
|
|
277
|
+
let overrideSourceCodeId = positiveInt(override?.dictionary_source_code_id);
|
|
278
|
+
if (!overrideSourceCodeId && usingOverride && overrideCode === blankToNull(row.source_code)) {
|
|
279
|
+
overrideSourceCodeId = positiveInt(row.source_code_id);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const overrideMessages = overrideRows.map(candidateFromOverride).filter(Boolean);
|
|
283
|
+
const lookedUp = effectiveCode ? messagesByCode.get(effectiveCode) || [] : [];
|
|
284
|
+
const match = chooseMessage(usingOverride ? [...overrideMessages, ...lookedUp] : lookedUp, row.ts, {
|
|
285
|
+
respectDoNotAttribute: !usingOverride
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const recommendedUuid = match?.message_uuid || null;
|
|
289
|
+
const fields = {
|
|
290
|
+
override_source_code_id: overrideSourceCodeId,
|
|
291
|
+
final_source_code_id: overrideSourceCodeId > 0 ? overrideSourceCodeId : positiveInt(row.source_code_id),
|
|
292
|
+
recommended_message_id: recommendedUuid,
|
|
293
|
+
override_message_id:
|
|
294
|
+
row.override_message_id && uuidIsValid(row.override_message_id) ? row.override_message_id : null,
|
|
295
|
+
final_message_id: null
|
|
296
|
+
};
|
|
297
|
+
fields.final_message_id = fields.override_message_id || recommendedUuid || null;
|
|
298
|
+
applyTransactionAttribution(tablesToUpsert, row, fields);
|
|
299
|
+
|
|
300
|
+
if (!match || !remoteId || match.message_id == null) return;
|
|
301
|
+
const botId = remotePluginIdFor(row, pluginInputs, override?.transaction_bot_id);
|
|
302
|
+
if (!botId) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`attribute matched a message for remote_entry_id ${remoteId} but plugin.remote_plugin_id was not loaded`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
mergeIntoQueue(
|
|
308
|
+
tablesToUpsert.attribution,
|
|
309
|
+
{
|
|
310
|
+
transaction_bot_id: botId,
|
|
311
|
+
remote_transaction_id: remoteId,
|
|
312
|
+
recommended_message_id: match.message_id
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
keyFields: ['transaction_bot_id', 'remote_transaction_id'],
|
|
316
|
+
merge: mergeAttribution,
|
|
317
|
+
label: 'attribution'
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export default {
|
|
324
|
+
bindings,
|
|
325
|
+
type,
|
|
326
|
+
transform
|
|
327
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getEntryTypeId, getTimelineEntryUUID } from '@engine9/input-tools';
|
|
2
|
+
/* appends a recurs_id no matter what */
|
|
3
|
+
function appendRecursId(d) {
|
|
4
|
+
//If there's a recurs_id that's positive, use it.
|
|
5
|
+
if (parseInt(d.recurs_id)) {
|
|
6
|
+
d.recurs_id = parseInt(d.recurs_id);
|
|
7
|
+
return d;
|
|
8
|
+
}
|
|
9
|
+
//Otherwise set it based on the 'recurs' flag
|
|
10
|
+
switch (d.recurs) {
|
|
11
|
+
case 'semi-annually':
|
|
12
|
+
d.recurs_id = 6;
|
|
13
|
+
break;
|
|
14
|
+
case 'annually':
|
|
15
|
+
d.recurs_id = 5;
|
|
16
|
+
break;
|
|
17
|
+
case 'quarterly':
|
|
18
|
+
d.recurs_id = 4;
|
|
19
|
+
break;
|
|
20
|
+
case 'monthly':
|
|
21
|
+
d.recurs_id = 3;
|
|
22
|
+
break;
|
|
23
|
+
case 'weekly':
|
|
24
|
+
d.recurs_id = 2;
|
|
25
|
+
break;
|
|
26
|
+
case 'daily':
|
|
27
|
+
d.recurs_id = 1;
|
|
28
|
+
break;
|
|
29
|
+
default: {
|
|
30
|
+
if (d.recurring_number > 1) d.recurs_id = 3;
|
|
31
|
+
else d.recurs_id = 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return d;
|
|
35
|
+
}
|
|
36
|
+
export const bindings = {
|
|
37
|
+
tablesToUpsert: { path: 'sql.tables.upsert' }
|
|
38
|
+
};
|
|
39
|
+
export async function transform(opts) {
|
|
40
|
+
const { batch, tablesToUpsert, pluginId } = opts;
|
|
41
|
+
if (batch.length === 0) return;
|
|
42
|
+
tablesToUpsert.transaction = tablesToUpsert.transaction || [];
|
|
43
|
+
const hasRecurs = batch.some((b) => b.recurs !== undefined || b.recurs_id !== undefined);
|
|
44
|
+
|
|
45
|
+
batch.forEach((o) => {
|
|
46
|
+
if (hasRecurs) {
|
|
47
|
+
appendRecursId(o);
|
|
48
|
+
}
|
|
49
|
+
o.entry_type_id = getEntryTypeId(o);
|
|
50
|
+
o.id = getTimelineEntryUUID(o, { defaults: { plugin_id: pluginId } });
|
|
51
|
+
tablesToUpsert.transaction.push(o);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export default {
|
|
55
|
+
bindings,
|
|
56
|
+
transform
|
|
57
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { settings } from './settings.js';
|
|
2
|
+
|
|
3
|
+
const metadata = {
|
|
4
|
+
name: '@engine9/schemas/utilities/limited-pii',
|
|
5
|
+
description:
|
|
6
|
+
'On-demand PII controls: exclude_pii steers installDefaultPlugins and default export table lists without installing the limited-pii stack by itself.',
|
|
7
|
+
unique: true
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { metadata, settings };
|
|
11
|
+
export default {
|
|
12
|
+
metadata,
|
|
13
|
+
settings
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const settings = [
|
|
2
|
+
{
|
|
3
|
+
name: 'exclude_pii',
|
|
4
|
+
type: 'boolean',
|
|
5
|
+
default: false,
|
|
6
|
+
description:
|
|
7
|
+
'When true, installDefaultPlugins prefers @engine9/schemas/stacks/limited-pii over default_stack, and installs of stacks/standard, person_email, person_phone, and person_address are refused. Does not drop existing plaintext tables. Also omits those tables from the default export list when tables is not set.'
|
|
8
|
+
}
|
|
9
|
+
];
|
|
10
|
+
export default {
|
|
11
|
+
settings
|
|
12
|
+
};
|