Skip to content

Commit

Permalink
new VeeamCapacityOverviewRow component
Browse files Browse the repository at this point in the history
  • Loading branch information
hervedombya committed Dec 6, 2023
1 parent 6026d7e commit 0abcf6a
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 46 deletions.
47 changes: 3 additions & 44 deletions src/react/databrowser/buckets/details/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@ import { DumbErrorModal } from '../../../ui-elements/ErrorHandlerModal';
import { HelpAsyncNotification } from '../../../ui-elements/Help';
import { CellLink, TableContainer } from '../../../ui-elements/Table';
import Table, * as T from '../../../ui-elements/TableKeyValue2';
import { VeeamCapacityModal } from '../../../ui-elements/Veeam/VeeamCapacityModal';
import { VeeamCapacityOverviewRow } from '../../../ui-elements/Veeam/VeeamCapacityOverviewRow';
import {
BUCKET_TAG_VEEAM_APPLICATION,
VeeamApplicationType,
} from '../../../ui-elements/Veeam/VeeamConstants';
import { decodeEntities } from '../../../ui-elements/Veeam/decodeEntities';
import { maybePluralize } from '../../../utils';
import {
getLocationIngestionState,
getLocationType,
} from '../../../utils/storageOptions';
import { useWorkflows } from '../../../workflow/Workflows';
import { decodeEntities } from '../../../ui-elements/Veeam/decodeEntities';
import prettyBytes from 'pretty-bytes';

