@scality/data-browser-library 1.1.6 → 1.1.8

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.
@@ -112,7 +112,10 @@ describe('BucketReplicationFormPage', ()=>{
112
112
  },
113
113
  status: 'success'
114
114
  });
115
- mockUseFeatures.mockReturnValue(true);
115
+ mockUseFeatures.mockImplementation((feature)=>{
116
+ if ('replicationAdvanced' === feature) return true;
117
+ return false;
118
+ });
116
119
  mockUseISVBucketStatus.mockReturnValue({
117
120
  isVeeamBucket: false,
118
121
  isCommvaultBucket: false,
@@ -1971,7 +1974,10 @@ describe('BucketReplicationFormPage', ()=>{
1971
1974
  });
1972
1975
  });
1973
1976
  it('shows Additional Options section when replicationAdvanced is enabled', ()=>{
1974
- mockUseFeatures.mockReturnValue(true);
1977
+ mockUseFeatures.mockImplementation((feature)=>{
1978
+ if ('replicationAdvanced' === feature) return true;
1979
+ return false;
1980
+ });
1975
1981
  renderBucketReplicationFormPage();
1976
1982
  expect(screen.getByText('Replication Time Control (RTC)')).toBeInTheDocument();
1977
1983
  expect(screen.getByText('RTC metrics and notifications')).toBeInTheDocument();
@@ -1979,4 +1985,203 @@ describe('BucketReplicationFormPage', ()=>{
1979
1985
  expect(screen.getByText('Delete marker replication')).toBeInTheDocument();
1980
1986
  });
1981
1987
  });
