@engine9-io/input-tools 1.9.10 → 2.0.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/ForEachEntry.js +18 -43
- package/ValidatingReadable.js +3 -6
- package/buildSamplePackets.js +11 -16
- package/eslint.config.mjs +15 -11
- package/file/FileUtilities.js +976 -1048
- package/file/GoogleDrive.js +32 -38
- package/file/Parquet.js +112 -124
- package/file/R2.js +27 -32
- package/file/S3.js +259 -293
- package/file/tools.js +334 -326
- package/index.js +63 -78
- package/package.json +2 -1
- package/test/cli.js +3 -4
- package/test/file.js +6 -7
- package/test/processing/bigDataMessage.js +8 -10
- package/test/processing/forEach.js +6 -8
- package/test/processing/forEachResume.js +6 -8
- package/test/processing/message.js +31 -39
- package/test/processing/zip.js +6 -7
- package/test/uuid.js +6 -11
- package/timelineTypes.js +2 -24
package/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import debug$0 from 'debug';
|
|
5
|
+
import unzipper from 'unzipper';
|
|
6
|
+
import uuid from 'uuid';
|
|
7
|
+
import archiver from 'archiver';
|
|
8
|
+
import handlebars from 'handlebars';
|
|
9
|
+
import FileUtilities from './file/FileUtilities.js';
|
|
10
|
+
import tools from './file/tools.js';
|
|
11
|
+
import ForEachEntry from './ForEachEntry.js';
|
|
12
|
+
import { TIMELINE_ENTRY_TYPES } from './timelineTypes.js';
|
|
13
|
+
const debug = debug$0('@engine9/input-tools');
|
|
14
|
+
const { v4: uuidv4, v5: uuidv5, v7: uuidv7, validate: uuidIsValid } = uuid;
|
|
15
15
|
const {
|
|
16
16
|
appendPostfix,
|
|
17
17
|
bool,
|
|
@@ -30,40 +30,28 @@ const {
|
|
|
30
30
|
streamPacket,
|
|
31
31
|
makeStrings,
|
|
32
32
|
writeTempFile
|
|
33
|
-
} =
|
|
34
|
-
|
|
35
|
-
const ForEachEntry = require('./ForEachEntry');
|
|
36
|
-
|
|
37
|
-
const { TIMELINE_ENTRY_TYPES } = require('./timelineTypes');
|
|
38
|
-
|
|
33
|
+
} = tools;
|
|
39
34
|
function getFormattedDate(dateObject, format = 'MMM DD,YYYY') {
|
|
40
35
|
let d = dateObject;
|
|
41
36
|
if (d === 'now') d = new Date();
|
|
42
37
|
if (d) return dayjs(d).format(format);
|
|
43
38
|
return '';
|
|
44
39
|
}
|
|
45
|
-
|
|
46
40
|
handlebars.registerHelper('date', (d, f) => {
|
|
47
41
|
let format;
|
|
48
42
|
if (typeof f === 'string') format = f;
|
|
49
43
|
return getFormattedDate(d, format);
|
|
50
44
|
});
|
|
51
45
|
handlebars.registerHelper('json', (d) => JSON.stringify(d));
|
|
52
|
-
|
|
53
46
|
handlebars.registerHelper('uuid', () => uuidv7());
|
|
54
|
-
|
|
55
47
|
handlebars.registerHelper('percent', (a, b) => `${((100 * a) / b).toFixed(2)}%`);
|
|
56
|
-
|
|
57
48
|
handlebars.registerHelper('or', (a, b, c) => a || b || c);
|
|
58
|
-
|
|
59
49
|
async function list(_path) {
|
|
60
50
|
const directory = await unzipper.Open.file(_path);
|
|
61
|
-
|
|
62
51
|
return new Promise((resolve, reject) => {
|
|
63
52
|
directory.files[0].stream().pipe(fs.createWriteStream('firstFile')).on('error', reject).on('finish', resolve);
|
|
64
53
|
});
|
|
65
54
|
}
|
|
66
|
-
|
|
67
55
|
async function extract(_path, _file) {
|
|
68
56
|
const directory = await unzipper.Open(_path);
|
|
69
57
|
// return directory.files.map((f) => f.path);
|
|
@@ -73,7 +61,6 @@ async function extract(_path, _file) {
|
|
|
73
61
|
file.stream().pipe(fs.createWriteStream(tempFilename)).on('error', reject).on('finish', resolve);
|
|
74
62
|
});
|
|
75
63
|
}
|
|
76
|
-
|
|
77
64
|
function appendFiles(existingFiles, _newFiles, options) {
|
|
78
65
|
const newFiles = getStringArray(_newFiles);
|
|
79
66
|
if (newFiles.length === 0) return;
|
|
@@ -82,7 +69,6 @@ function appendFiles(existingFiles, _newFiles, options) {
|
|
|
82
69
|
if (!dateCreated) dateCreated = new Date().toISOString();
|
|
83
70
|
let arr = newFiles;
|
|
84
71
|
if (!Array.isArray(newFiles)) arr = [arr];
|
|
85
|
-
|
|
86
72
|
arr.forEach((p) => {
|
|
87
73
|
const item = {
|
|
88
74
|
type,
|
|
@@ -90,13 +76,11 @@ function appendFiles(existingFiles, _newFiles, options) {
|
|
|
90
76
|
isNew: true,
|
|
91
77
|
dateCreated
|
|
92
78
|
};
|
|
93
|
-
|
|
94
79
|
if (typeof p === 'string') {
|
|
95
80
|
item.originalFilename = path.resolve(process.cwd(), p);
|
|
96
81
|
} else {
|
|
97
82
|
item.originalFilename = path.resolve(process.cwd(), item.originalFilename);
|
|
98
83
|
}
|
|
99
|
-
|
|
100
84
|
const file = item.originalFilename.split(path.sep).pop();
|
|
101
85
|
item.path = `${type}/${file}`;
|
|
102
86
|
const existingFile = existingFiles.find((f) => f.path === item.path);
|
|
@@ -104,7 +88,6 @@ function appendFiles(existingFiles, _newFiles, options) {
|
|
|
104
88
|
existingFiles.push(item);
|
|
105
89
|
});
|
|
106
90
|
}
|
|
107
|
-
|
|
108
91
|
async function create(options) {
|
|
109
92
|
const {
|
|
110
93
|
accountId = 'engine9',
|
|
@@ -116,16 +99,13 @@ async function create(options) {
|
|
|
116
99
|
statisticsFiles = [] // files with aggregate statistics
|
|
117
100
|
} = options;
|
|
118
101
|
if (options.peopleFiles) throw new Error('Unknown option: peopleFiles, did you mean personFiles?');
|
|
119
|
-
|
|
120
102
|
const files = [];
|
|
121
103
|
const dateCreated = new Date().toISOString();
|
|
122
104
|
appendFiles(files, messageFiles, { type: 'message', dateCreated });
|
|
123
105
|
appendFiles(files, personFiles, { type: 'person', dateCreated });
|
|
124
106
|
appendFiles(files, timelineFiles, { type: 'timeline', dateCreated });
|
|
125
107
|
appendFiles(files, statisticsFiles, { type: 'statistics', dateCreated });
|
|
126
|
-
|
|
127
108
|
const zipFilename = target || (await getTempFilename({ postfix: '.packet.zip' }));
|
|
128
|
-
|
|
129
109
|
const manifest = {
|
|
130
110
|
accountId,
|
|
131
111
|
source: {
|
|
@@ -134,7 +114,6 @@ async function create(options) {
|
|
|
134
114
|
dateCreated,
|
|
135
115
|
files
|
|
136
116
|
};
|
|
137
|
-
|
|
138
117
|
// create a file to stream archive data to.
|
|
139
118
|
const output = fs.createWriteStream(zipFilename);
|
|
140
119
|
const archive = archiver('zip', {
|
|
@@ -152,37 +131,30 @@ async function create(options) {
|
|
|
152
131
|
bytes: archive.pointer()
|
|
153
132
|
});
|
|
154
133
|
});
|
|
155
|
-
|
|
156
134
|
// This event is fired when the data source is drained no matter what was the data source.
|
|
157
135
|
// It is not part of this library but rather from the NodeJS Stream API.
|
|
158
136
|
// @see: https://nodejs.org/api/stream.html#stream_event_end
|
|
159
137
|
output.on('end', () => {
|
|
160
138
|
// debug('end event -- Data has been drained');
|
|
161
139
|
});
|
|
162
|
-
|
|
163
140
|
// warnings could be file not founds, etc, but we error even on those
|
|
164
141
|
archive.on('warning', (err) => {
|
|
165
142
|
reject(err);
|
|
166
143
|
});
|
|
167
|
-
|
|
168
144
|
// good practice to catch this error explicitly
|
|
169
145
|
archive.on('error', (err) => {
|
|
170
146
|
reject(err);
|
|
171
147
|
});
|
|
172
|
-
|
|
173
148
|
archive.pipe(output);
|
|
174
|
-
|
|
175
149
|
files.forEach(({ path: name, originalFilename }) => archive.file(originalFilename, { name }));
|
|
176
150
|
files.forEach((f) => {
|
|
177
151
|
delete f.originalFilename;
|
|
178
152
|
delete f.isNew;
|
|
179
153
|
});
|
|
180
|
-
|
|
181
154
|
archive.append(Buffer.from(JSON.stringify(manifest, null, 4), 'utf8'), { name: 'manifest.json' });
|
|
182
155
|
archive.finalize();
|
|
183
156
|
});
|
|
184
157
|
}
|
|
185
|
-
|
|
186
158
|
function intToByteArray(_v) {
|
|
187
159
|
// we want to represent the input as a 8-bytes array
|
|
188
160
|
const byteArray = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
@@ -192,14 +164,12 @@ function intToByteArray(_v) {
|
|
|
192
164
|
byteArray[index] = byte;
|
|
193
165
|
v = (v - byte) / 256;
|
|
194
166
|
}
|
|
195
|
-
|
|
196
167
|
return byteArray;
|
|
197
168
|
}
|
|
198
169
|
function getPluginUUID(uniqueNamespaceLikeDomainName, valueWithinNamespace) {
|
|
199
170
|
// Random custom namespace for plugins -- not secure, just a namespace:
|
|
200
171
|
return uuidv5(`${uniqueNamespaceLikeDomainName}::${valueWithinNamespace}`, 'f9e1024d-21ac-473c-bac6-64796dd771dd');
|
|
201
172
|
}
|
|
202
|
-
|
|
203
173
|
function getInputUUID(a, b) {
|
|
204
174
|
let pluginId = a;
|
|
205
175
|
let remoteInputId = b;
|
|
@@ -207,7 +177,6 @@ function getInputUUID(a, b) {
|
|
|
207
177
|
pluginId = a.pluginId;
|
|
208
178
|
remoteInputId = a.remoteInputId;
|
|
209
179
|
}
|
|
210
|
-
|
|
211
180
|
if (!pluginId) throw new Error('getInputUUID: Cowardly rejecting a blank plugin_id');
|
|
212
181
|
if (!uuidIsValid(pluginId)) throw new Error(`Invalid pluginId:${pluginId}, should be a UUID`);
|
|
213
182
|
const rid = (remoteInputId || '').trim();
|
|
@@ -216,7 +185,6 @@ function getInputUUID(a, b) {
|
|
|
216
185
|
// 3d0e5d99-6ba9-4fab-9bb2-c32304d3df8e
|
|
217
186
|
return uuidv5(`${pluginId}:${rid}`, '3d0e5d99-6ba9-4fab-9bb2-c32304d3df8e');
|
|
218
187
|
}
|
|
219
|
-
|
|
220
188
|
const timestampMatch = /^\d{13}$/;
|
|
221
189
|
function dateFromString(s) {
|
|
222
190
|
if (typeof s === 'number') return new Date(s);
|
|
@@ -225,7 +193,6 @@ function dateFromString(s) {
|
|
|
225
193
|
}
|
|
226
194
|
return new Date(s);
|
|
227
195
|
}
|
|
228
|
-
|
|
229
196
|
function getUUIDv7(date, inputUuid) {
|
|
230
197
|
/* optional date and input UUID */
|
|
231
198
|
const uuid = inputUuid || uuidv7();
|
|
@@ -234,7 +201,6 @@ function getUUIDv7(date, inputUuid) {
|
|
|
234
201
|
const d = dateFromString(date);
|
|
235
202
|
// isNaN behaves differently than Number.isNaN -- we're actually going for the
|
|
236
203
|
// attempted conversion here
|
|
237
|
-
|
|
238
204
|
if (isNaN(d)) throw new Error(`getUUIDv7 got an invalid date:${date || '<blank>'}`);
|
|
239
205
|
const dateBytes = intToByteArray(d.getTime()).reverse();
|
|
240
206
|
dateBytes.slice(2, 8).forEach((b, i) => {
|
|
@@ -248,13 +214,12 @@ function getUUIDTimestamp(uuid) {
|
|
|
248
214
|
const ts = parseInt(`${uuid}`.replace(/-/g, '').slice(0, 12), 16);
|
|
249
215
|
return new Date(ts);
|
|
250
216
|
}
|
|
251
|
-
|
|
252
217
|
function getEntryTypeId(o, { defaults = {} } = {}) {
|
|
253
218
|
let id = o.entry_type_id || defaults.entry_type_id;
|
|
254
219
|
if (id) return id;
|
|
255
220
|
const etype = o.entry_type || defaults.entry_type;
|
|
256
221
|
if (!etype) {
|
|
257
|
-
throw new Error('No entry_type, nor entry_type_id specified, specify a
|
|
222
|
+
throw new Error('No entry_type, nor entry_type_id specified, specify one to generate a timeline suitable ID');
|
|
258
223
|
}
|
|
259
224
|
id = TIMELINE_ENTRY_TYPES[etype];
|
|
260
225
|
if (id === undefined) throw new Error(`Invalid entry_type: ${etype}`);
|
|
@@ -263,32 +228,27 @@ function getEntryTypeId(o, { defaults = {} } = {}) {
|
|
|
263
228
|
function getEntryType(o, defaults = {}) {
|
|
264
229
|
let etype = o.entry_type || defaults.entry_type;
|
|
265
230
|
if (etype) return etype;
|
|
266
|
-
|
|
267
231
|
const id = o.entry_type_id || defaults.entry_type_id;
|
|
268
|
-
|
|
269
232
|
etype = TIMELINE_ENTRY_TYPES[id];
|
|
270
233
|
if (etype === undefined) throw new Error(`Invalid entry_type: ${etype}`);
|
|
271
234
|
return etype;
|
|
272
235
|
}
|
|
273
|
-
|
|
274
236
|
const requiredTimelineEntryFields = ['ts', 'entry_type_id', 'input_id', 'person_id'];
|
|
275
|
-
|
|
276
237
|
function getTimelineEntryUUID(inputObject, { defaults = {} } = {}) {
|
|
277
238
|
const o = { ...defaults, ...inputObject };
|
|
278
239
|
/*
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
240
|
+
Outside systems CAN specify a unique UUID as remote_entry_uuid,
|
|
241
|
+
which will be used for updates, etc.
|
|
242
|
+
If not, it will be generated using whatever info we have
|
|
243
|
+
*/
|
|
283
244
|
if (o.remote_entry_uuid) {
|
|
284
245
|
if (!uuidIsValid(o.remote_entry_uuid)) throw new Error('Invalid remote_entry_uuid, it must be a UUID');
|
|
285
246
|
return o.remote_entry_uuid;
|
|
286
247
|
}
|
|
287
248
|
/*
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
249
|
+
Outside systems CAN specify a unique remote_entry_id
|
|
250
|
+
If not, it will be generated using whatever info we have
|
|
251
|
+
*/
|
|
292
252
|
if (o.remote_entry_id) {
|
|
293
253
|
// get a temp ID
|
|
294
254
|
if (!o.input_id)
|
|
@@ -299,18 +259,14 @@ function getTimelineEntryUUID(inputObject, { defaults = {} } = {}) {
|
|
|
299
259
|
// may not match this standard, uuid sorting isn't guaranteed
|
|
300
260
|
return getUUIDv7(o.ts, uuid);
|
|
301
261
|
}
|
|
302
|
-
|
|
303
|
-
|
|
262
|
+
o.entry_type_id = getEntryTypeId(o);
|
|
304
263
|
const missing = requiredTimelineEntryFields.filter((d) => o[d] === undefined); // 0 could be an entry type value
|
|
305
|
-
|
|
306
264
|
if (missing.length > 0) throw new Error(`Missing required fields to append an entry_id:${missing.join(',')}`);
|
|
307
265
|
const ts = new Date(o.ts);
|
|
308
266
|
// isNaN behaves differently than Number.isNaN -- we're actually going for the
|
|
309
267
|
// attempted conversion here
|
|
310
|
-
|
|
311
268
|
if (isNaN(ts)) throw new Error(`getTimelineEntryUUID got an invalid date:${o.ts || '<blank>'}`);
|
|
312
|
-
const idString = `${ts.toISOString()}-${o.person_id}-${entry_type_id}-${o.source_code_id || 0}`;
|
|
313
|
-
|
|
269
|
+
const idString = `${ts.toISOString()}-${o.person_id}-${o.entry_type_id}-${o.source_code_id || 0}`;
|
|
314
270
|
if (!uuidIsValid(o.input_id)) {
|
|
315
271
|
throw new Error(`Invalid input_id:'${o.input_id}', type ${typeof o.input_id} -- should be a uuid`);
|
|
316
272
|
}
|
|
@@ -321,13 +277,11 @@ function getTimelineEntryUUID(inputObject, { defaults = {} } = {}) {
|
|
|
321
277
|
// may not match this standard, uuid sorting isn't guaranteed
|
|
322
278
|
return getUUIDv7(ts, uuid);
|
|
323
279
|
}
|
|
324
|
-
|
|
325
280
|
function getDateRangeArray(startDate, endDate) {
|
|
326
281
|
const start = new Date(startDate);
|
|
327
282
|
const end = new Date(endDate);
|
|
328
283
|
const result = [];
|
|
329
284
|
const msInDay = 24 * 60 * 60 * 1000;
|
|
330
|
-
|
|
331
285
|
function addDays(date, days) {
|
|
332
286
|
const d = new Date(date);
|
|
333
287
|
d.setDate(d.getDate() + days);
|
|
@@ -343,13 +297,10 @@ function getDateRangeArray(startDate, endDate) {
|
|
|
343
297
|
d.setFullYear(d.getFullYear() + years);
|
|
344
298
|
return d;
|
|
345
299
|
}
|
|
346
|
-
|
|
347
300
|
const diffDays = Math.floor((end - start) / msInDay);
|
|
348
301
|
const diffMonths = (end.getFullYear() - start.getFullYear()) * 12 + (end.getMonth() - start.getMonth());
|
|
349
302
|
const diffYears = end.getFullYear() - start.getFullYear();
|
|
350
|
-
|
|
351
303
|
let current = new Date(start);
|
|
352
|
-
|
|
353
304
|
let stepFn;
|
|
354
305
|
if (diffDays < 10) {
|
|
355
306
|
stepFn = (date) => addDays(date, 1);
|
|
@@ -364,7 +315,6 @@ function getDateRangeArray(startDate, endDate) {
|
|
|
364
315
|
} else {
|
|
365
316
|
stepFn = (date) => addYears(date, 1);
|
|
366
317
|
}
|
|
367
|
-
|
|
368
318
|
while (current <= end) {
|
|
369
319
|
result.push(new Date(current));
|
|
370
320
|
const next = stepFn(current);
|
|
@@ -377,7 +327,6 @@ function getDateRangeArray(startDate, endDate) {
|
|
|
377
327
|
}
|
|
378
328
|
return result;
|
|
379
329
|
}
|
|
380
|
-
|
|
381
330
|
class ObjectError extends Error {
|
|
382
331
|
constructor(data) {
|
|
383
332
|
if (typeof data === 'string') {
|
|
@@ -394,8 +343,44 @@ class ObjectError extends Error {
|
|
|
394
343
|
}
|
|
395
344
|
}
|
|
396
345
|
}
|
|
397
|
-
|
|
398
|
-
|
|
346
|
+
export { appendPostfix };
|
|
347
|
+
export { bool };
|
|
348
|
+
export { create };
|
|
349
|
+
export { list };
|
|
350
|
+
export { downloadFile };
|
|
351
|
+
export { extract };
|
|
352
|
+
export { ForEachEntry };
|
|
353
|
+
export { FileUtilities };
|
|
354
|
+
export { getBatchTransform };
|
|
355
|
+
export { getDateRangeArray };
|
|
356
|
+
export { getDebatchTransform };
|
|
357
|
+
export { getEntryType };
|
|
358
|
+
export { getEntryTypeId };
|
|
359
|
+
export { getFile };
|
|
360
|
+
export { getManifest };
|
|
361
|
+
export { getStringArray };
|
|
362
|
+
export { getTempDir };
|
|
363
|
+
export { getTempFilename };
|
|
364
|
+
export { getTimelineEntryUUID };
|
|
365
|
+
export { getPacketFiles };
|
|
366
|
+
export { getPluginUUID };
|
|
367
|
+
export { getInputUUID };
|
|
368
|
+
export { getUUIDv7 };
|
|
369
|
+
export { getUUIDTimestamp };
|
|
370
|
+
export { handlebars };
|
|
371
|
+
export { isValidDate };
|
|
372
|
+
export { makeStrings };
|
|
373
|
+
export { ObjectError };
|
|
374
|
+
export { parseJSON5 };
|
|
375
|
+
export { relativeDate };
|
|
376
|
+
export { streamPacket };
|
|
377
|
+
export { TIMELINE_ENTRY_TYPES };
|
|
378
|
+
export { writeTempFile };
|
|
379
|
+
export { uuidIsValid };
|
|
380
|
+
export { uuidv4 };
|
|
381
|
+
export { uuidv5 };
|
|
382
|
+
export { uuidv7 };
|
|
383
|
+
export default {
|
|
399
384
|
appendPostfix,
|
|
400
385
|
bool,
|
|
401
386
|
create,
|
package/package.json
CHANGED
package/test/cli.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import yargs from 'yargs/yargs';
|
|
2
|
+
import methods from '../index.js';
|
|
3
|
+
const argv = yargs(process.argv.slice(2)).parse();
|
|
4
4
|
async function run() {
|
|
5
5
|
if (typeof methods[argv._[0]] !== 'function') throw new Error(`${argv._[0]} is not a function`);
|
|
6
6
|
const output = await methods[argv._[0]](argv);
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
7
|
console.log(output);
|
|
9
8
|
}
|
|
10
9
|
run();
|
package/test/file.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import { FileUtilities } from '../index.js';
|
|
5
|
+
const { it } = nodetest;
|
|
6
|
+
const debug = debug$0('files');
|
|
7
7
|
it('Should list a directory', async () => {
|
|
8
8
|
const futil = new FileUtilities({ accountId: 'test' });
|
|
9
9
|
let files = await futil.list({ directory: '.' });
|
|
@@ -14,7 +14,6 @@ it('Should list a directory', async () => {
|
|
|
14
14
|
let endTest = await futil.list({ directory: '.', end: '1900-01-01' });
|
|
15
15
|
assert(endTest.length === 0, 'Should NOT have any files before past end date');
|
|
16
16
|
});
|
|
17
|
-
|
|
18
17
|
it('Should be able to analyze CSV files with and without header lines', async () => {
|
|
19
18
|
const futil = new FileUtilities({ accountId: 'test' });
|
|
20
19
|
const f1 = await futil.columns({ filename: __dirname + '/sample/fileWithHead.csv' });
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import promises from 'node:timers/promises';
|
|
5
|
+
import { ForEachEntry } from '../../index.js';
|
|
6
|
+
const { describe, it } = nodetest;
|
|
7
|
+
const debug = debug$0('test:big-data');
|
|
8
|
+
const { setTimeout } = promises;
|
|
9
9
|
describe('big-data message: forEachPerson', async () => {
|
|
10
10
|
it('message: forEachPerson should loop through 1000000 sample people', async () => {
|
|
11
11
|
const messageContent = [];
|
|
12
12
|
let counter = 0;
|
|
13
13
|
const forEach = new ForEachEntry();
|
|
14
|
-
|
|
15
14
|
const output = await forEach.process({
|
|
16
15
|
// packet: '../1000000_person_message.packet.zip',
|
|
17
16
|
filename: '../1000000_person_message.packet/person/1000000_fake_people.csv',
|
|
@@ -47,7 +46,6 @@ describe('big-data message: forEachPerson', async () => {
|
|
|
47
46
|
}
|
|
48
47
|
});
|
|
49
48
|
debug(output);
|
|
50
|
-
|
|
51
49
|
assert.equal(counter, 1000000, `Expected to loop through 1000000 people, actual:${counter}`);
|
|
52
50
|
});
|
|
53
51
|
debug('Completed all tests');
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import { ForEachEntry } from '../../index.js';
|
|
5
|
+
const { describe, it } = nodetest;
|
|
6
|
+
const debug = debug$0('test/forEach');
|
|
7
7
|
describe('Test Person File For Each', async () => {
|
|
8
8
|
it('forEachPerson Should loop through 1000 sample people', async () => {
|
|
9
9
|
let counter = 0;
|
|
@@ -24,7 +24,6 @@ describe('Test Person File For Each', async () => {
|
|
|
24
24
|
},
|
|
25
25
|
async transform(props) {
|
|
26
26
|
const { batch, timelineOutputFileStream, sampleOutputFileStream } = props;
|
|
27
|
-
|
|
28
27
|
batch.forEach((p) => {
|
|
29
28
|
if (Math.random() > 0.9) {
|
|
30
29
|
sampleOutputFileStream.push({
|
|
@@ -41,7 +40,6 @@ describe('Test Person File For Each', async () => {
|
|
|
41
40
|
entry_type: 'EMAIL_DELIVERED'
|
|
42
41
|
});
|
|
43
42
|
});
|
|
44
|
-
|
|
45
43
|
batch.forEach(() => {
|
|
46
44
|
counter += 1;
|
|
47
45
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import { ForEachEntry } from '../../index.js';
|
|
5
|
+
const { describe, it } = nodetest;
|
|
6
|
+
const debug = debug$0('test/forEach');
|
|
7
7
|
describe('Test Person File For Each', async () => {
|
|
8
8
|
it('forEachPerson Should loop through 1000 sample people', async () => {
|
|
9
9
|
let counter = 0;
|
|
@@ -24,7 +24,6 @@ describe('Test Person File For Each', async () => {
|
|
|
24
24
|
},
|
|
25
25
|
async transform(props) {
|
|
26
26
|
const { batch, timelineOutputFileStream, sampleOutputFileStream } = props;
|
|
27
|
-
|
|
28
27
|
batch.forEach((p) => {
|
|
29
28
|
if (Math.random() > 0.9) {
|
|
30
29
|
sampleOutputFileStream.push({
|
|
@@ -41,7 +40,6 @@ describe('Test Person File For Each', async () => {
|
|
|
41
40
|
entry_type: 'EMAIL_DELIVERED'
|
|
42
41
|
});
|
|
43
42
|
});
|
|
44
|
-
|
|
45
43
|
batch.forEach(() => {
|
|
46
44
|
counter += 1;
|
|
47
45
|
});
|
|
@@ -1,47 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const { forEachPerson } =
|
|
8
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import index from '../../index.js';
|
|
5
|
+
const { describe, it } = nodetest;
|
|
6
|
+
const debug = debug$0('message');
|
|
7
|
+
const { forEachPerson } = index;
|
|
9
8
|
describe('Test Person Packet Message For Each', async () => {
|
|
10
9
|
it('message: forEachPerson should loop through 1000 sample people', async () => {
|
|
11
10
|
const messageContent = [];
|
|
12
11
|
let counter = 0;
|
|
13
|
-
const results = await forEachPerson(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
handlebars: { type: 'handlebars' },
|
|
21
|
-
},
|
|
22
|
-
async transform({
|
|
23
|
-
batch,
|
|
24
|
-
message,
|
|
25
|
-
handlebars,
|
|
26
|
-
timelineOutputStream,
|
|
27
|
-
}) {
|
|
28
|
-
const template = handlebars.compile(message.content.text);
|
|
29
|
-
batch.forEach((person) => {
|
|
30
|
-
messageContent.push(template(person));
|
|
31
|
-
});
|
|
32
|
-
batch.forEach(() => { counter += 1; });
|
|
33
|
-
batch.forEach((p) => {
|
|
34
|
-
timelineOutputStream.push(
|
|
35
|
-
{
|
|
36
|
-
person_id: p.person_id,
|
|
37
|
-
email: p.email,
|
|
38
|
-
entry_type: 'EMAIL_DELIVERED',
|
|
39
|
-
},
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
},
|
|
12
|
+
const results = await forEachPerson({
|
|
13
|
+
packet: 'test/sample/1000_message.packet.zip',
|
|
14
|
+
batchSize: 50,
|
|
15
|
+
bindings: {
|
|
16
|
+
timelineOutputStream: { type: 'output.timeline' },
|
|
17
|
+
message: { type: 'packet.message' },
|
|
18
|
+
handlebars: { type: 'handlebars' }
|
|
43
19
|
},
|
|
44
|
-
|
|
20
|
+
async transform({ batch, message, handlebars, timelineOutputStream }) {
|
|
21
|
+
const template = handlebars.compile(message.content.text);
|
|
22
|
+
batch.forEach((person) => {
|
|
23
|
+
messageContent.push(template(person));
|
|
24
|
+
});
|
|
25
|
+
batch.forEach(() => {
|
|
26
|
+
counter += 1;
|
|
27
|
+
});
|
|
28
|
+
batch.forEach((p) => {
|
|
29
|
+
timelineOutputStream.push({
|
|
30
|
+
person_id: p.person_id,
|
|
31
|
+
email: p.email,
|
|
32
|
+
entry_type: 'EMAIL_DELIVERED'
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
45
37
|
debug(results);
|
|
46
38
|
assert.equal(counter, 1000, `Expected to loop through 1000 people, actual:${counter}`);
|
|
47
39
|
});
|
package/test/processing/zip.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug from 'debug';
|
|
4
|
+
import { create, getManifest } from '../../index.js';
|
|
5
|
+
const { describe, it } = nodetest;
|
|
7
6
|
describe('Test Person Packet Creator', async () => {
|
|
8
7
|
const pfile = './test/sample/message/5_fake_people.csv';
|
|
9
8
|
it(`should create a zip file from directory ${process.cwd()} with path ${pfile}`, async () => {
|
|
10
9
|
const out = await create({
|
|
11
10
|
personFiles: [pfile],
|
|
12
|
-
messageFiles: 'test/sample/message/message.json5'
|
|
11
|
+
messageFiles: 'test/sample/message/message.json5'
|
|
13
12
|
});
|
|
14
13
|
debug('Successfully created:', out);
|
|
15
14
|
return out;
|
package/test/uuid.js
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const { getUUIDv7, getUUIDTimestamp } = require('../index');
|
|
8
|
-
|
|
1
|
+
import nodetest from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import * as debug$0 from 'debug';
|
|
4
|
+
import { getUUIDv7, getUUIDTimestamp } from '../index.js';
|
|
5
|
+
const { it } = nodetest;
|
|
6
|
+
const debug = debug$0('message');
|
|
9
7
|
it('Should correctly calculate UUIDS given various inputs', async () => {
|
|
10
8
|
const now = new Date();
|
|
11
|
-
|
|
12
9
|
const uuid = getUUIDv7();
|
|
13
10
|
const ts = getUUIDTimestamp(uuid);
|
|
14
11
|
debug(uuid, ts, now);
|
|
15
12
|
const diff = now.getTime() - ts.getTime();
|
|
16
13
|
assert(diff < 1000 && diff >= 0, 'More than a second between newly created');
|
|
17
|
-
|
|
18
14
|
const uuid2 = getUUIDv7(now);
|
|
19
15
|
const ts2 = getUUIDTimestamp(uuid2);
|
|
20
16
|
const diff2 = now.getTime() - ts2.getTime();
|
|
21
17
|
assert(diff2 === 0, 'Timestamps should match');
|
|
22
|
-
|
|
23
18
|
const uuid3 = getUUIDv7(now);
|
|
24
19
|
assert(uuid2 !== uuid3, 'UUIDs should be unique match');
|
|
25
20
|
});
|