@l-v-yonsama/multi-platform-database-drivers 0.1.1

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.
Files changed (42) hide show
  1. package/.editorconfig +15 -0
  2. package/.eslintignore +1 -0
  3. package/.eslintrc.json +30 -0
  4. package/.github/workflows/npm-publish-github-packages.yml +50 -0
  5. package/.prettierrc +12 -0
  6. package/README.md +7 -0
  7. package/__tests__/db/drivers/AwsS3Driver.test.ts +97 -0
  8. package/__tests__/db/drivers/MySQLDriver.test.ts +140 -0
  9. package/__tests__/db/drivers/PostgresDriver.test.ts +139 -0
  10. package/__tests__/db/drivers/RedisDriver.test.ts +156 -0
  11. package/jest.config.js +18 -0
  12. package/package.json +76 -0
  13. package/src/db/DBError.ts +26 -0
  14. package/src/db/drivers/AwsS3Driver.ts +539 -0
  15. package/src/db/drivers/BaseDriver.ts +256 -0
  16. package/src/db/drivers/MongoDriver.js +110 -0
  17. package/src/db/drivers/MySQLDriver.ts +318 -0
  18. package/src/db/drivers/PostgresDriver.ts +363 -0
  19. package/src/db/drivers/RedisDriver.ts +277 -0
  20. package/src/db/manager.ts +95 -0
  21. package/src/db/resource/DbResource.ts +579 -0
  22. package/src/db/resource/MultipleResultSetDataHolder.ts +119 -0
  23. package/src/db/resource/ResourceUtil.ts +366 -0
  24. package/src/db/resource/ResultSetDataHolder.ts +806 -0
  25. package/src/db/resource/types/DBType.ts +43 -0
  26. package/src/db/resource/types/GeneralColumnType.ts +321 -0
  27. package/src/db/resource/types/MySQLColumnType.ts +134 -0
  28. package/src/db/resource/types/ODBCVendorType.ts +3 -0
  29. package/src/db/resource/types/PostgresColumnType.ts +94 -0
  30. package/src/db/resource/types/RedisKeyType.ts +48 -0
  31. package/src/db/resource/types/ResourceType.ts +10 -0
  32. package/src/main.ts +1 -0
  33. package/src/service/request/general_db_request.ts +20 -0
  34. package/src/service/request/redis_request.ts +21 -0
  35. package/src/service/request/s3_request.ts +15 -0
  36. package/src/service/response/GeneralReponse.ts +10 -0
  37. package/src/types/DateModifiedType.ts +75 -0
  38. package/src/types/FileKindType.ts +80 -0
  39. package/src/util/file_util.ts +178 -0
  40. package/tsconfig.json +23 -0
  41. package/tsconfig.release.json +23 -0
  42. package/unit-test.yml +41 -0
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@l-v-yonsama/multi-platform-database-drivers",
3
+ "version": "0.1.1",
4
+ "description": "multi-platform database drivers in TypeScript.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">= 18.12 <19"
8
+ },
9
+ "devDependencies": {
10
+ "@types/jest": "~29.2",
11
+ "@types/node": "~18",
12
+ "@types/pg": "^8.6.6",
13
+ "@typescript-eslint/eslint-plugin": "~5.44",
14
+ "@typescript-eslint/parser": "~5.44",
15
+ "eslint": "~8.28",
16
+ "eslint-config-prettier": "~8.5",
17
+ "eslint-plugin-jest": "~27.1",
18
+ "jest": "~29.3",
19
+ "prettier": "~2.8",
20
+ "rimraf": "~3.0",
21
+ "ts-jest": "~29.0",
22
+ "tsutils": "~3.21",
23
+ "typescript": "~4.9"
24
+ },
25
+ "scripts": {
26
+ "start": "node build/src/main.js",
27
+ "clean": "rimraf coverage build tmp",
28
+ "prebuild": "npm run lint",
29
+ "build": "tsc -p tsconfig.json",
30
+ "build:watch": "tsc -w -p tsconfig.json",
31
+ "build:release": "npm run clean && tsc -p tsconfig.release.json",
32
+ "lint": "eslint . --ext .ts --ext .mts",
33
+ "lint:fix": "eslint --fix . --ext .ts --ext .mts",
34
+ "test0": "jest --coverage",
35
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
36
+ "test:postgres": "node --experimental-vm-modules node_modules/jest/bin/jest.js __tests__/db/drivers/PostgresDriver.test.ts",
37
+ "test:minio": "node --experimental-vm-modules node_modules/jest/bin/jest.js __tests__/db/drivers/AwsS3Driver.test.ts",
38
+ "prettier": "prettier --config .prettierrc --write .",
39
+ "test:watch": "jest --watch"
40
+ },
41
+ "author": "l-v-yonsama <32474032+l-v-yonsama@users.noreply.github.com>",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "@aws-sdk/client-s3": "^3.279.0",
45
+ "@aws-sdk/credential-providers": "^3.279.0",
46
+ "dayjs": "^1.11.7",
47
+ "dt-sql-parser": "^4.0.0-beta.3.2",
48
+ "enum-values": "^1.2.1",
49
+ "get-port": "^6.1.2",
50
+ "ioredis": "^5.3.1",
51
+ "jconv": "^0.1.5",
52
+ "list-it": "^1.3.10",
53
+ "mysql2": "^3.1.2",
54
+ "pg": "^8.9.0",
55
+ "pretty-bytes": "^6.1.0",
56
+ "short-unique-id": "^4.4.4",
57
+ "shuffle-array": "^1.0.1",
58
+ "simple-statistics": "^7.8.3",
59
+ "tslib": "~2.4",
60
+ "tunnel-ssh": "^5.0.5"
61
+ },
62
+ "volta": {
63
+ "node": "18.12.1"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/l-v-yonsama/db-drivers.git"
68
+ },
69
+ "keywords": [
70
+ "database clients"
71
+ ],
72
+ "bugs": {
73
+ "url": "https://github.com/l-v-yonsama/db-drivers/issues"
74
+ },
75
+ "homepage": "https://github.com/l-v-yonsama/db-drivers#readme"
76
+ }
@@ -0,0 +1,26 @@
1
+ export default class DBError extends Error {
2
+ public code: any;
3
+ public errno: any;
4
+ public sqlMessage: any;
5
+ public sqlState: any;
6
+
7
+ constructor(
8
+ message: any,
9
+ code: any,
10
+ errno: any,
11
+ sqlMessage: any,
12
+ sqlState: any,
13
+ ) {
14
+ super(message);
15
+ this.name = this.constructor.name;
16
+ if (typeof Error.captureStackTrace === 'function') {
17
+ Error.captureStackTrace(this, this.constructor);
18
+ } else {
19
+ this.stack = new Error(message).stack;
20
+ }
21
+ this.code = code;
22
+ this.errno = errno;
23
+ this.sqlMessage = sqlMessage;
24
+ this.sqlState = sqlState;
25
+ }
26
+ }
@@ -0,0 +1,539 @@
1
+ /* eslint-disable no-async-promise-executor */
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ import BaseDriver, { RequestSqlOptions } from './BaseDriver';
4
+ import {
5
+ DbConnection,
6
+ DbResource,
7
+ DbDatabase,
8
+ DbS3Bucket,
9
+ DbS3Owner,
10
+ DbS3Key,
11
+ S3KeySearchResult,
12
+ TableRows,
13
+ SchemaAndTableHints,
14
+ } from '../resource/DbResource';
15
+ import ResultSetDataHolder from '../resource/ResultSetDataHolder';
16
+ import { DBType } from '../resource/types/DBType';
17
+ import {
18
+ CreateBucketCommand,
19
+ DeleteBucketCommand,
20
+ DeleteObjectCommand,
21
+ GetObjectCommand,
22
+ ListBucketsCommand,
23
+ ListObjectsCommand,
24
+ PutObjectCommand,
25
+ PutObjectCommandInput,
26
+ S3Client,
27
+ S3ClientConfig,
28
+ } from '@aws-sdk/client-s3';
29
+
30
+ export default class AwsS3Driver extends BaseDriver {
31
+ s3Client: S3Client;
32
+ finder: any;
33
+ config: S3ClientConfig;
34
+
35
+ constructor(conRes: DbConnection) {
36
+ super(conRes);
37
+ }
38
+
39
+ async connectSub(): Promise<string> {
40
+ this.config = {};
41
+ this.config.region = 'us-west-1';
42
+
43
+ if (this.conRes.region) {
44
+ this.config.region = this.conRes.region;
45
+ }
46
+
47
+ this.config.credentials = {
48
+ accessKeyId: this.conRes.user,
49
+ secretAccessKey: this.conRes.password,
50
+ };
51
+ if (this.conRes.db_type === DBType.Minio) {
52
+ (<any>this.config).endpoint = this.conRes.url;
53
+ (<any>this.config).s3ForcePathStyle = 'true';
54
+ }
55
+ this.s3Client = new S3Client(this.config);
56
+
57
+ return this.test(false);
58
+ }
59
+
60
+ async test(with_connect = false): Promise<string> {
61
+ let errorReason = '';
62
+ if (with_connect) {
63
+ errorReason = await this.asyncConnect();
64
+ }
65
+ if (this.s3Client) {
66
+ try {
67
+ const a = await this.s3Client.send(new ListBucketsCommand({}));
68
+ } catch (e) {
69
+ console.error(e);
70
+ errorReason = e.message;
71
+ }
72
+ }
73
+
74
+ if (with_connect) {
75
+ await this.asyncClose();
76
+ }
77
+
78
+ return errorReason;
79
+ }
80
+
81
+ // getSignedUrl(bucket: string, key: string, expire_minutes: number): string {
82
+ // const signedUrlExpireSeconds = 60 * expire_minutes;
83
+ // const url = this.s3.getSignedUrl('getObject', {
84
+ // Bucket: bucket,
85
+ // Key: key,
86
+ // Expires: signedUrlExpireSeconds,
87
+ // });
88
+ // return url;
89
+ // }
90
+
91
+ async countTables(
92
+ tables: SchemaAndTableHints,
93
+ options: any,
94
+ ): Promise<TableRows[]> {
95
+ return new Array<TableRows>();
96
+ }
97
+
98
+ abortSearch(): void {
99
+ if (this.finder) {
100
+ this.finder.abort();
101
+ }
102
+ }
103
+
104
+ // async search(
105
+ // bucket: string,
106
+ // prefix = '',
107
+ // options: {
108
+ // date_modified_type?: DateModifiedType;
109
+ // file_kind_type?: FileKindType;
110
+ // name?: string;
111
+ // progress_callback?: Function;
112
+ // },
113
+ // ): Promise<S3KeySearchResult> {
114
+ // const ret: S3KeySearchResult = {
115
+ // prefix,
116
+ // list: new Array<DbS3Key>(),
117
+ // ContinuationToken: '',
118
+ // };
119
+ // return new Promise<S3KeySearchResult>(async (resolve) => {
120
+ // try {
121
+ // this.finder = this.super_client.listObjects({
122
+ // recursive: true,
123
+ // s3Params: {
124
+ // Bucket: bucket,
125
+ // Delimiter: '',
126
+ // Prefix: prefix,
127
+ // },
128
+ // });
129
+ // this.finder.on('data', async function (data: any) {
130
+ // if (data.Contents) {
131
+ // for (let i = 0; i < data.Contents.length; i++) {
132
+ // const content = data.Contents[i];
133
+ // // {
134
+ // // Key: 'HLS/tutorial/1M/abc.m3u8',
135
+ // // LastModified: 2018-03-06T04:01:11.000Z,
136
+ // // ETag: '"9bcb47a6acdf633f72e113f442c7ba44"',
137
+ // // Size: 188,
138
+ // // StorageClass: 'STANDARD'
139
+ // // }
140
+ // const k = content.Key;
141
+ // if (k) {
142
+ // let valid = true;
143
+ // if (valid && options.name) {
144
+ // valid = ('' + k).indexOf(options.name) >= 0;
145
+ // }
146
+ // if (valid && options.date_modified_type) {
147
+ // valid = DateModifiedType.isValid(
148
+ // options.date_modified_type,
149
+ // content.LastModified,
150
+ // );
151
+ // }
152
+ // if (valid && options.file_kind_type) {
153
+ // valid = FileKindType.isValid(options.file_kind_type, k + '');
154
+ // }
155
+ // if (valid) {
156
+ // const key = new DbS3Key(k);
157
+ // key.updated = content.LastModified;
158
+ // // console.log('content.LastModified=', content.LastModified, content.LastModified.getTime());
159
+ // key.etag = content.ETag;
160
+ // key.size = content.Size;
161
+ // key.storage_class = content.StorageClass;
162
+ // // output_file_path
163
+ // const fResult = await FileUtil.getKeyFile(bucket, k);
164
+ // if (fResult.ok) {
165
+ // key.output_file_path = fResult.result;
166
+ // if (
167
+ // options.file_kind_type === FileKindType.Image &&
168
+ // key.size &&
169
+ // key.size < 100 * 1024
170
+ // ) {
171
+ // key.base64 = <string>(
172
+ // await FileUtil.readFile(fResult.result, 'base64')
173
+ // );
174
+ // }
175
+ // }
176
+ // ret.list.push(key);
177
+ // }
178
+ // }
179
+ // }
180
+ // }
181
+ // });
182
+ // this.finder.on('progress', () => {
183
+ // if (options.progress_callback) {
184
+ // options.progress_callback(
185
+ // this.finder.objectsFound,
186
+ // this.finder.progressAmount,
187
+ // this.finder.dirsFound,
188
+ // ret.list.length,
189
+ // );
190
+ // }
191
+ // });
192
+ // this.finder.on('end', function () {
193
+ // ret.list.sort((a, b) => b.updated.getTime() - a.updated.getTime());
194
+ // setTimeout(() => resolve(ret), 500);
195
+ // });
196
+ // } catch (e) {
197
+ // ret.error_message = e.message;
198
+ // resolve(ret);
199
+ // }
200
+ // });
201
+ // }
202
+
203
+ // async agetKeys(
204
+ // bucket: string,
205
+ // count: number,
206
+ // config: { prefix?: string; token?: string; scan_pattern?: string },
207
+ // ): Promise<S3KeySearchResult> {
208
+ // const opts: S3.ListObjectsV2Request = {
209
+ // Bucket: bucket,
210
+ // MaxKeys: count,
211
+ // Delimiter: '/',
212
+ // Prefix: '',
213
+ // };
214
+ // if (config) {
215
+ // if (config.prefix && config.prefix !== '/') {
216
+ // opts.Prefix = config.prefix;
217
+ // }
218
+ // if (config.scan_pattern) {
219
+ // opts.Prefix += config.scan_pattern;
220
+ // }
221
+ // if (config.token) {
222
+ // opts.ContinuationToken = config.token;
223
+ // }
224
+ // }
225
+ // const ret: S3KeySearchResult = {
226
+ // prefix: config.prefix,
227
+ // list: new Array<DbS3Key>(),
228
+ // ContinuationToken: '',
229
+ // };
230
+ // try {
231
+ // await this.listAllKeys(opts, count, ret);
232
+ // } catch (e) {
233
+ // ret.error_message = e.message;
234
+ // }
235
+ // return ret;
236
+ // }
237
+
238
+ // async asyncS3ListObjectsV2(
239
+ // opts: S3.ListObjectsV2Request,
240
+ // ): Promise<S3.Types.ListObjectsV2Output> {
241
+ // return new Promise<S3.Types.ListObjectsV2Output>((resolve) => {
242
+ // this.s3.listObjectsV2(opts, (err, data) => {
243
+ // resolve(data);
244
+ // });
245
+ // });
246
+ // }
247
+
248
+ // async listAllKeys(
249
+ // opts: S3.ListObjectsV2Request,
250
+ // count: number,
251
+ // result: S3KeySearchResult,
252
+ // ): Promise<void> {
253
+ // try {
254
+ // const data = await this.asyncS3ListObjectsV2(opts);
255
+ // console.info(LOG_PREFIX, 'data=', data);
256
+ // if (data.CommonPrefixes) {
257
+ // data.CommonPrefixes.forEach((p: any) => {
258
+ // let prefix = p.Prefix;
259
+ // // if (opts.Prefix) {
260
+ // // prefix = prefix.substring(opts.Prefix.length);
261
+ // // }
262
+ // const idx = prefix.lastIndexOf('/', prefix.length - 2);
263
+ // if (idx > 0) {
264
+ // prefix = prefix.substring(idx + 1);
265
+ // }
266
+
267
+ // const dir = new DbS3Key(prefix, true);
268
+ // console.log(LOG_PREFIX, 'L306 push', dir);
269
+ // result.list.push(dir);
270
+ // });
271
+ // }
272
+ // if (data.Contents) {
273
+ // for (let i = 0; i < data.Contents.length; i++) {
274
+ // const content = data.Contents[i];
275
+ // // {
276
+ // // Key: 'HLS/tutorial/1M/abc.m3u8',
277
+ // // LastModified: 2018-03-06T04:01:11.000Z,
278
+ // // ETag: '"9bcb47a6acdf633f72e113f442c7ba44"',
279
+ // // Size: 188,
280
+ // // StorageClass: 'STANDARD'
281
+ // // }
282
+ // let k = content.Key;
283
+ // if (opts.Prefix === k && k.endsWith('/')) {
284
+ // continue;
285
+ // }
286
+ // if (k) {
287
+ // const idx = k.lastIndexOf('/');
288
+ // if (idx > 0 && idx < k.length - 1) {
289
+ // k = k.substring(idx + 1);
290
+ // }
291
+ // const key = new DbS3Key(k);
292
+ // key.updated = content.LastModified;
293
+ // key.etag = content.ETag;
294
+ // key.size = content.Size;
295
+ // key.storage_class = content.StorageClass;
296
+ // // output_file_path
297
+ // const fResult = await FileUtil.getKeyFile(opts.Bucket, k);
298
+ // if (fResult.ok) {
299
+ // key.output_file_path = fResult.result;
300
+ // }
301
+ // console.log(LOG_PREFIX, 'L335 push', key);
302
+ // result.list.push(key);
303
+ // }
304
+ // }
305
+ // }
306
+ // if (data.IsTruncated) {
307
+ // const opts2: S3.ListObjectsV2Request = {
308
+ // Bucket: opts.Bucket,
309
+ // Delimiter: '/',
310
+ // Prefix: opts.Prefix,
311
+ // MaxKeys: count - result.list.length,
312
+ // };
313
+ // if (data.NextContinuationToken) {
314
+ // opts2.ContinuationToken = data.NextContinuationToken;
315
+ // result.ContinuationToken = data.NextContinuationToken;
316
+ // }
317
+ // await this.listAllKeys(opts2, count - result.list.length, result);
318
+ // }
319
+ // } catch (e) {
320
+ // console.error('L98', e);
321
+ // }
322
+ // }
323
+
324
+ // async getKeyRes(bucket: string, prefix: string): Promise<DbS3Key> {
325
+ // let key = null;
326
+ // try {
327
+ // const opts: S3.ListObjectsV2Request = {
328
+ // Bucket: bucket,
329
+ // MaxKeys: 1,
330
+ // Delimiter: '/',
331
+ // Prefix: prefix,
332
+ // };
333
+ // const data = await this.asyncS3ListObjectsV2(opts);
334
+ // if (data.Contents && data.Contents.length > 0) {
335
+ // const content = data.Contents[0];
336
+ // let k = content.Key;
337
+ // if (k) {
338
+ // const idx = k.lastIndexOf('/');
339
+ // if (idx > 0 && idx < k.length - 1) {
340
+ // k = k.substring(idx + 1);
341
+ // }
342
+ // key = new DbS3Key(k);
343
+ // key.updated = content.LastModified;
344
+ // key.etag = content.ETag;
345
+ // key.size = content.Size;
346
+ // key.storage_class = content.StorageClass;
347
+ // // output_file_path
348
+ // const fResult = await FileUtil.getKeyFile(opts.Bucket, k);
349
+ // if (fResult.ok) {
350
+ // key.output_file_path = fResult.result;
351
+ // }
352
+ // }
353
+ // }
354
+ // } catch (e) {
355
+ // console.error('L98', e);
356
+ // }
357
+ // return key;
358
+ // }
359
+
360
+ // async putObject(
361
+ // bucket: string,
362
+ // key: string,
363
+ // file_path: string,
364
+ // ): Promise<string | undefined> {
365
+ // const params: any = {
366
+ // Bucket: bucket,
367
+ // Key: key,
368
+ // };
369
+ // const v = fs.readFileSync(file_path);
370
+ // params.Body = v;
371
+ // return new Promise<string | undefined>((resolve, reject) => {
372
+ // this.s3.putObject(params, (err, data) => {
373
+ // resolve(data.ETag);
374
+ // });
375
+ // });
376
+ // }
377
+
378
+ async getResouces(options: {
379
+ progress_callback?: Function | undefined;
380
+ params?: any;
381
+ }): Promise<Array<DbResource>> {
382
+ if (!this.conRes) {
383
+ return [];
384
+ }
385
+ const dbResources = new Array<DbResource>();
386
+ const dbDatabase = new DbDatabase('S3');
387
+ dbResources.push(dbDatabase);
388
+
389
+ try {
390
+ const buckets = await this.s3Client.send(new ListBucketsCommand({}));
391
+ if (buckets.Buckets) {
392
+ for (const bucket of buckets.Buckets) {
393
+ const dbBucket = new DbS3Bucket(bucket.Name, bucket.CreationDate);
394
+ dbDatabase.addChild(dbBucket);
395
+ }
396
+ }
397
+ if (buckets.Owner) {
398
+ const dbOwner = new DbS3Owner(
399
+ buckets.Owner.ID,
400
+ buckets.Owner.DisplayName,
401
+ );
402
+ dbDatabase.addChild(dbOwner);
403
+ } else {
404
+ // log.info('Owner nothing.')
405
+ }
406
+ } catch (e) {
407
+ console.error(e);
408
+ // reject(e);
409
+ }
410
+ return dbResources;
411
+ }
412
+ async requestSql(
413
+ sql: string,
414
+ options?: RequestSqlOptions,
415
+ ): Promise<ResultSetDataHolder> {
416
+ return new ResultSetDataHolder([]);
417
+ }
418
+
419
+ // async asyncGetSignedUrl(
420
+ // bucket: string,
421
+ // key: string,
422
+ // ex: number,
423
+ // ): Promise<string> {
424
+ // const LOG_PREFIX = `【S3:署名付きURL取得】<Bucket:${bucket}> [Key:${key}]`;
425
+ // const signedUrlExpireSeconds = ex;
426
+
427
+ // const url = this.s3.getSignedUrl('getObject', {
428
+ // Bucket: bucket,
429
+ // Key: key,
430
+ // Expires: signedUrlExpireSeconds,
431
+ // });
432
+ // return url;
433
+ // }
434
+ // async asyncGet(bucket: string, key: string): Promise<string> {
435
+ // const LOG_PREFIX = `【S3:取得】<Bucket:${bucket}> [Key:${key}]`;
436
+ // const output_dir_path = await FileUtil.createKeyFolder(bucket, key);
437
+
438
+ // return new Promise<string>((resolve, reject) => {
439
+ // const params = {
440
+ // Bucket: bucket,
441
+ // Key: key,
442
+ // };
443
+ // const output_file_path = path.join(output_dir_path, path.basename(key));
444
+ // const file = fs.createWriteStream(output_file_path);
445
+ // this.s3
446
+ // .getObject(params)
447
+ // .createReadStream()
448
+ // .on('end', () => {
449
+ // return resolve(output_file_path);
450
+ // })
451
+ // .on('error', (error) => {
452
+ // return reject(error);
453
+ // })
454
+ // .pipe(file);
455
+ // });
456
+ // }
457
+
458
+ async createBucket({ bucket }: { bucket: string }): Promise<void> {
459
+ await this.s3Client.send(new CreateBucketCommand({ Bucket: bucket }));
460
+ }
461
+
462
+ async getValueByKey({
463
+ bucket,
464
+ key,
465
+ }: {
466
+ bucket: string;
467
+ key: string;
468
+ }): Promise<any> {
469
+ // Get the object from the Amazon S3 bucket. It is returned as a ReadableStream.
470
+ const data = await this.s3Client.send(
471
+ new GetObjectCommand({
472
+ Bucket: bucket,
473
+ Key: key,
474
+ }),
475
+ );
476
+ return data.Body.transformToString();
477
+ }
478
+
479
+ async putObject({
480
+ bucket,
481
+ key,
482
+ body,
483
+ }: {
484
+ bucket: string;
485
+ key: string;
486
+ body: PutObjectCommandInput['Body'];
487
+ }): Promise<void> {
488
+ await this.s3Client.send(
489
+ new PutObjectCommand({
490
+ Bucket: bucket,
491
+ Key: key,
492
+ Body: body,
493
+ }),
494
+ );
495
+ }
496
+
497
+ async removeBucket({ bucket }: { bucket: string }): Promise<void> {
498
+ await this.removeAllObjects({ bucket });
499
+ await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucket }));
500
+ }
501
+
502
+ async removeAllObjects({ bucket }: { bucket: string }): Promise<void> {
503
+ let truncated = true;
504
+ let pageMarker;
505
+ const bucketParams: any = { Bucket: bucket };
506
+
507
+ while (truncated) {
508
+ const response = await this.s3Client.send(
509
+ new ListObjectsCommand(bucketParams),
510
+ );
511
+ // return response; //For unit tests
512
+ for (let i = 0; i < response.Contents.length; i++) {
513
+ const item = response.Contents[i];
514
+ const delR = await this.s3Client.send(
515
+ new DeleteObjectCommand({
516
+ Bucket: bucket,
517
+ Key: item.Key,
518
+ }),
519
+ );
520
+ }
521
+ // Log the key of every item in the response to standard output.
522
+ truncated = response.IsTruncated;
523
+ // If truncated is true, assign the key of the last element in the response to the pageMarker variable.
524
+ if (truncated) {
525
+ pageMarker = response.Contents.slice(-1)[0].Key;
526
+ // Assign the pageMarker value to bucketParams so that the next iteration starts from the new pageMarker.
527
+ bucketParams.Marker = pageMarker;
528
+ }
529
+ // At end of the list, response.truncated is false, and the function exits the while loop.
530
+ }
531
+ }
532
+
533
+ async closeSub(): Promise<string> {
534
+ if (this.s3Client) {
535
+ this.s3Client.destroy();
536
+ }
537
+ return '';
538
+ }
539
+ }