diff --git a/src/browser/modules/DBMSInfo/DatabaseKernelInfo.tsx b/src/browser/modules/DBMSInfo/DatabaseKernelInfo.tsx index 46bfb5a014a..69f01886fd9 100644 --- a/src/browser/modules/DBMSInfo/DatabaseKernelInfo.tsx +++ b/src/browser/modules/DBMSInfo/DatabaseKernelInfo.tsx @@ -41,7 +41,7 @@ import { } from 'shared/modules/commands/commandsDuck' import { Database, - getClusterRole, + getClusterRoleForDb, getDatabases, getEdition, getStoreSize, @@ -133,12 +133,13 @@ export const DatabaseKernelInfo = ({ } const mapStateToProps = (state: any) => { + const dbName = getUsedDbName(state) return { version: getRawVersion(state), edition: getEdition(state), - dbName: getUsedDbName(state), + dbName, storeSize: getStoreSize(state), - role: getClusterRole(state), + role: getClusterRoleForDb(state, dbName), databases: getDatabases(state) } } diff --git a/src/browser/modules/Stream/Queries/QueriesFrame.tsx b/src/browser/modules/Stream/Queries/QueriesFrame.tsx index f62a725c8a2..2401242d498 100644 --- a/src/browser/modules/Stream/Queries/QueriesFrame.tsx +++ b/src/browser/modules/Stream/Queries/QueriesFrame.tsx @@ -57,9 +57,12 @@ import { killQueriesProcedure, listQueriesProcedure } from 'shared/modules/cypher/queriesProcedureHelper' -import { getRawVersion, hasProcedure } from 'shared/modules/dbMeta/dbMetaDuck' +import { + getRawVersion, + hasProcedure, + isOnCausalCluster +} from 'shared/modules/dbMeta/dbMetaDuck' import { getDefaultBoltScheme } from 'shared/modules/features/versionedFeatures' -import { isOnCausalCluster } from 'shared/utils/selectors' type QueriesFrameState = { queries: any[] diff --git a/src/browser/modules/Stream/SysInfoFrame/SysInfoFrame.tsx b/src/browser/modules/Stream/SysInfoFrame/SysInfoFrame.tsx index a06b17e049d..1a81884f629 100644 --- a/src/browser/modules/Stream/SysInfoFrame/SysInfoFrame.tsx +++ b/src/browser/modules/Stream/SysInfoFrame/SysInfoFrame.tsx @@ -48,11 +48,11 @@ import { getDatabases, getMetricsNamespacesEnabled, getMetricsPrefix, - isEnterprise + isEnterprise, + isOnCausalCluster } from 'shared/modules/dbMeta/dbMetaDuck' import { hasMultiDbSupport } from 'shared/modules/features/versionedFeatures' import { Frame } from 'shared/modules/frames/framesDuck' -import { isOnCausalCluster } from 'shared/utils/selectors' export type DatabaseMetric = { label: string; value?: string } export type SysInfoFrameState = { diff --git a/src/shared/modules/dbMeta/dbMetaDuck.ts b/src/shared/modules/dbMeta/dbMetaDuck.ts index fd2f36996a1..755c0218a30 100644 --- a/src/shared/modules/dbMeta/dbMetaDuck.ts +++ b/src/shared/modules/dbMeta/dbMetaDuck.ts @@ -21,7 +21,9 @@ import { versionHasEditorHistorySetting } from './utils' import { isConfigValFalsy } from 'services/bolt/boltHelpers' import { GlobalState } from 'shared/globalState' import { APP_START } from 'shared/modules/app/appDuck' +import { extractServerInfo } from './utils' import { coerce, SemVer } from 'semver' +import { gte } from 'lodash-es' export const UPDATE_META = 'meta/UPDATE_META' export const PARSE_META = 'meta/PARSE_META' @@ -33,6 +35,7 @@ export const DB_META_DONE = 'meta/DB_META_DONE' export const SYSTEM_DB = 'system' export const VERSION_FOR_EDITOR_HISTORY_SETTING = '4.3.0' +export const VERSION_FOR_CLUSTER_ROLE_IN_SHOW_DB = '4.3.0' export const metaQuery = ` CALL db.labels() YIELD label @@ -51,7 +54,6 @@ MATCH ()-[]->() RETURN { name:'relationships', data: count(*)} AS result export const serverInfoQuery = 'CALL dbms.components() YIELD name, versions, edition' -import { extractServerInfo } from './utils' export function fetchMetaData() { return { @@ -97,7 +99,7 @@ export const initialState = { properties: [], functions: [], procedures: [], - role: null, + role: null, // Used pre version 4.3 (before SHOW DATABASES had the role and we had to query for it) server: { version: null, edition: null, @@ -162,7 +164,6 @@ export const getEdition = (state: GlobalState) => state[NAME].server.edition export const hasEdition = (state: any) => state[NAME].server.edition !== initialState.server.edition export const getStoreSize = (state: any) => state[NAME].server.storeSize -export const getClusterRole = (state: any) => state[NAME].role export const isEnterprise = (state: any) => ['enterprise'].includes(state[NAME].server.edition) export const isBeta = (state: any) => /-/.test(state[NAME].server.version) @@ -219,6 +220,28 @@ export const shouldRetainConnectionCredentials = (state: any) => export const shouldRetainEditorHistory = (state: any) => !supportsEditorHistorySetting(state) || getRetainEditorHistory(state) +export const isOnCausalCluster = (state: GlobalState): boolean => { + const version = getSemanticVersion(state) + if (!version) return false + + if (gte(version, VERSION_FOR_CLUSTER_ROLE_IN_SHOW_DB)) { + return getDatabases(state).some(database => database.role !== 'standalone') + } else { + return hasProcedure(state, 'dbms.cluster.overview') + } +} +export const getClusterRoleForDb = (state: GlobalState, activeDb: string) => { + const version = getSemanticVersion(state) + if (!version) return false + + if (gte(version, VERSION_FOR_CLUSTER_ROLE_IN_SHOW_DB)) { + return getDatabases(state).find(database => database.name === activeDb) + ?.role + } else { + return state[NAME].role + } +} + // Reducers const dbMetaReducer = ( state = initialState, diff --git a/src/shared/modules/dbMeta/epics.ts b/src/shared/modules/dbMeta/dbMetaEpics.ts similarity index 97% rename from src/shared/modules/dbMeta/epics.ts rename to src/shared/modules/dbMeta/dbMetaEpics.ts index 7a99a0687ce..db819de1348 100644 --- a/src/shared/modules/dbMeta/epics.ts +++ b/src/shared/modules/dbMeta/dbMetaEpics.ts @@ -36,7 +36,9 @@ import { FORCE_FETCH, SYSTEM_DB, metaQuery, - serverInfoQuery + serverInfoQuery, + VERSION_FOR_CLUSTER_ROLE_IN_SHOW_DB, + isOnCausalCluster } from './dbMetaDuck' import { Database, @@ -70,12 +72,12 @@ import { } from 'shared/modules/connections/connectionsDuck' import { clearHistory } from 'shared/modules/history/historyDuck' import { backgroundTxMetadata } from 'shared/services/bolt/txMetadata' -import { isOnCausalCluster } from 'shared/utils/selectors' import { getListFunctionQuery, getListProcedureQuery } from '../cypher/functionsAndProceduresHelper' import { isInt, Record } from 'neo4j-driver' +import { gte } from 'semver' async function databaseList(store: any) { try { @@ -191,17 +193,22 @@ async function clusterRole(store: any) { return } + const version = getSemanticVersion(store.getState()) + if (version && gte(version, VERSION_FOR_CLUSTER_ROLE_IN_SHOW_DB)) { + // No need to query for the cluster role anymore since it's available in the data from SHOW DATABASES + return + } + const res = await bolt.directTransaction( getDbClusterRole(store.getState()), {}, backgroundTxMetadata ) - if (!res) return Rx.Observable.of(null) + if (!res) return const role = res.records[0].get(0) store.dispatch(update({ role })) - return Rx.Observable.of(null) } async function fetchServerInfo(store: any) { diff --git a/src/shared/modules/features/versionedFeatures.ts b/src/shared/modules/features/versionedFeatures.ts index 61d038ac3c5..3a5b5235781 100644 --- a/src/shared/modules/features/versionedFeatures.ts +++ b/src/shared/modules/features/versionedFeatures.ts @@ -25,6 +25,7 @@ import { guessSemverVersion } from './featureDuck.utils' import { GlobalState } from 'project-root/src/shared/globalState' const NEO4J_4_0 = '4.0.0-alpha01' +const NEO4J_5_0 = '5.0.0-alpha01' export const FIRST_MULTI_DB_SUPPORT = NEO4J_4_0 // Keep the following as 3.4.0 as 3.5.X has a @@ -50,6 +51,10 @@ export const getDbClusterRole = (state: GlobalState) => { if (!semver.valid(serverVersion)) { return pre4 } + if (serverVersion && semver.gte(serverVersion, NEO4J_5_0)) { + const db = getUseDb(state) + return `SHOW DATABASES YIELD role, name WHERE name = "${db}"` + } if (serverVersion && semver.gte(serverVersion, NEO4J_4_0)) { const db = getUseDb(state) return `CALL dbms.cluster.role("${db}") YIELD role` diff --git a/src/shared/rootEpic.ts b/src/shared/rootEpic.ts index 8e412dc53e7..af0c031831b 100644 --- a/src/shared/rootEpic.ts +++ b/src/shared/rootEpic.ts @@ -59,7 +59,7 @@ import { clearMetaOnDisconnectEpic, dbMetaEpic, serverConfigEpic -} from './modules/dbMeta/epics' +} from './modules/dbMeta/dbMetaEpics' import { discoveryOnStartupEpic, injectDiscoveryEpic diff --git a/src/shared/utils/selectors.ts b/src/shared/utils/selectors.ts index b5610f3a9e4..719e365ca98 100644 --- a/src/shared/utils/selectors.ts +++ b/src/shared/utils/selectors.ts @@ -9,9 +9,6 @@ import { import { getAllowOutgoingConnections, getClientsAllowTelemetry, - getDatabases, - getRawVersion, - hasProcedure, isServerConfigDone, shouldAllowOutgoingConnections } from 'shared/modules/dbMeta/dbMetaDuck' @@ -99,14 +96,3 @@ export const getTelemetrySettings = (state: GlobalState): TelemetrySettings => { return { source, ...rules[source] } } - -export const isOnCausalCluster = (state: GlobalState): boolean => { - const version = semver.coerce(getRawVersion(state)) - if (!version) return false - - if (semver.gte(version, '4.3.0')) { - return getDatabases(state).some(database => database.role !== 'standalone') - } else { - return hasProcedure(state, 'dbms.cluster.overview') - } -}