@scality/data-browser-library 1.0.0-preview.7 → 1.0.0-preview.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/components/__tests__/BucketCreate.test.d.ts +1 -0
  2. package/dist/components/__tests__/BucketCreate.test.js +408 -0
  3. package/dist/components/__tests__/BucketLifecycleFormPage.test.d.ts +1 -0
  4. package/dist/components/__tests__/BucketLifecycleFormPage.test.js +618 -0
  5. package/dist/components/__tests__/BucketLifecycleList.test.d.ts +1 -0
  6. package/dist/components/__tests__/BucketLifecycleList.test.js +325 -0
  7. package/dist/components/__tests__/BucketList.test.js +190 -0
  8. package/dist/components/__tests__/BucketOverview.test.js +298 -8
  9. package/dist/components/__tests__/BucketReplicationFormPage.test.d.ts +1 -0
  10. package/dist/components/__tests__/BucketReplicationFormPage.test.js +1757 -0
  11. package/dist/components/__tests__/BucketReplicationList.test.d.ts +1 -0
  12. package/dist/components/__tests__/BucketReplicationList.test.js +344 -0
  13. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.d.ts +1 -0
  14. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +196 -0
  15. package/dist/components/__tests__/EmptyBucketButton.test.d.ts +1 -0
  16. package/dist/components/__tests__/EmptyBucketButton.test.js +302 -0
  17. package/dist/components/buckets/BucketCreate.d.ts +49 -0
  18. package/dist/components/buckets/BucketCreate.js +237 -0
  19. package/dist/components/buckets/BucketDetails.js +62 -10
  20. package/dist/components/buckets/BucketLifecycleFormPage.d.ts +15 -0
  21. package/dist/components/buckets/BucketLifecycleFormPage.js +1070 -0
  22. package/dist/components/buckets/BucketLifecycleList.d.ts +10 -0
  23. package/dist/components/buckets/BucketLifecycleList.js +270 -0
  24. package/dist/components/buckets/BucketList.d.ts +5 -2
  25. package/dist/components/buckets/BucketList.js +38 -28
  26. package/dist/components/buckets/BucketOverview.d.ts +65 -4
  27. package/dist/components/buckets/BucketOverview.js +261 -179
  28. package/dist/components/buckets/BucketPage.js +1 -1
  29. package/dist/components/buckets/BucketReplicationFormPage.d.ts +1 -0
  30. package/dist/components/buckets/BucketReplicationFormPage.js +834 -0
  31. package/dist/components/buckets/BucketReplicationList.d.ts +11 -0
  32. package/dist/components/buckets/BucketReplicationList.js +189 -0
  33. package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +18 -0
  34. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +53 -0
  35. package/dist/components/buckets/EmptyBucketButton.d.ts +5 -0
  36. package/dist/components/buckets/EmptyBucketButton.js +232 -0
  37. package/dist/components/buckets/EmptyBucketSummary.d.ts +9 -0
  38. package/dist/components/buckets/EmptyBucketSummary.js +60 -0
  39. package/dist/components/buckets/EmptyBucketSummaryList.d.ts +13 -0
  40. package/dist/components/buckets/EmptyBucketSummaryList.js +140 -0
  41. package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +8 -8
  42. package/dist/components/index.d.ts +8 -1
  43. package/dist/components/index.js +9 -2
  44. package/dist/components/objects/ObjectLock/EditRetentionButton.d.ts +4 -0
  45. package/dist/components/objects/ObjectLock/EditRetentionButton.js +32 -0
  46. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.d.ts +3 -0
  47. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +211 -0
  48. package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +9 -0
  49. package/dist/components/objects/ObjectLock/ObjectLockSettings.js +158 -0
  50. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +8 -0
  51. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +39 -0
  52. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.d.ts +1 -0
  53. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +204 -0
  54. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.d.ts +1 -0
  55. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +374 -0
  56. package/dist/components/ui/ArrayFieldActions.d.ts +36 -0
  57. package/dist/components/ui/ArrayFieldActions.js +38 -0
  58. package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +16 -0
  59. package/dist/components/ui/ConfirmDeleteRuleModal.js +43 -0
  60. package/dist/components/ui/FilterFormSection.d.ts +44 -0
  61. package/dist/components/ui/FilterFormSection.js +159 -0
  62. package/dist/config/factory.d.ts +13 -2
  63. package/dist/config/factory.js +9 -6
  64. package/dist/hooks/__tests__/useISVBucketDetection.test.d.ts +1 -0
  65. package/dist/hooks/__tests__/useISVBucketDetection.test.js +188 -0
  66. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +44 -1
  67. package/dist/hooks/factories/useCreateS3QueryHook.js +22 -1
  68. package/dist/hooks/index.d.ts +4 -0
  69. package/dist/hooks/index.js +5 -1
  70. package/dist/hooks/useDeleteBucketConfigRule.d.ts +26 -0
  71. package/dist/hooks/useDeleteBucketConfigRule.js +46 -0
  72. package/dist/hooks/useEmptyBucket.d.ts +27 -0
  73. package/dist/hooks/useEmptyBucket.js +116 -0
  74. package/dist/hooks/useISVBucketDetection.d.ts +15 -0
  75. package/dist/hooks/useISVBucketDetection.js +27 -0
  76. package/dist/hooks/useTableRowSelection.d.ts +9 -0
  77. package/dist/hooks/useTableRowSelection.js +45 -0
  78. package/dist/test/setup.js +8 -0
  79. package/dist/test/testUtils.d.ts +99 -17
  80. package/dist/test/testUtils.js +64 -16
  81. package/dist/test/utils/errorHandling.test.js +39 -1
  82. package/dist/utils/constants.d.ts +12 -0
  83. package/dist/utils/constants.js +9 -0
  84. package/dist/utils/errorHandling.d.ts +9 -0
  85. package/dist/utils/errorHandling.js +6 -1
  86. package/dist/utils/index.d.ts +2 -0
  87. package/dist/utils/index.js +2 -0
  88. package/dist/utils/s3RuleUtils.d.ts +53 -0
  89. package/dist/utils/s3RuleUtils.js +101 -0
  90. package/package.json +1 -1
