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

change: [UIE-8228] - DBaaS Resize GA: Enable Downsizing, update node presentation #11311

Merged
merged 5 commits into from
Dec 2, 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/manager": Changed
---

DBaaS Resize GA: Enable Downsizing (horizontal and vertical), enable 'Shared' tab, updated node presentation ([#11311](https://github.com/linode/manager/pull/11311))
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import type {
DatabasePriceObject,
Engine,
} from '@linode/api-v4/lib/databases/types';
import type { Theme } from '@mui/material';
import type {
PlanSelectionType,
PlanSelectionWithDatabaseType,
Expand All @@ -36,9 +35,6 @@ interface Props {
selectedPlan: PlanSelectionWithDatabaseType | undefined;
selectedTab: number;
}
const typographyBaseStyles = (theme: Theme) => ({
color: theme.palette.mode === 'dark' ? theme.color.grey6 : theme.color.grey1,
});

export const DatabaseNodeSelector = (props: Props) => {
const {
Expand Down Expand Up @@ -87,17 +83,10 @@ export const DatabaseNodeSelector = (props: Props) => {
/>
);

const isDisabled = (nodeSize: ClusterSize) => {
return currentClusterSize && nodeSize < currentClusterSize;
};

const options = [
{
label: (
<Typography
component="div"
sx={isDisabled(1) ? typographyBaseStyles : undefined}
>
<Typography component="div">
<span>1 Node {` `}</span>
{currentClusterSize === 1 && currentChip}
<br />
Expand All @@ -115,10 +104,7 @@ export const DatabaseNodeSelector = (props: Props) => {
if (hasDedicated && selectedTab === 0 && isDatabasesV2Enabled) {
options.push({
label: (
<Typography
component="div"
sx={isDisabled(2) ? typographyBaseStyles : undefined}
>
<Typography component="div">
<span>2 Nodes - High Availability</span>
{currentClusterSize === 2 && currentChip}
<br />
Expand All @@ -135,10 +121,7 @@ export const DatabaseNodeSelector = (props: Props) => {

options.push({
label: (
<Typography
component="div"
sx={isDisabled(3) ? typographyBaseStyles : undefined}
>
<Typography component="div">
<span>3 Nodes - High Availability (recommended)</span>
{currentClusterSize === 3 && currentChip}
<br />
Expand Down Expand Up @@ -186,9 +169,6 @@ export const DatabaseNodeSelector = (props: Props) => {
>
{nodeOptions.map((nodeOption) => (
<FormControlLabel
disabled={
currentClusterSize && nodeOption.value < currentClusterSize
}
control={<Radio />}
data-qa-radio={nodeOption.label}
data-testid={`database-node-${nodeOption.value}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,17 +270,6 @@ describe('database resize', () => {
expect(selectedNodeRadioButton).toBeChecked();
});

it('should disable visible lower node selections', async () => {
const { getByTestId } = renderWithTheme(
<DatabaseResize database={mockDatabase} />,
{ flags }
);
await waitForElementToBeRemoved(getByTestId(loadingTestId));
const selectedNodeRadioButton = getByTestId('database-node-1').children[0]
.children[0] as HTMLInputElement;
expect(selectedNodeRadioButton).toBeDisabled();
});

it('should set price, enable resize button, and update resize summary when a new number of nodes is selected', async () => {
const mockDatabase = databaseFactory.build({
cluster_size: 1,
Expand Down Expand Up @@ -395,52 +384,21 @@ describe('database resize', () => {
},
};

const { findByTestId } = renderWithTheme(
const { getByRole, getByTestId } = renderWithTheme(
<DatabaseResize database={mockDatabase} />,
{ flags }
);
expect(getByTestId(loadingTestId)).toBeInTheDocument();
await waitForElementToBeRemoved(getByTestId(loadingTestId));

expect(await findByTestId('database-nodes')).toBeDefined();
expect(await findByTestId('database-node-1')).toBeDefined();
expect(await findByTestId('database-node-2')).toBeDefined();
expect(await findByTestId('database-node-3')).toBeDefined();
});

it('should disable lower node selections', async () => {
const mockDatabase = databaseFactory.build({
cluster_size: 3,
platform: 'rdbms-default',
type: 'g6-dedicated-2',
});

const flags = {
dbaasV2: {
beta: false,
enabled: true,
},
};
const dedicatedTab = getByRole('tab', { name: 'Dedicated CPU' });

// Mock route history so the Plan Selection table displays prices without requiring a region in the DB resize flow.
const history = createMemoryHistory();
history.push(`databases/${database.engine}/${database.id}/resize`);
await userEvent.click(dedicatedTab);

const { getByTestId } = renderWithTheme(
<Router history={history}>
<DatabaseResize database={mockDatabase} />
</Router>,
{ flags }
);
expect(getByTestId(loadingTestId)).toBeInTheDocument();
await waitForElementToBeRemoved(getByTestId(loadingTestId));
expect(
getByTestId('database-node-1').children[0].children[0]
).toBeDisabled();
expect(
getByTestId('database-node-2').children[0].children[0]
).toBeDisabled();
expect(
getByTestId('database-node-3').children[0].children[0]
).toBeEnabled();
expect(getByTestId('database-nodes')).toBeDefined();
expect(getByTestId('database-node-1')).toBeDefined();
expect(getByTestId('database-node-2')).toBeDefined();
expect(getByTestId('database-node-3')).toBeDefined();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import { DatabaseSummarySection } from 'src/features/Databases/DatabaseCreate/Da
import { DatabaseResizeCurrentConfiguration } from 'src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration';
import { useIsDatabasesEnabled } from 'src/features/Databases/utilities';
import { typeLabelDetails } from 'src/features/Linodes/presentation';
import { useDatabaseMutation } from 'src/queries/databases/databases';
import { useDatabaseTypesQuery } from 'src/queries/databases/databases';
import { useDatabaseMutation } from 'src/queries/databases/databases';
import { formatStorageUnits } from 'src/utilities/formatStorageUnits';
import { convertMegabytesTo } from 'src/utilities/unitConversions';

import {
StyledGrid,
Expand Down Expand Up @@ -81,7 +82,7 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
const onResize = () => {
const payload: UpdateDatabasePayload = {};

if (clusterSize && clusterSize > database.cluster_size && isDatabasesV2GA) {
if (clusterSize && isDatabasesV2GA) {
payload.cluster_size = clusterSize;
}

Expand Down Expand Up @@ -110,8 +111,7 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
const selectedEngine = database.engine.split('/')[0] as Engine;

const summaryText = React.useMemo(() => {
const nodeSelected = clusterSize && clusterSize > database.cluster_size;

const nodeSelected = clusterSize && clusterSize !== database.cluster_size;
const isSamePlanSelected = selectedPlanId === database.type;
if (!dbTypes) {
return undefined;
Expand Down Expand Up @@ -173,13 +173,19 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
if (!dbTypes) {
return [];
}

return dbTypes.map((type: DatabaseType) => {
const { label } = type;
const formattedLabel = formatStorageUnits(label);
const nodePricing = type.engines[selectedEngine].find(
(cluster: DatabaseClusterSizeObject) =>
cluster.quantity === database.cluster_size

const nodePricing = type.engines[
selectedEngine
].find((cluster: DatabaseClusterSizeObject) =>
selectedTab === 1 && database.cluster_size === 2
? cluster.quantity === 3
: cluster.quantity === clusterSize
);

const price = nodePricing?.price ?? {
hourly: null,
monthly: null,
Expand All @@ -196,7 +202,7 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
subHeadings,
};
});
}, [database.cluster_size, dbTypes, selectedEngine]);
}, [database.cluster_size, dbTypes, selectedEngine, selectedTab]);

const currentPlan = displayTypes?.find((type) => type.id === database.type);

Expand All @@ -207,14 +213,23 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
currentPlan?.heading
);
setSelectedTab(initialTab);
}, [database.type, displayTypes]);
}, []);

const currentPlanDisk = currentPlan ? currentPlan.disk : 0;
const disabledPlans = displayTypes?.filter((type) =>
type.class === 'dedicated'
? type.disk < currentPlanDisk
: type.disk <= currentPlanDisk
);
const disabledPlans = !isNewDatabaseGA
? displayTypes?.filter((type) =>
type.class === 'dedicated'
? type.disk < currentPlanDisk
: type.disk <= currentPlanDisk
)
: displayTypes?.filter(
(type) =>
database?.used_disk_size_gb &&
database.used_disk_size_gb >
+convertMegabytesTo(type.disk, true)
.split(/(GB|MB|KB)/i)[0]
.trim()
);
const isDisabledSharedTab = database.cluster_size === 2;

const shouldSubmitBeDisabled = React.useMemo(() => {
Expand Down Expand Up @@ -249,7 +264,7 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
setSelectedPlanId(database.type);
setClusterSize(database.cluster_size);
} else {
setClusterSize(undefined);
setClusterSize(3);
setSelectedPlanId(undefined);
}
}
Expand All @@ -274,11 +289,13 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => {
</Paper>
<Paper sx={{ marginTop: 2 }}>
<StyledPlansPanel
disabledTabs={
!isNewDatabaseGA && isDisabledSharedTab ? ['shared'] : []
}
currentPlanHeading={currentPlan?.heading}
data-qa-select-plan
disabled={disabled}
disabledSmallerPlans={disabledPlans}
disabledTabs={isDisabledSharedTab ? ['shared'] : []}
handleTabChange={handleTabChange}
header="Choose a Plan"
onSelect={(selected: string) => setSelectedPlanId(selected)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ export const DatabaseResizeCurrentConfiguration = ({ database }: Props) => {

const configuration =
database.cluster_size === 1
? 'Primary'
: `Primary +${database.cluster_size - 1} replicas`;
? 'Primary (1 Node)'
: database.cluster_size > 2
? `Primary (+${database.cluster_size - 1} Nodes)`
: `Primary (+${database.cluster_size - 1} Node)`;

const sxTooltipIcon = {
marginLeft: 0.5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { DatabaseSummary } from './DatabaseSummary';
import type { Database } from '@linode/api-v4';

const CLUSTER_CONFIGURATION = 'Cluster Configuration';
const THREE_NODE = 'Primary +2 replicas';
const TWO_NODE = 'Primary +1 replicas';
const THREE_NODE = 'Primary (+2 Nodes)';
const TWO_NODE = 'Primary (+1 Node)';
const VERSION = 'Version';

const CONNECTION_DETAILS = 'Connection Details';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { Database, DatabaseStatus } from '@linode/api-v4/lib/databases';

const STATUS_VALUE = 'Active';
const PLAN_VALUE = 'New DBaaS - Dedicated 8 GB';
const NODES_VALUE = 'Primary +1 replicas';
const NODES_VALUE = 'Primary (+1 Node)';
const REGION_ID = 'us-east';
const REGION_LABEL = 'Newark, NJ';

Expand Down Expand Up @@ -150,7 +150,7 @@ describe('DatabaseSummaryClusterConfiguration', () => {
expect(queryAllByText('Nanode 1 GB')).toHaveLength(1);

expect(queryAllByText('Nodes')).toHaveLength(1);
expect(queryAllByText('Primary')).toHaveLength(1);
expect(queryAllByText('Primary (1 Node)')).toHaveLength(1);

expect(queryAllByText('CPUs')).toHaveLength(1);
expect(queryAllByText(1)).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ export const DatabaseSummaryClusterConfiguration = (props: Props) => {

const configuration =
database.cluster_size === 1
? 'Primary'
: `Primary +${database.cluster_size - 1} replicas`;
? 'Primary (1 Node)'
: database.cluster_size > 2
? `Primary (+${database.cluster_size - 1} Nodes)`
: `Primary (+${database.cluster_size - 1} Node)`;

const sxTooltipIcon = {
marginLeft: '4px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ export const DatabaseSummaryClusterConfigurationLegacy = (props: Props) => {

const configuration =
database.cluster_size === 1
? 'Primary'
: `Primary +${database.cluster_size - 1} replicas`;
? 'Primary (1 Node)'
: database.cluster_size > 2
? `Primary (+${database.cluster_size - 1} Nodes)`
: `Primary (+${database.cluster_size - 1} Node)`;

const sxTooltipIcon = {
marginLeft: '4px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const PlanSelection = (props: PlanSelectionProps) => {
)}/mo ($${price?.hourly ?? UNKNOWN_PRICE}/hr)`;

const rowIsDisabled =
isSamePlan ||
(!isDatabaseFlow && isSamePlan) ||
planIsTooSmall ||
planBelongsToDisabledClass ||
planIsDisabled512Gb ||
Expand Down Expand Up @@ -125,7 +125,7 @@ export const PlanSelection = (props: PlanSelectionProps) => {
onClick={() => (!rowIsDisabled ? onSelect(plan.id) : undefined)}
>
<StyledRadioCell>
{!isSamePlan && (
{(!isSamePlan || (isDatabaseFlow && isSamePlan)) && (
<FormControlLabel
aria-label={`${plan.heading} ${
rowIsDisabled ? `- ${disabledPlanReasonCopy}` : ''
Expand Down