1988
+ describe('Feature Flag - replicationV1', ()=>{
1989
+ it('submits rule in V1 format when replicationV1 feature is enabled', async ()=>{
1990
+ mockUseFeatures.mockImplementation((feature)=>{
1991
+ if ('replicationV1' === feature) return true;
1992
+ return false;
1993
+ });
1994
+ mockUseGetBucketReplication.mockReturnValue({
1995
+ data: {
1996
+ ReplicationConfiguration: {
1997
+ Role: '',
1998
+ Rules: []
1999
+ }
2000
+ },
2001
+ status: 'success'
2002
+ });
2003
+ mockSuccessSubmit(mockMutate);
2004
+ renderBucketReplicationFormPage();
2005
+ await user_event.type(screen.getByRole('textbox', {
2006
+ name: /role arn/i
2007
+ }), 'arn:aws:iam::123456789012:role/replication');
2008
+ await user_event.type(screen.getByRole('textbox', {
2009
+ name: /rule id/i
2010
+ }), 'test-rule-v1');
2011
+ const targetBucketSelect = screen.getByLabelText(/target bucket/i);
2012
+ await user_event.click(targetBucketSelect);
2013
+ await user_event.click(screen.getByRole('option', {
2014
+ name: 'destination-bucket'
2015
+ }));
2016
+ await submitForm('create');
2017
+ await waitFor(()=>{
2018
+ expect(mockMutate).toHaveBeenCalled();
2019
+ });
2020
+ const submittedRule = mockMutate.mock.calls[0][0].ReplicationConfiguration.Rules[0];
2021
+ expect(submittedRule.Prefix).toBe('');
2022
+ expect(submittedRule.ID).toBe('test-rule-v1');
2023
+ expect(submittedRule.Status).toBe('Enabled');
2024
+ expect(submittedRule.Destination.Bucket).toBe('arn:aws:s3:::destination-bucket');
2025
+ expect(submittedRule).not.toHaveProperty('Filter');
2026
+ expect(submittedRule).not.toHaveProperty('Priority');
2027
+ expect(submittedRule).not.toHaveProperty('DeleteMarkerReplication');
2028
+ expect(submittedRule).not.toHaveProperty('SourceSelectionCriteria');
2029
+ });
2030
+ it('hides V2-only fields when replicationV1 is enabled', ()=>{
2031
+ mockUseFeatures.mockImplementation((feature)=>{
2032
+ if ('replicationV1' === feature) return true;
2033
+ return false;
2034
+ });
2035
+ mockUseGetBucketReplication.mockReturnValue({
2036
+ data: {
2037
+ ReplicationConfiguration: {
2038
+ Role: '',
2039
+ Rules: []
2040
+ }
2041
+ },
2042
+ status: 'success'
2043
+ });
2044
+ renderBucketReplicationFormPage();
2045
+ expect(screen.getByRole('textbox', {
2046
+ name: /role arn/i
2047
+ })).toBeInTheDocument();
2048
+ expect(screen.getByRole('textbox', {
2049
+ name: /rule id/i
2050
+ })).toBeInTheDocument();
2051
+ expect(screen.queryByRole('spinbutton', {
2052
+ name: /rule priority/i
2053
+ })).not.toBeInTheDocument();
2054
+ expect(screen.queryByText('Encryption')).not.toBeInTheDocument();
2055
+ expect(screen.queryByText('Additional Options')).not.toBeInTheDocument();
2056
+ });
2057
+ it('shows simplified destination without cross-account in V1 mode', ()=>{
2058
+ mockUseFeatures.mockImplementation((feature)=>{
2059
+ if ('replicationV1' === feature) return true;
2060
+ return false;
2061
+ });
2062
+ mockUseGetBucketReplication.mockReturnValue({
2063
+ data: {
2064
+ ReplicationConfiguration: {
2065
+ Role: '',
2066
+ Rules: []
2067
+ }
2068
+ },
2069
+ status: 'success'
2070
+ });
2071
+ renderBucketReplicationFormPage();
2072
+ expect(screen.getByText(/Target Bucket/i)).toBeInTheDocument();
2073
+ expect(screen.queryByText(/Same account destination/i)).not.toBeInTheDocument();
2074
+ });
2075
+ it('shows only prefix filter (no filter type selector) in V1 mode', ()=>{
2076
+ mockUseFeatures.mockImplementation((feature)=>{
2077
+ if ('replicationV1' === feature) return true;
2078
+ return false;
2079
+ });
2080
+ mockUseGetBucketReplication.mockReturnValue({
2081
+ data: {
2082
+ ReplicationConfiguration: {
2083
+ Role: '',
2084
+ Rules: []
2085
+ }
2086
+ },
2087
+ status: 'success'
2088
+ });
2089
+ renderBucketReplicationFormPage();
2090
+ expect(screen.getByRole('textbox', {
2091
+ name: /prefix/i
2092
+ })).toBeInTheDocument();
2093
+ expect(screen.queryByText('All objects')).not.toBeInTheDocument();
2094
+ expect(screen.queryByText('Tags filter')).not.toBeInTheDocument();
2095
+ });
2096
+ it('allows submission without priority in V1 mode', async ()=>{
2097
+ mockUseFeatures.mockImplementation((feature)=>{
2098
+ if ('replicationV1' === feature) return true;
2099
+ return false;
2100
+ });
2101
+ mockUseGetBucketReplication.mockReturnValue({
2102
+ data: {
2103
+ ReplicationConfiguration: {
2104
+ Role: '',
2105
+ Rules: []
2106
+ }
2107
+ },
2108
+ status: 'success'
2109
+ });
2110
+ mockSuccessSubmit(mockMutate);
2111
+ renderBucketReplicationFormPage();
2112
+ await user_event.type(screen.getByRole('textbox', {
2113
+ name: /role arn/i
2114
+ }), 'arn:aws:iam::123456789012:role/repl');
2115
+ await user_event.type(screen.getByRole('textbox', {
2116
+ name: /rule id/i
2117
+ }), 'v1-rule');
2118
+ const targetBucketSelect = screen.getByLabelText(/target bucket/i);
2119
+ await user_event.click(targetBucketSelect);
2120
+ await user_event.click(screen.getByRole('option', {
2121
+ name: 'destination-bucket'
2122
+ }));
2123
+ await submitForm('create');
2124
+ await waitFor(()=>{
2125
+ expect(mockMutate).toHaveBeenCalled();
2126
+ });
2127
+ const submittedRule = mockMutate.mock.calls[0][0].ReplicationConfiguration.Rules[0];
2128
+ expect(submittedRule.Prefix).toBe('');
2129
+ expect(submittedRule).not.toHaveProperty('Priority');
2130
+ });
2131
+ it('loads existing V1 rule (with Prefix at rule level) for editing', async ()=>{
2132
+ mockUseFeatures.mockImplementation((feature)=>{
2133
+ if ('replicationV1' === feature) return true;
2134
+ return false;
2135
+ });
2136
+ const v1Rule = {
2137
+ ID: 'existing-v1-rule',
2138
+ Status: 'Enabled',
2139
+ Prefix: 'documents/',
2140
+ Destination: {
2141
+ Bucket: 'arn:aws:s3:::destination-bucket',
2142
+ StorageClass: 'STANDARD'
2143
+ }
2144
+ };
2145
+ mockUseGetBucketReplication.mockReturnValue({
2146
+ data: {
2147
+ ReplicationConfiguration: {
2148
+ Role: 'arn:aws:iam::123456789012:role/repl',
2149
+ Rules: [
2150
+ v1Rule
2151
+ ]
2152
+ }
2153
+ },
2154
+ status: 'success'
2155
+ });
2156
+ renderBucketReplicationFormPage('test-bucket', 'existing-v1-rule');
2157
+ await waitFor(()=>{
2158
+ expect(screen.getByText('existing-v1-rule')).toBeInTheDocument();
2159
+ });
2160
+ const prefixInput = screen.getByRole('textbox', {
2161
+ name: /prefix/i
2162
+ });
2163
+ expect(prefixInput.value).toBe('documents/');
2164
+ });
2165
+ it('shows all V2 fields when replicationV1 is not enabled', ()=>{
2166
+ mockUseFeatures.mockImplementation((feature)=>{
2167
+ if ('replicationAdvanced' === feature) return true;
2168
+ return false;
2169
+ });
2170
+ mockUseGetBucketReplication.mockReturnValue({
2171
+ data: {
2172
+ ReplicationConfiguration: {
2173
+ Role: '',
2174
+ Rules: []
2175
+ }
2176
+ },
2177
+ status: 'success'
2178
+ });
2179
+ renderBucketReplicationFormPage();
2180
+ expect(screen.getByRole('spinbutton', {
2181
+ name: /rule priority/i
2182
+ })).toBeInTheDocument();
2183
+ expect(screen.getByText(/^Encryption/)).toBeInTheDocument();
2184
+ expect(screen.getByText(/^Additional Options/)).toBeInTheDocument();
2185
+ });
2186
+ });
1982
2187
  });
