Skip to content
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
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-13355-fixed-1770132340383.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

DBaaS Backup / delete dialog bugs ([#13355](https://github.com/linode/manager/pull/13355))
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ import Grid from '@mui/material/Grid';
import { Button } from 'akamai-cds-react-components';
import { enqueueSnackbar } from 'notistack';
import React, { useEffect, useMemo, useState } from 'react';
import {
Controller,
get,
useFieldArray,
useForm,
useWatch,
} from 'react-hook-form';
import { Controller, get, useFieldArray, useForm } from 'react-hook-form';
import type { SubmitHandler } from 'react-hook-form';

import { Link } from 'src/components/Link';
Expand Down Expand Up @@ -101,8 +95,6 @@ export const DatabaseAdvancedConfigurationDrawer = (props: Props) => {
name: 'configs',
});

const configs = useWatch({ control, name: 'configs' });

useEffect(() => {
if (existingConfigurations.length > 0 || open) {
reset({ configs: existingConfigurations });
Expand Down Expand Up @@ -212,12 +204,12 @@ export const DatabaseAdvancedConfigurationDrawer = (props: Props) => {
<CircleProgress size="sm" />
</Stack>
)}
{!isLoading && configs.length === 0 && (
{!isLoading && fields.length === 0 && (
<Typography align="center">
No advanced configurations have been added.
</Typography>
)}
{configs.map((config, index) => (
{fields.map((config, index) => (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Squeezing a small bug fix from #13350, no need for changeset since it hasn't been released yet.

Add a config option, then delete it by clicking x, you should not see undefined

Image

<Controller
control={control}
key={config.label}
Expand Down Expand Up @@ -246,7 +238,7 @@ export const DatabaseAdvancedConfigurationDrawer = (props: Props) => {
<ActionsPanel
primaryButtonProps={{
disabled: !isDirty,
label: hasRestartCluster(configs, existingConfigurations),
label: hasRestartCluster(fields, existingConfigurations),
loading: isUpdating,
type: 'submit',
title: 'Save',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,12 @@ export const DatabaseBackups = () => {
buttonType="primary"
data-qa-settings-button="restore"
disabled={
versionOption === 'dateTime' &&
(!date || !time || !!errors.time)
Boolean(unableToRestoreCopy) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For UIE-9258, in addition to the fix for the Backups Restore button disabling behavior, QA had suggested a change to the message we display when the first backup isn't available.

From the comments under the ticket, it isn't clear when the first backup would be completed from the current message. We should address this as part of the work for this ticket as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UPDATE: After reviewing, this issue will be looked into separately!

(versionOption === 'dateTime' &&
(!date || !time || !!errors.time))
}
onClick={() => setIsRestoreDialogOpen(true)}
tooltipText={unableToRestoreCopy}
>
Restore
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ActionsPanel, Dialog, Notice, Typography } from '@linode/ui';
import { useNavigate } from '@tanstack/react-router';
import { useSnackbar } from 'notistack';
import * as React from 'react';
import { useState } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';

import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
Expand All @@ -24,7 +23,6 @@ export const DatabaseBackupsDialog = (props: Props) => {
const { database, onClose, open } = props;
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const [isRestoring, setIsRestoring] = useState(false);

const { control } = useFormContext<DatabaseBackupsValues>();
const [date, time, region] = useWatch({
Expand All @@ -34,19 +32,25 @@ export const DatabaseBackupsDialog = (props: Props) => {

const formattedDate = toFormattedDate(date, time);

const { error, mutateAsync: restore } = useRestoreFromBackupMutation(
database.engine,
{
fork: toDatabaseFork(database.id, date, time),
region,
// Assign same VPC when forking to the same region, otherwise set VPC to null
private_network:
database.region === region ? database.private_network : null,
}
);
const {
error,
mutateAsync: restore,
reset,
isPending,
} = useRestoreFromBackupMutation(database.engine, {
fork: toDatabaseFork(database.id, date, time),
region,
// Assign same VPC when forking to the same region, otherwise set VPC to null
private_network:
database.region === region ? database.private_network : null,
});

const _onClose = () => {
onClose();
reset();
};

const handleRestoreDatabase = () => {
setIsRestoring(true);
restore().then((database: Database) => {
navigate({
to: `/databases/$engine/$databaseId`,
Expand All @@ -58,7 +62,7 @@ export const DatabaseBackupsDialog = (props: Props) => {
enqueueSnackbar('Your database is being restored.', {
variant: 'success',
});
onClose();
_onClose();
});
};

Expand All @@ -67,11 +71,20 @@ export const DatabaseBackupsDialog = (props: Props) => {

return (
<Dialog
onClose={onClose}
onClose={_onClose}
open={open}
subtitle={formattedDate && `From ${formattedDate} (UTC)`}
title={`Restore ${database.label}`}
>
{error && (
<Notice
text={
getAPIErrorOrDefault(error, 'Unable to restore this backup.')[0]
.reason
}
variant="error"
/>
)}
{isClusterWithVPCAndForkingToDifferentRegion && ( // Show warning when forking a cluster with VPC to a different region
<Notice variant="warning">
The database cluster is currently assigned to a VPC. When you restore
Expand All @@ -91,29 +104,20 @@ export const DatabaseBackupsDialog = (props: Props) => {
primaryButtonProps={{
'data-testid': 'submit',
label: 'Restore',
loading: isRestoring,
loading: isPending,
onClick: handleRestoreDatabase,
}}
secondaryButtonProps={{
'data-testid': 'cancel',
label: 'Cancel',
onClick: onClose,
onClick: _onClose,
}}
sx={{
display: 'flex',
marginBottom: '0',
paddingBottom: '0',
}}
/>
{error ? (
<Notice
text={
getAPIErrorOrDefault(error, 'Unable to restore this backup.')[0]
.reason
}
variant="error"
/>
) : null}
</Dialog>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { useFlags } from 'src/hooks/useFlags';

import AccessControls from '../AccessControls';
import { useDatabaseDetailContext } from '../DatabaseDetailContext';
import DatabaseSettingsDeleteClusterDialog from './DatabaseSettingsDeleteClusterDialog';
import { DatabaseSettingsDeleteClusterDialog } from './DatabaseSettingsDeleteClusterDialog';
import { DatabaseSettingsMaintenance } from './DatabaseSettingsMaintenance';
import DatabaseSettingsMenuItem from './DatabaseSettingsMenuItem';
import DatabaseSettingsResetPasswordDialog from './DatabaseSettingsResetPasswordDialog';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,33 @@ interface Props {
open: boolean;
}

export const DatabaseSettingsDeleteClusterDialog: React.FC<Props> = (props) => {
export const DatabaseSettingsDeleteClusterDialog = (props: Props) => {
const { databaseEngine, databaseID, databaseLabel, onClose, open } = props;
const { enqueueSnackbar } = useSnackbar();
const { mutateAsync: deleteDatabase } = useDeleteDatabaseMutation(
databaseEngine,
databaseID
);
const defaultError = 'There was an error deleting this Database Cluster.';
const [error, setError] = React.useState('');
const [isLoading, setIsLoading] = React.useState(false);
const {
mutateAsync: deleteDatabase,
error,
isPending,
reset,
} = useDeleteDatabaseMutation(databaseEngine, databaseID);
const navigate = useNavigate();

const _onClose = () => {
onClose();
reset();
};

const onDeleteCluster = () => {
setIsLoading(true);
deleteDatabase()
.then(() => {
setIsLoading(false);
enqueueSnackbar('Database Cluster deleted successfully.', {
variant: 'success',
});
onClose();
navigate({
to: '/databases',
});
})
.catch((e) => {
setIsLoading(false);
setError(getAPIErrorOrDefault(e, defaultError)[0].reason);
deleteDatabase().then(() => {
enqueueSnackbar('Database Cluster deleted successfully.', {
variant: 'success',
});
_onClose();
reset();
navigate({
to: '/databases',
});
});
};

return (
Expand All @@ -59,13 +57,23 @@ export const DatabaseSettingsDeleteClusterDialog: React.FC<Props> = (props) => {
}}
expand
label={'Cluster Name'}
loading={isLoading}
loading={isPending}
onClick={onDeleteCluster}
onClose={onClose}
onClose={_onClose}
open={open}
title={`Delete Database Cluster ${databaseLabel}`}
>
{error ? <Notice text={error} variant="error" /> : null}
{error ? (
<Notice
text={
getAPIErrorOrDefault(
error,
'There was an error deleting this Database Cluster.'
)[0].reason
}
variant="error"
/>
) : null}
<Notice variant="warning">
<Typography style={{ fontSize: '0.875rem' }}>
<strong>Warning:</strong> Deleting your entire database will delete
Expand All @@ -76,5 +84,3 @@ export const DatabaseSettingsDeleteClusterDialog: React.FC<Props> = (props) => {
</TypeToConfirmDialog>
);
};

export default DatabaseSettingsDeleteClusterDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import React from 'react';

import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty';
import DatabaseSettingsDeleteClusterDialog from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog';
import { DatabaseSettingsDeleteClusterDialog } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog';
import DatabaseSettingsResetPasswordDialog from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsResetPasswordDialog';
import { ManageAccessControlDrawer } from 'src/features/Databases/DatabaseDetail/ManageAccessControlDrawer';
import DatabaseLogo from 'src/features/Databases/DatabaseLanding/DatabaseLogo';
Expand Down