function capitalize(string: string) {
return string.toLowerCase().replace(/^\w/, (c) => {
Expand Down Expand Up @@ -234,28 +233,6 @@ function Overview({ bucket, ingestionStates }: Props) {
veeamTagApplication === VeeamApplicationType.VEEAM_OFFICE_365) &&
VEEAM_FEATURE_FLAG_ENABLED;

const isSOSAPIEnabled =
isVeeamBucket &&
veeamTagApplication === VeeamApplicationType.VEEAM_BACKUP_REPLICATION;

const s3Client = useS3Client();
const { data: veeamObject, status: veeamObjectStatus } = useQuery(
getVeeamObject({
bucketName: bucket.name,
s3Client,
}),
);

const xml = veeamObject?.Body?.toString();
const capacity =
new DOMParser()
?.parseFromString(xml || '', 'application/xml')
?.querySelector('Capacity')?.textContent || '0';
const prettyBytesClusterCapacity = prettyBytes(parseInt(capacity, 10), {
locale: 'en',
binary: true,
});

useEffect(() => {
dispatch(getBucketInfo(bucket.name));
}, [dispatch, bucket.name]);
Expand Down Expand Up @@ -343,25 +320,7 @@ function Overview({ bucket, ingestionStates }: Props) {
<T.Key> Application </T.Key>
<T.Value> Backup - {veeamTagApplication}</T.Value>
</T.Row>
{isSOSAPIEnabled && (
<T.Row>
<T.Key> Max repository Capacity </T.Key>
<T.GroupValues>
<>
{veeamObjectStatus === 'loading'
? 'Loading...'
: veeamObjectStatus === 'error'
? 'Error'
: prettyBytesClusterCapacity}
</>
<VeeamCapacityModal
bucketName={bucket.name}
maxCapacity={prettyBytesClusterCapacity}
status={veeamObjectStatus}
/>
</T.GroupValues>
</T.Row>
)}
<VeeamCapacityOverviewRow bucketName={bucket.name} />
</T.Group>
)}
<T.Group>
Expand Down
4 changes: 2 additions & 2 deletions src/react/ui-elements/Veeam/VeeamCapacityModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export const VeeamCapacityModalInternal = ({
const { mutate } = usePutObjectMutation();
const { showToast } = useToast();
const queryClient = useQueryClient();
const formRef = useRef(null);
const currentCapacity = watch('capacity');

const onSubmit = ({ capacity, capacityUnit }: VeeamCapacityForm) => {
Expand Down Expand Up @@ -125,6 +124,7 @@ export const VeeamCapacityModalInternal = ({
label="Cancel"
/>
<Button
form="capacity-form"
type="submit"
variant="primary"
aria-label="Update max capacity"
Expand All @@ -137,7 +137,7 @@ export const VeeamCapacityModalInternal = ({
</Wrap>
}
>
<form onSubmit={handleSubmit(onSubmit)} ref={formRef}>
<form id="capacity-form" onSubmit={handleSubmit(onSubmit)}>
<VeeamCapacityFormSection autoFocusEnabled={isCapacityModalOpen} />
</form>
</Modal>
Expand Down
80 changes: 80 additions & 0 deletions src/react/ui-elements/Veeam/VeeamCapacityOverviewRow.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { render, screen } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { VeeamCapacityOverviewRow } from './VeeamCapacityOverviewRow';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { bucketName } from '../../../js/mock/S3Client';
import { NewWrapper, TEST_API_BASE_URL } from '../../utils/testUtil';
import { VEEAM_XML_PREFIX } from './VeeamConstants';

const queryClient = new QueryClient();

describe('VeeamCapacityOverviewRow', () => {
const server = setupServer(
rest.get(
`${TEST_API_BASE_URL}/${bucketName}/${VEEAM_XML_PREFIX}/capacity.xml`,
(req, res, ctx) => {
return res(ctx.status(200));
},
),
);
beforeAll(() => {
server.listen({ onUnhandledRequest: 'error' });
});
afterEach(() => {
server.resetHandlers();
});
afterAll(() => server.close());

it('should render the row', () => {
render(
<QueryClientProvider client={queryClient}>
<VeeamCapacityOverviewRow bucketName="testBucket" />
</QueryClientProvider>,
{ wrapper: NewWrapper() },
);
expect(screen.getByText('Max repository Capacity')).toBeInTheDocument();
expect(screen.getByText('100 B')).toBeInTheDocument();
});

it('should not render the row if SOSAPI is not enabled', () => {
mockUseBucketTagging.mockReturnValue({
status: 'success',
value: { BUCKET_TAG_VEEAM_APPLICATION: 'OTHER_APPLICATION' },
});
render(
<QueryClientProvider client={queryClient}>
<VeeamCapacityOverviewRow bucketName="testBucket" />
</QueryClientProvider>,
);
expect(
screen.queryByText('Max repository Capacity'),
).not.toBeInTheDocument();
});

it('should display loading state', () => {
mockUseQuery.mockReturnValue({
data: null,
status: 'loading',
});
render(
<QueryClientProvider client={queryClient}>
<VeeamCapacityOverviewRow bucketName="testBucket" />
</QueryClientProvider>,
);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});

it('should display error state', () => {
mockUseQuery.mockReturnValue({
data: null,
status: 'error',
});
render(
<QueryClientProvider client={queryClient}>
<VeeamCapacityOverviewRow bucketName="testBucket" />
</QueryClientProvider>,
);
expect(screen.getByText('Error')).toBeInTheDocument();
});
});
73 changes: 73 additions & 0 deletions src/react/ui-elements/Veeam/VeeamCapacityOverviewRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import prettyBytes from 'pretty-bytes';
import { VEEAM_FEATURE } from '../../../js/config';
import { useBucketTagging } from '../../next-architecture/domain/business/buckets';
import { useConfig } from '../../next-architecture/ui/ConfigProvider';
import { useS3Client } from '../../next-architecture/ui/S3ClientProvider';
import { getVeeamObject } from '../../queries';
import * as T from '../TableKeyValue2';
import { VeeamCapacityModal } from './VeeamCapacityModal';
import {
BUCKET_TAG_VEEAM_APPLICATION,
VeeamApplicationType,
} from './VeeamConstants';
import { decodeEntities } from './decodeEntities';
import { useQuery } from 'react-query';

export const VeeamCapacityOverviewRow = ({
bucketName,
}: {
bucketName: string;
}) => {
const s3Client = useS3Client();
const { tags } = useBucketTagging({ bucketName });
const { features } = useConfig();
const VEEAM_FEATURE_FLAG_ENABLED = features.includes(VEEAM_FEATURE);
const veeamTagApplication =
tags.status === 'success' &&
decodeEntities(tags.value?.[BUCKET_TAG_VEEAM_APPLICATION]);

const isSOSAPIEnabled =
veeamTagApplication === VeeamApplicationType.VEEAM_BACKUP_REPLICATION &&
VEEAM_FEATURE_FLAG_ENABLED;

const { data: veeamObject, status: veeamObjectStatus } = useQuery(
getVeeamObject({
bucketName,
s3Client,
}),
);

const xml = veeamObject?.Body?.toString();
const capacity =
new DOMParser()
?.parseFromString(xml || '', 'application/xml')
?.querySelector('Capacity')?.textContent || '0';
const prettyBytesClusterCapacity = prettyBytes(parseInt(capacity, 10), {
locale: 'en',
binary: true,
});

if (isSOSAPIEnabled) {
return (
<T.Row>
<T.Key> Max repository Capacity </T.Key>
<T.GroupValues>
<>
{veeamObjectStatus === 'loading'
? 'Loading...'
: veeamObjectStatus === 'error'
? 'Error'
: prettyBytesClusterCapacity}
</>
<VeeamCapacityModal
bucketName={bucketName}
maxCapacity={prettyBytesClusterCapacity}
status={veeamObjectStatus}
/>
</T.GroupValues>
</T.Row>
);
}

return <></>;
};

0 comments on commit 0abcf6a

Please sign in to comment.