From c86a44480d9495b395b061c7e8d94ae17f35ba02 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Thu, 24 Aug 2023 18:56:52 -0400 Subject: [PATCH 01/36] Title not editable in standalone mode --- src/dashboard/header/DashboardTitle.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/dashboard/header/DashboardTitle.tsx b/src/dashboard/header/DashboardTitle.tsx index 43c5ecb9f..e6cd18d85 100644 --- a/src/dashboard/header/DashboardTitle.tsx +++ b/src/dashboard/header/DashboardTitle.tsx @@ -70,7 +70,8 @@ export const NeoDashboardTitle = ({ return (
{/* TODO : Replace with editable field if dashboard is editable */} - {editing ? ( + {/* only allow edit title if dashboard is not standalone - here we are in Title edit mode*/} + {editing && !isStandalone ? (
- ) : ( + ) : !isStandalone? ( /* out of edit mode - if Not Standalone we display the edit button */
{dashboardTitle} @@ -118,6 +119,10 @@ export const NeoDashboardTitle = ({ )}
+ ):( /* if we are in Standalone just title is displayed with no edit button */ +
+ {dashboardTitle} +
)} {/* If the app is not running in standalone mode (i.e. in edit mode) always show dashboard settings. */} {!isStandalone ? ( From f17cc9a1aa55662ff7af6c467d4ae8135e8e3287 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 25 Aug 2023 19:24:08 -0400 Subject: [PATCH 02/36] standalone load v1 (working) --- .../pages/developer-guide/configuration.adoc | 9 ++++++- .../developer-guide/state-management.adoc | 1 + public/config.json | 7 ++--- scripts/config-entrypoint.sh | 3 ++- src/application/ApplicationActions.ts | 4 ++- src/application/ApplicationReducer.ts | 2 ++ src/application/ApplicationSelectors.ts | 1 + src/application/ApplicationThunks.ts | 4 ++- src/dashboard/header/DashboardTitle.tsx | 23 ++++++++-------- src/modal/LoadModal.tsx | 27 ++++++++++++++----- 10 files changed, 56 insertions(+), 25 deletions(-) diff --git a/docs/modules/ROOT/pages/developer-guide/configuration.adoc b/docs/modules/ROOT/pages/developer-guide/configuration.adoc index 6df3fdfba..7a039ee7a 100644 --- a/docs/modules/ROOT/pages/developer-guide/configuration.adoc +++ b/docs/modules/ROOT/pages/developer-guide/configuration.adoc @@ -24,7 +24,8 @@ will look like this: "standaloneDatabase": "neo4j", "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", - "standaloneDashboardURL": "" + "standaloneDashboardURL": "", + "standaloneAllowLoad": true } .... @@ -84,6 +85,12 @@ use multiple databases. inside Neo4j and would like to run a standalone mode deployment with a dashboard from a URL, set this parameter to the complete URL pointing to the dashboard JSON. + +|standaloneAllowLoad |boolean |true |If set t yes the "Load Dashboard" +button will be enabled in standalone mode, allowing users to load +additional dashboards from Neo4J. +*NOTE*: when Load is enabled in standalone mode, only Database is available +as a source, not file. |=== == Configuring SSO diff --git a/docs/modules/ROOT/pages/developer-guide/state-management.adoc b/docs/modules/ROOT/pages/developer-guide/state-management.adoc index c7513269d..a2ee906d1 100644 --- a/docs/modules/ROOT/pages/developer-guide/state-management.adoc +++ b/docs/modules/ROOT/pages/developer-guide/state-management.adoc @@ -134,6 +134,7 @@ standalone mode. "standaloneDatabase": "neo4j", "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", + "standaloneAllowLoad": true, "notificationIsDismissable": null } .... diff --git a/public/config.json b/public/config.json index 6a203041f..07477d5ce 100644 --- a/public/config.json +++ b/public/config.json @@ -1,12 +1,13 @@ { "ssoEnabled": false, "ssoDiscoveryUrl": "https://example.com", - "standalone": false, + "standalone": true, "standaloneProtocol": "neo4j", "standaloneHost": "localhost", "standalonePort": "7687", - "standaloneDatabase": "neo4j", + "standaloneDatabase": "movies", "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", - "standaloneDashboardURL": "" + "standaloneDashboardURL": "", + "standaloneAllowLoad": true } diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index 25374b647..fa67391d1 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -15,5 +15,6 @@ echo " \ \"standalonePassword\": \"${standalonePassword:=}\", \ \"standaloneDashboardName\": \"${standaloneDashboardName:='My Dashboard'}\", \ \"standaloneDashboardDatabase\": \"${standaloneDashboardDatabase:='neo4j'}\", \ - \"standaloneDashboardURL\": \"${standaloneDashboardURL:=}\" \ + \"standaloneDashboardURL\": \"${standaloneDashboardURL:=}\", \ + \"standaloneAllowLoad\": \"${standaloneAllowLoad:=true}\" \ }" > /usr/share/nginx/html/config.json diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 49311d768..e9c2094df 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -143,7 +143,8 @@ export const setStandaloneEnabled = ( standaloneDashboardDatabase: string, standaloneDashboardURL: string, standaloneUsername: string, - standalonePassword: string + standalonePassword: string, + standaloneAllowLoad: boolean ) => ({ type: SET_STANDALONE_ENABLED, payload: { @@ -157,6 +158,7 @@ export const setStandaloneEnabled = ( standaloneDashboardURL, standaloneUsername, standalonePassword, + standaloneAllowLoad, }, }); diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 23dcdd9a1..52833221d 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -134,6 +134,7 @@ export const applicationReducer = (state = initialState, action: { type: any; pa standaloneDashboardURL, standaloneUsername, standalonePassword, + standaloneAllowLoad } = payload; state = update(state, { standalone: standalone, @@ -146,6 +147,7 @@ export const applicationReducer = (state = initialState, action: { type: any; pa standaloneDashboardURL: standaloneDashboardURL, standaloneUsername: standaloneUsername, standalonePassword: standalonePassword, + standaloneAllowLoad: standaloneAllowLoad, }); return state; } diff --git a/src/application/ApplicationSelectors.ts b/src/application/ApplicationSelectors.ts index 263fae4bf..a20543610 100644 --- a/src/application/ApplicationSelectors.ts +++ b/src/application/ApplicationSelectors.ts @@ -81,6 +81,7 @@ export const applicationGetStandaloneSettings = (state: any) => { standaloneDashboardURL: state.application.standaloneDashboardURL, standaloneUsername: state.application.standaloneUsername, standalonePassword: state.application.standalonePassword, + standaloneAllowLoad: state.application.standaloneAllowLoad, }; }; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index d09ee1ed5..a1b298da5 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -349,6 +349,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: standaloneDashboardName: 'My Dashboard', standaloneDashboardDatabase: 'dashboards', standaloneDashboardURL: '', + standaloneAllowLoad: true, }; try { config = await (await fetch('config.json')).json(); @@ -390,7 +391,8 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: config.standaloneDashboardDatabase, config.standaloneDashboardURL, config.standaloneUsername, - config.standalonePassword + config.standalonePassword, + config.standaloneAllowLoad, ) ); dispatch(setConnectionModalOpen(false)); diff --git a/src/dashboard/header/DashboardTitle.tsx b/src/dashboard/header/DashboardTitle.tsx index e6cd18d85..2f4868578 100644 --- a/src/dashboard/header/DashboardTitle.tsx +++ b/src/dashboard/header/DashboardTitle.tsx @@ -2,7 +2,7 @@ import React, { Suspense, useCallback, useEffect, useState } from 'react'; import debounce from 'lodash/debounce'; import { connect } from 'react-redux'; import { setDashboardTitle } from '../DashboardActions'; -import { applicationGetConnection, applicationIsStandalone } from '../../application/ApplicationSelectors'; +import { applicationGetConnection, applicationGetStandaloneSettings } from '../../application/ApplicationSelectors'; import { getDashboardTitle, getDashboardExtensions, getDashboardSettings } from '../DashboardSelectors'; import { getDashboardIsEditable } from '../../settings/SettingsSelectors'; import { updateDashboardSetting } from '../../settings/SettingsActions'; @@ -21,7 +21,7 @@ export const NeoDashboardTitle = ({ dashboardTitle, setDashboardTitle, editable, - isStandalone, + standaloneSettings, dashboardSettings, extensions, updateDashboardSetting, @@ -71,7 +71,7 @@ export const NeoDashboardTitle = ({
{/* TODO : Replace with editable field if dashboard is editable */} {/* only allow edit title if dashboard is not standalone - here we are in Title edit mode*/} - {editing && !isStandalone ? ( + {editing && !standaloneSettings.standalone ? (
- ) : !isStandalone? ( /* out of edit mode - if Not Standalone we display the edit button */ + ) : !standaloneSettings.standalone? ( /* out of edit mode - if Not Standalone we display the edit button */
{dashboardTitle} @@ -125,9 +125,9 @@ export const NeoDashboardTitle = ({
)} {/* If the app is not running in standalone mode (i.e. in edit mode) always show dashboard settings. */} - {!isStandalone ? ( + {!standaloneSettings.standalone ? (
- {editable ? : <>} + {(editable && !standaloneSettings.standalone) ? : <>} @@ -158,15 +158,14 @@ export const NeoDashboardTitle = ({ {renderExtensionsButtons()} - ) : ( - <> + ) : ( /* in standalone we allow load is standaloneAllowLoad parameter is enabled */ + <> )}
- ) : ( - <> - )} + ) : standaloneSettings.standaloneAllowLoad ? : <> + }
); }; @@ -174,7 +173,7 @@ export const NeoDashboardTitle = ({ const mapStateToProps = (state) => ({ dashboardTitle: getDashboardTitle(state), editable: getDashboardIsEditable(state), - isStandalone: applicationIsStandalone(state), + standaloneSettings: applicationGetStandaloneSettings(state), dashboardSettings: getDashboardSettings(state), extensions: getDashboardExtensions(state), connection: applicationGetConnection(state), diff --git a/src/modal/LoadModal.tsx b/src/modal/LoadModal.tsx index 1331612ae..c57c12184 100644 --- a/src/modal/LoadModal.tsx +++ b/src/modal/LoadModal.tsx @@ -9,17 +9,20 @@ import { } from '../dashboard/DashboardThunks'; import { DataGrid } from '@mui/x-data-grid'; import { Neo4jContext, Neo4jContextState } from 'use-neo4j/dist/neo4j.context'; -import { MenuItem, Button, Dialog, Dropdown } from '@neo4j-ndl/react'; +import { MenuItem, Button, Dialog, Dropdown, IconButton } from '@neo4j-ndl/react'; import { CloudArrowUpIconOutline, PlayIconSolid, DatabaseAddCircleIcon, DocumentPlusIconOutline, } from '@neo4j-ndl/react/icons'; +import { applicationIsStandalone } from '../application/ApplicationSelectors'; /** * A modal to save a dashboard as a JSON text string. - * The button to open the modal is intended to use in a drawer at the side of the page. + * The button to open the modal is renderedd as: + * - a ListItem to use in a drawer at the side of the page if the app is in Editor Mode. + * - a Button to be diplayed by itself if the app is in Standalone Mode. */ export const NeoLoadModal = ({ @@ -27,6 +30,7 @@ export const NeoLoadModal = ({ loadDatabaseListFromNeo4j, loadDashboardFromNeo4j, loadDashboardListFromNeo4j, + isStandalone }) => { const [loadModalOpen, setLoadModalOpen] = React.useState(false); const [loadFromNeo4jModalOpen, setLoadFromNeo4jModalOpen] = React.useState(false); @@ -97,7 +101,15 @@ export const NeoLoadModal = ({ return ( <> - } /> + {!isStandalone? + ( + } /> + ):( + + + + ) + } @@ -123,6 +135,7 @@ export const NeoLoadModal = ({ Select from Neo4j + {!isStandalone ? - + : <> + }
); - alert ('I am here and I am a dashboard') return content; }; diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index eca09c110..a7e8f6990 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -7,8 +7,6 @@ import { updateGlobalParametersThunk, updateParametersToNeo4jTypeThunk } from '. import { createUUID } from '../utils/uuid'; import { createLogThunk } from '../application/ApplicationThunks'; import { applicationGetLoggingSettings, applicationGetConnectionUser, applicationIsStandalone } from '../application/ApplicationSelectors'; -// import { Neo4jContext, Neo4jContextState } from 'use-neo4j/dist/neo4j.context'; -// import { useContext } from 'react'; export const removePageThunk = (number) => (dispatch: any, getState: any) => { try { @@ -56,12 +54,7 @@ export const movePageThunk = (oldIndex: number, newIndex: number) => (dispatch: }; export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { - const loggingState = getState(); - const loggingSettings = applicationGetLoggingSettings(loggingState) - const loguser = applicationGetConnectionUser(loggingState) - const neodashMode = applicationIsStandalone(loggingState) ? 'Standalone' : 'Editor' - - try { + try { if (text.length == 0) { throw 'No dashboard file specified. Did you select a file?'; } @@ -75,7 +68,6 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { if (dashboard._persist && dashboard.application && dashboard.dashboard) { dispatch( createNotificationThunk('Loaded a Debug Report', "Recovery-mode active. All report types were set to 'table'.") - ); dashboard.dashboard.pages.map((p) => { p.reports.map((r) => { @@ -142,9 +134,9 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { // Reverse engineer the minimal set of fields from the selection loaded. dashboard.pages.forEach((p) => { - r.fields = []; - p.reports.forEach((r) => { + p.reports.forEach((r) => { if (r.selection) { + r.fields = []; Object.keys(r.selection).forEach((f) => { r.fields.push([f, r.selection[f]]); }); @@ -153,21 +145,6 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { }); dispatch(setDashboard(dashboard)); - // if(loggingSettings.loggingMode > '1') { - // const { driver } = useContext(Neo4jContext); - // dispatch( - // createLogThunk( - // driver, - // loggingSettings.loggingDatabase, - // neodashMode, - // loguser, - // 'INF - load dashboard from file', - // loggingSettings.loggingDatabase, - // 'Name:'+dashboard.title, - // 'User '+loguser+' Loaded dashboard from file in '+neodashMode+' mode at '+Date.now() - // ) - // ) - // } const { application } = getState(); @@ -175,21 +152,6 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { dispatch(setParametersToLoadAfterConnecting(null)); dispatch(updateParametersToNeo4jTypeThunk()); } catch (e) { - // if(loggingSettings.loggingMode > '1') { - // const { driver } = useContext(Neo4jContext); - // dispatch( - // createLogThunk( - // driver, - // loggingSettings.loggingDatabase, - // neodashMode, - // loguser, - // 'ERR - load dashboard from file', - // loggingSettings.loggingDatabase, - // 'Name:Not Parsed', - // 'Error while trying to load dashboard from file in '+neodashMode+' mode at '+Date.now() - // ) - // ) - // } dispatch(createNotificationThunk('Unable to load dashboard', e)); } }; @@ -197,12 +159,12 @@ export const loadDashboardThunk = (text) => (dispatch: any, getState: any) => { export const saveDashboardToNeo4jThunk = (driver, database, dashboard, date, user, overwrite = false) => (dispatch: any, getState: any) => { - const loggingState = getState(); - const loggingSettings = applicationGetLoggingSettings(loggingState) - const loguser = applicationGetConnectionUser(loggingState) - const neodashMode = applicationIsStandalone(loggingState) ? 'Standalone' : 'Editor' + const loggingState = getState(); + const loggingSettings = applicationGetLoggingSettings(loggingState) + const loguser = applicationGetConnectionUser(loggingState) + const neodashMode = applicationIsStandalone(loggingState) ? 'Standalone' : 'Editor' - try { + try { const uuid = createUUID(); const { title, version } = dashboard; @@ -229,6 +191,20 @@ export const saveDashboardToNeo4jThunk = (records) => { if (records && records[0] && records[0]._fields && records[0]._fields[0] && records[0]._fields[0] == uuid) { dispatch(createNotificationThunk('🎉 Success!', 'Your current dashboard was saved to Neo4j.')); + if (loggingSettings.loggingMode > '1'){ + dispatch( + createLogThunk( + driver, + loggingSettings.loggingDatabase, + neodashMode, + loguser, + 'INF - save dashboard', + database, + 'Name:'+title, + 'User '+loguser+' saved dashboard to Neo4J in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) + ) + ); + } } else { dispatch( createNotificationThunk( @@ -236,11 +212,41 @@ export const saveDashboardToNeo4jThunk = `Do you have write access to the '${database}' database?` ) ); + if (loggingSettings.loggingMode > '1'){ + dispatch( + createLogThunk( + driver, + loggingSettings.loggingDatabase, + neodashMode, + loguser, + 'ERR - save dashboard', + database, + 'Name:'+title, + 'Error while trying to save dashboard to Neo4J in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) + ) + ); + } + } } ); } catch (e) { dispatch(createNotificationThunk('Unable to save dashboard to Neo4j', e)); + if (loggingSettings.loggingMode > '1'){ + dispatch( + createLogThunk( + driver, + loggingSettings.loggingDatabase, + neodashMode, + loguser, + 'ERR - save dashboard', + database, + 'Name:Not fetched', + 'Error while trying to save dashboard to Neo4J in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) + ) + ); + } + } }; @@ -276,7 +282,7 @@ try { 'ERR - load dashboard', database, 'UUID:'+uuid, - 'Error while trying to load dashboard by UUID in '+neodashMode+' mode at '+Date.now() + 'Error while trying to load dashboard by UUID in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -291,7 +297,7 @@ try { 'INF - load dashboard', database, 'UUID:'+uuid, - 'User '+loguser+' Loaded dashboard by UUID in '+neodashMode+' mode at '+Date.now() + 'User '+loguser+' Loaded dashboard by UUID in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -310,7 +316,7 @@ try { 'ERR - load dashboard', database, 'UUID:'+uuid, - 'Error while trying to load dashboard by UUID in '+neodashMode+' mode at '+Date.now() + 'Error while trying to load dashboard by UUID in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -322,7 +328,6 @@ export const loadDashboardFromNeo4jByNameThunk = (driver, database, name, callba const loggingSettings = applicationGetLoggingSettings(loggingState) const loguser = applicationGetConnectionUser(loggingState) const neodashMode = applicationIsStandalone(loggingState) ? 'Standalone' : 'Editor' - alert('loggingsettings: '+loggingSettings.loggingMode+'\n'+loggingSettings.loggingDatabase+'\n'+loguser+'\n'+neodashMode) try { const query = 'MATCH (d:_Neodash_Dashboard) WHERE d.title = $name RETURN d.content as dashboard ORDER by d.date DESC LIMIT 1'; @@ -351,7 +356,7 @@ export const loadDashboardFromNeo4jByNameThunk = (driver, database, name, callba 'ERR - load dashboard', database, 'Name:'+name, - 'Error while trying to load dashboard by Name in '+neodashMode+' mode at '+Date.now() + 'Error while trying to load dashboard by Name in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -370,7 +375,7 @@ export const loadDashboardFromNeo4jByNameThunk = (driver, database, name, callba 'ERR - load dashboard', database, 'Name:'+name, - 'Error while trying to load dashboard by Name in '+neodashMode+' mode at '+Date.now() + 'Error while trying to load dashboard by Name in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -387,7 +392,7 @@ export const loadDashboardFromNeo4jByNameThunk = (driver, database, name, callba 'INF - load dashboard', database, 'Name:'+name, - 'User '+loguser+' Loaded dashboard by UUID in '+neodashMode+' mode at '+Date.now() + 'User '+loguser+' Loaded dashboard by UUID in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } diff --git a/src/modal/ConnectionModal.tsx b/src/modal/ConnectionModal.tsx index ad98a65b3..bb9d931a8 100644 --- a/src/modal/ConnectionModal.tsx +++ b/src/modal/ConnectionModal.tsx @@ -217,8 +217,8 @@ export default function NeoConnectionModal({
{standaloneSettings.standaloneDashboardURL === '' ? ( <> - Sign in to continue. You will be connected to Neo4j, and load a dashboard called - {standaloneSettings.standaloneDashboardName}. + Sign in to continue. You will be connected to Neo4j, and load a dashboard called + {standaloneSettings.standaloneDashboardName}. ) : ( <> Sign in to continue. You will be connected to Neo4j, and load a dashboard. From 15fac3e5667b250dac8f3a7dd2d2415e85d10406 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Thu, 31 Aug 2023 01:06:23 -0400 Subject: [PATCH 09/36] fix on error notification --- src/application/ApplicationActions.ts | 4 +-- src/application/ApplicationReducer.ts | 2 +- src/application/ApplicationSelectors.ts | 5 --- src/application/ApplicationThunks.ts | 45 ++++++++++++++++--------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index d2e5d838e..2993431ba 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -185,9 +185,9 @@ export const setLoggingDatabase = (loggingDatabase: string) => ({ }); export const SET_LOG_ERROR_NOTIFICATION = 'APPLICATION/SET_LOG_ERROR_NOTIFICATION'; -export const setLogErrorNotification = (parameters: any) => ({ +export const setLogErrorNotification = (logErrorNotification: any) => ({ type: SET_LOG_ERROR_NOTIFICATION, - payload: { parameters }, + payload: { logErrorNotification }, }); export const SET_SSO_ENABLED = 'APPLICATION/SET_SSO_ENABLED'; diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 4a6f56f46..13dbf8e6f 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -54,7 +54,7 @@ const initialState = { waitForSSO: false, standalone: false, loggingMode: '0', - logErrorNotification: 3, + logErrorNotification: '3', }; export const applicationReducer = (state = initialState, action: { type: any; payload: any }) => { const { type, payload } = action; diff --git a/src/application/ApplicationSelectors.ts b/src/application/ApplicationSelectors.ts index 7a57b3bb5..4200bed73 100644 --- a/src/application/ApplicationSelectors.ts +++ b/src/application/ApplicationSelectors.ts @@ -96,11 +96,6 @@ export const applicationGetLoggingSettings = (state: any) => { return { loggingMode: state.application.loggingMode, loggingDatabase: state.application.loggingDatabase, - }; -}; - -export const applicationGetLogErrorNotification = (state: any) => { - return { logErrorNotification: state.application.logErrorNotification, }; }; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 0c201a373..61e07c352 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -42,8 +42,9 @@ import { setReportHelpModalOpen, } from './ApplicationActions'; import { version } from '../modal/AboutModal'; -import { applicationGetLoggingSettings, applicationIsStandalone, applicationGetLogErrorNotification } from './ApplicationSelectors'; +import { applicationGetLoggingSettings, applicationIsStandalone } from './ApplicationSelectors'; import { createUUID } from '../utils/uuid'; +import { valueIsRelationship } from '../chart/ChartUtils'; /** * Application Thunks (https://redux.js.org/usage/writing-logic-thunks) handle complex state manipulations. @@ -83,7 +84,7 @@ export const createConnectionThunk = 'ERR - connect to DB', database, '', - 'Error while trying to establish connection to Neo4j DB in ' +neodashMode+' mode at '+Date.now() + 'Error while trying to establish connection to Neo4j DB in ' +neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -104,7 +105,7 @@ export const createConnectionThunk = 'INF - connect to DB', database, '', - username +'established connection to Neo4j DB in ' +neodashMode+' mode at '+Date.now() + username +'established connection to Neo4j DB in ' +neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } @@ -387,6 +388,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: standaloneDashboardURL: '', loggingMode: '0', loggingDatabase: 'logs', + logErrorNotification: '3', }; try { config = await (await fetch('config.json')).json(); @@ -434,6 +436,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: dispatch(setLoggingMode(config.loggingMode)); dispatch(setLoggingDatabase(config.loggingDatabase)); + dispatch(setLogErrorNotification('3')); dispatch(setConnectionModalOpen(false)); @@ -644,7 +647,6 @@ export const createLogThunk = (dispatch: any, getState: any) => { try { const uuid = createUUID(); - // Generate a cypher query to save the log. const query = 'CREATE (n:_Neodash_Log) SET n.uuid = $uuid, n.user = $user, n.date = datetime(), n.neodash_mode = $neodashMode, n.action = $logAction, n.database = $logDatabase, n.dashboard = $logDashboard, n.message = $logMessage RETURN $uuid as uuid' @@ -670,26 +672,37 @@ export const createLogThunk = } else { //we only show error notification one time const state = getState() - const LogErrorNotification : number = Number(applicationGetLogErrorNotification(state)) - if (LogErrorNotification > 0 ) { - dispatch( + const loggingSettings = applicationGetLoggingSettings (state); + var LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) + console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') + if (LogErrorNotificationNum > 0 ) { + dispatch( createNotificationThunk( 'Error creating log', - `Please check logging configuration with your Neodash administrator` - ) + LogErrorNotificationNum>1 ? `Please check logging configuration with your Neodash administrator`:`Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` + ) ); - dispatch(setLogErrorNotification (LogErrorNotification - 1)); } - } + LogErrorNotificationNum = LogErrorNotificationNum-1 + dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); + } } ); } catch (e) { //we only show error notification 3 times const state = getState() - const LogErrorNotification : number = Number(applicationGetLogErrorNotification(state)) - if (LogErrorNotification > 0 ) { - dispatch(createNotificationThunk('Error creating log', e)); - dispatch(setLogErrorNotification (LogErrorNotification - 1)); + const loggingSettings = applicationGetLoggingSettings (state); + var LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) + console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') + if (LogErrorNotificationNum > 0 ) { + dispatch( + createNotificationThunk( + 'Error creating log', + LogErrorNotificationNum>1 ? `Please check logging configuration with your Neodash administrator`:`Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` + ) + ); } - } + LogErrorNotificationNum = LogErrorNotificationNum-1 + dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); + } }; \ No newline at end of file From 7b88f4eb2d1b1c232620fbf1b2c74ef0fbda7c4f Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Thu, 31 Aug 2023 08:33:13 -0400 Subject: [PATCH 10/36] minor refinement in documentation --- .../pages/developer-guide/configuration.adoc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/developer-guide/configuration.adoc b/docs/modules/ROOT/pages/developer-guide/configuration.adoc index 12fec8955..65f61982e 100644 --- a/docs/modules/ROOT/pages/developer-guide/configuration.adoc +++ b/docs/modules/ROOT/pages/developer-guide/configuration.adoc @@ -164,7 +164,19 @@ NeoDash mode (neodash_mmode), Action (action) and a short message (message). ⚠️ Logs are created using the credentials of the current user (either from logon or, if configured, from standaloneUsername), therefore, for it to work, administrator must ensure that every neodash user has write access to the -__Neodash_Log_ label in the database confugred in the loggingDatabase prameter (which must also be available on the same Neo4J instance where dashboards and data are stored). +__Neodash_Log_ label in the database confugred in the loggingDatabase prameter +(which must also be available on the same Neo4J instance where dashboards and +data are stored). This can be achieved by creating a standard NeoDashUser role in Neo4j with such permissions and assigning this role to every neoDash user. For more -details on Neo4J granular access control please refer to the link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/access-control/[product documentation] +details on Neo4J granular access control please refer to the link: +https://neo4j.com/docs/operations-manual/current/authentication-authorization/access-control/[product documentation] +In case of any misconfiguration (e.g. user not having grants to write in the +loggingDatabase), the application will show an error notification every time +a log creation fails (e.g. for a loggingMode set to '2', at user connection +attempt and any subsequent attempt to load or save a dashboard); the error will +not block the application flow (users will still be able to connect, load and +save dashboards normally); additionally, to limit user discomfort in such +scenario, the notification is shown for 3 times only in a single user session. +After 3rd time, a line will still be logged in the javascript console of user +browser but interactive notifictions will be suppressed. \ No newline at end of file From 7f97533d5c594a0f6d9405dee2acd78be6710293 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Thu, 31 Aug 2023 18:06:27 -0400 Subject: [PATCH 11/36] hide logout button in standalone mode --- .../header/DashboardHeaderLogoutButton.tsx | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/dashboard/header/DashboardHeaderLogoutButton.tsx b/src/dashboard/header/DashboardHeaderLogoutButton.tsx index c0fe918db..7d339b556 100644 --- a/src/dashboard/header/DashboardHeaderLogoutButton.tsx +++ b/src/dashboard/header/DashboardHeaderLogoutButton.tsx @@ -11,22 +11,20 @@ await StyleConfig.getInstance(); export const NeoLogoutButton = ({ standalone, onConnectionModalOpen }) => { return ( - - { - if (!standalone) { - onConnectionModalOpen(); - } - }} - size='large' - clean - > - - - + !standalone ? ( + + {onConnectionModalOpen();}} + size='large' + clean + > + + + + ):(<>) ); }; From f6e1a3a1b20bc0ae9b8f562eb3b4843d805bc7d3 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Thu, 31 Aug 2023 18:23:48 -0400 Subject: [PATCH 12/36] man merge d137081cfde4b2d5dcf1192b8a9be675e953bd0b --- src/application/ApplicationThunks.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 30b30cb57..d0e54f8ff 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -677,7 +677,7 @@ export const createLogThunk = //we only show error notification one time const state = getState() const loggingSettings = applicationGetLoggingSettings (state); - var LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) + let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') if (LogErrorNotificationNum > 0 ) { dispatch( @@ -687,7 +687,7 @@ export const createLogThunk = ) ); } - LogErrorNotificationNum = LogErrorNotificationNum-1 + LogErrorNotificationNum -= 1 dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); } } @@ -696,7 +696,7 @@ export const createLogThunk = //we only show error notification 3 times const state = getState() const loggingSettings = applicationGetLoggingSettings (state); - var LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) + let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') if (LogErrorNotificationNum > 0 ) { dispatch( @@ -706,7 +706,7 @@ export const createLogThunk = ) ); } - LogErrorNotificationNum = LogErrorNotificationNum-1 + LogErrorNotificationNum -= 1 dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); } }; \ No newline at end of file From ff6b05a24403b7bb73fc3deb93e560c2196b2ae7 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 1 Sep 2023 12:45:53 -0400 Subject: [PATCH 13/36] man chg 0484e44ed345e8a9008fcebc8753ddf881f038f1 --- src/dashboard/DashboardThunks.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index a7e8f6990..6ba1db59b 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -288,6 +288,7 @@ try { } } if (loggingSettings.loggingMode > '1'){ + const dashboard = JSON.parse(records[0]._fields[0]) dispatch( createLogThunk( driver, @@ -296,7 +297,7 @@ try { loguser, 'INF - load dashboard', database, - 'UUID:'+uuid, + 'Name:'+dashboard.title, 'User '+loguser+' Loaded dashboard by UUID in '+neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); From bccc09064a569d3ec93e56f6c2ab2332e8ec197a Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 8 Sep 2023 10:54:25 -0400 Subject: [PATCH 14/36] added configuration to allow multiple data DBs --- .../pages/developer-guide/configuration.adoc | 50 +++++++++++++++---- .../developer-guide/standalone-mode.adoc | 5 ++ .../developer-guide/state-management.adoc | 3 ++ public/config.json | 5 +- scripts/config-entrypoint.sh | 4 +- src/application/ApplicationActions.ts | 6 ++- src/application/ApplicationReducer.ts | 6 ++- src/application/ApplicationSelectors.ts | 2 + src/application/ApplicationThunks.ts | 4 ++ src/dashboard/header/DashboardHeader.tsx | 9 ++-- .../header/DashboardHeaderLogoutButton.tsx | 34 ++++++------- src/modal/ConnectionModal.tsx | 49 ++++++++++++++---- 12 files changed, 131 insertions(+), 46 deletions(-) diff --git a/docs/modules/ROOT/pages/developer-guide/configuration.adoc b/docs/modules/ROOT/pages/developer-guide/configuration.adoc index af9b9d071..fd448ae18 100644 --- a/docs/modules/ROOT/pages/developer-guide/configuration.adoc +++ b/docs/modules/ROOT/pages/developer-guide/configuration.adoc @@ -25,8 +25,10 @@ will look like this: "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", "standaloneDashboardURL": "", - "standaloneAllowLoad": true, - "standaloneLoadFromOtherDatabases": false + "standaloneAllowLoad": false, + "standaloneLoadFromOtherDatabases": false, + "standaloneMultiDatabase": false, + "standaloneDatabaseList": "neo4j" } .... @@ -87,16 +89,32 @@ inside Neo4j and would like to run a standalone mode deployment with a dashboard from a URL, set this parameter to the complete URL pointing to the dashboard JSON. -|standaloneAllowLoad |boolean |false |If set t yes the "Load Dashboard" -button will be enabled in standalone mode, allowing users to load -additional dashboards from Neo4J. +|standaloneAllowLoad |boolean |false |If set to yes the "Load Dashboard" +button will be enabled in standalone mode, allowing users to load +additional dashboards from Neo4J. This parameter is false by default +_unless you are using Neo4j Enterprise Edition_, which lets you use multiple +databases. *NOTE*: when Load is enabled in standalone mode, only Database is available as a source, not file. -|standaloneLoadFromOtherDatabases|boolean |false |If standaloneAllowLoad is -set to true, this parmeter enables or not users to load dashboards from -other databases than the one deifned in standaloneDashboardDatabase. If -standaloneAllowLoad is set to false this parameters has no effect. +|standaloneLoadFromOtherDatabases |boolean |false |If _standaloneAllowLoad_ is +set to true, this parmeter enables or not users to load dashboards from +other databases than the one deifned in _standaloneDashboardDatabase_. If +_standaloneAllowLoad_ is set to false this parameters has no effect. + +|standaloneMultiDatabase |boolean |false |If this parameter set to true, the +standalone configuration will ignore the _standaloneDatabase_ parameter and +allow users to choose which database to connect to in the login screen, among +the ones provided in _standaloneDatabaseList_, with a dropdown list. This +parameter is false by default _unless you are using Neo4j Enterprise Edition_, +which lets you use multiple databases. + +|standaloneDatabaseList |string |neo4j |If _standaloneMultiDatabase_ is +set to true, this parmeter must contain a comma separated list of database +names that will be displayed as options in the Database dropdown at user +login (e.g. 'neo4j,database1,database2' will populate the database dropdown +with the values 'neo4j','database1' and 'database2' in the connection screen). +If _standaloneMultiDatabase_ is set to false this parameters has no effect. |=== == Configuring SSO @@ -139,7 +157,7 @@ be enabled by changing the `standalone` config parameter: * If standalone mode is `false`, all other configuration parameters are ignored. NeoDash will run in Editor mode, and require a manual sign-in. * If standalone mode is `true`, NeoDash will read all configuration -parameters. A *fixed dashboard* will be auto-loaded, and no changes to +parameters. A *predefined dashboard* will be auto-loaded, and no changes to the dashboard can be made. There are two types of valid standalone deployments: ** A standalone deployment that *reads the fixed dashboard from Neo4j*. @@ -147,3 +165,15 @@ The `standaloneDashboardName` and `standaloneDashboardDatabase` config parameters are used to define these. ** A standalone deployment that *reads the fixed dashboard from a URL*. The `standaloneDashboardURL` config parameter is used to define this. + +* Standalone mode can also be configured to allow users load a different +dashboard after the predefined one is loaded (a `Load Dashboard` button +will be displayed on the right side of dashboard title). +The `standaloneAllowLoad` and `standaloneLoadFromOtherDatabases` are used +to define this. +* When allowing users to load dashboards dyamically in standalone mode, +they may also need to connect to different databases, depending on the +specific dashboard bing loaded. this can be enabled setting +`standaloneMultiDatabase` to true and providing a comma separated list +of the allowed database names in the`standaloneDatabaseList` parameter. + diff --git a/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc b/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc index 1259143ed..6a5392e6d 100644 --- a/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc +++ b/docs/modules/ROOT/pages/developer-guide/standalone-mode.adoc @@ -47,6 +47,11 @@ docker run -it --rm -p 5005:5005 \ -e standaloneDatabase="neo4j" \ -e standaloneDashboardName="My Dashboard" \ -e standaloneDashboardDatabase="dashboards" \ + -e standaloneDashboardURL="dashboards" \ + -e standaloneAllowLoad=false \ + -e standaloneLoadFromOtherDatabases=false \ + -e standaloneMultiDatabase=false \ + -e standaloneDatabaseList="neo4j" \ neo4jlabs/neodash .... diff --git a/docs/modules/ROOT/pages/developer-guide/state-management.adoc b/docs/modules/ROOT/pages/developer-guide/state-management.adoc index 78ff4aa74..653d4148e 100644 --- a/docs/modules/ROOT/pages/developer-guide/state-management.adoc +++ b/docs/modules/ROOT/pages/developer-guide/state-management.adoc @@ -134,8 +134,11 @@ standalone mode. "standaloneDatabase": "neo4j", "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", + "standaloneDashboardURL": "dashboards", "standaloneAllowLoad": false, "standaloneLoadFromOtherDatabases ": false, + "standaloneMultiDatabase": false, + "standaloneDatabaseList": "neo4j", "notificationIsDismissable": null } .... diff --git a/public/config.json b/public/config.json index 762820f28..9db04f5fa 100644 --- a/public/config.json +++ b/public/config.json @@ -10,5 +10,8 @@ "standaloneDashboardDatabase": "dashboards", "standaloneDashboardURL": "", "standaloneAllowLoad": false, - "standaloneLoadFromOtherDatabases": false + "standaloneLoadFromOtherDatabases": false, + "standaloneMultiDatabase": false, + "standaloneDatabaseList": "neo4j" + } diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index de2fc22b4..74297829e 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -17,5 +17,7 @@ echo " \ \"standaloneDashboardDatabase\": \"${standaloneDashboardDatabase:='neo4j'}\", \ \"standaloneDashboardURL\": \"${standaloneDashboardURL:=}\", \ \"standaloneAllowLoad\": \"${standaloneAllowLoad:=false}\", \ - \"standaloneLoadFromOtherDatabases\": \"${standaloneLoadFromOtherDatabases:=false}\" \ + \"standaloneLoadFromOtherDatabases\": \"${standaloneLoadFromOtherDatabases:=false}\", \ + \"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}\", \ + \"standaloneDatabaseList\": ${standaloneDatabaseList:='neo4j'}\" \ }" > /usr/share/nginx/html/config.json diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 296cdaac4..91b7fbb1c 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -145,7 +145,9 @@ export const setStandaloneEnabled = ( standaloneUsername: string, standalonePassword: string, standaloneAllowLoad: boolean, - standaloneLoadFromOtherDatabases: boolean + standaloneLoadFromOtherDatabases: boolean, + standaloneMultiDatabase: boolean, + standaloneDatabaseList: string ) => ({ type: SET_STANDALONE_ENABLED, payload: { @@ -161,6 +163,8 @@ export const setStandaloneEnabled = ( standalonePassword, standaloneAllowLoad, standaloneLoadFromOtherDatabases, + standaloneMultiDatabase, + standaloneDatabaseList, }, }); diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 6c8ad2338..3ddb6a004 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -135,7 +135,9 @@ export const applicationReducer = (state = initialState, action: { type: any; pa standaloneUsername, standalonePassword, standaloneAllowLoad, - standaloneLoadFromOtherDatabases + standaloneLoadFromOtherDatabases, + standaloneMultiDatabase, + standaloneDatabaseList, } = payload; state = update(state, { standalone: standalone, @@ -150,6 +152,8 @@ export const applicationReducer = (state = initialState, action: { type: any; pa standalonePassword: standalonePassword, standaloneAllowLoad: standaloneAllowLoad, standaloneLoadFromOtherDatabases: standaloneLoadFromOtherDatabases, + standaloneMultiDatabase: standaloneMultiDatabase, + standaloneDatabaseList: standaloneDatabaseList, }); return state; } diff --git a/src/application/ApplicationSelectors.ts b/src/application/ApplicationSelectors.ts index 81223fce5..f651dd42c 100644 --- a/src/application/ApplicationSelectors.ts +++ b/src/application/ApplicationSelectors.ts @@ -83,6 +83,8 @@ export const applicationGetStandaloneSettings = (state: any) => { standalonePassword: state.application.standalonePassword, standaloneAllowLoad: state.application.standaloneAllowLoad, standaloneLoadFromOtherDatabases: state.application.standaloneLoadFromOtherDatabases, + standaloneMultiDatabase: state.application.standaloneMultiDatabase, + standaloneDatabaseList: state.application.standaloneDatabaseList, }; }; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index e9661edcb..e671122e6 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -351,6 +351,8 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: standaloneDashboardURL: '', standaloneAllowLoad: false, standaloneLoadFromOtherDatabases: false, + standaloneMultiDatabase: false, + standaloneDatabaseList: 'neo4j', }; try { config = await (await fetch('config.json')).json(); @@ -395,6 +397,8 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: config.standalonePassword, config.standaloneAllowLoad, config.standaloneLoadFromOtherDatabases, + config.standaloneMultiDatabase, + config.standaloneDatabaseList, ) ); dispatch(setConnectionModalOpen(false)); diff --git a/src/dashboard/header/DashboardHeader.tsx b/src/dashboard/header/DashboardHeader.tsx index a218bc4be..b981540c3 100644 --- a/src/dashboard/header/DashboardHeader.tsx +++ b/src/dashboard/header/DashboardHeader.tsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { setDashboardTitle } from '../DashboardActions'; import { getDashboardSettings, getDashboardTheme, getDashboardTitle, getPages } from '../DashboardSelectors'; import { setConnectionModalOpen } from '../../application/ApplicationActions'; -import { applicationIsStandalone } from '../../application/ApplicationSelectors'; +import { applicationGetStandaloneSettings } from '../../application/ApplicationSelectors'; import { getDashboardIsEditable, getPageNumber } from '../../settings/SettingsSelectors'; import { NeoDashboardHeaderLogo } from './DashboardHeaderLogo'; import NeoAboutButton from './DashboardHeaderAboutButton'; @@ -15,7 +15,7 @@ import { DASHBOARD_HEADER_BUTTON_COLOR } from '../../config/ApplicationConfig'; import { Tooltip } from '@mui/material'; export const NeoDashboardHeader = ({ - standalone, + standaloneSettings, dashboardTitle, connection, settings, @@ -45,7 +45,6 @@ export const NeoDashboardHeader = ({ useEffect(() => { setTheme(isDarkMode ? 'dark' : 'light'); }, [isDarkMode]); - const content = (
@@ -72,7 +71,7 @@ export const NeoDashboardHeader = ({ {downloadImageEnabled && } - +
@@ -84,7 +83,7 @@ export const NeoDashboardHeader = ({ const mapStateToProps = (state) => ({ dashboardTitle: getDashboardTitle(state), - standalone: applicationIsStandalone(state), + standaloneSettings: applicationGetStandaloneSettings(state), pages: getPages(state), settings: getDashboardSettings(state), editable: getDashboardIsEditable(state), diff --git a/src/dashboard/header/DashboardHeaderLogoutButton.tsx b/src/dashboard/header/DashboardHeaderLogoutButton.tsx index c0fe918db..b8dbad28f 100644 --- a/src/dashboard/header/DashboardHeaderLogoutButton.tsx +++ b/src/dashboard/header/DashboardHeaderLogoutButton.tsx @@ -9,24 +9,24 @@ import { ArrowRightOnRectangleIconOutline } from '@neo4j-ndl/react/icons'; await StyleConfig.getInstance(); -export const NeoLogoutButton = ({ standalone, onConnectionModalOpen }) => { +export const NeoLogoutButton = ({ standaloneSettings, onConnectionModalOpen }) => { return ( - - { - if (!standalone) { - onConnectionModalOpen(); - } - }} - size='large' - clean - > - - - + (standaloneSettings.standalone && !standaloneSettings.standaloneMultiDatabase) ? + (<>) : + ( + + { onConnectionModalOpen(); }} + size='large' + clean + > + + + + ) ); }; diff --git a/src/modal/ConnectionModal.tsx b/src/modal/ConnectionModal.tsx index ad98a65b3..0769c8933 100644 --- a/src/modal/ConnectionModal.tsx +++ b/src/modal/ConnectionModal.tsx @@ -25,7 +25,7 @@ export default function NeoConnectionModal({ const [username, setUsername] = React.useState(connection.username); const [password, setPassword] = React.useState(connection.password); const [database, setDatabase] = React.useState(connection.database); - + // Make sure local vars are updated on external connection updates. useEffect(() => { setProtocol(connection.protocol); @@ -41,6 +41,14 @@ export default function NeoConnectionModal({ }, [JSON.stringify(ssoSettings)]); const discoveryAPIUrl = ssoSettings && ssoSettings.ssoDiscoveryUrl; + + //sice config is loaded asynchronously, value may not be yet defined when this runs for first time + let databaseList = ['neo4j'] + try{ + databaseList = standaloneSettings.standaloneDatabaseList.split(',') + } + catch(e){ + } return ( <> @@ -126,15 +134,36 @@ export default function NeoConnectionModal({ Neo4j Aura databases require a neo4j+s protocol. Your current configuration may not work. ) : null} - setDatabase(e.target.value)} - label='Database (optional)' - placeholder='neo4j' - fluid - /> + { + !standalone ? ( + setDatabase(e.target.value)} + label='Database (optional)' + placeholder='neo4j' + fluid + /> + ) : ( + {setDatabase(newValue.value);}, + //if application is running standalone and standaloneLoadFromOtherDatabases is not enabled, we do not allow changing database + options: databaseList.map((option) => ({ + label: option, + value: option, + })), + value: { label: database, value: database }, + menuPlacement: 'auto', + }} + fluid + > + ) + } {!ssoVisible ? ( Date: Fri, 8 Sep 2023 13:26:56 -0400 Subject: [PATCH 15/36] fix on config-entrypoint and reorder parameters --- public/config.json | 7 +++---- scripts/config-entrypoint.sh | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/public/config.json b/public/config.json index ed7224db5..a773f35b8 100644 --- a/public/config.json +++ b/public/config.json @@ -9,11 +9,10 @@ "standaloneDashboardName": "My Dashboard", "standaloneDashboardDatabase": "dashboards", "standaloneDashboardURL": "", - "loggingMode": "0", - "loggingDatabase": "logs", "standaloneAllowLoad": false, "standaloneLoadFromOtherDatabases": false, "standaloneMultiDatabase": false, - "standaloneDatabaseList": "neo4j" - + "standaloneDatabaseList": "neo4j", + "loggingMode": "0", + "loggingDatabase": "logs" } diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index f9030ce46..51f0600c7 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -6,7 +6,7 @@ echo " \ { \ \"ssoEnabled\": ${ssoEnabled:=false}, \ \"ssoDiscoveryUrl\": \"${ssoDiscoveryUrl:='https://example.com'}\", \ - \"standalone\": "${standalone:=false}", \ + \"standalone\": ${standalone:=false}, \ \"standaloneProtocol\": \"${standaloneProtocol:='neo4j+s'}\", \ \"standaloneHost\": \"${standaloneHost:='test.databases.neo4j.io'}\", \ \"standalonePort\": ${standalonePort:=7687}, \ @@ -16,10 +16,10 @@ echo " \ \"standaloneDashboardName\": \"${standaloneDashboardName:='My Dashboard'}\", \ \"standaloneDashboardDatabase\": \"${standaloneDashboardDatabase:='neo4j'}\", \ \"standaloneDashboardURL\": \"${standaloneDashboardURL:=}\", \ + \"standaloneAllowLoad\": ${standaloneAllowLoad:=false}, \ + \"standaloneLoadFromOtherDatabases\": ${standaloneLoadFromOtherDatabases:=false}, \ + \"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}, \ + \"standaloneDatabaseList\": \"${standaloneDatabaseList:='neo4j'}\", \ \"loggingMode\": \"${loggingMode:='0'}\", \ - \"loggingMode\": \"${loggingDatabase:='logs'}\", \ - \"standaloneAllowLoad\": \"${standaloneAllowLoad:=false}\", \ - \"standaloneLoadFromOtherDatabases\": \"${standaloneLoadFromOtherDatabases:=false}\", \ - \"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}\", \ - \"standaloneDatabaseList\": ${standaloneDatabaseList:='neo4j'}\" \ - }" > /usr/share/nginx/html/config.json + \"loggingMode\": \"${loggingDatabase:='logs'}\" \ + }" > /usr/share/nginx/html/config.json From 610af3c12f05aa7995c6f351173b3e1da5decd88 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Tue, 19 Sep 2023 22:22:06 -0400 Subject: [PATCH 16/36] bugfix on config-entrypoint.sh --- scripts/config-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index 51f0600c7..7fcd33822 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -21,5 +21,5 @@ echo " \ \"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}, \ \"standaloneDatabaseList\": \"${standaloneDatabaseList:='neo4j'}\", \ \"loggingMode\": \"${loggingMode:='0'}\", \ - \"loggingMode\": \"${loggingDatabase:='logs'}\" \ + \"loggingDatabase\": \"${loggingDatabase:='logs'}\" \ }" > /usr/share/nginx/html/config.json From aaa6bbd469e1669b7dd0a1c43b917904740a8b56 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Wed, 27 Sep 2023 15:25:38 -0400 Subject: [PATCH 17/36] fix to update standaloneDB for standaloneMultiDB --- src/dashboard/header/DashboardHeader.tsx | 2 +- src/modal/ConnectionModal.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dashboard/header/DashboardHeader.tsx b/src/dashboard/header/DashboardHeader.tsx index b981540c3..8f43003fa 100644 --- a/src/dashboard/header/DashboardHeader.tsx +++ b/src/dashboard/header/DashboardHeader.tsx @@ -51,7 +51,7 @@ export const NeoDashboardHeader = ({
diff --git a/src/modal/ConnectionModal.tsx b/src/modal/ConnectionModal.tsx index 81d52b41c..d31063ac0 100644 --- a/src/modal/ConnectionModal.tsx +++ b/src/modal/ConnectionModal.tsx @@ -2,6 +2,7 @@ import React, { useEffect } from 'react'; import { SSOLoginButton } from '../component/sso/SSOLoginButton'; import { Button, Dialog, Switch, TextInput, Dropdown, TextLink } from '@neo4j-ndl/react'; import { PlayIconOutline } from '@neo4j-ndl/react/icons'; + /** * Configures setting the current Neo4j database connection for the dashboard. */ @@ -25,6 +26,7 @@ export default function NeoConnectionModal({ const [username, setUsername] = React.useState(connection.username); const [password, setPassword] = React.useState(connection.password); const [database, setDatabase] = React.useState(connection.database); + const [standaloneDatabase, setStandaloneDatabases] = React.useState([standaloneSettings.standalone? standaloneSettings.standaloneDatabase : 'neo4j']); // Make sure local vars are updated on external connection updates. useEffect(() => { @@ -151,8 +153,10 @@ export default function NeoConnectionModal({ label='Database' type='select' selectProps={{ - onChange: (newValue) => {setDatabase(newValue.value);}, - //if application is running standalone and standaloneLoadFromOtherDatabases is not enabled, we do not allow changing database + onChange: (newValue) => { + setDatabase(newValue.value); + setStandaloneDatabases(newValue.value); + }, options: databaseList.map((option) => ({ label: option, value: option, From a0f06e49cb85243d41f4f775d40e03450bb947e9 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 01:18:37 -0400 Subject: [PATCH 18/36] added useffect in card.tsx to save DB --- src/card/Card.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/card/Card.tsx b/src/card/Card.tsx index fe6ffe0d9..4e14d7aea 100644 --- a/src/card/Card.tsx +++ b/src/card/Card.tsx @@ -54,6 +54,7 @@ const NeoCard = ({ onDatabaseChanged, // action to take when the user changes the database related to the card loadDatabaseListFromNeo4j, // Thunk to get the list of databases createNotification, // Thunk to create a global notification pop-up. + saveDatabase, // added to ensure the database is always saved in the session as soon as the card is created (and added in the dahsboard JSON) }) => { // Will be used to fetch the list of current databases const { driver } = useContext(Neo4jContext); @@ -117,6 +118,10 @@ const NeoCard = ({ setCollapseTimeout(report.collapseTimeout); }, [report.collapseTimeout]); + useEffect(() => { + saveDatabase(id, database); + }, [database]); + // TODO - get rid of some of the props-drilling here... const component = (
({ dispatch(toggleReportSettings(id)); }, onDatabaseChanged: (id: any, database: any) => { - dispatch(updateReportDatabaseThunk(id, database)); + dispatch(updateReportDatabaseThunk(id, database)); }, createNotification: (title: any, message: any) => { dispatch(createNotificationThunk(title, message)); }, loadDatabaseListFromNeo4j: (driver, callback) => dispatch(loadDatabaseListFromNeo4jThunk(driver, callback)), + saveDatabase: (id: any, database: any) => { + dispatch(updateReportDatabaseThunk(id, database)); + }, }); export default connect(mapStateToProps, mapDispatchToProps)(NeoCard); From d930e389dd0f01d4ee99e834f05413abc7d69509 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 12:35:08 -0400 Subject: [PATCH 19/36] query modified in saveDashboardThunks --- src/dashboard/DashboardThunks.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index 6ba1db59b..346e4823f 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -170,7 +170,12 @@ export const saveDashboardToNeo4jThunk = // Generate a cypher query to save the dashboard. const query = overwrite - ? 'OPTIONAL MATCH (n:_Neodash_Dashboard{title:$title}) DELETE n WITH 1 as X LIMIT 1 CREATE (n:_Neodash_Dashboard) SET n.uuid = $uuid, n.title = $title, n.version = $version, n.user = $user, n.content = $content, n.date = datetime($date) RETURN $uuid as uuid' + ? 'MATCH (n:_Neodash_Dashboard{title:$title}) WITH n ORDER BY n.date DESC LIMIT 1 '+ + ' FOREACH (ignoreMe IN CASE WHEN n IS NOT NULL THEN [1] ELSE [] END |'+ + ' SET n.uuid = $uuid, n.version = $version, n.user = $user, n.content = $content, n.date = datetime($date))'+ + ' FOREACH (ignoreMe IN CASE WHEN n IS NULL THEN [1] ELSE [] END |'+ + ' CREATE (m:_Neodash_Dashboard {uuid: $uuid, title: $title, version: $version, user: $user, content: $content, date: datetime($date)}))'+ + 'RETURN COALESCE(n.uuid, $uuid) as uuid' : 'CREATE (n:_Neodash_Dashboard) SET n.uuid = $uuid, n.title = $title, n.version = $version, n.user = $user, n.content = $content, n.date = datetime($date) RETURN $uuid as uuid'; const parameters = { From 118d21a4fee64166d131ec155563a1a6a2f3a6dd Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 17:10:01 -0400 Subject: [PATCH 20/36] updated config-entrypoint --- scripts/config-entrypoint.sh | 2 ++ src/config/ApplicationConfig.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index 7fcd33822..7b77d147c 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -23,3 +23,5 @@ echo " \ \"loggingMode\": \"${loggingMode:='0'}\", \ \"loggingDatabase\": \"${loggingDatabase:='logs'}\" \ }" > /usr/share/nginx/html/config.json + +echo "${styleConfigJson:={\}}" > /usr/share/nginx/html/style.config.json diff --git a/src/config/ApplicationConfig.ts b/src/config/ApplicationConfig.ts index 20869f3f9..1132f0b4b 100644 --- a/src/config/ApplicationConfig.ts +++ b/src/config/ApplicationConfig.ts @@ -18,7 +18,7 @@ export const DASHBOARD_HEADER_BUTTON_COLOR = styleConfig?.style?.DASHBOARD_HEADE export const DASHBOARD_HEADER_TITLE_COLOR = styleConfig?.style?.DASHBOARD_HEADER_TITLE_COLOR || '#FFFFFF'; // '#FFFFFF' export const DASHBOARD_HEADER_BRAND_LOGO = - styleConfig?.style?.DASHBOARD_HEADER_BRAND_LOGO || 'neo4j-icon-color-full.png'; + styleConfig?.style?.https://www.novartis.com/themes/custom/nvs_arctic/logo.svg || 'neo4j-icon-color-full.png'; export const IS_CUSTOM_LOGO = Boolean(styleConfig?.style?.DASHBOARD_HEADER_BRAND_LOGO); From e57d8aa0641ab22b7066f2a0206c3d0777d78cb2 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 18:16:06 -0400 Subject: [PATCH 21/36] added config parameter to set cusom Header --- public/config.json | 3 ++- scripts/config-entrypoint.sh | 3 ++- src/application/Application.tsx | 2 ++ src/application/ApplicationActions.ts | 6 ++++++ src/application/ApplicationReducer.ts | 6 ++++++ src/application/ApplicationSelectors.ts | 4 ++++ src/application/ApplicationThunks.ts | 4 ++++ src/dashboard/header/DashboardHeader.tsx | 8 ++++++-- 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/public/config.json b/public/config.json index a773f35b8..2e78dfba9 100644 --- a/public/config.json +++ b/public/config.json @@ -14,5 +14,6 @@ "standaloneMultiDatabase": false, "standaloneDatabaseList": "neo4j", "loggingMode": "0", - "loggingDatabase": "logs" + "loggingDatabase": "logs", + "customHeader": "" } diff --git a/scripts/config-entrypoint.sh b/scripts/config-entrypoint.sh index 7fcd33822..d34c7788d 100644 --- a/scripts/config-entrypoint.sh +++ b/scripts/config-entrypoint.sh @@ -21,5 +21,6 @@ echo " \ \"standaloneMultiDatabase\": ${standaloneMultiDatabase:=false}, \ \"standaloneDatabaseList\": \"${standaloneDatabaseList:='neo4j'}\", \ \"loggingMode\": \"${loggingMode:='0'}\", \ - \"loggingDatabase\": \"${loggingDatabase:='logs'}\" \ + \"loggingDatabase\": \"${loggingDatabase:='logs'}\", \ + \"customHeader\": \"${customHeader:=}\" \ }" > /usr/share/nginx/html/config.json diff --git a/src/application/Application.tsx b/src/application/Application.tsx index cd8d9759a..0d780ac99 100644 --- a/src/application/Application.tsx +++ b/src/application/Application.tsx @@ -34,6 +34,7 @@ import { setReportHelpModalOpen, setWaitForSSO, setWelcomeScreenOpen, + setCustomHeader, } from '../application/ApplicationActions'; import { resetDashboardState } from '../dashboard/DashboardActions'; import { NeoDashboardPlaceholder } from '../dashboard/placeholder/DashboardPlaceholder'; @@ -93,6 +94,7 @@ const Application = ({ onConnectionModalClose, onSSOAttempt, themeMode, + customHeader, }) => { const [initialized, setInitialized] = React.useState(false); diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 16b519e4c..1476462da 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -233,3 +233,9 @@ export const setParametersToLoadAfterConnecting = (parameters: any) => ({ type: SET_PARAMETERS_TO_LOAD_AFTER_CONNECTING, payload: { parameters }, }); + +export const SET_CUSTOM_HEADER = 'APPLICATION/SET_CUSTOM_HEADER'; +export const setCustomHeader = (customHeader: any) => ({ + type: SET_CUSTOM_HEADER, + payload: { customHeader }, +}); diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 8e5b632ad..a2a56afbe 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -29,6 +29,7 @@ import { SET_LOG_ERROR_NOTIFICATION, SET_WAIT_FOR_SSO, SET_WELCOME_SCREEN_OPEN, + SET_CUSTOM_HEADER, } from './ApplicationActions'; const update = (state, mutations) => Object.assign({}, state, mutations); @@ -267,5 +268,10 @@ export const applicationReducer = (state = initialState, action: { type: any; pa default: { return state; } + case SET_CUSTOM_HEADER: { + const { customHeader } = payload; + state = update(state, { customHeader: customHeader }); + return state; + } } }; diff --git a/src/application/ApplicationSelectors.ts b/src/application/ApplicationSelectors.ts index 670734ac8..62078196a 100644 --- a/src/application/ApplicationSelectors.ts +++ b/src/application/ApplicationSelectors.ts @@ -127,3 +127,7 @@ export const applicationGetDebugState = (state: any) => { } return copy; }; + +export const applicationGetCustomHeader = (state: any) => { + return state.application.customHeader; +}; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index aab36971f..648aaab44 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -40,6 +40,7 @@ import { setWaitForSSO, setParametersToLoadAfterConnecting, setReportHelpModalOpen, + setCustomHeader, } from './ApplicationActions'; import { version } from '../modal/AboutModal'; import { applicationGetLoggingSettings, applicationIsStandalone } from './ApplicationSelectors'; @@ -393,6 +394,7 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: standaloneLoadFromOtherDatabases: false, standaloneMultiDatabase: false, standaloneDatabaseList: 'neo4j', + customHeader: '', }; try { config = await (await fetch('config.json')).json(); @@ -448,6 +450,8 @@ export const loadApplicationConfigThunk = () => async (dispatch: any, getState: dispatch(setConnectionModalOpen(false)); + dispatch(setCustomHeader(config.customHeader)); + // Auto-upgrade the dashboard version if an old version is cached. if (state.dashboard && state.dashboard.version !== NEODASH_VERSION) { if (state.dashboard.version == '2.0') { diff --git a/src/dashboard/header/DashboardHeader.tsx b/src/dashboard/header/DashboardHeader.tsx index 8f43003fa..31f5bab0d 100644 --- a/src/dashboard/header/DashboardHeader.tsx +++ b/src/dashboard/header/DashboardHeader.tsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { setDashboardTitle } from '../DashboardActions'; import { getDashboardSettings, getDashboardTheme, getDashboardTitle, getPages } from '../DashboardSelectors'; import { setConnectionModalOpen } from '../../application/ApplicationActions'; -import { applicationGetStandaloneSettings } from '../../application/ApplicationSelectors'; +import { applicationGetStandaloneSettings, applicationGetCustomHeader } from '../../application/ApplicationSelectors'; import { getDashboardIsEditable, getPageNumber } from '../../settings/SettingsSelectors'; import { NeoDashboardHeaderLogo } from './DashboardHeaderLogo'; import NeoAboutButton from './DashboardHeaderAboutButton'; @@ -17,6 +17,7 @@ import { Tooltip } from '@mui/material'; export const NeoDashboardHeader = ({ standaloneSettings, dashboardTitle, + customHeader, connection, settings, onConnectionModalOpen, @@ -29,6 +30,8 @@ export const NeoDashboardHeader = ({ const downloadImageEnabled = settings ? settings.downloadImageEnabled : false; const [dashboardTitleText, setDashboardTitleText] = React.useState(dashboardTitle); + const [customHeaderText, setCustomHeader] = React.useState(customHeader); + const [isDarkMode, setDarkMode] = React.useState(themeMode !== 'light'); const toggleDarkMode = (checked: boolean) => { @@ -51,7 +54,7 @@ export const NeoDashboardHeader = ({
@@ -84,6 +87,7 @@ export const NeoDashboardHeader = ({ const mapStateToProps = (state) => ({ dashboardTitle: getDashboardTitle(state), standaloneSettings: applicationGetStandaloneSettings(state), + customHeader: applicationGetCustomHeader(state), pages: getPages(state), settings: getDashboardSettings(state), editable: getDashboardIsEditable(state), From 2dc5484ce586454948450f657aaf7047d0bfb7ab Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 18:28:45 -0400 Subject: [PATCH 22/36] documentation --- docs/modules/ROOT/pages/developer-guide/configuration.adoc | 5 +++++ public/config.json | 2 +- public/style.config.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/developer-guide/configuration.adoc b/docs/modules/ROOT/pages/developer-guide/configuration.adoc index 8ed8c3e14..ab728c4a3 100644 --- a/docs/modules/ROOT/pages/developer-guide/configuration.adoc +++ b/docs/modules/ROOT/pages/developer-guide/configuration.adoc @@ -31,6 +31,7 @@ will look like this: "standaloneDatabaseList": "neo4j" "loggingMode": "0", "loggingDatabase": "logs", + "customHeader": "", } .... @@ -132,6 +133,10 @@ must be granted to enble any user to create logs. |loggingDatabase |string |neo4j |When loggingMode is set to anything else than '0', the database to use for logging. Log records (nodes) will be created in this database. + +|customHeader |string |none |When set the dashboard header will display +the prameter value as a fixed string, otherwise it will display the host +and port of current connection. |=== == Configuring SSO diff --git a/public/config.json b/public/config.json index 2e78dfba9..6b0bcab25 100644 --- a/public/config.json +++ b/public/config.json @@ -15,5 +15,5 @@ "standaloneDatabaseList": "neo4j", "loggingMode": "0", "loggingDatabase": "logs", - "customHeader": "" + "customHeader": "B-Raven.com" } diff --git a/public/style.config.json b/public/style.config.json index 0967ef424..a45e212b2 100644 --- a/public/style.config.json +++ b/public/style.config.json @@ -1 +1 @@ -{} +{"https://fileserviceuploadsperm.blob.core.windows.net/files/file-XxcBUZewIjFojhjHXNFP191l?se=2023-10-13T23%3A27%3A09Z&sp=r&sv=2021-08-06&sr=b&rscd=attachment%3B%20filename%3D431265c9-1c38-4f45-8a74-791d96649f6b.png&sig=Yjqd%2BXra30D2XjzED647r/rT4Wqruh24QfzzioJ2fuw%3D"} From 13af4c6d762760c35ba0bbf025b626eb5147d761 Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 18:33:02 -0400 Subject: [PATCH 23/36] fix dirt in style.config --- public/style.config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/style.config.json b/public/style.config.json index a45e212b2..9e26dfeeb 100644 --- a/public/style.config.json +++ b/public/style.config.json @@ -1 +1 @@ -{"https://fileserviceuploadsperm.blob.core.windows.net/files/file-XxcBUZewIjFojhjHXNFP191l?se=2023-10-13T23%3A27%3A09Z&sp=r&sv=2021-08-06&sr=b&rscd=attachment%3B%20filename%3D431265c9-1c38-4f45-8a74-791d96649f6b.png&sig=Yjqd%2BXra30D2XjzED647r/rT4Wqruh24QfzzioJ2fuw%3D"} +{} \ No newline at end of file From 821c06f305df636830c2eb2a16f97e15c4e130ae Mon Sep 17 00:00:00 2001 From: BlackRaven Date: Fri, 13 Oct 2023 18:35:29 -0400 Subject: [PATCH 24/36] fix dirt in config.json --- public/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/config.json b/public/config.json index 6b0bcab25..2e78dfba9 100644 --- a/public/config.json +++ b/public/config.json @@ -15,5 +15,5 @@ "standaloneDatabaseList": "neo4j", "loggingMode": "0", "loggingDatabase": "logs", - "customHeader": "B-Raven.com" + "customHeader": "" } From f3a234d549f8a8d3a9a56627d7da8ebe266c0787 Mon Sep 17 00:00:00 2001 From: BlackRaven <35220904+8Rav3n@users.noreply.github.com> Date: Fri, 13 Oct 2023 18:53:47 -0400 Subject: [PATCH 25/36] Update ApplicationConfig.ts fix dirt --- src/config/ApplicationConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/ApplicationConfig.ts b/src/config/ApplicationConfig.ts index 1132f0b4b..20869f3f9 100644 --- a/src/config/ApplicationConfig.ts +++ b/src/config/ApplicationConfig.ts @@ -18,7 +18,7 @@ export const DASHBOARD_HEADER_BUTTON_COLOR = styleConfig?.style?.DASHBOARD_HEADE export const DASHBOARD_HEADER_TITLE_COLOR = styleConfig?.style?.DASHBOARD_HEADER_TITLE_COLOR || '#FFFFFF'; // '#FFFFFF' export const DASHBOARD_HEADER_BRAND_LOGO = - styleConfig?.style?.https://www.novartis.com/themes/custom/nvs_arctic/logo.svg || 'neo4j-icon-color-full.png'; + styleConfig?.style?.DASHBOARD_HEADER_BRAND_LOGO || 'neo4j-icon-color-full.png'; export const IS_CUSTOM_LOGO = Boolean(styleConfig?.style?.DASHBOARD_HEADER_BRAND_LOGO); From 5d30622343f301f0971971e07e782f3982cbd6d8 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 14 Nov 2023 17:29:41 +0100 Subject: [PATCH 26/36] moving logging logic to its own reducer --- public/config.json | 14 ++-- src/application/Application.tsx | 1 - src/application/ApplicationActions.ts | 18 ----- src/application/ApplicationReducer.ts | 26 ++++--- src/application/ApplicationSelectors.ts | 8 -- src/application/ApplicationThunks.ts | 81 +++------------------ src/application/logging/LoggingActions.ts | 19 +++++ src/application/logging/LoggingReducer.ts | 41 +++++++++++ src/application/logging/LoggingSelectors.ts | 8 ++ src/application/logging/LoggingThunk.ts | 70 ++++++++++++++++++ src/dashboard/DashboardThunks.ts | 7 +- 11 files changed, 174 insertions(+), 119 deletions(-) create mode 100644 src/application/logging/LoggingActions.ts create mode 100644 src/application/logging/LoggingReducer.ts create mode 100644 src/application/logging/LoggingSelectors.ts create mode 100644 src/application/logging/LoggingThunk.ts diff --git a/public/config.json b/public/config.json index 2e78dfba9..e86c2255b 100644 --- a/public/config.json +++ b/public/config.json @@ -2,18 +2,18 @@ "ssoEnabled": false, "ssoDiscoveryUrl": "https://example.com", "standalone": false, - "standaloneProtocol": "neo4j", + "standaloneProtocol": "bolt", "standaloneHost": "localhost", - "standalonePort": "7687", + "standalonePort": "11003", "standaloneDatabase": "neo4j", "standaloneDashboardName": "My Dashboard", - "standaloneDashboardDatabase": "dashboards", + "standaloneDashboardDatabase": "neo4j", "standaloneDashboardURL": "", - "standaloneAllowLoad": false, + "standaloneAllowLoad": true, "standaloneLoadFromOtherDatabases": false, - "standaloneMultiDatabase": false, + "standaloneMultiDatabase": true, "standaloneDatabaseList": "neo4j", - "loggingMode": "0", + "loggingMode": "1", "loggingDatabase": "logs", - "customHeader": "" + "customHeader": "Test" } diff --git a/src/application/Application.tsx b/src/application/Application.tsx index 0d780ac99..49d78ca1e 100644 --- a/src/application/Application.tsx +++ b/src/application/Application.tsx @@ -34,7 +34,6 @@ import { setReportHelpModalOpen, setWaitForSSO, setWelcomeScreenOpen, - setCustomHeader, } from '../application/ApplicationActions'; import { resetDashboardState } from '../dashboard/DashboardActions'; import { NeoDashboardPlaceholder } from '../dashboard/placeholder/DashboardPlaceholder'; diff --git a/src/application/ApplicationActions.ts b/src/application/ApplicationActions.ts index 1476462da..64f591a0c 100644 --- a/src/application/ApplicationActions.ts +++ b/src/application/ApplicationActions.ts @@ -180,24 +180,6 @@ export const setStandaloneDashboardDatabase = (dashboardDatabase: string) => ({ payload: { dashboardDatabase }, }); -export const SET_LOGGING_MODE = 'APPLICATION/SET_LOGGING_MODE'; -export const setLoggingMode = (loggingMode: string) => ({ - type: SET_LOGGING_MODE, - payload: { loggingMode }, -}); - -export const SET_LOGGING_DATABASE = 'APPLICATION/SET_LOGGING_DATABASE'; -export const setLoggingDatabase = (loggingDatabase: string) => ({ - type: SET_LOGGING_DATABASE, - payload: { loggingDatabase }, -}); - -export const SET_LOG_ERROR_NOTIFICATION = 'APPLICATION/SET_LOG_ERROR_NOTIFICATION'; -export const setLogErrorNotification = (logErrorNotification: any) => ({ - type: SET_LOG_ERROR_NOTIFICATION, - payload: { logErrorNotification }, -}); - export const SET_SSO_ENABLED = 'APPLICATION/SET_SSO_ENABLED'; export const setSSOEnabled = (enabled: boolean, discoveryUrl: string) => ({ type: SET_SSO_ENABLED, diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index a2a56afbe..2b1fba034 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -24,13 +24,17 @@ import { SET_STANDALONE_DASHBOARD_DATEBASE, SET_STANDALONE_ENABLED, SET_STANDALONE_MODE, - SET_LOGGING_MODE, - SET_LOGGING_DATABASE, - SET_LOG_ERROR_NOTIFICATION, SET_WAIT_FOR_SSO, SET_WELCOME_SCREEN_OPEN, SET_CUSTOM_HEADER, } from './ApplicationActions'; +import { + SET_LOGGING_MODE, + SET_LOGGING_DATABASE, + SET_LOG_ERROR_NOTIFICATION, + LOGGING_PREFIX +} from "./logging/LoggingActions"; +import { loggingReducer, LOGGING_INITIAL_STATE } from './logging/LoggingReducer'; const update = (state, mutations) => Object.assign({}, state, mutations); @@ -54,8 +58,7 @@ const initialState = { dashboardToLoadAfterConnecting: null, waitForSSO: false, standalone: false, - loggingMode: '0', - logErrorNotification: '3', + logging: LOGGING_INITIAL_STATE }; export const applicationReducer = (state = initialState, action: { type: any; payload: any }) => { const { type, payload } = action; @@ -63,7 +66,12 @@ export const applicationReducer = (state = initialState, action: { type: any; pa if (!action.type.startsWith('APPLICATION/')) { return state; } - + if (action.type.startsWith(LOGGING_PREFIX)){ + const enrichedPayload = update(payload, { logging: state.logging }); + const enrichedAction = { type, payload: enrichedPayload }; + return {...state, logging: loggingReducer(state.logging, enrichedAction) } + } + // Application state updates are handled here. switch (type) { case CREATE_NOTIFICATION: { @@ -265,13 +273,13 @@ export const applicationReducer = (state = initialState, action: { type: any; pa }); return state; } - default: { - return state; - } case SET_CUSTOM_HEADER: { const { customHeader } = payload; state = update(state, { customHeader: customHeader }); return state; } + default: { + return state; + } } }; diff --git a/src/application/ApplicationSelectors.ts b/src/application/ApplicationSelectors.ts index 62078196a..e823f34f4 100644 --- a/src/application/ApplicationSelectors.ts +++ b/src/application/ApplicationSelectors.ts @@ -96,14 +96,6 @@ export const applicationGetStandaloneSettings = (state: any) => { }; }; -export const applicationGetLoggingSettings = (state: any) => { - return { - loggingMode: state.application.loggingMode, - loggingDatabase: state.application.loggingDatabase, - logErrorNotification: state.application.logErrorNotification, - }; -}; - export const applicationHasWelcomeScreenOpen = (state: any) => { return state.application.welcomeScreenOpen; }; diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 648aaab44..a8aee62e6 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -34,18 +34,21 @@ import { setAboutModalOpen, setStandaloneMode, setStandaloneDashboardDatabase, - setLoggingMode, - setLoggingDatabase, - setLogErrorNotification, setWaitForSSO, setParametersToLoadAfterConnecting, setReportHelpModalOpen, setCustomHeader, } from './ApplicationActions'; +import { + setLoggingMode, + setLoggingDatabase, + setLogErrorNotification +} from "./logging/LoggingActions"; import { version } from '../modal/AboutModal'; -import { applicationGetLoggingSettings, applicationIsStandalone } from './ApplicationSelectors'; -import { createUUID } from '../utils/uuid'; +import { applicationIsStandalone } from './ApplicationSelectors'; +import { applicationGetLoggingSettings } from "./logging/LoggingSelectors"; import { valueIsRelationship } from '../chart/ChartUtils'; +import { createLogThunk } from './logging/LoggingThunk'; /** * Application Thunks (https://redux.js.org/usage/writing-logic-thunks) handle complex state manipulations. @@ -651,70 +654,4 @@ export const initializeApplicationAsStandaloneThunk = } else { dispatch(setConnectionModalOpen(true)); } - }; - -// Thunk to handle log events. -export const createLogThunk = -(loggingDriver, loggingDatabase, neodashMode, logUser, logAction, logDatabase, logDashboard = '', logMessage) => -(dispatch: any, getState: any) => { - try { - const uuid = createUUID(); - // Generate a cypher query to save the log. - const query = 'CREATE (n:_Neodash_Log) SET n.uuid = $uuid, n.user = $user, n.date = datetime(), n.neodash_mode = $neodashMode, n.action = $logAction, n.database = $logDatabase, n.dashboard = $logDashboard, n.message = $logMessage RETURN $uuid as uuid' - - const parameters = { - uuid: uuid, - user: logUser, - logAction: logAction, - logDatabase: logDatabase, - neodashMode: neodashMode, - logDashboard: logDashboard, - logMessage: logMessage - }; - runCypherQuery( - loggingDriver, - loggingDatabase, - query, - parameters, - 1, - () => {}, - (records) => { - if (records && records[0] && records[0]._fields && records[0]._fields[0] && records[0]._fields[0] == uuid) { - console.log('log created: '+uuid); - } else { - //we only show error notification one time - const state = getState() - const loggingSettings = applicationGetLoggingSettings (state); - let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) - console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') - if (LogErrorNotificationNum > 0 ) { - dispatch( - createNotificationThunk( - 'Error creating log', - LogErrorNotificationNum>1 ? `Please check logging configuration with your Neodash administrator`:`Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` - ) - ); - } - LogErrorNotificationNum -= 1 - dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); - } - } - ); - } catch (e) { - //we only show error notification 3 times - const state = getState() - const loggingSettings = applicationGetLoggingSettings (state); - let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification) - console.log('Error creating log for '+((LogErrorNotificationNum-4)*(-1))+' times') - if (LogErrorNotificationNum > 0 ) { - dispatch( - createNotificationThunk( - 'Error creating log', - LogErrorNotificationNum>1 ? `Please check logging configuration with your Neodash administrator`:`Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` - ) - ); - } - LogErrorNotificationNum -= 1 - dispatch(setLogErrorNotification (LogErrorNotificationNum.toString())); - } -}; \ No newline at end of file + }; \ No newline at end of file diff --git a/src/application/logging/LoggingActions.ts b/src/application/logging/LoggingActions.ts new file mode 100644 index 000000000..f9b8511b9 --- /dev/null +++ b/src/application/logging/LoggingActions.ts @@ -0,0 +1,19 @@ +export const LOGGING_PREFIX = 'APPLICATION/LOGGING/' + +export const SET_LOGGING_MODE = `${LOGGING_PREFIX}/SET_LOGGING_MODE`; +export const setLoggingMode = (loggingMode: string) => ({ + type: SET_LOGGING_MODE, + payload: { loggingMode }, +}); + +export const SET_LOGGING_DATABASE = `${LOGGING_PREFIX}/SET_LOGGING_DATABASE`; +export const setLoggingDatabase = (loggingDatabase: string) => ({ + type: SET_LOGGING_DATABASE, + payload: { loggingDatabase }, +}); + +export const SET_LOG_ERROR_NOTIFICATION = `${LOGGING_PREFIX}/SET_LOG_ERROR_NOTIFICATION`; +export const setLogErrorNotification = (logErrorNotification: any) => ({ + type: SET_LOG_ERROR_NOTIFICATION, + payload: { logErrorNotification }, +}); diff --git a/src/application/logging/LoggingReducer.ts b/src/application/logging/LoggingReducer.ts new file mode 100644 index 000000000..d5a25ded7 --- /dev/null +++ b/src/application/logging/LoggingReducer.ts @@ -0,0 +1,41 @@ +import { LOGGING_PREFIX, SET_LOGGING_DATABASE, SET_LOGGING_MODE, SET_LOG_ERROR_NOTIFICATION } from "./LoggingActions"; + +const update = (state, mutations) => Object.assign({}, state, mutations); + + +export const LOGGING_INITIAL_STATE = { + loggingMode: '0', + logErrorNotification: '3', + loggingDatabase: undefined + }; + +export const loggingReducer = (state = LOGGING_INITIAL_STATE, action: { type: any; payload: any }) => { + const { type, payload } = action; + + if (!action.type.startsWith(LOGGING_PREFIX)) { + return state; + } + + // Logging state updates are handled here. + switch (type) { + case SET_LOGGING_MODE: { + const { loggingMode } = payload; + state = update(state, { loggingMode: loggingMode }); + return state; + } + case SET_LOGGING_DATABASE: { + const { loggingDatabase } = payload; + state = update(state, { loggingDatabase: loggingDatabase }); + return state; + } + case SET_LOG_ERROR_NOTIFICATION: { + const { logErrorNotification } = payload; + state = update(state, { logErrorNotification: logErrorNotification }); + return state; + } + default: { + return state; + } + } + }; + \ No newline at end of file diff --git a/src/application/logging/LoggingSelectors.ts b/src/application/logging/LoggingSelectors.ts new file mode 100644 index 000000000..018eeca07 --- /dev/null +++ b/src/application/logging/LoggingSelectors.ts @@ -0,0 +1,8 @@ + +export const applicationGetLoggingSettings = (state: any) => { + return { + loggingMode: state.application.loggingMode, + loggingDatabase: state.application.loggingDatabase, + logErrorNotification: state.application.logErrorNotification, + }; +}; diff --git a/src/application/logging/LoggingThunk.ts b/src/application/logging/LoggingThunk.ts new file mode 100644 index 000000000..a861f194b --- /dev/null +++ b/src/application/logging/LoggingThunk.ts @@ -0,0 +1,70 @@ +import { createNotificationThunk } from '../../page/PageThunks'; +import { runCypherQuery } from '../../report/ReportQueryRunner'; +import { setLogErrorNotification } from "./LoggingActions"; +import { applicationGetLoggingSettings } from "./LoggingSelectors"; +import { createUUID } from '../../utils/uuid'; + +// Thunk to handle log events. + +export const createLogThunk = (loggingDriver, loggingDatabase, neodashMode, logUser, logAction, logDatabase, logDashboard = '', logMessage) => (dispatch: any, getState: any) => { + try { + const uuid = createUUID(); + // Generate a cypher query to save the log. + const query = 'CREATE (n:_Neodash_Log) SET n.uuid = $uuid, n.user = $user, n.date = datetime(), n.neodash_mode = $neodashMode, n.action = $logAction, n.database = $logDatabase, n.dashboard = $logDashboard, n.message = $logMessage RETURN $uuid as uuid'; + + const parameters = { + uuid: uuid, + user: logUser, + logAction: logAction, + logDatabase: logDatabase, + neodashMode: neodashMode, + logDashboard: logDashboard, + logMessage: logMessage + }; + runCypherQuery( + loggingDriver, + loggingDatabase, + query, + parameters, + 1, + () => { }, + (records) => { + if (records && records[0] && records[0]._fields && records[0]._fields[0] && records[0]._fields[0] == uuid) { + console.log('log created: ' + uuid); + } else { + //we only show error notification one time + const state = getState(); + const loggingSettings = applicationGetLoggingSettings(state); + let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification); + console.log('Error creating log for ' + ((LogErrorNotificationNum - 4) * (-1)) + ' times'); + if (LogErrorNotificationNum > 0) { + dispatch( + createNotificationThunk( + 'Error creating log', + LogErrorNotificationNum > 1 ? `Please check logging configuration with your Neodash administrator` : `Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` + ) + ); + } + LogErrorNotificationNum -= 1; + dispatch(setLogErrorNotification(LogErrorNotificationNum.toString())); + } + } + ); + } catch (e) { + //we only show error notification 3 times + const state = getState(); + const loggingSettings = applicationGetLoggingSettings(state); + let LogErrorNotificationNum = Number(loggingSettings.logErrorNotification); + console.log('Error creating log for ' + ((LogErrorNotificationNum - 4) * (-1)) + ' times'); + if (LogErrorNotificationNum > 0) { + dispatch( + createNotificationThunk( + 'Error creating log', + LogErrorNotificationNum > 1 ? `Please check logging configuration with your Neodash administrator` : `Please check logging configuration with your Neodash administrator - This message will not be displayed anymore in the current session` + ) + ); + } + LogErrorNotificationNum -= 1; + dispatch(setLogErrorNotification(LogErrorNotificationNum.toString())); + } +}; diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index 346e4823f..69fbc50ed 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -5,8 +5,9 @@ import { runCypherQuery } from '../report/ReportQueryRunner'; import { setParametersToLoadAfterConnecting, setWelcomeScreenOpen } from '../application/ApplicationActions'; import { updateGlobalParametersThunk, updateParametersToNeo4jTypeThunk } from '../settings/SettingsThunks'; import { createUUID } from '../utils/uuid'; -import { createLogThunk } from '../application/ApplicationThunks'; -import { applicationGetLoggingSettings, applicationGetConnectionUser, applicationIsStandalone } from '../application/ApplicationSelectors'; +import { createLogThunk } from "../application/logging/LoggingThunk"; +import { applicationGetConnectionUser, applicationIsStandalone } from '../application/ApplicationSelectors'; +import { applicationGetLoggingSettings } from "../application/logging/LoggingSelectors"; export const removePageThunk = (number) => (dispatch: any, getState: any) => { try { @@ -231,7 +232,6 @@ export const saveDashboardToNeo4jThunk = ) ); } - } } ); @@ -251,7 +251,6 @@ export const saveDashboardToNeo4jThunk = ) ); } - } }; From 6c21e5a7a5f8012c5fe86d3a60a49a22096c4f0a Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Mon, 20 Nov 2023 10:29:53 +0100 Subject: [PATCH 27/36] fixing new selector and small refactorings --- public/config.json | 16 ++++++++-------- src/application/Application.tsx | 1 - src/application/ApplicationReducer.ts | 3 --- src/application/ApplicationThunks.ts | 2 +- src/application/logging/LoggingSelectors.ts | 14 ++++++-------- src/card/Card.tsx | 2 +- 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/public/config.json b/public/config.json index e86c2255b..703e59314 100644 --- a/public/config.json +++ b/public/config.json @@ -2,18 +2,18 @@ "ssoEnabled": false, "ssoDiscoveryUrl": "https://example.com", "standalone": false, - "standaloneProtocol": "bolt", + "standaloneProtocol": "neo4j", "standaloneHost": "localhost", - "standalonePort": "11003", + "standalonePort": "7687", "standaloneDatabase": "neo4j", "standaloneDashboardName": "My Dashboard", - "standaloneDashboardDatabase": "neo4j", + "standaloneDashboardDatabase": "dashboards", "standaloneDashboardURL": "", - "standaloneAllowLoad": true, + "standaloneAllowLoad": false, "standaloneLoadFromOtherDatabases": false, - "standaloneMultiDatabase": true, + "standaloneMultiDatabase": false, "standaloneDatabaseList": "neo4j", - "loggingMode": "1", + "loggingMode": "0", "loggingDatabase": "logs", - "customHeader": "Test" -} + "customHeader": "" +} \ No newline at end of file diff --git a/src/application/Application.tsx b/src/application/Application.tsx index 49d78ca1e..cd8d9759a 100644 --- a/src/application/Application.tsx +++ b/src/application/Application.tsx @@ -93,7 +93,6 @@ const Application = ({ onConnectionModalClose, onSSOAttempt, themeMode, - customHeader, }) => { const [initialized, setInitialized] = React.useState(false); diff --git a/src/application/ApplicationReducer.ts b/src/application/ApplicationReducer.ts index 2b1fba034..42833814f 100644 --- a/src/application/ApplicationReducer.ts +++ b/src/application/ApplicationReducer.ts @@ -95,9 +95,6 @@ export const applicationReducer = (state = initialState, action: { type: any; pa } case SET_ABOUT_MODAL_OPEN: { const { open } = payload; - if (!open) { - console.log(''); - } state = update(state, { aboutModalOpen: open }); return state; } diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index a8aee62e6..00247b2e8 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -109,7 +109,7 @@ export const createConnectionThunk = 'INF - connect to DB', database, '', - username +'established connection to Neo4j DB in ' +neodashMode+' mode at '+Date(Date.now()).substring(0,33) + username +' established connection to Neo4j DB in ' +neodashMode+' mode at '+Date(Date.now()).substring(0,33) ) ); } diff --git a/src/application/logging/LoggingSelectors.ts b/src/application/logging/LoggingSelectors.ts index 018eeca07..2d1bfccec 100644 --- a/src/application/logging/LoggingSelectors.ts +++ b/src/application/logging/LoggingSelectors.ts @@ -1,8 +1,6 @@ - -export const applicationGetLoggingSettings = (state: any) => { - return { - loggingMode: state.application.loggingMode, - loggingDatabase: state.application.loggingDatabase, - logErrorNotification: state.application.logErrorNotification, - }; -}; +/** + * Selector function for retrieving logging settings from the application state. + * @param state - The application state. + * @returns An object with logging settings. + */ + export const applicationGetLoggingSettings = (state: any) => (state.application.logging); diff --git a/src/card/Card.tsx b/src/card/Card.tsx index 4e14d7aea..668395ccc 100644 --- a/src/card/Card.tsx +++ b/src/card/Card.tsx @@ -265,7 +265,7 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(toggleReportSettings(id)); }, onDatabaseChanged: (id: any, database: any) => { - dispatch(updateReportDatabaseThunk(id, database)); + dispatch(updateReportDatabaseThunk(id, database)); }, createNotification: (title: any, message: any) => { dispatch(createNotificationThunk(title, message)); From 3a43fab254cd89b33d2c01385a7609b5bff6d6e2 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Mon, 20 Nov 2023 15:21:10 +0100 Subject: [PATCH 28/36] cleaning code and testing standalone --- src/application/ApplicationThunks.ts | 1 - src/dashboard/header/DashboardHeader.tsx | 3 +-- src/dashboard/header/DashboardTitle.tsx | 1 - src/modal/ConnectionModal.tsx | 4 +--- src/modal/LoadModal.tsx | 5 ++--- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/application/ApplicationThunks.ts b/src/application/ApplicationThunks.ts index 00247b2e8..2533e10a4 100644 --- a/src/application/ApplicationThunks.ts +++ b/src/application/ApplicationThunks.ts @@ -47,7 +47,6 @@ import { import { version } from '../modal/AboutModal'; import { applicationIsStandalone } from './ApplicationSelectors'; import { applicationGetLoggingSettings } from "./logging/LoggingSelectors"; -import { valueIsRelationship } from '../chart/ChartUtils'; import { createLogThunk } from './logging/LoggingThunk'; /** diff --git a/src/dashboard/header/DashboardHeader.tsx b/src/dashboard/header/DashboardHeader.tsx index 31f5bab0d..bb8c1ccdd 100644 --- a/src/dashboard/header/DashboardHeader.tsx +++ b/src/dashboard/header/DashboardHeader.tsx @@ -30,7 +30,6 @@ export const NeoDashboardHeader = ({ const downloadImageEnabled = settings ? settings.downloadImageEnabled : false; const [dashboardTitleText, setDashboardTitleText] = React.useState(dashboardTitle); - const [customHeaderText, setCustomHeader] = React.useState(customHeader); const [isDarkMode, setDarkMode] = React.useState(themeMode !== 'light'); @@ -54,7 +53,7 @@ export const NeoDashboardHeader = ({
diff --git a/src/dashboard/header/DashboardTitle.tsx b/src/dashboard/header/DashboardTitle.tsx index 2f4868578..49ec77eec 100644 --- a/src/dashboard/header/DashboardTitle.tsx +++ b/src/dashboard/header/DashboardTitle.tsx @@ -14,7 +14,6 @@ import NeoLoadModal from '../../modal/LoadModal'; import NeoShareModal from '../../modal/ShareModal'; import NeoExtensionsModal from '../../extensions/ExtensionsModal'; import { EXTENSIONS_DRAWER_BUTTONS } from '../../extensions/ExtensionConfig'; - import { Tooltip } from '@mui/material'; export const NeoDashboardTitle = ({ diff --git a/src/modal/ConnectionModal.tsx b/src/modal/ConnectionModal.tsx index d31063ac0..cd056062f 100644 --- a/src/modal/ConnectionModal.tsx +++ b/src/modal/ConnectionModal.tsx @@ -26,7 +26,6 @@ export default function NeoConnectionModal({ const [username, setUsername] = React.useState(connection.username); const [password, setPassword] = React.useState(connection.password); const [database, setDatabase] = React.useState(connection.database); - const [standaloneDatabase, setStandaloneDatabases] = React.useState([standaloneSettings.standalone? standaloneSettings.standaloneDatabase : 'neo4j']); // Make sure local vars are updated on external connection updates. useEffect(() => { @@ -44,7 +43,7 @@ export default function NeoConnectionModal({ const discoveryAPIUrl = ssoSettings && ssoSettings.ssoDiscoveryUrl; - //sice config is loaded asynchronously, value may not be yet defined when this runs for first time + //since config is loaded asynchronously, value may not be yet defined when this runs for first time let databaseList = ['neo4j'] try{ databaseList = standaloneSettings.standaloneDatabaseList.split(',') @@ -155,7 +154,6 @@ export default function NeoConnectionModal({ selectProps={{ onChange: (newValue) => { setDatabase(newValue.value); - setStandaloneDatabases(newValue.value); }, options: databaseList.map((option) => ({ label: option, diff --git a/src/modal/LoadModal.tsx b/src/modal/LoadModal.tsx index bc563988b..984fa787f 100644 --- a/src/modal/LoadModal.tsx +++ b/src/modal/LoadModal.tsx @@ -1,5 +1,5 @@ import React, { useContext, useRef } from 'react'; -import { TextareaAutosize } from '@mui/material'; +import { TextareaAutosize, Tooltip } from '@mui/material'; import { connect } from 'react-redux'; import { loadDashboardFromNeo4jByUUIDThunk, @@ -17,7 +17,6 @@ import { DocumentPlusIconOutline, } from '@neo4j-ndl/react/icons'; import { applicationGetStandaloneSettings, applicationIsStandalone } from '../application/ApplicationSelectors'; -import { Tooltip } from '@mui/material'; /** * A modal to save a dashboard as a JSON text string. @@ -217,7 +216,7 @@ export const NeoLoadModal = ({ }); }, //if application is running standalone and standaloneLoadFromOtherDatabases is not enabled, we do not allow changing database - isDisabled: standaloneSettings.standalone&&!standaloneSettings.standaloneLoadFromOtherDatabases?true:false, + isDisabled: standaloneSettings.standalone && !standaloneSettings.standaloneLoadFromOtherDatabases ? true : false, options: databases.map((database) => ({ label: database, value: database })), value: { label: dashboardDatabase, value: dashboardDatabase }, menuPlacement: 'auto', From 06c0ba7a28e5aa3633026f645303e4117af3ff01 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 21 Nov 2023 12:16:18 +0100 Subject: [PATCH 29/36] adding database list check --- src/modal/ConnectionModal.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modal/ConnectionModal.tsx b/src/modal/ConnectionModal.tsx index cd056062f..88699bab3 100644 --- a/src/modal/ConnectionModal.tsx +++ b/src/modal/ConnectionModal.tsx @@ -44,11 +44,12 @@ export default function NeoConnectionModal({ const discoveryAPIUrl = ssoSettings && ssoSettings.ssoDiscoveryUrl; //since config is loaded asynchronously, value may not be yet defined when this runs for first time - let databaseList = ['neo4j'] + let standaloneDatabaseList = [standaloneSettings.standaloneDatabase] try{ - databaseList = standaloneSettings.standaloneDatabaseList.split(',') + standaloneDatabaseList = standaloneSettings.standaloneDatabaseList ? standaloneSettings.standaloneDatabaseList.split(',') : standaloneDatabaseList } catch(e){ + console.log(e) } return ( @@ -155,7 +156,7 @@ export default function NeoConnectionModal({ onChange: (newValue) => { setDatabase(newValue.value); }, - options: databaseList.map((option) => ({ + options: standaloneDatabaseList.map((option) => ({ label: option, value: option, })), From 46a15ba7531dd232bbb892f15b2d52ddc8e050cd Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 21 Nov 2023 12:50:46 +0100 Subject: [PATCH 30/36] changing version to 3.18 to address address Cve-2023-4863 and cve-2023-38039 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ebe0f16ed..0733ff216 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build stage -FROM node:lts-alpine AS build-stage +FROM node:lts-alpine3.18 AS build-stage RUN yarn global add typescript jest WORKDIR /usr/local/src/neodash @@ -16,7 +16,7 @@ COPY ./ /usr/local/src/neodash RUN yarn run build-minimal # production stage -FROM nginx:alpine AS neodash +FROM nginx:alpine3.18 AS neodash RUN apk upgrade ENV NGINX_PORT=5005 From 7f9e5a8fe91560c1520e9801c544b25c0a5a98a0 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 21 Nov 2023 14:00:26 +0100 Subject: [PATCH 31/36] removing unused imports --- src/dashboard/header/DashboardTitle.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dashboard/header/DashboardTitle.tsx b/src/dashboard/header/DashboardTitle.tsx index a8b36067a..734405b53 100644 --- a/src/dashboard/header/DashboardTitle.tsx +++ b/src/dashboard/header/DashboardTitle.tsx @@ -9,11 +9,9 @@ import { updateDashboardSetting } from '../../settings/SettingsActions'; import { Typography, IconButton, Menu, MenuItems, TextInput } from '@neo4j-ndl/react'; import { CheckBadgeIconOutline, EllipsisHorizontalIconOutline, PencilSquareIconOutline } from '@neo4j-ndl/react/icons'; import NeoSettingsModal from '../../settings/SettingsModal'; -import NeoShareModal from '../sidebar/modal/legacy/LegacyShareModal'; import NeoExtensionsModal from '../../extensions/ExtensionsModal'; import { EXTENSIONS_DRAWER_BUTTONS } from '../../extensions/ExtensionConfig'; import { Tooltip } from '@mui/material'; -import NeoDashboardSidebarExportModal from '../sidebar/modal/DashboardSidebarExportModal'; import NeoExportModal from '../../modal/ExportModal'; import { setDraft } from '../../application/ApplicationActions'; From 710294b4abd829a99ed72f34b462d4ee8776b7d6 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 21 Nov 2023 15:57:40 +0100 Subject: [PATCH 32/36] working on final release --- src/card/Card.tsx | 8 -------- src/dashboard/sidebar/DashboardSidebar.tsx | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/card/Card.tsx b/src/card/Card.tsx index 668395ccc..fe6ffe0d9 100644 --- a/src/card/Card.tsx +++ b/src/card/Card.tsx @@ -54,7 +54,6 @@ const NeoCard = ({ onDatabaseChanged, // action to take when the user changes the database related to the card loadDatabaseListFromNeo4j, // Thunk to get the list of databases createNotification, // Thunk to create a global notification pop-up. - saveDatabase, // added to ensure the database is always saved in the session as soon as the card is created (and added in the dahsboard JSON) }) => { // Will be used to fetch the list of current databases const { driver } = useContext(Neo4jContext); @@ -118,10 +117,6 @@ const NeoCard = ({ setCollapseTimeout(report.collapseTimeout); }, [report.collapseTimeout]); - useEffect(() => { - saveDatabase(id, database); - }, [database]); - // TODO - get rid of some of the props-drilling here... const component = (
({ dispatch(createNotificationThunk(title, message)); }, loadDatabaseListFromNeo4j: (driver, callback) => dispatch(loadDatabaseListFromNeo4jThunk(driver, callback)), - saveDatabase: (id: any, database: any) => { - dispatch(updateReportDatabaseThunk(id, database)); - }, }); export default connect(mapStateToProps, mapDispatchToProps)(NeoCard); diff --git a/src/dashboard/sidebar/DashboardSidebar.tsx b/src/dashboard/sidebar/DashboardSidebar.tsx index fda132a96..3b8ab5232 100644 --- a/src/dashboard/sidebar/DashboardSidebar.tsx +++ b/src/dashboard/sidebar/DashboardSidebar.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { connect } from 'react-redux'; import { getDashboardIsEditable, getPageNumber } from '../../settings/SettingsSelectors'; import { getDashboardSettings, getDashboardTitle } from '../DashboardSelectors'; @@ -10,6 +10,7 @@ import { DashboardSidebarListItem } from './DashboardSidebarListItem'; import { applicationGetConnection, applicationGetConnectionDatabase, + applicationGetStandaloneSettings, applicationIsStandalone, dashboardIsDraft, } from '../../application/ApplicationSelectors'; @@ -78,6 +79,7 @@ export const NeoDashboardSidebar = ({ loadDashboardFromNeo4j, saveDashboardToNeo4j, deleteDashboardFromNeo4j, + standaloneSettings, }) => { const { driver } = useContext(Neo4jContext); const [expanded, setOnExpanded] = useState(false); @@ -256,7 +258,9 @@ export const NeoDashboardSidebar = ({ // We changed the active dashboard database, reload the list in the sidebar. loadDashboardListFromNeo4j(driver, newDatabase, (list) => { setDashboards(list); - setDraft(true); + if (!readonly) { + setDraft(true); + } }); }} open={menuOpen == Menu.DATABASE} @@ -338,7 +342,7 @@ export const NeoDashboardSidebar = ({ Dashboards {/* Only let users create dashboards and change database when running in editor mode. */} - {readonly == false ? ( + {!readonly || (readonly && standaloneSettings.standaloneLoadFromOtherDatabases) ? ( <> - - - + {!readonly ? ( + + + + ) : ( + <> + )} ) : ( <> diff --git a/src/report/ReportQueryRunner.ts b/src/report/ReportQueryRunner.ts index a935a6d53..df55de0a0 100644 --- a/src/report/ReportQueryRunner.ts +++ b/src/report/ReportQueryRunner.ts @@ -84,6 +84,7 @@ export async function runCypherQuery( if (records.length == 0) { setStatus(QueryStatus.NO_DATA); // console.log("TODO remove this - QUERY RETURNED NO DATA!") + setRecords([]); transaction.commit(); return; } From 5a95e1dad59910c119197a9d4a5fccf72a498186 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Tue, 21 Nov 2023 17:22:28 +0100 Subject: [PATCH 34/36] removing useless import --- src/dashboard/sidebar/DashboardSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard/sidebar/DashboardSidebar.tsx b/src/dashboard/sidebar/DashboardSidebar.tsx index 224989e5d..0b9b061c4 100644 --- a/src/dashboard/sidebar/DashboardSidebar.tsx +++ b/src/dashboard/sidebar/DashboardSidebar.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useState } from 'react'; import { connect } from 'react-redux'; import { getDashboardIsEditable, getPageNumber } from '../../settings/SettingsSelectors'; import { getDashboardSettings, getDashboardTitle } from '../DashboardSelectors'; From 172dd1cd06fd87f62994f80c2cc2e3b433557c46 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Fri, 24 Nov 2023 16:43:19 +0100 Subject: [PATCH 35/36] removing change in runCypherQuery and reusing the status of the queryResult correctly to trigger db change --- src/dashboard/DashboardThunks.ts | 130 +++++++++++++++---------------- src/report/ReportQueryRunner.ts | 1 - 2 files changed, 62 insertions(+), 69 deletions(-) diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index b274e810b..31ce57093 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -183,13 +183,11 @@ export const saveDashboardToNeo4jThunk = loguser, 'INF - save dashboard', database, - `Name:${ title}`, - `User ${ - loguser - } saved dashboard to Neo4J in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${title}`, + `User ${loguser} saved dashboard to Neo4J in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -209,11 +207,11 @@ export const saveDashboardToNeo4jThunk = loguser, 'ERR - save dashboard', database, - `Name:${ title}`, - `Error while trying to save dashboard to Neo4J in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${title}`, + `Error while trying to save dashboard to Neo4J in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -232,10 +230,10 @@ export const saveDashboardToNeo4jThunk = 'ERR - save dashboard', database, 'Name:Not fetched', - `Error while trying to save dashboard to Neo4J in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Error while trying to save dashboard to Neo4J in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -316,11 +314,11 @@ export const loadDashboardFromNeo4jThunk = (driver, database, uuid, callback) => loguser, 'ERR - load dashboard', database, - `UUID:${ uuid}`, - `Error while trying to load dashboard by UUID in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `UUID:${uuid}`, + `Error while trying to load dashboard by UUID in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -336,13 +334,11 @@ export const loadDashboardFromNeo4jThunk = (driver, database, uuid, callback) => loguser, 'INF - load dashboard', database, - `Name:${ dashboard.title}`, - `User ${ - loguser - } Loaded dashboard by UUID in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${dashboard.title}`, + `User ${loguser} Loaded dashboard by UUID in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -360,11 +356,8 @@ export const loadDashboardFromNeo4jThunk = (driver, database, uuid, callback) => loguser, 'ERR - load dashboard', database, - `UUID:${ uuid}`, - `Error while trying to load dashboard by UUID in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `UUID:${uuid}`, + `Error while trying to load dashboard by UUID in ${neodashMode} mode at ${Date(Date.now()).substring(0, 33)}` ) ); } @@ -413,11 +406,11 @@ export const loadDashboardFromNeo4jByNameThunk = loguser, 'ERR - load dashboard', database, - `Name:${ name}`, - `Error while trying to load dashboard by Name in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${name}`, + `Error while trying to load dashboard by Name in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -435,11 +428,11 @@ export const loadDashboardFromNeo4jByNameThunk = loguser, 'ERR - load dashboard', database, - `Name:${ name}`, - `Error while trying to load dashboard by Name in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${name}`, + `Error while trying to load dashboard by Name in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -455,13 +448,11 @@ export const loadDashboardFromNeo4jByNameThunk = loguser, 'INF - load dashboard', database, - `Name:${ name}`, - `User ${ - loguser - } Loaded dashboard by UUID in ${ - neodashMode - } mode at ${ - Date(Date.now()).substring(0, 33)}` + `Name:${name}`, + `User ${loguser} Loaded dashboard by UUID in ${neodashMode} mode at ${Date(Date.now()).substring( + 0, + 33 + )}` ) ); } @@ -474,6 +465,23 @@ export const loadDashboardFromNeo4jByNameThunk = }; export const loadDashboardListFromNeo4jThunk = (driver, database, callback) => (dispatch: any) => { + function runCallback(records) { + if (!records || !records[0] || !records[0]._fields) { + callback([]); + return; + } + const result = records.map((r, index) => { + return { + uuid: r._fields[0], + title: r._fields[1], + date: r._fields[2], + author: r._fields[3], + version: r._fields[4], + index: index, + }; + }); + callback(result); + } try { runCypherQuery( driver, @@ -481,24 +489,10 @@ export const loadDashboardListFromNeo4jThunk = (driver, database, callback) => ( 'MATCH (n:_Neodash_Dashboard) RETURN n.uuid as uuid, n.title as title, toString(n.date) as date, n.user as author, n.version as version ORDER BY date DESC', {}, 1000, - () => {}, - (records) => { - if (!records || !records[0] || !records[0]._fields) { - callback([]); - return; - } - const result = records.map((r, index) => { - return { - uuid: r._fields[0], - title: r._fields[1], - date: r._fields[2], - author: r._fields[3], - version: r._fields[4], - index: index, - }; - }); - callback(result); - } + (status) => { + status == QueryStatus.NO_DATA ? runCallback([]) : () => {}; + }, + (records) => runCallback(records) ); } catch (e) { dispatch(createNotificationThunk('Unable to load dashboard list from Neo4j', e)); diff --git a/src/report/ReportQueryRunner.ts b/src/report/ReportQueryRunner.ts index df55de0a0..a935a6d53 100644 --- a/src/report/ReportQueryRunner.ts +++ b/src/report/ReportQueryRunner.ts @@ -84,7 +84,6 @@ export async function runCypherQuery( if (records.length == 0) { setStatus(QueryStatus.NO_DATA); // console.log("TODO remove this - QUERY RETURNED NO DATA!") - setRecords([]); transaction.commit(); return; } From bdb571154bfcd9504ffba219bda134d660682744 Mon Sep 17 00:00:00 2001 From: Alfred Rubin Date: Fri, 24 Nov 2023 16:48:59 +0100 Subject: [PATCH 36/36] removing change in runCypherQuery and reusing the status of the queryResult correctly to trigger db change --- src/dashboard/DashboardThunks.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dashboard/DashboardThunks.ts b/src/dashboard/DashboardThunks.ts index 31ce57093..81c9b9620 100644 --- a/src/dashboard/DashboardThunks.ts +++ b/src/dashboard/DashboardThunks.ts @@ -465,6 +465,11 @@ export const loadDashboardFromNeo4jByNameThunk = }; export const loadDashboardListFromNeo4jThunk = (driver, database, callback) => (dispatch: any) => { + function setStatus(status) { + if (status == QueryStatus.NO_DATA) { + runCallback([]); + } + } function runCallback(records) { if (!records || !records[0] || !records[0]._fields) { callback([]); @@ -489,9 +494,7 @@ export const loadDashboardListFromNeo4jThunk = (driver, database, callback) => ( 'MATCH (n:_Neodash_Dashboard) RETURN n.uuid as uuid, n.title as title, toString(n.date) as date, n.user as author, n.version as version ORDER BY date DESC', {}, 1000, - (status) => { - status == QueryStatus.NO_DATA ? runCallback([]) : () => {}; - }, + (status) => setStatus(status), (records) => runCallback(records) ); } catch (e) {