@@ -2,6 +2,10 @@ export { useS3Client } from "./useS3Client";
2
2
  export type { LoginConfig, LoginMutationResult } from "./useLoginMutation";
3
3
  export { useLoginMutation } from "./loginOperations";
4
4
  export { useIsBucketEmpty } from "./useIsBucketEmpty";
5
+ export { useEmptyBucket } from "./useEmptyBucket";
6
+ export { useDeleteBucketConfigRule } from "./useDeleteBucketConfigRule";
7
+ export { useTableRowSelection } from "./useTableRowSelection";
8
+ export { useISVBucketStatus } from "./useISVBucketDetection";
5
9
  export { useBuckets, useGetBucketLocation, useCreateBucket, useDeleteBucket, } from "./bucketOperations";
6
10
  export { useGetBucketAcl, useSetBucketAcl, useGetBucketPolicy, useSetBucketPolicy, useDeleteBucketPolicy, useGetBucketVersioning, useSetBucketVersioning, useGetBucketCors, useSetBucketCors, useDeleteBucketCors, useGetBucketLifecycle, useSetBucketLifecycle, useDeleteBucketLifecycle, useGetBucketNotification, useSetBucketNotification, useGetBucketEncryption, useSetBucketEncryption, useGetBucketTagging, useSetBucketTagging, useDeleteBucketTagging, useGetBucketObjectLockConfiguration, useSetBucketObjectLockConfiguration, useGetBucketReplication, useSetBucketReplication, useDeleteBucketReplication, } from "./bucketConfiguration";
7
11
  export { useListObjects, useListObjectVersions, useObjectMetadata, useObjectRetention, useGetObject, usePutObject, useCreateFolder, useUploadObjects, useDeleteObject, useDeleteObjects, useCopyObject, useSetObjectRetention, useObjectLegalHold, useSetObjectLegalHold, useObjectTagging, useSetObjectTagging, useDeleteObjectTagging, useObjectAcl, useSetObjectAcl, useGetObjectAttributes, useGetObjectTorrent, useRestoreObject, useSelectObjectContent, useListMultipartUploads, useSearchObjects, useSearchObjectsVersions, } from "./objectOperations";
@@ -1,8 +1,12 @@
1
1
  import { useS3Client } from "./useS3Client.js";
2
2
  import { useLoginMutation } from "./loginOperations.js";
3
3
  import { useIsBucketEmpty } from "./useIsBucketEmpty.js";
4
+ import { useEmptyBucket } from "./useEmptyBucket.js";
5
+ import { useDeleteBucketConfigRule } from "./useDeleteBucketConfigRule.js";
6
+ import { useTableRowSelection } from "./useTableRowSelection.js";
7
+ import { useISVBucketStatus } from "./useISVBucketDetection.js";
4
8
  import { useBuckets, useCreateBucket, useDeleteBucket, useGetBucketLocation } from "./bucketOperations.js";
5
9
  import { useDeleteBucketCors, useDeleteBucketLifecycle, useDeleteBucketPolicy, useDeleteBucketReplication, useDeleteBucketTagging, useGetBucketAcl, useGetBucketCors, useGetBucketEncryption, useGetBucketLifecycle, useGetBucketNotification, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning, useSetBucketAcl, useSetBucketCors, useSetBucketEncryption, useSetBucketLifecycle, useSetBucketNotification, useSetBucketObjectLockConfiguration, useSetBucketPolicy, useSetBucketReplication, useSetBucketTagging, useSetBucketVersioning } from "./bucketConfiguration.js";
6
10
  import { useCopyObject, useCreateFolder, useDeleteObject, useDeleteObjectTagging, useDeleteObjects, useGetObject, useGetObjectAttributes, useGetObjectTorrent, useListMultipartUploads, useListObjectVersions, useListObjects, useObjectAcl, useObjectLegalHold, useObjectMetadata, useObjectRetention, useObjectTagging, usePutObject, useRestoreObject, useSearchObjects, useSearchObjectsVersions, useSelectObjectContent, useSetObjectAcl, useSetObjectLegalHold, useSetObjectRetention, useSetObjectTagging, useUploadObjects } from "./objectOperations.js";
7
11
  import { useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload } from "./presignedOperations.js";
