console: support for mssql read replicas

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3630
Co-authored-by: Varun Choudhary <68095256+Varun-Choudhary@users.noreply.github.com>
GitOrigin-RevId: 9660a5d9cdec8aff17cf20fcad3f309bdb3d182e
This commit is contained in:
Sooraj 2022-02-18 14:39:33 +05:30 committed by hasura-bot
parent d50aae87a5
commit 9144d979d4
9 changed files with 168 additions and 49 deletions

View File

@ -96,12 +96,13 @@ const ConnectDatabase: React.FC<ConnectDatabaseProps> = props => {
const existingReadReplicas: ExtendedConnectDBState[] | [] = (
currentSourceInfo.configuration?.read_replicas ?? []
).map(replica => {
const replicaDBUrlInfo = getReadReplicaDBUrlInfo(replica);
const dbType = currentSourceInfo.kind ?? 'postgres';
const replicaDBUrlInfo = getReadReplicaDBUrlInfo(replica, dbType);
return {
chosenConnectionType:
replicaDBUrlInfo?.connectionType || connectionTypes.DATABASE_URL,
displayName: '',
dbType: currentSourceInfo.kind ?? 'postgres',
dbType,
connectionParamState: {
host: '',
port: '',
@ -347,6 +348,10 @@ const ConnectDatabase: React.FC<ConnectDatabaseProps> = props => {
type: 'UPDATE_DISPLAY_NAME',
data: `read-replica-${indexForName}`,
});
connectDBReadReplicaDispatch({
type: 'UPDATE_DB_DRIVER',
data: connectDBInputState.dbType,
});
};
const onClickCancelOnReadReplicaForm = () =>
@ -382,7 +387,7 @@ const ConnectDatabase: React.FC<ConnectDatabaseProps> = props => {
title="Edit Data Source"
>
{/* Should be rendered only on Pro and Cloud Console */}
{getSupportedDrivers('connectDbForm.read_replicas').includes(
{getSupportedDrivers('connectDbForm.read_replicas.edit').includes(
connectDBInputState.dbType
) &&
(window.__env.consoleId || window.__env.userRole) && (
@ -416,7 +421,7 @@ const ConnectDatabase: React.FC<ConnectDatabaseProps> = props => {
onSubmit={onSubmit}
>
{/* Should be rendered only on Pro and Cloud Console */}
{getSupportedDrivers('connectDbForm.read_replicas').includes(
{getSupportedDrivers('connectDbForm.read_replicas.create').includes(
connectDBInputState.dbType
) &&
(window.__env.consoleId || window.__env.userRole) && (

View File

@ -154,18 +154,15 @@ const ReadReplicaListItem: React.FC<ReadReplicaListItemProps> = ({
<p>{isFromEnvVar ? currentState.envVarState.envVar : host}</p>
{/* The connection string is redundant if it's provided via ENV VAR */}
{!isFromEnvVar && (
<span className={`${styles.db_large_string_break_words}`}>
<span className="px-sm py-xs max-w-xs align-top break-all">
{showUrl ? (
connectionString
) : (
<span
className={styles.show_connection_string}
className="text-secondary flex items-center cursor-pointer"
onClick={() => setShowUrl(true)}
>
<i
className={`${styles.showAdminSecret} fa fa-eye`}
aria-hidden="true"
/>
<i className="fa fa-eye" aria-hidden="true" />
<p style={{ marginLeft: 6 }}>Show Connection String</p>
</span>
)}

View File

@ -89,6 +89,13 @@ describe('dataSourceIsEqual works', () => {
'DRIVER={ODBC Driver 17 for SQL Server};SERVER=172.17.0.1;DATABASE=master;Uid=SA;Pwd=reallyStrongPwd123',
pool_settings: {},
},
read_replicas: [
{
connection_string:
'DRIVER={ODBC Driver 17 for SQL Server};SERVER=172.16.238.1,1502;Database=agtestdb;Uid=sa;Pwd=Password1;ApplicationIntent=ReadOnly',
pool_settings: { idle_timeout: 5, max_connections: 50 },
},
],
},
replace_configuration: false,
};
@ -112,15 +119,18 @@ describe('dataSourceIsEqual works', () => {
});
describe('getReadReplicaDBUrlInfo gives the correct result', () => {
it('for read replicas with db urls', () => {
const res = getReadReplicaDBUrlInfo({
use_prepared_statements: false,
database_url: 'postgres://postgres:test@172.17.0.1:6001/chinook',
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
it('for postgres read replicas with db urls', () => {
const res = getReadReplicaDBUrlInfo(
{
use_prepared_statements: false,
database_url: 'postgres://postgres:test@172.17.0.1:6001/chinook',
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
},
},
});
'postgres'
);
expect(res).toMatchInlineSnapshot(`
Object {
"connectionType": "DATABASE_URL",
@ -134,17 +144,68 @@ describe('getReadReplicaDBUrlInfo gives the correct result', () => {
}
`);
});
it('for read replicas with env vars', () => {
const res = getReadReplicaDBUrlInfo({
use_prepared_statements: false,
database_url: {
from_env: 'HASURA_GRAPHQL_DATABASE_URL',
it('for mssql read replicas with db urls', () => {
const res = getReadReplicaDBUrlInfo(
{
use_prepared_statements: false,
connection_string: 'postgres://postgres:test@172.17.0.1:6001/chinook',
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
},
},
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
'mssql'
);
expect(res).toMatchInlineSnapshot(`
Object {
"connectionType": "DATABASE_URL",
"databaseURLState": Object {
"datasets": "",
"dbURL": "postgres://postgres:test@172.17.0.1:6001/chinook",
"global_select_limit": 1000,
"projectId": "",
"serviceAccount": "",
},
}
`);
});
it('for postgres read replicas with env vars', () => {
const res = getReadReplicaDBUrlInfo(
{
use_prepared_statements: false,
database_url: {
from_env: 'HASURA_GRAPHQL_DATABASE_URL',
},
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
},
},
});
'postgres'
);
expect(res).toMatchInlineSnapshot(`
Object {
"connectionType": "ENVIRONMENT_VARIABLES",
"envVarState": Object {
"envVar": "HASURA_GRAPHQL_DATABASE_URL",
},
}
`);
});
it('for mssql read replicas with env vars', () => {
const res = getReadReplicaDBUrlInfo(
{
use_prepared_statements: false,
connection_string: {
from_env: 'HASURA_GRAPHQL_DATABASE_URL',
},
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
},
},
'mssql'
);
expect(res).toMatchInlineSnapshot(`
Object {
"connectionType": "ENVIRONMENT_VARIABLES",

View File

@ -120,23 +120,66 @@ export const dataSourceIsEqual = (
return isEqual(filterFields(sourceFromMetaData), filterFields(data));
};
export const getReadReplicaDBUrlInfo = (replica: SourceConnectionInfo) => {
if (!replica.database_url) return null;
if (typeof replica.database_url === 'string')
return {
connectionType: connectionTypes.DATABASE_URL,
databaseURLState: {
dbURL: replica.database_url,
serviceAccount: '',
global_select_limit: 1000,
projectId: '',
datasets: '',
},
};
return {
connectionType: connectionTypes.ENV_VAR,
envVarState: {
envVar: replica.database_url.from_env,
},
type TGetReadReplicaDBUrlInfoResponse = {
connectionType: string;
envVarState?: {
envVar: string;
};
databaseURLState?: {
dbURL: string;
serviceAccount: string;
global_select_limit: number;
projectId: string;
datasets: string;
};
};
export const getReadReplicaDBUrlInfo = (
replica: SourceConnectionInfo,
dbType: MetadataDataSource['kind']
): TGetReadReplicaDBUrlInfoResponse | null => {
const dbUrlConfig = {
dbURL: '',
serviceAccount: '',
global_select_limit: 1000,
projectId: '',
datasets: '',
};
if (!replica?.database_url && !replica?.connection_string) return null;
if (dbType === 'postgres') {
if (typeof replica?.database_url === 'string') {
return {
connectionType: connectionTypes.DATABASE_URL,
databaseURLState: {
...dbUrlConfig,
dbURL: replica?.database_url,
},
};
}
return {
connectionType: connectionTypes.ENV_VAR,
envVarState: {
envVar: replica?.database_url?.from_env ?? '',
},
};
}
if (dbType === 'mssql') {
if (typeof replica?.connection_string === 'string') {
return {
connectionType: connectionTypes.DATABASE_URL,
databaseURLState: {
...dbUrlConfig,
dbURL: replica?.connection_string,
},
};
}
return {
connectionType: connectionTypes.ENV_VAR,
envVarState: {
envVar: replica?.connection_string?.from_env ?? '',
},
};
}
return null;
};

View File

@ -202,7 +202,10 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
connectionParameters: true,
databaseURL: false,
environmentVariable: true,
read_replicas: false,
read_replicas: {
create: true,
edit: true,
},
prepared_statements: false,
isolation_level: false,
connectionSettings: false,

View File

@ -221,7 +221,10 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
connectionParameters: false,
databaseURL: true,
environmentVariable: true,
read_replicas: false,
read_replicas: {
create: true,
edit: true,
},
prepared_statements: false,
isolation_level: false,
connectionSettings: true,

View File

@ -682,7 +682,10 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
connectionParameters: true,
databaseURL: true,
environmentVariable: true,
read_replicas: true,
read_replicas: {
create: true,
edit: true,
},
prepared_statements: true,
isolation_level: true,
connectionSettings: true,

View File

@ -389,7 +389,10 @@ export type SupportedFeaturesType = {
connectionParameters: boolean;
databaseURL: boolean;
environmentVariable: boolean;
read_replicas: boolean;
read_replicas: {
create: boolean;
edit: boolean;
};
prepared_statements: boolean;
isolation_level: boolean;
connectionSettings: boolean;

View File

@ -42,6 +42,7 @@ export const addSource = (
connection_string: payload.dbUrl,
pool_settings: payload.connection_pool_settings,
},
read_replicas: replicas?.length ? replicas : null,
},
replace_configuration,
},