Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions redisinsight/ui/src/mocks/handlers/workbench/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import { commandExecutionFactory } from 'uiSrc/mocks/factories/workbench/command
import { INSTANCE_ID_MOCK } from '../instances/instancesHandlers'

const handlers: RestHandler[] = [
rest.get<CommandExecution[]>(
getMswURL(
getUrl(INSTANCE_ID_MOCK, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
),
async (_req, res, ctx) =>
res(ctx.status(200), ctx.json(commandExecutionFactory.buildList(1))),
),
rest.post<CommandExecution[]>(
getMswURL(
getUrl(INSTANCE_ID_MOCK, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS),
Expand Down
9 changes: 7 additions & 2 deletions redisinsight/ui/src/pages/vector-search/query/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ResultsMode,
CommandExecutionUI,
CommandExecution,
CommandExecutionType,
} from 'uiSrc/slices/interfaces'
import { PIPELINE_COUNT_DEFAULT } from 'uiSrc/constants/api'
import {
Expand All @@ -22,13 +23,13 @@ import {
findCommand,
removeCommand,
} from 'uiSrc/services/workbenchStorage'
import { useCommandsHistory } from 'uiSrc/services/commands-history/hooks/useCommandsHistory'
import {
createErrorResult,
createGroupItem,
executeApiCall,
generateCommandId,
limitHistoryLength,
loadHistoryData,
prepareNewItems,
scrollToElement,
sortCommandsByDate,
Expand All @@ -44,13 +45,17 @@ const useQuery = () => {
const [processing, setProcessing] = useState(false)
const [isLoaded, setIsLoaded] = useState(false)

const { getCommandsHistory } = useCommandsHistory({
commandExecutionType: CommandExecutionType.Search,
})

const resultsMode = ResultsMode.Default
const activeRunQueryMode = RunQueryMode.ASCII

useEffect(() => {
const loadHistory = async () => {
try {
const historyData = await loadHistoryData(instanceId)
const historyData = await getCommandsHistory(instanceId)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal is to preserve the current way of managing command history through the Search page (with this hook) while modifying only the underlying logic responsible for interacting with the data store.

This approach ensures that the existing components remain unchanged, while simultaneously achieving the goal of having an independent data store that can be easily changed via a feature flag.

And of course, by design, this hook (and the components that depend on it) remain unaware of this information, and they will not be affected by any future changes of the data source behind it.

setItems(historyData)
} catch (error) {
// Silently handle error
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CommandExecutionType } from 'uiSrc/slices/interfaces'
import { CommandsHistoryDatabase, CommandHistoryResult } from './interface'

export class CommandsHistoryIndexedDB implements CommandsHistoryDatabase {
async getCommandsHistory(
instanceId: string,
commandExecutionType: CommandExecutionType,
): Promise<CommandHistoryResult> {
// TODO: Implementation for fetching command history from IndexedDB
return Promise.resolve({
success: true,
data: [],
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { AxiosError } from 'axios'

import apiService from 'uiSrc/services/apiService'
import { ApiEndpoints } from 'uiSrc/constants'
import { getUrl, isStatusSuccessful } from 'uiSrc/utils'
import {
CommandExecution,
CommandExecutionType,
CommandExecutionUI,
} from 'uiSrc/slices/interfaces'

import { mapCommandExecutionToUI } from '../utils/command-execution.mapper'
import { CommandsHistoryDatabase, CommandHistoryResult } from './interface'

export class CommandsHistorySQLite implements CommandsHistoryDatabase {
Copy link
Member Author

@valkirilov valkirilov Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an i(example) implementation of the service that should work with the Commands History stored in the SQLite database. It should follow the common interface and keep all of the business login encapsulated inside it.

async getCommandsHistory(
instanceId: string,
commandExecutionType: CommandExecutionType,
): Promise<CommandHistoryResult> {
try {
const url = getUrl(instanceId, ApiEndpoints.WORKBENCH_COMMAND_EXECUTIONS)
const config = { params: { type: commandExecutionType } }

const { data, status } = await apiService.get<CommandExecution[]>(
url,
config,
)

if (isStatusSuccessful(status)) {
const results: CommandExecutionUI[] = data.map(mapCommandExecutionToUI)

return { success: true, data: results }
}

return {
success: false,
error: new Error(`Request failed with status ${status}`),
}
} catch (exception) {
return {
success: false,
error: exception as AxiosError,
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AxiosError } from 'axios'
import {
CommandExecutionType,
CommandExecutionUI,
} from 'uiSrc/slices/interfaces'

export interface CommandsHistoryDatabase {
Copy link
Member Author

@valkirilov valkirilov Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the interface that every service should comply with. It's basic CRUD, simple as that. And it's independent from the data source behind it.

getCommandsHistory(
instanceId: string,
commandExecutionType: CommandExecutionType,
): Promise<CommandHistoryResult>
}

export interface CommandHistoryResult {
success: boolean
data?: CommandExecutionUI[]
error?: Error | AxiosError | any
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
CommandExecutionType,
CommandExecutionUI,
} from 'uiSrc/slices/interfaces'
import { CommandsHistorySQLite } from '../database/CommandsHistorySQLite'
import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'
import { useDispatch, useSelector } from 'react-redux'
import { useEffect } from 'react'
import { CommandsHistoryDatabase } from '../database/interface'
import { CommandsHistoryIndexedDB } from '../database/CommandsHistoryIndexedDB'
import { FeatureFlags } from 'uiSrc/constants/featureFlags'
import { addErrorNotification } from 'uiSrc/slices/app/notifications'

interface CommandHistoryProps {
commandExecutionType: CommandExecutionType
}

interface CommandsHistoryHook {
getCommandsHistory: (instanceId: string) => Promise<CommandExecutionUI[]>
}

export const useCommandsHistory = ({
commandExecutionType,
}: CommandHistoryProps): CommandsHistoryHook => {
let commandsHistoryDatabase: CommandsHistoryDatabase

const dispatch = useDispatch()
const { [FeatureFlags.envDependent]: envDependentFeature } = useSelector(
appFeatureFlagsFeaturesSelector,
)

useEffect(() => {
if (envDependentFeature?.flag) {
commandsHistoryDatabase = new CommandsHistorySQLite()
} else {
commandsHistoryDatabase = new CommandsHistoryIndexedDB()
}
}, [envDependentFeature])

const getCommandsHistory = async (instanceId: string) => {
const { data, error } = await commandsHistoryDatabase.getCommandsHistory(
instanceId,
commandExecutionType,
)

if (error) {
dispatch(addErrorNotification(error))
}

return data || []
}

return {
getCommandsHistory,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { EMPTY_COMMAND } from 'uiSrc/constants'
import { CommandExecution, CommandExecutionUI } from 'uiSrc/slices/interfaces'

export const mapCommandExecutionToUI = (
item: CommandExecution,
): CommandExecutionUI => {
return {
...item,
command: item.command || EMPTY_COMMAND,
emptyCommand: !item.command,
}
}
7 changes: 2 additions & 5 deletions redisinsight/ui/src/slices/workbench/wb-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
ConnectionType,
StateWorkbenchResults,
} from '../interfaces'
import { mapCommandExecutionToUI } from 'uiSrc/services/commands-history/utils/command-execution.mapper'

export const initialState: StateWorkbenchResults = {
isLoaded: false,
Expand Down Expand Up @@ -80,11 +81,7 @@ const workbenchResultsSlice = createSlice({
state,
{ payload }: { payload: CommandExecution[] },
) => {
state.items = payload.map((item) => ({
...item,
command: item.command || EMPTY_COMMAND,
emptyCommand: !item.command,
}))
state.items = payload.map(mapCommandExecutionToUI)
state.loading = false
state.isLoaded = true
},
Expand Down
Loading