Skip to content

Commit 5f6d57f

Browse files
committed
feat(ui): introduce abstraction layer for commands history
- created interface to define the behavior for the services that should fetch commands history data (no matter of the data source) - created example implementation for fetching commands history data from SQLite - created hook to toggle the data source when fetching commands history data, based on a feature flag and target SQLite or INdexDB re #RI-7614
1 parent 9d31cba commit 5f6d57f

File tree

8 files changed

+163
-7
lines changed

8 files changed

+163
-7
lines changed

redisinsight/ui/src/mocks/handlers/workbench/commands.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import { commandExecutionFactory } from 'uiSrc/mocks/factories/workbench/command
77
import { INSTANCE_ID_MOCK } from '../instances/instancesHandlers'
88

99
const handlers: RestHandler[] = [
10+
rest.get<CommandExecution[]>(
11+
getMswURL(
12+
getUrl(INSTANCE_ID_MOCK, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
13+
),
14+
async (_req, res, ctx) =>
15+
res(ctx.status(200), ctx.json(commandExecutionFactory.buildList(1))),
16+
),
1017
rest.post<CommandExecution[]>(
1118
getMswURL(
1219
getUrl(INSTANCE_ID_MOCK, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),

redisinsight/ui/src/pages/vector-search/query/useQuery.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ResultsMode,
1515
CommandExecutionUI,
1616
CommandExecution,
17+
CommandExecutionType,
1718
} from 'uiSrc/slices/interfaces'
1819
import { PIPELINE_COUNT_DEFAULT } from 'uiSrc/constants/api'
1920
import {
@@ -22,13 +23,13 @@ import {
2223
findCommand,
2324
removeCommand,
2425
} from 'uiSrc/services/workbenchStorage'
26+
import { useCommandsHistory } from 'uiSrc/services/commands-history/hooks/useCommandsHistory'
2527
import {
2628
createErrorResult,
2729
createGroupItem,
2830
executeApiCall,
2931
generateCommandId,
3032
limitHistoryLength,
31-
loadHistoryData,
3233
prepareNewItems,
3334
scrollToElement,
3435
sortCommandsByDate,
@@ -44,13 +45,17 @@ const useQuery = () => {
4445
const [processing, setProcessing] = useState(false)
4546
const [isLoaded, setIsLoaded] = useState(false)
4647

48+
const { getCommandsHistory } = useCommandsHistory({
49+
commandExecutionType: CommandExecutionType.Search,
50+
})
51+
4752
const resultsMode = ResultsMode.Default
4853
const activeRunQueryMode = RunQueryMode.ASCII
4954

5055
useEffect(() => {
5156
const loadHistory = async () => {
5257
try {
53-
const historyData = await loadHistoryData(instanceId)
58+
const historyData = await getCommandsHistory(instanceId)
5459
setItems(historyData)
5560
} catch (error) {
5661
// Silently handle error
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { CommandExecutionType } from 'uiSrc/slices/interfaces'
2+
import { CommandsHistoryDatabase, CommandHistoryResult } from './interface'
3+
4+
export class CommandsHistoryIndexedDB implements CommandsHistoryDatabase {
5+
async getCommandsHistory(
6+
instanceId: string,
7+
commandExecutionType: CommandExecutionType,
8+
): Promise<CommandHistoryResult> {
9+
// TODO: Implementation for fetching command history from IndexedDB
10+
return Promise.resolve({
11+
success: true,
12+
data: [],
13+
})
14+
}
15+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { AxiosError } from 'axios'
2+
3+
import apiService from 'uiSrc/services/apiService'
4+
import { ApiEndpoints } from 'uiSrc/constants'
5+
import { getUrl, isStatusSuccessful } from 'uiSrc/utils'
6+
import {
7+
CommandExecution,
8+
CommandExecutionType,
9+
CommandExecutionUI,
10+
} from 'uiSrc/slices/interfaces'
11+
12+
import { mapCommandExecutionToUI } from '../utils/command-execution.mapper'
13+
import { CommandsHistoryDatabase, CommandHistoryResult } from './interface'
14+
15+
export class CommandsHistorySQLite implements CommandsHistoryDatabase {
16+
async getCommandsHistory(
17+
instanceId: string,
18+
commandExecutionType: CommandExecutionType,
19+
): Promise<CommandHistoryResult> {
20+
try {
21+
const url = getUrl(instanceId, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS)
22+
const config = { params: { type: commandExecutionType } }
23+
24+
const { data, status } = await apiService.get<CommandExecution[]>(
25+
url,
26+
config,
27+
)
28+
29+
if (isStatusSuccessful(status)) {
30+
const results: CommandExecutionUI[] = data.map(mapCommandExecutionToUI)
31+
32+
return { success: true, data: results }
33+
}
34+
35+
return {
36+
success: false,
37+
error: new Error(`Request failed with status ${status}`),
38+
}
39+
} catch (exception) {
40+
return {
41+
success: false,
42+
error: exception as AxiosError,
43+
}
44+
}
45+
}
46+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { AxiosError } from 'axios'
2+
import {
3+
CommandExecutionType,
4+
CommandExecutionUI,
5+
} from 'uiSrc/slices/interfaces'
6+
7+
export interface CommandsHistoryDatabase {
8+
getCommandsHistory(
9+
instanceId: string,
10+
commandExecutionType: CommandExecutionType,
11+
): Promise<CommandHistoryResult>
12+
}
13+
14+
export interface CommandHistoryResult {
15+
success: boolean
16+
data?: CommandExecutionUI[]
17+
error?: Error | AxiosError | any
18+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
CommandExecutionType,
3+
CommandExecutionUI,
4+
} from 'uiSrc/slices/interfaces'
5+
import { CommandsHistorySQLite } from '../database/CommandsHistorySQLite'
6+
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
7+
import { useDispatch, useSelector } from 'react-redux'
8+
import { useEffect } from 'react'
9+
import { CommandsHistoryDatabase } from '../database/interface'
10+
import { CommandsHistoryIndexedDB } from '../database/CommandsHistoryIndexedDB'
11+
import { FeatureFlags } from 'uiSrc/constants/featureFlags'
12+
import { addErrorNotification } from 'uiSrc/slices/app/notifications'
13+
14+
interface CommandHistoryProps {
15+
commandExecutionType: CommandExecutionType
16+
}
17+
18+
interface CommandsHistoryHook {
19+
getCommandsHistory: (instanceId: string) => Promise<CommandExecutionUI[]>
20+
}
21+
22+
export const useCommandsHistory = ({
23+
commandExecutionType,
24+
}: CommandHistoryProps): CommandsHistoryHook => {
25+
let commandsHistoryDatabase: CommandsHistoryDatabase
26+
27+
const dispatch = useDispatch()
28+
const { [FeatureFlags.envDependent]: envDependentFeature } = useSelector(
29+
appFeatureFlagsFeaturesSelector,
30+
)
31+
32+
useEffect(() => {
33+
if (envDependentFeature?.flag) {
34+
commandsHistoryDatabase = new CommandsHistorySQLite()
35+
} else {
36+
commandsHistoryDatabase = new CommandsHistoryIndexedDB()
37+
}
38+
}, [envDependentFeature])
39+
40+
const getCommandsHistory = async (instanceId: string) => {
41+
const { data, error } = await commandsHistoryDatabase.getCommandsHistory(
42+
instanceId,
43+
commandExecutionType,
44+
)
45+
46+
if (error) {
47+
dispatch(addErrorNotification(error))
48+
}
49+
50+
return data || []
51+
}
52+
53+
return {
54+
getCommandsHistory,
55+
}
56+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { EMPTY_COMMAND } from 'uiSrc/constants'
2+
import { CommandExecution, CommandExecutionUI } from 'uiSrc/slices/interfaces'
3+
4+
export const mapCommandExecutionToUI = (
5+
item: CommandExecution,
6+
): CommandExecutionUI => {
7+
return {
8+
...item,
9+
command: item.command || EMPTY_COMMAND,
10+
emptyCommand: !item.command,
11+
}
12+
}

redisinsight/ui/src/slices/workbench/wb-results.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
ConnectionType,
4949
StateWorkbenchResults,
5050
} from '../interfaces'
51+
import { mapCommandExecutionToUI } from 'uiSrc/services/commands-history/utils/command-execution.mapper'
5152

5253
export const initialState: StateWorkbenchResults = {
5354
isLoaded: false,
@@ -80,11 +81,7 @@ const workbenchResultsSlice = createSlice({
8081
state,
8182
{ payload }: { payload: CommandExecution[] },
8283
) => {
83-
state.items = payload.map((item) => ({
84-
...item,
85-
command: item.command || EMPTY_COMMAND,
86-
emptyCommand: !item.command,
87-
}))
84+
state.items = payload.map(mapCommandExecutionToUI)
8885
state.loading = false
8986
state.isLoaded = true
9087
},

0 commit comments

Comments
 (0)