8
- export { useBuckets, useCopyObject, useCreateBucket, useCreateFolder, useDeleteBucket, useDeleteBucketCors, useDeleteBucketLifecycle, useDeleteBucketPolicy, useDeleteBucketReplication, useDeleteBucketTagging, useDeleteObject, useDeleteObjectTagging, useDeleteObjects, useGetBucketAcl, useGetBucketCors, useGetBucketEncryption, useGetBucketLifecycle, useGetBucketLocation, useGetBucketNotification, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning, useGetObject, useGetObjectAttributes, useGetObjectTorrent, useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, useIsBucketEmpty, useListMultipartUploads, useListObjectVersions, useListObjects, useLoginMutation, useObjectAcl, useObjectLegalHold, useObjectMetadata, useObjectRetention, useObjectTagging, usePutObject, useRestoreObject, useS3Client, useSearchObjects, useSearchObjectsVersions, useSelectObjectContent, useSetBucketAcl, useSetBucketCors, useSetBucketEncryption, useSetBucketLifecycle, useSetBucketNotification, useSetBucketObjectLockConfiguration, useSetBucketPolicy, useSetBucketReplication, useSetBucketTagging, useSetBucketVersioning, useSetObjectAcl, useSetObjectLegalHold, useSetObjectRetention, useSetObjectTagging, useUploadObjects };
12
+ export { useBuckets, useCopyObject, useCreateBucket, useCreateFolder, useDeleteBucket, useDeleteBucketConfigRule, useDeleteBucketCors, useDeleteBucketLifecycle, useDeleteBucketPolicy, useDeleteBucketReplication, useDeleteBucketTagging, useDeleteObject, useDeleteObjectTagging, useDeleteObjects, useEmptyBucket, useGetBucketAcl, useGetBucketCors, useGetBucketEncryption, useGetBucketLifecycle, useGetBucketLocation, useGetBucketNotification, useGetBucketObjectLockConfiguration, useGetBucketPolicy, useGetBucketReplication, useGetBucketTagging, useGetBucketVersioning, useGetObject, useGetObjectAttributes, useGetObjectTorrent, useGetPresignedDownload, useGetPresignedPost, useGetPresignedUpload, useISVBucketStatus, useIsBucketEmpty, useListMultipartUploads, useListObjectVersions, useListObjects, useLoginMutation, useObjectAcl, useObjectLegalHold, useObjectMetadata, useObjectRetention, useObjectTagging, usePutObject, useRestoreObject, useS3Client, useSearchObjects, useSearchObjectsVersions, useSelectObjectContent, useSetBucketAcl, useSetBucketCors, useSetBucketEncryption, useSetBucketLifecycle, useSetBucketNotification, useSetBucketObjectLockConfiguration, useSetBucketPolicy, useSetBucketReplication, useSetBucketTagging, useSetBucketVersioning, useSetObjectAcl, useSetObjectLegalHold, useSetObjectRetention, useSetObjectTagging, useTableRowSelection, useUploadObjects };
@@ -0,0 +1,26 @@
1
+ import type { UseMutationResult } from "@tanstack/react-query";
2
+ interface BucketCommandInput {
3
+ Bucket: string | undefined;
4
+ }
5
+ interface UseDeleteBucketConfigRuleOptions<TRule extends {
6
+ ID?: string;
7
+ }, TUpdateInput extends BucketCommandInput, TUpdateOutput, TDeleteInput extends BucketCommandInput, TDeleteOutput, TError = unknown> {
8
+ bucketName: string;
9
+ ruleId: string;
10
+ rules: TRule[];
11
+ updateMutation: UseMutationResult<TUpdateOutput, TError, TUpdateInput, unknown>;
12
+ deleteMutation: UseMutationResult<TDeleteOutput, TError, TDeleteInput, unknown>;
13
+ buildUpdateInput: (remainingRules: TRule[]) => Omit<TUpdateInput, "Bucket">;
14
+ successMessage?: string;
15
+ errorMessage?: string;
16
+ onSuccess?: () => void;
17
+ onError?: (error: Error) => void;
18
+ }
19
+ interface UseDeleteBucketConfigRuleReturn {
20
+ isDeleting: boolean;
21
+ deleteRule: () => void;
22
+ }
23
+ export declare function useDeleteBucketConfigRule<TRule extends {
24
+ ID?: string;
25
+ }, TUpdateInput extends BucketCommandInput = BucketCommandInput, TUpdateOutput = unknown, TDeleteInput extends BucketCommandInput = BucketCommandInput, TDeleteOutput = unknown, TError = unknown>({ bucketName, ruleId, rules, updateMutation, deleteMutation, buildUpdateInput, successMessage, errorMessage, onSuccess, onError, }: UseDeleteBucketConfigRuleOptions<TRule, TUpdateInput, TUpdateOutput, TDeleteInput, TDeleteOutput, TError>): UseDeleteBucketConfigRuleReturn;
26
+ export {};
@@ -0,0 +1,46 @@
1
+ import { useToast } from "@scality/core-ui";
2
+ function useDeleteBucketConfigRule({ bucketName, ruleId, rules, updateMutation, deleteMutation, buildUpdateInput, successMessage = "Rule deleted successfully", errorMessage = "Failed to delete rule", onSuccess, onError }) {
3
+ const { showToast } = useToast();
4
+ const { mutate: updateConfig, status: updateStatus } = updateMutation;
5
+ const { mutate: deleteConfig, status: deleteStatus } = deleteMutation;
6
+ const isDeleting = "pending" === updateStatus || "pending" === deleteStatus;
7
+ const deleteRule = ()=>{
8
+ const remainingRules = rules.filter((rule)=>rule.ID !== ruleId);
9
+ const handleSuccess = ()=>{
10
+ showToast({
11
+ open: true,
12
+ message: successMessage,
13
+ status: "success"
14
+ });
15
+ onSuccess?.();
16
+ };
17
+ const handleError = (error)=>{
18
+ const displayMessage = error instanceof Error && error.message ? error.message : errorMessage;
19
+ const errorToReport = error instanceof Error ? error : new Error(displayMessage);
20
+ showToast({
21
+ open: true,
22
+ message: displayMessage,
23
+ status: "error"
24
+ });
25
+ onError?.(errorToReport);
26
+ };
27
+ if (0 === remainingRules.length) deleteConfig({
28
+ Bucket: bucketName
29
+ }, {
30
+ onSuccess: handleSuccess,
31
+ onError: handleError
32
+ });
33
+ else updateConfig({
34
+ Bucket: bucketName,
35
+ ...buildUpdateInput(remainingRules)
36
+ }, {
37
+ onSuccess: handleSuccess,
38
+ onError: handleError
39
+ });
40
+ };
41
+ return {
42
+ isDeleting,
43
+ deleteRule
44
+ };
45
+ }
46
+ export { useDeleteBucketConfigRule };
@@ -0,0 +1,27 @@
1
+ export interface EmptyBucketResult {
2
+ success: boolean;
3
+ deletedCount: number;
4
+ errors: Array<{
5
+ key: string;
6
+ code?: string;
7
+ message?: string;
8
+ }>;
9
+ limitReached: boolean;
10
+ }
11
+ interface UseEmptyBucketOptions {
12
+ maxObjects?: number;
13
+ maxKeysPerList?: number;
14
+ bypassGovernanceRetention?: boolean;
15
+ onProgress?: (deletedCount: number, totalAttempted: number) => void;
16
+ onSuccess?: (result: EmptyBucketResult) => void;
17
+ onError?: (error: Error) => void;
18
+ }
19
+ interface UseEmptyBucketReturn {
20
+ emptyBucket: (bucketName: string) => Promise<EmptyBucketResult | null>;
21
+ isEmptying: boolean;
22
+ error: Error | null;
23
+ result: EmptyBucketResult | null;
24
+ reset: () => void;
25
+ }
26
+ export declare const useEmptyBucket: (options?: UseEmptyBucketOptions) => UseEmptyBucketReturn;
27
+ export {};
@@ -0,0 +1,116 @@
1
+ import { useCallback, useState } from "react";
2
+ import { useQueryClient } from "@tanstack/react-query";
3
+ import { useS3Client } from "./useS3Client.js";
4
+ import { DeleteObjectsCommand, ListObjectVersionsCommand } from "@aws-sdk/client-s3";
5
+ const useEmptyBucket = (options = {})=>{
6
+ const { maxObjects = 20000, maxKeysPerList = 1000, bypassGovernanceRetention = true, onProgress, onSuccess, onError } = options;
7
+ const [isEmptying, setIsEmptying] = useState(false);
8
+ const [error, setError] = useState(null);
9
+ const [result, setResult] = useState(null);
10
+ const s3Client = useS3Client();
11
+ const queryClient = useQueryClient();
12
+ const emptyBucket = useCallback(async (bucketName)=>{
13
+ setIsEmptying(true);
14
+ setError(null);
15
+ setResult(null);
16
+ try {
17
+ let deletedCount = 0;
18
+ let totalAttempted = 0;
19
+ let keyMarker;
20
+ let versionIdMarker;
21
+ const allErrors = [];
22
+ let limitReached = false;
23
+ while(totalAttempted < maxObjects){
24
+ const listCommand = new ListObjectVersionsCommand({
25
+ Bucket: bucketName,
26
+ MaxKeys: maxKeysPerList,
27
+ KeyMarker: keyMarker,
28
+ VersionIdMarker: versionIdMarker
29
+ });
30
+ const listResponse = await s3Client.send(listCommand);
31
+ const objectsToDelete = [];
32
+ if (listResponse.Versions) objectsToDelete.push(...listResponse.Versions.filter((v)=>v.Key).map((v)=>({
33
+ Key: v.Key,
34
+ VersionId: v.VersionId
35
+ })));
36
+ if (listResponse.DeleteMarkers) objectsToDelete.push(...listResponse.DeleteMarkers.filter((dm)=>dm.Key).map((dm)=>({
37
+ Key: dm.Key,
38
+ VersionId: dm.VersionId
39
+ })));
40
+ if (0 === objectsToDelete.length) break;
41
+ const deleteCommand = new DeleteObjectsCommand({
42
+ Bucket: bucketName,
43
+ Delete: {
44
+ Objects: objectsToDelete
45
+ },
46
+ BypassGovernanceRetention: bypassGovernanceRetention
47
+ });
48
+ const deleteResponse = await s3Client.send(deleteCommand);
49
+ const successCount = deleteResponse.Deleted?.length || 0;
50
+ deletedCount += successCount;
51
+ totalAttempted += objectsToDelete.length;
52
+ if (deleteResponse.Errors) allErrors.push(...deleteResponse.Errors.map((error)=>({
53
+ key: error.Key || "unknown",
54
+ code: error.Code,
55
+ message: error.Message
56
+ })));
57
+ onProgress?.(deletedCount, totalAttempted);
58
+ if (!listResponse.IsTruncated) break;
59
+ if (totalAttempted >= maxObjects) {
60
+ limitReached = true;
61
+ break;
62
+ }
63
+ keyMarker = listResponse.NextKeyMarker;
64
+ versionIdMarker = listResponse.NextVersionIdMarker;
65
+ }
66
+ const bucketResult = {
67
+ success: 0 === allErrors.length,
68
+ deletedCount,
69
+ errors: allErrors,
70
+ limitReached
71
+ };
72
+ setResult(bucketResult);
73
+ queryClient.invalidateQueries({
74
+ queryKey: [
75
+ "ListObjects"
76
+ ]
77
+ });
78
+ queryClient.invalidateQueries({
79
+ queryKey: [
80
+ "ListObjectVersions"
81
+ ]
82
+ });
83
+ onSuccess?.(bucketResult);
84
+ return bucketResult;
85
+ } catch (err) {
86
+ const errorObj = err instanceof Error ? err : new Error("Failed to empty bucket");
87
+ setError(errorObj);
88
+ onError?.(errorObj);
89
+ return null;
90
+ } finally{
91
+ setIsEmptying(false);
92
+ }
93
+ }, [
94
+ s3Client,
95
+ maxObjects,
96
+ maxKeysPerList,
97
+ bypassGovernanceRetention,
98
+ onProgress,
99
+ onSuccess,
100
+ onError,
101
+ queryClient
102
+ ]);
103
+ const reset = useCallback(()=>{
104
+ setIsEmptying(false);
105
+ setError(null);
106
+ setResult(null);
107
+ }, []);
108
+ return {
109
+ emptyBucket,
110
+ isEmptying,
111
+ error,
112
+ result,
113
+ reset
114
+ };
115
+ };
116
+ export { useEmptyBucket };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Hook to detect if a bucket is managed by any ISV (Veeam or Commvault)
3
+ * Returns comprehensive information about ISV bucket status
4
+ *
5
+ * This hook fetches bucket tags once and derives all ISV-related states from it,
6
+ * avoiding multiple API calls for the same data.
7
+ */
8
+ export declare const useISVBucketStatus: (bucketName: string) => {
9
+ isVeeamBucket: boolean;
10
+ isCommvaultBucket: boolean;
11
+ isISVManaged: boolean;
12
+ isvApplication: string | undefined;
13
+ isLoading: boolean | undefined;
14
+ bucketTagsStatus: "error" | "success" | "pending";
15
+ };
@@ -0,0 +1,27 @@
1
+ import { useGetBucketTagging } from "./bucketConfiguration.js";
2
+ import { useFeatures } from "../utils/useFeatures.js";
3
+ import { BUCKET_TAG_APPLICATION, BUCKET_TAG_VEEAM_APPLICATION, COMMVAULT_APPLICATION, VEEAM_BACKUP_REPLICATION, VEEAM_OFFICE_365, VEEAM_OFFICE_365_V8, VEEAM_VBO_APPLICATION } from "../utils/constants.js";
4
+ const useISVBucketStatus = (bucketName)=>{
5
+ const isISVFeatureEnabled = useFeatures("ISV");
6
+ const { data: bucketTags, status: bucketTagsStatus } = useGetBucketTagging({
7
+ Bucket: bucketName
8
+ }, {
9
+ enabled: isISVFeatureEnabled
10
+ });
11
+ const veeamTagApplication = bucketTags?.TagSet?.find((tag)=>tag.Key === BUCKET_TAG_VEEAM_APPLICATION)?.Value;
12
+ const ISVApplicationTag = bucketTags?.TagSet?.find((tag)=>tag.Key === BUCKET_TAG_APPLICATION)?.Value;
13
+ const isVeeamBucket = veeamTagApplication === VEEAM_BACKUP_REPLICATION || veeamTagApplication === VEEAM_OFFICE_365 || veeamTagApplication === VEEAM_OFFICE_365_V8;
14
+ const isISVBucketTagAsVeeam = ISVApplicationTag === VEEAM_BACKUP_REPLICATION || ISVApplicationTag === VEEAM_VBO_APPLICATION;
15
+ const isCommvaultBucket = ISVApplicationTag === COMMVAULT_APPLICATION;
16
+ const isVeeam = isVeeamBucket || isISVBucketTagAsVeeam;
17
+ const isISVManaged = isVeeam || isCommvaultBucket;
18
+ return {
19
+ isVeeamBucket: isVeeam,
20
+ isCommvaultBucket,
21
+ isISVManaged,
22
+ isvApplication: isCommvaultBucket ? "Commvault" : isVeeam ? "Veeam" : void 0,
23
+ isLoading: isISVFeatureEnabled && "pending" === bucketTagsStatus,
24
+ bucketTagsStatus
25
+ };
26
+ };
27
+ export { useISVBucketStatus };
@@ -0,0 +1,9 @@
1
+ export declare function useTableRowSelection<TRow extends {
2
+ ID?: string;
3
+ }>(tableData: TRow[], autoSelectFirst?: boolean): {
4
+ selectedId: string | null;
5
+ onRowSelected: (row: {
6
+ original: TRow;
7
+ }) => void;
8
+ clearSelection: () => void;
9
+ };
@@ -0,0 +1,45 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ function useTableRowSelection(tableData, autoSelectFirst = true) {
3
+ const [selectedRuleId, setSelectedRuleId] = useState(null);
4
+ useEffect(()=>{
5
+ if (0 === tableData.length) {
6
+ if (null !== selectedRuleId) setSelectedRuleId(null);
7
+ return;
8
+ }
9
+ if (!autoSelectFirst) {
10
+ if (null !== selectedRuleId) {
11
+ const stillExists = tableData.some((row)=>row.ID === selectedRuleId);
12
+ if (!stillExists) setSelectedRuleId(null);
13
+ }
14
+ return;
15
+ }
16
+ if (null === selectedRuleId) {
17
+ const firstRowId = tableData[0]?.ID;
18
+ if (firstRowId) setSelectedRuleId(firstRowId);
19
+ return;
20
+ }
21
+ const stillExists = tableData.some((row)=>row.ID === selectedRuleId);
22
+ if (!stillExists) {
23
+ const firstRowId = tableData[0]?.ID;
24
+ firstRowId ? setSelectedRuleId(firstRowId) : setSelectedRuleId(null);
25
+ }
26
+ }, [
27
+ autoSelectFirst,
28
+ tableData,
29
+ selectedRuleId
30
+ ]);
31
+ const onRowSelected = useCallback((row)=>{
32
+ if (row.original.ID && row.original.ID !== selectedRuleId) setSelectedRuleId(row.original.ID);
33
+ }, [
34
+ selectedRuleId
35
+ ]);
36
+ const clearSelection = useCallback(()=>{
37
+ setSelectedRuleId(null);
38
+ }, []);
39
+ return {
40
+ selectedId: selectedRuleId,
41
+ onRowSelected,
42
+ clearSelection
43
+ };
44
+ }
45
+ export { useTableRowSelection };
@@ -57,6 +57,14 @@ jest.mock("pretty-bytes", ()=>({
57
57
  return `${Math.round(num)} ${sizes[i]}`;
58
58
  }
59
59
  }));
