Skip to content

Commit

Permalink
upcoming: [M3-8378] - Add Bucket Management Properties Tab for Obje…
Browse files Browse the repository at this point in the history
…ct Storage Gen2 (linode#10797)

* Add and init Properties tab setup for obj gen 2

* Fix hostname text alignments for smaller screens

* Clean up..

* Add Styled Action Panel and some cleanup..

* Add notice for success and error events

* Update todo comments

* Update todo comment

* Clean up..

* Add set default rate limit logic

* Passing endpointType prop to BucketProperties

* Add label for bucket rate limit

* Added changeset: Enhance bucket management Properties Tab for object storage gen2

* Update changeset file

* Add logic to disable save button

* Add loading for the hostname in properites tab

* Use location and history hooks for BucketBreadcrumb

* formating...

* Refactor code to integrate React Hook Form

* Clean up...

* Fix padding rule for StyledActionsPanel

* Replace  component with  for support links

* Add feature flag to Properties tab

* Add title to SupportLink

* Resolve Merge conficts and formatting...

* Refactor: Remove useWatch and access rateLimit value directly from field.value

* Refactoring...

* Fix flag check to use 'objectStorageGen2?.enabled' instead of 'objectStorageGen2'

* Remove some filtering by passing down bucket

* Update packages/manager/.changeset/pr-10795-upcoming-features-1724084768188.md

Co-authored-by: Hana Xu <115299789+hana-linode@users.noreply.github.com>

---------

Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com>
Co-authored-by: Jaalah Ramos <125309814+jaalah-akamai@users.noreply.github.com>
Co-authored-by: Hana Xu <115299789+hana-linode@users.noreply.github.com>
  • Loading branch information
4 people authored Aug 28, 2024
1 parent fb5cb06 commit 812843a
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add bucket management Properties Tab for Object Storage Gen2 ([#10795](https://github.com/linode/manager/pull/10795))
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { styled } from '@mui/material/styles';

import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { Paper } from 'src/components/Paper';
import { Typography } from 'src/components/Typography';

export const StyledText = styled(Typography, {
label: 'StyledText',
})(({ theme }) => ({
lineHeight: 0.5,
paddingLeft: 8,
[theme.breakpoints.down('lg')]: {
marginLeft: 8,
},
[theme.breakpoints.down('sm')]: {
lineHeight: 1,
},
}));

export const StyledRootContainer = styled(Paper, {
label: 'StyledRootContainer',
})(({ theme }) => ({
marginTop: 25,
padding: theme.spacing(3),
}));

export const StyledHelperText = styled(Typography, {
label: 'StyledHelperText',
})(({ theme }) => ({
lineHeight: 1.5,
paddingBottom: theme.spacing(),
paddingTop: theme.spacing(),
}));

export const StyledActionsPanel = styled(ActionsPanel, {
label: 'StyledActionsPanel',
})(() => ({
display: 'flex',
justifyContent: 'right',
padding: 0,
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import * as React from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useHistory, useLocation } from 'react-router-dom';

import { Link } from 'src/components/Link';
import { Notice } from 'src/components/Notice/Notice';
import { SupportLink } from 'src/components/SupportLink';
import { Typography } from 'src/components/Typography';
import { getQueryParamFromQueryString } from 'src/utilities/queryParams';

import { BucketRateLimitTable } from '../BucketLanding/BucketRateLimitTable';
import { BucketBreadcrumb } from './BucketBreadcrumb';
import {
StyledActionsPanel,
StyledHelperText,
StyledRootContainer,
StyledText,
} from './BucketProperties.styles';

import type { ObjectStorageBucket } from '@linode/api-v4';

interface Props {
bucket: ObjectStorageBucket;
}

export interface UpdateBucketRateLimitPayload {
rateLimit: string;
}

export const BucketProperties = React.memo((props: Props) => {
const { bucket } = props;
const { endpoint_type, hostname, label } = bucket;

const form = useForm<UpdateBucketRateLimitPayload>({
defaultValues: {
rateLimit: '1',
},
});

const {
formState: { errors, isDirty, isSubmitting },
handleSubmit,
} = form;

const location = useLocation();
const history = useHistory();
const prefix = getQueryParamFromQueryString(location.search, 'prefix');

const onSubmit = () => {
// TODO: OBJGen2 - Handle Bucket Rate Limit update logic once the endpoint for updating is available.
// The 'data' argument is expected -> data: UpdateBucketRateLimitPayload
};

return (
<FormProvider {...form}>
<BucketBreadcrumb bucketName={label} history={history} prefix={prefix} />
<StyledText>{hostname || 'Loading...'}</StyledText>

<StyledRootContainer>
<Typography variant="h2">Bucket Rate Limits</Typography>

{errors.root?.message ? (
<Notice text={errors.root?.message} variant="error" />
) : null}

{/* TODO: OBJGen2 - We need to handle link in upcoming PR */}
<StyledHelperText>
Specifies the maximum Requests Per Second (RPS) for an Endpoint. To
increase it to High,{' '}
<SupportLink
text="open a support ticket"
title="Request to Increase Bucket Rate Limits"
/>
. Understand <Link to="#">bucket rate limits</Link>.
</StyledHelperText>

<form onSubmit={handleSubmit(onSubmit)}>
<BucketRateLimitTable endpointType={endpoint_type} />
<StyledActionsPanel
primaryButtonProps={{
disabled: !isDirty,
label: 'Save',
loading: isSubmitting,
type: 'submit',
}}
/>
</form>
</StyledRootContainer>
</FormProvider>
);
});
31 changes: 25 additions & 6 deletions packages/manager/src/features/ObjectStorage/BucketDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const BucketSSL = React.lazy(() =>
default: module.BucketSSL,
}))
);
const BucketProperties = React.lazy(() =>
import('./BucketProperties').then((module) => ({
default: module.BucketProperties,
}))
);

interface MatchProps {
bucketName: string;
Expand Down Expand Up @@ -54,10 +59,11 @@ export const BucketDetailLanding = React.memo((props: Props) => {
};
const { bucketName, clusterId } = props.match.params;

const { endpoint_type: endpointType } =
bucketsData?.buckets.find(({ label }) => label === bucketName) ?? {};
const bucket = bucketsData?.buckets.find(({ label }) => label === bucketName);

const isSSLEnabled = endpointType !== 'E2' && endpointType === 'E3';
const { endpoint_type } = bucket ?? {};

const isSSLEnabled = endpoint_type !== 'E2' && endpoint_type === 'E3';

const tabs = [
{
Expand All @@ -68,6 +74,14 @@ export const BucketDetailLanding = React.memo((props: Props) => {
routeName: `${props.match.url}/access`,
title: 'Access',
},
...(flags.objectStorageGen2?.enabled
? [
{
routeName: `${props.match.url}/properties`,
title: 'Properties',
},
]
: []),
...(!isSSLEnabled
? [
{
Expand Down Expand Up @@ -112,16 +126,21 @@ export const BucketDetailLanding = React.memo((props: Props) => {
<React.Suspense fallback={<SuspenseLoader />}>
<TabPanels>
<SafeTabPanel index={0}>
<ObjectList {...props} endpointType={endpointType} />
<ObjectList {...props} endpointType={endpoint_type} />
</SafeTabPanel>
<SafeTabPanel index={1}>
<BucketAccess
bucketName={bucketName}
clusterId={clusterId}
endpointType={endpointType}
endpointType={endpoint_type}
/>
</SafeTabPanel>
<SafeTabPanel index={2}>
{flags.objectStorageGen2?.enabled && bucket && (
<SafeTabPanel index={2}>
<BucketProperties bucket={bucket} />
</SafeTabPanel>
)}
<SafeTabPanel index={tabs.length - 1}>
<BucketSSL bucketName={bucketName} clusterId={clusterId} />
</SafeTabPanel>
</TabPanels>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from 'react';
import { useController, useFormContext } from 'react-hook-form';

import { FormControlLabel } from 'src/components/FormControlLabel';
import { Radio } from 'src/components/Radio/Radio';
import { Table } from 'src/components/Table';
import { TableBody } from 'src/components/TableBody';
import { TableCell } from 'src/components/TableCell';
import { TableHead } from 'src/components/TableHead';
import { TableRow } from 'src/components/TableRow';

import type { UpdateBucketRateLimitPayload } from '../BucketDetail/BucketProperties';
import type { ObjectStorageEndpointTypes } from '@linode/api-v4';

/**
Expand All @@ -26,10 +29,14 @@ const tableData = ({ endpointType }: BucketRateLimitTableProps) => {
return [
{
checked: true,
id: '1',
label: 'Basic',
values: ['2,000', '500', '100', '200', '400'],
},
{
checked: false,
id: '2',
label: 'High',
values: [
isE3 ? '20,000' : '5,000',
isE3 ? '2,000' : '1,000',
Expand All @@ -43,58 +50,66 @@ const tableData = ({ endpointType }: BucketRateLimitTableProps) => {

export const BucketRateLimitTable = ({
endpointType,
}: BucketRateLimitTableProps) => (
<Table
sx={{
marginBottom: 3,
}}
>
<TableHead>
<TableRow>
{tableHeaders.map((header, index) => {
return (
<TableCell
sx={{
'&&:last-child': {
paddingRight: 2,
},
}}
key={`${index}-${header}`}
>
{header}
</TableCell>
);
})}
</TableRow>
</TableHead>
<TableBody>
{tableData({ endpointType }).map((row, rowIndex) => (
<TableRow key={rowIndex}>
<TableCell>
<Radio
checked={row.checked}
disabled
name="limit-selection"
onChange={() => {}}
value="2"
/>
</TableCell>
{row.values.map((value, index) => {
}: BucketRateLimitTableProps) => {
const { control } = useFormContext<UpdateBucketRateLimitPayload>();
const { field } = useController({
control,
name: 'rateLimit',
});

return (
<Table sx={{ marginBottom: 3 }}>
<TableHead>
<TableRow>
{tableHeaders.map((header, index) => {
return (
<TableCell
sx={{
'&&:last-child': {
paddingRight: 2,
},
}}
key={`${index}-${value}`}
key={`${index}-${header}`}
>
{value}
{header}
</TableCell>
);
})}
</TableRow>
))}
</TableBody>
</Table>
);
</TableHead>
<TableBody>
{tableData({ endpointType }).map((row, rowIndex) => (
<TableRow key={rowIndex}>
<TableCell>
<FormControlLabel
control={
<Radio
checked={field.value === row.id}
disabled
onChange={() => field.onChange(row.id)}
value={row.id}
/>
}
label={row.label}
/>
</TableCell>
{row.values.map((value, index) => {
return (
<TableCell
sx={{
'&&:last-child': {
paddingRight: 2,
},
}}
key={`${index}-${value}`}
>
{value}
</TableCell>
);
})}
</TableRow>
))}
</TableBody>
</Table>
);
};

0 comments on commit 812843a

Please sign in to comment.