From 952f64bd874b6a481a754ebfb15eaa42a272d027 Mon Sep 17 00:00:00 2001 From: Hui Zhao <10602282+HuiSF@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:01:25 -0700 Subject: [PATCH 1/4] fix(storage): multipart upload is firing on 0 bytes data (#13927) --- .../providers/s3/apis/uploadData/index.test.ts | 16 ++++++++++++++++ .../src/providers/s3/apis/uploadData/index.ts | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/storage/__tests__/providers/s3/apis/uploadData/index.test.ts b/packages/storage/__tests__/providers/s3/apis/uploadData/index.test.ts index 938ca8863ee..ad1ce8d4009 100644 --- a/packages/storage/__tests__/providers/s3/apis/uploadData/index.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/uploadData/index.test.ts @@ -180,6 +180,22 @@ describe('uploadData with path', () => { }, ); + it('should use putObject for 0 bytes data (e.g. create a folder)', () => { + const testInput = { + path: 'test-path', + data: '', // 0 bytes + }; + + uploadData(testInput); + + expect(mockPutObjectJob).toHaveBeenCalledWith( + testInput, + expect.any(AbortSignal), + expect.any(Number), + ); + expect(mockGetMultipartUploadHandlers).not.toHaveBeenCalled(); + }); + it('should use uploadTask', async () => { mockPutObjectJob.mockReturnValueOnce('putObjectJob'); mockCreateUploadTask.mockReturnValueOnce('uploadTask'); diff --git a/packages/storage/src/providers/s3/apis/uploadData/index.ts b/packages/storage/src/providers/s3/apis/uploadData/index.ts index 8669309ec53..39ccdac89a9 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/index.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/index.ts @@ -135,7 +135,7 @@ export function uploadData(input: UploadDataInput | UploadDataWithPathInput) { StorageValidationErrorCode.ObjectIsTooLarge, ); - if (dataByteLength && dataByteLength <= DEFAULT_PART_SIZE) { + if (dataByteLength !== undefined && dataByteLength <= DEFAULT_PART_SIZE) { // Single part upload const abortController = new AbortController(); From 60de5246910486cdfaf5dc23b19668cdfd5892e6 Mon Sep 17 00:00:00 2001 From: yuhengshs <94558971+yuhengshs@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:27:00 -0700 Subject: [PATCH 2/4] fix(core): fixed cache.clear() not working as expected (#13926) * fix: fixed cache.clear() not working as epected * fix: removed mockResolve function in unit test for cache clear * feat: update unit test to use real implementation instead of mockValues --- .../Cache/StorageCacheCommon.test.ts | 19 ++++++++++++------- packages/core/src/Cache/StorageCacheCommon.ts | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/core/__tests__/Cache/StorageCacheCommon.test.ts b/packages/core/__tests__/Cache/StorageCacheCommon.test.ts index a1488ef71e9..44e018a28a9 100644 --- a/packages/core/__tests__/Cache/StorageCacheCommon.test.ts +++ b/packages/core/__tests__/Cache/StorageCacheCommon.test.ts @@ -3,6 +3,7 @@ import { defaultConfig } from '../../src/Cache/constants'; import { StorageCacheCommon } from '../../src/Cache/StorageCacheCommon'; import { KeyValueStorageInterface } from '../../src/types'; import { ConsoleLogger } from '../../src/Logger'; +import { StorageCache } from '../../src/Cache/StorageCache'; import { getByteLength, getCurrentSizeKey, @@ -584,16 +585,20 @@ describe('StorageCacheCommon', () => { }); describe('clear()', () => { - const cache = getStorageCache(config); + const cache = new StorageCache(config); it('clears the cache, including the currentSizeKey', async () => { - mockGetAllCacheKeys.mockReturnValue([ - currentSizeKey, - `${keyPrefix}some-key`, - ]); + await cache.setItem('key1', 'value1'); + await cache.setItem('key2', 'value2'); + + expect(await cache.getItem('key1')).toBe('value1'); + expect(await cache.getItem('key2')).toBe('value2'); + await cache.clear(); - expect(loggerSpy.debug).toHaveBeenCalledWith('Clear Cache'); - expect(mockKeyValueStorageRemoveItem).toHaveBeenCalledTimes(2); + + expect(await cache.getItem('key1')).toBeNull(); + expect(await cache.getItem('key2')).toBeNull(); + expect(await cache.getCurrentCacheSize()).toBe(0); }); }); diff --git a/packages/core/src/Cache/StorageCacheCommon.ts b/packages/core/src/Cache/StorageCacheCommon.ts index 561c469330b..24ffa33e55c 100644 --- a/packages/core/src/Cache/StorageCacheCommon.ts +++ b/packages/core/src/Cache/StorageCacheCommon.ts @@ -588,7 +588,8 @@ export abstract class StorageCacheCommon { try { const keys = await this.getAllKeys(); for (const key of keys) { - await this.getStorage().removeItem(key); + const prefixedKey = `${this.config.keyPrefix}${key}`; + await this.getStorage().removeItem(prefixedKey); } } catch (e) { logger.warn(`clear failed! ${e}`); From 971bec4956762b3b4687f0f479c2ad17d853176a Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 17 Oct 2024 13:11:58 -0400 Subject: [PATCH 3/4] chore: remove "Preview" from issue template (#13807) remove "Preview" from template Co-authored-by: ashika112 <155593080+ashika112@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/1.bug_report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yaml b/.github/ISSUE_TEMPLATE/1.bug_report.yaml index 9d73aee3760..8d4572416d7 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yaml @@ -126,7 +126,7 @@ body: multiple: false options: - Amplify CLI - - Amplify Gen 2 (Preview) + - Amplify Gen 2 - CDK - Other - type: textarea From bf58ebc391dacf529aa1127e737c51b64d2fb96e Mon Sep 17 00:00:00 2001 From: Hui Zhao <10602282+HuiSF@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:40:52 -0700 Subject: [PATCH 4/4] fix(storage): nextToken is not surfaced when list result contain only CommonPrefix (#13933) --- .../__tests__/providers/s3/apis/list.test.ts | 41 +++++++++++++++++++ .../src/providers/s3/apis/internal/list.ts | 1 + 2 files changed, 42 insertions(+) diff --git a/packages/storage/__tests__/providers/s3/apis/list.test.ts b/packages/storage/__tests__/providers/s3/apis/list.test.ts index a13ae54b5a4..e01096a1113 100644 --- a/packages/storage/__tests__/providers/s3/apis/list.test.ts +++ b/packages/storage/__tests__/providers/s3/apis/list.test.ts @@ -506,6 +506,47 @@ describe('list API', () => { }, ); + it.each(pathTestCases)( + 'should list objects with CommonPrefix and nextToken in results with custom path: $path', + async ({ path }) => { + mockListObject.mockImplementationOnce(() => { + return { + CommonPrefixes: [ + { Prefix: 'photos/2023/' }, + { Prefix: 'photos/2024/' }, + { Prefix: 'photos/2025/' }, + { Prefix: 'photos/2026/' }, + { Prefix: 'photos/2027/' }, + { Prefix: 'photos/time-traveling/' }, + ], + NextContinuationToken: 'yup_there_is_more', + }; + }); + const response = await listPaginatedWrapper({ + path: resolvePath(path), + }); + expect(response.excludedSubpaths).toEqual([ + 'photos/2023/', + 'photos/2024/', + 'photos/2025/', + 'photos/2026/', + 'photos/2027/', + 'photos/time-traveling/', + ]); + + expect(response.nextToken).toEqual('yup_there_is_more'); + expect(listObjectsV2).toHaveBeenCalledTimes(1); + await expect(listObjectsV2).toBeLastCalledWithConfigAndInput( + listObjectClientConfig, + { + Bucket: bucket, + MaxKeys: 1000, + Prefix: resolvePath(path), + }, + ); + }, + ); + it.each(pathTestCases)( 'should list all objects having three pages with custom path: $path', async ({ path: inputPath }) => { diff --git a/packages/storage/src/providers/s3/apis/internal/list.ts b/packages/storage/src/providers/s3/apis/internal/list.ts index bbcb342a603..5b41b1f3a23 100644 --- a/packages/storage/src/providers/s3/apis/internal/list.ts +++ b/packages/storage/src/providers/s3/apis/internal/list.ts @@ -238,6 +238,7 @@ const _listWithPath = async ({ if (!contents) { return { items: [], + nextToken: nextContinuationToken, excludedSubpaths, }; }