@scality/data-browser-library 1.0.3 → 1.0.5

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 (51) hide show
  1. package/dist/components/DataBrowserUI.js +18 -8
  2. package/dist/components/__tests__/BucketList.test.js +74 -1
  3. package/dist/components/__tests__/ObjectList.test.js +94 -2
  4. package/dist/components/buckets/BucketCreate.d.ts +1 -0
  5. package/dist/components/buckets/BucketCreate.js +57 -7
  6. package/dist/components/buckets/BucketDetails.js +0 -1
  7. package/dist/components/buckets/BucketLifecycleFormPage.js +209 -213
  8. package/dist/components/buckets/BucketList.js +25 -4
  9. package/dist/components/buckets/BucketReplicationFormPage.js +9 -3
  10. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
  11. package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
  12. package/dist/components/objects/DeleteObjectButton.d.ts +1 -0
  13. package/dist/components/objects/DeleteObjectButton.js +11 -5
  14. package/dist/components/objects/ObjectDetails/FormComponents.d.ts +9 -0
  15. package/dist/components/objects/ObjectDetails/FormComponents.js +37 -0
  16. package/dist/components/objects/ObjectDetails/ObjectMetadata.js +182 -204
  17. package/dist/components/objects/ObjectDetails/ObjectSummary.js +22 -5
  18. package/dist/components/objects/ObjectDetails/ObjectTags.js +109 -154
  19. package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.d.ts +1 -0
  20. package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.js +230 -0
  21. package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.d.ts +1 -0
  22. package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.js +342 -0
  23. package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.d.ts +1 -0
  24. package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.js +202 -0
  25. package/dist/components/objects/ObjectDetails/index.d.ts +2 -1
  26. package/dist/components/objects/ObjectDetails/index.js +12 -16
  27. package/dist/components/objects/ObjectList.d.ts +3 -2
  28. package/dist/components/objects/ObjectList.js +204 -104
  29. package/dist/components/objects/ObjectPage.js +22 -5
  30. package/dist/components/ui/ArrayFieldActions.js +0 -2
  31. package/dist/components/ui/FilterFormSection.js +17 -36
  32. package/dist/components/ui/FormGrid.d.ts +7 -0
  33. package/dist/components/ui/FormGrid.js +37 -0
  34. package/dist/components/ui/Table.elements.js +1 -0
  35. package/dist/config/types.d.ts +45 -2
  36. package/dist/hooks/__tests__/usePresigningS3Client.test.d.ts +1 -0
  37. package/dist/hooks/__tests__/usePresigningS3Client.test.js +104 -0
  38. package/dist/hooks/factories/index.d.ts +1 -1
  39. package/dist/hooks/factories/index.js +2 -2
  40. package/dist/hooks/factories/useCreateS3MutationHook.d.ts +2 -1
  41. package/dist/hooks/factories/useCreateS3MutationHook.js +10 -3
  42. package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -0
  43. package/dist/hooks/factories/useCreateS3QueryHook.js +9 -6
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/index.js +2 -1
  46. package/dist/hooks/presignedOperations.js +4 -4
  47. package/dist/hooks/useBucketLocations.d.ts +6 -0
  48. package/dist/hooks/useBucketLocations.js +45 -0
  49. package/dist/hooks/usePresigningS3Client.d.ts +13 -0
  50. package/dist/hooks/usePresigningS3Client.js +21 -0
  51. package/package.json +4 -4
@@ -6,6 +6,7 @@ const Table = ({ children, ...props })=>/*#__PURE__*/ jsx(Box, {
6
6
  height: "100%",
7
7
  overflow: "auto",
8
8
  width: "100%",
9
+ paddingRight: spacing.r16,
9
10
  ...props,
10
11
  children: children
11
12
  });
@@ -144,12 +144,40 @@ export interface SectionConfig {
144
144
  render: () => React.ReactNode;
145
145
  }
146
146
  /**
147
- * Storage class selector props
147
+ * Base props for custom selector components injected into the library.
148
+ * `value` is the current selection; `onChange` is called when the user picks a new option.
148
149
  */
