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,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const lodash_1 = require("lodash");
|
|
40
|
+
const nanoid_1 = require("nanoid");
|
|
41
|
+
const events_1 = __importDefault(require("events"));
|
|
42
|
+
const aws_sdk_1 = require("aws-sdk");
|
|
43
|
+
const promise_pool_1 = __importDefault(require("@supercharge/promise-pool"));
|
|
44
|
+
const errors_1 = require("./errors");
|
|
45
|
+
class S3Client extends events_1.default {
|
|
46
|
+
constructor({ connectionString, parallelism = 10, AwsS3, }) {
|
|
47
|
+
super();
|
|
48
|
+
this.id = (0, nanoid_1.nanoid)(7);
|
|
49
|
+
const uri = new URL(connectionString);
|
|
50
|
+
this.bucket = uri.hostname;
|
|
51
|
+
this.parallelism = parallelism;
|
|
52
|
+
let [, ...subpath] = uri.pathname.split("/");
|
|
53
|
+
this.keyPrefix = [...(subpath || [])].join("/");
|
|
54
|
+
this.client =
|
|
55
|
+
AwsS3 ||
|
|
56
|
+
new aws_sdk_1.S3({
|
|
57
|
+
credentials: new aws_sdk_1.Credentials({
|
|
58
|
+
accessKeyId: uri.username,
|
|
59
|
+
secretAccessKey: uri.password,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param param0
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
getObject({ key }) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
try {
|
|
71
|
+
const options = {
|
|
72
|
+
Bucket: this.bucket,
|
|
73
|
+
Key: path.join(this.keyPrefix, key),
|
|
74
|
+
};
|
|
75
|
+
const response = yield this.client.getObject(options).promise();
|
|
76
|
+
this.emit("request", "getObject", options);
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof Error) {
|
|
81
|
+
if (error.name === "NoSuchKey") {
|
|
82
|
+
return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return Promise.reject(error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
* @param param0
|
|
92
|
+
* @returns
|
|
93
|
+
*/
|
|
94
|
+
putObject({ key, metadata, contentType, body, contentEncoding, }) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
try {
|
|
97
|
+
const options = {
|
|
98
|
+
Bucket: this.bucket,
|
|
99
|
+
Key: path.join(this.keyPrefix, key),
|
|
100
|
+
Metadata: Object.assign({}, metadata),
|
|
101
|
+
Body: body,
|
|
102
|
+
ContentType: contentType,
|
|
103
|
+
ContentEncoding: contentEncoding,
|
|
104
|
+
};
|
|
105
|
+
const response = yield this.client.putObject(options).promise();
|
|
106
|
+
this.emit("request", "putObject", options);
|
|
107
|
+
return response;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
this.emit("error", error);
|
|
111
|
+
return Promise.reject(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Proxy to AWS S3's headObject
|
|
117
|
+
* @param {Object} param
|
|
118
|
+
* @param {string} param.key
|
|
119
|
+
* @returns
|
|
120
|
+
*/
|
|
121
|
+
headObject({ key }) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
try {
|
|
124
|
+
const options = {
|
|
125
|
+
Bucket: this.bucket,
|
|
126
|
+
Key: path.join(this.keyPrefix, key),
|
|
127
|
+
};
|
|
128
|
+
const response = yield this.client.headObject(options).promise();
|
|
129
|
+
this.emit("request", "headObject", options);
|
|
130
|
+
return response;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof Error) {
|
|
134
|
+
if (error.name === "NoSuchKey" || error.name === "NotFound") {
|
|
135
|
+
return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.emit("error", error);
|
|
139
|
+
return Promise.reject(error);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Proxy to AWS S3's deleteObject
|
|
145
|
+
* @param {Object} param
|
|
146
|
+
* @param {string} param.key
|
|
147
|
+
* @returns
|
|
148
|
+
*/
|
|
149
|
+
deleteObject(key) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
try {
|
|
152
|
+
const options = {
|
|
153
|
+
Bucket: this.bucket,
|
|
154
|
+
Key: path.join(this.keyPrefix, key),
|
|
155
|
+
};
|
|
156
|
+
const response = yield this.client.deleteObject(options).promise();
|
|
157
|
+
this.emit("request", "deleteObject", options);
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.emit("error", error);
|
|
162
|
+
if (error instanceof Error) {
|
|
163
|
+
if (error.name === "NoSuchKey") {
|
|
164
|
+
return Promise.reject(new errors_1.ClientNoSuchKey({ bucket: this.bucket, key }));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return Promise.reject(error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Proxy to AWS S3's deleteObjects
|
|
173
|
+
* @param {Object} param
|
|
174
|
+
* @param {string} param.keys
|
|
175
|
+
* @returns
|
|
176
|
+
*/
|
|
177
|
+
deleteObjects(keys) {
|
|
178
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
179
|
+
const packages = (0, lodash_1.chunk)(keys, 1000);
|
|
180
|
+
const { results, errors } = yield promise_pool_1.default.for(packages)
|
|
181
|
+
.withConcurrency(this.parallelism)
|
|
182
|
+
.process((keys) => __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
try {
|
|
184
|
+
const options = {
|
|
185
|
+
Bucket: this.bucket,
|
|
186
|
+
Delete: {
|
|
187
|
+
Objects: keys.map((key) => ({
|
|
188
|
+
Key: path.join(this.keyPrefix, key),
|
|
189
|
+
})),
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
const response = yield this.client.deleteObjects(options).promise();
|
|
193
|
+
this.emit("request", "deleteObjects", options);
|
|
194
|
+
return response;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
this.emit("error", error);
|
|
198
|
+
return Promise.reject(error);
|
|
199
|
+
}
|
|
200
|
+
}));
|
|
201
|
+
return {
|
|
202
|
+
deleted: results,
|
|
203
|
+
notFound: errors,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
*
|
|
209
|
+
* @param param0
|
|
210
|
+
* @returns
|
|
211
|
+
*/
|
|
212
|
+
listObjects({ prefix, maxKeys = 1000, continuationToken, }) {
|
|
213
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
214
|
+
try {
|
|
215
|
+
const options = {
|
|
216
|
+
Bucket: this.bucket,
|
|
217
|
+
MaxKeys: maxKeys,
|
|
218
|
+
ContinuationToken: continuationToken,
|
|
219
|
+
Prefix: path.join(this.keyPrefix, prefix || ""),
|
|
220
|
+
};
|
|
221
|
+
const response = yield this.client.listObjectsV2(options).promise();
|
|
222
|
+
this.emit("request", "listObjectsV2", options);
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
this.emit("error", error);
|
|
227
|
+
return Promise.reject(error);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
count({ prefix } = {}) {
|
|
232
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
233
|
+
this.emit("request", "count", { prefix });
|
|
234
|
+
let count = 0;
|
|
235
|
+
let truncated = true;
|
|
236
|
+
let continuationToken;
|
|
237
|
+
while (truncated) {
|
|
238
|
+
const options = {
|
|
239
|
+
prefix,
|
|
240
|
+
continuationToken,
|
|
241
|
+
};
|
|
242
|
+
const res = yield this.listObjects(options);
|
|
243
|
+
count += res.KeyCount || 0;
|
|
244
|
+
truncated = res.IsTruncated || false;
|
|
245
|
+
continuationToken = res.NextContinuationToken;
|
|
246
|
+
}
|
|
247
|
+
return count;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
getAllKeys({ prefix } = {}) {
|
|
251
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
252
|
+
this.emit("request", "getAllKeys", { prefix });
|
|
253
|
+
let keys = [];
|
|
254
|
+
let truncated = true;
|
|
255
|
+
let continuationToken;
|
|
256
|
+
while (truncated) {
|
|
257
|
+
const options = {
|
|
258
|
+
prefix,
|
|
259
|
+
continuationToken,
|
|
260
|
+
};
|
|
261
|
+
const res = yield this.listObjects(options);
|
|
262
|
+
if (res.Contents) {
|
|
263
|
+
keys = keys.concat(res.Contents.map((x) => x.Key));
|
|
264
|
+
}
|
|
265
|
+
truncated = res.IsTruncated || false;
|
|
266
|
+
continuationToken = res.NextContinuationToken;
|
|
267
|
+
}
|
|
268
|
+
return keys
|
|
269
|
+
.map((x) => x.replace(this.keyPrefix, ""))
|
|
270
|
+
.map((x) => (x.startsWith("/") ? x.replace(`/`, "") : x));
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
exports.default = S3Client;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const flat_1 = require("flat");
|
|
16
|
+
const lodash_1 = require("lodash");
|
|
17
|
+
const events_1 = __importDefault(require("events"));
|
|
18
|
+
const resource_class_1 = __importDefault(require("./resource.class"));
|
|
19
|
+
const s3_client_class_1 = __importDefault(require("./s3-client.class"));
|
|
20
|
+
const validator_1 = require("./validator");
|
|
21
|
+
const errors_1 = require("./errors");
|
|
22
|
+
class S3db extends events_1.default {
|
|
23
|
+
/**
|
|
24
|
+
* Constructor
|
|
25
|
+
*/
|
|
26
|
+
constructor(options) {
|
|
27
|
+
super();
|
|
28
|
+
this.keyPrefix = "";
|
|
29
|
+
this.bucket = "s3db";
|
|
30
|
+
this.cache = false;
|
|
31
|
+
this.version = "1";
|
|
32
|
+
this.resources = {};
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.parallelism = parseInt(options.parallelism + "") || 10;
|
|
35
|
+
this.plugins = options.plugins || [];
|
|
36
|
+
this.cache = options.cache;
|
|
37
|
+
this.passphrase = options.passphrase || "";
|
|
38
|
+
this.validatorInstance = (0, validator_1.ValidatorFactory)({
|
|
39
|
+
passphrase: options === null || options === void 0 ? void 0 : options.passphrase,
|
|
40
|
+
});
|
|
41
|
+
this.client = new s3_client_class_1.default({
|
|
42
|
+
connectionString: options.uri,
|
|
43
|
+
parallelism: this.parallelism,
|
|
44
|
+
});
|
|
45
|
+
this.bucket = this.client.bucket;
|
|
46
|
+
this.keyPrefix = this.client.keyPrefix;
|
|
47
|
+
this.startPlugins();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Remotely setups s3db file.
|
|
51
|
+
*/
|
|
52
|
+
connect() {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
let metadata = null;
|
|
55
|
+
try {
|
|
56
|
+
metadata = yield this.getMetadataFile();
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof errors_1.S3dbMissingMetadata) {
|
|
60
|
+
metadata = this.blankMetadataStructure();
|
|
61
|
+
yield this.uploadMetadataFile();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.emit("error", error);
|
|
65
|
+
return Promise.reject(error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const resource of Object.entries(metadata.resources)) {
|
|
69
|
+
const [name, definition] = resource;
|
|
70
|
+
this.resources[name] = new resource_class_1.default({
|
|
71
|
+
name,
|
|
72
|
+
s3db: this,
|
|
73
|
+
s3Client: this.client,
|
|
74
|
+
schema: definition.schema,
|
|
75
|
+
options: definition.options,
|
|
76
|
+
validatorInstance: this.validatorInstance,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
this.emit("connected", new Date());
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
startPlugins() {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
if (this.plugins && !(0, lodash_1.isEmpty)(this.plugins)) {
|
|
85
|
+
const startProms = this.plugins.map((plugin) => plugin.setup(this));
|
|
86
|
+
yield Promise.all(startProms);
|
|
87
|
+
this.plugins.map((plugin) => plugin.start());
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Downloads current metadata.
|
|
93
|
+
* If there isnt any file, creates an empty metadata.
|
|
94
|
+
* @returns MetadataInterface
|
|
95
|
+
*/
|
|
96
|
+
getMetadataFile() {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
try {
|
|
99
|
+
const request = yield this.client.getObject({ key: `s3db.json` });
|
|
100
|
+
const metadata = JSON.parse(String(request === null || request === void 0 ? void 0 : request.Body));
|
|
101
|
+
return this.unserializeMetadata(metadata);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof errors_1.ClientNoSuchKey) {
|
|
105
|
+
return Promise.reject(new errors_1.S3dbMissingMetadata({ bucket: this.bucket, cause: error }));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
return Promise.reject(error);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
unserializeMetadata(metadata) {
|
|
114
|
+
const file = Object.assign({}, metadata);
|
|
115
|
+
if ((0, lodash_1.isEmpty)(file.resources))
|
|
116
|
+
return file;
|
|
117
|
+
for (const [name, structure] of Object.entries(file.resources)) {
|
|
118
|
+
for (const [attr, value] of Object.entries(structure.schema)) {
|
|
119
|
+
file.resources[name].schema[attr] = JSON.parse(value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return file;
|
|
123
|
+
}
|
|
124
|
+
uploadMetadataFile() {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
const file = {
|
|
127
|
+
version: this.version,
|
|
128
|
+
resources: Object.entries(this.resources).reduce((acc, definition) => {
|
|
129
|
+
const [name, resource] = definition;
|
|
130
|
+
acc[name] = resource.export();
|
|
131
|
+
return acc;
|
|
132
|
+
}, {}),
|
|
133
|
+
};
|
|
134
|
+
yield this.client.putObject({
|
|
135
|
+
key: `s3db.json`,
|
|
136
|
+
body: JSON.stringify(file, null, 2),
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Generates empty metadata structure.
|
|
142
|
+
* @returns MetadataInterface
|
|
143
|
+
*/
|
|
144
|
+
blankMetadataStructure() {
|
|
145
|
+
return {
|
|
146
|
+
version: `1`,
|
|
147
|
+
resources: {},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generates a new resorce with its translators and validatos.
|
|
152
|
+
* @param {Object} param
|
|
153
|
+
* @param {string} param.resourceName
|
|
154
|
+
* @param {Object} param.attributes
|
|
155
|
+
* @param {Object} param.options
|
|
156
|
+
*/
|
|
157
|
+
createResource({ resourceName, attributes, options = {}, }) {
|
|
158
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
const schema = (0, flat_1.flatten)(attributes, { safe: true });
|
|
160
|
+
const resource = new resource_class_1.default({
|
|
161
|
+
schema,
|
|
162
|
+
s3db: this,
|
|
163
|
+
name: resourceName,
|
|
164
|
+
s3Client: this.client,
|
|
165
|
+
validatorInstance: this.validatorInstance,
|
|
166
|
+
options: Object.assign({ autoDecrypt: true, cache: this.cache }, options),
|
|
167
|
+
});
|
|
168
|
+
this.resources[resourceName] = resource;
|
|
169
|
+
yield this.uploadMetadataFile();
|
|
170
|
+
return resource;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Looper
|
|
175
|
+
* @param {string} resourceName
|
|
176
|
+
* @returns
|
|
177
|
+
*/
|
|
178
|
+
resource(resourceName) {
|
|
179
|
+
if (!this.resources[resourceName]) {
|
|
180
|
+
return Promise.reject(`resource ${resourceName} does not exist`);
|
|
181
|
+
}
|
|
182
|
+
return this.resources[resourceName];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.default = S3db;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
const path = __importStar(require("path"));
|
|
36
|
+
const lodash_1 = require("lodash");
|
|
37
|
+
const node_stream_1 = require("node:stream");
|
|
38
|
+
const promise_pool_1 = require("@supercharge/promise-pool");
|
|
39
|
+
class ResourceIdsReadStream extends node_stream_1.Readable {
|
|
40
|
+
constructor({ resource }) {
|
|
41
|
+
super({
|
|
42
|
+
objectMode: true,
|
|
43
|
+
highWaterMark: resource.s3Client.parallelism * 3,
|
|
44
|
+
});
|
|
45
|
+
this.resource = resource;
|
|
46
|
+
this.pagesCount = 0;
|
|
47
|
+
this.content = [];
|
|
48
|
+
this.finishedReadingResource = false;
|
|
49
|
+
this.loading = this.getItems();
|
|
50
|
+
}
|
|
51
|
+
_read(size) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
if (this.content.length === 0) {
|
|
54
|
+
if (this.loading) {
|
|
55
|
+
yield this.loading;
|
|
56
|
+
}
|
|
57
|
+
else if (this.finishedReadingResource) {
|
|
58
|
+
this.push(null);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const data = this.content.shift();
|
|
63
|
+
this.push(data);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
getItems({ continuationToken = null, } = {}) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
this.emit("page", this.pagesCount++);
|
|
69
|
+
const res = yield this.resource.s3Client.listObjects({
|
|
70
|
+
prefix: `resource=${this.resource.name}`,
|
|
71
|
+
continuationToken,
|
|
72
|
+
});
|
|
73
|
+
if (res.Contents) {
|
|
74
|
+
const contents = (0, lodash_1.chunk)(res.Contents, this.resource.s3Client.parallelism);
|
|
75
|
+
yield promise_pool_1.PromisePool.for(contents)
|
|
76
|
+
.withConcurrency(5)
|
|
77
|
+
.handleError((error, content) => __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
this.emit("error", error, content);
|
|
79
|
+
}))
|
|
80
|
+
.process((pkg) => {
|
|
81
|
+
const ids = pkg.map((obj) => {
|
|
82
|
+
return (obj.Key || "").replace(path.join(this.resource.s3Client.keyPrefix, `resource=${this.resource.name}`, "id="), "");
|
|
83
|
+
});
|
|
84
|
+
this.content.push(ids);
|
|
85
|
+
ids.forEach((id) => this.emit("id", this.resource.name, id));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
this.finishedReadingResource = !res.IsTruncated;
|
|
89
|
+
if (res.NextContinuationToken) {
|
|
90
|
+
this.loading = this.getItems({
|
|
91
|
+
continuationToken: res.NextContinuationToken,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.loading = null;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.default = ResourceIdsReadStream;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const lodash_1 = require("lodash");
|
|
13
|
+
const promise_pool_1 = require("@supercharge/promise-pool");
|
|
14
|
+
const node_stream_1 = require("node:stream");
|
|
15
|
+
class ResourceIdsToDataTransformer extends node_stream_1.Transform {
|
|
16
|
+
constructor({ resource }) {
|
|
17
|
+
super({ objectMode: true, highWaterMark: resource.s3Client.parallelism * 2 });
|
|
18
|
+
this.resource = resource;
|
|
19
|
+
}
|
|
20
|
+
_transform(chunk, encoding, callback) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
if (!(0, lodash_1.isArray)(chunk))
|
|
23
|
+
this.push(null);
|
|
24
|
+
this.emit("page", chunk);
|
|
25
|
+
yield promise_pool_1.PromisePool.for(chunk)
|
|
26
|
+
.withConcurrency(this.resource.s3Client.parallelism)
|
|
27
|
+
.handleError((error, content) => __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
this.emit("error", error, content);
|
|
29
|
+
}))
|
|
30
|
+
.process((id) => __awaiter(this, void 0, void 0, function* () {
|
|
31
|
+
this.emit("id", id);
|
|
32
|
+
const data = yield this.resource.getById(id);
|
|
33
|
+
this.push(data);
|
|
34
|
+
return data;
|
|
35
|
+
}));
|
|
36
|
+
callback(null);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.default = ResourceIdsToDataTransformer;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const lodash_1 = require("lodash");
|
|
13
|
+
const node_stream_1 = require("node:stream");
|
|
14
|
+
class ResourceWriteStream extends node_stream_1.Writable {
|
|
15
|
+
constructor({ resource }) {
|
|
16
|
+
super({ objectMode: true, highWaterMark: resource.s3Client.parallelism * 2 });
|
|
17
|
+
this.resource = resource;
|
|
18
|
+
this.contents = [];
|
|
19
|
+
this.running = null;
|
|
20
|
+
this.receivedFinalMessage = false;
|
|
21
|
+
}
|
|
22
|
+
_write(chunk, encoding, callback) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
if (this.running)
|
|
25
|
+
yield this.running;
|
|
26
|
+
if (!(0, lodash_1.isEmpty)(chunk)) {
|
|
27
|
+
this.contents.push(chunk);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.receivedFinalMessage = true;
|
|
31
|
+
}
|
|
32
|
+
this.running = this.writeOrWait();
|
|
33
|
+
return callback(null);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
_writev(chunks, callback) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
if (this.running)
|
|
39
|
+
yield this.running;
|
|
40
|
+
if (!(0, lodash_1.isEmpty)(chunks)) {
|
|
41
|
+
for (const obj of chunks.map((c) => c.chunk)) {
|
|
42
|
+
this.contents.push(obj);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.receivedFinalMessage = true;
|
|
47
|
+
}
|
|
48
|
+
this.running = this.writeOrWait();
|
|
49
|
+
return callback(null);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
writeOrWait() {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
if (this.receivedFinalMessage) {
|
|
55
|
+
const data = this.contents.splice(0, this.contents.length - 1);
|
|
56
|
+
yield this.resource.bulkInsert(data);
|
|
57
|
+
this.emit("end");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (this.contents.length < this.resource.s3Client.parallelism)
|
|
61
|
+
return;
|
|
62
|
+
const objs = this.contents.splice(0, this.resource.s3Client.parallelism);
|
|
63
|
+
objs.forEach((obj) => this.emit("id", obj.id));
|
|
64
|
+
yield this.resource.bulkInsert(objs);
|
|
65
|
+
objs.forEach((obj) => this.emit("data", obj));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
_final(callback) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
+
this.receivedFinalMessage = true;
|
|
71
|
+
yield this.writeOrWait();
|
|
72
|
+
callback(null);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.default = ResourceWriteStream;
|