@scality/data-browser-library 1.0.4 → 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.
- package/dist/components/DataBrowserUI.js +18 -8
- package/dist/components/__tests__/BucketList.test.js +74 -1
- package/dist/components/__tests__/ObjectList.test.js +94 -2
- package/dist/components/buckets/BucketCreate.d.ts +1 -0
- package/dist/components/buckets/BucketCreate.js +57 -7
- package/dist/components/buckets/BucketDetails.js +0 -1
- package/dist/components/buckets/BucketLifecycleFormPage.js +209 -213
- package/dist/components/buckets/BucketList.js +25 -4
- package/dist/components/buckets/BucketReplicationFormPage.js +9 -3
- package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
- package/dist/components/buckets/notifications/BucketNotificationList.js +1 -1
- package/dist/components/objects/DeleteObjectButton.d.ts +1 -0
- package/dist/components/objects/DeleteObjectButton.js +11 -5
- package/dist/components/objects/ObjectDetails/FormComponents.d.ts +9 -0
- package/dist/components/objects/ObjectDetails/FormComponents.js +37 -0
- package/dist/components/objects/ObjectDetails/ObjectMetadata.js +182 -204
- package/dist/components/objects/ObjectDetails/ObjectSummary.js +22 -5
- package/dist/components/objects/ObjectDetails/ObjectTags.js +109 -154
- package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +3 -3
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectMetadata.test.js +230 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/ObjectTags.test.js +342 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.d.ts +1 -0
- package/dist/components/objects/ObjectDetails/__tests__/formUtils.test.js +202 -0
- package/dist/components/objects/ObjectDetails/index.d.ts +2 -1
- package/dist/components/objects/ObjectDetails/index.js +12 -16
- package/dist/components/objects/ObjectList.d.ts +3 -2
- package/dist/components/objects/ObjectList.js +204 -104
- package/dist/components/objects/ObjectPage.js +22 -5
- package/dist/components/ui/ArrayFieldActions.js +0 -2
- package/dist/components/ui/FilterFormSection.js +17 -36
- package/dist/components/ui/FormGrid.d.ts +7 -0
- package/dist/components/ui/FormGrid.js +37 -0
- package/dist/components/ui/Table.elements.js +1 -0
- package/dist/config/types.d.ts +45 -2
- package/dist/hooks/__tests__/usePresigningS3Client.test.d.ts +1 -0
- package/dist/hooks/__tests__/usePresigningS3Client.test.js +104 -0
- package/dist/hooks/factories/index.d.ts +1 -1
- package/dist/hooks/factories/index.js +2 -2
- package/dist/hooks/factories/useCreateS3MutationHook.d.ts +2 -1
- package/dist/hooks/factories/useCreateS3MutationHook.js +10 -3
- package/dist/hooks/factories/useCreateS3QueryHook.d.ts +1 -0
- package/dist/hooks/factories/useCreateS3QueryHook.js +9 -6
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/presignedOperations.js +4 -4
- package/dist/hooks/useBucketLocations.d.ts +6 -0
- package/dist/hooks/useBucketLocations.js +45 -0
- package/dist/hooks/usePresigningS3Client.d.ts +13 -0
- package/dist/hooks/usePresigningS3Client.js +21 -0
- package/package.json +1 -1
package/dist/config/types.d.ts
CHANGED
|
@@ -144,12 +144,40 @@ export interface SectionConfig {
|
|
|
144
144
|
render: () => React.ReactNode;
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
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
|
-
|
|
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 @@
|
|
|
1
|
+
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
|
|
35
|
+
function createFunctionMutationHook(operation, useClient, invalidationKeys) {
|
|
35
36
|
return (options)=>{
|
|
36
37
|
const { s3ConfigIdentifier } = useDataBrowserContext();
|
|
37
|
-
const s3Client =
|
|
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
|
-
|
|
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 };
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/hooks/index.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
70
|
-
const useGetPresignedUpload =
|
|
71
|
-
const useGetPresignedPost =
|
|
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 };
|