s3db.js 1.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/.github/workflows/pipeline.yml +16 -0
- package/README.md +742 -0
- package/build/cache/avro.serializer.js +16 -0
- package/build/cache/json.serializer.js +7 -0
- package/build/cache/s3-cache.class.js +157 -0
- package/build/cache/s3-resource-cache.class.js +77 -0
- package/build/cache/serializers.type.js +8 -0
- package/build/errors.js +64 -0
- package/build/index.js +9 -0
- package/build/metadata.interface.js +2 -0
- package/build/plugin.interface.js +2 -0
- package/build/resource.class.js +485 -0
- package/build/resource.interface.js +2 -0
- package/build/s3-client.class.js +274 -0
- package/build/s3db-config.interface.js +2 -0
- package/build/s3db.class.js +185 -0
- package/build/stream/resource-ids-read-stream.class.js +100 -0
- package/build/stream/resource-ids-transformer.class.js +40 -0
- package/build/stream/resource-write-stream.class.js +76 -0
- package/build/validator.js +37 -0
- package/examples/1-bulk-insert.js +64 -0
- package/examples/2-read-stream.js +61 -0
- package/examples/3-read-stream-to-csv.js +57 -0
- package/examples/4-read-stream-to-zip.js +56 -0
- package/examples/5-write-stream.js +98 -0
- package/examples/6-jwt-tokens.js +124 -0
- package/examples/concerns/index.js +64 -0
- package/jest.config.ts +10 -0
- package/package.json +51 -0
- package/src/cache/avro.serializer.ts +12 -0
- package/src/cache/json.serializer.ts +4 -0
- package/src/cache/s3-cache.class.ts +155 -0
- package/src/cache/s3-resource-cache.class.ts +75 -0
- package/src/cache/serializers.type.ts +8 -0
- package/src/errors.ts +96 -0
- package/src/index.ts +4 -0
- package/src/metadata.interface.ts +4 -0
- package/src/plugin.interface.ts +4 -0
- package/src/resource.class.ts +531 -0
- package/src/resource.interface.ts +21 -0
- package/src/s3-client.class.ts +297 -0
- package/src/s3db-config.interface.ts +9 -0
- package/src/s3db.class.ts +215 -0
- package/src/stream/resource-ids-read-stream.class.ts +90 -0
- package/src/stream/resource-ids-transformer.class.ts +38 -0
- package/src/stream/resource-write-stream.class.ts +78 -0
- package/src/validator.ts +39 -0
- package/tests/cache.spec.ts +187 -0
- package/tests/concerns/index.ts +16 -0
- package/tests/config.spec.ts +29 -0
- package/tests/resources.spec.ts +197 -0
- package/tsconfig.json +111 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { nanoid } from "nanoid";
|
|
3
|
+
import CryptoJS from "crypto-js";
|
|
4
|
+
import EventEmitter from "events";
|
|
5
|
+
import { flatten, unflatten } from "flat";
|
|
6
|
+
import { sortBy, chunk, isArray, merge } from "lodash";
|
|
7
|
+
import { PromisePool } from "@supercharge/promise-pool";
|
|
8
|
+
|
|
9
|
+
import S3db from "./s3db.class";
|
|
10
|
+
import S3Client from "./s3-client.class";
|
|
11
|
+
import { S3dbInvalidResource } from "./errors";
|
|
12
|
+
import S3ResourceCache from "./cache/s3-resource-cache.class";
|
|
13
|
+
import ResourceWriteStream from "./stream/resource-write-stream.class";
|
|
14
|
+
import ResourceIdsReadStream from "./stream/resource-ids-read-stream.class";
|
|
15
|
+
import ResourceIdsToDataTransformer from "./stream/resource-ids-transformer.class";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ResourceInterface,
|
|
19
|
+
ResourceConfigInterface,
|
|
20
|
+
} from "./resource.interface";
|
|
21
|
+
|
|
22
|
+
export default class Resource
|
|
23
|
+
extends EventEmitter
|
|
24
|
+
implements ResourceInterface
|
|
25
|
+
{
|
|
26
|
+
name: any;
|
|
27
|
+
schema: any;
|
|
28
|
+
mapObj: any;
|
|
29
|
+
options: any;
|
|
30
|
+
validator: any;
|
|
31
|
+
reversedMapObj: any;
|
|
32
|
+
|
|
33
|
+
s3db: S3db;
|
|
34
|
+
s3Client: S3Client;
|
|
35
|
+
s3Cache: S3ResourceCache | undefined;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Constructor
|
|
39
|
+
*/
|
|
40
|
+
constructor(params: ResourceConfigInterface) {
|
|
41
|
+
super();
|
|
42
|
+
|
|
43
|
+
this.s3db = params.s3db;
|
|
44
|
+
this.name = params.name;
|
|
45
|
+
this.schema = params.schema;
|
|
46
|
+
this.options = params.options;
|
|
47
|
+
this.s3Client = params.s3Client;
|
|
48
|
+
|
|
49
|
+
this.validator = params.validatorInstance.compile(this.schema);
|
|
50
|
+
|
|
51
|
+
const { mapObj, reversedMapObj } = this.getMappersFromSchema(this.schema);
|
|
52
|
+
this.mapObj = mapObj;
|
|
53
|
+
this.reversedMapObj = reversedMapObj;
|
|
54
|
+
|
|
55
|
+
this.studyOptions();
|
|
56
|
+
|
|
57
|
+
if (this.options.cache === true) {
|
|
58
|
+
this.s3Cache = new S3ResourceCache({
|
|
59
|
+
resource: this,
|
|
60
|
+
compressData: true,
|
|
61
|
+
serializer: "json",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getMappersFromSchema(schema: any) {
|
|
67
|
+
let i = 0;
|
|
68
|
+
|
|
69
|
+
const mapObj = sortBy(Object.entries(schema), ["0"]).reduce(
|
|
70
|
+
(acc: any, [key, value]) => {
|
|
71
|
+
acc[key] = String(i++);
|
|
72
|
+
return acc;
|
|
73
|
+
},
|
|
74
|
+
{}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const reversedMapObj = Object.entries(mapObj).reduce(
|
|
78
|
+
(acc: any, [key, value]) => {
|
|
79
|
+
acc[String(value)] = key;
|
|
80
|
+
return acc;
|
|
81
|
+
},
|
|
82
|
+
{}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
mapObj,
|
|
87
|
+
reversedMapObj,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export() {
|
|
92
|
+
const data = {
|
|
93
|
+
name: this.name,
|
|
94
|
+
schema: { ...this.schema },
|
|
95
|
+
mapper: this.mapObj,
|
|
96
|
+
options: this.options,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
for (const [name, definition] of Object.entries(this.schema)) {
|
|
100
|
+
data.schema[name] = JSON.stringify(definition as any);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
studyOptions() {
|
|
107
|
+
if (!this.options.afterUnmap) this.options.beforeMap = {};
|
|
108
|
+
if (!this.options.afterUnmap) this.options.afterUnmap = {};
|
|
109
|
+
|
|
110
|
+
const schema: any = flatten(this.schema, { safe: true });
|
|
111
|
+
|
|
112
|
+
const addRule = (arr: string, attribute: string, action: string) => {
|
|
113
|
+
if (!this.options[arr][attribute]) this.options[arr][attribute] = [];
|
|
114
|
+
|
|
115
|
+
this.options[arr][attribute] = [
|
|
116
|
+
...new Set([...this.options[arr][attribute], action]),
|
|
117
|
+
];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const [name, definition] of Object.entries(schema)) {
|
|
121
|
+
if ((definition as string).includes("secret")) {
|
|
122
|
+
if (this.options.autoDecrypt === true) {
|
|
123
|
+
addRule("afterUnmap", name, "decrypt");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if ((definition as string).includes("array")) {
|
|
127
|
+
addRule("beforeMap", name, "fromArray");
|
|
128
|
+
addRule("afterUnmap", name, "toArray");
|
|
129
|
+
}
|
|
130
|
+
if ((definition as string).includes("number")) {
|
|
131
|
+
addRule("beforeMap", name, "toString");
|
|
132
|
+
addRule("afterUnmap", name, "toNumber");
|
|
133
|
+
}
|
|
134
|
+
if ((definition as string).includes("boolean")) {
|
|
135
|
+
addRule("beforeMap", name, "toJson");
|
|
136
|
+
addRule("afterUnmap", name, "fromJson");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private check(data: any) {
|
|
142
|
+
const result = {
|
|
143
|
+
original: { ...data },
|
|
144
|
+
isValid: false,
|
|
145
|
+
errors: [],
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const check = this.validator(data);
|
|
149
|
+
|
|
150
|
+
if (check === true) {
|
|
151
|
+
result.isValid = true;
|
|
152
|
+
} else {
|
|
153
|
+
result.errors = check;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
...result,
|
|
158
|
+
data,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
validate(data: any) {
|
|
163
|
+
return this.check(flatten(data, { safe: true }));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
map(data: any) {
|
|
167
|
+
let obj: any = { ...data };
|
|
168
|
+
|
|
169
|
+
for (const [attribute, actions] of Object.entries(this.options.beforeMap)) {
|
|
170
|
+
for (const action of actions as string[]) {
|
|
171
|
+
if (action === "fromArray") {
|
|
172
|
+
obj[attribute] = (obj[attribute] || []).join("|");
|
|
173
|
+
} else if (action === "toString") {
|
|
174
|
+
obj[attribute] = String(obj[attribute]);
|
|
175
|
+
} else if (action === "toJson") {
|
|
176
|
+
obj[attribute] = JSON.stringify(obj[attribute]);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
obj = Object.entries(obj).reduce((acc: any, [key, value]) => {
|
|
182
|
+
acc[this.mapObj[key]] = isArray(value) ? value.join("|") : value;
|
|
183
|
+
return acc;
|
|
184
|
+
}, {});
|
|
185
|
+
|
|
186
|
+
return obj;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
unmap(data: any) {
|
|
190
|
+
const obj = Object.entries(data).reduce((acc: any, [key, value]) => {
|
|
191
|
+
acc[this.reversedMapObj[key]] = value;
|
|
192
|
+
return acc;
|
|
193
|
+
}, {});
|
|
194
|
+
|
|
195
|
+
for (const [attribute, actions] of Object.entries(
|
|
196
|
+
this.options.afterUnmap
|
|
197
|
+
)) {
|
|
198
|
+
for (const action of actions as string[]) {
|
|
199
|
+
if (action === "decrypt") {
|
|
200
|
+
let content = obj[attribute];
|
|
201
|
+
content = CryptoJS.AES.decrypt(content, this.s3db.passphrase);
|
|
202
|
+
content = content.toString(CryptoJS.enc.Utf8);
|
|
203
|
+
obj[attribute] = content;
|
|
204
|
+
} else if (action === "toArray") {
|
|
205
|
+
obj[attribute] = (obj[attribute] || "").split("|");
|
|
206
|
+
} else if (action === "toNumber") {
|
|
207
|
+
obj[attribute] = Number(obj[attribute] || "");
|
|
208
|
+
} else if (action === "fromJson") {
|
|
209
|
+
obj[attribute] = JSON.parse(obj[attribute]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return obj;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Inserts a new object into the resource list.
|
|
219
|
+
* @param {Object} param
|
|
220
|
+
* @returns
|
|
221
|
+
*/
|
|
222
|
+
async insert(attributes: any) {
|
|
223
|
+
let { id, ...attrs }: { id: any; attrs: any } = flatten(attributes, {
|
|
224
|
+
safe: true,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// validate
|
|
228
|
+
const { isValid, errors, data: validated } = this.check(attrs);
|
|
229
|
+
|
|
230
|
+
if (!isValid) {
|
|
231
|
+
return Promise.reject(
|
|
232
|
+
new S3dbInvalidResource({
|
|
233
|
+
bucket: this.s3Client.bucket,
|
|
234
|
+
resourceName: this.name,
|
|
235
|
+
attributes,
|
|
236
|
+
validation: errors,
|
|
237
|
+
})
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (!id && id !== 0) id = nanoid();
|
|
242
|
+
|
|
243
|
+
// save
|
|
244
|
+
await this.s3Client.putObject({
|
|
245
|
+
key: path.join(`resource=${this.name}`, `id=${id}`),
|
|
246
|
+
body: "",
|
|
247
|
+
metadata: this.map(validated),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const final = {
|
|
251
|
+
id,
|
|
252
|
+
...(unflatten(validated) as object),
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
this.emit("inserted", final);
|
|
256
|
+
this.s3db.emit("inserted", this.name, final);
|
|
257
|
+
|
|
258
|
+
if (this.s3Cache) {
|
|
259
|
+
await this.s3Cache?.purge();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return final;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get a resource by id
|
|
267
|
+
* @param {Object} param
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
270
|
+
async getById(id: any) {
|
|
271
|
+
const request = await this.s3Client.headObject({
|
|
272
|
+
key: path.join(`resource=${this.name}`, `id=${id}`),
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
let data: any = this.unmap(request.Metadata);
|
|
276
|
+
data = unflatten(data);
|
|
277
|
+
|
|
278
|
+
data.id = id;
|
|
279
|
+
data._length = request.ContentLength;
|
|
280
|
+
data._createdAt = request.LastModified;
|
|
281
|
+
|
|
282
|
+
if (request.Expiration) data._expiresAt = request.Expiration;
|
|
283
|
+
|
|
284
|
+
this.emit("got", data);
|
|
285
|
+
this.s3db.emit("got", this.name, data);
|
|
286
|
+
|
|
287
|
+
return data;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Update a resource by id
|
|
292
|
+
* @param {Object} param
|
|
293
|
+
* @returns
|
|
294
|
+
*/
|
|
295
|
+
async updateById(id: any, attributes: any) {
|
|
296
|
+
const obj = await this.getById(id);
|
|
297
|
+
|
|
298
|
+
let attrs1 = flatten(attributes, { safe: true });
|
|
299
|
+
let attrs2 = flatten(obj, { safe: true });
|
|
300
|
+
|
|
301
|
+
const attrs = merge(attrs2, attrs1) as any;
|
|
302
|
+
delete attrs.id;
|
|
303
|
+
|
|
304
|
+
const { isValid, errors, data: validated } = this.check(attrs);
|
|
305
|
+
|
|
306
|
+
if (!isValid) {
|
|
307
|
+
return Promise.reject(
|
|
308
|
+
new S3dbInvalidResource({
|
|
309
|
+
bucket: this.s3Client.bucket,
|
|
310
|
+
resourceName: this.name,
|
|
311
|
+
attributes,
|
|
312
|
+
validation: errors,
|
|
313
|
+
})
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!id && id !== 0) id = nanoid();
|
|
318
|
+
|
|
319
|
+
// save
|
|
320
|
+
await this.s3Client.putObject({
|
|
321
|
+
key: path.join(`resource=${this.name}`, `id=${id}`),
|
|
322
|
+
body: "",
|
|
323
|
+
metadata: this.map(validated),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const final = {
|
|
327
|
+
id,
|
|
328
|
+
...(unflatten(validated) as object),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
this.emit("updated", final);
|
|
332
|
+
this.s3db.emit("updated", this.name, final);
|
|
333
|
+
|
|
334
|
+
if (this.s3Cache) {
|
|
335
|
+
await this.s3Cache?.purge();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return final;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Delete a resource by id
|
|
343
|
+
* @param {Object} param
|
|
344
|
+
* @returns
|
|
345
|
+
*/
|
|
346
|
+
async deleteById(id: any) {
|
|
347
|
+
const key = path.join(`resource=${this.name}`, `id=${id}`);
|
|
348
|
+
const response = await this.s3Client.deleteObject(key);
|
|
349
|
+
|
|
350
|
+
this.emit("deleted", id);
|
|
351
|
+
this.s3db.emit("deleted", this.name, id);
|
|
352
|
+
|
|
353
|
+
if (this.s3Cache) {
|
|
354
|
+
await this.s3Cache?.purge();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return response;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
*
|
|
362
|
+
*/
|
|
363
|
+
async bulkInsert(objects: any[]) {
|
|
364
|
+
const { results } = await PromisePool.for(objects)
|
|
365
|
+
.withConcurrency(this.s3db.parallelism)
|
|
366
|
+
.handleError(async (error, content) => {
|
|
367
|
+
this.emit("error", error, content);
|
|
368
|
+
this.s3db.emit("error", this.name, error, content);
|
|
369
|
+
})
|
|
370
|
+
.process(async (attributes: any) => {
|
|
371
|
+
const result = await this.insert(attributes);
|
|
372
|
+
return result;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
return results;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
*
|
|
380
|
+
* @returns number
|
|
381
|
+
*/
|
|
382
|
+
async count() {
|
|
383
|
+
if (this.s3Cache) {
|
|
384
|
+
const cached = await this.s3Cache.get({ action: "count" });
|
|
385
|
+
if (cached) return cached;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const count = await this.s3Client.count({
|
|
389
|
+
prefix: `resource=${this.name}`,
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (this.s3Cache) {
|
|
393
|
+
await this.s3Cache.put({ action: "count", data: count });
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return count;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Delete resources by a list of ids
|
|
401
|
+
* @param {Object} param
|
|
402
|
+
* @returns
|
|
403
|
+
*/
|
|
404
|
+
async bulkDelete(ids: any[]): Promise<any[]> {
|
|
405
|
+
let packages = chunk(
|
|
406
|
+
ids.map((x) => path.join(`resource=${this.name}`, `id=${x}`)),
|
|
407
|
+
1000
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
const { results } = await PromisePool.for(packages)
|
|
411
|
+
.withConcurrency(this.s3db.parallelism)
|
|
412
|
+
.handleError(async (error, content) => {
|
|
413
|
+
this.emit("error", error, content);
|
|
414
|
+
this.s3db.emit("error", this.name, error, content);
|
|
415
|
+
})
|
|
416
|
+
.process(async (keys: string[]) => {
|
|
417
|
+
const response = await this.s3Client.deleteObjects(keys);
|
|
418
|
+
|
|
419
|
+
keys.forEach((key) => {
|
|
420
|
+
const id = key.split("=").pop();
|
|
421
|
+
this.emit("deleted", id);
|
|
422
|
+
this.s3db.emit("deleted", this.name, id);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
return response;
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
if (this.s3Cache) {
|
|
429
|
+
await this.s3Cache?.purge();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return results;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
async getAllIds() {
|
|
436
|
+
if (this.s3Cache) {
|
|
437
|
+
const cached = await this.s3Cache.get({ action: "getAllIds" });
|
|
438
|
+
if (cached) return cached;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const keys = await this.s3Client.getAllKeys({
|
|
442
|
+
prefix: `resource=${this.name}`,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const ids = keys.map((x) => x.replace(`resource=${this.name}/id=`, ""));
|
|
446
|
+
|
|
447
|
+
if (this.s3Cache) {
|
|
448
|
+
await this.s3Cache.put({ action: "getAllIds", data: ids });
|
|
449
|
+
const x = await this.s3Cache.get({ action: "getAllIds" });
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return ids;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async deleteAll() {
|
|
456
|
+
const ids = await this.getAllIds();
|
|
457
|
+
await this.bulkDelete(ids);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async getByIdList(ids: string[]) {
|
|
461
|
+
if (this.s3Cache) {
|
|
462
|
+
const cached = await this.s3Cache.get({ action: "getAll" });
|
|
463
|
+
if (cached) return cached;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const { results } = await PromisePool.for(ids)
|
|
467
|
+
.withConcurrency(this.s3Client.parallelism)
|
|
468
|
+
.process(async (id: string) => {
|
|
469
|
+
this.emit("id", id);
|
|
470
|
+
const data = await this.getById(id);
|
|
471
|
+
this.emit("data", data);
|
|
472
|
+
return data;
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
if (this.s3Cache) {
|
|
476
|
+
await this.s3Cache.put({ action: "getAll", data: results });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return results;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async getAll() {
|
|
483
|
+
if (this.s3Cache) {
|
|
484
|
+
const cached = await this.s3Cache.get({ action: "getAll" });
|
|
485
|
+
if (cached) return cached;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
let ids: string[] = [];
|
|
489
|
+
let gotFromCache = false;
|
|
490
|
+
|
|
491
|
+
if (this.s3Cache) {
|
|
492
|
+
const cached = await this.s3Cache.get({ action: "getAllIds" });
|
|
493
|
+
if (cached) {
|
|
494
|
+
ids = cached;
|
|
495
|
+
gotFromCache = true;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (!gotFromCache) ids = await this.getAllIds();
|
|
500
|
+
|
|
501
|
+
if (ids.length === 0) return [];
|
|
502
|
+
|
|
503
|
+
const { results } = await PromisePool.for(ids)
|
|
504
|
+
.withConcurrency(this.s3Client.parallelism)
|
|
505
|
+
.process(async (id: string) => {
|
|
506
|
+
this.emit("id", id);
|
|
507
|
+
const data = await this.getById(id);
|
|
508
|
+
this.emit("data", data);
|
|
509
|
+
return data;
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
if (this.s3Cache && results.length > 0) {
|
|
513
|
+
await this.s3Cache.put({ action: "getAll", data: results });
|
|
514
|
+
const x = await this.s3Cache.get({ action: "getAll" });
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return results;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
readable() {
|
|
521
|
+
const stream = new ResourceIdsReadStream({ resource: this });
|
|
522
|
+
const transformer = new ResourceIdsToDataTransformer({ resource: this });
|
|
523
|
+
|
|
524
|
+
return stream.pipe(transformer);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
writable() {
|
|
528
|
+
const stream = new ResourceWriteStream({ resource: this });
|
|
529
|
+
return stream;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import S3db from "./s3db.class";
|
|
2
|
+
import S3Client from "./s3-client.class";
|
|
3
|
+
|
|
4
|
+
export interface MetadataResourceInterface {
|
|
5
|
+
schema: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ResourceInterface {
|
|
9
|
+
schema: any;
|
|
10
|
+
validator: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ResourceConfigInterface {
|
|
14
|
+
s3db: S3db;
|
|
15
|
+
name: string;
|
|
16
|
+
schema: any;
|
|
17
|
+
options?: any;
|
|
18
|
+
cache?: boolean
|
|
19
|
+
s3Client: S3Client;
|
|
20
|
+
validatorInstance: any;
|
|
21
|
+
}
|