Skip to content

Commit

Permalink
[Discover] Tentatively implement Create Database screen (gravitationa…
Browse files Browse the repository at this point in the history
…l#1372)

* Extract reusable label creating logic to own component
* Add new database endpoints (create and update)
  • Loading branch information
kimlisa authored Nov 21, 2022
1 parent 62f42e9 commit 290811a
Show file tree
Hide file tree
Showing 14 changed files with 585 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright 2022 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 { CreateDatabaseView } from './CreateDatabase';

import type { State } from './useCreateDatabase';

export default {
title: 'Teleport/Discover/Database/CreateDatabase',
};

export const Init = () => (
<MemoryRouter>
<CreateDatabaseView {...props} />
</MemoryRouter>
);

export const Processing = () => (
<MemoryRouter>
<CreateDatabaseView {...props} attempt={{ status: 'processing' }} />
</MemoryRouter>
);

export const Failed = () => (
<MemoryRouter>
<CreateDatabaseView
{...props}
attempt={{ status: 'failed', statusText: 'some error message' }}
/>
</MemoryRouter>
);

const props: State = {
attempt: { status: '' },
createDbAndQueryDb: () => null,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Copyright 2022 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, { useState } from 'react';
import { Text, Box } from 'design';
import { Danger } from 'design/Alert';
import Validation, { Validator } from 'shared/components/Validation';
import FieldInput from 'shared/components/FieldInput';
import { requiredField } from 'shared/components/Validation/rules';

import {
ActionButtons,
HeaderSubtitle,
Header,
LabelsCreater,
} from '../../Shared';

import { useCreateDatabase, State } from './useCreateDatabase';

import type { AgentStepProps } from '../../types';
import type { AgentLabel } from 'teleport/services/agents';

export function CreateDatabase(props: AgentStepProps) {
const state = useCreateDatabase(props);
return <CreateDatabaseView {...state} />;
}

export function CreateDatabaseView({ attempt, createDbAndQueryDb }: State) {
const [dbName, setDbName] = useState('');
const [dbUri, setDbUri] = useState('');
const [labels, setLabels] = useState<AgentLabel[]>([]);

// TODO (lisa or ryan): these depend on if user chose AWS options:
// const [awsAccountId, setAwsAccountId] = useState('')
// const [awsResourceId, setAwsResourceId] = useState('')

function handleOnProceed(validator: Validator) {
if (!validator.validate()) {
return;
}

// TODO (lisa or ryan): preserve "self hosted" or "aws"
// and protocol on first step, and use it here.
createDbAndQueryDb({
labels,
name: dbName,
uri: dbUri,
// TODO (lisa or ryan) hard coded for now as example.
protocol: 'postgres',
// TODO (lisa or ryan) add AWS fields
});
}

return (
<Validation>
{({ validator }) => (
<Box>
<Header>Register a Database</Header>
<HeaderSubtitle>Lorem ipsum dolores</HeaderSubtitle>
{attempt.status === 'failed' && (
<Danger children={attempt.statusText} />
)}
<Box width="500px" mb={2}>
<FieldInput
label="Database Name"
rule={requiredField('database name is required')}
autoFocus
value={dbName}
placeholder="Enter database name"
onChange={e => setDbName(e.target.value)}
/>
</Box>
<Box width="500px" mb={6}>
<FieldInput
label="Database Connection Endpoint"
rule={requiredField('database connection endpoint is required')}
autoFocus
value={dbUri}
placeholder="Enter database connection endpoint"
onChange={e => setDbUri(e.target.value)}
/>
</Box>
{/* TODO (lisa or ryan): add AWS input fields */}
<Box>
<Text bold>Labels (optional)</Text>
<LabelsCreater
labels={labels}
setLabels={setLabels}
isLabelOptional={true}
disableBtns={attempt.status === 'processing'}
/>
</Box>
<ActionButtons
onProceed={() => handleOnProceed(validator)}
// On failure, allow user to attempt again.
disableProceed={attempt.status === 'processing'}
/>
</Box>
)}
</Validation>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright 2022 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.
*/

export { CreateDatabase } from './CreateDatabase';
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright 2022 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 useAttempt from 'shared/hooks/useAttemptNext';

import useTeleport from 'teleport/useTeleport';

import type { AgentStepProps } from '../../types';
import type { CreateDatabaseRequest } from 'teleport/services/databases';

export function useCreateDatabase(props: AgentStepProps) {
const ctx = useTeleport();
const { attempt, setAttempt } = useAttempt('processing');

async function createDbAndQueryDb(db: CreateDatabaseRequest) {
setAttempt({ status: 'processing' });
try {
const clusterId = ctx.storeUser.getClusterId();
// Create the Database.
await ctx.databaseService.createDatabase(clusterId, db);

// Query for the created database by searching through database services.
// As discussed, if a service was already available, the new db should be
// picked up immediately.
let numSteps;
const request = {
search: db.name,
limit: 1,
};
const queryResult = await ctx.databaseService.fetchDatabases(
clusterId,
request
);

// If an agent was found, skip the next step that requires you
// to set up the db service, and set the database we queried to
// refer to it in later steps (this queried db will include current
// db users and db names).
const queriedDb = queryResult.agents[0];
if (queriedDb) {
numSteps = 2;
props.updateAgentMeta({
...props.agentMeta,
resourceName: queriedDb.name,
db: queriedDb,
});
}
props.nextStep(numSteps);
} catch (err) {
let message;
if (err instanceof Error) message = err.message;
else message = String(err);
setAttempt({ status: 'failed', statusText: message });
}
}

return {
attempt,
createDbAndQueryDb,
};
}

export type State = ReturnType<typeof useCreateDatabase>;
Loading

0 comments on commit 290811a

Please sign in to comment.