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

feat: [UIE-8090] - DBaaS Settings and Landing Page #11152

Merged
merged 12 commits into from
Nov 5, 2024

Conversation

smans-akamai
Copy link
Contributor

@smans-akamai smans-akamai commented Oct 23, 2024

Description 📝

Adding Suspend and Resume functionality to Landing Page and database details Settings Views for GA

Changes 🔄

  • Suspend Cluster button can now appear in the DatabaseSettings that displays and Suspend Cluster dialog
  • Suspend and Resume actions added to Database Landing table
  • In the Database Landing table, clusters with suspending, suspended, and resuming status will not have detail links

Target release date 🗓️

11/12/2024

Preview 📷

Before After After 2
Before-Db-Settings After-Db-Settings-no-dialog After-Db-Settings-Dialog
database-landing-before db-landing-after

How to test 🧪

Prerequisites

  • Managed Databases account capability
  • LD flag: beta: false, enabled: true

Verification steps

  • Toggle LD will show Suspend and resume functionality
  • Check DatabaseSettings to see new Suspend Button and Dialog behavior
  • Check DatabaseLanding to see new suspend and resume actions
  • Check DatabaseLanding to confirm that Suspend can only be used on active databases
  • Check DatabaseLanding to confirm that the resume action can only be used on Suspending and Suspended database clusters
  • Check DatabaseLanding to confirm that clusters with suspending, suspended, and resuming status do not have detail links under Cluster Label column

As an Author I have considered 🤔

Check all that apply

  • [ x] 👀 Doing a self review
  • ❔ Our contribution guidelines
  • 🤏 Splitting feature into small PRs
  • ➕ Adding a changeset
  • 🧪 Providing/Improving test coverage
  • 🔐 Removing all sensitive information from the code and PR description
  • 🚩 Using a feature flag to protect the release
  • 👣 Providing comprehensive reproduction steps
  • 📑 Providing or updating our documentation
  • 🕛 Scheduling a pair reviewing session
  • 📱 Providing mobile support
  • ♿ Providing accessibility support

Commit message and pull request title format standards

Note: Remove this section before opening the pull request
Make sure your PR title and commit message on squash and merge are as shown below

<commit type>: [JIRA-ticket-number] - <description>

Commit Types:

  • feat: New feature for the user (not a part of the code, or ci, ...).
  • fix: Bugfix for the user (not a fix to build something, ...).
  • change: Modifying an existing visual UI instance. Such as a component or a feature.
  • refactor: Restructuring existing code without changing its external behavior or visual UI. Typically to improve readability, maintainability, and performance.
  • test: New tests or changes to existing tests. Does not change the production code.
  • upcoming: A new feature that is in progress, not visible to users yet, and usually behind a feature flag.

Example: feat: [M3-1234] - Allow user to view their login history


@smans-akamai smans-akamai requested a review from a team as a code owner October 23, 2024 19:52
@smans-akamai smans-akamai requested review from carrillo-erik and cpathipa and removed request for a team October 23, 2024 19:52
@bnussman-akamai bnussman-akamai added the DBaaS Relates to Database as a Service label Oct 23, 2024
@smans-akamai smans-akamai force-pushed the UIE-8090-dbaas-settings-landing branch 2 times, most recently from 2ccd150 to 5be73c9 Compare October 23, 2024 19:59
Copy link

github-actions bot commented Oct 23, 2024

Coverage Report:
Base Coverage: 87.33%
Current Coverage: 87.33%

@smans-akamai smans-akamai force-pushed the UIE-8090-dbaas-settings-landing branch from 5be73c9 to 4ac31c4 Compare October 23, 2024 22:15
Copy link
Member

Choose a reason for hiding this comment

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

I'd recommend breaking up this PR up so that this banner into its own PR. The banner logic seems somewhat complex and the implementation may be flawed (see comment below)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai After discussion, we've removed the banner and associated logic from this pull request and will separate that into it's own ticket. The design and logic will be reworked as part of that new ticket.

);
};

export default DatabaseSettingsSuspendClusterDialog;
Copy link
Member

Choose a reason for hiding this comment

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

Please avoid using default exports. You can just use the export const DatabaseSettingsSuspendClusterDialog and remove this export

);

