mirror of
https://github.com/hasura/graphql-engine.git
synced 2025-01-05 22:34:22 +03:00
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:
parent
d50aae87a5
commit
9144d979d4
@ -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) && (
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
@ -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",
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user