@scality/data-browser-library 1.1.10 → 1.1.12
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/components/__tests__/BucketCorsPage.test.js +14 -6
- package/dist/components/__tests__/BucketLifecycleFormPage.test.js +129 -0
- package/dist/components/__tests__/BucketLifecycleList.test.js +78 -0
- package/dist/components/__tests__/BucketNotificationFormPage.test.js +15 -3
- package/dist/components/buckets/BucketCorsPage.js +5 -7
- package/dist/components/buckets/BucketLifecycleFormPage.js +18 -12
- package/dist/components/buckets/BucketLifecycleList.js +5 -5
- package/dist/components/buckets/BucketOverview.js +1 -1
- package/dist/components/buckets/BucketPolicyPage.js +4 -7
- package/dist/components/buckets/BucketReplicationFormPage.js +12 -6
- package/dist/components/buckets/BucketVersioning.js +1 -1
- package/dist/components/buckets/notifications/BucketNotificationFormPage.js +2 -2
- package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
- package/dist/components/objects/DeleteObjectButton.js +1 -1
- package/dist/components/objects/GetPresignedUrlButton.js +1 -1
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +1 -1
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +118 -7
- package/dist/components/objects/ObjectDetails/ObjectTags.js +1 -1
- package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +142 -3
- package/dist/components/objects/ObjectList.js +42 -16
- package/dist/components/objects/ObjectLock/ObjectLockSettings.js +1 -1
- package/dist/components/objects/__tests__/GetPresignedUrlButton.test.js +30 -1
- package/dist/components/providers/QueryProvider.js +2 -1
- package/dist/config/types.d.ts +4 -0
- package/dist/hooks/bucketConfiguration.js +15 -15
- package/dist/hooks/bucketOperations.js +1 -1
- package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +7 -35
- package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +1 -1
- package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +1 -1
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +1 -1
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +1 -1
- package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +2 -2
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +3 -3
- package/dist/hooks/factories/useCreateS3MutationHook.js +20 -8
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -1
- package/dist/hooks/factories/useCreateS3QueryHook.js +2 -2
- package/dist/hooks/objectOperations.js +6 -6
- package/dist/hooks/presignedOperations.js +1 -1
- package/dist/hooks/useDeleteFolder.js +1 -1
- package/dist/test/utils/errorHandling.test.js +45 -33
- package/dist/utils/__tests__/coldStorage.test.d.ts +1 -0
- package/dist/utils/__tests__/coldStorage.test.js +24 -0
- package/dist/utils/coldStorage.d.ts +12 -0
- package/dist/utils/coldStorage.js +23 -0
- package/dist/utils/errorHandling.d.ts +2 -1
- package/dist/utils/errorHandling.js +8 -7
- package/package.json +1 -1
|
@@ -306,16 +306,24 @@ describe('BucketCorsPage', ()=>{
|
|
|
306
306
|
replace: true
|
|
307
307
|
});
|
|
308
308
|
});
|
|
309
|
-
it('shows
|
|
310
|
-
const
|
|
311
|
-
accessDeniedError.name = 'AccessDenied';
|
|
309
|
+
it('shows permission message when fetching CORS fails with 403 error', ()=>{
|
|
310
|
+
const authError = new EnhancedS3Error("You don't have permission to load CORS configuration. Contact your administrator.", 'AccessDenied', ErrorCategory.AUTHORIZATION, new Error('Access Denied'), 403);
|
|
312
311
|
mockUseGetBucketCors.mockReturnValue(createMockQueryResult({
|
|
313
312
|
status: 'error',
|
|
314
|
-
error:
|
|
313
|
+
error: authError
|
|
315
314
|
}));
|
|
316
315
|
renderBucketCorsPage();
|
|
317
|
-
expect(screen.getByText(
|
|
318
|
-
expect(screen.
|
|
316
|
+
expect(screen.getByText("You don't have permission to load CORS configuration. Contact your administrator.")).toBeInTheDocument();
|
|
317
|
+
expect(screen.queryByTestId('cors-editor')).not.toBeInTheDocument();
|
|
318
|
+
});
|
|
319
|
+
it('shows original error message when fetching CORS fails with non-403 error', ()=>{
|
|
320
|
+
const serverError = new EnhancedS3Error('Internal Server Error', 'InternalError', ErrorCategory.SERVER_ERROR, new Error('Internal Server Error'), 500);
|
|
321
|
+
mockUseGetBucketCors.mockReturnValue(createMockQueryResult({
|
|
322
|
+
status: 'error',
|
|
323
|
+
error: serverError
|
|
324
|
+
}));
|
|
325
|
+
renderBucketCorsPage();
|
|
326
|
+
expect(screen.getByText('Internal Server Error')).toBeInTheDocument();
|
|
319
327
|
expect(screen.queryByTestId('cors-editor')).not.toBeInTheDocument();
|
|
320
328
|
});
|
|
321
329
|
});
|
|
@@ -441,6 +441,31 @@ describe('BucketLifecycleFormPage', ()=>{
|
|
|
441
441
|
});
|
|
442
442
|
});
|
|
443
443
|
describe('Rule Data Loading', ()=>{
|
|
444
|
+
it('loads rule with empty prefix filter as "All objects" (no filter)', async ()=>{
|
|
445
|
+
const rule = {
|
|
446
|
+
ID: 'no-filter-rule',
|
|
447
|
+
Status: 'Enabled',
|
|
448
|
+
Filter: {
|
|
449
|
+
Prefix: ''
|
|
450
|
+
},
|
|
451
|
+
Expiration: {
|
|
452
|
+
Days: 30
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
mockUseGetBucketLifecycle.mockReturnValue({
|
|
456
|
+
data: {
|
|
457
|
+
Rules: [
|
|
458
|
+
rule
|
|
459
|
+
]
|
|
460
|
+
},
|
|
461
|
+
status: 'success'
|
|
462
|
+
});
|
|
463
|
+
renderBucketLifecycleFormPage('test-bucket', 'no-filter-rule');
|
|
464
|
+
await waitFor(()=>{
|
|
465
|
+
expect(screen.getByText('All objects')).toBeInTheDocument();
|
|
466
|
+
});
|
|
467
|
+
expect(screen.queryByPlaceholderText('folder/')).not.toBeInTheDocument();
|
|
468
|
+
});
|
|
444
469
|
it('loads rule with prefix filter correctly', async ()=>{
|
|
445
470
|
const rule = {
|
|
446
471
|
ID: 'prefix-rule',
|
|
@@ -684,6 +709,110 @@ describe('BucketLifecycleFormPage', ()=>{
|
|
|
684
709
|
});
|
|
685
710
|
});
|
|
686
711
|
});
|
|
712
|
+
describe('editing immediate transition rules (0 days)', ()=>{
|
|
713
|
+
it('preserves 0 days when editing a current version transition', async ()=>{
|
|
714
|
+
mockUseGetBucketLifecycle.mockReturnValue({
|
|
715
|
+
data: {
|
|
716
|
+
Rules: [
|
|
717
|
+
{
|
|
718
|
+
ID: 'immediate-glacier',
|
|
719
|
+
Status: 'Enabled',
|
|
720
|
+
Filter: {},
|
|
721
|
+
Transitions: [
|
|
722
|
+
{
|
|
723
|
+
Days: 0,
|
|
724
|
+
StorageClass: 'GLACIER'
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
}
|
|
728
|
+
]
|
|
729
|
+
},
|
|
730
|
+
status: 'success'
|
|
731
|
+
});
|
|
732
|
+
renderBucketLifecycleFormPage('test-bucket', 'immediate-glacier');
|
|
733
|
+
await waitFor(()=>{
|
|
734
|
+
const transitionToggle = findToggleByLabel('Transition current version');
|
|
735
|
+
expect(transitionToggle).toBeChecked();
|
|
736
|
+
const daysInput = document.getElementById('transition-days-0');
|
|
737
|
+
expect(daysInput).toHaveValue(0);
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
it('preserves 0 days when editing a noncurrent version transition', async ()=>{
|
|
741
|
+
mockUseGetBucketLifecycle.mockReturnValue({
|
|
742
|
+
data: {
|
|
743
|
+
Rules: [
|
|
744
|
+
{
|
|
745
|
+
ID: 'immediate-noncurrent',
|
|
746
|
+
Status: 'Enabled',
|
|
747
|
+
Filter: {},
|
|
748
|
+
NoncurrentVersionTransitions: [
|
|
749
|
+
{
|
|
750
|
+
NoncurrentDays: 0,
|
|
751
|
+
StorageClass: 'GLACIER'
|
|
752
|
+
}
|
|
753
|
+
]
|
|
754
|
+
}
|
|
755
|
+
]
|
|
756
|
+
},
|
|
757
|
+
status: 'success'
|
|
758
|
+
});
|
|
759
|
+
renderBucketLifecycleFormPage('test-bucket', 'immediate-noncurrent');
|
|
760
|
+
await waitFor(()=>{
|
|
761
|
+
const noncurrentToggle = findToggleByLabel('Transition noncurrent version');
|
|
762
|
+
expect(noncurrentToggle).toBeChecked();
|
|
763
|
+
const daysInput = document.getElementById('noncurrent-transition-days-0');
|
|
764
|
+
expect(daysInput).toHaveValue(0);
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
it('preserves 0 days when editing a current version expiration', async ()=>{
|
|
768
|
+
mockUseGetBucketLifecycle.mockReturnValue({
|
|
769
|
+
data: {
|
|
770
|
+
Rules: [
|
|
771
|
+
{
|
|
772
|
+
ID: 'immediate-expire',
|
|
773
|
+
Status: 'Enabled',
|
|
774
|
+
Filter: {},
|
|
775
|
+
Expiration: {
|
|
776
|
+
Days: 0
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
},
|
|
781
|
+
status: 'success'
|
|
782
|
+
});
|
|
783
|
+
renderBucketLifecycleFormPage('test-bucket', 'immediate-expire');
|
|
784
|
+
await waitFor(()=>{
|
|
785
|
+
const expirationToggle = findToggleByLabel('Expiration current version');
|
|
786
|
+
expect(expirationToggle).toBeChecked();
|
|
787
|
+
const daysInput = screen.getByLabelText(/^days$/i);
|
|
788
|
+
expect(daysInput).toHaveValue(0);
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
it('preserves 0 days when editing a noncurrent version expiration', async ()=>{
|
|
792
|
+
mockUseGetBucketLifecycle.mockReturnValue({
|
|
793
|
+
data: {
|
|
794
|
+
Rules: [
|
|
795
|
+
{
|
|
796
|
+
ID: 'immediate-noncurrent-expire',
|
|
797
|
+
Status: 'Enabled',
|
|
798
|
+
Filter: {},
|
|
799
|
+
NoncurrentVersionExpiration: {
|
|
800
|
+
NoncurrentDays: 0
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
]
|
|
804
|
+
},
|
|
805
|
+
status: 'success'
|
|
806
|
+
});
|
|
807
|
+
renderBucketLifecycleFormPage('test-bucket', 'immediate-noncurrent-expire');
|
|
808
|
+
await waitFor(()=>{
|
|
809
|
+
const noncurrentExpirationToggle = findToggleByLabel('Expiration noncurrent version');
|
|
810
|
+
expect(noncurrentExpirationToggle).toBeChecked();
|
|
811
|
+
const noncurrentDaysInput = screen.getByLabelText(/noncurrent days/i);
|
|
812
|
+
expect(noncurrentDaysInput).toHaveValue(0);
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
});
|
|
687
816
|
describe('Transition Auto-Add in Create Mode', ()=>{
|
|
688
817
|
it('auto-adds a transition row when enabling transitions', async ()=>{
|
|
689
818
|
renderBucketLifecycleFormPage();
|
|
@@ -314,6 +314,84 @@ describe('BucketLifecycleList', ()=>{
|
|
|
314
314
|
});
|
|
315
315
|
expect(screen.getByText('Delete expired markers')).toBeInTheDocument();
|
|
316
316
|
});
|
|
317
|
+
describe('immediate transition rules (0 days)', ()=>{
|
|
318
|
+
it("shows current version transition description for immediate transition", ()=>{
|
|
319
|
+
renderBucketLifecycleList({
|
|
320
|
+
lifecycleRules: [
|
|
321
|
+
{
|
|
322
|
+
ID: 'immediate-glacier',
|
|
323
|
+
Status: 'Enabled',
|
|
324
|
+
Transitions: [
|
|
325
|
+
{
|
|
326
|
+
Days: 0,
|
|
327
|
+
StorageClass: 'GLACIER'
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
});
|
|
333
|
+
expect(screen.getByText(/Transition current \(0 days\)/)).toBeInTheDocument();
|
|
334
|
+
});
|
|
335
|
+
it("shows noncurrent version transition description for immediate transition", ()=>{
|
|
336
|
+
renderBucketLifecycleList({
|
|
337
|
+
lifecycleRules: [
|
|
338
|
+
{
|
|
339
|
+
ID: 'immediate-noncurrent',
|
|
340
|
+
Status: 'Enabled',
|
|
341
|
+
NoncurrentVersionTransitions: [
|
|
342
|
+
{
|
|
343
|
+
NoncurrentDays: 0,
|
|
344
|
+
StorageClass: 'GLACIER'
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
});
|
|
350
|
+
expect(screen.getByText(/Transition noncurrent \(0 days\)/)).toBeInTheDocument();
|
|
351
|
+
});
|
|
352
|
+
it("shows expiration description for immediate expiration", ()=>{
|
|
353
|
+
renderBucketLifecycleList({
|
|
354
|
+
lifecycleRules: [
|
|
355
|
+
{
|
|
356
|
+
ID: 'immediate-expire',
|
|
357
|
+
Status: 'Enabled',
|
|
358
|
+
Expiration: {
|
|
359
|
+
Days: 0
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
});
|
|
364
|
+
expect(screen.getByText(/Expire current \(0 days\)/)).toBeInTheDocument();
|
|
365
|
+
});
|
|
366
|
+
it("shows noncurrent expiration description for immediate expiration", ()=>{
|
|
367
|
+
renderBucketLifecycleList({
|
|
368
|
+
lifecycleRules: [
|
|
369
|
+
{
|
|
370
|
+
ID: 'immediate-noncurrent-expire',
|
|
371
|
+
Status: 'Enabled',
|
|
372
|
+
NoncurrentVersionExpiration: {
|
|
373
|
+
NoncurrentDays: 0
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
});
|
|
378
|
+
expect(screen.getByText(/Expire noncurrent \(0 days\)/)).toBeInTheDocument();
|
|
379
|
+
});
|
|
380
|
+
it("shows abort MPU description for immediate abort", ()=>{
|
|
381
|
+
renderBucketLifecycleList({
|
|
382
|
+
lifecycleRules: [
|
|
383
|
+
{
|
|
384
|
+
ID: 'immediate-mpu',
|
|
385
|
+
Status: 'Enabled',
|
|
386
|
+
AbortIncompleteMultipartUpload: {
|
|
387
|
+
DaysAfterInitiation: 0
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
]
|
|
391
|
+
});
|
|
392
|
+
expect(screen.getByText(/Abort Incomplete MPU \(0 days\)/)).toBeInTheDocument();
|
|
393
|
+
});
|
|
394
|
+
});
|
|
317
395
|
it('shows info icon for multiple actions', ()=>{
|
|
318
396
|
renderBucketLifecycleList({
|
|
319
397
|
lifecycleRules: mockLifecycleRules
|
|
@@ -5,6 +5,7 @@ import { MemoryRouter, Route, Routes } from "react-router";
|
|
|
5
5
|
import { useGetBucketNotification, useSetBucketNotification } from "../../hooks/index.js";
|
|
6
6
|
import { useSupportedNotificationEvents } from "../../hooks/useSupportedNotificationEvents.js";
|
|
7
7
|
import { createTestWrapper } from "../../test/testUtils.js";
|
|
8
|
+
import { EnhancedS3Error, ErrorCategory } from "../../utils/errorHandling.js";
|
|
8
9
|
import { BucketNotificationFormPage } from "../buckets/notifications/BucketNotificationFormPage.js";
|
|
9
10
|
jest.mock('../../hooks', ()=>({
|
|
10
11
|
useGetBucketNotification: jest.fn(),
|
|
@@ -235,14 +236,25 @@ describe('BucketNotificationFormPage', ()=>{
|
|
|
235
236
|
renderCreatePage();
|
|
236
237
|
expect(screen.getByText(/failed to fetch notification configuration/i)).toBeInTheDocument();
|
|
237
238
|
});
|
|
238
|
-
it('shows
|
|
239
|
+
it('shows permission message when notification data fails to load with 403 error', ()=>{
|
|
240
|
+
const authError = new EnhancedS3Error("You don't have permission to load the notification configuration. Contact your administrator.", 'AccessDenied', ErrorCategory.AUTHORIZATION, new Error('Access Denied'), 403);
|
|
239
241
|
mockUseGetBucketNotification.mockReturnValue({
|
|
240
242
|
data: void 0,
|
|
241
243
|
status: 'error',
|
|
242
|
-
error:
|
|
244
|
+
error: authError
|
|
243
245
|
});
|
|
244
246
|
renderCreatePage();
|
|
245
|
-
expect(screen.getByText(
|
|
247
|
+
expect(screen.getByText("You don't have permission to load the notification configuration. Contact your administrator.")).toBeInTheDocument();
|
|
248
|
+
});
|
|
249
|
+
it('shows error message from EnhancedS3Error', ()=>{
|
|
250
|
+
const error = new EnhancedS3Error('Something went wrong', 'InternalError', ErrorCategory.SERVER_ERROR, new Error('Something went wrong'), 500);
|
|
251
|
+
mockUseGetBucketNotification.mockReturnValue({
|
|
252
|
+
data: void 0,
|
|
253
|
+
status: 'error',
|
|
254
|
+
error
|
|
255
|
+
});
|
|
256
|
+
renderCreatePage();
|
|
257
|
+
expect(screen.getByText(/Something went wrong/i)).toBeInTheDocument();
|
|
246
258
|
});
|
|
247
259
|
});
|
|
248
260
|
describe('Edit Mode', ()=>{
|
|
@@ -102,7 +102,7 @@ const BucketCorsPage = ()=>{
|
|
|
102
102
|
onError: (error)=>{
|
|
103
103
|
setError('content', {
|
|
104
104
|
type: 'server',
|
|
105
|
-
message: error
|
|
105
|
+
message: error.message
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
});
|
|
@@ -123,7 +123,7 @@ const BucketCorsPage = ()=>{
|
|
|
123
123
|
onError: (error)=>{
|
|
124
124
|
setError('content', {
|
|
125
125
|
type: 'server',
|
|
126
|
-
message: error
|
|
126
|
+
message: error.message
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
});
|
|
@@ -163,12 +163,9 @@ const BucketCorsPage = ()=>{
|
|
|
163
163
|
size: "2x",
|
|
164
164
|
color: "statusWarning"
|
|
165
165
|
}),
|
|
166
|
-
/*#__PURE__*/
|
|
166
|
+
/*#__PURE__*/ jsx(Text, {
|
|
167
167
|
variant: "Large",
|
|
168
|
-
children:
|
|
169
|
-
"Failed to load CORS configuration: ",
|
|
170
|
-
corsError.message || 'Unknown error'
|
|
171
|
-
]
|
|
168
|
+
children: corsError.message
|
|
172
169
|
})
|
|
173
170
|
]
|
|
174
171
|
})
|
|
@@ -214,6 +211,7 @@ const BucketCorsPage = ()=>{
|
|
|
214
211
|
id: "corsConfiguration",
|
|
215
212
|
label: "CORS Rules",
|
|
216
213
|
direction: "vertical",
|
|
214
|
+
helpErrorPosition: "bottom",
|
|
217
215
|
error: errors.content?.message,
|
|
218
216
|
content: /*#__PURE__*/ jsxs(Stack, {
|
|
219
217
|
direction: "horizontal",
|
|
@@ -209,7 +209,7 @@ const ruleToFormValues = (rule)=>{
|
|
|
209
209
|
key: tag.Key || '',
|
|
210
210
|
value: tag.Value || ''
|
|
211
211
|
})) || [];
|
|
212
|
-
} else if (void 0 !== rule.Filter.Prefix) {
|
|
212
|
+
} else if (void 0 !== rule.Filter.Prefix && '' !== rule.Filter.Prefix) {
|
|
213
213
|
formValues.filterType = 'prefix';
|
|
214
214
|
formValues.prefix = rule.Filter.Prefix;
|
|
215
215
|
} else if (rule.Filter.Tag) {
|
|
@@ -226,13 +226,13 @@ const ruleToFormValues = (rule)=>{
|
|
|
226
226
|
formValues.transitionsEnabled = true;
|
|
227
227
|
formValues.transitions = rule.Transitions.map((t)=>({
|
|
228
228
|
timeType: void 0 !== t.Days ? 'days' : 'date',
|
|
229
|
-
days: t.Days
|
|
229
|
+
days: t.Days ?? 30,
|
|
230
230
|
date: t.Date ? new Date(t.Date).toISOString().split('T')[0] : '',
|
|
231
231
|
storageClass: t.StorageClass || 'GLACIER'
|
|
232
232
|
}));
|
|
233
233
|
}
|
|
234
234
|
if (rule.Expiration) {
|
|
235
|
-
if (rule.Expiration.Days) {
|
|
235
|
+
if (null != rule.Expiration.Days) {
|
|
236
236
|
formValues.expirationEnabled = true;
|
|
237
237
|
formValues.expirationType = 'days';
|
|
238
238
|
formValues.expirationDays = rule.Expiration.Days;
|
|
@@ -246,19 +246,19 @@ const ruleToFormValues = (rule)=>{
|
|
|
246
246
|
if (rule.NoncurrentVersionTransitions && rule.NoncurrentVersionTransitions.length > 0) {
|
|
247
247
|
formValues.noncurrentTransitionsEnabled = true;
|
|
248
248
|
formValues.noncurrentTransitions = rule.NoncurrentVersionTransitions.map((t)=>({
|
|
249
|
-
noncurrentDays: t.NoncurrentDays
|
|
249
|
+
noncurrentDays: t.NoncurrentDays ?? 30,
|
|
250
250
|
storageClass: t.StorageClass || 'GLACIER',
|
|
251
251
|
newerNoncurrentVersions: t.NewerNoncurrentVersions || 0
|
|
252
252
|
}));
|
|
253
253
|
}
|
|
254
254
|
if (rule.NoncurrentVersionExpiration) {
|
|
255
255
|
formValues.noncurrentExpirationEnabled = true;
|
|
256
|
-
formValues.noncurrentExpirationDays = rule.NoncurrentVersionExpiration.NoncurrentDays
|
|
256
|
+
formValues.noncurrentExpirationDays = rule.NoncurrentVersionExpiration.NoncurrentDays ?? 30;
|
|
257
257
|
formValues.noncurrentNewerVersions = rule.NoncurrentVersionExpiration.NewerNoncurrentVersions || 0;
|
|
258
258
|
}
|
|
259
259
|
if (rule.AbortIncompleteMultipartUpload) {
|
|
260
260
|
formValues.abortMpuEnabled = true;
|
|
261
|
-
formValues.abortMpuDays = rule.AbortIncompleteMultipartUpload.DaysAfterInitiation
|
|
261
|
+
formValues.abortMpuDays = rule.AbortIncompleteMultipartUpload.DaysAfterInitiation ?? 7;
|
|
262
262
|
}
|
|
263
263
|
return formValues;
|
|
264
264
|
};
|
|
@@ -347,7 +347,8 @@ function BucketLifecycleFormPage() {
|
|
|
347
347
|
understandISVRisk: true
|
|
348
348
|
}
|
|
349
349
|
});
|
|
350
|
-
const { handleSubmit, register, control, watch, reset, formState: { isValid,
|
|
350
|
+
const { handleSubmit, register, control, watch, reset, formState: { isValid, dirtyFields, errors } } = methods;
|
|
351
|
+
const hasRuleChanges = Object.keys(dirtyFields).some((key)=>'understandISVRisk' !== key);
|
|
351
352
|
const { fields: transitionFields, append: appendTransition, remove: removeTransition } = useFieldArray({
|
|
352
353
|
control,
|
|
353
354
|
name: 'transitions'
|
|
@@ -390,18 +391,23 @@ function BucketLifecycleFormPage() {
|
|
|
390
391
|
useEffect(()=>{
|
|
391
392
|
if (isEditMode && existingRule) {
|
|
392
393
|
const formValues = ruleToFormValues(existingRule);
|
|
393
|
-
reset(
|
|
394
|
+
reset({
|
|
395
|
+
...formValues,
|
|
396
|
+
understandISVRisk: !isISVManaged
|
|
397
|
+
});
|
|
394
398
|
}
|
|
395
399
|
}, [
|
|
396
400
|
isEditMode,
|
|
397
401
|
existingRule,
|
|
398
|
-
reset
|
|
402
|
+
reset,
|
|
403
|
+
isISVManaged
|
|
399
404
|
]);
|
|
400
405
|
useEffect(()=>{
|
|
401
|
-
methods.setValue('understandISVRisk', !isISVManaged, {
|
|
406
|
+
if (!isEditMode) methods.setValue('understandISVRisk', !isISVManaged, {
|
|
402
407
|
shouldValidate: true
|
|
403
408
|
});
|
|
404
409
|
}, [
|
|
410
|
+
isEditMode,
|
|
405
411
|
isISVManaged,
|
|
406
412
|
methods
|
|
407
413
|
]);
|
|
@@ -496,7 +502,7 @@ function BucketLifecycleFormPage() {
|
|
|
496
502
|
navigate(`/buckets/${bucketName}?tab=lifecycle`);
|
|
497
503
|
},
|
|
498
504
|
onError: (error)=>{
|
|
499
|
-
const errorMessage = error
|
|
505
|
+
const errorMessage = error.message;
|
|
500
506
|
showToast({
|
|
501
507
|
open: true,
|
|
502
508
|
message: errorMessage,
|
|
@@ -554,7 +560,7 @@ function BucketLifecycleFormPage() {
|
|
|
554
560
|
icon: isEditMode ? /*#__PURE__*/ jsx(Icon, {
|
|
555
561
|
name: "Save"
|
|
556
562
|
}) : void 0,
|
|
557
|
-
disabled: isEditMode ? !
|
|
563
|
+
disabled: isEditMode ? !hasRuleChanges || !isValid || isSaving : !isValid || isSaving
|
|
558
564
|
})
|
|
559
565
|
]
|
|
560
566
|
}),
|
|
@@ -9,7 +9,7 @@ const pluralizeDays = (days)=>1 === days ? `${days} day` : `${days} days`;
|
|
|
9
9
|
const formatActions = (rule)=>{
|
|
10
10
|
const actions = [];
|
|
11
11
|
if (rule.Expiration) {
|
|
12
|
-
if (rule.Expiration.Days) actions.push({
|
|
12
|
+
if (null != rule.Expiration.Days) actions.push({
|
|
13
13
|
text: `Expire current (${pluralizeDays(rule.Expiration.Days)})`,
|
|
14
14
|
description: `Expire current versions of objects after ${pluralizeDays(rule.Expiration.Days)}`
|
|
15
15
|
});
|
|
@@ -23,7 +23,7 @@ const formatActions = (rule)=>{
|
|
|
23
23
|
description: 'Delete expired object delete markers'
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
if (rule.NoncurrentVersionExpiration?.NoncurrentDays) {
|
|
26
|
+
if (rule.NoncurrentVersionExpiration?.NoncurrentDays != null) {
|
|
27
27
|
const keepText = rule.NoncurrentVersionExpiration.NewerNoncurrentVersions ? `, keep ${rule.NoncurrentVersionExpiration.NewerNoncurrentVersions}` : '';
|
|
28
28
|
const keepDescription = rule.NoncurrentVersionExpiration.NewerNoncurrentVersions ? `, keep ${rule.NoncurrentVersionExpiration.NewerNoncurrentVersions} newest versions` : '';
|
|
29
29
|
actions.push({
|
|
@@ -32,7 +32,7 @@ const formatActions = (rule)=>{
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
if (rule.Transitions && rule.Transitions.length > 0) rule.Transitions.forEach((transition)=>{
|
|
35
|
-
if (transition.Days) actions.push({
|
|
35
|
+
if (null != transition.Days) actions.push({
|
|
36
36
|
text: `Transition current (${pluralizeDays(transition.Days)})`,
|
|
37
37
|
description: `Transition current versions of objects after ${pluralizeDays(transition.Days)}`
|
|
38
38
|
});
|
|
@@ -43,7 +43,7 @@ const formatActions = (rule)=>{
|
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
if (rule.NoncurrentVersionTransitions && rule.NoncurrentVersionTransitions.length > 0) rule.NoncurrentVersionTransitions.forEach((transition)=>{
|
|
46
|
-
if (transition.NoncurrentDays) {
|
|
46
|
+
if (null != transition.NoncurrentDays) {
|
|
47
47
|
const keepText = transition.NewerNoncurrentVersions ? `, keep ${transition.NewerNoncurrentVersions}` : '';
|
|
48
48
|
const keepDescription = transition.NewerNoncurrentVersions ? `, keep ${transition.NewerNoncurrentVersions} newest versions` : '';
|
|
49
49
|
actions.push({
|
|
@@ -52,7 +52,7 @@ const formatActions = (rule)=>{
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
|
-
if (rule.AbortIncompleteMultipartUpload?.DaysAfterInitiation) actions.push({
|
|
55
|
+
if (rule.AbortIncompleteMultipartUpload?.DaysAfterInitiation != null) actions.push({
|
|
56
56
|
text: `Abort Incomplete MPU (${pluralizeDays(rule.AbortIncompleteMultipartUpload.DaysAfterInitiation)})`,
|
|
57
57
|
description: `Abort incomplete multipart uploads after ${pluralizeDays(rule.AbortIncompleteMultipartUpload.DaysAfterInitiation)}`
|
|
58
58
|
});
|
|
@@ -476,7 +476,7 @@ const PermissionsSection = /*#__PURE__*/ memo(({ onEditPolicy, onEditCors, owner
|
|
|
476
476
|
onError: (error)=>{
|
|
477
477
|
showToast({
|
|
478
478
|
open: true,
|
|
479
|
-
message: error
|
|
479
|
+
message: error.message,
|
|
480
480
|
status: 'error'
|
|
481
481
|
});
|
|
482
482
|
}
|
|
@@ -94,7 +94,7 @@ const BucketPolicyPage = ()=>{
|
|
|
94
94
|
onError: (error)=>{
|
|
95
95
|
setError('content', {
|
|
96
96
|
type: 'server',
|
|
97
|
-
message: error
|
|
97
|
+
message: error.message
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
});
|
|
@@ -114,7 +114,7 @@ const BucketPolicyPage = ()=>{
|
|
|
114
114
|
onError: (error)=>{
|
|
115
115
|
setError('content', {
|
|
116
116
|
type: 'server',
|
|
117
|
-
message: error
|
|
117
|
+
message: error.message
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
});
|
|
@@ -153,12 +153,9 @@ const BucketPolicyPage = ()=>{
|
|
|
153
153
|
size: "2x",
|
|
154
154
|
color: "statusWarning"
|
|
155
155
|
}),
|
|
156
|
-
/*#__PURE__*/
|
|
156
|
+
/*#__PURE__*/ jsx(Text, {
|
|
157
157
|
variant: "Large",
|
|
158
|
-
children:
|
|
159
|
-
"Failed to load bucket policy: ",
|
|
160
|
-
policyError.message || 'Unknown error'
|
|
161
|
-
]
|
|
158
|
+
children: policyError.message
|
|
162
159
|
})
|
|
163
160
|
]
|
|
164
161
|
})
|
|
@@ -450,7 +450,8 @@ function BucketReplicationFormPage() {
|
|
|
450
450
|
understandISVRisk: true
|
|
451
451
|
}
|
|
452
452
|
});
|
|
453
|
-
const { handleSubmit, register, control, watch, reset, formState: { isValid, isDirty, errors } } = methods;
|
|
453
|
+
const { handleSubmit, register, control, watch, reset, formState: { isValid, isDirty, dirtyFields, errors } } = methods;
|
|
454
|
+
const hasRuleChanges = Object.keys(dirtyFields).some((key)=>'understandISVRisk' !== key);
|
|
454
455
|
const { fields: tagFields, append: appendTag, remove: removeTag } = useFieldArray({
|
|
455
456
|
control,
|
|
456
457
|
name: 'tags'
|
|
@@ -491,7 +492,10 @@ function BucketReplicationFormPage() {
|
|
|
491
492
|
if (isEditMode && existingRule) {
|
|
492
493
|
if (loadedRuleIdRef.current !== existingRule.ID) {
|
|
493
494
|
const formValues = ruleToFormValues(existingRule, existingRole);
|
|
494
|
-
reset(
|
|
495
|
+
reset({
|
|
496
|
+
...formValues,
|
|
497
|
+
understandISVRisk: !isISVManaged
|
|
498
|
+
});
|
|
495
499
|
loadedRuleIdRef.current = existingRule.ID || null;
|
|
496
500
|
}
|
|
497
501
|
} else if (!isEditMode && !isDirty) {
|
|
@@ -509,7 +513,8 @@ function BucketReplicationFormPage() {
|
|
|
509
513
|
replicationRoleDefault,
|
|
510
514
|
nextAvailablePriority,
|
|
511
515
|
isDirty,
|
|
512
|
-
reset
|
|
516
|
+
reset,
|
|
517
|
+
isISVManaged
|
|
513
518
|
]);
|
|
514
519
|
const prevFilterTypeRef = useRef();
|
|
515
520
|
useEffect(()=>{
|
|
@@ -564,10 +569,11 @@ function BucketReplicationFormPage() {
|
|
|
564
569
|
methods
|
|
565
570
|
]);
|
|
566
571
|
useEffect(()=>{
|
|
567
|
-
methods.setValue('understandISVRisk', !isISVManaged, {
|
|
572
|
+
if (!isEditMode) methods.setValue('understandISVRisk', !isISVManaged, {
|
|
568
573
|
shouldValidate: true
|
|
569
574
|
});
|
|
570
575
|
}, [
|
|
576
|
+
isEditMode,
|
|
571
577
|
isISVManaged,
|
|
572
578
|
methods
|
|
573
579
|
]);
|
|
@@ -601,7 +607,7 @@ function BucketReplicationFormPage() {
|
|
|
601
607
|
navigate(`/buckets/${bucketName}?tab=replication`);
|
|
602
608
|
},
|
|
603
609
|
onError: (error)=>{
|
|
604
|
-
const errorMessage = error
|
|
610
|
+
const errorMessage = error.message;
|
|
605
611
|
showToast({
|
|
606
612
|
open: true,
|
|
607
613
|
message: errorMessage,
|
|
@@ -660,7 +666,7 @@ function BucketReplicationFormPage() {
|
|
|
660
666
|
icon: isEditMode ? /*#__PURE__*/ jsx(Icon, {
|
|
661
667
|
name: "Save"
|
|
662
668
|
}) : void 0,
|
|
663
|
-
disabled: !
|
|
669
|
+
disabled: isEditMode ? !hasRuleChanges || !isValid || isSaving : !isValid || isSaving
|
|
664
670
|
})
|
|
665
671
|
]
|
|
666
672
|
}),
|
|
@@ -157,7 +157,7 @@ function BucketNotificationFormPage() {
|
|
|
157
157
|
navigate(`/buckets/${bucketName}?tab=notification`);
|
|
158
158
|
},
|
|
159
159
|
onError: (error)=>{
|
|
160
|
-
const errorMessage = error
|
|
160
|
+
const errorMessage = error.message;
|
|
161
161
|
showToast({
|
|
162
162
|
open: true,
|
|
163
163
|
message: errorMessage,
|
|
@@ -181,7 +181,7 @@ function BucketNotificationFormPage() {
|
|
|
181
181
|
})
|
|
182
182
|
});
|
|
183
183
|
if ('error' === notificationStatus && notificationError) {
|
|
184
|
-
const errorMessage = notificationError
|
|
184
|
+
const errorMessage = notificationError.message;
|
|
185
185
|
return /*#__PURE__*/ jsx(Form, {
|
|
186
186
|
layout: {
|
|
187
187
|
kind: 'page',
|
|
@@ -45,7 +45,7 @@ function BucketNotificationList({ bucketName, notificationRules, notificationSta
|
|
|
45
45
|
});
|
|
46
46
|
},
|
|
47
47
|
onError: (error)=>{
|
|
48
|
-
const errorMessage = error
|
|
48
|
+
const errorMessage = error.message;
|
|
49
49
|
showToast({
|
|
50
50
|
open: true,
|
|
51
51
|
message: errorMessage,
|
|
@@ -110,7 +110,7 @@ const DeleteObjectButton = ({ objects, bucketName, onDeleteSuccess })=>{
|
|
|
110
110
|
} catch (error) {
|
|
111
111
|
showToast({
|
|
112
112
|
open: true,
|
|
113
|
-
message: error instanceof Error ? error.message : '
|
|
113
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
114
114
|
status: 'error'
|
|
115
115
|
});
|
|
116
116
|
} finally{
|
|
@@ -62,7 +62,7 @@ const GetPresignedUrlButton = ({ bucketName, objectKey, versionId })=>{
|
|
|
62
62
|
} catch (error) {
|
|
63
63
|
showToast({
|
|
64
64
|
open: true,
|
|
65
|
-
message: error instanceof Error ? error.message : '
|
|
65
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
66
66
|
status: 'error'
|
|
67
67
|
});
|
|
68
68
|
}
|
|
@@ -160,7 +160,7 @@ const ObjectMetadata = ({ bucketName, objectKey, versionId })=>{
|
|
|
160
160
|
status: 'success'
|
|
161
161
|
});
|
|
162
162
|
} catch (error) {
|
|
163
|
-
const errorMessage = error instanceof Error ? error.message : '
|
|
163
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
164
164
|
showToast({
|
|
165
165
|
open: true,
|
|
166
166
|
message: errorMessage,
|