@scality/data-browser-library 1.0.0-preview.5 → 1.0.0-preview.7
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__/BucketNotificationCreatePage.test.d.ts +1 -0
- package/dist/components/__tests__/BucketNotificationCreatePage.test.js +316 -0
- package/dist/components/buckets/notifications/BucketNotificationCreatePage.d.ts +1 -0
- package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +234 -0
- package/dist/components/buckets/notifications/EventsSection.d.ts +1 -0
- package/dist/components/buckets/notifications/EventsSection.js +123 -0
- package/dist/components/buckets/notifications/events.d.ts +12 -0
- package/dist/components/buckets/notifications/events.js +27 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +2 -1
- package/dist/hooks/__tests__/useIsBucketEmpty.test.js +8 -8
- package/dist/hooks/useIsBucketEmpty.js +4 -4
- package/dist/test/testUtils.js +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/utils/s3Client.js +25 -24
- package/package.json +10 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,316 @@
|
|
|
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 { BucketNotificationCreatePage } from "../buckets/notifications/BucketNotificationCreatePage.js";
|
|
7
|
+
import { useGetBucketNotification, useSetBucketNotification } from "../../hooks/index.js";
|
|
8
|
+
jest.mock("../../hooks", ()=>({
|
|
9
|
+
useGetBucketNotification: jest.fn(),
|
|
10
|
+
useSetBucketNotification: jest.fn()
|
|
11
|
+
}));
|
|
12
|
+
const mockUseGetBucketNotification = jest.mocked(useGetBucketNotification);
|
|
13
|
+
const mockUseSetBucketNotification = jest.mocked(useSetBucketNotification);
|
|
14
|
+
const mockNavigate = jest.fn();
|
|
15
|
+
jest.mock("react-router-dom", ()=>({
|
|
16
|
+
...jest.requireActual("react-router-dom"),
|
|
17
|
+
useNavigate: ()=>mockNavigate
|
|
18
|
+
}));
|
|
19
|
+
const renderBucketNotificationCreatePage = (bucketName = "test-bucket")=>{
|
|
20
|
+
const Wrapper = createTestWrapper();
|
|
21
|
+
return render(/*#__PURE__*/ jsx(MemoryRouter, {
|
|
22
|
+
initialEntries: [
|
|
23
|
+
`/buckets/${bucketName}/notifications/create`
|
|
24
|
+
],
|
|
25
|
+
children: /*#__PURE__*/ jsx(Wrapper, {
|
|
26
|
+
children: /*#__PURE__*/ jsx(Routes, {
|
|
27
|
+
children: /*#__PURE__*/ jsx(Route, {
|
|
28
|
+
path: "/buckets/:bucketName/notifications/create",
|
|
29
|
+
element: /*#__PURE__*/ jsx(BucketNotificationCreatePage, {})
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
describe("BucketNotificationCreatePage", ()=>{
|
|
36
|
+
const mockMutate = jest.fn();
|
|
37
|
+
beforeEach(()=>{
|
|
38
|
+
jest.clearAllMocks();
|
|
39
|
+
mockNavigate.mockClear();
|
|
40
|
+
mockUseGetBucketNotification.mockReturnValue({
|
|
41
|
+
data: void 0
|
|
42
|
+
});
|
|
43
|
+
mockUseSetBucketNotification.mockReturnValue({
|
|
44
|
+
mutate: mockMutate,
|
|
45
|
+
isPending: false
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
it("renders create notification form with all fields", ()=>{
|
|
49
|
+
renderBucketNotificationCreatePage();
|
|
50
|
+
expect(screen.getByText("Create Bucket Notification")).toBeInTheDocument();
|
|
51
|
+
expect(screen.getByLabelText(/rule name/i)).toBeInTheDocument();
|
|
52
|
+
expect(screen.getByText("Events")).toBeInTheDocument();
|
|
53
|
+
expect(screen.getByLabelText(/destination queue/i)).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByLabelText(/prefix/i)).toBeInTheDocument();
|
|
55
|
+
expect(screen.getByLabelText(/suffix/i)).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
it("disables create button when form is pristine", ()=>{
|
|
58
|
+
renderBucketNotificationCreatePage();
|
|
59
|
+
expect(screen.getByRole("button", {
|
|
60
|
+
name: /create/i
|
|
61
|
+
})).toBeDisabled();
|
|
62
|
+
});
|
|
63
|
+
it("shows validation error when rule name is empty", async ()=>{
|
|
64
|
+
renderBucketNotificationCreatePage();
|
|
65
|
+
const ruleNameInput = screen.getByLabelText(/rule name/i);
|
|
66
|
+
await user_event.type(ruleNameInput, "a");
|
|
67
|
+
await user_event.clear(ruleNameInput);
|
|
68
|
+
await waitFor(()=>{
|
|
69
|
+
expect(screen.getByText(/this field is required/i)).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
it("shows validation error when rule name already exists", async ()=>{
|
|
73
|
+
const existingConfig = {
|
|
74
|
+
QueueConfigurations: [
|
|
75
|
+
{
|
|
76
|
+
Id: "existing-rule",
|
|
77
|
+
QueueArn: "arn:aws:sqs:us-east-1:123:queue",
|
|
78
|
+
Events: [
|
|
79
|
+
"s3:ObjectCreated:*"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
mockUseGetBucketNotification.mockReturnValue({
|
|
85
|
+
data: existingConfig
|
|
86
|
+
});
|
|
87
|
+
renderBucketNotificationCreatePage();
|
|
88
|
+
const ruleNameInput = screen.getByLabelText(/rule name/i);
|
|
89
|
+
await user_event.type(ruleNameInput, "existing-rule");
|
|
90
|
+
await waitFor(()=>{
|
|
91
|
+
expect(screen.getByText(/a rule with this name already exists/i)).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
it("shows validation error when queue ARN is invalid", async ()=>{
|
|
95
|
+
renderBucketNotificationCreatePage();
|
|
96
|
+
const queueArnInput = screen.getByLabelText(/destination queue/i);
|
|
97
|
+
await user_event.type(queueArnInput, "invalid-arn");
|
|
98
|
+
await waitFor(()=>{
|
|
99
|
+
expect(screen.getByText(/must be a valid arn/i)).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
it("shows validation error when no event is selected", async ()=>{
|
|
103
|
+
renderBucketNotificationCreatePage();
|
|
104
|
+
const ruleNameInput = screen.getByLabelText(/rule name/i);
|
|
105
|
+
await user_event.type(ruleNameInput, "test-rule");
|
|
106
|
+
const queueArnInput = screen.getByLabelText(/destination queue/i);
|
|
107
|
+
await user_event.type(queueArnInput, "arn:aws:sqs:us-east-1:123:queue");
|
|
108
|
+
await waitFor(()=>{
|
|
109
|
+
expect(screen.getByRole("button", {
|
|
110
|
+
name: /create/i
|
|
111
|
+
})).toBeDisabled();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
it("enables create button when form is valid", async ()=>{
|
|
115
|
+
renderBucketNotificationCreatePage();
|
|
116
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "test-rule");
|
|
117
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:queue");
|
|
118
|
+
const checkbox = screen.getByRole("checkbox", {
|
|
119
|
+
name: /s3:ObjectCreated:\*/i
|
|
120
|
+
});
|
|
121
|
+
fireEvent.click(checkbox);
|
|
122
|
+
await waitFor(()=>{
|
|
123
|
+
expect(screen.getByRole("button", {
|
|
124
|
+
name: /create/i
|
|
125
|
+
})).toBeEnabled();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it("creates notification with all fields and navigates on success", async ()=>{
|
|
129
|
+
renderBucketNotificationCreatePage();
|
|
130
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "test-rule");
|
|
131
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:my-queue");
|
|
132
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
133
|
+
name: /s3:ObjectCreated:Put/i
|
|
134
|
+
}));
|
|
135
|
+
await user_event.type(screen.getByLabelText(/prefix/i), "uploads/");
|
|
136
|
+
await user_event.type(screen.getByLabelText(/suffix/i), ".jpg");
|
|
137
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
138
|
+
options?.onSuccess?.();
|
|
139
|
+
});
|
|
140
|
+
const createButton = screen.getByRole("button", {
|
|
141
|
+
name: /create/i
|
|
142
|
+
});
|
|
143
|
+
await waitFor(()=>expect(createButton).toBeEnabled());
|
|
144
|
+
fireEvent.click(createButton);
|
|
145
|
+
await waitFor(()=>{
|
|
146
|
+
expect(mockMutate).toHaveBeenCalledWith({
|
|
147
|
+
Bucket: "test-bucket",
|
|
148
|
+
NotificationConfiguration: {
|
|
149
|
+
QueueConfigurations: [
|
|
150
|
+
{
|
|
151
|
+
Id: "test-rule",
|
|
152
|
+
QueueArn: "arn:aws:sqs:us-east-1:123:my-queue",
|
|
153
|
+
Events: [
|
|
154
|
+
"s3:ObjectCreated:Put"
|
|
155
|
+
],
|
|
156
|
+
Filter: {
|
|
157
|
+
Key: {
|
|
158
|
+
FilterRules: [
|
|
159
|
+
{
|
|
160
|
+
Name: "prefix",
|
|
161
|
+
Value: "uploads/"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
Name: "suffix",
|
|
165
|
+
Value: ".jpg"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
}, expect.any(Object));
|
|
174
|
+
expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
it("creates notification without filters when not provided", async ()=>{
|
|
178
|
+
renderBucketNotificationCreatePage();
|
|
179
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "test-rule");
|
|
180
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:my-queue");
|
|
181
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
182
|
+
name: /s3:ObjectRemoved:\*/i
|
|
183
|
+
}));
|
|
184
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
185
|
+
options?.onSuccess?.();
|
|
186
|
+
});
|
|
187
|
+
const createButton = screen.getByRole("button", {
|
|
188
|
+
name: /create/i
|
|
189
|
+
});
|
|
190
|
+
await waitFor(()=>expect(createButton).toBeEnabled());
|
|
191
|
+
fireEvent.click(createButton);
|
|
192
|
+
await waitFor(()=>{
|
|
193
|
+
expect(mockMutate).toHaveBeenCalledWith({
|
|
194
|
+
Bucket: "test-bucket",
|
|
195
|
+
NotificationConfiguration: {
|
|
196
|
+
QueueConfigurations: [
|
|
197
|
+
{
|
|
198
|
+
Id: "test-rule",
|
|
199
|
+
QueueArn: "arn:aws:sqs:us-east-1:123:my-queue",
|
|
200
|
+
Events: [
|
|
201
|
+
"s3:ObjectRemoved:*"
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
}, expect.any(Object));
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
it("preserves existing queue configurations when creating new notification", async ()=>{
|
|
210
|
+
const existingConfig = {
|
|
211
|
+
QueueConfigurations: [
|
|
212
|
+
{
|
|
213
|
+
Id: "existing-rule",
|
|
214
|
+
QueueArn: "arn:aws:sqs:us-east-1:123:existing-queue",
|
|
215
|
+
Events: [
|
|
216
|
+
"s3:ObjectCreated:*"
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
mockUseGetBucketNotification.mockReturnValue({
|
|
222
|
+
data: existingConfig
|
|
223
|
+
});
|
|
224
|
+
renderBucketNotificationCreatePage();
|
|
225
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "new-rule");
|
|
226
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:new-queue");
|
|
227
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
228
|
+
name: /^s3:ObjectRemoved:Delete$/i
|
|
229
|
+
}));
|
|
230
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
231
|
+
options?.onSuccess?.();
|
|
232
|
+
});
|
|
233
|
+
const createButton = screen.getByRole("button", {
|
|
234
|
+
name: /create/i
|
|
235
|
+
});
|
|
236
|
+
await waitFor(()=>expect(createButton).toBeEnabled());
|
|
237
|
+
fireEvent.click(createButton);
|
|
238
|
+
await waitFor(()=>{
|
|
239
|
+
const call = mockMutate.mock.calls[0][0];
|
|
240
|
+
expect(call.NotificationConfiguration.QueueConfigurations).toHaveLength(2);
|
|
241
|
+
expect(call.NotificationConfiguration.QueueConfigurations[0]).toEqual(existingConfig.QueueConfigurations[0]);
|
|
242
|
+
expect(call.NotificationConfiguration.QueueConfigurations[1]).toMatchObject({
|
|
243
|
+
Id: "new-rule",
|
|
244
|
+
QueueArn: "arn:aws:sqs:us-east-1:123:new-queue",
|
|
245
|
+
Events: [
|
|
246
|
+
"s3:ObjectRemoved:Delete"
|
|
247
|
+
]
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
it("displays error message when creation fails", async ()=>{
|
|
252
|
+
renderBucketNotificationCreatePage();
|
|
253
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "test-rule");
|
|
254
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:my-queue");
|
|
255
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
256
|
+
name: /s3:ObjectCreated:\*/i
|
|
257
|
+
}));
|
|
258
|
+
const error = new Error("Access Denied");
|
|
259
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
260
|
+
options?.onError?.(error);
|
|
261
|
+
});
|
|
262
|
+
const createButton = screen.getByRole("button", {
|
|
263
|
+
name: /create/i
|
|
264
|
+
});
|
|
265
|
+
await waitFor(()=>expect(createButton).toBeEnabled());
|
|
266
|
+
fireEvent.click(createButton);
|
|
267
|
+
await waitFor(()=>{
|
|
268
|
+
expect(mockNavigate).not.toHaveBeenCalled();
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
it("handles cancel button click and navigates back", ()=>{
|
|
272
|
+
renderBucketNotificationCreatePage();
|
|
273
|
+
const cancelButton = screen.getByRole("button", {
|
|
274
|
+
name: /cancel/i
|
|
275
|
+
});
|
|
276
|
+
fireEvent.click(cancelButton);
|
|
277
|
+
expect(mockNavigate).toHaveBeenCalledWith("/buckets/test-bucket");
|
|
278
|
+
});
|
|
279
|
+
it("allows selecting multiple events", async ()=>{
|
|
280
|
+
renderBucketNotificationCreatePage();
|
|
281
|
+
await user_event.type(screen.getByLabelText(/rule name/i), "multi-event");
|
|
282
|
+
await user_event.type(screen.getByLabelText(/destination queue/i), "arn:aws:sqs:us-east-1:123:queue");
|
|
283
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
284
|
+
name: /^s3:ObjectCreated:Put$/i
|
|
285
|
+
}));
|
|
286
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
287
|
+
name: /^s3:ObjectCreated:Copy$/i
|
|
288
|
+
}));
|
|
289
|
+
fireEvent.click(screen.getByRole("checkbox", {
|
|
290
|
+
name: /^s3:ObjectRemoved:Delete$/i
|
|
291
|
+
}));
|
|
292
|
+
mockMutate.mockImplementation((_, options)=>{
|
|
293
|
+
options?.onSuccess?.();
|
|
294
|
+
});
|
|
295
|
+
const createButton = screen.getByRole("button", {
|
|
296
|
+
name: /create/i
|
|
297
|
+
});
|
|
298
|
+
await waitFor(()=>expect(createButton).toBeEnabled());
|
|
299
|
+
fireEvent.click(createButton);
|
|
300
|
+
await waitFor(()=>{
|
|
301
|
+
expect(mockMutate).toHaveBeenCalledWith(expect.objectContaining({
|
|
302
|
+
NotificationConfiguration: {
|
|
303
|
+
QueueConfigurations: [
|
|
304
|
+
expect.objectContaining({
|
|
305
|
+
Events: expect.arrayContaining([
|
|
306
|
+
"s3:ObjectCreated:Put",
|
|
307
|
+
"s3:ObjectCreated:Copy",
|
|
308
|
+
"s3:ObjectRemoved:Delete"
|
|
309
|
+
])
|
|
310
|
+
})
|
|
311
|
+
]
|
|
312
|
+
}
|
|
313
|
+
}), expect.any(Object));
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function BucketNotificationCreatePage(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { joiResolver } from "@hookform/resolvers/joi";
|
|
3
|
+
import { Form, FormGroup, FormSection, Loader, Stack, Text, useToast } from "@scality/core-ui";
|
|
4
|
+
import { convertRemToPixels } from "@scality/core-ui/dist/components/tablev2/TableUtils";
|
|
5
|
+
import { Button, Input } from "@scality/core-ui/dist/next";
|
|
6
|
+
import { array, object, string } from "joi";
|
|
7
|
+
import { useCallback } from "react";
|
|
8
|
+
import { FormProvider, useForm } from "react-hook-form";
|
|
9
|
+
import { useNavigate, useParams } from "react-router-dom";
|
|
10
|
+
import { useGetBucketNotification, useSetBucketNotification } from "../../../hooks/index.js";
|
|
11
|
+
import { EventsSection } from "./EventsSection.js";
|
|
12
|
+
const schema = object({
|
|
13
|
+
ruleName: string().required().messages({
|
|
14
|
+
"string.empty": "This field is required"
|
|
15
|
+
}),
|
|
16
|
+
queueArn: string().required().pattern(/^arn:aws:sqs:[a-z0-9-]+:\d+:.+$/).messages({
|
|
17
|
+
"string.empty": "This field is required",
|
|
18
|
+
"string.pattern.base": "Must be a valid ARN (e.g., arn:aws:sqs:region:account-id:queue-name)"
|
|
19
|
+
}),
|
|
20
|
+
events: array().min(1).required().messages({
|
|
21
|
+
"array.min": "At least one event must be selected"
|
|
22
|
+
}),
|
|
23
|
+
prefix: string().allow("").optional(),
|
|
24
|
+
suffix: string().allow("").optional()
|
|
25
|
+
});
|
|
26
|
+
function BucketNotificationCreatePage() {
|
|
27
|
+
const { bucketName } = useParams();
|
|
28
|
+
const navigate = useNavigate();
|
|
29
|
+
const { showToast } = useToast();
|
|
30
|
+
const { data: existingNotificationData, status: notificationStatus } = useGetBucketNotification({
|
|
31
|
+
Bucket: bucketName
|
|
32
|
+
});
|
|
33
|
+
const existingRuleNames = existingNotificationData?.QueueConfigurations?.map((config)=>config.Id) || [];
|
|
34
|
+
const dynamicSchema = schema.keys({
|
|
35
|
+
ruleName: string().required().invalid(...existingRuleNames).messages({
|
|
36
|
+
"string.empty": "This field is required",
|
|
37
|
+
"any.invalid": "A rule with this name already exists"
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
const methods = useForm({
|
|
41
|
+
resolver: joiResolver(dynamicSchema),
|
|
42
|
+
mode: "onChange",
|
|
43
|
+
defaultValues: {
|
|
44
|
+
ruleName: "",
|
|
45
|
+
queueArn: "",
|
|
46
|
+
events: [],
|
|
47
|
+
prefix: "",
|
|
48
|
+
suffix: ""
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const { mutate: setNotification, isPending: isSaving } = useSetBucketNotification();
|
|
52
|
+
const { handleSubmit, register, formState: { isValid, isDirty, errors } } = methods;
|
|
53
|
+
const handleCancel = useCallback(()=>{
|
|
54
|
+
navigate(`/buckets/${bucketName}`);
|
|
55
|
+
}, [
|
|
56
|
+
navigate,
|
|
57
|
+
bucketName
|
|
58
|
+
]);
|
|
59
|
+
const onSubmit = useCallback((data)=>{
|
|
60
|
+
if (!bucketName) return;
|
|
61
|
+
const filterRules = [
|
|
62
|
+
...data.prefix ? [
|
|
63
|
+
{
|
|
64
|
+
Name: "prefix",
|
|
65
|
+
Value: data.prefix
|
|
66
|
+
}
|
|
67
|
+
] : [],
|
|
68
|
+
...data.suffix ? [
|
|
69
|
+
{
|
|
70
|
+
Name: "suffix",
|
|
71
|
+
Value: data.suffix
|
|
72
|
+
}
|
|
73
|
+
] : []
|
|
74
|
+
];
|
|
75
|
+
const filteredEvents = data.events.filter((event)=>{
|
|
76
|
+
if (event.endsWith("*")) return true;
|
|
77
|
+
if (event.startsWith("s3:ObjectCreated:")) return !data.events.includes("s3:ObjectCreated:*");
|
|
78
|
+
if (event.startsWith("s3:ObjectRemoved:")) return !data.events.includes("s3:ObjectRemoved:*");
|
|
79
|
+
if (event.startsWith("s3:LifecycleExpiration:")) return !data.events.includes("s3:LifecycleExpiration:*");
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
const newQueueConfiguration = {
|
|
83
|
+
Id: data.ruleName,
|
|
84
|
+
QueueArn: data.queueArn,
|
|
85
|
+
Events: filteredEvents,
|
|
86
|
+
...filterRules.length > 0 && {
|
|
87
|
+
Filter: {
|
|
88
|
+
Key: {
|
|
89
|
+
FilterRules: filterRules
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
setNotification({
|
|
95
|
+
Bucket: bucketName,
|
|
96
|
+
NotificationConfiguration: {
|
|
97
|
+
QueueConfigurations: [
|
|
98
|
+
...existingNotificationData?.QueueConfigurations || [],
|
|
99
|
+
newQueueConfiguration
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
}, {
|
|
103
|
+
onSuccess: ()=>{
|
|
104
|
+
showToast({
|
|
105
|
+
open: true,
|
|
106
|
+
message: "Notification created successfully",
|
|
107
|
+
status: "success"
|
|
108
|
+
});
|
|
109
|
+
navigate(`/buckets/${bucketName}`);
|
|
110
|
+
},
|
|
111
|
+
onError: (error)=>{
|
|
112
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to create notification";
|
|
113
|
+
showToast({
|
|
114
|
+
open: true,
|
|
115
|
+
message: errorMessage,
|
|
116
|
+
status: "error"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}, [
|
|
121
|
+
bucketName,
|
|
122
|
+
setNotification,
|
|
123
|
+
navigate,
|
|
124
|
+
showToast,
|
|
125
|
+
existingNotificationData
|
|
126
|
+
]);
|
|
127
|
+
if ("pending" === notificationStatus) return /*#__PURE__*/ jsx(Loader, {
|
|
128
|
+
centered: true,
|
|
129
|
+
children: /*#__PURE__*/ jsx(Text, {
|
|
130
|
+
children: "Loading..."
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
return /*#__PURE__*/ jsx(FormProvider, {
|
|
134
|
+
...methods,
|
|
135
|
+
children: /*#__PURE__*/ jsxs(Form, {
|
|
136
|
+
layout: {
|
|
137
|
+
kind: "page",
|
|
138
|
+
title: "Create Bucket Notification"
|
|
139
|
+
},
|
|
140
|
+
requireMode: "partial",
|
|
141
|
+
onSubmit: handleSubmit(onSubmit),
|
|
142
|
+
rightActions: /*#__PURE__*/ jsxs(Stack, {
|
|
143
|
+
gap: "r16",
|
|
144
|
+
children: [
|
|
145
|
+
/*#__PURE__*/ jsx(Button, {
|
|
146
|
+
id: "cancel-btn",
|
|
147
|
+
variant: "outline",
|
|
148
|
+
type: "button",
|
|
149
|
+
label: "Cancel",
|
|
150
|
+
onClick: handleCancel,
|
|
151
|
+
disabled: isSaving
|
|
152
|
+
}),
|
|
153
|
+
/*#__PURE__*/ jsx(Button, {
|
|
154
|
+
id: "create-notification-btn",
|
|
155
|
+
type: "submit",
|
|
156
|
+
variant: "primary",
|
|
157
|
+
label: "Create",
|
|
158
|
+
disabled: !isDirty || !isValid || isSaving
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
}),
|
|
162
|
+
children: [
|
|
163
|
+
/*#__PURE__*/ jsx(FormSection, {
|
|
164
|
+
forceLabelWidth: convertRemToPixels(13),
|
|
165
|
+
children: /*#__PURE__*/ jsx(FormGroup, {
|
|
166
|
+
label: "Rule name",
|
|
167
|
+
id: "ruleName",
|
|
168
|
+
direction: "horizontal",
|
|
169
|
+
error: errors?.ruleName?.message,
|
|
170
|
+
helpErrorPosition: "bottom",
|
|
171
|
+
labelHelpTooltip: /*#__PURE__*/ jsx(Fragment, {}),
|
|
172
|
+
required: true,
|
|
173
|
+
content: /*#__PURE__*/ jsx(Input, {
|
|
174
|
+
id: "ruleName",
|
|
175
|
+
...register("ruleName")
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
}),
|
|
179
|
+
/*#__PURE__*/ jsx(EventsSection, {}),
|
|
180
|
+
/*#__PURE__*/ jsx(FormSection, {
|
|
181
|
+
forceLabelWidth: convertRemToPixels(13),
|
|
182
|
+
children: /*#__PURE__*/ jsx(FormGroup, {
|
|
183
|
+
label: "Destination Queue",
|
|
184
|
+
id: "queueArn",
|
|
185
|
+
direction: "horizontal",
|
|
186
|
+
error: errors?.queueArn?.message,
|
|
187
|
+
helpErrorPosition: "bottom",
|
|
188
|
+
labelHelpTooltip: /*#__PURE__*/ jsx(Fragment, {}),
|
|
189
|
+
required: true,
|
|
190
|
+
content: /*#__PURE__*/ jsx(Input, {
|
|
191
|
+
id: "queueArn",
|
|
192
|
+
placeholder: "arn:aws:sqs:us-east-1:1xx:name",
|
|
193
|
+
...register("queueArn")
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
}),
|
|
197
|
+
/*#__PURE__*/ jsx(FormSection, {
|
|
198
|
+
title: {
|
|
199
|
+
name: "Filters"
|
|
200
|
+
},
|
|
201
|
+
children: /*#__PURE__*/ jsxs(Stack, {
|
|
202
|
+
direction: "horizontal",
|
|
203
|
+
gap: "r16",
|
|
204
|
+
children: [
|
|
205
|
+
/*#__PURE__*/ jsx(FormGroup, {
|
|
206
|
+
label: "Prefix",
|
|
207
|
+
id: "prefix",
|
|
208
|
+
direction: "horizontal",
|
|
209
|
+
content: /*#__PURE__*/ jsx(Input, {
|
|
210
|
+
id: "prefix",
|
|
211
|
+
placeholder: "",
|
|
212
|
+
size: "2/3",
|
|
213
|
+
...register("prefix")
|
|
214
|
+
})
|
|
215
|
+
}),
|
|
216
|
+
/*#__PURE__*/ jsx(FormGroup, {
|
|
217
|
+
label: "Suffix",
|
|
218
|
+
id: "suffix",
|
|
219
|
+
direction: "horizontal",
|
|
220
|
+
content: /*#__PURE__*/ jsx(Input, {
|
|
221
|
+
id: "suffix",
|
|
222
|
+
placeholder: "",
|
|
223
|
+
size: "2/3",
|
|
224
|
+
...register("suffix")
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
]
|
|
231
|
+
})
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
export { BucketNotificationCreatePage };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function EventsSection(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Checkbox, FormGroup, FormSection, Stack, Text, Wrap } from "@scality/core-ui";
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { Controller, useFormContext } from "react-hook-form";
|
|
5
|
+
import { objectCreationEvents, objectDeletionEvents, othersEvents } from "./events.js";
|
|
6
|
+
import { Box } from "@scality/core-ui/dist/next";
|
|
7
|
+
function EventsSection() {
|
|
8
|
+
const { control, formState: { errors } } = useFormContext();
|
|
9
|
+
const toggleEvent = useCallback((currentEvents, eventValue, onChange)=>{
|
|
10
|
+
const isWildcard = eventValue.endsWith("*");
|
|
11
|
+
const isSelected = currentEvents.includes(eventValue);
|
|
12
|
+
if (isWildcard) if (isSelected) {
|
|
13
|
+
const relatedEvents = eventValue.startsWith("s3:ObjectCreated") ? objectCreationEvents.events : eventValue.startsWith("s3:ObjectRemoved") ? objectDeletionEvents.events : eventValue.startsWith("s3:LifecycleExpiration") ? othersEvents.events.filter((e)=>e.startsWith("s3:LifecycleExpiration")) : [
|
|
14
|
+
eventValue
|
|
15
|
+
];
|
|
16
|
+
onChange(currentEvents.filter((e)=>!relatedEvents.includes(e)));
|
|
17
|
+
} else {
|
|
18
|
+
const relatedEvents = eventValue.startsWith("s3:ObjectCreated") ? objectCreationEvents.events : eventValue.startsWith("s3:ObjectRemoved") ? objectDeletionEvents.events : eventValue.startsWith("s3:LifecycleExpiration") ? othersEvents.events.filter((e)=>e.startsWith("s3:LifecycleExpiration")) : [
|
|
19
|
+
eventValue
|
|
20
|
+
];
|
|
21
|
+
const newEvents = [
|
|
22
|
+
...currentEvents.filter((e)=>!relatedEvents.includes(e)),
|
|
23
|
+
...relatedEvents
|
|
24
|
+
];
|
|
25
|
+
onChange(newEvents);
|
|
26
|
+
}
|
|
27
|
+
else onChange(isSelected ? currentEvents.filter((e)=>e !== eventValue) : [
|
|
28
|
+
...currentEvents,
|
|
29
|
+
eventValue
|
|
30
|
+
]);
|
|
31
|
+
}, []);
|
|
32
|
+
const isEventDisabled = (event, selectedEvents)=>{
|
|
33
|
+
if (event.endsWith("*")) return false;
|
|
34
|
+
if (event.startsWith("s3:ObjectCreated")) return selectedEvents.includes("s3:ObjectCreated:*");
|
|
35
|
+
if (event.startsWith("s3:ObjectRemoved")) return selectedEvents.includes("s3:ObjectRemoved:*");
|
|
36
|
+
if (event.startsWith("s3:LifecycleExpiration")) return selectedEvents.includes("s3:LifecycleExpiration:*");
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
return /*#__PURE__*/ jsxs(FormSection, {
|
|
40
|
+
children: [
|
|
41
|
+
/*#__PURE__*/ jsx(Wrap, {
|
|
42
|
+
children: /*#__PURE__*/ jsx(Box, {
|
|
43
|
+
display: "flex",
|
|
44
|
+
flexDirection: "row",
|
|
45
|
+
gap: "r8",
|
|
46
|
+
children: /*#__PURE__*/ jsx(Text, {
|
|
47
|
+
isEmphazed: true,
|
|
48
|
+
color: "textPrimary",
|
|
49
|
+
children: "Events"
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}),
|
|
53
|
+
/*#__PURE__*/ jsx(FormGroup, {
|
|
54
|
+
id: "events",
|
|
55
|
+
label: "",
|
|
56
|
+
direction: "vertical",
|
|
57
|
+
error: errors?.events?.message,
|
|
58
|
+
content: /*#__PURE__*/ jsx(Controller, {
|
|
59
|
+
control: control,
|
|
60
|
+
name: "events",
|
|
61
|
+
render: ({ field: { value, onChange } })=>/*#__PURE__*/ jsxs(Stack, {
|
|
62
|
+
direction: "vertical",
|
|
63
|
+
gap: "r16",
|
|
64
|
+
children: [
|
|
65
|
+
/*#__PURE__*/ jsxs(Stack, {
|
|
66
|
+
direction: "vertical",
|
|
67
|
+
gap: "r8",
|
|
68
|
+
children: [
|
|
69
|
+
/*#__PURE__*/ jsx(Text, {
|
|
70
|
+
color: "textPrimary",
|
|
71
|
+
children: objectCreationEvents.label
|
|
72
|
+
}),
|
|
73
|
+
objectCreationEvents.events.map((event)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
74
|
+
id: event,
|
|
75
|
+
label: event,
|
|
76
|
+
checked: value.includes(event),
|
|
77
|
+
disabled: isEventDisabled(event, value),
|
|
78
|
+
onChange: ()=>toggleEvent(value, event, onChange)
|
|
79
|
+
}, event))
|
|
80
|
+
]
|
|
81
|
+
}),
|
|
82
|
+
/*#__PURE__*/ jsxs(Stack, {
|
|
83
|
+
direction: "vertical",
|
|
84
|
+
gap: "r8",
|
|
85
|
+
children: [
|
|
86
|
+
/*#__PURE__*/ jsx(Text, {
|
|
87
|
+
color: "textPrimary",
|
|
88
|
+
children: objectDeletionEvents.label
|
|
89
|
+
}),
|
|
90
|
+
objectDeletionEvents.events.map((event)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
91
|
+
id: event,
|
|
92
|
+
label: event,
|
|
93
|
+
checked: value.includes(event),
|
|
94
|
+
disabled: isEventDisabled(event, value),
|
|
95
|
+
onChange: ()=>toggleEvent(value, event, onChange)
|
|
96
|
+
}, event))
|
|
97
|
+
]
|
|
98
|
+
}),
|
|
99
|
+
/*#__PURE__*/ jsxs(Stack, {
|
|
100
|
+
direction: "vertical",
|
|
101
|
+
gap: "r8",
|
|
102
|
+
children: [
|
|
103
|
+
/*#__PURE__*/ jsx(Text, {
|
|
104
|
+
color: "textPrimary",
|
|
105
|
+
children: othersEvents.label
|
|
106
|
+
}),
|
|
107
|
+
othersEvents.events.map((event)=>/*#__PURE__*/ jsx(Checkbox, {
|
|
108
|
+
id: event,
|
|
109
|
+
label: event,
|
|
110
|
+
checked: value.includes(event),
|
|
111
|
+
disabled: isEventDisabled(event, value),
|
|
112
|
+
onChange: ()=>toggleEvent(value, event, onChange)
|
|
113
|
+
}, event))
|
|
114
|
+
]
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export { EventsSection };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const objectCreationEvents: {
|
|
2
|
+
label: string;
|
|
3
|
+
events: string[];
|
|
4
|
+
};
|
|
5
|
+
export declare const objectDeletionEvents: {
|
|
6
|
+
label: string;
|
|
7
|
+
events: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare const othersEvents: {
|
|
10
|
+
label: string;
|
|
11
|
+
events: string[];
|
|
12
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const objectCreationEvents = {
|
|
2
|
+
label: "Object creation",
|
|
3
|
+
events: [
|
|
4
|
+
"s3:ObjectCreated:*",
|
|
5
|
+
"s3:ObjectCreated:Put",
|
|
6
|
+
"s3:ObjectCreated:Copy",
|
|
7
|
+
"s3:ObjectCreated:CompleteMultipartUpload"
|
|
8
|
+
]
|
|
9
|
+
};
|
|
10
|
+
const objectDeletionEvents = {
|
|
11
|
+
label: "Object deletion",
|
|
12
|
+
events: [
|
|
13
|
+
"s3:ObjectRemoved:*",
|
|
14
|
+
"s3:ObjectRemoved:Delete",
|
|
15
|
+
"s3:ObjectRemoved:DeleteMarkerCreated"
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
const othersEvents = {
|
|
19
|
+
label: "Others",
|
|
20
|
+
events: [
|
|
21
|
+
"s3:Replication:OperationFailedReplication",
|
|
22
|
+
"s3:LifecycleExpiration:*",
|
|
23
|
+
"s3:LifecycleExpiration:DeleteMarkerCreated",
|
|
24
|
+
"s3:LifecycleExpiration:Delete"
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
export { objectCreationEvents, objectDeletionEvents, othersEvents };
|
|
@@ -3,6 +3,7 @@ export { BucketList } from "./buckets/BucketList";
|
|
|
3
3
|
export { BucketOverview } from "./buckets/BucketOverview";
|
|
4
4
|
export { BucketPage } from "./buckets/BucketPage";
|
|
5
5
|
export { BucketPolicyPage } from "./buckets/BucketPolicyPage";
|
|
6
|
+
export { BucketNotificationCreatePage } from "./buckets/notifications/BucketNotificationCreatePage";
|
|
6
7
|
export { UploadButton } from "./objects/UploadButton";
|
|
7
8
|
export { CreateFolderButton } from "./objects/CreateFolderButton";
|
|
8
9
|
export { ObjectDetails } from "./objects/ObjectDetails";
|
package/dist/components/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { BucketList } from "./buckets/BucketList.js";
|
|
|
3
3
|
import { BucketOverview } from "./buckets/BucketOverview.js";
|
|
4
4
|
import { BucketPage } from "./buckets/BucketPage.js";
|
|
5
5
|
import { BucketPolicyPage } from "./buckets/BucketPolicyPage.js";
|
|
6
|
+
import { BucketNotificationCreatePage } from "./buckets/notifications/BucketNotificationCreatePage.js";
|
|
6
7
|
import { UploadButton } from "./objects/UploadButton.js";
|
|
7
8
|
import { CreateFolderButton } from "./objects/CreateFolderButton.js";
|
|
8
9
|
import { ObjectDetails } from "./objects/ObjectDetails/index.js";
|
|
@@ -10,4 +11,4 @@ import { ObjectList } from "./objects/ObjectList.js";
|
|
|
10
11
|
import { ObjectPage } from "./objects/ObjectPage.js";
|
|
11
12
|
import { MetadataSearch } from "./search/MetadataSearch.js";
|
|
12
13
|
import { DataBrowserProvider, useDataBrowserContext, useDataBrowserTheme } from "./providers/DataBrowserProvider.js";
|
|
13
|
-
export { BucketList, BucketOverview, BucketPage, BucketPolicyPage, CreateFolderButton, DataBrowserProvider, DeleteBucketButton, MetadataSearch, ObjectDetails, ObjectList, ObjectPage, UploadButton, useDataBrowserContext, useDataBrowserTheme };
|
|
14
|
+
export { BucketList, BucketNotificationCreatePage, BucketOverview, BucketPage, BucketPolicyPage, CreateFolderButton, DataBrowserProvider, DeleteBucketButton, MetadataSearch, ObjectDetails, ObjectList, ObjectPage, UploadButton, useDataBrowserContext, useDataBrowserTheme };
|
|
@@ -23,8 +23,8 @@ describe("useIsBucketEmpty", ()=>{
|
|
|
23
23
|
data: {
|
|
24
24
|
pages: [
|
|
25
25
|
{
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
Contents: [],
|
|
27
|
+
CommonPrefixes: []
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
},
|
|
@@ -40,12 +40,12 @@ describe("useIsBucketEmpty", ()=>{
|
|
|
40
40
|
data: {
|
|
41
41
|
pages: [
|
|
42
42
|
{
|
|
43
|
-
|
|
43
|
+
Contents: [
|
|
44
44
|
{
|
|
45
45
|
Key: "file.txt"
|
|
46
46
|
}
|
|
47
47
|
],
|
|
48
|
-
|
|
48
|
+
CommonPrefixes: []
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
},
|
|
@@ -61,8 +61,8 @@ describe("useIsBucketEmpty", ()=>{
|
|
|
61
61
|
data: {
|
|
62
62
|
pages: [
|
|
63
63
|
{
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
Contents: [],
|
|
65
|
+
CommonPrefixes: [
|
|
66
66
|
{
|
|
67
67
|
Prefix: "folder/"
|
|
68
68
|
}
|
|
@@ -94,7 +94,7 @@ describe("useIsBucketEmpty", ()=>{
|
|
|
94
94
|
data: {
|
|
95
95
|
pages: [
|
|
96
96
|
{
|
|
97
|
-
|
|
97
|
+
Contents: [
|
|
98
98
|
{
|
|
99
99
|
Key: void 0
|
|
100
100
|
},
|
|
@@ -102,7 +102,7 @@ describe("useIsBucketEmpty", ()=>{
|
|
|
102
102
|
Key: ""
|
|
103
103
|
}
|
|
104
104
|
],
|
|
105
|
-
|
|
105
|
+
CommonPrefixes: [
|
|
106
106
|
{
|
|
107
107
|
Prefix: null
|
|
108
108
|
},
|
|
@@ -13,12 +13,12 @@ const useIsBucketEmpty = (bucketName)=>{
|
|
|
13
13
|
if ("pending" === status || !data) return null;
|
|
14
14
|
if (!isInfiniteData(data)) return null;
|
|
15
15
|
for (const page of data.pages){
|
|
16
|
-
if (page?.
|
|
17
|
-
const validObjects = page.
|
|
16
|
+
if (page?.Contents && page.Contents.length > 0) {
|
|
17
|
+
const validObjects = page.Contents.filter((obj)=>obj?.Key);
|
|
18
18
|
if (validObjects.length > 0) return false;
|
|
19
19
|
}
|
|
20
|
-
if (page?.
|
|
21
|
-
const validPrefixes = page.
|
|
20
|
+
if (page?.CommonPrefixes && page.CommonPrefixes.length > 0) {
|
|
21
|
+
const validPrefixes = page.CommonPrefixes.filter((cp)=>cp?.Prefix);
|
|
22
22
|
if (validPrefixes.length > 0) return false;
|
|
23
23
|
}
|
|
24
24
|
}
|
package/dist/test/testUtils.js
CHANGED
package/dist/types/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface S3BrowserConfig extends Omit<S3ClientConfig, "credentials"> {
|
|
|
8
8
|
useDevProxy?: boolean;
|
|
9
9
|
realS3Host?: string;
|
|
10
10
|
proxyPath?: string;
|
|
11
|
+
proxyHost?: string;
|
|
12
|
+
proxyPort?: number;
|
|
11
13
|
publicAclIndicator?: string;
|
|
12
14
|
}
|
|
13
15
|
export interface S3Credentials extends AwsCredentialIdentity {
|
package/dist/utils/s3Client.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import { S3Client } from "@aws-sdk/client-s3";
|
|
2
2
|
import { createProxyMiddleware } from "./proxyMiddleware.js";
|
|
3
|
-
import {
|
|
3
|
+
import { S3ConfigurationFactory } from "../config/factory.js";
|
|
4
|
+
function extractProxyConfig(config) {
|
|
5
|
+
if (config.useDevProxy && config.realS3Host && config.proxyPath) return {
|
|
6
|
+
realS3Host: config.realS3Host,
|
|
7
|
+
proxyBasePath: config.proxyPath,
|
|
8
|
+
proxyHost: config.proxyHost || "localhost",
|
|
9
|
+
proxyPort: config.proxyPort || 8084
|
|
10
|
+
};
|
|
11
|
+
const proxyConfig = S3ConfigurationFactory.createProxyConfiguration();
|
|
12
|
+
if (proxyConfig.useProxy && proxyConfig.proxyConfig) return {
|
|
13
|
+
realS3Host: proxyConfig.proxyConfig.realHost,
|
|
14
|
+
proxyBasePath: proxyConfig.proxyConfig.proxyBasePath,
|
|
15
|
+
proxyHost: proxyConfig.proxyConfig.proxyHost,
|
|
16
|
+
proxyPort: proxyConfig.proxyConfig.proxyPort
|
|
17
|
+
};
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
4
20
|
const createS3Client = (config)=>{
|
|
5
21
|
const client = new S3Client({
|
|
6
22
|
...config,
|
|
7
23
|
credentials: config.credentials,
|
|
8
|
-
forcePathStyle: true,
|
|
24
|
+
forcePathStyle: config.forcePathStyle ?? true,
|
|
9
25
|
region: config.region
|
|
10
26
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
proxyPort: proxyConfig.proxyConfig.proxyPort
|
|
19
|
-
}));
|
|
20
|
-
const buildInfo = getBuildInfo();
|
|
21
|
-
console.log("S3Client configured with proxy middleware", {
|
|
22
|
-
buildInfo,
|
|
23
|
-
proxyConfig: proxyConfig.proxyConfig
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
const buildInfo = getBuildInfo();
|
|
28
|
-
console.log("S3Client configured for direct connection", {
|
|
29
|
-
buildInfo,
|
|
30
|
-
endpoint: config.endpoint
|
|
31
|
-
});
|
|
32
|
-
}
|
|
27
|
+
const proxyConfig = extractProxyConfig(config);
|
|
28
|
+
if (proxyConfig) client.middlewareStack.use(createProxyMiddleware({
|
|
29
|
+
realS3Host: proxyConfig.realS3Host,
|
|
30
|
+
proxyBasePath: proxyConfig.proxyBasePath,
|
|
31
|
+
proxyHost: proxyConfig.proxyHost,
|
|
32
|
+
proxyPort: proxyConfig.proxyPort
|
|
33
|
+
}));
|
|
33
34
|
return client;
|
|
34
35
|
};
|
|
35
36
|
export { createS3Client };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scality/data-browser-library",
|
|
3
|
-
"version": "1.0.0-preview.
|
|
3
|
+
"version": "1.0.0-preview.7",
|
|
4
4
|
"description": "A modular React component library for browsing S3 buckets and objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -27,21 +27,26 @@
|
|
|
27
27
|
"@aws-sdk/protocol-http": "^3.370.0",
|
|
28
28
|
"@aws-sdk/s3-presigned-post": "^3.888.0",
|
|
29
29
|
"@aws-sdk/s3-request-presigner": "^3.478.0",
|
|
30
|
+
"@hookform/resolvers": "^5.2.2",
|
|
30
31
|
"@monaco-editor/react": "^4.7.0",
|
|
31
32
|
"@tanstack/react-query": "^5.8.0",
|
|
32
33
|
"@tanstack/react-query-devtools": "^5.8.0",
|
|
33
34
|
"@testing-library/user-event": "^14.6.1",
|
|
35
|
+
"joi": "^18.0.1",
|
|
34
36
|
"react-dropzone": "^14.2.0",
|
|
35
|
-
"react-hook-form": "^7.48.0"
|
|
37
|
+
"react-hook-form": "^7.48.0",
|
|
38
|
+
"@scality/zenkoclient": "^2.0.0-preview.1"
|
|
36
39
|
},
|
|
37
40
|
"peerDependencies": {
|
|
38
|
-
"@scality/core-ui": "0.174.0",
|
|
41
|
+
"@scality/core-ui": "^0.174.0",
|
|
39
42
|
"react": ">=18.0.0",
|
|
40
|
-
"react-dom": ">=18.0.0"
|
|
43
|
+
"react-dom": ">=18.0.0",
|
|
44
|
+
"react-router-dom": ">=6.0.0",
|
|
45
|
+
"styled-components": "^5.0.0"
|
|
41
46
|
},
|
|
42
47
|
"devDependencies": {
|
|
43
|
-
"@rslib/core": "^0.14.0",
|
|
44
48
|
"@rsbuild/plugin-react": "^1.4.1",
|
|
49
|
+
"@rslib/core": "^0.14.0",
|
|
45
50
|
"@testing-library/jest-dom": "^6.8.0",
|
|
46
51
|
"@testing-library/react": "^15.0.6",
|
|
47
52
|
"@testing-library/user-event": "^14.6.1",
|