Skip to content

Commit

Permalink
upcoming: [M3-7694] - OBJ Multi Cluster Copy updates (linode#10188)
Browse files Browse the repository at this point in the history
* Remove text "S3 Endpoint"

* Update Error message in Add / Edit Access Key Drawer

* Add tooltip and change column name "None" to "No Access"

* Added changeset: OBJ Multi Cluster Copy updates.

* Update packages/manager/.changeset/pr-10188-upcoming-features-1707837776464.md

Co-authored-by: Mariah Jacobs <[email protected]>

* Update packages/manager/src/features/ObjectStorage/AccessKeyLanding/LimitedAccessControls.tsx

Co-authored-by: Mariah Jacobs <[email protected]>

* Hide copy all when one host name exists

* Adjust styles for hostname label

* Adjust padding

---------

Co-authored-by: Mariah Jacobs <[email protected]>
  • Loading branch information
cpathipa and mjac0bs authored Feb 16, 2024
1 parent 42137a0 commit 8e5bb02
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Update OBJ Multi-Cluster copy ([#10188](https://github.com/linode/manager/pull/10188))
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@ import { styled } from '@mui/material/styles';
import React, { useState } from 'react';

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 { TableBody } from 'src/components/TableBody';

import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';

import { HostNamesDrawer } from '../HostNamesDrawer';
import { OpenAccessDrawer } from '../types';

import { AccessKeyTableBody } from './AccessKeyTableBody';

export interface AccessKeyTableProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ export const BucketPermissionsTable = React.memo((props: Props) => {
return (
<StyledTableRoot
aria-label="Object Storage Access Key Permissions"
spacingTop={24}
spacingTop={16}
>
<TableHead>
<TableRow>
<TableCell data-qa-perm-region>Region</TableCell>
<TableCell data-qa-perm-bucket>Bucket</TableCell>
<TableCell data-qa-perm-none>None</TableCell>
<TableCell data-qa-perm-none sx={{ minWidth: '100px' }}>
No Access
</TableCell>
<TableCell data-qa-perm-read sx={{ minWidth: '100px' }}>
Read Only
</TableCell>
Expand Down Expand Up @@ -170,7 +172,10 @@ export const BucketPermissionsTable = React.memo((props: Props) => {
key={scopeName}
mode={mode}
>
<StyledClusterCell padding="checkbox">
<StyledClusterCell
padding="checkbox"
sx={{ minWidth: '150px' }}
>
{regionsLookup[thisScope.region ?? '']?.label}
</StyledClusterCell>
<StyledBucketCell padding="checkbox">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import { InputLabel } from 'src/components/InputLabel';
import { Tooltip } from 'src/components/Tooltip';

export interface Props {
hideShowAll?: boolean;
text: string;
}

export const CopyAll = (props: Props) => {
export const CopyAllHostnames = (props: Props) => {
const [copied, setCopied] = React.useState<boolean>(false);
const { text } = props;
const { hideShowAll = false, text } = props;

const handleIconClick = () => {
setCopied(true);
Expand All @@ -23,28 +24,31 @@ export const CopyAll = (props: Props) => {

return (
<StyledBox>
<InputLabel>S3 Endpoint Hostnames</InputLabel>
<Tooltip
className="copy-tooltip"
data-qa-copied
placement="top"
title={copied ? 'Copied!' : 'Copy'}
>
<StyledLinkButton
aria-label={`Copy ${text} to clipboard`}
name={text}
onClick={handleIconClick}
type="button"
<InputLabel sx={{ margin: 0 }}>S3 Endpoint Hostnames</InputLabel>
{!hideShowAll && (
<Tooltip
className="copy-tooltip"
data-qa-copied
placement="top"
title={copied ? 'Copied!' : 'Copy'}
>
Copy all
</StyledLinkButton>
</Tooltip>
<StyledLinkButton
aria-label={`Copy ${text} to clipboard`}
name={text}
onClick={handleIconClick}
type="button"
>
Copy all
</StyledLinkButton>
</Tooltip>
)}
</StyledBox>
);
};

const StyledBox = styled(Box, { label: 'StyledBox' })(({ theme }) => ({
borderColor: theme.name === 'light' ? '#ccc' : '#222',
borderColor: theme.name === 'light' ? theme.color.grey3 : theme.color.black,
display: 'flex',
justifyContent: 'space-between',
marginBottom: theme.spacing(1),
}));
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ describe('HostNamesDrawer', () => {
).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: 'Copy S3 Endpoint: Atlanta, GA: endpoint2 to clipboard',
name: 'Copy Atlanta, GA: endpoint2 to clipboard',
})
).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: 'Copy S3 Endpoint: Newark, NJ: endpoint1 to clipboard',
name: 'Copy Newark, NJ: endpoint1 to clipboard',
})
).toBeInTheDocument();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Drawer } from 'src/components/Drawer';
import { useRegionsQuery } from 'src/queries/regions';
import { getRegionsByRegionId } from 'src/utilities/regions';

import { CopyAll } from './CopyAll';
import { CopyAllHostnames } from './CopyAllHostnames';

interface Props {
onClose: () => void;
Expand All @@ -27,14 +27,12 @@ export const HostNamesDrawer = (props: Props) => {
return (
<Drawer onClose={onClose} open={open} title="Regions / S3 Hostnames">
<Box sx={(theme) => ({ marginTop: theme.spacing(3) })}>
<CopyAll
<CopyAllHostnames
text={
regions
.map(
(region) =>
`S3 Endpoint: ${regionsLookup[region.id]?.label}: ${
region.s3_endpoint
}`
`${regionsLookup[region.id]?.label}: ${region.s3_endpoint}`
)
.join('\n') ?? ''
}
Expand All @@ -49,13 +47,11 @@ export const HostNamesDrawer = (props: Props) => {
>
{regions.map((region, index) => (
<CopyableTextField
value={`S3 Endpoint: ${regionsLookup[region.id]?.label}: ${
region.s3_endpoint
}`}
hideLabel
key={index}
label={`${region.id}: ${region.s3_endpoint}`}
sx={{ border: 'none', maxWidth: '100%' }}
value={`${regionsLookup[region.id]?.label}: ${region.s3_endpoint}`}
/>
))}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,28 @@ import { Toggle } from 'src/components/Toggle/Toggle';
import { Typography } from 'src/components/Typography';
import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { TooltipIcon } from 'src/components/TooltipIcon';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';

import { AccessTable } from './AccessTable';
import { BucketPermissionsTable } from './BucketPermissionsTable';
import { MODE } from './types';

type LabelWithTooltipProps = {
labelText: string;
tooltipText: string;
};

const LabelWithTooltip = ({
labelText,
tooltipText,
}: LabelWithTooltipProps) => (
<React.Fragment>
<Typography component="span">{labelText}</Typography>
{tooltipText && <TooltipIcon status="help" text={tooltipText} />}
</React.Fragment>
);

interface Props {
bucket_access: Scope[] | null;
checked: boolean;
Expand All @@ -36,6 +52,10 @@ export const LimitedAccessControls = React.memo((props: Props) => {
return (
<>
<FormControlLabel
sx={(theme) => ({
marginTop: theme.spacing(0.5),
marginBottom: theme.spacing(0.5),
})}
control={
<Toggle
checked={checked}
Expand All @@ -44,7 +64,16 @@ export const LimitedAccessControls = React.memo((props: Props) => {
onChange={handleToggle}
/>
}
label={'Limited Access'}
label={
isObjMultiClusterEnabled ? (
<LabelWithTooltip
labelText="Limited Access"
tooltipText="A Limited Access key has no permissions and you can manually set them. If you don't turn on Limited Access, the key is granted full permission in all regions."
/>
) : (
'Limited Access'
)
}
/>
<Typography>
Limited access keys can list all buckets, regardless of access. They can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {
Scope,
UpdateObjectStorageKeyRequest,
} from '@linode/api-v4/lib/object-storage';
import { createObjectStorageKeysSchema } from '@linode/validation/lib/objectStorageKeys.schema';
import { useFormik, FormikProps } from 'formik';
import {
createObjectStorageKeysSchema,
updateObjectStorageKeysSchema,
} from '@linode/validation/lib/objectStorageKeys.schema';
import { FormikProps, useFormik } from 'formik';
import React, { useEffect, useState } from 'react';

import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
Expand Down Expand Up @@ -166,7 +169,9 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
},
validateOnBlur: true,
validateOnChange: false,
validationSchema: createObjectStorageKeysSchema,
validationSchema: createMode
? createObjectStorageKeysSchema
: updateObjectStorageKeysSchema,
});

const isSaveDisabled =
Expand Down Expand Up @@ -295,6 +300,17 @@ export const OMC_AccessKeyDrawer = (props: AccessKeyDrawerProps) => {
required
selectedRegion={formik.values.regions}
/>
{createMode && (
<Typography
sx={(theme) => ({
marginTop: theme.spacing(2),
})}
>
Unlimited S3 access key can be used to create buckets in the
selected region using S3 Endpoint returned on successful creation
of the key.
</Typography>
)}
{createMode && !bucketsError && (
<LimitedAccessControls
bucket_access={formik.values.bucket_access}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { ConfirmationDialog } from 'src/components/ConfirmationDialog/Confirmati
import { CopyableAndDownloadableTextField } from 'src/components/CopyableAndDownloadableTextField';
import { CopyableTextField } from 'src/components/CopyableTextField/CopyableTextField';
import { Notice } from 'src/components/Notice/Notice';
import { CopyAll } from 'src/features/ObjectStorage/AccessKeyLanding/CopyAll';
import { CopyAllHostnames } from 'src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames';
import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { useRegionsQuery } from 'src/queries/regions';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';
import { getRegionsByRegionId } from 'src/utilities/regions';

import type { ObjectStorageKey } from '@linode/api-v4/lib/object-storage';
interface Props {
Expand All @@ -37,6 +39,9 @@ const renderActions = (
export const SecretTokenDialog = (props: Props) => {
const { objectStorageKey, onClose, open, title, value } = props;

const { data: regionsData } = useRegionsQuery();
const regionsLookup = regionsData && getRegionsByRegionId(regionsData);

const flags = useFlags();
const { account } = useAccountManagement();

Expand Down Expand Up @@ -73,11 +78,17 @@ export const SecretTokenDialog = (props: Props) => {
/>
{isObjMultiClusterEnabled && (
<div>
<CopyAll
<CopyAllHostnames
hideShowAll={Boolean(
objectStorageKey && objectStorageKey?.regions?.length <= 1
)}
text={
objectStorageKey?.regions
.map(
(region) => `S3 Endpoint: ${region.id}: ${region.s3_endpoint}`
(region) =>
`${regionsLookup?.[region.id]?.label}: ${
region.s3_endpoint
}`
)
.join('\n') ?? ''
}
Expand All @@ -87,23 +98,20 @@ export const SecretTokenDialog = (props: Props) => {
{isObjMultiClusterEnabled && (
<Box
sx={(theme) => ({
'.copyIcon': {
marginRight: 0,
paddingRight: 0,
},
backgroundColor: theme.bg.main,
border: `1px solid ${theme.color.grey3}`,
borderColor: theme.name === 'light' ? '#ccc' : '#222',
padding: theme.spacing(1),
})}
>
{objectStorageKey?.regions.map((region, index) => (
<CopyableTextField
value={`${regionsLookup?.[region.id]?.label}: ${
region.s3_endpoint
}`}
hideLabel
key={index}
label="Create a Filesystem"
sx={{ border: 'none', maxWidth: '100%' }}
value={`S3 Endpoint: ${region.id}: ${region.s3_endpoint}`}
/>
))}
</Box>
Expand Down
7 changes: 5 additions & 2 deletions packages/validation/src/objectStorageKeys.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const createObjectStorageKeysSchema = object({
.trim(),
regions: array()
.of(string())
.min(1, 'Regions must include at least one region')
.min(1, 'Select at least one region to continue')
.notRequired(),
});

Expand All @@ -22,6 +22,9 @@ export const updateObjectStorageKeysSchema = object({
.trim(),
regions: array()
.of(string())
.min(1, 'Regions must include at least one region')
.min(
1,
'You need to select at least one region. To delete all keys, go to the Access Keys page in Cloud Manager and select Revoke.'
)
.notRequired(),
});

0 comments on commit 8e5bb02

Please sign in to comment.