@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,130 @@
|
|
|
1
|
+
export const tables = [
|
|
2
|
+
{
|
|
3
|
+
name: 'message',
|
|
4
|
+
columns: {
|
|
5
|
+
id: 'id_uuid',
|
|
6
|
+
message_set_id: 'foreign_uuid',
|
|
7
|
+
channel: 'string',
|
|
8
|
+
name: 'string',
|
|
9
|
+
status: 'string',
|
|
10
|
+
publish_date: 'datetime',
|
|
11
|
+
primary_source_code: { type: 'string', length: 180 },
|
|
12
|
+
primary_source_code_override: { type: 'string', length: 180 },
|
|
13
|
+
final_primary_source_code: { type: 'string', length: 180 },
|
|
14
|
+
source_remote_id: 'string',
|
|
15
|
+
source_submodule: 'string',
|
|
16
|
+
created_at: 'created_at',
|
|
17
|
+
modified_at: 'modified_at'
|
|
18
|
+
},
|
|
19
|
+
indexes: [{ columns: 'id', primary: true }, { columns: ['publish_date'] }, { columns: ['message_set_id'] }]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'message_set',
|
|
23
|
+
columns: {
|
|
24
|
+
id: 'id_uuid',
|
|
25
|
+
campaign_id: 'foreign_uuid',
|
|
26
|
+
name: 'string',
|
|
27
|
+
remote_message_set_id: 'string',
|
|
28
|
+
remote_message_set_name: 'string',
|
|
29
|
+
message_template_id: 'foreign_uuid',
|
|
30
|
+
type: 'string',
|
|
31
|
+
status: 'string',
|
|
32
|
+
schedule: 'string',
|
|
33
|
+
source_code_format: 'string',
|
|
34
|
+
// default_content: 'json', //unused?
|
|
35
|
+
default_variables: 'json',
|
|
36
|
+
required_targeting: 'json', // This is set initially, and can't be changed by users, for things such as Emailable people, etc
|
|
37
|
+
targeting: 'json',
|
|
38
|
+
sequencing: 'json', // sequence of messages
|
|
39
|
+
send_strategy: 'string', // Sending strategy, such as Best open rate, best giving rate, etc
|
|
40
|
+
created_at: 'created_at',
|
|
41
|
+
modified_at: 'modified_at'
|
|
42
|
+
},
|
|
43
|
+
indexes: [
|
|
44
|
+
{ columns: 'id', primary: true },
|
|
45
|
+
{ columns: ['campaign_id', 'remote_message_set_id'], unique: true }
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'campaign',
|
|
50
|
+
columns: {
|
|
51
|
+
id: 'id_uuid',
|
|
52
|
+
name: 'string',
|
|
53
|
+
channel: 'string',
|
|
54
|
+
plugin_id: 'id_uuid', // can't be null, must have a value
|
|
55
|
+
remote_campaign_id: 'string',
|
|
56
|
+
remote_campaign_name: 'string',
|
|
57
|
+
created_at: 'created_at',
|
|
58
|
+
modified_at: 'modified_at'
|
|
59
|
+
},
|
|
60
|
+
indexes: [
|
|
61
|
+
{ columns: 'id', primary: true },
|
|
62
|
+
{ columns: ['plugin_id', 'remote_campaign_id'], unique: true }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'message_content',
|
|
67
|
+
columns: {
|
|
68
|
+
id: 'id_uuid',
|
|
69
|
+
message_id: 'foreign_uuid',
|
|
70
|
+
content: 'json',
|
|
71
|
+
remote_data: 'json',
|
|
72
|
+
variables: 'json',
|
|
73
|
+
targeting: 'json',
|
|
74
|
+
estimated_targets: 'int',
|
|
75
|
+
created_at: 'created_at',
|
|
76
|
+
modified_at: 'modified_at'
|
|
77
|
+
},
|
|
78
|
+
indexes: [{ columns: 'id', primary: true }, { columns: ['message_id'], unique: true }]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'message_override',
|
|
82
|
+
columns: {
|
|
83
|
+
id: 'id_uuid',
|
|
84
|
+
message_id: 'foreign_uuid',
|
|
85
|
+
overrides: 'json',
|
|
86
|
+
created_at: 'created_at',
|
|
87
|
+
modified_at: 'modified_at'
|
|
88
|
+
},
|
|
89
|
+
indexes: [{ columns: 'id', primary: true }, { columns: ['message_id'], unique: true }]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'message_statistics',
|
|
93
|
+
columns: {
|
|
94
|
+
id: 'id_uuid',
|
|
95
|
+
message_id: 'foreign_uuid',
|
|
96
|
+
statistics: 'json',
|
|
97
|
+
created_at: 'created_at',
|
|
98
|
+
modified_at: 'modified_at'
|
|
99
|
+
},
|
|
100
|
+
indexes: [{ columns: 'id', primary: true }, { columns: ['message_id'], unique: true }]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'message_queue',
|
|
104
|
+
columns: {
|
|
105
|
+
id: 'id_uuid',
|
|
106
|
+
message_id: 'foreign_uuid',
|
|
107
|
+
publish_metadata: 'json',
|
|
108
|
+
error: 'json',
|
|
109
|
+
created_at: 'created_at',
|
|
110
|
+
modified_at: 'modified_at'
|
|
111
|
+
},
|
|
112
|
+
indexes: [{ columns: 'id', primary: true }, { columns: ['message_id'], unique: true }]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'message_template',
|
|
116
|
+
columns: {
|
|
117
|
+
id: 'id_uuid',
|
|
118
|
+
name: 'string',
|
|
119
|
+
content: 'json',
|
|
120
|
+
created_at: 'created_at',
|
|
121
|
+
modified_at: 'modified_at'
|
|
122
|
+
},
|
|
123
|
+
indexes: [{ columns: 'id', primary: true }]
|
|
124
|
+
}
|
|
125
|
+
// message_summary / global_message*_summary stay out of core until messaging is
|
|
126
|
+
// finalized; Frakture deployments may still ship those views separately.
|
|
127
|
+
];
|
|
128
|
+
export default {
|
|
129
|
+
tables
|
|
130
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
{
|
|
2
|
+
menu: {
|
|
3
|
+
'message-menu': {
|
|
4
|
+
title: 'Messages',
|
|
5
|
+
icon: 'messages',
|
|
6
|
+
type: 'collapse',
|
|
7
|
+
children: {
|
|
8
|
+
campaigns: {
|
|
9
|
+
title: 'Campaigns',
|
|
10
|
+
icon: 'campaign',
|
|
11
|
+
url: '/campaign'
|
|
12
|
+
}
|
|
13
|
+
/* messages:{
|
|
14
|
+
title: 'Messages',
|
|
15
|
+
icon:'message',
|
|
16
|
+
url: '/message',
|
|
17
|
+
},
|
|
18
|
+
*/
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
routes: {
|
|
23
|
+
campaign: {
|
|
24
|
+
layout: 'full_width',
|
|
25
|
+
components: {
|
|
26
|
+
header: [
|
|
27
|
+
'Campaigns',
|
|
28
|
+
{
|
|
29
|
+
component: 'Button',
|
|
30
|
+
properties: {
|
|
31
|
+
icon: 'plus',
|
|
32
|
+
content: 'Add Campaign',
|
|
33
|
+
onClick: {
|
|
34
|
+
action: 'navigate',
|
|
35
|
+
url: '/campaign/create'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
main: [
|
|
41
|
+
{
|
|
42
|
+
component: 'RecordTable',
|
|
43
|
+
properties: {
|
|
44
|
+
table: 'campaign',
|
|
45
|
+
columns: [
|
|
46
|
+
{
|
|
47
|
+
title: 'Id',
|
|
48
|
+
dataIndex: 'id',
|
|
49
|
+
sorter: true,
|
|
50
|
+
width: '20%'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: 'Name',
|
|
54
|
+
dataIndex: 'name',
|
|
55
|
+
sorter: true,
|
|
56
|
+
template: '{{name}}',
|
|
57
|
+
width: '40%'
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
onRecord: {
|
|
61
|
+
onClick: {
|
|
62
|
+
action: 'navigate',
|
|
63
|
+
url: '/campaign/{{record.id}}'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
'campaign/create': {
|
|
72
|
+
layout: 'full_width',
|
|
73
|
+
components: {
|
|
74
|
+
main: [
|
|
75
|
+
{
|
|
76
|
+
path: 'edit',
|
|
77
|
+
component: 'RecordForm',
|
|
78
|
+
properties: {
|
|
79
|
+
table: 'campaign',
|
|
80
|
+
form: {
|
|
81
|
+
title: 'Create Campaign',
|
|
82
|
+
type: 'object',
|
|
83
|
+
required: [],
|
|
84
|
+
properties: {
|
|
85
|
+
name: { type: 'string', title: 'Name', default: '' }
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
uiSchema: {},
|
|
89
|
+
onSave: {
|
|
90
|
+
action: 'navigate',
|
|
91
|
+
url: '/campaign/{{record.id}}'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
'campaign/:id/*': {
|
|
99
|
+
layout: 'sidebar',
|
|
100
|
+
components: {
|
|
101
|
+
sidebar: {
|
|
102
|
+
items: [
|
|
103
|
+
{ component: 'Title', properties: { content: 'Emails' } },
|
|
104
|
+
{
|
|
105
|
+
component: 'Button',
|
|
106
|
+
properties: {
|
|
107
|
+
icon: 'plus',
|
|
108
|
+
content: 'Add Message Set',
|
|
109
|
+
onClick: {
|
|
110
|
+
action: 'object.create',
|
|
111
|
+
data: {
|
|
112
|
+
type: 'message_set',
|
|
113
|
+
attributes: {
|
|
114
|
+
label: 'Messages - {{date "MMMM, yyyy"}}',
|
|
115
|
+
campaign_id: '{{parameters.id}}'
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
redirect: '/campaign/{{parameters.id}}/message_set/{{record.id}}'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
component: 'RecordList',
|
|
124
|
+
properties: {
|
|
125
|
+
table: 'message_set',
|
|
126
|
+
conditions: [{ eql: 'campaign_id={{id}}' }],
|
|
127
|
+
onRecord: {
|
|
128
|
+
onClick: {
|
|
129
|
+
action: 'navigate',
|
|
130
|
+
url: '/campaign/{{parameters.id}}/message_set/{{record.id}}/'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
main: [
|
|
138
|
+
{ component: 'Title', properties: { content: 'Campaign Summary' } },
|
|
139
|
+
{
|
|
140
|
+
// 'path':'overview', //no path is the default
|
|
141
|
+
component: 'RecordDisplay',
|
|
142
|
+
properties: {
|
|
143
|
+
table: 'campaign',
|
|
144
|
+
id: '{{parameters.id}}',
|
|
145
|
+
template: '{{name}}'
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
message: {
|
|
152
|
+
layout: 'full_width',
|
|
153
|
+
components: {
|
|
154
|
+
main: [
|
|
155
|
+
{
|
|
156
|
+
component: 'RecordTable',
|
|
157
|
+
properties: {
|
|
158
|
+
table: 'message',
|
|
159
|
+
columns: [
|
|
160
|
+
{
|
|
161
|
+
title: 'Id',
|
|
162
|
+
dataIndex: 'id',
|
|
163
|
+
sorter: true,
|
|
164
|
+
width: '20%'
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
title: 'Publish Date',
|
|
168
|
+
dataIndex: 'publish_date',
|
|
169
|
+
sorter: true,
|
|
170
|
+
width: '40%'
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
title: 'Name',
|
|
174
|
+
dataIndex: 'name',
|
|
175
|
+
sorter: true,
|
|
176
|
+
template: '{{name}}',
|
|
177
|
+
width: '40%'
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
onRecord: {
|
|
181
|
+
onClick: {
|
|
182
|
+
action: 'navigate',
|
|
183
|
+
url: '/message/{{record.id}}'
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
'message/:id/*': {
|
|
192
|
+
layout: 'sidebar',
|
|
193
|
+
components: {
|
|
194
|
+
sidebar: [
|
|
195
|
+
{
|
|
196
|
+
component: 'RecordDisplay',
|
|
197
|
+
properties: {
|
|
198
|
+
table: 'message',
|
|
199
|
+
id: '{{parameters.id}}',
|
|
200
|
+
components: ['{{name}}']
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
main: [
|
|
205
|
+
{
|
|
206
|
+
// 'path':'overview', //no path is the default
|
|
207
|
+
component: 'RecordDisplay',
|
|
208
|
+
properties: {
|
|
209
|
+
table: 'message',
|
|
210
|
+
id: '{{parameters.id}}',
|
|
211
|
+
components: ['{{name}}']
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
path: 'source_codes',
|
|
216
|
+
label: 'Source Codes',
|
|
217
|
+
component: 'RecordDisplay',
|
|
218
|
+
properties: {
|
|
219
|
+
table: 'message',
|
|
220
|
+
id: '{{parameters.id}}',
|
|
221
|
+
includes: {
|
|
222
|
+
source_codes: {
|
|
223
|
+
table: 'source_code'
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
components: ['{{source_code}}']
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@engine9/schemas",
|
|
3
|
+
"version": "1.9.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Engine9 standard schemas: shared tables, transforms, searches, and segments",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --test person/upsert.test.js person_address/normalize.test.js person_email/upsert.test.js person_hash/hash.test.js person_phone/upsert.test.js stacks/stacks.test.js transaction/core/attribute.test.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@engine9/input-tools": "^2.4.1",
|
|
17
|
+
"@zerodep/address-normalize": "^2.4.10",
|
|
18
|
+
"handlebars": "^4.7.8",
|
|
19
|
+
"i18n-iso-countries": "^7.14.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"eslint": "^9.34.0",
|
|
23
|
+
"eslint-plugin-jsonc": "^2.15.1",
|
|
24
|
+
"eslint-plugin-n": "^17.21.3"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/person/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import schema from './schema.js';
|
|
2
|
+
import upsertPerson from './transforms/inbound/upsert_tables.js';
|
|
3
|
+
import normalizeFieldNames from './transforms/inbound/normalize_field_names.js';
|
|
4
|
+
import simpleMap from './transforms/simpleMap.js';
|
|
5
|
+
import appendName from './transforms/outbound/appendName.js';
|
|
6
|
+
import metrics from './metrics.js';
|
|
7
|
+
const metadata = {
|
|
8
|
+
name: '@engine9/schemas/person',
|
|
9
|
+
schemas: ['schema.js'],
|
|
10
|
+
// Inbound people pipeline slots -> transform export keys (woven by core when installed)
|
|
11
|
+
inbound: {
|
|
12
|
+
normalize: ['normalizeFieldNames'],
|
|
13
|
+
upsert: ['upsertPerson']
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const transforms = {
|
|
17
|
+
normalizeFieldNames,
|
|
18
|
+
upsertPerson,
|
|
19
|
+
simpleMap,
|
|
20
|
+
appendName
|
|
21
|
+
};
|
|
22
|
+
export const search = {
|
|
23
|
+
person: {
|
|
24
|
+
title: 'Primary Info',
|
|
25
|
+
form: {
|
|
26
|
+
title: 'Primary Info',
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
givenName: {
|
|
30
|
+
title: 'First Name',
|
|
31
|
+
type: 'string'
|
|
32
|
+
},
|
|
33
|
+
familyName: {
|
|
34
|
+
title: 'Last Name',
|
|
35
|
+
type: 'string'
|
|
36
|
+
},
|
|
37
|
+
ids: {
|
|
38
|
+
title: 'Person Ids (comma delimited)',
|
|
39
|
+
type: 'string'
|
|
40
|
+
},
|
|
41
|
+
idGreaterThanEqual: {
|
|
42
|
+
title: 'ID Greater than or equal to',
|
|
43
|
+
type: 'number'
|
|
44
|
+
},
|
|
45
|
+
idLessThan: {
|
|
46
|
+
title: 'ID less than',
|
|
47
|
+
type: 'number'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
required: []
|
|
51
|
+
},
|
|
52
|
+
optionsToEQL: (options) => {
|
|
53
|
+
const text = [];
|
|
54
|
+
const { ids, idGreaterThanEqual, idLessThan, givenName = '', familyName = '' } = options;
|
|
55
|
+
const conditions = [];
|
|
56
|
+
if (ids) {
|
|
57
|
+
let arr = ids || '';
|
|
58
|
+
if (typeof ids === 'string') arr = ids.split(',');
|
|
59
|
+
if (arr.length === 0) arr = [0];
|
|
60
|
+
conditions.push({ eql: `id in (${arr.map((p) => parseInt(p, 10)).join(',')})` });
|
|
61
|
+
text.push(`Person ID in ${arr.map((p) => parseInt(p, 10)).join(',')}`);
|
|
62
|
+
}
|
|
63
|
+
if (idGreaterThanEqual) {
|
|
64
|
+
conditions.push({ eql: `id>=${idGreaterThanEqual}` });
|
|
65
|
+
text.push(`Person ID greater or equal to ${idGreaterThanEqual}`);
|
|
66
|
+
}
|
|
67
|
+
if (idLessThan) {
|
|
68
|
+
conditions.push({ eql: `id<${idLessThan}` });
|
|
69
|
+
text.push(`Person ID less than ${idLessThan}`);
|
|
70
|
+
}
|
|
71
|
+
if (givenName.length > 0) {
|
|
72
|
+
text.push(`First name like '${givenName}'`);
|
|
73
|
+
conditions.push({ eql: `given_name like '${givenName}'` });
|
|
74
|
+
}
|
|
75
|
+
if (familyName.length > 0) {
|
|
76
|
+
text.push(`Last name like '${givenName}'`);
|
|
77
|
+
conditions.push({ eql: `family_name like '${familyName}'` });
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
text,
|
|
81
|
+
eql: {
|
|
82
|
+
table: 'person',
|
|
83
|
+
columns: ['id'],
|
|
84
|
+
conditions
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
export { metadata };
|
|
91
|
+
export { schema };
|
|
92
|
+
export { transforms };
|
|
93
|
+
export { metrics };
|
|
94
|
+
export default {
|
|
95
|
+
metadata,
|
|
96
|
+
schema,
|
|
97
|
+
transforms,
|
|
98
|
+
metrics,
|
|
99
|
+
search
|
|
100
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const people = () => {
|
|
2
|
+
return {
|
|
3
|
+
label: 'People',
|
|
4
|
+
description: '',
|
|
5
|
+
eql: {
|
|
6
|
+
table: 'person',
|
|
7
|
+
columns: [
|
|
8
|
+
{ eql: 'min(created_at)', name: 'first_date' },
|
|
9
|
+
{ eql: 'max(created_at)', name: 'last_date' },
|
|
10
|
+
{ eql: 'count', name: 'records' }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export default {
|
|
16
|
+
people
|
|
17
|
+
};
|
package/person/schema.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const tables = [
|
|
2
|
+
{
|
|
3
|
+
name: 'person',
|
|
4
|
+
columns: {
|
|
5
|
+
id: 'id',
|
|
6
|
+
given_name: 'string',
|
|
7
|
+
family_name: 'string',
|
|
8
|
+
created_at: 'created_at',
|
|
9
|
+
modified_at: 'modified_at'
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'person_identifier',
|
|
14
|
+
columns: {
|
|
15
|
+
id: 'id',
|
|
16
|
+
person_id: 'person_id',
|
|
17
|
+
source_input_id: 'uuid',
|
|
18
|
+
id_type: {
|
|
19
|
+
type: 'enum',
|
|
20
|
+
nullable: false,
|
|
21
|
+
default_value: '',
|
|
22
|
+
values: ['', 'remote_person_id', 'email_hash_v1', 'phone_hash_v1', 'delegate']
|
|
23
|
+
},
|
|
24
|
+
id_value: 'id_string'
|
|
25
|
+
},
|
|
26
|
+
indexes: [
|
|
27
|
+
{ columns: ['person_id'] },
|
|
28
|
+
{ columns: ['id_value'] },
|
|
29
|
+
// useful for finding anyone related to an input
|
|
30
|
+
// same input can't have the same id value twice
|
|
31
|
+
{ columns: ['source_input_id', 'id_value', 'person_id'], unique: true }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
/*
|
|
35
|
+
//sometimes useful, debating whether useful enough to be global
|
|
36
|
+
create view person_identifier_summary
|
|
37
|
+
as select p.*,
|
|
38
|
+
input.remote_input_id,
|
|
39
|
+
input.remote_input_name,input_type,
|
|
40
|
+
plugin_id,plugin.path,plugin.name,plugin.remote_plugin_id
|
|
41
|
+
from person_identifier p
|
|
42
|
+
join input on (p.source_input_id=input.id)
|
|
43
|
+
join plugin on (input.plugin_id=plugin.id); */
|
|
44
|
+
];
|
|
45
|
+
export default {
|
|
46
|
+
tables
|
|
47
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export async function transform({ batch }) {
|
|
2
|
+
batch.forEach((row) => {
|
|
3
|
+
const normalized = {};
|
|
4
|
+
Object.entries(row).forEach(([key, value]) => {
|
|
5
|
+
// Keep '.' so person_custom table.column keys survive normalize.
|
|
6
|
+
const normalizedKey = String(key).toLowerCase().replace(/[^a-z0-9_.]/g, '_');
|
|
7
|
+
normalized[normalizedKey] = value;
|
|
8
|
+
});
|
|
9
|
+
Object.keys(row).forEach((key) => delete row[key]);
|
|
10
|
+
Object.assign(row, normalized);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
transform
|
|
16
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const bindings = {
|
|
2
|
+
tablesToUpsert: { path: 'sql.tables.upsert' }
|
|
3
|
+
};
|
|
4
|
+
const NAME_COLUMNS = ['given_name', 'family_name'];
|
|
5
|
+
|
|
6
|
+
export async function transform({ batch, tablesToUpsert }) {
|
|
7
|
+
if (batch.length === 0) return;
|
|
8
|
+
batch.forEach((o) => {
|
|
9
|
+
const present = NAME_COLUMNS.filter((column) => Object.hasOwn(o, column));
|
|
10
|
+
if (present.length === 0) return;
|
|
11
|
+
if (!o.person_id) throw new Error('Cannot update name, no person_id');
|
|
12
|
+
tablesToUpsert.person = tablesToUpsert.person || [];
|
|
13
|
+
const row = { id: o.person_id };
|
|
14
|
+
for (const column of present) row[column] = o[column];
|
|
15
|
+
tablesToUpsert.person.push(row);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export default {
|
|
19
|
+
bindings,
|
|
20
|
+
transform
|
|
21
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const description = 'Append a name';
|
|
2
|
+
export const bindings = {
|
|
3
|
+
names: {
|
|
4
|
+
path: 'sql.query',
|
|
5
|
+
options: {
|
|
6
|
+
table: 'person',
|
|
7
|
+
columns: ['id', 'given_name', 'family_name'],
|
|
8
|
+
lookup: [
|
|
9
|
+
{
|
|
10
|
+
personIdField: 'person_id',
|
|
11
|
+
tableLookupField: 'id'
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export const transform = ({ batch, names }) => {
|
|
18
|
+
const nameMap = names.reduce((a, b) => {
|
|
19
|
+
a[b.id] = a[b.id] || b;
|
|
20
|
+
return a;
|
|
21
|
+
}, {});
|
|
22
|
+
batch.forEach((data) => {
|
|
23
|
+
const o = nameMap[data.person_id] || {};
|
|
24
|
+
data.given_name = o.given_name || null;
|
|
25
|
+
data.family_name = o.family_name || null;
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
export default {
|
|
29
|
+
description,
|
|
30
|
+
bindings,
|
|
31
|
+
transform
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import handlebars from 'handlebars';
|
|
2
|
+
|
|
3
|
+
const moduleExports = {
|
|
4
|
+
async transform({ batch, options }) {
|
|
5
|
+
if (!options._compiledMap) {
|
|
6
|
+
if (!options?.map) {
|
|
7
|
+
throw new Error('Error with transform, no options.map provided');
|
|
8
|
+
}
|
|
9
|
+
options._compiledMap = {};
|
|
10
|
+
Object.entries(options.map).forEach(([k, source]) => {
|
|
11
|
+
if (k === '*') {
|
|
12
|
+
options._compiledMap['*'] = '*'; // just applies all the other fields
|
|
13
|
+
} else {
|
|
14
|
+
options._compiledMap[k] = handlebars.compile(source);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const newBatch = batch.map((o) => {
|
|
19
|
+
const out = {};
|
|
20
|
+
Object.entries(options._compiledMap).forEach(([k, func]) => {
|
|
21
|
+
if (k === '*') {
|
|
22
|
+
Object.assign(out, o);
|
|
23
|
+
} else if (out[k] === undefined) {
|
|
24
|
+
// first one wins, specify it first if you want ahead of the '*'
|
|
25
|
+
out[k] = func(o);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return out;
|
|
29
|
+
});
|
|
30
|
+
return { batch: newBatch };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default moduleExports;
|