149
- export interface StorageClassSelectorProps {
150
+ interface SelectorProps {
150
151
  value: string;
151
152
  onChange: (newValue: string) => void;
152
153
  }
154
+ /**
155
+ * Props for a custom storage class selector used in replication and lifecycle forms.
156
+ * The component receives the current storage class value and must call `onChange` with the new value.
157
+ *
158
+ * `context` indicates which form is rendering the selector, allowing the consumer
159
+ * to apply different filtering logic (e.g. replication targets vs transition targets).
160
+ */
161
+ export interface StorageClassSelectorProps extends SelectorProps {
162
+ context: 'replication' | 'lifecycle';
163
+ }
164
+ /**
165
+ * Props for a custom location selector in bucket creation (maps to S3 `LocationConstraint`).
166
+ * The component receives the current location value and must call `onChange` with the new value.
167
+ */
168
+ export interface LocationSelectorProps extends SelectorProps {
169
+ }
170
+ /**
171
+ * Props passed to the custom versioning renderer in bucket creation.
172
+ * Provides commonly-needed form state as props for convenience.
173
+ *
174
+ * The component is rendered inside a `FormProvider`, so consumers can also call
175
+ * `useFormContext()` for full form access (register, setValue, watch, etc.).
176
+ */
177
+ export interface BucketCreateVersioningProps {
178
+ isVersioning: boolean;
179
+ isObjectLockEnabled: boolean;
180
+ }
153
181
  /**
154
182
  * Main DataBrowserUI component props
155
183
  */
@@ -161,6 +189,20 @@ export interface DataBrowserUIProps {
161
189
  */
162
190
  header?: React.ReactNode;
163
191
  storageClassSelector?: React.ComponentType<StorageClassSelectorProps>;
192
+ /** Label for the storage class selector. Defaults to "Storage Class". */
193
+ storageClassLabel?: string;
194
+ /** Custom location selector for bucket creation (LocationConstraint). */
195
+ locationSelector?: React.ComponentType<LocationSelectorProps>;
196
+ /** Extra form fields rendered after the location selector inside bucket creation. Components can use useFormContext() to access form state. */
197
+ bucketCreateExtraFields?: React.ReactNode;
198
+ /**
199
+ * Transform form data before the default bucket creation logic runs.
200
+ * The function receives all form values and must return the full object —
201
+ * only modify the fields you need (e.g. append ':ingest' suffix to locationConstraint).
202
+ */
203
+ transformBucketCreateData?: <T extends Record<string, unknown>>(data: T) => T;
204
+ /** Custom versioning section for bucket creation. When provided, replaces the default versioning FormGroup entirely. */
205
+ bucketCreateVersioning?: React.ComponentType<BucketCreateVersioningProps>;
164
206
  extraBucketListColumns?: ColumnConfig<Bucket>[];
165
207
  extraBucketListActions?: ActionConfig[];
166
208
  extraBucketTabs?: TabConfig[];
@@ -222,3 +264,4 @@ export interface DataBrowserUIProps {
222
264
  }
223
265
  export type S3EventType = 's3:ObjectCreated:*' | 's3:ObjectCreated:Put' | 's3:ObjectCreated:Post' | 's3:ObjectCreated:Copy' | 's3:ObjectCreated:CompleteMultipartUpload' | 's3:ObjectRemoved:*' | 's3:ObjectRemoved:Delete' | 's3:ObjectRemoved:DeleteMarkerCreated' | 's3:ObjectRestore:*' | 's3:ObjectRestore:Post' | 's3:ObjectRestore:Completed' | 's3:ObjectRestore:Delete' | 's3:LifecycleExpiration:*' | 's3:LifecycleExpiration:Delete' | 's3:LifecycleExpiration:DeleteMarkerCreated' | 's3:LifecycleTransition' | 's3:Replication:*' | 's3:Replication:OperationFailedReplication' | 's3:Replication:OperationMissedThreshold' | 's3:Replication:OperationReplicatedAfterThreshold' | 's3:Replication:OperationNotTracked' | 's3:ObjectTagging:*' | 's3:ObjectTagging:Put' | 's3:ObjectTagging:Delete' | 's3:ReducedRedundancyLostObject' | 's3:IntelligentTiering' | 's3:ObjectAcl:Put' | 's3:TestEvent';
224
266
  export type S3EventCategory = 'Object Creation' | 'Object Deletion' | 'Object Restoration' | 'Lifecycle' | 'Replication' | 'Object Tagging' | 'Storage & Access' | 'Testing';
267
+ export {};
@@ -0,0 +1,104 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { renderHook } from "@testing-library/react";
3
+ import { createTestWrapper, testConfig, testCredentials } from "../../test/testUtils.js";
4
+ import { usePresigningS3Client } from "../usePresigningS3Client.js";
5
+ jest.mock('@aws-sdk/client-s3');
6
+ const MockedS3Client = S3Client;
7
+ const proxyConfig = {
8
+ ...testConfig,
9
+ proxy: {
10
+ enabled: true,
11
+ endpoint: 'https://proxy.example.com/s3',
12
+ target: 'http://internal-s3.cluster.local'
13
+ }
14
+ };
15
+ describe('usePresigningS3Client', ()=>{
16
+ beforeEach(()=>{
17
+ jest.clearAllMocks();
18
+ MockedS3Client.mockImplementation(()=>({
19
+ config: {},
20
+ middlewareStack: {
21
+ use: jest.fn(),
22
+ add: jest.fn()
23
+ }
24
+ }));
25
+ });
26
+ it('should create an S3 client with the direct endpoint when no proxy is configured', ()=>{
27
+ renderHook(()=>usePresigningS3Client(), {
28
+ wrapper: createTestWrapper(testConfig, testCredentials)
29
+ });
30
+ expect(MockedS3Client).toHaveBeenCalledWith(expect.objectContaining({
31
+ endpoint: testConfig.endpoint,
32
+ region: testConfig.region,
33
+ forcePathStyle: true
34
+ }));
35
+ });
36
+ it('should create an S3 client with the proxy endpoint (not target) when proxy is enabled', ()=>{
37
+ renderHook(()=>usePresigningS3Client(), {
38
+ wrapper: createTestWrapper(proxyConfig, testCredentials)
39
+ });
40
+ expect(MockedS3Client).toHaveBeenCalledWith(expect.objectContaining({
41
+ endpoint: 'https://proxy.example.com/s3',
42
+ region: testConfig.region,
43
+ forcePathStyle: true
44
+ }));
45
+ const constructorCall = MockedS3Client.mock.calls[0][0];
46
+ expect(constructorCall?.endpoint).not.toContain('internal-s3.cluster.local');
47
+ });
48
+ it('should not attach proxy middleware even when proxy is enabled', ()=>{
49
+ const mockUse = jest.fn();
50
+ MockedS3Client.mockImplementation(()=>({
51
+ config: {},
52
+ middlewareStack: {
53
+ use: mockUse,
54
+ add: jest.fn()
55
+ }
56
+ }));
57
+ renderHook(()=>usePresigningS3Client(), {
58
+ wrapper: createTestWrapper(proxyConfig, testCredentials)
59
+ });
60
+ expect(mockUse).not.toHaveBeenCalled();
61
+ });
62
+ it('should use origin-relative proxy endpoint resolved with window.location.origin', ()=>{
63
+ const originRelativeConfig = {
64
+ ...testConfig,
65
+ proxy: {
66
+ enabled: true,
67
+ endpoint: '/zenko/s3',
68
+ target: 'http://artesca-data-connector-s3api.zenko.svc.cluster.local'
69
+ }
70
+ };
71
+ const originalOrigin = window.location.origin;
72
+ renderHook(()=>usePresigningS3Client(), {
73
+ wrapper: createTestWrapper(originRelativeConfig, testCredentials)
74
+ });
75
+ expect(MockedS3Client).toHaveBeenCalledWith(expect.objectContaining({
76
+ endpoint: `${originalOrigin}/zenko/s3`
77
+ }));
78
+ });
79
+ it('should use direct endpoint when proxy is disabled', ()=>{
80
+ const disabledProxyConfig = {
81
+ ...testConfig,
82
+ proxy: {
83
+ enabled: false,
84
+ endpoint: 'https://proxy.example.com/s3'
85
+ }
86
+ };
87
+ renderHook(()=>usePresigningS3Client(), {
88
+ wrapper: createTestWrapper(disabledProxyConfig, testCredentials)
89
+ });
90
+ expect(MockedS3Client).toHaveBeenCalledWith(expect.objectContaining({
91
+ endpoint: testConfig.endpoint
92
+ }));
93
+ });
94
+ it('should return the same client instance on re-render when s3ConfigIdentifier is unchanged', ()=>{
95
+ const { result, rerender } = renderHook(()=>usePresigningS3Client(), {
96
+ wrapper: createTestWrapper(testConfig, testCredentials)
97
+ });
98
+ const firstClient = result.current;
99
+ rerender();
100
+ const secondClient = result.current;
101
+ expect(firstClient).toBe(secondClient);
102
+ expect(MockedS3Client).toHaveBeenCalledTimes(1);
103
+ });
104
+ });
@@ -14,5 +14,5 @@
14
14
  */
15
15
  export { useCreateS3InfiniteQueryHook } from './useCreateS3InfiniteQueryHook';
16
16
  export { useCreateS3LoginHook } from './useCreateS3LoginHook';
17
- export { useCreateS3FunctionMutationHook, useCreateS3MutationHook, } from './useCreateS3MutationHook';
17
+ export { useCreatePresigningMutationHook, useCreateS3FunctionMutationHook, useCreateS3MutationHook, } from './useCreateS3MutationHook';
18
18
  export { useCreateS3QueryHook } from './useCreateS3QueryHook';
@@ -1,5 +1,5 @@
1
1
  import { useCreateS3InfiniteQueryHook } from "./useCreateS3InfiniteQueryHook.js";
2
2
  import { useCreateS3LoginHook } from "./useCreateS3LoginHook.js";
3
- import { useCreateS3FunctionMutationHook, useCreateS3MutationHook } from "./useCreateS3MutationHook.js";
3
+ import { useCreatePresigningMutationHook, useCreateS3FunctionMutationHook, useCreateS3MutationHook } from "./useCreateS3MutationHook.js";
4
4
  import { useCreateS3QueryHook } from "./useCreateS3QueryHook.js";
5
- export { useCreateS3FunctionMutationHook, useCreateS3InfiniteQueryHook, useCreateS3LoginHook, useCreateS3MutationHook, useCreateS3QueryHook };
5
+ export { useCreatePresigningMutationHook, useCreateS3FunctionMutationHook, useCreateS3InfiniteQueryHook, useCreateS3LoginHook, useCreateS3MutationHook, useCreateS3QueryHook };
@@ -2,4 +2,5 @@ import type { S3Client } from '@aws-sdk/client-s3';
2
2
  import { type UseMutationOptions, type UseMutationResult } from '@tanstack/react-query';
3
3
  import { type EnhancedS3Error } from '../../utils/errorHandling';
4
4
  export declare function useCreateS3MutationHook<TInput extends object, TOutput>(Command: new (input: TInput) => any, operationName: string, invalidationKeys?: string[]): (options?: Omit<UseMutationOptions<TOutput, EnhancedS3Error, TInput>, "mutationFn">) => UseMutationResult<TOutput, EnhancedS3Error, TInput>;
5
- export declare function useCreateS3FunctionMutationHook<TInput, TOutput>(operation: (s3Client: S3Client, input: TInput) => Promise<TOutput>, invalidationKeys?: string[]): (options?: Omit<UseMutationOptions<TOutput, EnhancedS3Error, TInput>, "mutationFn">) => UseMutationResult<TOutput, EnhancedS3Error, TInput>;
5
+ export declare function useCreateS3FunctionMutationHook<TInput, TOutput>(operation: (s3Client: S3Client, input: TInput) => Promise<TOutput>, invalidationKeys?: string[]): (options?: Omit<UseMutationOptions<TOutput, EnhancedS3Error, TInput, unknown>, "mutationFn"> | undefined) => UseMutationResult<TOutput, EnhancedS3Error, TInput>;
6
+ export declare function useCreatePresigningMutationHook<TInput, TOutput>(operation: (s3Client: S3Client, input: TInput) => Promise<TOutput>, invalidationKeys?: string[]): (options?: Omit<UseMutationOptions<TOutput, EnhancedS3Error, TInput, unknown>, "mutationFn"> | undefined) => UseMutationResult<TOutput, EnhancedS3Error, TInput>;
@@ -1,6 +1,7 @@
1
1
  import { useMutation, useQueryClient } from "@tanstack/react-query";
2
2
  import { useDataBrowserContext } from "../../components/providers/DataBrowserProvider.js";
3
3
  import { createS3OperationError } from "../../utils/errorHandling.js";
4
+ import { usePresigningS3Client } from "../usePresigningS3Client.js";
4
5
  import { useS3Client } from "../useS3Client.js";
5
6
  function useCreateS3MutationHook(Command, operationName, invalidationKeys) {
6
7
  return (options)=>{
@@ -31,10 +32,10 @@ function useCreateS3MutationHook(Command, operationName, invalidationKeys) {
31
32
  });
32
33
  };
33
34
  }
34
- function useCreateS3FunctionMutationHook(operation, invalidationKeys) {
35
+ function createFunctionMutationHook(operation, useClient, invalidationKeys) {
35
36
  return (options)=>{
36
37
  const { s3ConfigIdentifier } = useDataBrowserContext();
37
- const s3Client = useS3Client();
38
+ const s3Client = useClient();
38
39
  const queryClient = useQueryClient();
39
40
  return useMutation({
40
41
  mutationFn: async (params)=>operation(s3Client, params),
@@ -52,4 +53,10 @@ function useCreateS3FunctionMutationHook(operation, invalidationKeys) {
52
53
  });
53
54
  };
54
55
  }
55
- export { useCreateS3FunctionMutationHook, useCreateS3MutationHook };
56
+ function useCreateS3FunctionMutationHook(operation, invalidationKeys) {
57
+ return createFunctionMutationHook(operation, useS3Client, invalidationKeys);
58
+ }
59
+ function useCreatePresigningMutationHook(operation, invalidationKeys) {
60
+ return createFunctionMutationHook(operation, usePresigningS3Client, invalidationKeys);
61
+ }
62
+ export { useCreatePresigningMutationHook, useCreateS3FunctionMutationHook, useCreateS3MutationHook };
@@ -1,3 +1,4 @@
1
1
  import { type QueryKey, type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query';
2
2
  import { type EnhancedS3Error } from '../../utils/errorHandling';
3
+ export declare function createS3QueryKey(s3ConfigIdentifier: string, operationName: string, params?: object): QueryKey;
3
4
  export declare function useCreateS3QueryHook<TInput extends object, TOutput>(Command: new (input: TInput) => any, operationName: string): (params?: TInput, options?: Omit<UseQueryOptions<TOutput, EnhancedS3Error, TOutput, QueryKey>, "queryKey" | "queryFn">) => UseQueryResult<TOutput, EnhancedS3Error>;
@@ -21,15 +21,18 @@ function getEmptyConfigForOperation(operationName) {
21
21
  return null;
22
22
  }
23
23
  }
24
+ function createS3QueryKey(s3ConfigIdentifier, operationName, params) {
25
+ return [
26
+ s3ConfigIdentifier,
27
+ operationName,
28
+ params
29
+ ];
30
+ }
24
31
  function useCreateS3QueryHook(Command, operationName) {
25
32
  return (params, options)=>{
26
33
  const { s3ConfigIdentifier } = useDataBrowserContext();
27
34
  const s3Client = useS3Client();
28
- const queryKey = [
29
- s3ConfigIdentifier,
30
- operationName,
31
- params
32
- ];
35
+ const queryKey = createS3QueryKey(s3ConfigIdentifier, operationName, params);
33
36
  return useQuery({
34
37
  queryKey,
35
38
  queryFn: async ({ signal })=>{
@@ -53,4 +56,4 @@ function useCreateS3QueryHook(Command, operationName) {
53
56
  });
54
57
  };
55
58
  }
56
- export { useCreateS3QueryHook };
59
+ export { createS3QueryKey, useCreateS3QueryHook };
@@ -7,6 +7,7 @@ export { useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, }
7
7
  export { getAccessibleBucketsStorageKey, getLimitedAccessFlagKey, setLimitedAccessFlag, useAccessibleBuckets, } from './useAccessibleBuckets';
8
8
  export { useBatchObjectLegalHold } from './useBatchObjectLegalHold';
9
9
  export { type BucketConfigEditorConfig, type BucketConfigEditorResult, useBucketConfigEditor, } from './useBucketConfigEditor';
10
+ export { useBucketLocations } from './useBucketLocations';
10
11
  export { useDeleteBucketConfigRule } from './useDeleteBucketConfigRule';
11
12
  export { useEmptyBucket } from './useEmptyBucket';
12
13
  export { useFeatures } from './useFeatures';
@@ -6,6 +6,7 @@ import { useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload } f
6
6
  import { getAccessibleBucketsStorageKey, getLimitedAccessFlagKey, setLimitedAccessFlag, useAccessibleBuckets } from "./useAccessibleBuckets.js";
7
7
  import { useBatchObjectLegalHold } from "./useBatchObjectLegalHold.js";
8
8
  import { useBucketConfigEditor } from "./useBucketConfigEditor.js";
9
+ import { useBucketLocations } from "./useBucketLocations.js";
9
10
  import { useDeleteBucketConfigRule } from "./useDeleteBucketConfigRule.js";
10
11
  import { useEmptyBucket } from "./useEmptyBucket.js";
11
12
  import { useFeatures } from "./useFeatures.js";
@@ -16,4 +17,4 @@ import { useS3Client } from "./useS3Client.js";
16
17
  import { useS3ConfigSwitch } from "./useS3ConfigSwitch.js";
17
18
  import { useSupportedNotificationEvents } from "./useSupportedNotificationEvents.js";
18
19
  import { useTableRowSelection } from "./useTableRowSelection.js";
19
- export { getAccessibleBucketsStorageKey, getLimitedAccessFlagKey, setLimitedAccessFlag, useAccessibleBuckets, useBatchObjectLegalHold, useBucketConfigEditor, useBuckets, useCopyObject, useCreateBucket, useCreateFolder, useDeleteBucket, useDeleteBucketConfigRule, useDeleteBucketCors, useDeleteBucketLifecycle, useDeleteBucketPolicy, useDeleteBucketReplication, useDeleteBucketTagging, useDeleteObject, useDeleteObjectTagging, useDeleteObjects, useEmptyBucket, useFeatures, useGetBucketAcl, useGetBucketCors, useGetBucketEncryption, useGetBucketLifecycle, useGetBucketLocation, useGetBucketNotification, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning, useGetObject, useGetObjectAttributes, useGetObjectTorrent, useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, useGetPublicAccessBlock, useISVBucketStatus, useIsBucketEmpty, useLimitedAccessFlow, useListMultipartUploads, useListObjectVersions, useListObjects, useLoginMutation, useObjectAcl, useObjectLegalHold, useObjectMetadata, useObjectRetention, useObjectTagging, usePutObject, useRestoreObject, useS3Client, useS3ConfigSwitch, useSearchObjects, useSearchObjectsVersions, useSelectObjectContent, useSetBucketAcl, useSetBucketCors, useSetBucketEncryption, useSetBucketLifecycle, useSetBucketNotification, useSetBucketObjectLockConfiguration, useSetBucketPolicy, useSetBucketReplication, useSetBucketTagging, useSetBucketVersioning, useSetObjectAcl, useSetObjectLegalHold, useSetObjectRetention, useSetObjectTagging, useSupportedNotificationEvents, useTableRowSelection, useUploadObjects, useValidateBucketAccess };
20
+ export { getAccessibleBucketsStorageKey, getLimitedAccessFlagKey, setLimitedAccessFlag, useAccessibleBuckets, useBatchObjectLegalHold, useBucketConfigEditor, useBucketLocations, useBuckets, useCopyObject, useCreateBucket, useCreateFolder, useDeleteBucket, useDeleteBucketConfigRule, useDeleteBucketCors, useDeleteBucketLifecycle, useDeleteBucketPolicy, useDeleteBucketReplication, useDeleteBucketTagging, useDeleteObject, useDeleteObjectTagging, useDeleteObjects, useEmptyBucket, useFeatures, useGetBucketAcl, useGetBucketCors, useGetBucketEncryption, useGetBucketLifecycle, useGetBucketLocation, useGetBucketNotification, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning, useGetObject, useGetObjectAttributes, useGetObjectTorrent, useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, useGetPublicAccessBlock, useISVBucketStatus, useIsBucketEmpty, useLimitedAccessFlow, useListMultipartUploads, useListObjectVersions, useListObjects, useLoginMutation, useObjectAcl, useObjectLegalHold, useObjectMetadata, useObjectRetention, useObjectTagging, usePutObject, useRestoreObject, useS3Client, useS3ConfigSwitch, useSearchObjects, useSearchObjectsVersions, useSelectObjectContent, useSetBucketAcl, useSetBucketCors, useSetBucketEncryption, useSetBucketLifecycle, useSetBucketNotification, useSetBucketObjectLockConfiguration, useSetBucketPolicy, useSetBucketReplication, useSetBucketTagging, useSetBucketVersioning, useSetObjectAcl, useSetObjectLegalHold, useSetObjectRetention, useSetObjectTagging, useSupportedNotificationEvents, useTableRowSelection, useUploadObjects, useValidateBucketAccess };
@@ -2,7 +2,7 @@ import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
2
2
  import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
3
3
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
4
4
  import { createS3OperationError } from "../utils/errorHandling.js";
5
- import { useCreateS3FunctionMutationHook } from "./factories/index.js";
5
+ import { useCreatePresigningMutationHook } from "./factories/index.js";
6
6
  const generatePresignedDownloadUrl = async (client, config)=>{
7
7
  try {
8
8
  const { Bucket, Key, expiresIn = 3600, ...awsOptions } = config;
@@ -66,7 +66,7 @@ const generatePresignedPost = async (client, config)=>{
66
66
  throw createS3OperationError(error, 'GeneratePresignedPost', config.Bucket, config.Key);
67
67
  }
68
68
  };
69
- const useGetPresignedDownload = useCreateS3FunctionMutationHook(generatePresignedDownloadUrl);
70
- const useGetPresignedUpload = useCreateS3FunctionMutationHook(generatePresignedUploadUrl);
71
- const useGetPresignedPost = useCreateS3FunctionMutationHook(generatePresignedPost);
69
+ const useGetPresignedDownload = useCreatePresigningMutationHook(generatePresignedDownloadUrl);
70
+ const useGetPresignedUpload = useCreatePresigningMutationHook(generatePresignedUploadUrl);
71
+ const useGetPresignedPost = useCreatePresigningMutationHook(generatePresignedPost);
72
72
  export { useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Hook for fetching locations of all provided buckets in parallel.
3
+ * Shares query cache with useGetBucketLocation (same query keys via createS3QueryKey).
4
+ * Returns a Map<bucketName, locationConstraint>.
5
+ */
6
+ export declare const useBucketLocations: (bucketNames: string[]) => Map<string, string>;
@@ -0,0 +1,45 @@
1
+ import { GetBucketLocationCommand } from "@aws-sdk/client-s3";
2
+ import { useQueries } from "@tanstack/react-query";
3
+ import { useMemo } from "react";
4
+ import { useDataBrowserContext } from "../components/providers/DataBrowserProvider.js";
5
+ import { createS3OperationError, shouldRetryError } from "../utils/errorHandling.js";
6
+ import { createS3QueryKey } from "./factories/useCreateS3QueryHook.js";
7
+ import { useS3Client } from "./useS3Client.js";
8
+ const useBucketLocations = (bucketNames)=>{
9
+ const { s3ConfigIdentifier } = useDataBrowserContext();
10
+ const s3Client = useS3Client();
11
+ const results = useQueries({
12
+ queries: bucketNames.map((name)=>({
13
+ queryKey: createS3QueryKey(s3ConfigIdentifier, 'GetBucketLocation', {
14
+ Bucket: name
15
+ }),
16
+ queryFn: async ({ signal })=>{
17
+ try {
18
+ return await s3Client.send(new GetBucketLocationCommand({
19
+ Bucket: name
20
+ }), {
21
+ abortSignal: signal
22
+ });
23
+ } catch (error) {
24
+ throw createS3OperationError(error, 'GetBucketLocation', name);
25
+ }
26
+ },
27
+ retry: (failureCount, error)=>shouldRetryError(error, failureCount)
28
+ }))
29
+ });
30
+ const locationsKey = results.map((r, i)=>{
31
+ if ('success' === r.status && r.data) return `${bucketNames[i]}=${r.data.LocationConstraint || 'us-east-1'}`;
32
+ return `${bucketNames[i]}:${r.status}`;
33
+ }).join(',');
34
+ return useMemo(()=>{
35
+ const map = new Map();
36
+ for(let i = 0; i < bucketNames.length; i++){
37
+ const result = results[i];
38
+ if ('success' === result.status && result.data) map.set(bucketNames[i], result.data.LocationConstraint || 'us-east-1');
39
+ }
40
+ return map;
41
+ }, [
42
+ locationsKey
43
+ ]);
44
+ };
45
+ export { useBucketLocations };
@@ -0,0 +1,13 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ /**
3
+ * Hook to get an S3 client specifically for presigned URL generation.
4
+ *
5
+ * When proxy is configured, presigned URLs must use the proxy endpoint
6
+ * (browser-accessible) rather than the internal target. This client is
7
+ * created WITHOUT proxy middleware so that `getSignedUrl` produces URLs
8
+ * pointing to the proxy endpoint instead of the internal S3 service.
9
+ *
10
+ * For non-proxy configurations, this returns a standard S3 client
11
+ * identical to `useS3Client`.
12
+ */
13
+ export declare const usePresigningS3Client: () => S3Client;
@@ -0,0 +1,21 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ import { useMemo } from "react";
3
+ import { useDataBrowserContext } from "../components/providers/DataBrowserProvider.js";
4
+ import { resolveProxyEndpoint } from "../utils/proxyMiddleware.js";
5
+ const usePresigningS3Client = ()=>{
6
+ const { s3ConfigIdentifier, getS3Config } = useDataBrowserContext();
7
+ if (!getS3Config) throw new Error('usePresigningS3Client: S3 config not available. Ensure DataBrowserProvider has getS3Config prop set.');
8
+ return useMemo(()=>{
9
+ const config = getS3Config();
10
+ const endpoint = config.proxy?.enabled ? resolveProxyEndpoint(config.proxy.endpoint) : config.endpoint;
11
+ return new S3Client({
12
+ endpoint,
13
+ credentials: config.credentials,
14
+ forcePathStyle: config.forcePathStyle ?? true,
15
+ region: config.region
16
+ });
17
+ }, [
18
+ s3ConfigIdentifier
19
+ ]);
20
+ };
21
+ export { usePresigningS3Client };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/data-browser-library",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",
@@ -25,10 +25,10 @@
25
25
  "clean": "rm -rf dist"
26
26
  },
27
27
  "dependencies": {
28
- "@aws-sdk/client-s3": "^3.478.0",
28
+ "@aws-sdk/client-s3": "^3.983.0",
29
29
  "@aws-sdk/protocol-http": "^3.370.0",
30
- "@aws-sdk/s3-presigned-post": "^3.888.0",
31
- "@aws-sdk/s3-request-presigner": "^3.478.0",
30
+ "@aws-sdk/s3-presigned-post": "^3.983.0",
31
+ "@aws-sdk/s3-request-presigner": "^3.983.0",
32
32
  "@hookform/resolvers": "^5.2.2",
33
33
  "@monaco-editor/react": "^4.7.0",
34
34
  "@scality/zenkoclient": "^2.0.0-preview.1",