Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upcoming: [M3-7798] - Update Buckets landing page to use regions instead of clusters. #10244

Merged
merged 15 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Upcoming Features
---

Add temporary deleteBucketWithRegion method for OBJ Multicluster ([#10244](https://github.com/linode/manager/pull/10244))
29 changes: 29 additions & 0 deletions packages/api-v4/src/object-storage/buckets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,35 @@ export const deleteBucket = ({
setMethod('DELETE')
);

/**
* deleteBucketWithRegion
*
* Removes a Bucket from your account with region.
*
* NOTE: Attempting to delete a non-empty bucket will result in an error.
*/
/*
@TODO OBJ Multicluster: deleteBucketWithRegion is a function,
once feature is rolled out we replace it with existing deleteBucket
by updating it with region instead of cluster.
*/

export const deleteBucketWithRegion = ({
region,
label,
}: {
region: string;
label: string;
}) =>
Request<ObjectStorageBucket>(
setURL(
`${API_ROOT}/object-storage/buckets/${encodeURIComponent(
region
)}/${encodeURIComponent(label)}`
),
setMethod('DELETE')
);

/**
* Returns a list of Objects in a given Bucket.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Update Buckets landing page to use regions instead of clusters ([#10244](https://github.com/linode/manager/pull/10244))
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,9 @@ describe('object storage smoke tests', () => {
cy.visitWithLogin('/object-storage');
cy.wait(['@getRegions', '@getBuckets']);

ui.button
.findByTitle('Create Bucket')
.should('be.visible')
.should('be.enabled')
.click();
ui.entityHeader.find().within(() => {
ui.button.findByTitle('Create Bucket').should('be.visible').click();
});

ui.drawer
.findByTitle('Create Bucket')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Region } from '@linode/api-v4';
import {
ACLType,
getBucketAccess,
Expand All @@ -11,9 +12,12 @@ import { Divider } from 'src/components/Divider';
import { Drawer } from 'src/components/Drawer';
import { Link } from 'src/components/Link';
import { Typography } from 'src/components/Typography';
import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { useObjectStorageClusters } from 'src/queries/objectStorage';
import { useProfile } from 'src/queries/profile';
import { useRegionsQuery } from 'src/queries/regions';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';
import { formatDate } from 'src/utilities/formatDate';
import { pluralize } from 'src/utilities/pluralize';
import { truncateMiddle } from 'src/utilities/truncate';
Expand All @@ -22,6 +26,7 @@ import { readableBytes } from 'src/utilities/unitConversions';
import { AccessSelect } from '../BucketDetail/AccessSelect';
export interface BucketDetailsDrawerProps {
bucketLabel?: string;
bucketRegion?: Region;
cluster?: string;
created?: string;
hostname?: string;
Expand All @@ -35,6 +40,7 @@ export const BucketDetailsDrawer = React.memo(
(props: BucketDetailsDrawerProps) => {
const {
bucketLabel,
bucketRegion,
cluster,
created,
hostname,
Expand All @@ -44,6 +50,16 @@ export const BucketDetailsDrawer = React.memo(
size,
} = props;

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

const isObjMultiClusterEnabled = isFeatureEnabled(
'Object Storage Access Key Regions',
Boolean(flags.objMultiCluster),
account?.capabilities ?? []
);

// @TODO OBJ Multicluster: Once the feature is rolled out to production, we can clean this up by removing the useObjectStorageClusters and useRegionsQuery, which will not be required at that time.
const { data: clusters } = useObjectStorageClusters();
const { data: regions } = useRegionsQuery();
const { data: profile } = useProfile();
Expand All @@ -70,13 +86,15 @@ export const BucketDetailsDrawer = React.memo(
Created: {formattedCreated}
</Typography>
) : null}

{cluster ? (
{isObjMultiClusterEnabled ? (
<Typography data-testid="cluster" variant="subtitle2">
{bucketRegion?.label}
</Typography>
) : cluster ? (
<Typography data-testid="cluster" variant="subtitle2">
{region?.label ?? cluster}
</Typography>
) : null}

{hostname ? (
<StyledLinkContainer>
<Link external to={`https://${hostname}`}>
Expand All @@ -85,38 +103,55 @@ export const BucketDetailsDrawer = React.memo(
<StyledCopyTooltip sx={{ marginLeft: 4 }} text={hostname} />
</StyledLinkContainer>
) : null}

{formattedCreated || cluster ? (
<Divider spacingBottom={16} spacingTop={16} />
) : null}

{typeof size === 'number' ? (
<Typography variant="subtitle2">
{readableBytes(size).formatted}
</Typography>
) : null}

{/* @TODO OBJ Multicluster: use region instead of cluster if isObjMultiClusterEnabled. */}
{typeof objectsNumber === 'number' ? (
<Link to={`/object-storage/buckets/${cluster}/${bucketLabel}`}>
<Link
to={`/object-storage/buckets/${
isObjMultiClusterEnabled && bucketRegion
? bucketRegion.id
: cluster
}/${bucketLabel}`}
>
{pluralize('object', 'objects', objectsNumber)}
</Link>
) : null}

{typeof size === 'number' || typeof objectsNumber === 'number' ? (
<Divider spacingBottom={16} spacingTop={16} />
) : null}

{/* @TODO OBJ Multicluster: use region instead of cluster if isObjMultiClusterEnabled
to getBucketAccess and updateBucketAccess. */}
{cluster && bucketLabel ? (
<AccessSelect
getAccess={() =>
getBucketAccess(
isObjMultiClusterEnabled && bucketRegion
? bucketRegion.id
: cluster,
bucketLabel
)
}
updateAccess={(acl: ACLType, cors_enabled: boolean) => {
// Don't send the ACL with the payload if it's "custom", since it's
// not valid (though it's a valid return type).
const payload =
acl === 'custom' ? { cors_enabled } : { acl, cors_enabled };

return updateBucketAccess(cluster, bucketLabel, payload);
return updateBucketAccess(
isObjMultiClusterEnabled && bucketRegion
? bucketRegion.id
: cluster,
bucketLabel,
payload
);
}}
getAccess={() => getBucketAccess(cluster, bucketLabel)}
name={bucketLabel}
variant="bucket"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ const RenderData: React.FC<RenderDataProps> = (props) => {
{data.map((bucket, index) => (
<BucketTableRow
{...bucket}
key={`${bucket.label}-${index}-${bucket.cluster}`}
key={`${bucket.label}-${index}-${bucket.region ?? bucket.cluster}`}
onDetails={() => onDetails(bucket)}
onRemove={() => onRemove(bucket)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import { DateTimeDisplay } from 'src/components/DateTimeDisplay';
import { Hidden } from 'src/components/Hidden';
import { TableCell } from 'src/components/TableCell';
import { Typography } from 'src/components/Typography';
import { useAccountManagement } from 'src/hooks/useAccountManagement';
import { useFlags } from 'src/hooks/useFlags';
import { useObjectStorageClusters } from 'src/queries/objectStorage';
import { useRegionsQuery } from 'src/queries/regions';
import { isFeatureEnabled } from 'src/utilities/accountCapabilities';
import { getRegionsByRegionId } from 'src/utilities/regions';
import { readableBytes } from 'src/utilities/unitConversions';

import { BucketActionMenu } from './BucketActionMenu';
Expand All @@ -34,14 +38,26 @@ export const BucketTableRow = (props: BucketTableRowProps) => {
objects,
onDetails,
onRemove,
region,
size,
} = props;

const { data: clusters } = useObjectStorageClusters();
const { data: regions } = useRegionsQuery();

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

const isObjMultiClusterEnabled = isFeatureEnabled(
'Object Storage Access Key Regions',
Boolean(flags.objMultiCluster),
account?.capabilities ?? []
);

const actualCluster = clusters?.find((c) => c.id === cluster);
const region = regions?.find((r) => r.id === actualCluster?.region);
const clusterRegion = regions?.find((r) => r.id === actualCluster?.region);

const regionsLookup = regions && getRegionsByRegionId(regions);

return (
<StyledBucketRow ariaLabel={label} data-qa-bucket-cell={label} key={label}>
Expand All @@ -51,7 +67,9 @@ export const BucketTableRow = (props: BucketTableRowProps) => {
<StyledBucketNameWrapper>
<Typography component="h3" data-qa-label variant="body1">
<StyledBucketLabelLink
to={`/object-storage/buckets/${cluster}/${label}`}
to={`/object-storage/buckets/${
isObjMultiClusterEnabled ? region : cluster
}/${label}`}
>
{label}{' '}
</StyledBucketLabelLink>
Expand All @@ -65,7 +83,9 @@ export const BucketTableRow = (props: BucketTableRowProps) => {
<Hidden smDown>
<StyledBucketRegionCell>
<Typography data-qa-region variant="body1">
{region?.label ?? cluster}
{isObjMultiClusterEnabled && regionsLookup && region
? regionsLookup[region].label
: clusterRegion?.label ?? cluster}
</Typography>
</StyledBucketRegionCell>
</Hidden>
Expand Down
Loading
Loading