return (
<Dialog
Copy link
Member

Choose a reason for hiding this comment

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

You may want to consider using the Confirmation Dialog component rather than a pure Dialog. It may help you to fix the style/spacing issues

Screenshot 2024-10-23 at 6 24 42 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai Can the confirmation dialog support a checkbox that enables/disables the Suspend Cluster button?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai I've updated the suspend dialog to use the confirmation dialog.

suspend-updated-to-confirmation

Comment on lines 34 to 35
const [error, setError] = React.useState('');
const [isLoading, setIsLoading] = React.useState(false);
Copy link
Member

Choose a reason for hiding this comment

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

No need to define your own error and loading state. The useSuspendDatabaseMutation hook returns a isPending and error state you can use

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've updated this to leverage isPending and error from useSuspendDatabaseMutation

export const DatabaseSettingsSuspendClusterDialog: React.FC<SuspendDialogProps> = (
props
) => {
Copy link
Member

Choose a reason for hiding this comment

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

We prefer to not use React.FC in our repo. You can type your props this way:

Suggested change
export const DatabaseSettingsSuspendClusterDialog: React.FC<SuspendDialogProps> = (
props
) => {
export const DatabaseSettingsSuspendClusterDialog = (
props: SuspendDialogProps
) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I've made this update.

return notifications;
}
// Loop through new databases to find suspended databases and check if a notification should be displayed.
newDatabases.data.forEach((database) => {
Copy link
Member

Choose a reason for hiding this comment

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

We may need to reconsider/rearchitect this banner feature. Our tables are API-paginated, meaning that newDatabases only contains the first page of databases. This logic may not produce the desired result a suspended database is not on the current API paginated page.

I do not reccomend fetching all databases to solve this because it kind of defeats the purpose of API based pagunation.

There may be a clever way we can use api-v4 x-filtering to query for only suspended databases, which might be a more scaleable solution.

I'm happy to do a proof of concept or talk through alternatives to this, just let me know!

const isNewDatabase = isV2Enabled && !!newDatabases?.data.length;
const showSuspend = isDatabasesV2GA && !!newDatabases?.data.length;

const getSuspendNotification = (
Copy link
Member

Choose a reason for hiding this comment

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

In general, I recommend extracting stuff like this into utility functions outside of this component. It makes it easier to unit test and makes the code more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai Thanks for the recommendation. We'll follow moving this into utilities once/if it's used in a second place.

Comment on lines 252 to 266
{showSuspend
? suspendNotifications.map((notification) => (
<DismissibleBanner
preferenceKey={`${notification.id}-suspend-notice`}
variant="warning"
key={`${notification.id}-key`}
>
<Box>
<Typography>
{`${notification.label} has been suspended for ${SUSPEND_WARNING_DAYS} days. If it is not resumed to be operational it will be automatically deleted ${notification.expirationCopy}.`}
</Typography>
</Box>
</DismissibleBanner>
))
: null}
Copy link
Member

Choose a reason for hiding this comment

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

This is going to look very bad if a user has lots of suspended databases (especially on mobile). I think we should consider a more scaleable UI. Cloud Manager already has enough banners.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai Thanks, we'll note this concern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai After discussion, we've removed the banner and associated logic from this pull request and will separate that into it's own ticket. The design and logic will be reworked as part of that new ticket.

setIsSuspendClusterDialogOpen(true);
};

const handleResume = (database: DatabaseInstance) => {
Copy link
Member

Choose a reason for hiding this comment

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

Because this resume logic isn't really used in this component we might be able to move it down into the DatabaseActionMenu component

queryClient.invalidateQueries({
queryKey: databaseQueries.databases.queryKey,
});
queryClient.removeQueries({
Copy link
Member

Choose a reason for hiding this comment

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

Is the removeQueries call here and in useResumeDatabaseMutation intentional? I believe that will evict that database from the cache.

invalidateQueries might be the desired function here. invalidateQueries will make Cloud Manager refetch the database as needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After reviewing your comment, I believe that invalidateQueries makes more sense for this use case as the database hasn't been removed, only updated. So we'll want to trigger a refetch on stale data rather than removing the cache entirely.

I'll update this for both useResumeDatabaseMutation and useSuspendDatabaseMutation.

@cpathipa cpathipa removed the request for review from carrillo-erik October 24, 2024 14:16
@smans-akamai smans-akamai force-pushed the UIE-8090-dbaas-settings-landing branch 3 times, most recently from 9129ac6 to cd8bab9 Compare October 25, 2024 16:38
@smans-akamai smans-akamai force-pushed the UIE-8090-dbaas-settings-landing branch from cd8bab9 to 7394385 Compare October 25, 2024 18:09
Copy link
Member

@bnussman-akamai bnussman-akamai left a comment

Choose a reason for hiding this comment

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

Looking great. Thanks for responding to that initial feedback and breaking up this PR, we appreciate it!

Just a few more changes and I think this will be good to go

Comment on lines 80 to 92
<ConfirmationDialog
maxWidth="sm"
actions={actions}
onClose={onClose}
open={open}
title={`Suspend ${databaseLabel} cluster?`}
>
{isError && (
<Notice
text={getAPIErrorOrDefault(error, defaultError)[0].reason}
variant="error"
/>
)}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<ConfirmationDialog
maxWidth="sm"
actions={actions}
onClose={onClose}
open={open}
title={`Suspend ${databaseLabel} cluster?`}
>
{isError && (
<Notice
text={getAPIErrorOrDefault(error, defaultError)[0].reason}
variant="error"
/>
)}
<ConfirmationDialog
error={
error ? getAPIErrorOrDefault(error, defaultError)[0].reason : undefined
}
actions={actions}
maxWidth="sm"
onClose={onClose}
open={open}
title={`Suspend ${databaseLabel} cluster?`}
>

Our ConfirmationDialog component has an error prop you can use to show the error state. It looks a bit different than the notice, but it allows us to maintain consistancy across Cloud Manager.

We also have an upcoming effort to improve this error state (Ticket M3-8792) so it will look very similar to what you implemented soon

<b>{suspendClusterCopy}</b>
</Typography>
</Notice>
<div>
Copy link
Member

Choose a reason for hiding this comment

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

This <div> might be unnecessary

const closeButton = getByTestId('CloseIcon');
expect(closeButton).toBeVisible();

fireEvent.click(closeButton);
Copy link
Member

Choose a reason for hiding this comment

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

We should use await userEvent rather than fireEvent whenever possible. It is recommended for testing interactions by React Testing library (docs)

variant: 'success',
});
})
.catch((e: any) => {
Copy link
Member

Choose a reason for hiding this comment

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

Let's use APIError[] here

Suggested change
.catch((e: any) => {
.catch((e: APIError[]) => {

Copy link
Contributor

@cpathipa cpathipa left a comment

Choose a reason for hiding this comment

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

Functionality LGTM! left few comments to lift conditional rendering for modals and nit-picky things to improve readability.

You can resume the clusters work within 180 days from its suspension.
After that time, the cluster will be deleted permanently.`;

const actions = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Use capital naming conventions for JSX elements used within a component (Contributing Guidelines)

Copy link
Contributor

Choose a reason for hiding this comment

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

I get the point of this comment, but if you search the code for const actions = you'll see there are a dozen like this and none with capitalization. For now, it seems like consistency would be preferred, but I am new to this whole thing, so please correct me if I am wrong.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are indeed transitioning to this new approach to maintain a cleaner and consistent codebase.

setHasConfirmed(false);
};

const suspendClusterCopy = `A suspended cluster stops working immediately and you won't be billed for it.
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be moved to constants file or out side of the component.

Copy link
Contributor

Choose a reason for hiding this comment

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

It could be but there are two different "suspendClusterCopy" constants in the code and they are different. I'm just a visitor here while Sam is on vacation :) but I prefer the way Sam has done it, being close to the one place it's used.

reset,
} = useSuspendDatabaseMutation(databaseEngine, databaseId);

const defaultError = 'There was an error suspending this Database Cluster.';
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be moved to constants file or out side of the component.

Copy link
Contributor

Choose a reason for hiding this comment

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

See comment above but in this case it's an even more generic name and is being used once in this area. I toyed with the idea of removing it altogether and just inlining the text as is done throughout this codebase, it seems, but in the end decided to leave it alone.

Copy link
Member

@bnussman-akamai bnussman-akamai left a comment

Choose a reason for hiding this comment

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

Looks good to me!

There is a conflict that needs to be addressed

Comment on lines +90 to +92
<Typography style={{ fontSize: '0.875rem' }}>
<b>{suspendClusterCopy}</b>
</Typography>
Copy link
Member

Choose a reason for hiding this comment

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

We might just want to pass

Suggested change
<Typography style={{ fontSize: '0.875rem' }}>
<b>{suspendClusterCopy}</b>
</Typography>
{suspendClusterCopy}

and let the Notice handle the text size and bolding

import { useResumeDatabaseMutation } from 'src/queries/databases/databases';
import { enqueueSnackbar } from 'notistack';
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
import { APIError } from '@linode/api-v4/src/types';
Copy link
Member

Choose a reason for hiding this comment

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

Our import should not contain src. Might cause problems in the future

Suggested change
import { APIError } from '@linode/api-v4/src/types';
import { APIError } from '@linode/api-v4';

Copy link
Contributor

Choose a reason for hiding this comment

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

This was removed because the import was removed. (It was being used to because someone had added a type defn to the catch's error, but doing so was causing "TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified." so I changed APIError to any and this import was no longer needed.) Good to know going forward though to look for this problem.

rodonnel-akamai and others added 2 commits November 1, 2024 14:48
…nding

UIE-8090: Add suspend and resume to Landing Page and details Settings…
Copy link
Contributor

@cpathipa cpathipa left a comment

Choose a reason for hiding this comment

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

Thank you for addressing the feedback, LGTM! Please update this branch with the latest from develop.

@cpathipa cpathipa added the Approved Multiple approvals and ready to merge! label Nov 4, 2024
@cpathipa
Copy link
Contributor

cpathipa commented Nov 5, 2024

@smans-akamai @mpolotsk-akamai @rodonnel-akamai Looks like there are some type errors in the DatabaseSettings file.
image

@cpathipa
Copy link
Contributor

cpathipa commented Nov 5, 2024

Also, there is a failed unit test in DatabaseSetting
image

@linode-gh-bot
Copy link
Collaborator

Cloud Manager UI test results

🎉 445 passing tests on test run #13 ↗︎

❌ Failing✅ Passing↪️ Skipped🕐 Duration
0 Failing445 Passing2 Skipped90m 8s

@cpathipa
Copy link
Contributor

cpathipa commented Nov 5, 2024

Confirming failed tests passed and type errors are fixed.

@cpathipa cpathipa merged commit f8d33b5 into linode:develop Nov 5, 2024
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved Multiple approvals and ready to merge! DBaaS Relates to Database as a Service
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants