Skip to content

Commit

Permalink
Merge branch 'bugfix/ZKUI-370-disable-versioning-on-bucket-that-creat…
Browse files Browse the repository at this point in the history
…e-on-azure-blob-location' into tmp/octopus/w/2.0/bugfix/ZKUI-370-disable-versioning-on-bucket-that-create-on-azure-blob-location
  • Loading branch information
bert-e committed Jul 13, 2023
2 parents 90802dd + 000b959 commit 8087801
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 248 deletions.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"@hookform/resolvers": "^2.8.8",
"@js-temporal/polyfill": "^0.4.3",
"@monaco-editor/react": "^4.4.5",
"@scality/core-ui": "github:scality/core-ui#0.86.0",
"@scality/core-ui": "github:scality/core-ui#0.89.0",
"@types/react-table": "^7.7.10",
"@types/react-virtualized": "^9.21.20",
"@types/react-window": "^1.8.5",
Expand Down
15 changes: 15 additions & 0 deletions src/js/mock/S3Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ export const bucketInfoResponseObjectLockDefaultRetention: BucketInfo = {
},
},
};

export const bucketInfoResponseVersioningDisabled: BucketInfo = {
name: bucketName,
policy: false,
owner: ownerName,
aclGrantees: 0,
cors: false,
versioning: 'Disabled',
isVersioning: false,
public: false,
locationConstraint: 'azure-blob',
objectLockConfiguration: {
ObjectLockEnabled: 'Disabled',
},
};
export class MockS3Client implements S3ClientInterface {
listBucketsWithLocation() {
return Promise.resolve({
Expand Down
86 changes: 44 additions & 42 deletions src/react/databrowser/buckets/BucketCreate.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {
Banner,
Checkbox,
Form,
FormGroup,
FormSection,
Icon,
Stack,
Toggle,
} from '@scality/core-ui';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import { ChangeEvent, useMemo, useRef } from 'react';
Expand Down Expand Up @@ -75,6 +75,7 @@ function BucketCreate() {
const { isValid, errors } = formState;
const isObjectLockEnabled = watch('isObjectLockEnabled');
const isAsyncNotification = watch('isAsyncNotification');
const isVersioning = watch('isVersioning');
const watchLocationName = watch('locationName');
const dispatch = useDispatch();
const hasError = useSelector(
Expand Down Expand Up @@ -103,6 +104,13 @@ function BucketCreate() {
[watchLocationName, locations, capabilities],
);

const isLocationAzureOrGcpSelected = (locationName: string) =>
locations?.[locationName]?.locationType === 'location-azure-v1' ||
locations?.[locationName]?.locationType === 'location-gcp-v1';

const isWatchedLocationAzureOrGCPSelected =
isLocationAzureOrGcpSelected(watchLocationName);

const clearServerError = () => {
if (hasError) {
dispatch(clearError());
Expand Down Expand Up @@ -305,6 +313,11 @@ function BucketCreate() {
// Note: when changing location we make sure
// to reset isAsyncNotification toggle to false.
setValue('isAsyncNotification', false);
if (isLocationAzureOrGcpSelected(value)) {
setValue('isVersioning', false);
} else if (isObjectLockEnabled) {
setValue('isVersioning', true);
}
}}
placeholder="Location Name"
value={locationName}
Expand Down Expand Up @@ -345,27 +358,15 @@ function BucketCreate() {
'Enabling Async Metadata updates automatically activates Versioning for the bucket, and you won’t be able to suspend Versioning.'
}
content={
<Controller
control={control}
name="isAsyncNotification"
render={({
field: { onChange, value: isAsyncNotification },
}) => {
return (
<>
<Toggle
disabled={!isAsyncNotificationReady}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
onChange(e.target.checked);
matchVersioning(e.target.checked);
}}
label={isAsyncNotification ? 'Enabled' : 'Disabled'}
toggle={isAsyncNotification}
placeholder="isAsyncNotification"
/>
</>
);
}}
<Checkbox
id="isAsyncNotification"
disabled={!isAsyncNotificationReady}
label={isAsyncNotification ? 'Enabled' : 'Disabled'}
{...register('isAsyncNotification', {
onChange(e: ChangeEvent<HTMLInputElement>) {
matchVersioning(e.target.checked);
},
})}
/>
}
/>
Expand All @@ -389,38 +390,39 @@ function BucketCreate() {
</ul>
}
content={
<Controller
control={control}
name="isVersioning"
render={({ field: { onChange, value: isVersioning } }) => {
return (
<Toggle
disabled={isObjectLockEnabled || isAsyncNotification}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
onChange(e.target.checked)
}
placeholder="Versioning"
label={isVersioning ? 'Active' : 'Inactive'}
toggle={isVersioning}
/>
);
}}
<Checkbox
id="isVersioning"
disabled={
isObjectLockEnabled ||
isAsyncNotification ||
isWatchedLocationAzureOrGCPSelected
}
label={isVersioning ? 'Active' : 'Inactive'}
{...register('isVersioning')}
/>
}
disabled={isObjectLockEnabled || isAsyncNotification}
disabled={
isObjectLockEnabled ||
isAsyncNotification ||
isWatchedLocationAzureOrGCPSelected
}
helpErrorPosition="bottom"
help={
isObjectLockEnabled || isAsyncNotification
isWatchedLocationAzureOrGCPSelected
? 'Selected Storage Location does not support versioning.'
: isObjectLockEnabled || isAsyncNotification
? `Automatically activated when
${isObjectLockEnabled ? 'Object-lock' : ''}
${isObjectLockEnabled && isAsyncNotification ? 'or' : ''}
${isAsyncNotification ? 'Async Metadata updates' : ''}
is Enabled`
: 'Automatically activated when Object-lock is Active.'
: ''
}
/>
</FormSection>
<ObjectLockRetentionSettings />
<ObjectLockRetentionSettings
isLocationAzureOrGcpSelected={isWatchedLocationAzureOrGCPSelected}
/>
</Form>
</FormProvider>
);
Expand Down
110 changes: 62 additions & 48 deletions src/react/databrowser/buckets/ObjectLockRetentionSettings.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import { FormProvider, useForm } from 'react-hook-form';
import ObjectLockRetentionSettings from './ObjectLockRetentionSettings';
import { reduxMountAct } from '../../utils/testUtil';
import { act } from 'react-dom/test-utils';
import { reduxRender } from '../../utils/testUtil';
import { fireEvent, screen } from '@testing-library/react';
import { useEffect } from 'react';
import userEvent from '@testing-library/user-event';
import { notFalsyTypeGuard } from '../../../types/typeGuards';

describe('ObjectLockRetentionSettings', () => {
it('should enable retention settings when default retention is checked', async () => {
const selectors = {
defaultRetention: () => screen.getByLabelText(/Default Retention/i),
governanceRetentionMode: () => screen.getByLabelText(/Governance/i),
complianceRetentionMode: () => screen.getByLabelText(/Compliance/i),
retentionPeriodInput: () => screen.getByLabelText(/retention period/i),
retentionPeriodDaysOption: () =>
screen.getByRole('option', { name: /days/i }),
retentionPeriodYearsOption: () =>
screen.getByRole('option', { name: /years/i }),
objectlock: () => screen.getByLabelText(/object-lock/i),
};
it('should enable retention settings when default retention is checked', () => {
//S
const Form = () => {
const methods = useForm({
defaultValues: {
Expand All @@ -18,32 +33,50 @@ describe('ObjectLockRetentionSettings', () => {
);
};

const component = await reduxMountAct(<Form />);
const {
component: { container },
} = reduxRender(<Form />);
//E
userEvent.click(selectors.defaultRetention());
//V
expect(selectors.governanceRetentionMode()).toBeEnabled();
expect(selectors.complianceRetentionMode()).toBeEnabled();
expect(selectors.retentionPeriodInput()).toBeEnabled();
userEvent.click(selectors.retentionPeriodInput());

const objectLockDefaultRetentionEnabled = component.find(
'input[placeholder="isDefaultRetentionEnabled"]',
const selector = notFalsyTypeGuard(
container.querySelector('.sc-select__control'),
);

await act(async () => {
objectLockDefaultRetentionEnabled.simulate('change', {
target: {
checked: true,
fireEvent.keyDown(selector, { key: 'ArrowDown', which: 40, keyCode: 40 });
expect(selectors.retentionPeriodYearsOption()).toBeInTheDocument();
expect(selectors.retentionPeriodDaysOption()).toBeInTheDocument();
});
it('should disable the object-lock while editing on bucket that was created with object-lock enabled and should disable retention setting since retention is not active', () => {
//S
const Form = () => {
const methods = useForm({
defaultValues: {
isObjectLockEnabled: true,
isDefaultRetentionEnabled: false,
},
});
});
expect(
component.find('input[value="GOVERNANCE"]').getDOMNode().disabled,
).toBe(false);
expect(
component.find('input[value="COMPLIANCE"]').getDOMNode().disabled,
).toBe(false);
expect(
component.find('input[name="retentionPeriod"]').getDOMNode().disabled,
).toBe(false);
expect(
component.find('input[id="retentionPeriodFrequencyChoice"]').getDOMNode()
.disabled,
).toBe(false);
return (
<FormProvider {...methods}>
<ObjectLockRetentionSettings isEditRetentionSetting />
</FormProvider>
);
};
reduxRender(<Form />);
//E+V
expect(selectors.objectlock()).toBeChecked();
expect(selectors.objectlock()).toBeDisabled();
expect(selectors.defaultRetention()).not.toBeChecked();
expect(selectors.defaultRetention()).toBeEnabled();
// The retention setting should be disabled since default retention is not enabled
expect(selectors.governanceRetentionMode()).not.toBeChecked();
expect(selectors.governanceRetentionMode()).toBeDisabled();
expect(selectors.complianceRetentionMode()).not.toBeChecked();
expect(selectors.complianceRetentionMode()).toBeDisabled();
});
it('should display retention period error message', async () => {
const Form = () => {
Expand All @@ -60,32 +93,13 @@ describe('ObjectLockRetentionSettings', () => {

return (
<FormProvider {...methods}>
<ObjectLockRetentionSettings />
<ObjectLockRetentionSettings isLocationAzureOrGcpSelected={false} />
</FormProvider>
);
};

const component = await reduxMountAct(<Form />);
await act(async () => {
const objectLockDefaultRetentionEnabled = component.find(
'input[placeholder="isDefaultRetentionEnabled"]',
);
objectLockDefaultRetentionEnabled.simulate('change', {
target: {
checked: true,
},
});
});
expect(
component
.find(
`#${component
.find('input[name="retentionPeriod"]')
.getDOMNode()
.getAttribute('aria-describedby')}`,
)
.first()
.text(),
).toContain('Expected error');
reduxRender(<Form />);
userEvent.click(selectors.defaultRetention());
expect(screen.getByText(/expected error/i)).toBeInTheDocument();
});
});
Loading

0 comments on commit 8087801

Please sign in to comment.