@scality/data-browser-library 1.0.8 → 1.0.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.
Files changed (37) hide show
  1. package/dist/components/__tests__/BucketCorsPage.test.js +67 -9
  2. package/dist/components/__tests__/BucketDetails.test.js +1 -0
  3. package/dist/components/__tests__/BucketLifecycleFormPage.test.js +16 -11
  4. package/dist/components/__tests__/BucketNotificationFormPage.test.js +45 -0
  5. package/dist/components/__tests__/BucketOverview.test.js +92 -2
  6. package/dist/components/__tests__/BucketPolicyPage.test.js +70 -51
  7. package/dist/components/__tests__/BucketReplicationFormPage.test.js +18 -24
  8. package/dist/components/__tests__/ObjectList.test.js +43 -2
  9. package/dist/components/buckets/BucketConfigEditButton.d.ts +2 -0
  10. package/dist/components/buckets/BucketConfigEditButton.js +9 -3
  11. package/dist/components/buckets/BucketCorsPage.js +57 -20
  12. package/dist/components/buckets/BucketDetails.js +27 -2
  13. package/dist/components/buckets/BucketLifecycleFormPage.js +310 -270
  14. package/dist/components/buckets/BucketOverview.js +21 -18
  15. package/dist/components/buckets/BucketPolicyPage.js +119 -83
  16. package/dist/components/buckets/BucketReplicationFormPage.js +39 -29
  17. package/dist/components/buckets/BucketVersioning.js +16 -10
  18. package/dist/components/buckets/__tests__/BucketVersioning.test.js +76 -23
  19. package/dist/components/buckets/notifications/BucketNotificationFormPage.js +13 -5
  20. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +3 -3
  21. package/dist/components/objects/ObjectList.js +22 -25
  22. package/dist/components/objects/ObjectLock/EditRetentionButton.js +2 -2
  23. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +2 -0
  24. package/dist/hooks/index.d.ts +1 -1
  25. package/dist/hooks/objectOperations.d.ts +3 -3
  26. package/dist/hooks/objectOperations.js +3 -3
  27. package/dist/hooks/useBucketConfigEditor.d.ts +4 -4
  28. package/dist/hooks/useBucketConfigEditor.js +16 -31
  29. package/dist/test/mocks/esmOnlyModules.js +4 -0
  30. package/dist/types/index.d.ts +0 -1
  31. package/dist/utils/__tests__/proxyMiddleware.test.js +34 -0
  32. package/dist/utils/proxyMiddleware.js +2 -0
  33. package/package.json +4 -4
  34. package/dist/components/Editor.d.ts +0 -12
  35. package/dist/components/Editor.js +0 -28
  36. package/dist/types/monaco.d.ts +0 -13
  37. package/dist/types/monaco.js +0 -0
@@ -16,23 +16,22 @@ const mockUseGetBucketVersioning = jest.mocked(useGetBucketVersioning);
16
16
  const mockUseGetBucketObjectLockConfiguration = jest.mocked(useGetBucketObjectLockConfiguration);
17
17
  const mockUseISVBucketStatus = jest.mocked(useISVBucketStatus);
18
18
  const mockUseSetBucketVersioning = jest.mocked(useSetBucketVersioning);
