Skip to content

Commit 6dbabd7

Browse files
authored
upcoming: [UIE-9327] - Add service URIs to Database summary tab (#13261)
## Description 📝 - Display general Service URI for all databases under Connection details in the summary tab - If applicable, display PgBouncer connection details Service URI for postgres clusters with at least one connection pool ## How to test 🧪 ### Prerequisites (How to setup test environment) - Ensure the Database PgBouncer feature flag is enabled and turn on the legacy MSW ### Verification steps (How to verify changes) - [ ] Go to a mysql Database cluster's summary page and confirm that a general mysql service URI displays under Connection Details. There should not be a PgBouncer section. - [ ] Go to a postgres Database cluster's summary page and confirm that a general postgres service URI displays under Connection Details. There should be a PgBouncer section
1 parent 173688a commit 6dbabd7

File tree

9 files changed

+342
-182
lines changed

9 files changed

+342
-182
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Add service URIs to Database summary tab ([#13261](https://github.com/linode/manager/pull/13261))

packages/manager/src/features/Databases/DatabaseDetail/ConnectionDetailsHostRows.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { Database } from '@linode/api-v4/lib/databases/types';
2020

2121
interface ConnectionDetailsHostRowsProps {
2222
database: Database;
23+
isSummaryTab?: boolean;
2324
}
2425

2526
type HostContentMode = 'default' | 'private' | 'public';
@@ -30,7 +31,7 @@ type HostContentMode = 'default' | 'private' | 'public';
3031
export const ConnectionDetailsHostRows = (
3132
props: ConnectionDetailsHostRowsProps
3233
) => {
33-
const { database } = props;
34+
const { database, isSummaryTab } = props;
3435
const { classes } = useStyles();
3536

3637
const sxTooltipIcon = {
@@ -136,21 +137,28 @@ export const ConnectionDetailsHostRows = (
136137

137138
return (
138139
<>
139-
<ConnectionDetailsRow label={hasVPC ? 'Private Host' : 'Host'}>
140+
<ConnectionDetailsRow
141+
isSummaryTab={isSummaryTab}
142+
label={hasVPC ? 'Private Host' : 'Host'}
143+
>
140144
{getHostContent(hasVPC ? 'private' : 'default')}
141145
</ConnectionDetailsRow>
142146
{hasPublicVPC && (
143-
<ConnectionDetailsRow label="Public Host">
147+
<ConnectionDetailsRow isSummaryTab={isSummaryTab} label="Public Host">
144148
{getHostContent('public')}
145149
</ConnectionDetailsRow>
146150
)}
147151
<ConnectionDetailsRow
152+
isSummaryTab={isSummaryTab}
148153
label={hasVPC ? 'Private Read-only Host' : readonlyHostLabel}
149154
>
150155
{getReadOnlyHostContent(hasVPC ? 'private' : 'default')}
151156
</ConnectionDetailsRow>
152157
{hasPublicVPC && (
153-
<ConnectionDetailsRow label="Public Read-only Host">
158+
<ConnectionDetailsRow
159+
isSummaryTab={isSummaryTab}
160+
label="Public Read-only Host"
161+
>
154162
{getReadOnlyHostContent('public')}
155163
</ConnectionDetailsRow>
156164
)}

packages/manager/src/features/Databases/DatabaseDetail/ConnectionDetailsRow.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@ import {
88

99
interface ConnectionDetailsRowProps {
1010
children: React.ReactNode;
11+
isSummaryTab?: boolean;
1112
label: string;
1213
}
1314

1415
export const ConnectionDetailsRow = (props: ConnectionDetailsRowProps) => {
15-
const { children, label } = props;
16+
const { children, label, isSummaryTab } = props;
1617
return (
1718
<>
1819
<Grid
1920
size={{
20-
md: 4,
21+
md: isSummaryTab ? 3 : 4,
2122
xs: 3,
2223
}}
2324
>
2425
<StyledLabelTypography>{label}</StyledLabelTypography>
2526
</Grid>
26-
<StyledValueGrid size={{ md: 8, xs: 9 }}>{children}</StyledValueGrid>
27+
<StyledValueGrid size={{ md: isSummaryTab ? 9 : 8, xs: 9 }}>
28+
{children}
29+
</StyledValueGrid>
2730
</>
2831
);
2932
};
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { getSSLFields } from '@linode/api-v4/lib/databases/databases';
2+
import { TooltipIcon } from '@linode/ui';
3+
import { downloadFile } from '@linode/utilities';
4+
import { styled } from '@mui/material/styles';
5+
import { Button } from 'akamai-cds-react-components';
6+
import { useSnackbar } from 'notistack';
7+
import * as React from 'react';
8+
9+
import DownloadIcon from 'src/assets/icons/lke-download.svg';
10+
import { getErrorStringOrDefault } from 'src/utilities/errorUtils';
11+
12+
import { sxTooltipIcon } from './DatabaseSummaryConnectionDetails';
13+
14+
import type { Database, SSLFields } from '@linode/api-v4';
15+
16+
interface Props {
17+
database: Database;
18+
}
19+
20+
export const DatabaseCaCert = (props: Props) => {
21+
const { database } = props;
22+
const { enqueueSnackbar } = useSnackbar();
23+
const [isCACertDownloading, setIsCACertDownloading] =
24+
React.useState<boolean>(false);
25+
26+
const handleDownloadCACertificate = () => {
27+
setIsCACertDownloading(true);
28+
getSSLFields(database.engine, database.id)
29+
.then((response: SSLFields) => {
30+
// Convert to utf-8 from base64
31+
try {
32+
const decodedFile = window.atob(response.ca_certificate);
33+
downloadFile(`${database.label}-ca-certificate.crt`, decodedFile);
34+
setIsCACertDownloading(false);
35+
} catch {
36+
enqueueSnackbar('Error parsing your CA Certificate file', {
37+
variant: 'error',
38+
});
39+
setIsCACertDownloading(false);
40+
return;
41+
}
42+
})
43+
.catch((errorResponse: any) => {
44+
const error = getErrorStringOrDefault(
45+
errorResponse,
46+
'Unable to download your CA Certificate'
47+
);
48+
setIsCACertDownloading(false);
49+
enqueueSnackbar(error, { variant: 'error' });
50+
});
51+
};
52+
53+
const disableDownloadCACertificateBtn = database.status === 'provisioning';
54+
55+
return (
56+
<>
57+
<StyledCaCertButton
58+
data-testid="download-ca-certificate"
59+
disabled={disableDownloadCACertificateBtn}
60+
onClick={handleDownloadCACertificate}
61+
processing={isCACertDownloading}
62+
variant="link"
63+
>
64+
<DownloadIcon />
65+
Download CA Certificate
66+
</StyledCaCertButton>
67+
{disableDownloadCACertificateBtn && (
68+
<span style={{ alignContent: 'center' }}>
69+
<TooltipIcon
70+
status="info"
71+
sxTooltipIcon={sxTooltipIcon}
72+
text="Your Database Cluster is currently provisioning."
73+
/>
74+
</span>
75+
)}
76+
</>
77+
);
78+
};
79+
80+
export const StyledCaCertButton = styled(Button, {
81+
label: 'StyledCaCertButton',
82+
})(({ theme }) => ({
83+
'&:hover': {
84+
backgroundColor: 'transparent',
85+
opacity: 0.7,
86+
},
87+
'&[disabled]': {
88+
'& g': {
89+
stroke: theme.tokens.color.Neutrals[30],
90+
},
91+
'&:hover': {
92+
backgroundColor: 'inherit',
93+
textDecoration: 'none',
94+
},
95+
// Override disabled background color defined for dark mode
96+
backgroundColor: 'transparent',
97+
color: theme.tokens.color.Neutrals[30],
98+
cursor: 'default',
99+
},
100+
color: theme.palette.primary.main,
101+
font: theme.font.bold,
102+
fontSize: '0.875rem',
103+
lineHeight: '1.125rem',
104+
minHeight: 'auto',
105+
minWidth: 'auto',
106+
padding: 0,
107+
}));

packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
1-
import { Paper } from '@linode/ui';
1+
import { useDatabaseConnectionPoolsQuery } from '@linode/queries';
2+
import { Paper, Typography } from '@linode/ui';
23
import Grid from '@mui/material/Grid';
4+
import { styled } from '@mui/material/styles';
35
import * as React from 'react';
46

57
import ClusterConfiguration from 'src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration';
68
import ConnectionDetails from 'src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails';
9+
import { useFlags } from 'src/hooks/useFlags';
710

811
import { useDatabaseDetailContext } from '../DatabaseDetailContext';
12+
import { ServiceURI } from '../ServiceURI';
13+
import { DatabaseCaCert } from './DatabaseCaCert';
914

1015
export const DatabaseSummary = () => {
1116
const { database } = useDatabaseDetailContext();
17+
const flags = useFlags();
18+
19+
const { data: connectionPools } = useDatabaseConnectionPoolsQuery(
20+
database.id,
21+
flags.databasePgBouncer,
22+
{}
23+
);
24+
25+
const showPgBouncerConnectionDetails =
26+
flags.databasePgBouncer &&
27+
database.engine === 'postgresql' &&
28+
connectionPools &&
29+
connectionPools.data.length > 0;
1230

1331
return (
1432
<Paper>
@@ -29,7 +47,34 @@ export const DatabaseSummary = () => {
2947
>
3048
<ConnectionDetails database={database} />
3149
</Grid>
50+
{showPgBouncerConnectionDetails && (
51+
<Grid
52+
size={{
53+
md: 12,
54+
sm: 12,
55+
}}
56+
>
57+
<Typography mb={2} variant="h3">
58+
PgBouncer Connection Details
59+
</Typography>
60+
<ServiceURI database={database} />
61+
</Grid>
62+
)}
3263
</Grid>
64+
{database.ssl_connection && (
65+
<StyledButtonCtn>
66+
<DatabaseCaCert database={database} />
67+
</StyledButtonCtn>
68+
)}
3369
</Paper>
3470
);
3571
};
72+
73+
export const StyledButtonCtn = styled('div', {
74+
label: 'StyledButtonCtn',
75+
})(({ theme }) => ({
76+
display: 'flex',
77+
justifyContent: 'flex-end',
78+
marginTop: '10px',
79+
padding: `${theme.spacingFunction(8)} 0`,
80+
}));

packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.style.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,6 @@ import { makeStyles } from 'tss-react/mui';
33
import type { Theme } from '@mui/material/styles';
44

55
export const useStyles = makeStyles()((theme: Theme) => ({
6-
actionBtnsCtn: {
7-
display: 'flex',
8-
justifyContent: 'flex-end',
9-
marginTop: '10px',
10-
padding: `${theme.spacing(1)} 0`,
11-
},
12-
caCertBtn: {
13-
'& svg': {
14-
marginRight: theme.spacing(),
15-
},
16-
'&:hover': {
17-
backgroundColor: 'transparent',
18-
opacity: 0.7,
19-
},
20-
'&[disabled]': {
21-
'& g': {
22-
stroke: theme.tokens.color.Neutrals[30],
23-
},
24-
'&:hover': {
25-
backgroundColor: 'inherit',
26-
textDecoration: 'none',
27-
},
28-
// Override disabled background color defined for dark mode
29-
backgroundColor: 'transparent',
30-
color: theme.tokens.color.Neutrals[30],
31-
cursor: 'default',
32-
},
33-
color: theme.palette.primary.main,
34-
font: theme.font.bold,
35-
fontSize: '0.875rem',
36-
lineHeight: '1.125rem',
37-
marginLeft: theme.spacing(),
38-
minHeight: 'auto',
39-
minWidth: 'auto',
40-
padding: 0,
41-
},
42-
tooltipIcon: {
43-
alignContent: 'center',
44-
},
456
connectionDetailsCtn: {
467
'& p': {
478
lineHeight: '1.5rem',

0 commit comments

Comments
 (0)