@scality/data-browser-library 1.0.0-preview.7 → 1.0.0-preview.9
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__/BucketCreate.test.d.ts +1 -0
- package/dist/components/__tests__/BucketCreate.test.js +408 -0
- package/dist/components/__tests__/BucketLifecycleFormPage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketLifecycleFormPage.test.js +618 -0
- package/dist/components/__tests__/BucketLifecycleList.test.d.ts +1 -0
- package/dist/components/__tests__/BucketLifecycleList.test.js +325 -0
- package/dist/components/__tests__/BucketList.test.js +190 -0
- package/dist/components/__tests__/BucketOverview.test.js +298 -8
- package/dist/components/__tests__/BucketReplicationFormPage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketReplicationFormPage.test.js +1757 -0
- package/dist/components/__tests__/BucketReplicationList.test.d.ts +1 -0
- package/dist/components/__tests__/BucketReplicationList.test.js +344 -0
- package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.d.ts +1 -0
- package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +196 -0
- package/dist/components/__tests__/EmptyBucketButton.test.d.ts +1 -0
- package/dist/components/__tests__/EmptyBucketButton.test.js +302 -0
- package/dist/components/buckets/BucketCreate.d.ts +49 -0
- package/dist/components/buckets/BucketCreate.js +237 -0
- package/dist/components/buckets/BucketDetails.js +62 -10
- package/dist/components/buckets/BucketLifecycleFormPage.d.ts +15 -0
- package/dist/components/buckets/BucketLifecycleFormPage.js +1070 -0
- package/dist/components/buckets/BucketLifecycleList.d.ts +10 -0
- package/dist/components/buckets/BucketLifecycleList.js +270 -0
- package/dist/components/buckets/BucketList.d.ts +5 -2
- package/dist/components/buckets/BucketList.js +38 -28
- package/dist/components/buckets/BucketOverview.d.ts +65 -4
- package/dist/components/buckets/BucketOverview.js +261 -179
- package/dist/components/buckets/BucketPage.js +1 -1
- package/dist/components/buckets/BucketReplicationFormPage.d.ts +1 -0
- package/dist/components/buckets/BucketReplicationFormPage.js +834 -0
- package/dist/components/buckets/BucketReplicationList.d.ts +11 -0
- package/dist/components/buckets/BucketReplicationList.js +189 -0
- package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +18 -0
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +53 -0
- package/dist/components/buckets/EmptyBucketButton.d.ts +5 -0
- package/dist/components/buckets/EmptyBucketButton.js +232 -0
- package/dist/components/buckets/EmptyBucketSummary.d.ts +9 -0
- package/dist/components/buckets/EmptyBucketSummary.js +60 -0
- package/dist/components/buckets/EmptyBucketSummaryList.d.ts +13 -0
- package/dist/components/buckets/EmptyBucketSummaryList.js +140 -0
- package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +8 -8
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +9 -2
- package/dist/components/objects/ObjectLock/EditRetentionButton.d.ts +4 -0
- package/dist/components/objects/ObjectLock/EditRetentionButton.js +32 -0
- package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.d.ts +3 -0
- package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +211 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +9 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettings.js +158 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +8 -0
- package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +39 -0
- package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.d.ts +1 -0
- package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +204 -0
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.d.ts +1 -0
- package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +374 -0
- package/dist/components/ui/ArrayFieldActions.d.ts +36 -0
- package/dist/components/ui/ArrayFieldActions.js +38 -0
- package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +16 -0
- package/dist/components/ui/ConfirmDeleteRuleModal.js +43 -0
- package/dist/components/ui/FilterFormSection.d.ts +44 -0
- package/dist/components/ui/FilterFormSection.js +159 -0
- package/dist/config/factory.d.ts +13 -2
- package/dist/config/factory.js +9 -6
- package/dist/hooks/__tests__/useISVBucketDetection.test.d.ts +1 -0
- package/dist/hooks/__tests__/useISVBucketDetection.test.js +188 -0
- package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +44 -1
- package/dist/hooks/factories/useCreateS3QueryHook.js +22 -1
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +5 -1
- package/dist/hooks/useDeleteBucketConfigRule.d.ts +26 -0
- package/dist/hooks/useDeleteBucketConfigRule.js +46 -0
- package/dist/hooks/useEmptyBucket.d.ts +27 -0
- package/dist/hooks/useEmptyBucket.js +116 -0
- package/dist/hooks/useISVBucketDetection.d.ts +15 -0
- package/dist/hooks/useISVBucketDetection.js +27 -0
- package/dist/hooks/useTableRowSelection.d.ts +9 -0
- package/dist/hooks/useTableRowSelection.js +45 -0
- package/dist/test/setup.js +8 -0
- package/dist/test/testUtils.d.ts +99 -17
- package/dist/test/testUtils.js +64 -16
- package/dist/test/utils/errorHandling.test.js +39 -1
- package/dist/utils/constants.d.ts +12 -0
- package/dist/utils/constants.js +9 -0
- package/dist/utils/errorHandling.d.ts +9 -0
- package/dist/utils/errorHandling.js +6 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/s3RuleUtils.d.ts +53 -0
- package/dist/utils/s3RuleUtils.js +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GetObjectRetentionCommandOutput } from "@aws-sdk/client-s3";
|
|
2
|
+
export declare function getDefaultRetention(retentionData: GetObjectRetentionCommandOutput | null | undefined): {
|
|
3
|
+
isDefaultRetentionEnabled: boolean;
|
|
4
|
+
defaultRetentionUntilDate: Date;
|
|
5
|
+
defaultRetentionMode: import("@aws-sdk/client-s3").ObjectLockRetentionMode;
|
|
6
|
+
};
|
|
7
|
+
export declare function getDefaultMinRetainUntilDate(d: Date | null | undefined, mode: string): string;
|
|
8
|
+
export declare function getRetainUntilDateHint(dateString: string): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function getDefaultRetention(retentionData) {
|
|
2
|
+
const isDefaultRetentionEnabled = retentionData?.Retention !== void 0 && null !== retentionData.Retention;
|
|
3
|
+
const defaultRetentionUntilDate = retentionData?.Retention?.RetainUntilDate ? new Date(retentionData.Retention.RetainUntilDate) : new Date();
|
|
4
|
+
const defaultRetentionMode = retentionData?.Retention?.Mode || "GOVERNANCE";
|
|
5
|
+
return {
|
|
6
|
+
isDefaultRetentionEnabled,
|
|
7
|
+
defaultRetentionUntilDate,
|
|
8
|
+
defaultRetentionMode
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function getDefaultMinRetainUntilDate(d, mode) {
|
|
12
|
+
const tomorrow = new Date();
|
|
13
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
14
|
+
const futureDate = formatDateForInput(tomorrow);
|
|
15
|
+
if (!d) return futureDate;
|
|
16
|
+
const previousRetainUntilDate = "GOVERNANCE" === mode ? formatDateForInput(d) : formatDateForInput(addDays(d, 1));
|
|
17
|
+
return previousRetainUntilDate > futureDate ? previousRetainUntilDate : futureDate;
|
|
18
|
+
}
|
|
19
|
+
function formatDateForInput(date) {
|
|
20
|
+
const year = date.getFullYear();
|
|
21
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
22
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
23
|
+
return `${year}-${month}-${day}`;
|
|
24
|
+
}
|
|
25
|
+
function addDays(date, days) {
|
|
26
|
+
const result = new Date(date);
|
|
27
|
+
result.setDate(result.getDate() + days);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function getRetainUntilDateHint(dateString) {
|
|
31
|
+
const now = new Date();
|
|
32
|
+
const retainUntilDate = new Date(dateString);
|
|
33
|
+
const diffInMs = retainUntilDate.getTime() - now.getTime();
|
|
34
|
+
const diffInDays = Math.floor(diffInMs / 86400000);
|
|
35
|
+
if (diffInDays < 1) return "(within 24 hours from now)";
|
|
36
|
+
if (1 === diffInDays) return "(1 day from now)";
|
|
37
|
+
return `(${diffInDays} days from now)`;
|
|
38
|
+
}
|
|
39
|
+
export { getDefaultMinRetainUntilDate, getDefaultRetention, getRetainUntilDateHint };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter, useNavigate } from "react-router-dom";
|
|
4
|
+
import { createTestWrapper } from "../../../../test/testUtils.js";
|
|
5
|
+
import { EditRetentionButton } from "../EditRetentionButton.js";
|
|
6
|
+
import { useISVBucketStatus } from "../../../../hooks/index.js";
|
|
7
|
+
jest.mock("../../../../hooks");
|
|
8
|
+
jest.mock("react-router-dom", ()=>({
|
|
9
|
+
...jest.requireActual("react-router-dom"),
|
|
10
|
+
useNavigate: jest.fn()
|
|
11
|
+
}));
|
|
12
|
+
const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
|
|
13
|
+
const mockUseNavigate = jest.mocked(useNavigate);
|
|
14
|
+
const renderEditRetentionButton = (props = {})=>{
|
|
15
|
+
const Wrapper = createTestWrapper();
|
|
16
|
+
const mockNavigate = jest.fn();
|
|
17
|
+
mockUseNavigate.mockReturnValue(mockNavigate);
|
|
18
|
+
const result = render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
19
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
20
|
+
children: /*#__PURE__*/ jsx(EditRetentionButton, {
|
|
21
|
+
bucketName: "test-bucket",
|
|
22
|
+
...props
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
}));
|
|
26
|
+
return {
|
|
27
|
+
...result,
|
|
28
|
+
navigate: mockNavigate
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
beforeEach(()=>{
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
34
|
+
isVeeamBucket: false,
|
|
35
|
+
isCommvaultBucket: false,
|
|
36
|
+
isISVManaged: false,
|
|
37
|
+
isvApplication: void 0,
|
|
38
|
+
isLoading: false,
|
|
39
|
+
bucketTagsStatus: "success"
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it("renders edit button", ()=>{
|
|
43
|
+
renderEditRetentionButton();
|
|
44
|
+
const editButton = screen.getByRole("button", {
|
|
45
|
+
name: /edit default retention/i
|
|
46
|
+
});
|
|
47
|
+
expect(editButton).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
it("navigates to object lock settings page when clicked", ()=>{
|
|
50
|
+
const { navigate } = renderEditRetentionButton();
|
|
51
|
+
const editButton = screen.getByRole("button", {
|
|
52
|
+
name: /edit default retention/i
|
|
53
|
+
});
|
|
54
|
+
fireEvent.click(editButton);
|
|
55
|
+
expect(navigate).toHaveBeenCalledWith("/buckets/test-bucket/objects/object-lock-settings");
|
|
56
|
+
});
|
|
57
|
+
it("navigates with correct bucket name", ()=>{
|
|
58
|
+
const { navigate } = renderEditRetentionButton();
|
|
59
|
+
const editButton = screen.getByRole("button", {
|
|
60
|
+
name: /edit default retention/i
|
|
61
|
+
});
|
|
62
|
+
fireEvent.click(editButton);
|
|
63
|
+
expect(navigate).toHaveBeenCalledWith("/buckets/test-bucket/objects/object-lock-settings");
|
|
64
|
+
});
|
|
65
|
+
it("is enabled for regular buckets without ISV tags", ()=>{
|
|
66
|
+
renderEditRetentionButton();
|
|
67
|
+
const editButton = screen.getByRole("button", {
|
|
68
|
+
name: /edit default retention/i
|
|
69
|
+
});
|
|
70
|
+
expect(editButton).not.toBeDisabled();
|
|
71
|
+
});
|
|
72
|
+
it("is disabled for Veeam Backup & Replication buckets", ()=>{
|
|
73
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
74
|
+
isVeeamBucket: true,
|
|
75
|
+
isCommvaultBucket: false,
|
|
76
|
+
isISVManaged: true,
|
|
77
|
+
isvApplication: "Veeam",
|
|
78
|
+
isLoading: false,
|
|
79
|
+
bucketTagsStatus: "success"
|
|
80
|
+
});
|
|
81
|
+
renderEditRetentionButton();
|
|
82
|
+
const editButton = screen.getByRole("button", {
|
|
83
|
+
name: /edit default retention/i
|
|
84
|
+
});
|
|
85
|
+
expect(editButton).toBeDisabled();
|
|
86
|
+
});
|
|
87
|
+
it("is disabled for Veeam Office 365 v6/v7 buckets", ()=>{
|
|
88
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
89
|
+
isVeeamBucket: true,
|
|
90
|
+
isCommvaultBucket: false,
|
|
91
|
+
isISVManaged: true,
|
|
92
|
+
isvApplication: "Veeam",
|
|
93
|
+
isLoading: false,
|
|
94
|
+
bucketTagsStatus: "success"
|
|
95
|
+
});
|
|
96
|
+
renderEditRetentionButton();
|
|
97
|
+
const editButton = screen.getByRole("button", {
|
|
98
|
+
name: /edit default retention/i
|
|
99
|
+
});
|
|
100
|
+
expect(editButton).toBeDisabled();
|
|
101
|
+
});
|
|
102
|
+
it("is disabled for Veeam Office 365 v8+ buckets", ()=>{
|
|
103
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
104
|
+
isVeeamBucket: true,
|
|
105
|
+
isCommvaultBucket: false,
|
|
106
|
+
isISVManaged: true,
|
|
107
|
+
isvApplication: "Veeam",
|
|
108
|
+
isLoading: false,
|
|
109
|
+
bucketTagsStatus: "success"
|
|
110
|
+
});
|
|
111
|
+
renderEditRetentionButton();
|
|
112
|
+
const editButton = screen.getByRole("button", {
|
|
113
|
+
name: /edit default retention/i
|
|
114
|
+
});
|
|
115
|
+
expect(editButton).toBeDisabled();
|
|
116
|
+
});
|
|
117
|
+
it("is disabled for Commvault buckets", ()=>{
|
|
118
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
119
|
+
isVeeamBucket: false,
|
|
120
|
+
isCommvaultBucket: true,
|
|
121
|
+
isISVManaged: true,
|
|
122
|
+
isvApplication: "Commvault",
|
|
123
|
+
isLoading: false,
|
|
124
|
+
bucketTagsStatus: "success"
|
|
125
|
+
});
|
|
126
|
+
renderEditRetentionButton();
|
|
127
|
+
const editButton = screen.getByRole("button", {
|
|
128
|
+
name: /edit default retention/i
|
|
129
|
+
});
|
|
130
|
+
expect(editButton).toBeDisabled();
|
|
131
|
+
});
|
|
132
|
+
it("is disabled for ISV buckets tagged as Veeam Backup for Microsoft 365", ()=>{
|
|
133
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
134
|
+
isVeeamBucket: true,
|
|
135
|
+
isCommvaultBucket: false,
|
|
136
|
+
isISVManaged: true,
|
|
137
|
+
isvApplication: "Veeam",
|
|
138
|
+
isLoading: false,
|
|
139
|
+
bucketTagsStatus: "success"
|
|
140
|
+
});
|
|
141
|
+
renderEditRetentionButton();
|
|
142
|
+
const editButton = screen.getByRole("button", {
|
|
143
|
+
name: /edit default retention/i
|
|
144
|
+
});
|
|
145
|
+
expect(editButton).toBeDisabled();
|
|
146
|
+
});
|
|
147
|
+
it("is disabled for ISV buckets tagged as Veeam Backup & Replication", ()=>{
|
|
148
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
149
|
+
isVeeamBucket: true,
|
|
150
|
+
isCommvaultBucket: false,
|
|
151
|
+
isISVManaged: true,
|
|
152
|
+
isvApplication: "Veeam",
|
|
153
|
+
isLoading: false,
|
|
154
|
+
bucketTagsStatus: "success"
|
|
155
|
+
});
|
|
156
|
+
renderEditRetentionButton();
|
|
157
|
+
const editButton = screen.getByRole("button", {
|
|
158
|
+
name: /edit default retention/i
|
|
159
|
+
});
|
|
160
|
+
expect(editButton).toBeDisabled();
|
|
161
|
+
});
|
|
162
|
+
it("handles missing bucket tags gracefully", ()=>{
|
|
163
|
+
renderEditRetentionButton();
|
|
164
|
+
const editButton = screen.getByRole("button", {
|
|
165
|
+
name: /edit default retention/i
|
|
166
|
+
});
|
|
167
|
+
expect(editButton).not.toBeDisabled();
|
|
168
|
+
});
|
|
169
|
+
it("handles empty TagSet gracefully", ()=>{
|
|
170
|
+
renderEditRetentionButton();
|
|
171
|
+
const editButton = screen.getByRole("button", {
|
|
172
|
+
name: /edit default retention/i
|
|
173
|
+
});
|
|
174
|
+
expect(editButton).not.toBeDisabled();
|
|
175
|
+
});
|
|
176
|
+
it("is enabled for buckets with non-ISV tags", ()=>{
|
|
177
|
+
renderEditRetentionButton();
|
|
178
|
+
const editButton = screen.getByRole("button", {
|
|
179
|
+
name: /edit default retention/i
|
|
180
|
+
});
|
|
181
|
+
expect(editButton).not.toBeDisabled();
|
|
182
|
+
});
|
|
183
|
+
it("handles bucket tagging query error gracefully", ()=>{
|
|
184
|
+
renderEditRetentionButton();
|
|
185
|
+
const editButton = screen.getByRole("button", {
|
|
186
|
+
name: /edit default retention/i
|
|
187
|
+
});
|
|
188
|
+
expect(editButton).not.toBeDisabled();
|
|
189
|
+
});
|
|
190
|
+
it("shows loading state when fetching bucket tags", ()=>{
|
|
191
|
+
mockUseISVBucketStatus.mockReturnValue({
|
|
192
|
+
isVeeamBucket: false,
|
|
193
|
+
isCommvaultBucket: false,
|
|
194
|
+
isISVManaged: false,
|
|
195
|
+
isvApplication: void 0,
|
|
196
|
+
isLoading: true,
|
|
197
|
+
bucketTagsStatus: "pending"
|
|
198
|
+
});
|
|
199
|
+
renderEditRetentionButton();
|
|
200
|
+
const editButton = screen.getByRole("button", {
|
|
201
|
+
name: /edit default retention/i
|
|
202
|
+
});
|
|
203
|
+
expect(editButton).toBeInTheDocument();
|
|
204
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import user_event from "@testing-library/user-event";
|
|
4
|
+
import { MemoryRouter, Route, Routes } from "react-router-dom";
|
|
5
|
+
import { createTestWrapper } from "../../../../test/testUtils.js";
|
|
6
|
+
import { ObjectLockSettings } from "../ObjectLockSettings.js";
|
|
7
|
+
import { useGetBucketObjectLockConfiguration, useSetBucketObjectLockConfiguration } from "../../../../hooks/index.js";
|
|
8
|
+
jest.mock("../../../../hooks", ()=>({
|
|
9
|
+
useGetBucketObjectLockConfiguration: jest.fn(),
|
|
10
|
+
useSetBucketObjectLockConfiguration: jest.fn()
|
|
11
|
+
}));
|
|
12
|
+
const mockUseGetBucketObjectLockConfiguration = jest.mocked(useGetBucketObjectLockConfiguration);
|
|
13
|
+
const mockUseSetBucketObjectLockConfiguration = jest.mocked(useSetBucketObjectLockConfiguration);
|
|
14
|
+
const mockNavigate = jest.fn();
|
|
15
|
+
jest.mock("react-router-dom", ()=>({
|
|
16
|
+
...jest.requireActual("react-router-dom"),
|
|
17
|
+
useNavigate: ()=>mockNavigate
|
|
18
|
+
}));
|
|
19
|
+
const renderObjectLockSettings = (bucketName = "test-bucket")=>{
|
|
20
|
+
const Wrapper = createTestWrapper();
|
|
21
|
+
return render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
22
|
+
initialEntries: [
|
|
23
|
+
`/buckets/${bucketName}/objects/settings`
|
|
24
|
+
],
|
|
25
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
26
|
+
children: /*#__PURE__*/ jsx(Routes, {
|
|
27
|
+
children: /*#__PURE__*/ jsx(Route, {
|
|
28
|
+
path: "/buckets/:bucketName/objects/settings",
|
|
29
|
+
element: /*#__PURE__*/ jsx(ObjectLockSettings, {})
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
describe("ObjectLockSettings", ()=>{
|
|
36
|
+
const mockMutate = jest.fn();
|
|
37
|
+
beforeEach(()=>{
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
mockNavigate.mockClear();
|
|
40
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
41
|
+
data: void 0,
|
|
42
|
+
status: "success"
|
|
43
|
+
});
|
|
44
|
+
mockUseSetBucketObjectLockConfiguration.mockReturnValue({
|
|
45
|
+
mutate: mockMutate,
|
|
46
|
+
isPending: false
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
it("displays loading state while fetching bucket configuration", ()=>{
|
|
50
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
51
|
+
data: void 0,
|
|
52
|
+
status: "pending"
|
|
53
|
+
});
|
|
54
|
+
renderObjectLockSettings();
|
|
55
|
+
expect(screen.getByText("Loading retention settings...")).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
it("renders form with Object Lock checkbox", async ()=>{
|
|
58
|
+
renderObjectLockSettings();
|
|
59
|
+
await waitFor(()=>{
|
|
60
|
+
expect(screen.getByText("Object-lock settings")).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
expect(screen.getByLabelText(/object-lock/i)).toBeInTheDocument();
|
|
63
|
+
});
|
|
64
|
+
it("disables save button when Object Lock is not enabled", async ()=>{
|
|
65
|
+
renderObjectLockSettings();
|
|
66
|
+
await waitFor(()=>{
|
|
67
|
+
const saveButton = screen.getByRole("button", {
|
|
68
|
+
name: /save/i
|
|
69
|
+
});
|
|
70
|
+
expect(saveButton).toBeDisabled();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it("shows Default Retention fields when Object Lock is enabled", async ()=>{
|
|
74
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
75
|
+
data: {
|
|
76
|
+
ObjectLockConfiguration: {
|
|
77
|
+
ObjectLockEnabled: "Enabled"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
status: "success"
|
|
81
|
+
});
|
|
82
|
+
renderObjectLockSettings();
|
|
83
|
+
await waitFor(()=>{
|
|
84
|
+
expect(screen.getByText("Default Retention")).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText("Retention mode")).toBeInTheDocument();
|
|
86
|
+
expect(screen.getByText("Retention period")).toBeInTheDocument();
|
|
87
|
+
}, {
|
|
88
|
+
timeout: 3000
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it("enables save button when Object Lock is enabled", async ()=>{
|
|
92
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
93
|
+
data: {
|
|
94
|
+
ObjectLockConfiguration: {
|
|
95
|
+
ObjectLockEnabled: "Enabled"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
status: "success"
|
|
99
|
+
});
|
|
100
|
+
renderObjectLockSettings();
|
|
101
|
+
await waitFor(()=>{
|
|
102
|
+
const saveButton = screen.getByRole("button", {
|
|
103
|
+
name: /save/i
|
|
104
|
+
});
|
|
105
|
+
expect(saveButton).toBeEnabled();
|
|
106
|
+
}, {
|
|
107
|
+
timeout: 3000
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it("enables retention fields when Default Retention is checked", async ()=>{
|
|
111
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
112
|
+
data: {
|
|
113
|
+
ObjectLockConfiguration: {
|
|
114
|
+
ObjectLockEnabled: "Enabled"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
status: "success"
|
|
118
|
+
});
|
|
119
|
+
renderObjectLockSettings();
|
|
120
|
+
await waitFor(()=>{
|
|
121
|
+
expect(screen.getByText("Default Retention")).toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
124
|
+
expect(defaultRetentionCheckbox).not.toBeChecked();
|
|
125
|
+
fireEvent.click(defaultRetentionCheckbox);
|
|
126
|
+
await waitFor(()=>{
|
|
127
|
+
const governanceRadio = screen.getByLabelText(/governance/i);
|
|
128
|
+
expect(governanceRadio).not.toBeDisabled();
|
|
129
|
+
}, {
|
|
130
|
+
timeout: 3000
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
it("disables retention fields when Default Retention is unchecked", async ()=>{
|
|
134
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
135
|
+
data: {
|
|
136
|
+
ObjectLockConfiguration: {
|
|
137
|
+
ObjectLockEnabled: "Enabled",
|
|
138
|
+
Rule: {
|
|
139
|
+
DefaultRetention: {
|
|
140
|
+
Mode: "GOVERNANCE",
|
|
141
|
+
Days: 30
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
status: "success"
|
|
147
|
+
});
|
|
148
|
+
renderObjectLockSettings();
|
|
149
|
+
await waitFor(()=>{
|
|
150
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
151
|
+
fireEvent.click(defaultRetentionCheckbox);
|
|
152
|
+
});
|
|
153
|
+
await waitFor(()=>{
|
|
154
|
+
const governanceRadio = screen.getByLabelText(/governance/i);
|
|
155
|
+
expect(governanceRadio).toBeDisabled();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
it("shows validation error when retention period is invalid", async ()=>{
|
|
159
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
160
|
+
data: {
|
|
161
|
+
ObjectLockConfiguration: {
|
|
162
|
+
ObjectLockEnabled: "Enabled"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
status: "success"
|
|
166
|
+
});
|
|
167
|
+
renderObjectLockSettings();
|
|
168
|
+
await waitFor(()=>{
|
|
169
|
+
expect(screen.getByText("Default Retention")).toBeInTheDocument();
|
|
170
|
+
});
|
|
171
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
172
|
+
fireEvent.click(defaultRetentionCheckbox);
|
|
173
|
+
await waitFor(()=>{
|
|
174
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
175
|
+
expect(retentionPeriodInput).toBeInTheDocument();
|
|
176
|
+
});
|
|
177
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
178
|
+
await user_event.clear(retentionPeriodInput);
|
|
179
|
+
await user_event.type(retentionPeriodInput, "0");
|
|
180
|
+
await waitFor(()=>{
|
|
181
|
+
const saveButton = screen.getByRole("button", {
|
|
182
|
+
name: /save/i
|
|
183
|
+
});
|
|
184
|
+
expect(saveButton).toBeDisabled();
|
|
185
|
+
}, {
|
|
186
|
+
timeout: 3000
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
it("saves Object Lock configuration with Default Retention and navigates on success", async ()=>{
|
|
190
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
191
|
+
data: {
|
|
192
|
+
ObjectLockConfiguration: {
|
|
193
|
+
ObjectLockEnabled: "Enabled"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
status: "success"
|
|
197
|
+
});
|
|
198
|
+
renderObjectLockSettings();
|
|
199
|
+
await waitFor(()=>{
|
|
200
|
+
expect(screen.getByText("Default Retention")).toBeInTheDocument();
|
|
201
|
+
});
|
|
202
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
203
|
+
fireEvent.click(defaultRetentionCheckbox);
|
|
204
|
+
await waitFor(()=>{
|
|
205
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
206
|
+
expect(retentionPeriodInput).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
209
|
+
await user_event.clear(retentionPeriodInput);
|
|
210
|
+
await user_event.type(retentionPeriodInput, "30");
|
|
211
|
+
const governanceRadio = screen.getByLabelText(/governance/i);
|
|
212
|
+
fireEvent.click(governanceRadio);
|
|
213
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
214
|
+
options?.onSuccess?.();
|
|
215
|
+
});
|
|
216
|
+
const saveButton = screen.getByRole("button", {
|
|
217
|
+
name: /save/i
|
|
218
|
+
});
|
|
219
|
+
await waitFor(()=>expect(saveButton).toBeEnabled(), {
|
|
220
|
+
timeout: 3000
|
|
221
|
+
});
|
|
222
|
+
fireEvent.click(saveButton);
|
|
223
|
+
await waitFor(()=>{
|
|
224
|
+
expect(mockMutate).toHaveBeenCalled();
|
|
225
|
+
const callArgs = mockMutate.mock.calls[0][0];
|
|
226
|
+
expect(callArgs.Bucket).toBe("test-bucket");
|
|
227
|
+
expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
|
|
228
|
+
expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Mode).toBe("GOVERNANCE");
|
|
229
|
+
expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Days || callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Years).toBe(30);
|
|
230
|
+
expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
it("saves Object Lock configuration with Years frequency", async ()=>{
|
|
234
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
235
|
+
data: {
|
|
236
|
+
ObjectLockConfiguration: {
|
|
237
|
+
ObjectLockEnabled: "Enabled"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
status: "success"
|
|
241
|
+
});
|
|
242
|
+
renderObjectLockSettings();
|
|
243
|
+
await waitFor(()=>{
|
|
244
|
+
expect(screen.getByText("Default Retention")).toBeInTheDocument();
|
|
245
|
+
});
|
|
246
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
247
|
+
fireEvent.click(defaultRetentionCheckbox);
|
|
248
|
+
await waitFor(()=>{
|
|
249
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
250
|
+
expect(retentionPeriodInput).toBeInTheDocument();
|
|
251
|
+
});
|
|
252
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
253
|
+
await user_event.clear(retentionPeriodInput);
|
|
254
|
+
await user_event.type(retentionPeriodInput, "2");
|
|
255
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
256
|
+
options?.onSuccess?.();
|
|
257
|
+
});
|
|
258
|
+
const saveButton = screen.getByRole("button", {
|
|
259
|
+
name: /save/i
|
|
260
|
+
});
|
|
261
|
+
await waitFor(()=>expect(saveButton).toBeEnabled(), {
|
|
262
|
+
timeout: 3000
|
|
263
|
+
});
|
|
264
|
+
fireEvent.click(saveButton);
|
|
265
|
+
await waitFor(()=>{
|
|
266
|
+
expect(mockMutate).toHaveBeenCalled();
|
|
267
|
+
const callArgs = mockMutate.mock.calls[0][0];
|
|
268
|
+
expect(callArgs.Bucket).toBe("test-bucket");
|
|
269
|
+
expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
|
|
270
|
+
expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Mode).toBe("GOVERNANCE");
|
|
271
|
+
expect(callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Days || callArgs.ObjectLockConfiguration.Rule.DefaultRetention.Years).toBe(2);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
it("saves Object Lock configuration without Default Retention", async ()=>{
|
|
275
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
276
|
+
data: {
|
|
277
|
+
ObjectLockConfiguration: {
|
|
278
|
+
ObjectLockEnabled: "Enabled"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
status: "success"
|
|
282
|
+
});
|
|
283
|
+
renderObjectLockSettings();
|
|
284
|
+
await waitFor(()=>{
|
|
285
|
+
const saveButton = screen.getByRole("button", {
|
|
286
|
+
name: /save/i
|
|
287
|
+
});
|
|
288
|
+
expect(saveButton).toBeEnabled();
|
|
289
|
+
}, {
|
|
290
|
+
timeout: 3000
|
|
291
|
+
});
|
|
292
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
293
|
+
options?.onSuccess?.();
|
|
294
|
+
});
|
|
295
|
+
const saveButton = screen.getByRole("button", {
|
|
296
|
+
name: /save/i
|
|
297
|
+
});
|
|
298
|
+
fireEvent.click(saveButton);
|
|
299
|
+
await waitFor(()=>{
|
|
300
|
+
expect(mockMutate).toHaveBeenCalled();
|
|
301
|
+
const callArgs = mockMutate.mock.calls[0][0];
|
|
302
|
+
expect(callArgs.Bucket).toBe("test-bucket");
|
|
303
|
+
expect(callArgs.ObjectLockConfiguration.ObjectLockEnabled).toBe("Enabled");
|
|
304
|
+
expect(callArgs.ObjectLockConfiguration.Rule).toBeUndefined();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
it("displays error toast when save fails", async ()=>{
|
|
308
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
309
|
+
data: {
|
|
310
|
+
ObjectLockConfiguration: {
|
|
311
|
+
ObjectLockEnabled: "Enabled"
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
status: "success"
|
|
315
|
+
});
|
|
316
|
+
renderObjectLockSettings();
|
|
317
|
+
await waitFor(()=>{
|
|
318
|
+
const saveButton = screen.getByRole("button", {
|
|
319
|
+
name: /save/i
|
|
320
|
+
});
|
|
321
|
+
expect(saveButton).toBeEnabled();
|
|
322
|
+
}, {
|
|
323
|
+
timeout: 3000
|
|
324
|
+
});
|
|
325
|
+
const error = new Error("Access Denied");
|
|
326
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
327
|
+
options?.onError?.(error);
|
|
328
|
+
});
|
|
329
|
+
const saveButton = screen.getByRole("button", {
|
|
330
|
+
name: /save/i
|
|
331
|
+
});
|
|
332
|
+
fireEvent.click(saveButton);
|
|
333
|
+
await waitFor(()=>{
|
|
334
|
+
expect(mockNavigate).not.toHaveBeenCalled();
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
it("navigates back when cancel button is clicked", async ()=>{
|
|
338
|
+
renderObjectLockSettings();
|
|
339
|
+
await waitFor(()=>{
|
|
340
|
+
const cancelButton = screen.getByRole("button", {
|
|
341
|
+
name: /cancel/i
|
|
342
|
+
});
|
|
343
|
+
fireEvent.click(cancelButton);
|
|
344
|
+
});
|
|
345
|
+
expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
|
|
346
|
+
});
|
|
347
|
+
it("populates form with existing bucket configuration", async ()=>{
|
|
348
|
+
mockUseGetBucketObjectLockConfiguration.mockReturnValue({
|
|
349
|
+
data: {
|
|
350
|
+
ObjectLockConfiguration: {
|
|
351
|
+
ObjectLockEnabled: "Enabled",
|
|
352
|
+
Rule: {
|
|
353
|
+
DefaultRetention: {
|
|
354
|
+
Mode: "COMPLIANCE",
|
|
355
|
+
Days: 60
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
status: "success"
|
|
361
|
+
});
|
|
362
|
+
renderObjectLockSettings();
|
|
363
|
+
await waitFor(()=>{
|
|
364
|
+
const objectLockCheckbox = screen.getByLabelText(/object-lock/i);
|
|
365
|
+
expect(objectLockCheckbox).toBeChecked();
|
|
366
|
+
});
|
|
367
|
+
const defaultRetentionCheckbox = screen.getByLabelText(/default retention/i);
|
|
368
|
+
expect(defaultRetentionCheckbox).toBeChecked();
|
|
369
|
+
const complianceRadio = screen.getByLabelText(/compliance/i);
|
|
370
|
+
expect(complianceRadio).toBeChecked();
|
|
371
|
+
const retentionPeriodInput = screen.getByLabelText(/retention period/i);
|
|
372
|
+
expect(retentionPeriodInput).toHaveValue(60);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type ArrayFieldActionsProps = {
|
|
2
|
+
/**
|
|
3
|
+
* Callback when remove button is clicked
|
|
4
|
+
*/
|
|
5
|
+
onRemove: () => void;
|
|
6
|
+
/**
|
|
7
|
+
* Callback when add button is clicked
|
|
8
|
+
* Only called if showAdd is true
|
|
9
|
+
*/
|
|
10
|
+
onAdd: () => void;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the remove button should be enabled
|
|
13
|
+
*/
|
|
14
|
+
canRemove: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the add button should be enabled
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
canAdd?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to show the add button
|
|
22
|
+
* Typically true only for the last item in the array
|
|
23
|
+
*/
|
|
24
|
+
showAdd: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Aria label for remove button
|
|
27
|
+
* @default "Remove"
|
|
28
|
+
*/
|
|
29
|
+
removeLabel?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Aria label for add button
|
|
32
|
+
* @default "Add"
|
|
33
|
+
*/
|
|
34
|
+
addLabel?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function ArrayFieldActions({ onRemove, onAdd, canRemove, canAdd, showAdd, removeLabel, addLabel, }: ArrayFieldActionsProps): import("react/jsx-runtime").JSX.Element;
|