@@ -45,15 +45,46 @@ const storageClassOptions = [
45
45
  label: 'Glacier Instant Retrieval'
46
46
  }
47
47
  ];
48
- const createSchema = (hasExistingRules, hasCustomDestination)=>joi.object({
49
- role: hasExistingRules ? joi.string().optional() : joi.string().required().messages({
50
- 'string.empty': 'Role ARN is required for first replication rule'
51
- }),
52
- ruleId: joi.string().required().max(AWS_RULE_LIMITS.RULE_ID_MAX_LENGTH).messages({
53
- 'string.empty': 'Rule ID is required',
54
- 'string.max': `Rule ID must not exceed ${AWS_RULE_LIMITS.RULE_ID_MAX_LENGTH} characters`
55
- }),
56
- status: joi.string().valid(...STATUS_OPTIONS.map((o)=>o.value)).required(),
48
+ const createSchema = (hasExistingRules, hasCustomDestination, isV1)=>{
49
+ const roleSchema = hasExistingRules ? joi.string().optional() : joi.string().required().messages({
50
+ 'string.empty': 'Role ARN is required for first replication rule'
51
+ });
52
+ const ruleIdSchema = joi.string().required().max(AWS_RULE_LIMITS.RULE_ID_MAX_LENGTH).messages({
53
+ 'string.empty': 'Rule ID is required',
54
+ 'string.max': `Rule ID must not exceed ${AWS_RULE_LIMITS.RULE_ID_MAX_LENGTH} characters`
55
+ });
56
+ const statusSchema = joi.string().valid(...STATUS_OPTIONS.map((o)=>o.value)).required();
57
+ const targetBucketSchema = hasCustomDestination ? joi.string().allow('').optional() : joi.string().required().messages({
58
+ 'string.empty': 'Target bucket is required'
59
+ });
60
+ if (isV1) return joi.object({
61
+ role: roleSchema,
62
+ ruleId: ruleIdSchema,
63
+ status: statusSchema,
64
+ prefix: joi.string().allow('').optional(),
65
+ targetBucket: targetBucketSchema,
66
+ storageClass: joi.string().allow('').optional(),
67
+ sameAccount: joi.any().optional(),
68
+ targetAccountId: joi.any().optional(),
69
+ priority: joi.any().optional(),
70
+ filterType: joi.any().optional(),
71
+ tags: joi.any().optional(),
72
+ includeEncryptedObjects: joi.any().optional(),
73
+ replicaModifications: joi.any().optional(),
74
+ encryptReplicatedObjects: joi.any().optional(),
75
+ replicaKmsKeyId: joi.any().optional(),
76
+ enforceRTC: joi.any().optional(),
77
+ enableRTCNotification: joi.any().optional(),
78
+ deleteMarkerReplication: joi.any().optional(),
79
+ switchObjectOwnership: joi.any().optional(),
80
+ understandISVRisk: joi.boolean().invalid(false).messages({
81
+ 'any.invalid': 'You must acknowledge the risk'
82
+ })
83
+ });
84
+ return joi.object({
85
+ role: roleSchema,
86
+ ruleId: ruleIdSchema,
87
+ status: statusSchema,
57
88
  priority: joi.number().integer().min(0).allow(null).optional().messages({
58
89
  'number.base': 'Priority must be a number',
59
90
  'number.min': 'Priority must be at least 0'
@@ -62,9 +93,7 @@ const createSchema = (hasExistingRules, hasCustomDestination)=>joi.object({
62
93
  includeEncryptedObjects: joi.boolean(),
63
94
  replicaModifications: joi.boolean(),
64
95
  sameAccount: joi.boolean(),
65
- targetBucket: hasCustomDestination ? joi.string().allow('').optional() : joi.string().required().messages({
66
- 'string.empty': 'Target bucket is required'
67
- }),
96
+ targetBucket: targetBucketSchema,
68
97
  targetAccountId: hasCustomDestination ? joi.string().allow('').optional() : joi.when('sameAccount', {
69
98
  is: false,
70
99
  then: joi.string().required().messages({
@@ -101,6 +130,7 @@ const createSchema = (hasExistingRules, hasCustomDestination)=>joi.object({
101
130
  'any.invalid': 'You must acknowledge the risk'
102
131
  })
103
132
  });
133
+ };
104
134
  const ruleToFormValues = (rule, role)=>{
105
135
  const formValues = {
106
136
  role,
@@ -238,7 +268,21 @@ const buildSourceSelectionCriteria = (data)=>{
238
268
  }
239
269
  };
240
270
  };
241
- const buildReplicationRule = (data, sourceBucketName)=>{
271
+ const buildReplicationRule = (data, sourceBucketName, useV1Format)=>{
272
+ if (useV1Format) {
273
+ const bucketName = data.targetBucket || sourceBucketName;
274
+ return {
275
+ ID: data.ruleId,
276
+ Status: data.status,
277
+ Prefix: data.prefix || '',
278
+ Destination: {
279
+ Bucket: `arn:aws:s3:::${bucketName}`,
280
+ ...data.storageClass && '' !== data.storageClass.trim() && {
281
+ StorageClass: data.storageClass
282
+ }
283
+ }
284
+ };
285
+ }
242
286
  const rule = {
243
287
  ID: data.ruleId,
244
288
  Status: data.status,
@@ -339,7 +383,9 @@ function BucketReplicationFormPage() {
339
383
  const { showToast } = useToast();
340
384
  const isEditMode = !!ruleId;
341
385
  const showAdvancedReplication = useFeatures('replicationAdvanced');
386
+ const useV1Format = useFeatures('replicationV1');
342
387
  const { isISVManaged, isvApplication, isLoading: isISVLoading } = useISVBucketStatus(bucketName);
388
+ const { data: bucketsData } = useBuckets();
343
389
  const { data: replicationData, status: replicationStatus } = useGetBucketReplication({
344
390
  Bucket: bucketName
345
391
  });
@@ -367,7 +413,7 @@ function BucketReplicationFormPage() {
367
413
  }, [
368
414
  replicationData
369
415
  ]);
370
- const dynamicSchema = useMemo(()=>createSchema(hasExistingRules, !!CustomDestinationFields).keys({
416
+ const dynamicSchema = useMemo(()=>createSchema(hasExistingRules, !!CustomDestinationFields, !!useV1Format).keys({
371
417
  ruleId: joi.string().required().invalid(...existingRuleIds).messages({
372
418
  'string.empty': 'Rule ID is required',
373
419
  'any.invalid': 'A rule with this ID already exists'
@@ -375,7 +421,8 @@ function BucketReplicationFormPage() {
375
421
  }), [
376
422
  existingRuleIds,
377
423
  hasExistingRules,
378
- CustomDestinationFields
424
+ CustomDestinationFields,
425
+ useV1Format
379
426
  ]);
380
427
  const methods = useForm({
381
428
  resolver: joiResolver(dynamicSchema),
@@ -532,7 +579,7 @@ function BucketReplicationFormPage() {
532
579
  ]);
533
580
  const onSubmit = useCallback((data)=>{
534
581
  if (!bucketName) return;
535
- const rule = buildReplicationRule(data, bucketName);
582
+ const rule = buildReplicationRule(data, bucketName, !!useV1Format);
536
583
  const existingRules = replicationData?.ReplicationConfiguration?.Rules || [];
537
584
  const updatedRules = isEditMode ? existingRules.map((r)=>r.ID === existingRule?.ID ? rule : r) : [
538
585
  ...existingRules,
@@ -569,7 +616,8 @@ function BucketReplicationFormPage() {
569
616
  showToast,
570
617
  replicationData,
571
618
  isEditMode,
572
- existingRule
619
+ existingRule,
620
+ useV1Format
573
621
  ]);
574
622
  if ('pending' === replicationStatus || isISVLoading) return /*#__PURE__*/ jsx(Loader, {
575
623
  centered: true,
@@ -718,7 +766,7 @@ function BucketReplicationFormPage() {
718
766
  })
719
767
  })
720
768
  }),
721
- /*#__PURE__*/ jsx(FormGroup, {
769
+ useV1Format ? /*#__PURE__*/ jsx(Fragment, {}) : /*#__PURE__*/ jsx(FormGroup, {
722
770
  label: "Rule priority",
723
771
  id: "priority",
724
772
  direction: "horizontal",
@@ -743,7 +791,25 @@ function BucketReplicationFormPage() {
743
791
  })
744
792
  ]
745
793
  }),
746
- /*#__PURE__*/ jsx(FilterFormSection, {
794
+ useV1Format ? /*#__PURE__*/ jsx(FormSection, {
795
+ title: {
796
+ name: 'Prefix'
797
+ },
798
+ forceLabelWidth: convertRemToPixels(15),
799
+ children: /*#__PURE__*/ jsx(FormGroup, {
800
+ label: "Prefix",
801
+ id: "prefix",
802
+ direction: "horizontal",
803
+ error: errors?.prefix?.message,
804
+ helpErrorPosition: "bottom",
805
+ labelHelpTooltip: "Object key prefix to filter which objects are replicated",
806
+ content: /*#__PURE__*/ jsx(Input, {
807
+ id: "prefix",
808
+ placeholder: "folder/",
809
+ ...register('prefix')
810
+ })
811
+ })
812
+ }) : /*#__PURE__*/ jsx(FilterFormSection, {
747
813
  filterType: filterType,
748
814
  onFilterTypeChange: (value)=>methods.setValue('filterType', value),
749
815
  prefixRegister: register('prefix'),
@@ -766,6 +832,32 @@ function BucketReplicationFormPage() {
766
832
  storageClassField,
767
833
  /*#__PURE__*/ jsx(CustomDestinationFields, {})
768
834
  ]
835
+ }) : useV1Format ? /*#__PURE__*/ jsxs(Fragment, {
836
+ children: [
837
+ /*#__PURE__*/ jsx(FormGroup, {
838
+ label: "Target Bucket",
839
+ id: "targetBucket",
840
+ direction: "horizontal",
841
+ error: errors?.targetBucket?.message,
842
+ helpErrorPosition: "bottom",
843
+ required: true,
844
+ content: /*#__PURE__*/ jsx(Controller, {
845
+ name: "targetBucket",
846
+ control: control,
847
+ render: ({ field })=>/*#__PURE__*/ jsx(Select, {
848
+ id: "targetBucket",
849
+ value: field.value,
850
+ onChange: field.onChange,
851
+ placeholder: "Select a bucket",
852
+ children: (bucketsData?.Buckets || []).filter((bucket)=>bucket.Name !== bucketName).map((bucket)=>/*#__PURE__*/ jsx(Select.Option, {
853
+ value: bucket.Name || '',
854
+ children: bucket.Name
855
+ }, bucket.Name))
856
+ })
857
+ })
858
+ }),
859
+ storageClassField
860
+ ]
769
861
  }) : /*#__PURE__*/ jsxs(Fragment, {
770
862
  children: [
771
863
  /*#__PURE__*/ jsx(DefaultReplicationDestinationFields, {}),
@@ -773,7 +865,7 @@ function BucketReplicationFormPage() {
773
865
  ]
774
866
  })
775
867
  }),
776
- /*#__PURE__*/ jsxs(FormSection, {
868
+ useV1Format ? null : /*#__PURE__*/ jsxs(FormSection, {
777
869
  title: {
778
870
  name: 'Encryption'
779
871
  },
@@ -837,7 +929,7 @@ function BucketReplicationFormPage() {
837
929
  }) : /*#__PURE__*/ jsx(Fragment, {})
838
930
  ]
839
931
  }),
840
- showAdvancedReplication && /*#__PURE__*/ jsxs(FormSection, {
932
+ showAdvancedReplication && !useV1Format ? /*#__PURE__*/ jsxs(FormSection, {
841
933
  title: {
842
934
  name: 'Additional Options'
843
935
  },
@@ -915,7 +1007,7 @@ function BucketReplicationFormPage() {
915
1007
  })
916
1008
  })
917
1009
  ]
918
- })
1010
+ }) : null
919
1011
  ]
920
1012
  })
921
1013
  });
@@ -1,7 +1,8 @@
1
+ import type { Feature } from '../types';
1
2
  /**
2
3
  * Hook to check if a feature is enabled in the S3 configuration.
3
4
  *
4
5
  * @param feature - The feature name to check
5
6
  * @returns true if enabled, false if disabled, undefined if config not available
6
7
  */
7
- export declare function useFeatures(feature: string): boolean | undefined;
8
+ export declare function useFeatures(feature: Feature): boolean | undefined;
@@ -2,6 +2,10 @@ import type { S3ClientConfig } from '@aws-sdk/client-s3';
2
2
  import type { AwsCredentialIdentity } from '@aws-sdk/types';
3
3
  import type { ProxyConfiguration, S3EventType } from '../config/types';
4
4
  export type { _Object, Bucket, BucketCannedACL, BucketLocationConstraint, CopyObjectCommandInput, CopyObjectCommandOutput, CreateBucketCommandInput, CreateBucketCommandOutput, DeleteBucketCommandInput, DeleteBucketCommandOutput, DeleteBucketCorsCommandOutput, DeleteBucketLifecycleCommandOutput, DeleteBucketPolicyCommandOutput, DeleteBucketReplicationCommandOutput, DeleteBucketTaggingCommandOutput, DeleteMarkerEntry, DeleteObjectCommandInput, DeleteObjectCommandOutput, DeleteObjectsCommandInput, DeleteObjectsCommandOutput, DeleteObjectTaggingCommandOutput, GetBucketAclCommandOutput, GetBucketCorsCommandOutput, GetBucketEncryptionCommandOutput, GetBucketLifecycleConfigurationCommandOutput, GetBucketLocationCommandInput, GetBucketLocationCommandOutput, GetBucketNotificationConfigurationCommandOutput, GetBucketPolicyCommandOutput, GetBucketReplicationCommandOutput, GetBucketTaggingCommandOutput, GetBucketVersioningCommandOutput, GetObjectAclCommandOutput, GetObjectAttributesCommandInput, GetObjectAttributesCommandOutput, GetObjectCommandInput, GetObjectCommandOutput, GetObjectLegalHoldCommandOutput, GetObjectLockConfigurationCommandOutput, GetObjectRetentionCommandOutput, GetObjectTaggingCommandOutput, GetObjectTorrentCommandOutput, HeadObjectCommandInput, HeadObjectCommandOutput, ListBucketsCommandOutput, ListMultipartUploadsCommandInput, ListMultipartUploadsCommandOutput, ListObjectsV2CommandInput, ListObjectsV2CommandOutput, ListObjectVersionsCommandInput, ListObjectVersionsCommandOutput, ObjectCannedACL, ObjectVersion, Owner, PutBucketAclCommandInput, PutBucketCorsCommandInput, PutBucketEncryptionCommandInput, PutBucketLifecycleConfigurationCommandInput, PutBucketNotificationConfigurationCommandInput, PutBucketPolicyCommandInput, PutBucketReplicationCommandInput, PutBucketTaggingCommandInput, PutBucketTaggingCommandOutput, PutBucketVersioningCommandInput, PutObjectAclCommandInput, PutObjectCommandInput, PutObjectCommandOutput, PutObjectLegalHoldCommandInput, PutObjectLockConfigurationCommandInput, PutObjectRetentionCommandInput, PutObjectTaggingCommandInput, RestoreObjectCommandInput, RestoreObjectCommandOutput, SelectObjectContentCommandInput, SelectObjectContentCommandOutput, Tag, } from '@aws-sdk/client-s3';
5
+ /**
6
+ * Known feature flags for the data browser.
7
+ */
8
+ export type Feature = 'replicationAdvanced' | 'replicationV1' | 'metadatasearch' | 'ISV';
5
9
  /**
6
10
  * S3 backend capabilities configuration.
7
11
  */
@@ -22,7 +26,7 @@ export interface S3BrowserConfig extends Omit<S3ClientConfig, 'credentials'> {
22
26
  proxy?: ProxyConfiguration;
23
27
  publicAclIndicator?: string;
24
28
  s3Capabilities?: S3Capabilities;
25
- features?: string[];
29
+ features?: Feature[];
26
30
  /**
27
31
  * Stable identifier for React Query cache isolation.
28
32
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/data-browser-library",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
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",