19
- const renderBucketVersioning = (props = {})=>{
20
- const mockMutate = jest.fn();
19
+ const renderBucketVersioning = (props = {}, { simulateSuccess = false } = {})=>{
20
+ const mockMutate = jest.fn().mockImplementation((_params, options)=>{
21
+ if (simulateSuccess && options?.onSuccess) options.onSuccess();
22
+ });
21
23
  mockUseSetBucketVersioning.mockReturnValue(createMockMutationResult(mockMutate));
22
24
  const Wrapper = createTestWrapper();
23
- return {
24
- ...render(/*#__PURE__*/ jsx(Wrapper, {
25
- children: /*#__PURE__*/ jsx(BucketOverviewContext.Provider, {
26
- value: {
27
- bucketName: 'test-bucket'
28
- },
29
- children: /*#__PURE__*/ jsx(BucketVersioning, {
30
- ...props
31
- })
25
+ return render(/*#__PURE__*/ jsx(Wrapper, {
26
+ children: /*#__PURE__*/ jsx(BucketOverviewContext.Provider, {
27
+ value: {
28
+ bucketName: 'test-bucket'
29
+ },
30
+ children: /*#__PURE__*/ jsx(BucketVersioning, {
31
+ ...props
32
32
  })
33
- })),
34
- mutate: mockMutate
35
- };
33
+ })
34
+ }));
36
35
  };
37
36
  const mockHookDefaults = ()=>{
38
37
  mockUseGetBucketVersioning.mockReturnValue({
@@ -86,10 +85,10 @@ describe('BucketVersioning', ()=>{
86
85
  expect(toggle).not.toBeChecked();
87
86
  });
88
87
  it('calls mutation with Suspended status when disabling versioning', ()=>{
89
- const { mutate } = renderBucketVersioning();
88
+ renderBucketVersioning();
90
89
  const toggle = findToggleByLabel('Active');
91
90
  fireEvent.click(toggle);
92
- expect(mutate).toHaveBeenCalledWith({
91
+ expect(mockUseSetBucketVersioning().mutate).toHaveBeenCalledWith({
93
92
  Bucket: 'test-bucket',
94
93
  VersioningConfiguration: {
95
94
  Status: 'Suspended'
@@ -104,10 +103,10 @@ describe('BucketVersioning', ()=>{
104
103
  status: 'success',
105
104
  error: null
106
105
  });
107
- const { mutate } = renderBucketVersioning();
106
+ renderBucketVersioning();
108
107
  const toggle = findToggleByLabel('Inactive');
109
108
  fireEvent.click(toggle);
110
- expect(mutate).toHaveBeenCalledWith({
109
+ expect(mockUseSetBucketVersioning().mutate).toHaveBeenCalledWith({
111
110
  Bucket: 'test-bucket',
112
111
  VersioningConfiguration: {
113
112
  Status: 'Enabled'
@@ -135,16 +134,35 @@ describe('BucketVersioning', ()=>{
135
134
  });
136
135
  renderBucketVersioning();
137
136
  expect(screen.getByText('Enabled')).toBeInTheDocument();
138
- expect(screen.getByText(/Versioning cannot be suspended because Object-lock is enabled/i)).toBeInTheDocument();
137
+ expect(screen.getByText(/Versioning cannot be disabled because Object-lock is enabled/i)).toBeInTheDocument();
139
138
  expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
140
139
  });
141
- it('disables toggle when bucket is managed by Veeam', ()=>{
142
- mockUseISVBucketStatus.mockReturnValue({
140
+ it.each([
141
+ {
142
+ isvApplication: 'Veeam',
143
143
  isVeeamBucket: true,
144
144
  isCommvaultBucket: false,
145
- isKastenBucket: false,
145
+ isKastenBucket: false
146
+ },
147
+ {
148
+ isvApplication: 'Commvault',
149
+ isVeeamBucket: false,
150
+ isCommvaultBucket: true,
151
+ isKastenBucket: false
152
+ },
153
+ {
154
+ isvApplication: 'Kasten',
155
+ isVeeamBucket: false,
156
+ isCommvaultBucket: false,
157
+ isKastenBucket: true
158
+ }
159
+ ])('disables toggle when bucket is managed by $isvApplication', ({ isvApplication, isVeeamBucket, isCommvaultBucket, isKastenBucket })=>{
160
+ mockUseISVBucketStatus.mockReturnValue({
161
+ isVeeamBucket,
162
+ isCommvaultBucket,
163
+ isKastenBucket,
146
164
  isISVManaged: true,
147
- isvApplication: 'Veeam',
165
+ isvApplication,
148
166
  isLoading: false,
149
167
  bucketTagsStatus: 'success'
150
168
  });
@@ -152,6 +170,41 @@ describe('BucketVersioning', ()=>{
152
170
  const toggle = screen.getByRole('checkbox');
153
171
  expect(toggle).toHaveAttribute('aria-disabled', 'true');
154
172
  });
173
+ it('shows toast with correct message when disabling versioning', ()=>{
174
+ jest.useFakeTimers();
175
+ renderBucketVersioning({}, {
176
+ simulateSuccess: true
177
+ });
178
+ const toggle = findToggleByLabel('Active');
179
+ fireEvent.click(toggle);
180
+ jest.advanceTimersByTime(500);
181
+ expect(mockShowToast).toHaveBeenCalledWith(expect.objectContaining({
182
+ message: 'Bucket versioning disabled',
183
+ status: 'success'
184
+ }));
185
+ jest.useRealTimers();
186
+ });
187
+ it('shows toast with correct message when enabling versioning', ()=>{
188
+ jest.useFakeTimers();
189
+ mockUseGetBucketVersioning.mockReturnValue({
190
+ data: {
191
+ Status: 'Suspended'
192
+ },
193
+ status: 'success',
194
+ error: null
195
+ });
196
+ renderBucketVersioning({}, {
197
+ simulateSuccess: true
198
+ });
199
+ const toggle = findToggleByLabel('Inactive');
200
+ fireEvent.click(toggle);
201
+ jest.advanceTimersByTime(500);
202
+ expect(mockShowToast).toHaveBeenCalledWith(expect.objectContaining({
203
+ message: 'Bucket versioning enabled',
204
+ status: 'success'
205
+ }));
206
+ jest.useRealTimers();
207
+ });
155
208
  it('disables toggle when versioning query errors', ()=>{
156
209
  mockUseGetBucketVersioning.mockReturnValue({
157
210
  data: void 0,
@@ -1,4 +1,4 @@
1
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { joiResolver } from "@hookform/resolvers/joi";
3
3
  import { Form, FormGroup, FormSection, Icon, Loader, Stack, Text, spacing, useToast } from "@scality/core-ui";
4
4
  import { convertRemToPixels } from "@scality/core-ui/dist/components/tablev2/TableUtils";
@@ -9,6 +9,7 @@ import { FormProvider, useForm } from "react-hook-form";
9
9
  import { useParams } from "react-router";
10
10
  import { useGetBucketNotification, useSetBucketNotification } from "../../../hooks/index.js";
11
11
  import { useDataBrowserNavigate } from "../../../hooks/useDataBrowserNavigate.js";
12
+ import { useSupportedNotificationEvents } from "../../../hooks/useSupportedNotificationEvents.js";
12
13
  import { EventsSection } from "./EventsSection.js";
13
14
  const baseSchema = joi.object({
14
15
  ruleName: joi.string().required().messages({
@@ -29,6 +30,7 @@ function BucketNotificationFormPage() {
29
30
  const navigate = useDataBrowserNavigate();
30
31
  const { showToast } = useToast();
31
32
  const isEditMode = !!ruleId;
33
+ const supportedEvents = useSupportedNotificationEvents();
32
34
  const { data: existingNotificationData, status: notificationStatus, error: notificationError } = useGetBucketNotification({
33
35
  Bucket: bucketName
34
36
  });
@@ -70,10 +72,15 @@ function BucketNotificationFormPage() {
70
72
  if (isEditMode && existingRule) {
71
73
  const prefixRule = existingRule.Filter?.Key?.FilterRules?.find((r)=>'prefix' === r.Name);
72
74
  const suffixRule = existingRule.Filter?.Key?.FilterRules?.find((r)=>'suffix' === r.Name);
75
+ let events = existingRule.Events || [];
76
+ if (supportedEvents?.length) {
77
+ const supported = new Set(supportedEvents);
78
+ events = events.filter((event)=>supported.has(event));
79
+ }
73
80
  reset({
74
81
  ruleName: existingRule.Id || '',
75
82
  queueArn: existingRule.QueueArn || '',
76
- events: existingRule.Events || [],
83
+ events,
77
84
  prefix: prefixRule?.Value || '',
78
85
  suffix: suffixRule?.Value || ''
79
86
  });
@@ -81,7 +88,8 @@ function BucketNotificationFormPage() {
81
88
  }, [
82
89
  isEditMode,
83
90
  existingRule,
84
- reset
91
+ reset,
92
+ supportedEvents
85
93
  ]);
86
94
  const handleCancel = useCallback(()=>{
87
95
  navigate(`/buckets/${bucketName}?tab=notification`);
@@ -248,7 +256,7 @@ function BucketNotificationFormPage() {
248
256
  direction: "horizontal",
249
257
  error: errors?.ruleName?.message,
250
258
  helpErrorPosition: "bottom",
251
- labelHelpTooltip: /*#__PURE__*/ jsx(Fragment, {}),
259
+ labelHelpTooltip: "Must be unique across all notification rules in this bucket. Cannot be changed after creation.",
252
260
  required: true,
253
261
  content: isEditMode ? /*#__PURE__*/ jsx(Text, {
254
262
  children: existingRule?.Id
@@ -267,7 +275,7 @@ function BucketNotificationFormPage() {
267
275
  direction: "horizontal",
268
276
  error: errors?.queueArn?.message,
269
277
  helpErrorPosition: "bottom",
270
- labelHelpTooltip: /*#__PURE__*/ jsx(Fragment, {}),
278
+ labelHelpTooltip: "Expected format: arn:aws:sqs:region:account-id:queue-name",
271
279
  required: true,
272
280
  content: /*#__PURE__*/ jsx(Input, {
273
281
  id: "queueArn",
@@ -5,7 +5,7 @@ import { cleanup, render, screen } from "@testing-library/react";
5
5
  import { MemoryRouter, Route, Routes } from "react-router";
6
6
  import { DataBrowserUICustomizationProvider } from "../../../../contexts/DataBrowserUICustomizationContext.js";
7
7
  var __webpack_modules__ = {
8
- "../index" (module) {
8
+ ".." (module) {
9
9
  module.exports = __rspack_external__index_js_95fdb65a;
10
10
  }
11
11
  };
@@ -19,7 +19,7 @@ function __webpack_require__(moduleId) {
19
19
  __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
20
20
  return module.exports;
21
21
  }
22
- var external_index_js_ = __webpack_require__("../index");
22
+ var external_index_js_ = __webpack_require__("..");
23
23
  jest.mock('../ObjectSummary', ()=>({
24
24
  ObjectSummary: ()=>/*#__PURE__*/ jsx("div", {
25
25
  "data-testid": "object-summary",
@@ -398,7 +398,7 @@ describe('ObjectDetails', ()=>{
398
398
  expect(screen.getByTestId('object-summary')).toBeInTheDocument();
399
399
  });
400
400
  it('should throw error when useObjectDetailsContext is used outside provider', ()=>{
401
- const { useObjectDetailsContext } = __webpack_require__("../index");
401
+ const { useObjectDetailsContext } = __webpack_require__("..");
402
402
  const TestComponent = ()=>{
403
403
  useObjectDetailsContext();
404
404
  return null;
@@ -5,7 +5,7 @@ import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "rea
5
5
  import { useLocation, useNavigate } from "react-router";
6
6
  import styled_components from "styled-components";
7
7
  import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
8
- import { useSearchObjects, useSearchObjectsVersions } from "../../hooks/index.js";
8
+ import { useListObjectVersions, useListObjects, useSearchObjects, useSearchObjectsVersions } from "../../hooks/index.js";
9
9
  import { useGetPresignedDownload } from "../../hooks/presignedOperations.js";
10
10
  import { useBatchObjectLegalHold } from "../../hooks/useBatchObjectLegalHold.js";
11
11
  import { useFeatures } from "../../hooks/useFeatures.js";
@@ -370,28 +370,8 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange, onSele
370
370
  getPresignedDownload,
371
371
  showToast
372
372
  ]);
373
- const searchParams = useMemo(()=>{
374
- const baseParams = {
375
- Bucket: bucketName,
376
- Prefix: prefix,
377
- MaxKeys: DEFAULT_PAGE_SIZE,
378
- Delimiter: '/'
379
- };
380
- if (isMetadataSearchEnabled && metadataSearchQuery) return {
381
- ...baseParams,
382
- Search: metadataSearchQuery
383
- };
384
- return baseParams;
385
- }, [
386
- bucketName,
387
- prefix,
388
- isMetadataSearchEnabled,
389
- metadataSearchQuery
390
- ]);
391
- const listObjectsQuery = useSearchObjects(searchParams, {
392
- enabled: Boolean(bucketName)
393
- });
394
- const versionSearchParams = useMemo(()=>({
373
+ const isSearchActive = isMetadataSearchEnabled && Boolean(metadataSearchQuery);
374
+ const baseParams = useMemo(()=>({
395
375
  Bucket: bucketName,
396
376
  Prefix: prefix,
397
377
  MaxKeys: DEFAULT_PAGE_SIZE,
@@ -400,9 +380,26 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange, onSele
400
380
  bucketName,
401
381
  prefix
402
382
  ]);
403
- const listVersionsQuery = useSearchObjectsVersions(versionSearchParams, {
404
- enabled: showVersions && Boolean(bucketName)
383
+ const regularListQuery = useListObjects(baseParams, {
384
+ enabled: Boolean(bucketName) && !isSearchActive
385
+ });
386
+ const searchListQuery = useSearchObjects({
387
+ ...baseParams,
388
+ Query: metadataSearchQuery || ''
389
+ }, {
390
+ enabled: Boolean(bucketName) && isSearchActive
391
+ });
392
+ const listObjectsQuery = isSearchActive ? searchListQuery : regularListQuery;
393
+ const regularVersionsQuery = useListObjectVersions(baseParams, {
394
+ enabled: showVersions && Boolean(bucketName) && !isSearchActive
395
+ });
396
+ const searchVersionsQuery = useSearchObjectsVersions({
397
+ ...baseParams,
398
+ Query: metadataSearchQuery || ''
399
+ }, {
400
+ enabled: showVersions && Boolean(bucketName) && isSearchActive
405
401
  });
402
+ const listVersionsQuery = isSearchActive ? searchVersionsQuery : regularVersionsQuery;
406
403
  useEffect(()=>{
407
404
  setSelectedObjects([]);
408
405
  onObjectSelectRef.current(null);
@@ -5,8 +5,8 @@ import { useISVBucketStatus } from "../../../hooks/index.js";
5
5
  import { useDataBrowserNavigate } from "../../../hooks/useDataBrowserNavigate.js";
6
6
  const EditRetentionButton = ({ bucketName })=>{
7
7
  const navigate = useDataBrowserNavigate();
8
- const { isVeeamBucket, isCommvaultBucket, isISVManaged, isLoading } = useISVBucketStatus(bucketName);
9
- const tooltipOverlay = isCommvaultBucket ? 'Edition is disabled as it is managed by Commvault.' : isVeeamBucket ? 'Edition is disabled as it is managed by Veeam.' : void 0;
8
+ const { isISVManaged, isvApplication, isLoading } = useISVBucketStatus(bucketName);
9
+ const tooltipOverlay = isISVManaged ? `Edition is disabled as it is managed by ${isvApplication}.` : void 0;
10
10
  return /*#__PURE__*/ jsx(Button, {
11
11
  id: "edit-retention-btn",
12
12
  variant: "outline",
@@ -46,6 +46,7 @@ function getPaginationParams(operationName, pageParam) {
46
46
  ContinuationToken: pageParam
47
47
  };
48
48
  case 'ListObjectVersions':
49
+ case 'SearchObjectsVersions':
49
50
  if (pageParam.includes('|')) {
50
51
  const [keyMarker, versionIdMarker] = pageParam.split('|');
51
52
  return {
@@ -71,6 +72,7 @@ function getNextPageToken(operationName, lastPage) {
71
72
  case 'ListObjects':
72
73
  return lastPage.IsTruncated ? lastPage.NextContinuationToken : void 0;
73
74
  case 'ListObjectVersions':
75
+ case 'SearchObjectsVersions':
74
76
  return lastPage.IsTruncated && (lastPage.NextKeyMarker || lastPage.NextVersionIdMarker) ? `${lastPage.NextKeyMarker || ''}|${lastPage.NextVersionIdMarker || ''}` : void 0;
75
77
  default:
76
78
  return lastPage.IsTruncated ? lastPage.NextContinuationToken : void 0;
@@ -6,7 +6,7 @@ export { useCopyObject, useCreateFolder, useDeleteObject, useDeleteObjects, useD
6
6
  export { useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, } from './presignedOperations';
7
7
  export { getAccessibleBucketsStorageKey, getLimitedAccessFlagKey, setLimitedAccessFlag, useAccessibleBuckets, } from './useAccessibleBuckets';
8
8
  export { useBatchObjectLegalHold } from './useBatchObjectLegalHold';
9
- export { type BucketConfigEditorConfig, type BucketConfigEditorResult, useBucketConfigEditor, } from './useBucketConfigEditor';
9
+ export { type BucketConfigEditorConfig, type BucketConfigEditorResult, useBucketConfigEditor } from './useBucketConfigEditor';
10
10
  export { useBucketLocations } from './useBucketLocations';
11
11
  export { useDeleteBucketConfigRule } from './useDeleteBucketConfigRule';
12
12
  export { useEmptyBucket } from './useEmptyBucket';
@@ -6,7 +6,7 @@
6
6
  * configurations like retention and legal hold.
7
7
  */
8
8
  import { type CopyObjectCommandInput, type CopyObjectCommandOutput, type DeleteObjectCommandInput, type DeleteObjectCommandOutput, type DeleteObjectsCommandInput, type DeleteObjectsCommandOutput, type DeleteObjectTaggingCommandInput, type DeleteObjectTaggingCommandOutput, type GetObjectAclCommandInput, type GetObjectAclCommandOutput, type GetObjectAttributesCommandInput, type GetObjectAttributesCommandOutput, type GetObjectCommandInput, type GetObjectCommandOutput, type GetObjectLegalHoldCommandInput, type GetObjectLegalHoldCommandOutput, type GetObjectRetentionCommandInput, type GetObjectRetentionCommandOutput, type GetObjectTaggingCommandInput, type GetObjectTaggingCommandOutput, type GetObjectTorrentCommandInput, type GetObjectTorrentCommandOutput, type HeadObjectCommandInput, type HeadObjectCommandOutput, type ListMultipartUploadsCommandInput, type ListMultipartUploadsCommandOutput, type ListObjectsV2CommandInput, type ListObjectVersionsCommandInput, type PutObjectAclCommandInput, type PutObjectAclCommandOutput, type PutObjectCommandInput, type PutObjectCommandOutput, type PutObjectLegalHoldCommandInput, type PutObjectLegalHoldCommandOutput, type PutObjectRetentionCommandInput, type PutObjectRetentionCommandOutput, type PutObjectTaggingCommandInput, type PutObjectTaggingCommandOutput, type RestoreObjectCommandInput, type RestoreObjectCommandOutput, type SelectObjectContentCommandInput, type SelectObjectContentCommandOutput } from '@aws-sdk/client-s3';
9
- import { type SearchObjectsV2CommandInput, type SearchObjectsVersionCommandInput } from '@scality/zenkoclient';
9
+ import { type ListObjectsV2ExtendedInput, type ListObjectVersionsExtendedInput } from '@scality/cloudserverclient';
10
10
  /**
11
11
  * Hook for listing S3 objects with infinite scroll support
12
12
  *
@@ -27,14 +27,14 @@ export declare const useListObjectVersions: (params: ListObjectVersionsCommandIn
27
27
  * Provides paginated search of objects in an S3 bucket using SQL-like query syntax.
28
28
  * Supports advanced filtering and pattern matching on object keys and metadata.
29
29
  */
30
- export declare const useSearchObjects: (params: SearchObjectsV2CommandInput, options?: Partial<import("@tanstack/react-query").UseInfiniteQueryOptions<any, import("..").EnhancedS3Error, any, (string | SearchObjectsV2CommandInput)[], string | undefined>> | undefined) => import("@tanstack/react-query").UseInfiniteQueryResult<any, import("..").EnhancedS3Error>;
30
+ export declare const useSearchObjects: (params: ListObjectsV2ExtendedInput, options?: Partial<import("@tanstack/react-query").UseInfiniteQueryOptions<any, import("..").EnhancedS3Error, any, (string | ListObjectsV2ExtendedInput)[], string | undefined>> | undefined) => import("@tanstack/react-query").UseInfiniteQueryResult<any, import("..").EnhancedS3Error>;
31
31
  /**
32
32
  * Hook for searching S3 object versions with infinite scroll support
33
33
  *
34
34
  * Provides paginated search of object versions in an S3 bucket using SQL-like query syntax.
35
35
  * Supports advanced filtering and pattern matching on object keys and metadata across all versions.
36
36
  */
37
- export declare const useSearchObjectsVersions: (params: SearchObjectsVersionCommandInput, options?: Partial<import("@tanstack/react-query").UseInfiniteQueryOptions<any, import("..").EnhancedS3Error, any, (string | SearchObjectsVersionCommandInput)[], string | undefined>> | undefined) => import("@tanstack/react-query").UseInfiniteQueryResult<any, import("..").EnhancedS3Error>;
37
+ export declare const useSearchObjectsVersions: (params: ListObjectVersionsExtendedInput, options?: Partial<import("@tanstack/react-query").UseInfiniteQueryOptions<any, import("..").EnhancedS3Error, any, (string | ListObjectVersionsExtendedInput)[], string | undefined>> | undefined) => import("@tanstack/react-query").UseInfiniteQueryResult<any, import("..").EnhancedS3Error>;
38
38
  /**
39
39
  * Hook for retrieving S3 object metadata
40
40
  *
@@ -1,12 +1,12 @@
1
1
  import { CopyObjectCommand, DeleteObjectCommand, DeleteObjectTaggingCommand, DeleteObjectsCommand, GetObjectAclCommand, GetObjectAttributesCommand, GetObjectCommand, GetObjectLegalHoldCommand, GetObjectRetentionCommand, GetObjectTaggingCommand, GetObjectTorrentCommand, HeadObjectCommand, ListMultipartUploadsCommand, ListObjectVersionsCommand, ListObjectsV2Command, PutObjectAclCommand, PutObjectCommand, PutObjectLegalHoldCommand, PutObjectRetentionCommand, PutObjectTaggingCommand, RestoreObjectCommand, SelectObjectContentCommand } from "@aws-sdk/client-s3";
2
- import { SearchObjectsV2Command, SearchObjectsVersionCommand } from "@scality/zenkoclient";
2
+ import { ListObjectVersionsExtendedCommand, ListObjectsV2ExtendedCommand } from "@scality/cloudserverclient";
3
3
  import { useCreateS3InfiniteQueryHook } from "./factories/useCreateS3InfiniteQueryHook.js";
4
4
  import { useCreateS3MutationHook } from "./factories/useCreateS3MutationHook.js";
5
5
  import { useCreateS3QueryHook } from "./factories/useCreateS3QueryHook.js";
6
6
  const useListObjects = useCreateS3InfiniteQueryHook(ListObjectsV2Command, 'ListObjects');
7
7
  const useListObjectVersions = useCreateS3InfiniteQueryHook(ListObjectVersionsCommand, 'ListObjectVersions');
8
- const useSearchObjects = useCreateS3InfiniteQueryHook(SearchObjectsV2Command, 'SearchObjects');
9
- const useSearchObjectsVersions = useCreateS3InfiniteQueryHook(SearchObjectsVersionCommand, 'SearchObjectsVersions');
8
+ const useSearchObjects = useCreateS3InfiniteQueryHook(ListObjectsV2ExtendedCommand, 'SearchObjects');
9
+ const useSearchObjectsVersions = useCreateS3InfiniteQueryHook(ListObjectVersionsExtendedCommand, 'SearchObjectsVersions');
10
10
  const useObjectMetadata = useCreateS3QueryHook(HeadObjectCommand, 'HeadObject');
11
11
  const useGetObject = useCreateS3QueryHook(GetObjectCommand, 'GetObject');
12
12
  const usePutObject = useCreateS3MutationHook(PutObjectCommand, 'PutObject', [
@@ -1,4 +1,4 @@
1
- import type { BeforeMount } from '@monaco-editor/react';
1
+ import type { JSONSchema7 } from 'json-schema';
2
2
  import { useForm } from 'react-hook-form';
3
3
  type FormValues = {
4
4
  content: string;
@@ -13,10 +13,9 @@ export type BucketConfigEditorConfig = {
13
13
  initialContent: string | undefined;
14
14
  defaultTemplate: string;
15
15
  isLoading: boolean;
16
- notFoundErrorNames: string[];
17
16
  errorInstance: Error | null;
18
17
  validate: (content: string) => ValidationResult;
19
- monacoSchema?: unknown;
18
+ jsonSchema?: JSONSchema7;
20
19
  };
21
20
  export type BucketConfigEditorResult = {
22
21
  bucketName: string;
@@ -24,8 +23,9 @@ export type BucketConfigEditorResult = {
24
23
  content: string;
25
24
  isCreateMode: boolean;
26
25
  isValidFormat: boolean;
27
- handleBeforeMount: BeforeMount;
26
+ jsonSchema?: JSONSchema7;
28
27
  navigateToBucket: () => void;
28
+ loadTemplate: () => void;
29
29
  };
30
30
  export declare const useBucketConfigEditor: (config: BucketConfigEditorConfig) => BucketConfigEditorResult;
31
31
  export {};
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo } from "react";
2
2
  import { useForm } from "react-hook-form";
3
+ import { isNotFoundError } from "../utils/errorHandling.js";
3
4
  import { useDataBrowserNavigate } from "./useDataBrowserNavigate.js";
4
5
  const useBucketConfigEditor = (config)=>{
5
6
  const navigate = useDataBrowserNavigate();
@@ -12,12 +13,8 @@ const useBucketConfigEditor = (config)=>{
12
13
  });
13
14
  const { watch, reset } = form;
14
15
  const content = watch('content');
15
- const isCreateMode = useMemo(()=>{
16
- if (!config.errorInstance) return false;
17
- return config.notFoundErrorNames.some((name)=>config.errorInstance?.name === name);
18
- }, [
19
- config.errorInstance,
20
- config.notFoundErrorNames
16
+ const isCreateMode = useMemo(()=>isNotFoundError(config.errorInstance), [
17
+ config.errorInstance
21
18
  ]);
22
19
  const initialValue = useMemo(()=>{
23
20
  if (config.initialContent) try {
@@ -26,10 +23,9 @@ const useBucketConfigEditor = (config)=>{
26
23
  } catch {
27
24
  return config.initialContent;
28
25
  }
29
- return config.defaultTemplate;
26
+ return '';
30
27
  }, [
31
- config.initialContent,
32
- config.defaultTemplate
28
+ config.initialContent
33
29
  ]);
34
30
  useEffect(()=>{
35
31
  reset({
@@ -43,40 +39,29 @@ const useBucketConfigEditor = (config)=>{
43
39
  content,
44
40
  config.validate
45
41
  ]);
46
- const handleBeforeMount = useCallback((monaco)=>{
47
- const jsonLanguage = monaco.languages.json;
48
- if (jsonLanguage?.jsonDefaults) if (config.monacoSchema) jsonLanguage.jsonDefaults.setDiagnosticsOptions({
49
- validate: true,
50
- schemas: [
51
- {
52
- uri: 'http://myserver/schema.json',
53
- fileMatch: [
54
- '*'
55
- ],
56
- schema: config.monacoSchema
57
- }
58
- ]
59
- });
60
- else jsonLanguage.jsonDefaults.setDiagnosticsOptions({
61
- validate: true
62
- });
63
- }, [
64
- config.monacoSchema
65
- ]);
66
42
  const navigateToBucket = useCallback(()=>{
67
43
  navigate(`/buckets/${bucketName}`);
68
44
  }, [
69
45
  navigate,
70
46
  bucketName
71
47
  ]);
48
+ const loadTemplate = useCallback(()=>{
49
+ form.setValue('content', config.defaultTemplate, {
50
+ shouldDirty: true
51
+ });
52
+ }, [
53
+ form,
54
+ config.defaultTemplate
55
+ ]);
72
56
  return {
73
57
  bucketName,
74
58
  form,
75
59
  content,
76
60
  isCreateMode,
77
61
  isValidFormat,
78
- handleBeforeMount,
79
- navigateToBucket
62
+ jsonSchema: config.jsonSchema,
63
+ navigateToBucket,
64
+ loadTemplate
80
65
  };
81
66
  };
82
67
  export { useBucketConfigEditor };
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ createHighlighterCore: ()=>Promise.resolve({}),
3
+ fromHighlighter: ()=>()=>{}
4
+ };
@@ -2,7 +2,6 @@ import type { S3ClientConfig } from '@aws-sdk/client-s3';
2
2
  import type { AwsCredentialIdentity } from '@aws-sdk/types';
3
3
  import type { ProxyConfiguration, S3EventType } from '../config/types';
4
4
  export type { _Object, Bucket, BucketCannedACL, BucketLocationConstraint, CopyObjectCommandInput, CopyObjectCommandOutput, CreateBucketCommandInput, CreateBucketCommandOutput, DeleteBucketCommandInput, DeleteBucketCommandOutput, DeleteBucketCorsCommandOutput, DeleteBucketLifecycleCommandOutput, DeleteBucketPolicyCommandOutput, DeleteBucketReplicationCommandOutput, DeleteBucketTaggingCommandOutput, DeleteMarkerEntry, DeleteObjectCommandInput, DeleteObjectCommandOutput, DeleteObjectsCommandInput, DeleteObjectsCommandOutput, DeleteObjectTaggingCommandOutput, GetBucketAclCommandOutput, GetBucketCorsCommandOutput, GetBucketEncryptionCommandOutput, GetBucketLifecycleConfigurationCommandOutput, GetBucketLocationCommandInput, GetBucketLocationCommandOutput, GetBucketNotificationConfigurationCommandOutput, GetBucketPolicyCommandOutput, GetBucketReplicationCommandOutput, GetBucketTaggingCommandOutput, GetBucketVersioningCommandOutput, GetObjectAclCommandOutput, GetObjectAttributesCommandInput, GetObjectAttributesCommandOutput, GetObjectCommandInput, GetObjectCommandOutput, GetObjectLegalHoldCommandOutput, GetObjectLockConfigurationCommandOutput, GetObjectRetentionCommandOutput, GetObjectTaggingCommandOutput, GetObjectTorrentCommandOutput, HeadObjectCommandInput, HeadObjectCommandOutput, ListBucketsCommandOutput, ListMultipartUploadsCommandInput, ListMultipartUploadsCommandOutput, ListObjectsV2CommandInput, ListObjectsV2CommandOutput, ListObjectVersionsCommandInput, ListObjectVersionsCommandOutput, ObjectCannedACL, ObjectVersion, Owner, PutBucketAclCommandInput, PutBucketCorsCommandInput, PutBucketEncryptionCommandInput, PutBucketLifecycleConfigurationCommandInput, PutBucketNotificationConfigurationCommandInput, PutBucketPolicyCommandInput, PutBucketReplicationCommandInput, PutBucketTaggingCommandInput, PutBucketTaggingCommandOutput, PutBucketVersioningCommandInput, PutObjectAclCommandInput, PutObjectCommandInput, PutObjectCommandOutput, PutObjectLegalHoldCommandInput, PutObjectLockConfigurationCommandInput, PutObjectRetentionCommandInput, PutObjectTaggingCommandInput, RestoreObjectCommandInput, RestoreObjectCommandOutput, SelectObjectContentCommandInput, SelectObjectContentCommandOutput, Tag, } from '@aws-sdk/client-s3';
5
- export type { MonacoJsonDefaults, MonacoLanguagesJson } from './monaco';
6
5
  /**
7
6
  * S3 backend capabilities configuration.
8
7
  */
@@ -333,12 +333,45 @@ describe('createProxyMiddleware', ()=>{
333
333
  expect(mockRequest.protocol).toBe('http:');
334
334
  expect(mockRequest.path).toBe('/api/s3/bucket/key');
335
335
  expect(mockRequest.headers.host).toBe('localhost:3000');
336
+ expect(mockRequest.headers['x-s3-target-endpoint']).toBe('https://s3.amazonaws.com');
336
337
  expect(mockRequest.query).toEqual({
337
338
  param: 'value'
338
339
  });
339
340
  expect(mockRequest.headers.authorization).toBe('AWS4-HMAC-SHA256 Credential=...');
340
341
  expect(mockNext).toHaveBeenCalledTimes(1);
341
342
  });
343
+ it('should set X-S3-Target-Endpoint header with custom port', async ()=>{
344
+ const mockNext = jest.fn().mockResolvedValue({
345
+ output: {}
346
+ });
347
+ const mockStack = {
348
+ add: jest.fn((middleware, config)=>{
349
+ if ('finalizeRequest' === config.step) mockStack.postSigningMiddleware = middleware;
350
+ }),
351
+ postSigningMiddleware: null
352
+ };
353
+ const middleware = createProxyMiddleware({
354
+ enabled: true,
355
+ endpoint: 'http://localhost:3000/api/s3',
356
+ target: 'http://s3.local:9000'
357
+ });
358
+ middleware.applyToStack(mockStack);
359
+ const postSigningMiddleware = mockStack.postSigningMiddleware(mockNext);
360
+ const mockRequest = new HttpRequest({
361
+ hostname: 's3.local',
362
+ port: 9000,
363
+ path: '/bucket/key',
364
+ protocol: 'http:',
365
+ headers: {
366
+ host: 's3.local:9000'
367
+ }
368
+ });
369
+ await postSigningMiddleware({
370
+ request: mockRequest,
371
+ input: {}
372
+ });
373
+ expect(mockRequest.headers['x-s3-target-endpoint']).toBe('http://s3.local:9000');
374
+ });
342
375
  it('should preserve authorization header when routing through proxy', async ()=>{
343
376
  const mockNext = jest.fn().mockResolvedValue({
344
377
  output: {}
@@ -574,6 +607,7 @@ describe('createProxyMiddleware', ()=>{
574
607
  expect(mockRequest.hostname).toBe(originalHostname);
575
608
  expect(mockRequest.path).toBe(originalPath);
576
609
  expect(mockRequest.port).toBe(originalPort);
610
+ expect(mockRequest.headers['x-s3-target-endpoint']).toBeUndefined();
577
611
  });
578
612
  });
579
613
  });
@@ -52,6 +52,7 @@ const createProxyMiddleware = (config)=>{
52
52
  const resolvedProxyEndpoint = resolveProxyEndpoint(config.endpoint);
53
53
  const target = parseEndpoint(config.target);
54
54
  const proxy = parseEndpoint(resolvedProxyEndpoint);
55
+ const targetEndpointUrl = `${target.protocol}//${target.hostname}${80 !== target.port && 443 !== target.port ? `:${target.port}` : ''}`;
55
56
  return {
56
57
  applyToStack: (clientStack)=>{
57
58
  const preSigningMiddleware = (next)=>async (args)=>{
@@ -90,6 +91,7 @@ const createProxyMiddleware = (config)=>{
90
91
  request.protocol = proxy.protocol;
91
92
  request.path = '' === proxy.pathname || '/' === proxy.pathname ? request.path : proxy.pathname + request.path;
92
93
  request.headers.host = proxy.port && 80 !== proxy.port && 443 !== proxy.port ? `${proxy.hostname}:${proxy.port}` : proxy.hostname;
94
+ request.headers['x-s3-target-endpoint'] = targetEndpointUrl;
93
95
  request.query = queryParams;
94
96
  }
95
97
  return next(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/data-browser-library",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
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",
@@ -30,8 +30,7 @@
30
30
  "@aws-sdk/s3-presigned-post": "^3.983.0",
31
31
  "@aws-sdk/s3-request-presigner": "^3.983.0",
32
32
  "@hookform/resolvers": "^5.2.2",
33
- "@monaco-editor/react": "^4.7.0",
34
- "@scality/zenkoclient": "^2.0.0-preview.1",
33
+ "@scality/cloudserverclient": "^1.0.4",
35
34
  "@tanstack/react-query": "^5.8.0",
36
35
  "@tanstack/react-query-devtools": "^5.8.0",
37
36
  "@testing-library/user-event": "^14.6.1",
@@ -40,7 +39,7 @@
40
39
  "react-hook-form": "^7.48.0"
41
40
  },
42
41
  "peerDependencies": {
43
- "@scality/core-ui": ">=0.198.0",
42
+ "@scality/core-ui": ">=0.202.0",
44
43
  "react": ">=18.0.0",
45
44
  "react-dom": ">=18.0.0",
46
45
  "react-router": ">=7.1.3",
@@ -54,6 +53,7 @@
54
53
  "@testing-library/react": "^15.0.6",
55
54
  "@testing-library/user-event": "^14.6.1",
56
55
  "@types/jest": "^30.0.0",
56
+ "@types/json-schema": "^7.0.15",
57
57
  "@types/react": "^18.2.0",
58
58
  "@types/react-dom": "^18.2.0",
59
59
  "@types/styled-components": "^5.1.34",
@@ -1,12 +0,0 @@
1
- import { type BeforeMount, type EditorProps } from '@monaco-editor/react';
2
- interface EditorComponentProps {
3
- value: string;
4
- onChange: (value: string | undefined) => void;
5
- readOnly?: boolean;
6
- language?: string;
7
- height?: string;
8
- width?: string;
9
- beforeMount?: BeforeMount;
10
- }
11
- export declare const Editor: React.FC<EditorComponentProps & EditorProps>;
12
- export {};