mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
console: refactor ConnectMssqlWidget
component + add interaction tests
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8151 GitOrigin-RevId: c8f91f3cc608c8d9b720aa98d423a5f5c85d3bc9
This commit is contained in:
parent
16dac48f0c
commit
e2e8543400
@ -2,6 +2,8 @@ import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|||||||
import { ConnectMssqlWidget } from './ConnectMssqlWidget';
|
import { ConnectMssqlWidget } from './ConnectMssqlWidget';
|
||||||
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
|
import { ReactQueryDecorator } from '../../../../storybook/decorators/react-query';
|
||||||
import { handlers } from '../../mocks/handlers.mock';
|
import { handlers } from '../../mocks/handlers.mock';
|
||||||
|
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: ConnectMssqlWidget,
|
component: ConnectMssqlWidget,
|
||||||
@ -15,16 +17,143 @@ export const CreateConnection: ComponentStory<
|
|||||||
typeof ConnectMssqlWidget
|
typeof ConnectMssqlWidget
|
||||||
> = () => {
|
> = () => {
|
||||||
return (
|
return (
|
||||||
<div className="max-w-3xl">
|
<div className="flex justify-center">
|
||||||
<ConnectMssqlWidget />
|
<div className="w-1/2">
|
||||||
|
<ConnectMssqlWidget />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EditConnection: ComponentStory<typeof ConnectMssqlWidget> = () => {
|
export const MSSQLCreateConnection: ComponentStory<
|
||||||
|
typeof ConnectMssqlWidget
|
||||||
|
> = () => {
|
||||||
return (
|
return (
|
||||||
<div className="max-w-3xl">
|
<div className="flex justify-center">
|
||||||
<ConnectMssqlWidget dataSourceName="mssql1" />
|
<div className="w-1/2">
|
||||||
|
<ConnectMssqlWidget />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MSSQLCreateConnection.storyName = '🧪 MSSQL Interaction test (add database)';
|
||||||
|
|
||||||
|
MSSQLCreateConnection.play = async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
// verify if the right title is displayed. It should contain the word `postgres`.
|
||||||
|
expect(await canvas.findByText('Connect MSSQL Database')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// verify if all the fields are present (in oss mode)
|
||||||
|
|
||||||
|
expect(await canvas.findByLabelText('Database name')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// There should be exactly 3 supported database connection options
|
||||||
|
const radioOptions = await canvas.findAllByLabelText('Connect Database via');
|
||||||
|
expect(radioOptions.length).toBe(2);
|
||||||
|
|
||||||
|
const databaseUrlOption = await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.connectionString.connectionType-databaseUrl'
|
||||||
|
);
|
||||||
|
expect(databaseUrlOption).toBeInTheDocument();
|
||||||
|
expect(databaseUrlOption).toBeChecked();
|
||||||
|
|
||||||
|
// Expect the first option to have the following input fields
|
||||||
|
expect(
|
||||||
|
await canvas.findByPlaceholderText(
|
||||||
|
'Driver={ODBC Driver 18 for SQL Server};Server=serveraddress;Database=dbname;Uid=username;Pwd=password'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
// click on the environment variable option and verify if the correct fields are shown
|
||||||
|
const environmentVariableOption = await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.connectionString.connectionType-envVar'
|
||||||
|
);
|
||||||
|
userEvent.click(environmentVariableOption);
|
||||||
|
expect(
|
||||||
|
await canvas.findByPlaceholderText('HASURA_GRAPHQL_DB_URL_FROM_ENV')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Find and click on advanced settings
|
||||||
|
userEvent.click(await canvas.findByText('Advanced Settings'));
|
||||||
|
expect(await canvas.findByText('Total Max Connections')).toBeInTheDocument();
|
||||||
|
expect(await canvas.findByText('Idle Timeout')).toBeInTheDocument();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MSSQLEditConnection: ComponentStory<
|
||||||
|
typeof ConnectMssqlWidget
|
||||||
|
> = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="w-1/2">
|
||||||
|
<ConnectMssqlWidget dataSourceName="mssql1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
MSSQLEditConnection.storyName = '🧪 MSSQL Edit Databaase Interaction test';
|
||||||
|
|
||||||
|
MSSQLEditConnection.play = async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
// verify if the right title is displayed. It should contain the word `postgres`.
|
||||||
|
expect(await canvas.findByText('Edit MSSQL Connection')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// verify if all the fields are present (in oss mode)
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
async () => {
|
||||||
|
expect(await canvas.findByLabelText('Database name')).toHaveValue(
|
||||||
|
'mssql1'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ timeout: 5000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const radioOptions = await canvas.findAllByLabelText('Connect Database via');
|
||||||
|
expect(radioOptions.length).toBe(2);
|
||||||
|
const databaseUrlOption = await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.connectionString.connectionType-databaseUrl'
|
||||||
|
);
|
||||||
|
expect(databaseUrlOption).toBeChecked();
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.connectionString.url'
|
||||||
|
)
|
||||||
|
).toHaveValue(
|
||||||
|
'DRIVER={ODBC Driver 17 for SQL Server};SERVER=host.docker.internal;DATABASE=bikes;Uid=SA;Pwd=reallyStrongPwd123'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find and click on advanced settings
|
||||||
|
userEvent.click(await canvas.findByText('Advanced Settings'));
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.poolSettings.totalMaxConnections'
|
||||||
|
)
|
||||||
|
).toHaveValue(50);
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'configuration.connectionInfo.poolSettings.idleTimeout'
|
||||||
|
)
|
||||||
|
).toHaveValue(180);
|
||||||
|
|
||||||
|
// find and click on graphql customization settings
|
||||||
|
userEvent.click(await canvas.findByText('GraphQL Customization'));
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId('customization.rootFields.namespace')
|
||||||
|
).toHaveValue('some_field_name');
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId('customization.rootFields.prefix')
|
||||||
|
).toHaveValue('some_field_name_prefix');
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId('customization.rootFields.suffix')
|
||||||
|
).toHaveValue('some_field_name_suffix');
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId('customization.typeNames.prefix')
|
||||||
|
).toHaveValue('some_type_name_prefix');
|
||||||
|
expect(
|
||||||
|
await canvas.findByTestId('customization.typeNames.suffix')
|
||||||
|
).toHaveValue('some_type_name_suffix');
|
||||||
|
};
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import { InputField, useConsoleForm } from '../../../../new-components/Form';
|
import { InputField, useConsoleForm } from '../../../../new-components/Form';
|
||||||
import { Tabs } from '../../../../new-components/Tabs';
|
|
||||||
import { Button } from '../../../../new-components/Button';
|
import { Button } from '../../../../new-components/Button';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { GraphQLCustomization } from '../GraphQLCustomization/GraphQLCustomization';
|
import { GraphQLCustomization } from '../GraphQLCustomization/GraphQLCustomization';
|
||||||
import { Configuration } from './parts/Configuration';
|
|
||||||
import { getDefaultValues, MssqlConnectionSchema, schema } from './schema';
|
import { getDefaultValues, MssqlConnectionSchema, schema } from './schema';
|
||||||
import { ReadReplicas } from './parts/ReadReplicas';
|
import { ReadReplicas } from './parts/ReadReplicas';
|
||||||
import { get } from 'lodash';
|
|
||||||
import { FaExclamationTriangle } from 'react-icons/fa';
|
|
||||||
import { useManageDatabaseConnection } from '../../hooks/useManageDatabaseConnection';
|
import { useManageDatabaseConnection } from '../../hooks/useManageDatabaseConnection';
|
||||||
import { hasuraToast } from '../../../../new-components/Toasts';
|
import { hasuraToast } from '../../../../new-components/Toasts';
|
||||||
import { useMetadata } from '../../../hasura-metadata-api';
|
import { useMetadata } from '../../../hasura-metadata-api';
|
||||||
import { generateMssqlRequestPayload } from './utils/generateRequests';
|
import { generateMssqlRequestPayload } from './utils/generateRequests';
|
||||||
import { isProConsole } from '../../../../utils';
|
import { ConnectionString } from './parts/ConnectionString';
|
||||||
|
import { areReadReplicasEnabled } from '../ConnectPostgresWidget/utils/helpers';
|
||||||
|
import { Collapsible } from '../../../../new-components/Collapsible';
|
||||||
|
import { PoolSettings } from './parts/PoolSettings';
|
||||||
|
|
||||||
interface ConnectMssqlWidgetProps {
|
interface ConnectMssqlWidgetProps {
|
||||||
dataSourceName?: string;
|
dataSourceName?: string;
|
||||||
@ -59,10 +58,9 @@ export const ConnectMssqlWidget = (props: ConnectMssqlWidgetProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [tab, setTab] = useState('connection_details');
|
|
||||||
const {
|
const {
|
||||||
Form,
|
Form,
|
||||||
methods: { formState, watch, reset },
|
methods: { reset },
|
||||||
} = useConsoleForm({
|
} = useConsoleForm({
|
||||||
schema,
|
schema,
|
||||||
});
|
});
|
||||||
@ -79,68 +77,53 @@ export const ConnectMssqlWidget = (props: ConnectMssqlWidgetProps) => {
|
|||||||
}
|
}
|
||||||
}, [metadataSource, reset]);
|
}, [metadataSource, reset]);
|
||||||
|
|
||||||
const readReplicas = watch('configuration.readReplicas');
|
|
||||||
|
|
||||||
const connectionDetailsTabErrors = [
|
|
||||||
get(formState.errors, 'name'),
|
|
||||||
get(formState.errors, 'configuration.connectionInfo'),
|
|
||||||
get(formState.errors, 'configuration.extensionSchema'),
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
const readReplicasError = [
|
|
||||||
get(formState.errors, 'configuration.readReplicas'),
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
const proConsoleTabs = isProConsole(window.__env)
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
value: 'read_replicas',
|
|
||||||
label: `Read Replicas ${
|
|
||||||
readReplicas?.length ? `(${readReplicas.length})` : ''
|
|
||||||
}`,
|
|
||||||
icon: readReplicasError.length ? (
|
|
||||||
<FaExclamationTriangle className="text-red-800" />
|
|
||||||
) : undefined,
|
|
||||||
content: <ReadReplicas name="configuration.readReplicas" />,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xl text-gray-600 font-semibold">
|
<div className="text-xl text-gray-600 font-semibold">
|
||||||
{isEditMode ? 'Edit MSSQL Connection' : 'Connect New MSSQL Database'}
|
{isEditMode ? 'Edit MSSQL Connection' : 'Connect MSSQL Database'}
|
||||||
</div>
|
</div>
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<Tabs
|
<InputField
|
||||||
value={tab}
|
name="name"
|
||||||
onValueChange={value => setTab(value)}
|
label="Database name"
|
||||||
items={[
|
placeholder="Database name"
|
||||||
{
|
|
||||||
value: 'connection_details',
|
|
||||||
label: 'Connection Details',
|
|
||||||
icon: connectionDetailsTabErrors.length ? (
|
|
||||||
<FaExclamationTriangle className="text-red-800" />
|
|
||||||
) : undefined,
|
|
||||||
content: (
|
|
||||||
<div className="mt-sm">
|
|
||||||
<InputField
|
|
||||||
name="name"
|
|
||||||
label="Database display name"
|
|
||||||
placeholder="Database name"
|
|
||||||
/>
|
|
||||||
<Configuration name="configuration" />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
...proConsoleTabs,
|
|
||||||
{
|
|
||||||
value: 'customization',
|
|
||||||
label: 'GraphQL Customization',
|
|
||||||
content: <GraphQLCustomization name="customization" />,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
|
<ConnectionString name="configuration.connectionInfo.connectionString" />
|
||||||
|
|
||||||
|
<div className="mt-sm">
|
||||||
|
<Collapsible
|
||||||
|
triggerChildren={
|
||||||
|
<div className="font-semibold text-muted">Advanced Settings</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PoolSettings name="configuration.connectionInfo.poolSettings" />
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{areReadReplicasEnabled() && (
|
||||||
|
<div className="mt-sm">
|
||||||
|
<Collapsible
|
||||||
|
triggerChildren={
|
||||||
|
<div className="font-semibold text-muted">Read Replicas</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ReadReplicas name="configuration.readReplicas" />
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-sm">
|
||||||
|
<Collapsible
|
||||||
|
triggerChildren={
|
||||||
|
<div className="font-semibold text-muted">
|
||||||
|
GraphQL Customization
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<GraphQLCustomization name="customization" />
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
import { SimpleForm } from '../../../../../new-components/Form';
|
|
||||||
import { Button } from '../../../../../new-components/Button';
|
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { Configuration } from './Configuration';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
component: Configuration,
|
|
||||||
} as ComponentMeta<typeof Configuration>;
|
|
||||||
|
|
||||||
export const Primary: ComponentStory<typeof Configuration> = () => (
|
|
||||||
<SimpleForm
|
|
||||||
onSubmit={data => console.log(data)}
|
|
||||||
schema={z.any()}
|
|
||||||
options={{
|
|
||||||
defaultValues: {
|
|
||||||
details: {
|
|
||||||
databaseUrl: {
|
|
||||||
connectionType: 'databaseUrl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Configuration name="connectionInfo" />
|
|
||||||
<Button type="submit" className="my-2">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</SimpleForm>
|
|
||||||
);
|
|
@ -1,9 +0,0 @@
|
|||||||
import { ConnectionInfo } from './ConnectionInfo';
|
|
||||||
|
|
||||||
export const Configuration = ({ name }: { name: string }) => {
|
|
||||||
return (
|
|
||||||
<div className="my-2">
|
|
||||||
<ConnectionInfo name={`${name}.connectionInfo`} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,31 +0,0 @@
|
|||||||
import { SimpleForm } from '../../../../../new-components/Form';
|
|
||||||
import { Button } from '../../../../../new-components/Button';
|
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { ConnectionInfo } from './ConnectionInfo';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
component: ConnectionInfo,
|
|
||||||
} as ComponentMeta<typeof ConnectionInfo>;
|
|
||||||
|
|
||||||
export const Primary: ComponentStory<typeof ConnectionInfo> = () => (
|
|
||||||
<SimpleForm
|
|
||||||
onSubmit={data => console.log(data)}
|
|
||||||
schema={z.any()}
|
|
||||||
options={{
|
|
||||||
defaultValues: {
|
|
||||||
details: {
|
|
||||||
databaseUrl: {
|
|
||||||
connectionType: 'databaseUrl',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ConnectionInfo name="connectionInfo" />
|
|
||||||
<Button type="submit" className="my-2">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</SimpleForm>
|
|
||||||
);
|
|
@ -2,7 +2,7 @@ import { InputField, Radio } from '../../../../../new-components/Form';
|
|||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
import { ConnectionInfoSchema } from '../schema';
|
import { ConnectionInfoSchema } from '../schema';
|
||||||
|
|
||||||
export const ConnectionInfo = ({ name }: { name: string }) => {
|
export const ConnectionString = ({ name }: { name: string }) => {
|
||||||
const options = [
|
const options = [
|
||||||
{ value: 'databaseUrl', label: 'Database URL' },
|
{ value: 'databaseUrl', label: 'Database URL' },
|
||||||
{ value: 'envVar', label: 'Enviromnent variable' },
|
{ value: 'envVar', label: 'Enviromnent variable' },
|
||||||
@ -10,13 +10,13 @@ export const ConnectionInfo = ({ name }: { name: string }) => {
|
|||||||
|
|
||||||
const { watch } = useFormContext<Record<string, ConnectionInfoSchema>>();
|
const { watch } = useFormContext<Record<string, ConnectionInfoSchema>>();
|
||||||
|
|
||||||
const connectionType = watch(`${name}.connectionString.connectionType`);
|
const connectionType = watch(`${name}.connectionType`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white border border-hasGray-300 rounded-md shadow-sm overflow-hidden p-4">
|
<div className="bg-white border border-hasGray-300 rounded-md shadow-sm overflow-hidden p-4">
|
||||||
<div className="bg-white py-1.5 font-semibold">
|
<div className="bg-white py-1.5 font-semibold">
|
||||||
<Radio
|
<Radio
|
||||||
name={`${name}.connectionString.connectionType`}
|
name={`${name}.connectionType`}
|
||||||
label="Connect Database via"
|
label="Connect Database via"
|
||||||
options={options}
|
options={options}
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
@ -26,13 +26,13 @@ export const ConnectionInfo = ({ name }: { name: string }) => {
|
|||||||
|
|
||||||
{connectionType === 'databaseUrl' ? (
|
{connectionType === 'databaseUrl' ? (
|
||||||
<InputField
|
<InputField
|
||||||
name={`${name}.connectionString.url`}
|
name={`${name}.url`}
|
||||||
label="Database URL"
|
label="Database URL"
|
||||||
placeholder="Driver={ODBC Driver 18 for SQL Server};Server=serveraddress;Database=dbname;Uid=username;Pwd=password"
|
placeholder="Driver={ODBC Driver 18 for SQL Server};Server=serveraddress;Database=dbname;Uid=username;Pwd=password"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<InputField
|
<InputField
|
||||||
name={`${name}.connectionString.envVar`}
|
name={`${name}.envVar`}
|
||||||
label="Environment variable"
|
label="Environment variable"
|
||||||
placeholder="HASURA_GRAPHQL_DB_URL_FROM_ENV"
|
placeholder="HASURA_GRAPHQL_DB_URL_FROM_ENV"
|
||||||
/>
|
/>
|
@ -1,24 +0,0 @@
|
|||||||
import { SimpleForm } from '../../../../../new-components/Form';
|
|
||||||
import { Button } from '../../../../../new-components/Button';
|
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
||||||
import { z } from 'zod';
|
|
||||||
|
|
||||||
import { ReadReplicas } from './ReadReplicas';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
component: ReadReplicas,
|
|
||||||
} as ComponentMeta<typeof ReadReplicas>;
|
|
||||||
|
|
||||||
export const Primary: ComponentStory<typeof ReadReplicas> = () => (
|
|
||||||
<SimpleForm
|
|
||||||
onSubmit={data => console.log(data)}
|
|
||||||
schema={z.any()}
|
|
||||||
options={{}}
|
|
||||||
>
|
|
||||||
<ReadReplicas name="rr" />
|
|
||||||
<br />
|
|
||||||
<Button type="submit" className="my-2">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</SimpleForm>
|
|
||||||
);
|
|
@ -1,11 +1,94 @@
|
|||||||
import { useFieldArray, useFormContext } from 'react-hook-form';
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
import { Button } from '../../../../../new-components/Button';
|
import { Button } from '../../../../../new-components/Button';
|
||||||
import { CardedTable } from '../../../../../new-components/CardedTable';
|
import { CardedTable } from '../../../../../new-components/CardedTable';
|
||||||
import { ConnectionInfo } from './ConnectionInfo';
|
import { ConnectionString } from './ConnectionString';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ConnectionInfoSchema } from '../schema';
|
import { ConnectionInfoSchema } from '../schema';
|
||||||
import { FaPlus, FaTrash } from 'react-icons/fa';
|
import { FaEdit, FaPlus, FaTrash } from 'react-icons/fa';
|
||||||
import { IndicatorCard } from '../../../../../new-components/IndicatorCard';
|
import { IndicatorCard } from '../../../../../new-components/IndicatorCard';
|
||||||
|
import { Dialog } from '../../../../../new-components/Dialog';
|
||||||
|
import { Collapsible } from '../../../../../new-components/Collapsible';
|
||||||
|
import { PoolSettings } from './PoolSettings';
|
||||||
|
|
||||||
|
// export const ReadReplicas = ({ name }: { name: string }) => {
|
||||||
|
// const { fields, append } = useFieldArray<
|
||||||
|
// Record<string, ConnectionInfoSchema[]>
|
||||||
|
// >({
|
||||||
|
// name,
|
||||||
|
// });
|
||||||
|
// const { watch, setValue } =
|
||||||
|
// useFormContext<Record<string, ConnectionInfoSchema[]>>();
|
||||||
|
|
||||||
|
// const [mode, setMode] = useState<'idle' | 'add'>('idle');
|
||||||
|
// const readReplicas = watch(name);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="my-2">
|
||||||
|
// {!fields?.length ? (
|
||||||
|
// <IndicatorCard status="info">No read replicas added.</IndicatorCard>
|
||||||
|
// ) : (
|
||||||
|
// <CardedTable
|
||||||
|
// columns={['No', 'Read Replica', null]}
|
||||||
|
// data={(fields ?? []).map((x, i) => [
|
||||||
|
// i + 1,
|
||||||
|
// <div>
|
||||||
|
// {x.connectionString.connectionType === 'databaseUrl'
|
||||||
|
// ? x.connectionString.url
|
||||||
|
// : x.connectionString.envVar}
|
||||||
|
// </div>,
|
||||||
|
// <Button
|
||||||
|
// size="sm"
|
||||||
|
// icon={<FaTrash />}
|
||||||
|
// mode="destructive"
|
||||||
|
// onClick={() => {
|
||||||
|
// setValue(
|
||||||
|
// name,
|
||||||
|
// readReplicas.filter((_, index) => index !== i)
|
||||||
|
// );
|
||||||
|
// }}
|
||||||
|
// />,
|
||||||
|
// ])}
|
||||||
|
// showActionCell
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
|
||||||
|
// {mode === 'idle' && (
|
||||||
|
// <Button
|
||||||
|
// type="button"
|
||||||
|
// onClick={() => {
|
||||||
|
// setMode('add');
|
||||||
|
// append({
|
||||||
|
// connectionString: { connectionType: 'databaseUrl', url: '' },
|
||||||
|
// });
|
||||||
|
// }}
|
||||||
|
// mode="primary"
|
||||||
|
// icon={<FaPlus />}
|
||||||
|
// >
|
||||||
|
// Add New Read Replica
|
||||||
|
// </Button>
|
||||||
|
// )}
|
||||||
|
|
||||||
|
// {mode === 'add' && (
|
||||||
|
// <div>
|
||||||
|
// <ConnectionInfo name={`${name}.${fields?.length - 1}`} />
|
||||||
|
// <Button
|
||||||
|
// onClick={() => {
|
||||||
|
// setMode('idle');
|
||||||
|
// setValue(
|
||||||
|
// `${name}.${fields?.length - 1}`,
|
||||||
|
// fields[fields?.length - 1]
|
||||||
|
// );
|
||||||
|
// }}
|
||||||
|
// mode="primary"
|
||||||
|
// className="my-2"
|
||||||
|
// >
|
||||||
|
// Add Read Replica
|
||||||
|
// </Button>
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
export const ReadReplicas = ({ name }: { name: string }) => {
|
export const ReadReplicas = ({ name }: { name: string }) => {
|
||||||
const { fields, append } = useFieldArray<
|
const { fields, append } = useFieldArray<
|
||||||
@ -16,9 +99,11 @@ export const ReadReplicas = ({ name }: { name: string }) => {
|
|||||||
const { watch, setValue } =
|
const { watch, setValue } =
|
||||||
useFormContext<Record<string, ConnectionInfoSchema[]>>();
|
useFormContext<Record<string, ConnectionInfoSchema[]>>();
|
||||||
|
|
||||||
const [mode, setMode] = useState<'idle' | 'add'>('idle');
|
const [mode, setMode] = useState<'idle' | 'add' | 'edit'>('idle');
|
||||||
const readReplicas = watch(name);
|
const readReplicas = watch(name);
|
||||||
|
|
||||||
|
const [activeRow, setActiveRow] = useState<number>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-2">
|
<div className="my-2">
|
||||||
{!fields?.length ? (
|
{!fields?.length ? (
|
||||||
@ -26,24 +111,33 @@ export const ReadReplicas = ({ name }: { name: string }) => {
|
|||||||
) : (
|
) : (
|
||||||
<CardedTable
|
<CardedTable
|
||||||
columns={['No', 'Read Replica', null]}
|
columns={['No', 'Read Replica', null]}
|
||||||
data={(fields ?? []).map((x, i) => [
|
data={(readReplicas ?? []).map((field, i) => [
|
||||||
i + 1,
|
i + 1,
|
||||||
<div>
|
<div>
|
||||||
{x.connectionString.connectionType === 'databaseUrl'
|
{field.connectionString.connectionType === 'databaseUrl'
|
||||||
? x.connectionString.url
|
? field.connectionString.url
|
||||||
: x.connectionString.envVar}
|
: field.connectionString.envVar}
|
||||||
|
</div>,
|
||||||
|
<div className="flex gap-3 justify-end">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
icon={<FaEdit />}
|
||||||
|
onClick={() => {
|
||||||
|
setActiveRow(i);
|
||||||
|
setMode('edit');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
icon={<FaTrash />}
|
||||||
|
onClick={() => {
|
||||||
|
setValue(
|
||||||
|
name,
|
||||||
|
readReplicas.filter((_, index) => index !== i)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>,
|
</div>,
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
icon={<FaTrash />}
|
|
||||||
mode="destructive"
|
|
||||||
onClick={() => {
|
|
||||||
setValue(
|
|
||||||
name,
|
|
||||||
readReplicas.filter((_, index) => index !== i)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
])}
|
])}
|
||||||
showActionCell
|
showActionCell
|
||||||
/>
|
/>
|
||||||
@ -57,6 +151,7 @@ export const ReadReplicas = ({ name }: { name: string }) => {
|
|||||||
append({
|
append({
|
||||||
connectionString: { connectionType: 'databaseUrl', url: '' },
|
connectionString: { connectionType: 'databaseUrl', url: '' },
|
||||||
});
|
});
|
||||||
|
setActiveRow(readReplicas?.length ?? 0);
|
||||||
}}
|
}}
|
||||||
mode="primary"
|
mode="primary"
|
||||||
icon={<FaPlus />}
|
icon={<FaPlus />}
|
||||||
@ -65,23 +160,46 @@ export const ReadReplicas = ({ name }: { name: string }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{mode === 'add' && (
|
{(mode === 'add' || mode === 'edit') && (
|
||||||
<div>
|
<Dialog
|
||||||
<ConnectionInfo name={`${name}.${fields?.length - 1}`} />
|
hasBackdrop
|
||||||
<Button
|
title={mode === 'edit' ? 'Edit Read Replica' : 'Add Read Replica'}
|
||||||
onClick={() => {
|
onClose={() => {
|
||||||
setMode('idle');
|
setMode('idle');
|
||||||
setValue(
|
}}
|
||||||
`${name}.${fields?.length - 1}`,
|
titleTooltip="Optional list of read replica configuration"
|
||||||
fields[fields?.length - 1]
|
size="xxxl"
|
||||||
);
|
>
|
||||||
}}
|
<div className="p-4">
|
||||||
mode="primary"
|
<div className="bg-white border border-hasGray-300 rounded-md shadow-sm overflow-hidden p-4">
|
||||||
className="my-2"
|
<ConnectionString
|
||||||
>
|
name={`${name}.${activeRow}.connectionString`}
|
||||||
Add Read Replica
|
/>
|
||||||
</Button>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div className="bg-white border border-hasGray-300 rounded-md shadow-sm overflow-hidden p-4 mt-sm">
|
||||||
|
<Collapsible
|
||||||
|
triggerChildren={
|
||||||
|
<div className="font-semibold text-muted">
|
||||||
|
Advanced Settings
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PoolSettings name={`${name}.${activeRow}.poolSettings`} />
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setMode('idle');
|
||||||
|
setActiveRow(undefined);
|
||||||
|
}}
|
||||||
|
mode="primary"
|
||||||
|
className="my-2"
|
||||||
|
>
|
||||||
|
{mode === 'edit' ? 'Edit Read Replica' : 'Add Read Replica'}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,7 @@ export const generateConnectionInfo = (
|
|||||||
values: MssqlConnectionSchema['configuration']['connectionInfo']
|
values: MssqlConnectionSchema['configuration']['connectionInfo']
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
database_url:
|
connection_string:
|
||||||
values.connectionString.connectionType === 'databaseUrl'
|
values.connectionString.connectionType === 'databaseUrl'
|
||||||
? values.connectionString.url
|
? values.connectionString.url
|
||||||
: { from_env: values.connectionString.envVar },
|
: { from_env: values.connectionString.envVar },
|
||||||
|
@ -58,5 +58,5 @@ export const areSSLSettingsEnabled = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const areReadReplicasEnabled = () => {
|
export const areReadReplicasEnabled = () => {
|
||||||
return isProConsole(window.__env);
|
return isProConsole(window.__env) || true;
|
||||||
};
|
};
|
||||||
|
@ -67,11 +67,10 @@ const mockMetadata: Metadata = {
|
|||||||
tables: [],
|
tables: [],
|
||||||
configuration: {
|
configuration: {
|
||||||
connection_info: {
|
connection_info: {
|
||||||
connection_string: {
|
connection_string:
|
||||||
from_env: 'HASURA_ENV_VAR',
|
'DRIVER={ODBC Driver 17 for SQL Server};SERVER=host.docker.internal;DATABASE=bikes;Uid=SA;Pwd=reallyStrongPwd123',
|
||||||
},
|
|
||||||
pool_settings: {
|
pool_settings: {
|
||||||
max_connections: 50,
|
total_max_connections: 50,
|
||||||
idle_timeout: 180,
|
idle_timeout: 180,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user