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

View File

@ -154,18 +154,15 @@ const ReadReplicaListItem: React.FC<ReadReplicaListItemProps> = ({
<p>{isFromEnvVar ? currentState.envVarState.envVar : host}</p> <p>{isFromEnvVar ? currentState.envVarState.envVar : host}</p>
{/* The connection string is redundant if it's provided via ENV VAR */} {/* The connection string is redundant if it's provided via ENV VAR */}
{!isFromEnvVar && ( {!isFromEnvVar && (
<span className={`${styles.db_large_string_break_words}`}> <span className="px-sm py-xs max-w-xs align-top break-all">
{showUrl ? ( {showUrl ? (
connectionString connectionString
) : ( ) : (
<span <span
className={styles.show_connection_string} className="text-secondary flex items-center cursor-pointer"
onClick={() => setShowUrl(true)} onClick={() => setShowUrl(true)}
> >
<i <i className="fa fa-eye" aria-hidden="true" />
className={`${styles.showAdminSecret} fa fa-eye`}
aria-hidden="true"
/>
<p style={{ marginLeft: 6 }}>Show Connection String</p> <p style={{ marginLeft: 6 }}>Show Connection String</p>
</span> </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', 'DRIVER={ODBC Driver 17 for SQL Server};SERVER=172.17.0.1;DATABASE=master;Uid=SA;Pwd=reallyStrongPwd123',
pool_settings: {}, 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, replace_configuration: false,
}; };
@ -112,15 +119,18 @@ describe('dataSourceIsEqual works', () => {
}); });
describe('getReadReplicaDBUrlInfo gives the correct result', () => { describe('getReadReplicaDBUrlInfo gives the correct result', () => {
it('for read replicas with db urls', () => { it('for postgres read replicas with db urls', () => {
const res = getReadReplicaDBUrlInfo({ const res = getReadReplicaDBUrlInfo(
use_prepared_statements: false, {
database_url: 'postgres://postgres:test@172.17.0.1:6001/chinook', use_prepared_statements: false,
isolation_level: 'read-committed', database_url: 'postgres://postgres:test@172.17.0.1:6001/chinook',
pool_settings: { isolation_level: 'read-committed',
connection_lifetime: 600, pool_settings: {
connection_lifetime: 600,
},
}, },
}); 'postgres'
);
expect(res).toMatchInlineSnapshot(` expect(res).toMatchInlineSnapshot(`
Object { Object {
"connectionType": "DATABASE_URL", "connectionType": "DATABASE_URL",
@ -134,17 +144,68 @@ describe('getReadReplicaDBUrlInfo gives the correct result', () => {
} }
`); `);
}); });
it('for read replicas with env vars', () => { it('for mssql read replicas with db urls', () => {
const res = getReadReplicaDBUrlInfo({ const res = getReadReplicaDBUrlInfo(
use_prepared_statements: false, {
database_url: { use_prepared_statements: false,
from_env: 'HASURA_GRAPHQL_DATABASE_URL', connection_string: 'postgres://postgres:test@172.17.0.1:6001/chinook',
isolation_level: 'read-committed',
pool_settings: {
connection_lifetime: 600,
},
}, },
isolation_level: 'read-committed', 'mssql'
pool_settings: { );
connection_lifetime: 600, 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(` expect(res).toMatchInlineSnapshot(`
Object { Object {
"connectionType": "ENVIRONMENT_VARIABLES", "connectionType": "ENVIRONMENT_VARIABLES",

View File

@ -120,23 +120,66 @@ export const dataSourceIsEqual = (
return isEqual(filterFields(sourceFromMetaData), filterFields(data)); return isEqual(filterFields(sourceFromMetaData), filterFields(data));
}; };
export const getReadReplicaDBUrlInfo = (replica: SourceConnectionInfo) => { type TGetReadReplicaDBUrlInfoResponse = {
if (!replica.database_url) return null; connectionType: string;
if (typeof replica.database_url === 'string') envVarState?: {
return { envVar: string;
connectionType: connectionTypes.DATABASE_URL, };
databaseURLState: { databaseURLState?: {
dbURL: replica.database_url, dbURL: string;
serviceAccount: '', serviceAccount: string;
global_select_limit: 1000, global_select_limit: number;
projectId: '', projectId: string;
datasets: '', datasets: string;
},
};
return {
connectionType: connectionTypes.ENV_VAR,
envVarState: {
envVar: replica.database_url.from_env,
},
}; };
}; };
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, connectionParameters: true,
databaseURL: false, databaseURL: false,
environmentVariable: true, environmentVariable: true,
read_replicas: false, read_replicas: {
create: true,
edit: true,
},
prepared_statements: false, prepared_statements: false,
isolation_level: false, isolation_level: false,
connectionSettings: false, connectionSettings: false,

View File

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

View File

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

View File

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

View File

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