mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
WebDiscover: Check for RDS length before setting a limit for listing DBs (#27194)
* Fix bug: Check for fetched rds results before determining limit for fetching db servers * Address crs
This commit is contained in:
parent
8442a4d74a
commit
8b994fcec0
|
@ -15,11 +15,8 @@
|
|||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Box, ButtonPrimary, Text, Flex, ButtonSecondary } from 'design';
|
||||
import FieldSelect from 'shared/components/FieldSelect';
|
||||
import { Option } from 'shared/components/Select';
|
||||
import { requiredField } from 'shared/components/Validation/rules';
|
||||
import Validation, { Validator } from 'shared/components/Validation';
|
||||
import { Box, Text, Flex, ButtonSecondary, LabelInput } from 'design';
|
||||
import Select, { Option } from 'shared/components/Select';
|
||||
import { Refresh as RefreshIcon } from 'design/Icon';
|
||||
|
||||
import { awsRegionMap, Regions } from 'teleport/services/integrations';
|
||||
|
@ -27,83 +24,59 @@ import { awsRegionMap, Regions } from 'teleport/services/integrations';
|
|||
export function AwsRegionSelector({
|
||||
onFetch,
|
||||
onRefresh,
|
||||
disableFetch,
|
||||
disableSelector,
|
||||
clear,
|
||||
}: {
|
||||
onFetch(region: Regions): void;
|
||||
onRefresh(): void;
|
||||
disableFetch: boolean;
|
||||
disableSelector: boolean;
|
||||
clear(): void;
|
||||
}) {
|
||||
const [selectedRegion, setSelectedRegion] = useState<RegionOption>();
|
||||
|
||||
function handleFetch(validator: Validator) {
|
||||
if (!validator.validate()) {
|
||||
return;
|
||||
}
|
||||
onFetch(selectedRegion.value);
|
||||
}
|
||||
|
||||
function handleRegionSelect(option: RegionOption) {
|
||||
clear();
|
||||
setSelectedRegion(option);
|
||||
onFetch(option.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<Validation>
|
||||
{({ validator }) => (
|
||||
<Box>
|
||||
<Text mt={4}>
|
||||
Select the AWS Region you would like to see databases for:
|
||||
</Text>
|
||||
<Flex alignItems="center" gap={3} mt={2} mb={3}>
|
||||
<Box width="320px">
|
||||
<FieldSelect
|
||||
label="AWS Region"
|
||||
rule={requiredField('Region is required')}
|
||||
placeholder="Select a Region"
|
||||
isSearchable
|
||||
isSimpleValue
|
||||
value={selectedRegion}
|
||||
onChange={handleRegionSelect}
|
||||
options={options}
|
||||
isDisabled={disableSelector}
|
||||
/>
|
||||
</Box>
|
||||
<Flex alignItems="center">
|
||||
<ButtonPrimary
|
||||
disabled={disableFetch || !selectedRegion}
|
||||
onClick={() => handleFetch(validator)}
|
||||
width="160px"
|
||||
height="40px"
|
||||
mt={1}
|
||||
>
|
||||
Fetch Databases
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary
|
||||
onClick={onRefresh}
|
||||
ml={3}
|
||||
mt={1}
|
||||
title="Refresh database table"
|
||||
height="40px"
|
||||
width="30px"
|
||||
css={`
|
||||
&:disabled {
|
||||
opacity: 0.35;
|
||||
pointer-events: none;
|
||||
}
|
||||
`}
|
||||
disabled={disableSelector || !disableFetch}
|
||||
>
|
||||
<RefreshIcon fontSize={3} />
|
||||
</ButtonSecondary>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box>
|
||||
<Text mt={4}>
|
||||
Select the AWS Region you would like to see databases for:
|
||||
</Text>
|
||||
<Flex alignItems="center" gap={3} mt={2} mb={3}>
|
||||
<Box width="320px" mb={4}>
|
||||
<LabelInput htmlFor={'select'}>AWS Region</LabelInput>
|
||||
<Select
|
||||
inputId="select"
|
||||
isSearchable
|
||||
value={selectedRegion}
|
||||
onChange={handleRegionSelect}
|
||||
options={options}
|
||||
placeholder="Select a region"
|
||||
autoFocus
|
||||
isDisabled={disableSelector}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Validation>
|
||||
<ButtonSecondary
|
||||
onClick={onRefresh}
|
||||
mt={1}
|
||||
title="Refresh database table"
|
||||
height="40px"
|
||||
width="30px"
|
||||
css={`
|
||||
&:disabled {
|
||||
opacity: 0.35;
|
||||
pointer-events: none;
|
||||
}
|
||||
`}
|
||||
disabled={disableSelector || !selectedRegion}
|
||||
>
|
||||
<RefreshIcon fontSize={3} />
|
||||
</ButtonSecondary>
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ export default {
|
|||
export const AwsRegionsSelectorDisabled = () => (
|
||||
<AwsRegionSelector
|
||||
onFetch={() => null}
|
||||
disableFetch={true}
|
||||
onRefresh={() => null}
|
||||
disableSelector={true}
|
||||
clear={() => null}
|
||||
|
@ -37,7 +36,6 @@ export const AwsRegionsSelectorDisabled = () => (
|
|||
export const AwsRegionsSelectorEnabled = () => (
|
||||
<AwsRegionSelector
|
||||
onFetch={() => null}
|
||||
disableFetch={false}
|
||||
onRefresh={() => null}
|
||||
disableSelector={false}
|
||||
clear={() => null}
|
||||
|
@ -47,7 +45,6 @@ export const AwsRegionsSelectorEnabled = () => (
|
|||
export const AwsRegionsSelectorRefreshEnabled = () => (
|
||||
<AwsRegionSelector
|
||||
onFetch={() => null}
|
||||
disableFetch={true}
|
||||
onRefresh={() => null}
|
||||
disableSelector={false}
|
||||
clear={() => null}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* Copyright 2023 Gravitational, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import { render, screen, fireEvent } from 'design/utils/testing';
|
||||
|
||||
import { ContextProvider } from 'teleport';
|
||||
import {
|
||||
AwsRdsDatabase,
|
||||
integrationService,
|
||||
} from 'teleport/services/integrations';
|
||||
import { createTeleportContext } from 'teleport/mocks/contexts';
|
||||
import cfg from 'teleport/config';
|
||||
import TeleportContext from 'teleport/teleportContext';
|
||||
import {
|
||||
DiscoverContextState,
|
||||
DiscoverProvider,
|
||||
} from 'teleport/Discover/useDiscover';
|
||||
import {
|
||||
DatabaseEngine,
|
||||
DatabaseLocation,
|
||||
} from 'teleport/Discover/SelectResource';
|
||||
import { FeaturesContextProvider } from 'teleport/FeaturesContext';
|
||||
|
||||
import { EnrollRdsDatabase } from './EnrollRdsDatabase';
|
||||
|
||||
describe('test EnrollRdsDatabase.tsx', () => {
|
||||
const ctx = createTeleportContext();
|
||||
const discoverCtx: DiscoverContextState = {
|
||||
agentMeta: {} as any,
|
||||
currentStep: 0,
|
||||
nextStep: jest.fn(x => x),
|
||||
prevStep: () => null,
|
||||
onSelectResource: () => null,
|
||||
resourceSpec: {
|
||||
dbMeta: {
|
||||
location: DatabaseLocation.Aws,
|
||||
engine: DatabaseEngine.AuroraMysql,
|
||||
},
|
||||
} as any,
|
||||
viewConfig: null,
|
||||
indexedViews: [],
|
||||
setResourceSpec: () => null,
|
||||
updateAgentMeta: jest.fn(x => x),
|
||||
emitErrorEvent: () => null,
|
||||
emitEvent: () => null,
|
||||
eventState: null,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest
|
||||
.spyOn(ctx.databaseService, 'fetchDatabases')
|
||||
.mockResolvedValue({ agents: [] });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('without rds database result, does not attempt to fetch db servers', async () => {
|
||||
renderRdsDatabase(ctx, discoverCtx);
|
||||
jest
|
||||
.spyOn(integrationService, 'fetchAwsRdsDatabases')
|
||||
.mockResolvedValue({ databases: [] });
|
||||
|
||||
// select a region from selector.
|
||||
const selectEl = screen.getByLabelText(/aws region/i);
|
||||
fireEvent.focus(selectEl);
|
||||
fireEvent.keyDown(selectEl, { key: 'ArrowDown', keyCode: 40 });
|
||||
fireEvent.click(screen.getByText('us-east-2'));
|
||||
|
||||
// No results are rendered.
|
||||
await screen.findByText(/no result/i);
|
||||
|
||||
expect(integrationService.fetchAwsRdsDatabases).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.databaseService.fetchDatabases).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('with rds database result, makes a fetch request for db servers', async () => {
|
||||
renderRdsDatabase(ctx, discoverCtx);
|
||||
jest.spyOn(integrationService, 'fetchAwsRdsDatabases').mockResolvedValue({
|
||||
databases: mockAwsDbs,
|
||||
});
|
||||
|
||||
// select a region from selector.
|
||||
const selectEl = screen.getByLabelText(/aws region/i);
|
||||
fireEvent.focus(selectEl);
|
||||
fireEvent.keyDown(selectEl, { key: 'ArrowDown', keyCode: 40 });
|
||||
fireEvent.click(screen.getByText('us-east-2'));
|
||||
|
||||
// Rds results renders result.
|
||||
await screen.findByText(/rds-1/i);
|
||||
|
||||
expect(integrationService.fetchAwsRdsDatabases).toHaveBeenCalledTimes(1);
|
||||
expect(ctx.databaseService.fetchDatabases).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
function renderRdsDatabase(
|
||||
ctx: TeleportContext,
|
||||
discoverCtx: DiscoverContextState
|
||||
) {
|
||||
return render(
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
{ pathname: cfg.routes.discover, state: { entity: 'database' } },
|
||||
]}
|
||||
>
|
||||
<ContextProvider ctx={ctx}>
|
||||
<FeaturesContextProvider value={[]}>
|
||||
<DiscoverProvider mockCtx={discoverCtx}>
|
||||
<EnrollRdsDatabase />
|
||||
</DiscoverProvider>
|
||||
</FeaturesContextProvider>
|
||||
</ContextProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
const mockAwsDbs: AwsRdsDatabase[] = [
|
||||
{
|
||||
engine: 'postgres',
|
||||
name: 'rds-1',
|
||||
uri: 'endpoint-1',
|
||||
status: 'available',
|
||||
labels: [{ name: 'env', value: 'prod' }],
|
||||
accountId: 'account-id-1',
|
||||
resourceId: 'resource-id-1',
|
||||
},
|
||||
];
|
|
@ -114,6 +114,13 @@ export function EnrollRdsDatabase() {
|
|||
}
|
||||
);
|
||||
|
||||
// Abort if there were no rds dbs for the selected region.
|
||||
if (fetchedRdsDbs.length <= 0) {
|
||||
setFetchDbAttempt({ status: 'success' });
|
||||
setTableData({ ...data, fetchStatus: 'disabled' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if fetched rds databases have a database
|
||||
// server for it, to prevent user from enrolling
|
||||
// the same db and getting an error from it.
|
||||
|
@ -206,9 +213,6 @@ export function EnrollRdsDatabase() {
|
|||
onRefresh={refreshDatabaseList}
|
||||
clear={clear}
|
||||
disableSelector={fetchDbAttempt.status === 'processing'}
|
||||
disableFetch={
|
||||
fetchDbAttempt.status === 'processing' || tableData.items.length > 0
|
||||
}
|
||||
/>
|
||||
<DatabaseList
|
||||
items={tableData.items}
|
||||
|
|
Loading…
Reference in a new issue