60
+ jest.mock("joi", ()=>{
61
+ const Joi = jest.requireActual("joi");
62
+ return {
63
+ __esModule: true,
64
+ default: Joi,
65
+ ...Joi
66
+ };
67
+ });
60
68
  if (!File.prototype.arrayBuffer) File.prototype.arrayBuffer = function() {
61
69
  return new Promise((resolve)=>{
62
70
  const reader = new FileReader();
@@ -1,13 +1,49 @@
1
- import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
1
+ /**
2
+ * @fileoverview Test utilities for data-browser-library
3
+ *
4
+ * This file provides common utilities for testing:
5
+ * - Test configuration and credentials
6
+ * - Mock setup utilities (S3, DOM, etc.)
7
+ * - React Testing Library wrappers
8
+ * - React Query wrappers
9
+ * - Global configuration utilities
10
+ * - Factory pattern validation utilities
11
+ * - Form testing helpers
12
+ */
2
13
  import React from "react";
14
+ import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
3
15
  import { S3BrowserConfig, S3Credentials } from "../types";
16
+ import type { S3Configuration, DevelopmentConfiguration } from "../config/types";
17
+ /**
18
+ * Default S3 configuration for tests
19
+ */
4
20
  export declare const testConfig: S3BrowserConfig;
21
+ /**
22
+ * Default S3 credentials for tests
23
+ */
5
24
  export declare const testCredentials: S3Credentials;
25
+ /**
26
+ * Mock S3 client for tests
27
+ */
6
28
  export declare const mockS3Client: {
7
29
  send: jest.Mock<any, any, any>;
8
30
  };
9
31
  export declare const MockedS3Client: jest.MockedClass<typeof S3Client>;
10
32
  export declare const MockedPutObjectCommand: jest.MockedClass<typeof PutObjectCommand>;
33
+ /**
34
+ * Sets up S3 mocks for testing
35
+ */
36
+ export declare const setupS3Mocks: () => void;
37
+ /**
38
+ * Common mock setup that should be run before each test
39
+ */
40
+ export declare const setupCommonMocks: () => void;
41
+ /**
42
+ * Mocks DOM measurements APIs for virtualization and layout testing
43
+ * AutoSizer uses offsetWidth and offsetHeight which Jest/JSDom doesn't support
44
+ * @param width - The mock width in pixels
45
+ * @param height - The mock height in pixels
46
+ */
11
47
  export declare function mockOffsetSize(width: number, height: number): void;
12
48
  /**
13
49
  * Creates a simple QueryClient wrapper for tests that don't need DataBrowser context
@@ -33,18 +69,6 @@ export declare const renderHookWithWrapper: <TProps, TResult>(hook: (props: TPro
33
69
  * Creates a test File object
34
70
  */
35
71
  export declare const createTestFile: (name: string, content: string, type?: string) => File;
36
- /**
37
- * Sets up S3 mocks for testing
38
- */
39
- export declare const setupS3Mocks: () => void;
40
- /**
41
- * Common mock setup that should be run before each test
42
- */
43
- export declare const setupCommonMocks: () => void;
44
- /**
45
- * Configuration test utilities for overriding build-time globals
46
- */
47
- import type { S3Configuration, DevelopmentConfiguration } from "../config/types";
48
72
  type GlobalConfigOverrides = {
49
73
  s3?: Partial<S3Configuration>;
50
74
  dev?: Partial<DevelopmentConfiguration>;
@@ -67,16 +91,74 @@ export declare const resetGlobalConfig: () => void;
67
91
  * Test helper for running tests with specific global configuration
68
92
  */
69
93
  export declare const withGlobalConfig: (overrides: GlobalConfigOverrides, testFn: () => void | Promise<void>) => () => Promise<void>;
70
- /**
71
- * Validates that a hook follows the expected factory pattern
72
- */
73
- export declare const validateFactoryHook: (hook: any, operationName: string) => void;
74
94
  /**
75
95
  * Creates a factory test error
76
96
  */
77
97
  export declare const createFactoryTestError: (testContext: string, operationName: string, details?: string) => Error;
98
+ /**
99
+ * Validates that a hook follows the expected factory pattern
100
+ */
101
+ export declare const validateFactoryHook: (hook: any, operationName: string) => void;
78
102
  /**
79
103
  * Validates that a hook result has expected React Query properties
80
104
  */
81
105
  export declare const validateHookResult: (result: any, hookType: "query" | "mutation" | "infiniteQuery") => void;
106
+ /**
107
+ * Creates a mock UseMutationResult for testing React Query mutations
108
+ *
109
+ * This helper provides a complete mock object that matches the UseMutationResult type
110
+ * from @tanstack/react-query, eliminating the need for 'as any' type assertions.
111
+ *
112
+ * @param mutate - The mocked mutate function (usually jest.fn())
113
+ * @param overrides - Optional overrides for any properties (e.g., { isPending: true })
114
+ * @returns A complete UseMutationResult mock object
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const mockCreateBucket = jest.fn();
119
+ * const mockResult = createMockMutationResult(mockCreateBucket, { isPending: true });
120
+ * mockUseCreateBucket.mockReturnValue(mockResult);
121
+ * ```
122
+ */
123
+ export declare const createMockMutationResult: (mutate: jest.Mock, overrides?: {}) => {
124
+ mutate: jest.Mock<any, any, any>;
125
+ mutateAsync: jest.Mock<any, any, any>;
126
+ reset: jest.Mock<any, any, any>;
127
+ isPending: false;
128
+ isIdle: true;
129
+ isError: false;
130
+ isSuccess: false;
131
+ status: "idle";
132
+ data: undefined;
133
+ error: null;
134
+ variables: undefined;
135
+ context: undefined;
136
+ failureCount: number;
137
+ failureReason: null;
138
+ isPaused: false;
139
+ submittedAt: number;
140
+ };
141
+ /**
142
+ * Finds a Toggle input element by its label text
143
+ * Traverses the DOM to locate the associated checkbox input
144
+ * @param labelText - The text content of the label
145
+ * @returns The toggle input element or null if not found
146
+ */
147
+ export declare const findToggleByLabel: (labelText: string) => HTMLInputElement | null;
148
+ /**
149
+ * Waits for a submit button to be enabled and clicks it
150
+ * @param buttonName - The type of button to click ("create" or "save")
151
+ */
152
+ export declare const submitForm: (buttonName?: "create" | "save") => Promise<void>;
153
+ /**
154
+ * Sets up a mock mutation to simulate successful form submission
155
+ * @param mockMutate - The jest mock function for the mutation
156
+ */
157
+ export declare const mockSuccessSubmit: (mockMutate: jest.Mock) => void;
158
+ /**
159
+ * Sets up a mock mutation to simulate failed form submission
160
+ * @param mockMutate - The jest mock function for the mutation
161
+ * @param errorMessage - The error message to return (default: "Network Error")
162
+ */
163
+ export declare const mockErrorSubmit: (mockMutate: jest.Mock, errorMessage?: string) => void;
82
164
  export { setupMswServer, overrideHandlers } from "./msw";
@@ -1,12 +1,12 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import "react";
2
3
  import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
3
4
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
- import { renderHook } from "@testing-library/react";
5
- import "react";
6
- import { DataBrowserProvider } from "../components/providers/DataBrowserProvider.js";
5
+ import { fireEvent, renderHook, screen, waitFor } from "@testing-library/react";
7
6
  import { coreUIAvailableThemes } from "@scality/core-ui/dist/style/theme";
8
7
  import { CoreUiThemeProvider } from "@scality/core-ui/dist/next";
9
8
  import { ToastProvider } from "@scality/core-ui";
9
+ import { DataBrowserProvider } from "../components/providers/DataBrowserProvider.js";
10
10
  import { overrideHandlers, setupMswServer } from "./msw/index.js";
11
11
  var __webpack_require__ = {};
12
12
  (()=>{
@@ -34,6 +34,17 @@ const mockS3Client = {
34
34
  };
35
35
  const MockedS3Client = S3Client;
36
36
  const MockedPutObjectCommand = PutObjectCommand;
37
+ const setupS3Mocks = ()=>{
38
+ jest.clearAllMocks();
39
+ MockedS3Client.mockImplementation(()=>mockS3Client);
40
+ MockedPutObjectCommand.mockImplementation((input)=>({
41
+ input,
42
+ resolveMiddleware: jest.fn()
43
+ }));
44
+ };
45
+ const setupCommonMocks = ()=>{
46
+ setupS3Mocks();
47
+ };
37
48
  function mockOffsetSize(width, height) {
38
49
  __webpack_require__.g.ResizeObserver = jest.fn().mockImplementation(()=>({
39
50
  observe: jest.fn(),
@@ -149,17 +160,6 @@ const createTestFile = (name, content, type = "text/plain")=>new File([
149
160
  ], name, {
150
161
  type
151
162
  });
152
- const setupS3Mocks = ()=>{
153
- jest.clearAllMocks();
154
- MockedS3Client.mockImplementation(()=>mockS3Client);
155
- MockedPutObjectCommand.mockImplementation((input)=>({
156
- input,
157
- resolveMiddleware: jest.fn()
158
- }));
159
- };
160
- const setupCommonMocks = ()=>{
161
- setupS3Mocks();
162
- };
163
163
  const overrideGlobalConfig = (overrides)=>{
164
164
  const currentS3 = globalThis.__S3_CONFIG__;
165
165
  const currentDev = globalThis.__DEV_CONFIG__;
@@ -201,10 +201,10 @@ const withGlobalConfig = (overrides, testFn)=>async ()=>{
201
201
  resetGlobalConfig();
202
202
  }
203
203
  };
204
+ const createFactoryTestError = (testContext, operationName, details)=>new Error(`Factory Test Failure [${testContext}]: ${operationName} - ${details || "Unknown error"}. Check factory implementation and hook usage patterns.`);
204
205
  const validateFactoryHook = (hook, operationName)=>{
205
206
  if ("function" != typeof hook) throw createFactoryTestError("Factory Validation", operationName, "hook should be a function - check if useCreate*Hook factory is properly returning a hook function");
206
207
  };
207
- const createFactoryTestError = (testContext, operationName, details)=>new Error(`Factory Test Failure [${testContext}]: ${operationName} - ${details || "Unknown error"}. Check factory implementation and hook usage patterns.`);
208
208
  const validateHookResult = (result, hookType)=>{
209
209
  const commonProps = [
210
210
  "isError",
@@ -233,4 +233,52 @@ const validateHookResult = (result, hookType)=>{
233
233
  ];
234
234
  for (const prop of expectedProps)if (!(prop in result)) throw createFactoryTestError("Hook Validation", hookType, `Missing property '${prop}' - check if factory is returning proper React Query hook result`);
235
235
  };
236
- export { MockedPutObjectCommand, MockedS3Client, createFactoryTestError, createQueryWrapper, createTestFile, createTestWrapper, mockOffsetSize, mockS3Client, overrideGlobalConfig, overrideHandlers, renderHookWithWrapper, resetGlobalConfig, setupCommonMocks, setupMswServer, setupS3Mocks, testConfig, testCredentials, validateFactoryHook, validateHookResult, withGlobalConfig };
236
+ const createMockMutationResult = (mutate, overrides = {})=>({
237
+ mutate,
238
+ mutateAsync: jest.fn(),
239
+ reset: jest.fn(),
240
+ isPending: false,
241
+ isIdle: true,
242
+ isError: false,
243
+ isSuccess: false,
244
+ status: "idle",
245
+ data: void 0,
246
+ error: null,
247
+ variables: void 0,
248
+ context: void 0,
249
+ failureCount: 0,
250
+ failureReason: null,
251
+ isPaused: false,
252
+ submittedAt: 0,
253
+ ...overrides
254
+ });
255
+ const findToggleByLabel = (labelText)=>{
256
+ const label = screen.getByText(labelText);
257
+ let current = label.parentElement;
258
+ while(current && !current.querySelector('input[type="checkbox"]'))current = current.parentElement;
259
+ return current?.querySelector('input[type="checkbox"]');
260
+ };
261
+ const submitForm = async (buttonName = "create")=>{
262
+ await waitFor(()=>{
263
+ const button = screen.getByRole("button", {
264
+ name: "create" === buttonName ? /create/i : /save/i
265
+ });
266
+ expect(button).toBeEnabled();
267
+ });
268
+ const button = screen.getByRole("button", {
269
+ name: "create" === buttonName ? /create/i : /save/i
270
+ });
271
+ fireEvent.click(button);
272
+ };
273
+ const mockSuccessSubmit = (mockMutate)=>{
274
+ mockMutate.mockImplementation((_, options)=>{
275
+ options?.onSuccess?.();
276
+ });
277
+ };
278
+ const mockErrorSubmit = (mockMutate, errorMessage = "Network Error")=>{
279
+ const error = new Error(errorMessage);
280
+ mockMutate.mockImplementation((_, options)=>{
281
+ options?.onError?.(error);
282
+ });
283
+ };
284
+ export { MockedPutObjectCommand, MockedS3Client, createFactoryTestError, createMockMutationResult, createQueryWrapper, createTestFile, createTestWrapper, findToggleByLabel, mockErrorSubmit, mockOffsetSize, mockS3Client, mockSuccessSubmit, overrideGlobalConfig, overrideHandlers, renderHookWithWrapper, resetGlobalConfig, setupCommonMocks, setupMswServer, setupS3Mocks, submitForm, testConfig, testCredentials, validateFactoryHook, validateHookResult, withGlobalConfig };