Skip to content

Commit a3d6dd4

Browse files
authored
fix: General Service URI text in Database Summary page (#13519)
- Add `<username>:` to the general Service URI in the Database Summary page - Display `ssl-mode=REQUIRED` for mysql clusters - Display placeholder text while hosts are still being retrieved
1 parent 988fc5f commit a3d6dd4

2 files changed

Lines changed: 67 additions & 13 deletions

File tree

packages/manager/src/features/Databases/DatabaseDetail/ServiceURI.test.tsx

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { renderWithTheme } from 'src/utilities/testHelpers';
88

99
import { ServiceURI } from './ServiceURI';
1010

11-
import type { DatabaseStatus } from '@linode/api-v4';
11+
import type { DatabaseStatus, Engine } from '@linode/api-v4';
1212

1313
const mockCredentials = {
1414
password: 'password123',
@@ -170,7 +170,7 @@ describe('ServiceURI', () => {
170170
refetch: vi.fn(),
171171
});
172172

173-
it('should render the service URI component and copy icon', async () => {
173+
it('should render the PgBouncer service URI component and copy icon', async () => {
174174
const { container } = renderWithTheme(
175175
<ServiceURI database={databaseWithNoVPC} />
176176
);
@@ -221,6 +221,24 @@ describe('ServiceURI', () => {
221221
);
222222
});
223223

224+
it('should render general service URI with ssl-mode=REQUIRED if isGeneralServiceURI is true and the engine is mysql', () => {
225+
const mockDb = {
226+
...databaseWithNoVPC,
227+
engine: 'mysql' as Engine,
228+
};
229+
renderWithTheme(<ServiceURI database={mockDb} isGeneralServiceURI />);
230+
231+
const revealPasswordBtn = screen.getByRole('button', {
232+
name: '{click to reveal password}',
233+
});
234+
const serviceURIText = screen.getByTestId('service-uri').textContent;
235+
236+
expect(revealPasswordBtn).toBeInTheDocument();
237+
expect(serviceURIText).toBe(
238+
`mysql://{click to reveal password}@${DEFAULT_PRIMARY}:3306/defaultdb?ssl-mode=REQUIRED`
239+
);
240+
});
241+
224242
it('should reveal general service URI password after clicking reveal button', async () => {
225243
renderWithTheme(
226244
<ServiceURI database={databaseWithNoVPC} isGeneralServiceURI />
@@ -234,7 +252,7 @@ describe('ServiceURI', () => {
234252
const serviceURIText = screen.getByTestId('service-uri').textContent;
235253
expect(revealPasswordBtn).not.toBeInTheDocument();
236254
expect(serviceURIText).toBe(
237-
`postgres://password123@${DEFAULT_PRIMARY}:3306/defaultdb?sslmode=require`
255+
`postgres://lnroot:password123@${DEFAULT_PRIMARY}:3306/defaultdb?sslmode=require`
238256
);
239257
});
240258

@@ -318,6 +336,22 @@ describe('ServiceURI', () => {
318336
);
319337
});
320338

339+
it('should render private service URI placeholder text if there is a VPC with public access, isGeneralServiceURI and showPrivateVPC is true, but hosts are not yet available', () => {
340+
const mockDb = {
341+
...databaseWithPublicVPC,
342+
hosts: null,
343+
};
344+
345+
renderWithTheme(
346+
<ServiceURI database={mockDb} isGeneralServiceURI showPrivateVPC />
347+
);
348+
349+
const serviceURIText = screen.getByTestId('service-uri').textContent;
350+
expect(serviceURIText).toBe(
351+
'Your Service URI will appear here once it is available.'
352+
);
353+
});
354+
321355
it('should disable the reveal password and copy icon if the Database is suspended', async () => {
322356
const mockDatabase = {
323357
...databaseWithNoVPC,

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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useDatabaseCredentialsQuery } from '@linode/queries';
2-
import { Button, TooltipIcon } from '@linode/ui';
2+
import { Button, TooltipIcon, Typography } from '@linode/ui';
33
import { Grid, styled } from '@mui/material';
44
import copy from 'copy-to-clipboard';
55
import { enqueueSnackbar } from 'notistack';
@@ -34,6 +34,8 @@ export const ServiceURI = (props: ServiceURIProps) => {
3434
const [isCopying, setIsCopying] = useState(false);
3535
const engine =
3636
database.engine === 'postgresql' ? 'postgres' : database.engine;
37+
const generalSslmode =
38+
engine === 'mysql' ? 'ssl-mode=REQUIRED' : 'sslmode=require';
3739

3840
const {
3941
data: credentials,
@@ -87,17 +89,11 @@ export const ServiceURI = (props: ServiceURIProps) => {
8789
isGeneralServiceURI?: boolean
8890
) => {
8991
if (isGeneralServiceURI) {
90-
return `${engine}://${credentials?.password}@${primaryHost?.address}:${primaryHost?.port}/defaultdb?sslmode=require`;
92+
return `${engine}://${credentials?.username}:${credentials?.password}@${primaryHost?.address}:${primaryHost?.port}/defaultdb?${generalSslmode}`;
9193
}
9294
return `postgres://${credentials?.username}:${credentials?.password}@${primaryConnectionPoolHost?.address}:${primaryConnectionPoolHost?.port}/{connection pool label}?sslmode=require`;
9395
};
9496

95-
const getCredentials = (isGeneralServiceURI: boolean) => {
96-
return !isGeneralServiceURI
97-
? `${credentials?.username}:${credentials?.password}`
98-
: credentials?.password;
99-
};
100-
10197
// hide loading state if the user clicks on the copy icon
10298
const showBtnLoading =
10399
!hidePassword && !isCopying && (credentialsLoading || credentialsFetching);
@@ -141,9 +137,33 @@ export const ServiceURI = (props: ServiceURIProps) => {
141137
);
142138
}
143139

144-
return getCredentials(isGeneralServiceURI);
140+
return `${credentials?.username}:${credentials?.password}`;
145141
};
146142

143+
if (
144+
(isGeneralServiceURI && !primaryHost) ||
145+
(engine === 'postgres' && !primaryConnectionPoolHost)
146+
) {
147+
return (
148+
<Grid display="contents">
149+
<StyledValueGrid
150+
data-testid="service-uri"
151+
size="grow"
152+
sx={{
153+
overflowX: 'auto',
154+
overflowY: 'hidden',
155+
p: '0',
156+
}}
157+
whiteSpace="pre"
158+
>
159+
<Typography fontStyle="italic">
160+
Your Service URI will appear here once it is available.
161+
</Typography>
162+
</StyledValueGrid>
163+
</Grid>
164+
);
165+
}
166+
147167
return (
148168
<Grid display="contents">
149169
<StyledValueGrid
@@ -161,7 +181,7 @@ export const ServiceURI = (props: ServiceURIProps) => {
161181
{isGeneralServiceURI ? (
162182
<>
163183
@{primaryHost?.address}:
164-
{`${primaryHost?.port}/defaultdb?sslmode=require`}
184+
{`${primaryHost?.port}/defaultdb?${generalSslmode}`}
165185
</>
166186
) : (
167187
<>

0 commit comments

Comments
 (0)