@geekmidas/storage 0.0.4 → 0.1.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/README.md +71 -1
- package/dist/{AmazonStorageClient-DT9DeKVz.cjs → AmazonStorageClient-BKzXhCX0.cjs} +40 -10
- package/dist/AmazonStorageClient-BKzXhCX0.cjs.map +1 -0
- package/dist/{AmazonStorageClient-DJMWn8vO.mjs → AmazonStorageClient-CwUey48q.mjs} +15 -7
- package/dist/AmazonStorageClient-CwUey48q.mjs.map +1 -0
- package/dist/{AmazonStorageClient-C16fG3G4.d.cts → AmazonStorageClient-Gz0Fh1sy.d.mts} +7 -3
- package/dist/{AmazonStorageClient-CP8Tx5oE.d.mts → AmazonStorageClient-aduVU3_p.d.cts} +7 -3
- package/dist/AmazonStorageClient.cjs +1 -1
- package/dist/AmazonStorageClient.d.cts +2 -2
- package/dist/AmazonStorageClient.d.mts +2 -2
- package/dist/AmazonStorageClient.mjs +2 -2
- package/dist/{StorageClient-CZGIC_fz.mjs → StorageClient-4ADnTPnJ.mjs} +2 -1
- package/dist/StorageClient-4ADnTPnJ.mjs.map +1 -0
- package/dist/StorageClient-BDRVrMJj.cjs +2 -1
- package/dist/StorageClient-BDRVrMJj.cjs.map +1 -0
- package/dist/{StorageClient-D-y4QLDq.d.cts → StorageClient-CcOAW3YV.d.cts} +5 -1
- package/dist/{StorageClient-CJbHRTVV.d.mts → StorageClient-XgMsiUrd.d.mts} +5 -1
- package/dist/StorageClient.d.cts +1 -1
- package/dist/StorageClient.d.mts +1 -1
- package/dist/StorageClient.mjs +1 -1
- package/dist/aws.cjs +1 -1
- package/dist/aws.d.cts +2 -2
- package/dist/aws.d.mts +2 -2
- package/dist/aws.mjs +2 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/package.json +28 -3
- package/src/AmazonStorageClient.ts +22 -3
- package/src/StorageClient.ts +2 -0
- package/src/__tests__/AmazonStorageClient.spec.ts +183 -0
- package/dist/__tests__/AmazonStorageClient.spec.cjs +0 -234
- package/dist/__tests__/AmazonStorageClient.spec.d.cts +0 -1
- package/dist/__tests__/AmazonStorageClient.spec.d.mts +0 -1
- package/dist/__tests__/AmazonStorageClient.spec.mjs +0 -233
- package/dist/__tests__/StorageClient.spec.cjs +0 -81
- package/dist/__tests__/StorageClient.spec.d.cts +0 -1
- package/dist/__tests__/StorageClient.spec.d.mts +0 -1
- package/dist/__tests__/StorageClient.spec.mjs +0 -81
- package/dist/chunk-DWy1uDak.cjs +0 -39
- package/dist/magic-string.es-BvJNBYzY.mjs +0 -1014
- package/dist/magic-string.es-CKiDiYoS.cjs +0 -1015
- package/dist/vi.bdSIJ99Y-CucmVasy.mjs +0 -14928
- package/dist/vi.bdSIJ99Y-D0m6qImj.cjs +0 -14926
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
8
8
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
9
9
|
|
|
10
|
+
import type { Cache } from '@geekmidas/cache';
|
|
10
11
|
import {
|
|
11
12
|
type DocumentVersion,
|
|
12
13
|
type File,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
|
|
19
20
|
export class AmazonStorageClient implements StorageClient {
|
|
20
21
|
readonly provider = StorageProvider.AWSS3;
|
|
22
|
+
|
|
21
23
|
static create(
|
|
22
24
|
options: AmazonStorageClientCreateOptions,
|
|
23
25
|
): AmazonStorageClient {
|
|
@@ -29,6 +31,7 @@ export class AmazonStorageClient implements StorageClient {
|
|
|
29
31
|
endpoint,
|
|
30
32
|
secretAccessKey,
|
|
31
33
|
forcePathStyle = false,
|
|
34
|
+
cache,
|
|
32
35
|
} = options;
|
|
33
36
|
const hasCredentials = accessKeyId && secretAccessKey;
|
|
34
37
|
const credentials = hasCredentials
|
|
@@ -42,12 +45,13 @@ export class AmazonStorageClient implements StorageClient {
|
|
|
42
45
|
forcePathStyle,
|
|
43
46
|
});
|
|
44
47
|
|
|
45
|
-
return new AmazonStorageClient(client, bucket, acl);
|
|
48
|
+
return new AmazonStorageClient(client, bucket, acl, cache);
|
|
46
49
|
}
|
|
47
50
|
constructor(
|
|
48
51
|
private readonly client: S3Client,
|
|
49
52
|
private readonly bucket: string,
|
|
50
53
|
private readonly acl = AmazonCannedAccessControlList.AuthenticatedRead,
|
|
54
|
+
readonly cache?: Cache<string>,
|
|
51
55
|
) {}
|
|
52
56
|
|
|
53
57
|
getVersionDownloadURL(file: File, versionId: string): Promise<string> {
|
|
@@ -79,7 +83,14 @@ export class AmazonStorageClient implements StorageClient {
|
|
|
79
83
|
}));
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
getDownloadURL(file: File, expiresIn = 60 * 60): Promise<string> {
|
|
86
|
+
async getDownloadURL(file: File, expiresIn = 60 * 60): Promise<string> {
|
|
87
|
+
const cacheKey = `download-url:${file.path}`;
|
|
88
|
+
const cachedURL = await this.cache?.get(cacheKey);
|
|
89
|
+
|
|
90
|
+
if (cachedURL) {
|
|
91
|
+
return cachedURL;
|
|
92
|
+
}
|
|
93
|
+
|
|
83
94
|
const ResponseContentDisposition = file.name
|
|
84
95
|
? `attachment; filename=${encodeURIComponent(file.name)}`
|
|
85
96
|
: undefined;
|
|
@@ -90,7 +101,14 @@ export class AmazonStorageClient implements StorageClient {
|
|
|
90
101
|
ResponseContentDisposition,
|
|
91
102
|
});
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
const url = await getSignedUrl(this.client, command, { expiresIn });
|
|
105
|
+
const cacheExpiration = Math.max(expiresIn - 60, 0);
|
|
106
|
+
|
|
107
|
+
if (cacheExpiration) {
|
|
108
|
+
await this.cache?.set(cacheKey, url, cacheExpiration);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return url;
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
async getUploadURL(
|
|
@@ -173,4 +191,5 @@ interface AmazonStorageClientCreateOptions {
|
|
|
173
191
|
secretAccessKey?: string;
|
|
174
192
|
endpoint?: string;
|
|
175
193
|
forcePathStyle?: boolean;
|
|
194
|
+
cache?: Cache<string>;
|
|
176
195
|
}
|
package/src/StorageClient.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Cache } from '@geekmidas/cache';
|
|
1
2
|
export interface DocumentVersion {
|
|
2
3
|
id: string;
|
|
3
4
|
createdAt: Date;
|
|
@@ -11,6 +12,7 @@ export enum StorageProvider {
|
|
|
11
12
|
|
|
12
13
|
export interface StorageClient {
|
|
13
14
|
readonly provider: StorageProvider;
|
|
15
|
+
readonly cache?: Cache<string>;
|
|
14
16
|
/**
|
|
15
17
|
* Get a URL to upload a file to.
|
|
16
18
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import { InMemoryCache } from '@geekmidas/cache/memory';
|
|
2
3
|
import { beforeAll, describe, expect, it } from 'vitest';
|
|
3
4
|
import {
|
|
4
5
|
AmazonCannedAccessControlList,
|
|
@@ -324,4 +325,186 @@ describe('AmazonStorageClient Integration Tests', () => {
|
|
|
324
325
|
expect(storageClient.provider).toBe(StorageProvider.AWSS3);
|
|
325
326
|
});
|
|
326
327
|
});
|
|
328
|
+
|
|
329
|
+
describe('cache functionality', () => {
|
|
330
|
+
it('should cache download URLs', async () => {
|
|
331
|
+
const cache = new InMemoryCache<string>();
|
|
332
|
+
const clientWithCache = AmazonStorageClient.create({
|
|
333
|
+
bucket: testBucket,
|
|
334
|
+
region: 'us-east-1',
|
|
335
|
+
accessKeyId: 'geekmidas',
|
|
336
|
+
secretAccessKey: 'geekmidas',
|
|
337
|
+
endpoint: 'http://localhost:9000',
|
|
338
|
+
forcePathStyle: true,
|
|
339
|
+
cache,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const key = 'test-files/cache-test.txt';
|
|
343
|
+
await clientWithCache.upload(key, testContent, testContentType);
|
|
344
|
+
|
|
345
|
+
// First call should generate and cache the URL
|
|
346
|
+
const url1 = await clientWithCache.getDownloadURL({ path: key });
|
|
347
|
+
|
|
348
|
+
// Second call should return the cached URL
|
|
349
|
+
const url2 = await clientWithCache.getDownloadURL({ path: key });
|
|
350
|
+
|
|
351
|
+
// URLs should be identical (same cached value)
|
|
352
|
+
expect(url1).toBe(url2);
|
|
353
|
+
|
|
354
|
+
// Verify the URL was cached
|
|
355
|
+
const cacheKey = `download-url:${key}`;
|
|
356
|
+
const cachedUrl = await cache.get(cacheKey);
|
|
357
|
+
expect(cachedUrl).toBe(url1);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should respect cache expiration based on URL expiry', async () => {
|
|
361
|
+
const cache = new InMemoryCache<string>();
|
|
362
|
+
const clientWithCache = AmazonStorageClient.create({
|
|
363
|
+
bucket: testBucket,
|
|
364
|
+
region: 'us-east-1',
|
|
365
|
+
accessKeyId: 'geekmidas',
|
|
366
|
+
secretAccessKey: 'geekmidas',
|
|
367
|
+
endpoint: 'http://localhost:9000',
|
|
368
|
+
forcePathStyle: true,
|
|
369
|
+
cache,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const key = 'test-files/cache-expiry.txt';
|
|
373
|
+
await clientWithCache.upload(key, testContent, testContentType);
|
|
374
|
+
|
|
375
|
+
// Generate URL with short expiry that won't be cached
|
|
376
|
+
await clientWithCache.getDownloadURL({ path: key }, 30); // 30 seconds
|
|
377
|
+
|
|
378
|
+
// Check cache doesn't contain the URL (too short to cache)
|
|
379
|
+
const cacheKey = `download-url:${key}`;
|
|
380
|
+
const cachedShortUrl = await cache.get(cacheKey);
|
|
381
|
+
expect(cachedShortUrl).toBeUndefined();
|
|
382
|
+
|
|
383
|
+
// Generate URL with longer expiry that will be cached
|
|
384
|
+
const longExpiryUrl = await clientWithCache.getDownloadURL(
|
|
385
|
+
{ path: key },
|
|
386
|
+
3600,
|
|
387
|
+
); // 1 hour
|
|
388
|
+
|
|
389
|
+
// Check cache contains the URL
|
|
390
|
+
const cachedLongUrl = await cache.get(cacheKey);
|
|
391
|
+
expect(cachedLongUrl).toBe(longExpiryUrl);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('should use cached URL when available', async () => {
|
|
395
|
+
const cache = new InMemoryCache<string>();
|
|
396
|
+
const clientWithCache = AmazonStorageClient.create({
|
|
397
|
+
bucket: testBucket,
|
|
398
|
+
region: 'us-east-1',
|
|
399
|
+
accessKeyId: 'geekmidas',
|
|
400
|
+
secretAccessKey: 'geekmidas',
|
|
401
|
+
endpoint: 'http://localhost:9000',
|
|
402
|
+
forcePathStyle: true,
|
|
403
|
+
cache,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const key = 'test-files/pre-cached.txt';
|
|
407
|
+
const cachedUrl = 'https://pre-cached-url.example.com';
|
|
408
|
+
const cacheKey = `download-url:${key}`;
|
|
409
|
+
|
|
410
|
+
// Pre-populate cache
|
|
411
|
+
await cache.set(cacheKey, cachedUrl);
|
|
412
|
+
|
|
413
|
+
// Get download URL should return cached value
|
|
414
|
+
const url = await clientWithCache.getDownloadURL({ path: key });
|
|
415
|
+
expect(url).toBe(cachedUrl);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('should work without cache', async () => {
|
|
419
|
+
// Client without cache should work normally
|
|
420
|
+
const clientNoCache = AmazonStorageClient.create({
|
|
421
|
+
bucket: testBucket,
|
|
422
|
+
region: 'us-east-1',
|
|
423
|
+
accessKeyId: 'geekmidas',
|
|
424
|
+
secretAccessKey: 'geekmidas',
|
|
425
|
+
endpoint: 'http://localhost:9000',
|
|
426
|
+
forcePathStyle: true,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const key = 'test-files/no-cache.txt';
|
|
430
|
+
await clientNoCache.upload(key, testContent, testContentType);
|
|
431
|
+
|
|
432
|
+
const url1 = await clientNoCache.getDownloadURL({ path: key });
|
|
433
|
+
const url2 = await clientNoCache.getDownloadURL({ path: key });
|
|
434
|
+
|
|
435
|
+
// Without cache, new URLs might be generated each time
|
|
436
|
+
// Both should be valid URLs
|
|
437
|
+
expect(url1).toMatch(
|
|
438
|
+
/^http:\/\/localhost:9000\/geekmidas\/test-files\/no-cache\.txt/,
|
|
439
|
+
);
|
|
440
|
+
expect(url2).toMatch(
|
|
441
|
+
/^http:\/\/localhost:9000\/geekmidas\/test-files\/no-cache\.txt/,
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('should pass cache to constructor', () => {
|
|
446
|
+
const cache = new InMemoryCache<string>();
|
|
447
|
+
const s3Client = new S3Client({
|
|
448
|
+
region: 'us-east-1',
|
|
449
|
+
credentials: {
|
|
450
|
+
accessKeyId: 'geekmidas',
|
|
451
|
+
secretAccessKey: 'geekmidas',
|
|
452
|
+
},
|
|
453
|
+
endpoint: 'http://localhost:9000',
|
|
454
|
+
forcePathStyle: true,
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const clientWithCache = new AmazonStorageClient(
|
|
458
|
+
s3Client,
|
|
459
|
+
testBucket,
|
|
460
|
+
AmazonCannedAccessControlList.PublicRead,
|
|
461
|
+
cache,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(clientWithCache.cache).toBe(cache);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
describe('getVersionDownloadURL', () => {
|
|
469
|
+
it('should generate version-specific download URL', async () => {
|
|
470
|
+
const versionKey = 'test-files/version-download.txt';
|
|
471
|
+
const versionId = 'test-version-id';
|
|
472
|
+
|
|
473
|
+
// Upload a file first
|
|
474
|
+
await client.upload(versionKey, testContent, testContentType);
|
|
475
|
+
|
|
476
|
+
// Generate version-specific download URL
|
|
477
|
+
const downloadUrl = await client.getVersionDownloadURL(
|
|
478
|
+
{ path: versionKey, name: 'versioned-file.txt' },
|
|
479
|
+
versionId,
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
expect(downloadUrl).toMatch(
|
|
483
|
+
/^http:\/\/localhost:9000\/geekmidas\/test-files\/version-download\.txt/,
|
|
484
|
+
);
|
|
485
|
+
expect(downloadUrl).toContain('X-Amz-Algorithm=AWS4-HMAC-SHA256');
|
|
486
|
+
expect(downloadUrl).toContain(
|
|
487
|
+
'response-content-disposition=attachment%3B%20filename%3Dversioned-file.txt',
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should generate version URL without filename', async () => {
|
|
492
|
+
const versionKey = 'test-files/version-no-name.txt';
|
|
493
|
+
const versionId = 'test-version-id';
|
|
494
|
+
|
|
495
|
+
// Upload a file first
|
|
496
|
+
await client.upload(versionKey, testContent, testContentType);
|
|
497
|
+
|
|
498
|
+
// Generate version-specific download URL without filename
|
|
499
|
+
const downloadUrl = await client.getVersionDownloadURL(
|
|
500
|
+
{ path: versionKey },
|
|
501
|
+
versionId,
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
expect(downloadUrl).toMatch(
|
|
505
|
+
/^http:\/\/localhost:9000\/geekmidas\/test-files\/version-no-name\.txt/,
|
|
506
|
+
);
|
|
507
|
+
expect(downloadUrl).not.toContain('response-content-disposition');
|
|
508
|
+
});
|
|
509
|
+
});
|
|
327
510
|
});
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
const require_chunk = require('../chunk-DWy1uDak.cjs');
|
|
2
|
-
const require_StorageClient = require('../StorageClient-BDRVrMJj.cjs');
|
|
3
|
-
const require_AmazonStorageClient = require('../AmazonStorageClient-DT9DeKVz.cjs');
|
|
4
|
-
const require_vi_bdSIJ99Y = require('../vi.bdSIJ99Y-D0m6qImj.cjs');
|
|
5
|
-
const __aws_sdk_client_s3 = require_chunk.__toESM(require("@aws-sdk/client-s3"));
|
|
6
|
-
|
|
7
|
-
//#region src/__tests__/AmazonStorageClient.spec.ts
|
|
8
|
-
require_vi_bdSIJ99Y.describe("AmazonStorageClient Integration Tests", () => {
|
|
9
|
-
let client;
|
|
10
|
-
const testBucket = "geekmidas";
|
|
11
|
-
const testKey = "test-files/test.txt";
|
|
12
|
-
const testContent = "Hello, World!";
|
|
13
|
-
const testContentType = "text/plain";
|
|
14
|
-
require_vi_bdSIJ99Y.beforeAll(async () => {
|
|
15
|
-
client = require_AmazonStorageClient.AmazonStorageClient.create({
|
|
16
|
-
bucket: testBucket,
|
|
17
|
-
region: "us-east-1",
|
|
18
|
-
accessKeyId: "geekmidas",
|
|
19
|
-
secretAccessKey: "geekmidas",
|
|
20
|
-
endpoint: "http://localhost:9000",
|
|
21
|
-
acl: require_AmazonStorageClient.AmazonCannedAccessControlList.PublicRead,
|
|
22
|
-
forcePathStyle: true
|
|
23
|
-
});
|
|
24
|
-
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
25
|
-
});
|
|
26
|
-
require_vi_bdSIJ99Y.describe("provider property", () => {
|
|
27
|
-
require_vi_bdSIJ99Y.it("should return AWS S3 provider", () => {
|
|
28
|
-
require_vi_bdSIJ99Y.globalExpect(client.provider).toBe(require_StorageClient.StorageProvider.AWSS3);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
require_vi_bdSIJ99Y.describe("upload and download workflow", () => {
|
|
32
|
-
require_vi_bdSIJ99Y.it.skip("should upload a file directly and generate download URL", async () => {
|
|
33
|
-
await client.upload(testKey, testContent, testContentType);
|
|
34
|
-
const downloadUrl = await client.getDownloadURL({ path: testKey });
|
|
35
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toMatch(/^http:\/\/localhost:9000\/geekmidas\/test-files\/test\.txt/);
|
|
36
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("X-Amz-Algorithm=AWS4-HMAC-SHA256");
|
|
37
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("X-Amz-Credential=");
|
|
38
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("X-Amz-Date=");
|
|
39
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("X-Amz-SignedHeaders=");
|
|
40
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("X-Amz-Signature=");
|
|
41
|
-
const response = await fetch(downloadUrl);
|
|
42
|
-
require_vi_bdSIJ99Y.globalExpect(response.ok).toBe(true);
|
|
43
|
-
const downloadedContent = await response.text();
|
|
44
|
-
require_vi_bdSIJ99Y.globalExpect(downloadedContent).toBe(testContent);
|
|
45
|
-
});
|
|
46
|
-
require_vi_bdSIJ99Y.it("should upload a file with filename and generate download URL with content disposition", async () => {
|
|
47
|
-
const keyWithName = "test-files/named-file.txt";
|
|
48
|
-
const fileName = "my-document.txt";
|
|
49
|
-
await client.upload(keyWithName, testContent, testContentType);
|
|
50
|
-
const downloadUrl = await client.getDownloadURL({
|
|
51
|
-
path: keyWithName,
|
|
52
|
-
name: fileName
|
|
53
|
-
});
|
|
54
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toContain("response-content-disposition=attachment%3B%20filename%3Dmy-document.txt");
|
|
55
|
-
});
|
|
56
|
-
require_vi_bdSIJ99Y.it("should handle binary data upload", async () => {
|
|
57
|
-
const binaryKey = "test-files/binary.bin";
|
|
58
|
-
const binaryData = Buffer.from([
|
|
59
|
-
72,
|
|
60
|
-
101,
|
|
61
|
-
108,
|
|
62
|
-
108,
|
|
63
|
-
111
|
|
64
|
-
]);
|
|
65
|
-
await client.upload(binaryKey, binaryData, "application/octet-stream");
|
|
66
|
-
const downloadUrl = await client.getDownloadURL({ path: binaryKey });
|
|
67
|
-
const response = await fetch(downloadUrl);
|
|
68
|
-
const downloadedBuffer = await response.arrayBuffer();
|
|
69
|
-
require_vi_bdSIJ99Y.globalExpect(new Uint8Array(downloadedBuffer)).toEqual(new Uint8Array(binaryData));
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
require_vi_bdSIJ99Y.describe("presigned upload URLs", () => {
|
|
73
|
-
require_vi_bdSIJ99Y.it("should generate presigned PUT upload URL", async () => {
|
|
74
|
-
const uploadKey = "test-files/presigned-upload.txt";
|
|
75
|
-
const uploadUrl = await client.getUploadURL({
|
|
76
|
-
path: uploadKey,
|
|
77
|
-
contentType: testContentType,
|
|
78
|
-
contentLength: testContent.length
|
|
79
|
-
});
|
|
80
|
-
require_vi_bdSIJ99Y.globalExpect(uploadUrl).toMatch(/^http:\/\/localhost:9000\/geekmidas\/test-files\/presigned-upload\.txt/);
|
|
81
|
-
require_vi_bdSIJ99Y.globalExpect(uploadUrl).toContain("X-Amz-Algorithm=AWS4-HMAC-SHA256");
|
|
82
|
-
const uploadResponse = await fetch(uploadUrl, {
|
|
83
|
-
method: "PUT",
|
|
84
|
-
headers: {
|
|
85
|
-
"Content-Type": testContentType,
|
|
86
|
-
"Content-Length": testContent.length.toString()
|
|
87
|
-
},
|
|
88
|
-
body: testContent
|
|
89
|
-
});
|
|
90
|
-
require_vi_bdSIJ99Y.globalExpect(uploadResponse.ok).toBe(true);
|
|
91
|
-
const downloadUrl = await client.getDownloadURL({ path: uploadKey });
|
|
92
|
-
const downloadResponse = await fetch(downloadUrl);
|
|
93
|
-
const downloadedContent = await downloadResponse.text();
|
|
94
|
-
require_vi_bdSIJ99Y.globalExpect(downloadedContent).toBe(testContent);
|
|
95
|
-
});
|
|
96
|
-
require_vi_bdSIJ99Y.it("should generate presigned POST upload with form fields", async () => {
|
|
97
|
-
const uploadKey = "test-files/presigned-post.txt";
|
|
98
|
-
const uploadData = await client.getUpload({
|
|
99
|
-
path: uploadKey,
|
|
100
|
-
contentType: testContentType,
|
|
101
|
-
contentLength: testContent.length
|
|
102
|
-
});
|
|
103
|
-
require_vi_bdSIJ99Y.globalExpect(uploadData.url).toBe("http://localhost:9000/geekmidas");
|
|
104
|
-
require_vi_bdSIJ99Y.globalExpect(uploadData.fields).toContainEqual({
|
|
105
|
-
key: "key",
|
|
106
|
-
value: uploadKey
|
|
107
|
-
});
|
|
108
|
-
require_vi_bdSIJ99Y.globalExpect(uploadData.fields).toContainEqual({
|
|
109
|
-
key: "acl",
|
|
110
|
-
value: "public-read"
|
|
111
|
-
});
|
|
112
|
-
const formData = new FormData();
|
|
113
|
-
uploadData.fields.forEach(({ key, value }) => {
|
|
114
|
-
formData.append(key, value);
|
|
115
|
-
});
|
|
116
|
-
formData.append("file", new Blob([testContent], { type: testContentType }));
|
|
117
|
-
const uploadResponse = await fetch(uploadData.url, {
|
|
118
|
-
method: "POST",
|
|
119
|
-
body: formData
|
|
120
|
-
});
|
|
121
|
-
require_vi_bdSIJ99Y.globalExpect(uploadResponse.ok).toBe(true);
|
|
122
|
-
const downloadUrl = await client.getDownloadURL({ path: uploadKey });
|
|
123
|
-
const downloadResponse = await fetch(downloadUrl);
|
|
124
|
-
const downloadedContent = await downloadResponse.blob();
|
|
125
|
-
require_vi_bdSIJ99Y.globalExpect(downloadedContent).toEqual(new Blob([testContent], { type: testContentType }));
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
require_vi_bdSIJ99Y.describe("versioning", () => {
|
|
129
|
-
require_vi_bdSIJ99Y.it("should handle file versions", async () => {
|
|
130
|
-
const versionKey = "test-files/versioned.txt";
|
|
131
|
-
const content1 = "Version 1";
|
|
132
|
-
const content2 = "Version 2";
|
|
133
|
-
await client.upload(versionKey, content1, testContentType);
|
|
134
|
-
await client.upload(versionKey, content2, testContentType);
|
|
135
|
-
const versions = await client.getVersions(versionKey);
|
|
136
|
-
require_vi_bdSIJ99Y.globalExpect(Array.isArray(versions)).toBe(true);
|
|
137
|
-
if (versions.length > 0) {
|
|
138
|
-
require_vi_bdSIJ99Y.globalExpect(versions[0]).toHaveProperty("id");
|
|
139
|
-
require_vi_bdSIJ99Y.globalExpect(versions[0]).toHaveProperty("createdAt");
|
|
140
|
-
require_vi_bdSIJ99Y.globalExpect(versions[0].createdAt).toBeInstanceOf(Date);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
require_vi_bdSIJ99Y.describe("error handling", () => {
|
|
145
|
-
require_vi_bdSIJ99Y.it("should handle download URL generation for non-existent files", async () => {
|
|
146
|
-
const downloadUrl = await client.getDownloadURL({ path: "non-existent/file.txt" });
|
|
147
|
-
require_vi_bdSIJ99Y.globalExpect(downloadUrl).toMatch(/^http:\/\/localhost:9000\/geekmidas\/non-existent\/file\.txt/);
|
|
148
|
-
const response = await fetch(downloadUrl);
|
|
149
|
-
require_vi_bdSIJ99Y.globalExpect(response.status).toBe(404);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
require_vi_bdSIJ99Y.describe("custom expiration times", () => {
|
|
153
|
-
require_vi_bdSIJ99Y.it("should respect custom expiration for download URLs", async () => {
|
|
154
|
-
const key = "test-files/expiration-test.txt";
|
|
155
|
-
await client.upload(key, testContent, testContentType);
|
|
156
|
-
const shortExpirationUrl = await client.getDownloadURL({ path: key }, 1);
|
|
157
|
-
const longExpirationUrl = await client.getDownloadURL({ path: key }, 3600);
|
|
158
|
-
require_vi_bdSIJ99Y.globalExpect(shortExpirationUrl).toContain("X-Amz-Expires=1");
|
|
159
|
-
require_vi_bdSIJ99Y.globalExpect(longExpirationUrl).toContain("X-Amz-Expires=3600");
|
|
160
|
-
});
|
|
161
|
-
require_vi_bdSIJ99Y.it("should respect custom expiration for upload URLs", async () => {
|
|
162
|
-
const key = "test-files/upload-expiration-test.txt";
|
|
163
|
-
const shortExpirationUrl = await client.getUploadURL({
|
|
164
|
-
path: key,
|
|
165
|
-
contentType: testContentType,
|
|
166
|
-
contentLength: testContent.length
|
|
167
|
-
}, 1);
|
|
168
|
-
const longExpirationUrl = await client.getUploadURL({
|
|
169
|
-
path: key,
|
|
170
|
-
contentType: testContentType,
|
|
171
|
-
contentLength: testContent.length
|
|
172
|
-
}, 3600);
|
|
173
|
-
require_vi_bdSIJ99Y.globalExpect(shortExpirationUrl).toContain("X-Amz-Expires=1");
|
|
174
|
-
require_vi_bdSIJ99Y.globalExpect(longExpirationUrl).toContain("X-Amz-Expires=3600");
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
require_vi_bdSIJ99Y.describe("factory method", () => {
|
|
178
|
-
require_vi_bdSIJ99Y.it("should create client with minimal configuration", () => {
|
|
179
|
-
const minimalClient = require_AmazonStorageClient.AmazonStorageClient.create({ bucket: "geekmidas" });
|
|
180
|
-
require_vi_bdSIJ99Y.globalExpect(minimalClient).toBeInstanceOf(require_AmazonStorageClient.AmazonStorageClient);
|
|
181
|
-
require_vi_bdSIJ99Y.globalExpect(minimalClient.provider).toBe(require_StorageClient.StorageProvider.AWSS3);
|
|
182
|
-
});
|
|
183
|
-
require_vi_bdSIJ99Y.it("should create client with full configuration", () => {
|
|
184
|
-
const fullClient = require_AmazonStorageClient.AmazonStorageClient.create({
|
|
185
|
-
bucket: "geekmidas",
|
|
186
|
-
region: "us-west-2",
|
|
187
|
-
accessKeyId: "test-key",
|
|
188
|
-
secretAccessKey: "test-secret",
|
|
189
|
-
endpoint: "https://custom-s3.example.com",
|
|
190
|
-
acl: require_AmazonStorageClient.AmazonCannedAccessControlList.Private
|
|
191
|
-
});
|
|
192
|
-
require_vi_bdSIJ99Y.globalExpect(fullClient).toBeInstanceOf(require_AmazonStorageClient.AmazonStorageClient);
|
|
193
|
-
require_vi_bdSIJ99Y.globalExpect(fullClient.provider).toBe(require_StorageClient.StorageProvider.AWSS3);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
require_vi_bdSIJ99Y.describe("different ACL configurations", () => {
|
|
197
|
-
require_vi_bdSIJ99Y.it("should work with different ACL settings", async () => {
|
|
198
|
-
const privateClient = require_AmazonStorageClient.AmazonStorageClient.create({
|
|
199
|
-
bucket: testBucket,
|
|
200
|
-
region: "us-east-1",
|
|
201
|
-
accessKeyId: "minioadmin",
|
|
202
|
-
secretAccessKey: "minioadmin",
|
|
203
|
-
endpoint: "http://localhost:9000",
|
|
204
|
-
acl: require_AmazonStorageClient.AmazonCannedAccessControlList.Private
|
|
205
|
-
});
|
|
206
|
-
const uploadData = await privateClient.getUpload({
|
|
207
|
-
path: "test-files/private.txt",
|
|
208
|
-
contentType: testContentType,
|
|
209
|
-
contentLength: testContent.length
|
|
210
|
-
});
|
|
211
|
-
require_vi_bdSIJ99Y.globalExpect(uploadData.fields).toContainEqual({
|
|
212
|
-
key: "acl",
|
|
213
|
-
value: "private"
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
require_vi_bdSIJ99Y.describe("S3Client integration", () => {
|
|
218
|
-
require_vi_bdSIJ99Y.it("should work with externally created S3Client", () => {
|
|
219
|
-
const s3Client = new __aws_sdk_client_s3.S3Client({
|
|
220
|
-
region: "us-east-1",
|
|
221
|
-
credentials: {
|
|
222
|
-
accessKeyId: "minioadmin",
|
|
223
|
-
secretAccessKey: "minioadmin"
|
|
224
|
-
},
|
|
225
|
-
endpoint: "http://localhost:9000"
|
|
226
|
-
});
|
|
227
|
-
const storageClient = new require_AmazonStorageClient.AmazonStorageClient(s3Client, testBucket, require_AmazonStorageClient.AmazonCannedAccessControlList.PublicRead);
|
|
228
|
-
require_vi_bdSIJ99Y.globalExpect(storageClient).toBeInstanceOf(require_AmazonStorageClient.AmazonStorageClient);
|
|
229
|
-
require_vi_bdSIJ99Y.globalExpect(storageClient.provider).toBe(require_StorageClient.StorageProvider.AWSS3);
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
//#endregion
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|