@aws-amplify/storage 6.11.2-unstable.a361dd1.0 → 6.12.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/dist/cjs/internals/apis/remove.js +13 -12
- package/dist/cjs/internals/apis/remove.js.map +1 -1
- package/dist/cjs/providers/s3/apis/internal/remove.js +53 -29
- package/dist/cjs/providers/s3/apis/internal/remove.js.map +1 -1
- package/dist/cjs/providers/s3/apis/remove.js +6 -1
- package/dist/cjs/providers/s3/apis/remove.js.map +1 -1
- package/dist/cjs/providers/s3/apis/server/remove.js +6 -1
- package/dist/cjs/providers/s3/apis/server/remove.js.map +1 -1
- package/dist/cjs/providers/s3/utils/client/s3data/deleteObjects.js +70 -0
- package/dist/cjs/providers/s3/utils/client/s3data/deleteObjects.js.map +1 -0
- package/dist/cjs/providers/s3/utils/client/s3data/index.js +3 -1
- package/dist/cjs/providers/s3/utils/client/s3data/index.js.map +1 -1
- package/dist/cjs/providers/s3/utils/createAbortableTask.js +35 -0
- package/dist/cjs/providers/s3/utils/createAbortableTask.js.map +1 -0
- package/dist/cjs/providers/s3/utils/deleteFolderContents.js +80 -0
- package/dist/cjs/providers/s3/utils/deleteFolderContents.js.map +1 -0
- package/dist/cjs/providers/s3/utils/generateDeleteObjectsXml.js +38 -0
- package/dist/cjs/providers/s3/utils/generateDeleteObjectsXml.js.map +1 -0
- package/dist/cjs/providers/s3/utils/index.js +13 -1
- package/dist/cjs/providers/s3/utils/index.js.map +1 -1
- package/dist/cjs/providers/s3/utils/isPathFolder.js +38 -0
- package/dist/cjs/providers/s3/utils/isPathFolder.js.map +1 -0
- package/dist/cjs/providers/s3/utils/resolveFinalKey.js +22 -0
- package/dist/cjs/providers/s3/utils/resolveFinalKey.js.map +1 -0
- package/dist/cjs/providers/s3/utils/validateRemovePath.js +21 -0
- package/dist/cjs/providers/s3/utils/validateRemovePath.js.map +1 -0
- package/dist/esm/internals/apis/remove.d.ts +2 -1
- package/dist/esm/internals/apis/remove.mjs +13 -12
- package/dist/esm/internals/apis/remove.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/copy.mjs +8 -7
- package/dist/esm/providers/s3/apis/internal/copy.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/downloadData.d.ts +1 -1
- package/dist/esm/providers/s3/apis/internal/downloadData.mjs +6 -5
- package/dist/esm/providers/s3/apis/internal/downloadData.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/getProperties.mjs +7 -6
- package/dist/esm/providers/s3/apis/internal/getProperties.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/getUrl.mjs +2 -1
- package/dist/esm/providers/s3/apis/internal/getUrl.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/list.mjs +8 -7
- package/dist/esm/providers/s3/apis/internal/list.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/remove.d.ts +3 -2
- package/dist/esm/providers/s3/apis/internal/remove.mjs +58 -27
- package/dist/esm/providers/s3/apis/internal/remove.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/index.mjs +13 -0
- package/dist/esm/providers/s3/apis/internal/uploadData/index.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/initialUpload.mjs +1 -0
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/initialUpload.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadCache.mjs +1 -0
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadCache.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadHandlers.mjs +6 -5
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadHandlers.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadPartExecutor.mjs +1 -0
- package/dist/esm/providers/s3/apis/internal/uploadData/multipart/uploadPartExecutor.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/internal/uploadData/putObjectJob.mjs +7 -6
- package/dist/esm/providers/s3/apis/internal/uploadData/putObjectJob.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/remove.d.ts +6 -6
- package/dist/esm/providers/s3/apis/remove.mjs +6 -1
- package/dist/esm/providers/s3/apis/remove.mjs.map +1 -1
- package/dist/esm/providers/s3/apis/server/remove.d.ts +6 -6
- package/dist/esm/providers/s3/apis/server/remove.mjs +6 -1
- package/dist/esm/providers/s3/apis/server/remove.mjs.map +1 -1
- package/dist/esm/providers/s3/types/index.d.ts +1 -1
- package/dist/esm/providers/s3/types/inputs.d.ts +24 -1
- package/dist/esm/providers/s3/utils/client/s3data/deleteObjects.d.ts +30 -0
- package/dist/esm/providers/s3/utils/client/s3data/deleteObjects.mjs +74 -0
- package/dist/esm/providers/s3/utils/client/s3data/deleteObjects.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/client/s3data/index.d.ts +1 -0
- package/dist/esm/providers/s3/utils/client/s3data/index.mjs +1 -0
- package/dist/esm/providers/s3/utils/client/s3data/index.mjs.map +1 -1
- package/dist/esm/providers/s3/utils/client/s3data/types.d.ts +26 -0
- package/dist/esm/providers/s3/utils/createAbortableTask.d.ts +6 -0
- package/dist/esm/providers/s3/utils/createAbortableTask.mjs +33 -0
- package/dist/esm/providers/s3/utils/createAbortableTask.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/deleteFolderContents.d.ts +16 -0
- package/dist/esm/providers/s3/utils/deleteFolderContents.mjs +90 -0
- package/dist/esm/providers/s3/utils/deleteFolderContents.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/generateDeleteObjectsXml.d.ts +10 -0
- package/dist/esm/providers/s3/utils/generateDeleteObjectsXml.mjs +35 -0
- package/dist/esm/providers/s3/utils/generateDeleteObjectsXml.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/index.d.ts +6 -0
- package/dist/esm/providers/s3/utils/index.mjs +6 -0
- package/dist/esm/providers/s3/utils/index.mjs.map +1 -1
- package/dist/esm/providers/s3/utils/isPathFolder.d.ts +13 -0
- package/dist/esm/providers/s3/utils/isPathFolder.mjs +48 -0
- package/dist/esm/providers/s3/utils/isPathFolder.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/resolveFinalKey.d.ts +9 -0
- package/dist/esm/providers/s3/utils/resolveFinalKey.mjs +20 -0
- package/dist/esm/providers/s3/utils/resolveFinalKey.mjs.map +1 -0
- package/dist/esm/providers/s3/utils/validateRemovePath.d.ts +8 -0
- package/dist/esm/providers/s3/utils/validateRemovePath.mjs +18 -0
- package/dist/esm/providers/s3/utils/validateRemovePath.mjs.map +1 -0
- package/dist/esm/types/common.d.ts +3 -0
- package/package.json +117 -117
- package/src/internals/apis/remove.ts +17 -10
- package/src/providers/s3/apis/internal/remove.ts +96 -41
- package/src/providers/s3/apis/remove.ts +11 -6
- package/src/providers/s3/apis/server/remove.ts +11 -6
- package/src/providers/s3/types/index.ts +2 -0
- package/src/providers/s3/types/inputs.ts +24 -1
- package/src/providers/s3/utils/client/s3data/deleteObjects.ts +122 -0
- package/src/providers/s3/utils/client/s3data/index.ts +5 -0
- package/src/providers/s3/utils/client/s3data/types.ts +20 -0
- package/src/providers/s3/utils/createAbortableTask.ts +45 -0
- package/src/providers/s3/utils/deleteFolderContents.ts +118 -0
- package/src/providers/s3/utils/generateDeleteObjectsXml.ts +38 -0
- package/src/providers/s3/utils/index.ts +6 -0
- package/src/providers/s3/utils/isPathFolder.ts +50 -0
- package/src/providers/s3/utils/resolveFinalKey.ts +22 -0
- package/src/providers/s3/utils/validateRemovePath.ts +16 -0
- package/src/types/common.ts +5 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Endpoint,
|
|
6
|
+
HttpRequest,
|
|
7
|
+
HttpResponse,
|
|
8
|
+
parseMetadata,
|
|
9
|
+
} from '@aws-amplify/core/internals/aws-client-utils';
|
|
10
|
+
import {
|
|
11
|
+
AmplifyUrl,
|
|
12
|
+
AmplifyUrlSearchParams,
|
|
13
|
+
} from '@aws-amplify/core/internals/utils';
|
|
14
|
+
import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
assignStringVariables,
|
|
18
|
+
buildStorageServiceError,
|
|
19
|
+
s3TransferHandler,
|
|
20
|
+
validateS3RequiredParameter,
|
|
21
|
+
} from '../utils';
|
|
22
|
+
import { generateDeleteObjectsXml } from '../../generateDeleteObjectsXml';
|
|
23
|
+
import { calculateContentMd5 } from '../../md5';
|
|
24
|
+
|
|
25
|
+
import type {
|
|
26
|
+
DeleteObjectsCommandInput,
|
|
27
|
+
DeleteObjectsCommandOutput,
|
|
28
|
+
} from './types';
|
|
29
|
+
import { defaultConfig, parseXmlError } from './base';
|
|
30
|
+
|
|
31
|
+
export type DeleteObjectsInput = Pick<
|
|
32
|
+
DeleteObjectsCommandInput,
|
|
33
|
+
'Bucket' | 'Delete' | 'ExpectedBucketOwner'
|
|
34
|
+
> & {
|
|
35
|
+
ContentMD5?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type DeleteObjectsOutput = DeleteObjectsCommandOutput;
|
|
39
|
+
|
|
40
|
+
const deleteObjectsSerializer = async (
|
|
41
|
+
input: DeleteObjectsInput,
|
|
42
|
+
endpoint: Endpoint,
|
|
43
|
+
): Promise<HttpRequest> => {
|
|
44
|
+
const url = new AmplifyUrl(endpoint.url.toString());
|
|
45
|
+
validateS3RequiredParameter(!!input.Delete, 'Delete');
|
|
46
|
+
url.pathname = '/';
|
|
47
|
+
url.search = new AmplifyUrlSearchParams({
|
|
48
|
+
delete: '',
|
|
49
|
+
}).toString();
|
|
50
|
+
|
|
51
|
+
const body = generateDeleteObjectsXml(
|
|
52
|
+
input.Delete!.Objects,
|
|
53
|
+
input.Delete!.Quiet ?? false,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const contentMd5 = input.ContentMD5 ?? (await calculateContentMd5(body));
|
|
57
|
+
|
|
58
|
+
const headers = assignStringVariables({
|
|
59
|
+
'x-amz-expected-bucket-owner': input.ExpectedBucketOwner,
|
|
60
|
+
'content-type': 'application/xml',
|
|
61
|
+
'content-md5': contentMd5,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const request = {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers,
|
|
67
|
+
url,
|
|
68
|
+
body,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return request;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const deleteObjectsDeserializer = async (
|
|
75
|
+
response: HttpResponse,
|
|
76
|
+
): Promise<DeleteObjectsOutput> => {
|
|
77
|
+
if (response.statusCode >= 300) {
|
|
78
|
+
const error = await parseXmlError(response);
|
|
79
|
+
throw buildStorageServiceError(error!);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const text = await response.body.text();
|
|
83
|
+
|
|
84
|
+
const deleted: { Key?: string }[] = [];
|
|
85
|
+
const errors: { Key?: string; Code?: string; Message?: string }[] = [];
|
|
86
|
+
|
|
87
|
+
const deletedMatches = text.matchAll(/<Deleted>[\s\S]*?<\/Deleted>/g);
|
|
88
|
+
for (const match of deletedMatches) {
|
|
89
|
+
const keyMatch = match[0].match(/<Key>(.*?)<\/Key>/);
|
|
90
|
+
if (keyMatch) {
|
|
91
|
+
deleted.push({ Key: keyMatch[1] });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const errorMatches = text.matchAll(/<Error>[\s\S]*?<\/Error>/g);
|
|
96
|
+
for (const match of errorMatches) {
|
|
97
|
+
const keyMatch = match[0].match(/<Key>(.*?)<\/Key>/);
|
|
98
|
+
const codeMatch = match[0].match(/<Code>(.*?)<\/Code>/);
|
|
99
|
+
const messageMatch = match[0].match(/<Message>(.*?)<\/Message>/);
|
|
100
|
+
|
|
101
|
+
errors.push({
|
|
102
|
+
Key: keyMatch?.[1],
|
|
103
|
+
Code: codeMatch?.[1],
|
|
104
|
+
Message: messageMatch?.[1],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = {
|
|
109
|
+
Deleted: deleted,
|
|
110
|
+
Errors: errors,
|
|
111
|
+
$metadata: parseMetadata(response),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const deleteObjects = composeServiceApi(
|
|
118
|
+
s3TransferHandler,
|
|
119
|
+
deleteObjectsSerializer,
|
|
120
|
+
deleteObjectsDeserializer,
|
|
121
|
+
{ ...defaultConfig, responseType: 'text' },
|
|
122
|
+
);
|
|
@@ -5452,4 +5452,24 @@ export type StorageClass = (typeof StorageClass)[keyof typeof StorageClass];
|
|
|
5452
5452
|
export type TaggingDirective =
|
|
5453
5453
|
(typeof TaggingDirective)[keyof typeof TaggingDirective];
|
|
5454
5454
|
|
|
5455
|
+
/**
|
|
5456
|
+
* @public
|
|
5457
|
+
*/
|
|
5458
|
+
export interface DeleteObjectsCommandInput {
|
|
5459
|
+
Bucket: string;
|
|
5460
|
+
Delete: {
|
|
5461
|
+
Objects: Array<{ Key: string }>;
|
|
5462
|
+
Quiet?: boolean;
|
|
5463
|
+
};
|
|
5464
|
+
ExpectedBucketOwner?: string;
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
/**
|
|
5468
|
+
* @public
|
|
5469
|
+
*/
|
|
5470
|
+
export interface DeleteObjectsCommandOutput extends __MetadataBearer {
|
|
5471
|
+
Deleted?: Array<{ Key?: string }>;
|
|
5472
|
+
Errors?: Array<{ Key?: string; Code?: string; Message?: string }>;
|
|
5473
|
+
}
|
|
5474
|
+
|
|
5455
5475
|
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { NonPausableTransferTask } from '../../../types/common';
|
|
5
|
+
|
|
6
|
+
export function createAbortableTask<T>(
|
|
7
|
+
executor: (abortController: AbortController) => Promise<T>,
|
|
8
|
+
): NonPausableTransferTask<T> & {
|
|
9
|
+
then: Promise<T>['then'];
|
|
10
|
+
catch: Promise<T>['catch'];
|
|
11
|
+
finally: Promise<T>['finally'];
|
|
12
|
+
} {
|
|
13
|
+
const abortController = new AbortController();
|
|
14
|
+
let state: NonPausableTransferTask<T>['state'] = 'IN_PROGRESS';
|
|
15
|
+
|
|
16
|
+
const resultPromise = executor(abortController);
|
|
17
|
+
|
|
18
|
+
const wrappedPromise = resultPromise
|
|
19
|
+
.then(result => {
|
|
20
|
+
state = 'SUCCESS';
|
|
21
|
+
|
|
22
|
+
return result;
|
|
23
|
+
})
|
|
24
|
+
.catch(error => {
|
|
25
|
+
state = abortController.signal.aborted ? 'CANCELED' : 'ERROR';
|
|
26
|
+
|
|
27
|
+
throw error;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const operation = {
|
|
31
|
+
result: wrappedPromise,
|
|
32
|
+
cancel: () => {
|
|
33
|
+
abortController.abort();
|
|
34
|
+
state = 'CANCELED';
|
|
35
|
+
},
|
|
36
|
+
get state() {
|
|
37
|
+
return state;
|
|
38
|
+
},
|
|
39
|
+
then: wrappedPromise.then.bind(wrappedPromise),
|
|
40
|
+
catch: wrappedPromise.catch.bind(wrappedPromise),
|
|
41
|
+
finally: wrappedPromise.finally.bind(wrappedPromise),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return operation;
|
|
45
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { StorageAction } from '@aws-amplify/core/internals/utils';
|
|
5
|
+
|
|
6
|
+
import { CanceledError } from '../../../errors/CanceledError';
|
|
7
|
+
import { ProgressInfo, RemoveWithPathOutput } from '../types';
|
|
8
|
+
|
|
9
|
+
import { deleteObjects, listObjectsV2 } from './client/s3data';
|
|
10
|
+
import { getStorageUserAgentValue } from './userAgent';
|
|
11
|
+
|
|
12
|
+
const MAX_KEYS_PER_BATCH = 1000;
|
|
13
|
+
|
|
14
|
+
export interface DeleteFolderContentsParams {
|
|
15
|
+
s3Config: any;
|
|
16
|
+
bucket: string;
|
|
17
|
+
folderKey: string;
|
|
18
|
+
expectedBucketOwner?: string;
|
|
19
|
+
onProgress?(progress: ProgressInfo): void;
|
|
20
|
+
abortSignal?: AbortSignal;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Deletes all contents of a folder in S3 using batch operations
|
|
25
|
+
*
|
|
26
|
+
* @param params - Configuration object for the delete operation
|
|
27
|
+
* @returns Promise that resolves to the removal result
|
|
28
|
+
*/
|
|
29
|
+
export const deleteFolderContents = async (
|
|
30
|
+
params: DeleteFolderContentsParams,
|
|
31
|
+
): Promise<RemoveWithPathOutput> => {
|
|
32
|
+
const {
|
|
33
|
+
s3Config,
|
|
34
|
+
bucket,
|
|
35
|
+
folderKey,
|
|
36
|
+
expectedBucketOwner,
|
|
37
|
+
onProgress,
|
|
38
|
+
abortSignal,
|
|
39
|
+
} = params;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const prefix = folderKey.endsWith('/') ? folderKey : `${folderKey}/`;
|
|
43
|
+
const progressCallback =
|
|
44
|
+
onProgress ??
|
|
45
|
+
(() => {
|
|
46
|
+
// no-op
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let continuationToken: string | undefined;
|
|
50
|
+
|
|
51
|
+
do {
|
|
52
|
+
if (abortSignal?.aborted) {
|
|
53
|
+
throw new CanceledError({ message: 'Operation was canceled' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const listResult = await listObjectsV2(
|
|
57
|
+
{
|
|
58
|
+
...s3Config,
|
|
59
|
+
userAgentValue: getStorageUserAgentValue(StorageAction.Remove),
|
|
60
|
+
abortSignal,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
Bucket: bucket,
|
|
64
|
+
Prefix: prefix,
|
|
65
|
+
MaxKeys: MAX_KEYS_PER_BATCH,
|
|
66
|
+
ContinuationToken: continuationToken,
|
|
67
|
+
ExpectedBucketOwner: expectedBucketOwner,
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!listResult.Contents || listResult.Contents.length === 0) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (abortSignal?.aborted) {
|
|
76
|
+
throw new CanceledError({ message: 'Operation was canceled' });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const batch = listResult.Contents.map(obj => ({ Key: obj.Key! }));
|
|
80
|
+
|
|
81
|
+
const deleteResult = await deleteObjects(
|
|
82
|
+
{
|
|
83
|
+
...s3Config,
|
|
84
|
+
userAgentValue: getStorageUserAgentValue(StorageAction.Remove),
|
|
85
|
+
abortSignal,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
Bucket: bucket,
|
|
89
|
+
Delete: {
|
|
90
|
+
Objects: batch,
|
|
91
|
+
Quiet: false,
|
|
92
|
+
},
|
|
93
|
+
ExpectedBucketOwner: expectedBucketOwner,
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const deleted =
|
|
98
|
+
deleteResult.Deleted?.map(obj => ({ path: obj.Key! })) || [];
|
|
99
|
+
const failed =
|
|
100
|
+
deleteResult.Errors?.map(err => ({
|
|
101
|
+
path: err.Key!,
|
|
102
|
+
code: err.Code!,
|
|
103
|
+
message: err.Message!,
|
|
104
|
+
})) || [];
|
|
105
|
+
|
|
106
|
+
progressCallback({ deleted, failed });
|
|
107
|
+
|
|
108
|
+
continuationToken = listResult.NextContinuationToken;
|
|
109
|
+
} while (continuationToken);
|
|
110
|
+
|
|
111
|
+
return { path: folderKey };
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (abortSignal?.aborted) {
|
|
114
|
+
throw new CanceledError({ message: 'Operation was canceled' });
|
|
115
|
+
}
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Escapes special XML characters in a string
|
|
6
|
+
* @param str - String to escape
|
|
7
|
+
* @returns XML-escaped string
|
|
8
|
+
*/
|
|
9
|
+
const escapeXml = (str: string): string => {
|
|
10
|
+
return str
|
|
11
|
+
.replace(/&/g, '&')
|
|
12
|
+
.replace(/</g, '<')
|
|
13
|
+
.replace(/>/g, '>')
|
|
14
|
+
.replace(/"/g, '"')
|
|
15
|
+
.replace(/'/g, ''');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generates XML for S3 batch delete operations
|
|
20
|
+
*
|
|
21
|
+
* @param objects - Array of objects to delete with their keys
|
|
22
|
+
* @param quiet - Whether to use quiet mode (default: true)
|
|
23
|
+
* @returns XML string for the delete request
|
|
24
|
+
*/
|
|
25
|
+
export const generateDeleteObjectsXml = (
|
|
26
|
+
objects: { Key: string }[],
|
|
27
|
+
quiet: boolean,
|
|
28
|
+
): string => {
|
|
29
|
+
const objectsXml = objects
|
|
30
|
+
.map(obj => `<Object><Key>${escapeXml(obj.Key)}</Key></Object>`)
|
|
31
|
+
.join('');
|
|
32
|
+
|
|
33
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
34
|
+
<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
|
35
|
+
<Quiet>${quiet ? 'true' : 'false'}</Quiet>
|
|
36
|
+
${objectsXml}
|
|
37
|
+
</Delete>`;
|
|
38
|
+
};
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
export { calculateContentMd5 } from './md5';
|
|
5
|
+
export { deleteFolderContents } from './deleteFolderContents';
|
|
6
|
+
export { generateDeleteObjectsXml } from './generateDeleteObjectsXml';
|
|
5
7
|
export { resolveS3ConfigAndInput } from './resolveS3ConfigAndInput';
|
|
8
|
+
export { resolveFinalKey } from './resolveFinalKey';
|
|
6
9
|
export { createDownloadTask, createUploadTask } from './transferTask';
|
|
7
10
|
export { validateBucketOwnerID } from './validateBucketOwnerID';
|
|
11
|
+
export { validateRemovePath } from './validateRemovePath';
|
|
8
12
|
export { validateStorageOperationInput } from './validateStorageOperationInput';
|
|
9
13
|
export { validateStorageOperationInputWithPrefix } from './validateStorageOperationInputWithPrefix';
|
|
10
14
|
export { isInputWithPath } from './isInputWithPath';
|
|
15
|
+
export { isPathFolder } from './isPathFolder';
|
|
11
16
|
export { urlDecode } from './urlDecoder';
|
|
17
|
+
export { createAbortableTask } from './createAbortableTask';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { StorageAction } from '@aws-amplify/core/internals/utils';
|
|
5
|
+
|
|
6
|
+
import { listObjectsV2 } from './client/s3data';
|
|
7
|
+
import { getStorageUserAgentValue } from './userAgent';
|
|
8
|
+
|
|
9
|
+
export interface IsPathFolderParams {
|
|
10
|
+
s3Config: any;
|
|
11
|
+
bucket: string;
|
|
12
|
+
key: string;
|
|
13
|
+
expectedBucketOwner?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Determines if a given S3 key represents a folder by checking if objects exist with that prefix.
|
|
18
|
+
*
|
|
19
|
+
* @param params - Configuration object for the folder check
|
|
20
|
+
* @returns Promise that resolves to true if the key represents a folder, false otherwise
|
|
21
|
+
*/
|
|
22
|
+
export const isPathFolder = async (
|
|
23
|
+
params: IsPathFolderParams,
|
|
24
|
+
): Promise<boolean> => {
|
|
25
|
+
const { s3Config, bucket, key, expectedBucketOwner } = params;
|
|
26
|
+
try {
|
|
27
|
+
const prefix = key.endsWith('/') ? key : `${key}/`;
|
|
28
|
+
|
|
29
|
+
const result = await listObjectsV2(
|
|
30
|
+
{
|
|
31
|
+
...s3Config,
|
|
32
|
+
userAgentValue: getStorageUserAgentValue(StorageAction.Remove),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
Bucket: bucket,
|
|
36
|
+
Prefix: prefix,
|
|
37
|
+
MaxKeys: 1,
|
|
38
|
+
ExpectedBucketOwner: expectedBucketOwner,
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const isFolder =
|
|
43
|
+
!!(result.Contents && result.Contents.length > 0) ||
|
|
44
|
+
!!(result.CommonPrefixes && result.CommonPrefixes.length > 0);
|
|
45
|
+
|
|
46
|
+
return isFolder;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import { STORAGE_INPUT_KEY } from './constants';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the final S3 key based on input type and key prefix.
|
|
8
|
+
*
|
|
9
|
+
* @param inputType - The type of input (key-based or path-based)
|
|
10
|
+
* @param objectKey - The object key from the input
|
|
11
|
+
* @param keyPrefix - The key prefix to prepend for key-based inputs
|
|
12
|
+
* @returns The final S3 key to use for the operation
|
|
13
|
+
*/
|
|
14
|
+
export const resolveFinalKey = (
|
|
15
|
+
inputType: string,
|
|
16
|
+
objectKey: string,
|
|
17
|
+
keyPrefix: string,
|
|
18
|
+
): string => {
|
|
19
|
+
return inputType === STORAGE_INPUT_KEY
|
|
20
|
+
? `${keyPrefix}${objectKey}`
|
|
21
|
+
: objectKey;
|
|
22
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validates that the path is safe for removal operations.
|
|
6
|
+
* Prevents deletion of dangerous paths that could affect entire buckets.
|
|
7
|
+
*
|
|
8
|
+
* @param path - The path to validate
|
|
9
|
+
* @throws Error if the path is considered dangerous
|
|
10
|
+
*/
|
|
11
|
+
export const validateRemovePath = (path: string): void => {
|
|
12
|
+
const DANGEROUS_PATHS = ['', '/', '*'];
|
|
13
|
+
if (DANGEROUS_PATHS.includes(path.trim())) {
|
|
14
|
+
throw new Error('Cannot delete root or bucket-wide paths');
|
|
15
|
+
}
|
|
16
|
+
};
|
package/src/types/common.ts
CHANGED
|
@@ -49,6 +49,11 @@ export interface TransferTask<Result> {
|
|
|
49
49
|
result: Promise<Result>;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export interface NonPausableTransferTask<T>
|
|
53
|
+
extends Omit<TransferTask<T>, 'pause' | 'resume' | 'state'> {
|
|
54
|
+
state: Omit<TransferTask<T>['state'], 'PAUSED'>;
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
export type DownloadTask<Result> = Omit<
|
|
53
58
|
TransferTask<Result>,
|
|
54
59
|
'pause' | 'resume'
|