Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ const CliBody = (props: Props) => {
<EuiFlexItem grow>
<div className={styles.output}>{data}</div>
{!error && !(loading || settingsLoading) ? (
<span style={{ paddingBottom: 5 }}>
<span style={{ paddingBottom: 5, paddingTop: 17 }}>
<CliInputWrapper
command={command}
setCommand={setCommand}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import {
processUnsupportedCommand,
sendCliClusterCommandAction,
} from 'uiSrc/slices/cli/cli-output'
import { BrowserStorageItem } from 'uiSrc/constants'
import { InitOutputText } from 'uiSrc/constants/cliOutput'
import { processCliClient } from 'uiSrc/slices/cli/cli-settings'
import { connectedInstanceSelector } from 'uiSrc/slices/instances'
import { sessionStorageService } from 'uiSrc/services'

import CliBodyWrapper from './CliBodyWrapper'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import {
sendCliCommandAction,
sendCliClusterCommandAction,
processUnsupportedCommand,
processUnrepeatableNumber,
} from 'uiSrc/slices/cli/cli-output'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { getCommandRepeat, isRepeatCountCorrect } from 'uiSrc/utils'
import { ConnectionType } from 'uiSrc/slices/interfaces'
import { ClusterNodeRole } from 'uiSrc/slices/interfaces/cli'
import { connectedInstanceSelector } from 'uiSrc/slices/instances'
import { checkUnsupportedCommand, clearOutput } from 'uiSrc/utils/cliHelper'
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { InitOutputText, ConnectionSuccessOutputText } from 'uiSrc/constants/cliOutput'
import { checkUnsupportedCommand, clearOutput, cliCommandOutput } from 'uiSrc/utils/cliHelper'
import { SendClusterCommandDto } from 'apiSrc/modules/cli/dto/cli.dto'
import CliBody from './CliBody'

Expand Down Expand Up @@ -88,15 +90,23 @@ const CliBodyWrapper = () => {
}

const handleSubmit = () => {
const commandLine = decode(command).trim()
const [commandLine, countRepeat] = getCommandRepeat(decode(command).trim())
const unsupportedCommand = checkUnsupportedCommand(unsupportedCommands, commandLine)
dispatch(concatToOutput(cliCommandOutput(command)))

if (!isRepeatCountCorrect(countRepeat)) {
dispatch(processUnrepeatableNumber(commandLine, resetCommand))
return
}

if (unsupportedCommand) {
dispatch(processUnsupportedCommand(commandLine, unsupportedCommand, resetCommand))
return
}

sendCommand(commandLine)
for (let i = 0; i < countRepeat; i++) {
sendCommand(commandLine)
}
}

const sendCommand = (command: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isUndefined } from 'lodash'
import React from 'react'
import { useSelector } from 'react-redux'
import { getCommandRepeat } from 'uiSrc/utils'
import { appRedisCommandsSelector } from 'uiSrc/slices/app/redis-commands'
import CliAutocomplete from './CliAutocomplete'

Expand All @@ -17,7 +18,9 @@ export interface Props {
const CliInputWrapper = (props: Props) => {
const { command = '', wordsTyped, setInputEl, setCommand, onKeyDown } = props
const { spec: ALL_REDIS_COMMANDS } = useSelector(appRedisCommandsSelector)
const [firstCommand, secondCommand] = command.split(' ')

const [commandLine, repeatCommand] = getCommandRepeat(command)
const [firstCommand, secondCommand] = commandLine.split(' ')
const firstCommandMatch = firstCommand.toUpperCase()
const secondCommandMatch = `${firstCommandMatch} ${secondCommand ? secondCommand.toUpperCase() : null}`

Expand All @@ -35,7 +38,11 @@ const CliInputWrapper = (props: Props) => {
onKeyDown={onKeyDown}
/>
{matchedCmd && (
<CliAutocomplete commandName={commandName} wordsTyped={wordsTyped} {...matchedCmd} />
<CliAutocomplete
commandName={commandName}
wordsTyped={repeatCommand === 1 ? wordsTyped : wordsTyped - 1}
{...matchedCmd}
/>
)}
</>
)
Expand Down
3 changes: 2 additions & 1 deletion redisinsight/ui/src/constants/cliOutput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const ConnectionSuccessOutputText = [
'Connected.',
'\n',
'Ready to execute commands.',
'\n\n\n',
'\n\n',
]

const unsupportedCommandTextCli = ' is not supported by the RedisInsight CLI. The list of all unsupported commands: '
Expand All @@ -32,4 +32,5 @@ export const cliTexts = {
commandLine + unsupportedCommandTextCli + commands,
WORKBENCH_UNSUPPORTED_COMMANDS: (commandLine: string, commands: string) =>
commandLine + unsupportedCommandTextWorkbench + commands,
REPEAT_COUNT_INVALID: 'Invalid repeat command option value'
}
4 changes: 2 additions & 2 deletions redisinsight/ui/src/pages/instance/InstancePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import {
fetchInstanceAction,
getDatabaseConfigInfoAction,
} from 'uiSrc/slices/instances'
import { BrowserStorageItem } from 'uiSrc/constants'
import { localStorageService } from 'uiSrc/services'
import {
appContextSelector,
setAppContextConnectedInstanceId,
setAppContextInitialState,
} from 'uiSrc/slices/app/context'
import { resetKeys } from 'uiSrc/slices/keys'
import { BrowserStorageItem } from 'uiSrc/constants'
import { localStorageService } from 'uiSrc/services'
import { resetOutput } from 'uiSrc/slices/cli/cli-output'
import { cliSettingsSelector } from 'uiSrc/slices/cli/cli-settings'
import BottomGroupComponents from 'uiSrc/components/bottom-group-components/BottomGroupComponents'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
cliParseTextResponse,
splitMonacoValuePerLines,
getMultiCommands,
isRepeatCountCorrect,
getCommandRepeat,
} from 'uiSrc/utils'
import {
sendWBCommandAction,
Expand Down Expand Up @@ -123,12 +125,21 @@ const WBViewWrapper = () => {
}
}, [historyItems])

const getUnsupportedCommandResponse = (commandLine = '') => {
const getUnsupportedCommandResponse = (command = '') => {
const [commandLine, countRepeat] = getCommandRepeat(command)
const { modules } = state.instance
const { unsupportedCommands, blockingCommands } = state
const unsupportedCommand = checkUnsupportedCommand(unsupportedCommands, commandLine)
|| checkBlockingCommand(blockingCommands, commandLine)

if (!isRepeatCountCorrect(countRepeat)) {
return cliParseTextResponse(
cliTexts.REPEAT_COUNT_INVALID,
commandLine,
CommandExecutionStatus.Fail,
)
}

if (unsupportedCommand) {
return cliParseTextResponse(
cliTexts.WORKBENCH_UNSUPPORTED_COMMANDS(
Expand Down
24 changes: 19 additions & 5 deletions redisinsight/ui/src/slices/cli/cli-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { CliOutputFormatterType, cliTexts } from 'uiSrc/constants/cliOutput'
import { apiService, localStorageService } from 'uiSrc/services'
import { ApiEndpoints, BrowserStorageItem } from 'uiSrc/constants'
import {
cliCommandOutput,
cliParseTextResponseWithOffset,
cliParseTextResponseWithRedirect,
} from 'uiSrc/utils/cliHelper'
Expand Down Expand Up @@ -92,7 +91,6 @@ export function sendCliCommandAction(
const state = stateInit()
const { id = '' } = state.connections?.instances?.connectedInstance

dispatch(concatToOutput(cliCommandOutput(command)))
if (command === '') {
onSuccessAction?.()
return
Expand Down Expand Up @@ -134,7 +132,6 @@ export function sendCliClusterCommandAction(
const state = stateInit()
const { id = '' } = state.connections.instances?.connectedInstance

dispatch(concatToOutput(cliCommandOutput(command)))
if (command === '') {
onSuccessAction?.()
return
Expand Down Expand Up @@ -192,8 +189,6 @@ export function processUnsupportedCommand(
const state = stateInit()
const { unsupportedCommands } = state.cli.settings

dispatch(concatToOutput(cliCommandOutput(command)))

dispatch(
concatToOutput(
cliParseTextResponseWithOffset(
Expand All @@ -210,3 +205,22 @@ export function processUnsupportedCommand(
onSuccessAction?.()
}
}

export function processUnrepeatableNumber(
command: string = '',
onSuccessAction?: () => void
) {
return async (dispatch: AppDispatch) => {
dispatch(
concatToOutput(
cliParseTextResponseWithOffset(
cliTexts.REPEAT_COUNT_INVALID,
command,
CommandExecutionStatus.Fail
)
)
)

onSuccessAction?.()
}
}
7 changes: 0 additions & 7 deletions redisinsight/ui/src/slices/tests/cli/cli-output.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
concatToOutput(
cliParseTextResponseWithOffset(
cliTexts.CLI_UNSUPPORTED_COMMANDS(command, unsupportedCommands.join(', ')),
Expand Down Expand Up @@ -210,7 +209,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandSuccess(),
concatToOutput(cliParseTextResponseWithOffset(data.response, command, data.status)),
Expand All @@ -234,7 +232,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandSuccess(),
concatToOutput(cliParseTextResponseWithOffset(data.response, command, data.status)),
Expand All @@ -261,7 +258,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandFailure(responsePayload.response.data.message),
concatToOutput(cliParseTextResponseWithOffset(errorMessage, command, CommandExecutionStatus.Fail)),
Expand Down Expand Up @@ -300,7 +296,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandSuccess(),
concatToOutput(
Expand Down Expand Up @@ -331,7 +326,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandSuccess(),
concatToOutput(
Expand Down Expand Up @@ -361,7 +355,6 @@ describe('cliOutput slice', () => {

// Assert
const expectedActions = [
concatToOutput(cliCommandOutput(command)),
sendCliCommand(),
sendCliCommandFailure(responsePayload.response.data.message),
concatToOutput(cliParseTextResponseWithOffset(errorMessage, command, CommandExecutionStatus.Fail)),
Expand Down
6 changes: 3 additions & 3 deletions redisinsight/ui/src/utils/cliHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ const cliParseTextResponseWithRedirect = (
const { host, port, slot } = redirectTo
redirectMessage = `-> Redirected to slot [${slot}] located at ${host}:${port}`
}
return [redirectMessage, '\n', cliParseTextResponse(text, command, status), '\n\n']
return [redirectMessage, '\n', cliParseTextResponse(text, command, status), '\n']
}

const cliParseTextResponseWithOffset = (
text: string = '',
command: string = '',
status: CommandExecutionStatus = CommandExecutionStatus.Success,
) => [cliParseTextResponse(text, command, status), '\n\n']
) => [cliParseTextResponse(text, command, status), '\n']

const cliParseTextResponse = (
text: string = '',
Expand All @@ -60,7 +60,7 @@ const cliParseTextResponse = (
</span>
)

const cliCommandOutput = (command: string) => [bashTextValue(), cliCommandWrapper(command), '\n']
const cliCommandOutput = (command: string) => ['\n', bashTextValue(), cliCommandWrapper(command), '\n']

const bashTextValue = () => '> '

Expand Down
16 changes: 15 additions & 1 deletion redisinsight/ui/src/utils/commands.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { flatten, isArray, isEmpty, reject } from 'lodash'
import { flatten, isArray, isEmpty, isNumber, reject, toNumber, isNaN, isInteger } from 'lodash'
import {
CommandArgsType,
CommandGroup,
Expand Down Expand Up @@ -86,3 +86,17 @@ export const getDocUrlForCommand = (
return `https://redis.io/commands/${command}`
}
}

export const getCommandRepeat = (command = ''): [string, number] => {
const [countRepeatStr, ...restCommand] = command.split(' ')
let countRepeat = toNumber(countRepeatStr)
let commandLine = restCommand.join(' ')
if (!isNumber(countRepeat) || isNaN(countRepeat) || !command) {
countRepeat = 1
commandLine = command
}

return [commandLine, countRepeat]
}

export const isRepeatCountCorrect = (number: number): boolean => number >= 1 && isInteger(number)
12 changes: 10 additions & 2 deletions redisinsight/ui/src/utils/monacoRedisComplitionProvider.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isNaN } from 'lodash'
import * as monacoEditor from 'monaco-editor'
import { ICommand, ICommandArgGenerated, ICommands } from 'uiSrc/constants'
import { generateArgs, generateArgsNames, getDocUrlForCommand } from 'uiSrc/utils/commands'
Expand Down Expand Up @@ -65,12 +66,19 @@ monacoEditor.languages.CompletionItemProvider => {
position: monacoEditor.Position
): monacoEditor.languages.CompletionList => {
const word = model.getWordUntilPosition(position)
const line = model.getLineContent(position.lineNumber)
const indexOfSpace = line.indexOf(' ')

const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: 1,
endColumn: word.endColumn
endColumn: word.endColumn,
startColumn:
word.startColumn > indexOfSpace && !isNaN(+line.slice(0, indexOfSpace))
? indexOfSpace + 2
: 1,
}

// display suggestions only for words that don't belong to a folding area
if (!model.getValueInRange(range).startsWith(' ')) {
return {
Expand Down
16 changes: 15 additions & 1 deletion redisinsight/ui/src/utils/monacoUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isEmpty, isUndefined, reject } from 'lodash'
import { ICommands } from 'uiSrc/constants'
import { IMonacoCommand } from './monacoInterfaces'
import { Nullable } from './types'
import { getCommandRepeat, isRepeatCountCorrect } from './commands'

const COMMENT_SYMBOLS = '//'
const BLANK_LINE_REGEX = /^\s*\n/gm
Expand All @@ -21,7 +22,20 @@ const removeCommentsFromLine = (text: string = '', prefix: string = ''): string
return prefix + text.replace(/\/\/.*/, '')
}

export const splitMonacoValuePerLines = (command = '') => command.split(/\n(?=[^\s])/g)
export const splitMonacoValuePerLines = (command = '') => {
const linesResult: string[] = []
const lines = command.split(/\n(?=[^\s])/g)
lines.forEach((line) => {
const [commandLine, countRepeat] = getCommandRepeat(line)

if (!isRepeatCountCorrect(countRepeat)) {
linesResult.push(line)
return
}
linesResult.push(...Array(countRepeat).fill(commandLine))
})
return linesResult
}

export const getMultiCommands = (commands:string[] = []) => reject(commands, isEmpty).join('\n') ?? ''

Expand Down
Loading