Skip to content

Commit 19e7664

Browse files
Merge pull request #116 from RedisInsight/fix/RI-2156_cluster-redirection
Fix/ CLI Redirection message
2 parents 0b741bc + afec6f3 commit 19e7664

File tree

10 files changed

+72
-30
lines changed

10 files changed

+72
-30
lines changed

redisinsight/api/src/modules/cli/dto/cli.dto.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ export enum ClusterNodeRole {
2727
Slave = 'SLAVE',
2828
}
2929

30+
class ClusterNode extends EndpointDto {
31+
@ApiPropertyOptional({
32+
description: 'Cluster node slot.',
33+
type: Number,
34+
example: 0,
35+
})
36+
slot?: number;
37+
}
38+
3039
export class CreateCliClientDto {
3140
@ApiPropertyOptional({
3241
type: String,
@@ -123,10 +132,10 @@ export class SendClusterCommandResponse {
123132
response: any;
124133

125134
@ApiPropertyOptional({
126-
type: () => EndpointDto,
135+
type: () => ClusterNode,
127136
description: 'Redis Cluster Node info',
128137
})
129-
node?: EndpointDto;
138+
node?: ClusterNode;
130139

131140
@ApiProperty({
132141
description: 'Redis CLI command execution status',

redisinsight/api/src/modules/cli/services/cli-business/cli-business.service.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ describe('CliBusinessService', () => {
555555
const command = 'set foo bar';
556556
const mockResult: SendClusterCommandResponse = {
557557
response: 'OK',
558-
node: { ...mockNode, port: 7002 },
558+
node: { ...mockNode, port: 7002, slot: 7008 },
559559
status: CommandExecutionStatus.Success,
560560
};
561561
cliTool.execCommandForNode
@@ -586,7 +586,7 @@ describe('CliBusinessService', () => {
586586
const command = 'set foo bar';
587587
const mockResult: SendClusterCommandResponse = {
588588
response: '-> Redirected to slot [7008] located at 127.0.0.1:7002\nOK',
589-
node: { ...mockNode, port: 7002 },
589+
node: { ...mockNode, port: 7002, slot: 7008 },
590590
status: CommandExecutionStatus.Success,
591591
};
592592
cliTool.execCommandForNode

redisinsight/api/src/modules/cli/services/cli-business/cli-business.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ export class CliBusinessService {
306306
replyEncoding,
307307
);
308308
result.response = formatter.format(result.response, { slot, address });
309+
result.slot = parseInt(slot, 10);
309310
} else {
310311
result.response = formatter.format(result.response);
311312
}
@@ -316,9 +317,9 @@ export class CliBusinessService {
316317
{ command, outputFormat },
317318
);
318319
const {
319-
host, port, error, ...rest
320+
host, port, error, slot, ...rest
320321
} = result;
321-
return { ...rest, node: { host, port } };
322+
return { ...rest, node: { host, port, slot } };
322323
} catch (error) {
323324
this.logger.error('Failed to execute redis.cluster CLI command.', error);
324325

redisinsight/api/src/modules/cli/services/cli-tool/cli-tool.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface ICliExecResultFromNode {
2525
port: number;
2626
response: any;
2727
status: CommandExecutionStatus;
28+
slot?: number;
2829
error?: any,
2930
}
3031

redisinsight/api/test/api/cli/POST-instance-id-cli-uuid-send_cluster_command.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const responseSchema = Joi.array().items(Joi.object().keys({
3737
node: Joi.object().keys({
3838
host: Joi.string().required(),
3939
port: Joi.number().integer().required(),
40+
slot: Joi.number().integer(),
4041
})
4142
}).required());
4243

@@ -46,6 +47,7 @@ const responseRawSchema = Joi.array().items(Joi.object().keys({
4647
node: Joi.object().keys({
4748
host: Joi.string().required(),
4849
port: Joi.number().integer().required(),
50+
slot: Joi.number().integer(),
4951
})
5052
}).required());
5153

redisinsight/ui/src/slices/cli/cli-output.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { createSlice } from '@reduxjs/toolkit'
33
import { CliOutputFormatterType, cliTexts } from 'uiSrc/constants/cliOutput'
44
import { apiService, localStorageService } from 'uiSrc/services'
55
import { ApiEndpoints, BrowserStorageItem } from 'uiSrc/constants'
6-
import { cliCommandOutput, cliParseTextResponseWithOffset } from 'uiSrc/utils/cliHelper'
7-
import { getUrl, getApiErrorMessage, isStatusSuccessful } from 'uiSrc/utils'
86
import {
9-
SendClusterCommandDto,
10-
SendClusterCommandResponse,
11-
SendCommandResponse,
12-
} from 'apiSrc/modules/cli/dto/cli.dto'
7+
cliCommandOutput,
8+
cliParseTextResponseWithOffset,
9+
cliParseTextResponseWithRedirect,
10+
} from 'uiSrc/utils/cliHelper'
11+
import { getApiErrorMessage, getUrl, isStatusSuccessful } from 'uiSrc/utils'
12+
import { SendClusterCommandDto, SendClusterCommandResponse, SendCommandResponse, } from 'apiSrc/modules/cli/dto/cli.dto'
1313

1414
import { AppDispatch, RootState } from '../store'
1515
import { CommandExecutionStatus, StateCliOutput } from '../interfaces/cli'
@@ -124,6 +124,7 @@ export function sendCliClusterCommandAction(
124124
) {
125125
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
126126
try {
127+
const outputFormat = CliOutputFormatterType.Raw
127128
const state = stateInit()
128129
const { id = '' } = state.connections.instances?.connectedInstance
129130

@@ -137,7 +138,7 @@ export function sendCliClusterCommandAction(
137138

138139
const {
139140
data: [
140-
{ response, status: dataStatus }
141+
{ response, status: dataStatus, node: nodeOptionsResponse }
141142
] = [],
142143
status
143144
} = await apiService.post<SendClusterCommandResponse[]>(
@@ -147,15 +148,23 @@ export function sendCliClusterCommandAction(
147148
state.cli.settings?.cliClientUuid,
148149
ApiEndpoints.SEND_CLUSTER_COMMAND
149150
),
150-
{ ...options, command, outputFormat: CliOutputFormatterType.Raw }
151+
{ ...options, command, outputFormat }
151152
)
152153

153154
if (isStatusSuccessful(status)) {
155+
let isRedirected = false
156+
if (options.nodeOptions && nodeOptionsResponse) {
157+
const requestNodeAddress = `${options.nodeOptions.host}:${options.nodeOptions.port}`
158+
const responseNodeAddress = `${nodeOptionsResponse.host}:${nodeOptionsResponse.port}`
159+
isRedirected = requestNodeAddress !== responseNodeAddress
160+
}
154161
onSuccessAction?.()
155162
dispatch(sendCliCommandSuccess())
156-
dispatch(
157-
concatToOutput(cliParseTextResponseWithOffset(response, command, dataStatus))
158-
)
163+
const result = outputFormat === CliOutputFormatterType.Raw && isRedirected
164+
? cliParseTextResponseWithRedirect(response, command, dataStatus, nodeOptionsResponse)
165+
: cliParseTextResponseWithOffset(response, command, dataStatus)
166+
167+
dispatch(concatToOutput(result))
159168
}
160169
} catch (error) {
161170
const errorMessage = getApiErrorMessage(error)

redisinsight/ui/src/slices/interfaces/instances.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ export interface ClusterNode {
193193
host: string;
194194
port: number;
195195
role?: 'slave' | 'master';
196+
slot?: number;
196197
}
197198

198199
export enum RedisCloudSubscriptionStatus {

redisinsight/ui/src/slices/tests/cli/cli-output.spec.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111
import { ClusterNodeRole, CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
1212
import { apiService } from 'uiSrc/services'
1313
import { cliTexts } from 'uiSrc/constants/cliOutput'
14-
import { cliCommandOutput, cliParseTextResponseWithOffset } from 'uiSrc/utils/cliHelper'
14+
import {
15+
cliCommandOutput,
16+
cliParseTextResponseWithOffset,
17+
cliParseTextResponseWithRedirect
18+
} from 'uiSrc/utils/cliHelper'
1519
import reducer, {
1620
initialState,
1721
concatToOutput,
@@ -282,9 +286,9 @@ describe('cliOutput slice', () => {
282286
const command = 'keys *'
283287
const data: SendClusterCommandResponse[] = [
284288
{
285-
response: '-> Redirected to slot [6918] located at 127.0.0.1:7002\n(nil)',
289+
response: '(nil)',
286290
status: 'success',
287-
node: { host: '127.0.0.1', port: 7002 },
291+
node: { host: '127.0.0.1', port: 7002, slot: 6918 },
288292
},
289293
]
290294
const responsePayload = { data, status: 200 }
@@ -300,7 +304,9 @@ describe('cliOutput slice', () => {
300304
sendCliCommand(),
301305
sendCliCommandSuccess(),
302306
concatToOutput(
303-
cliParseTextResponseWithOffset(first(data)?.response, command, first(data)?.status)
307+
cliParseTextResponseWithRedirect(
308+
first(data)?.response, command, first(data)?.status, first(data)?.node
309+
)
304310
),
305311
]
306312
expect(clearStoreActions(store.getActions())).toEqual(clearStoreActions(expectedActions))
@@ -311,9 +317,9 @@ describe('cliOutput slice', () => {
311317
const command = 'keys *'
312318
const data: SendClusterCommandResponse[] = [
313319
{
314-
response: '-> Redirected to slot [6918] located at 127.0.0.1:7002\n(nil)',
320+
response: null,
315321
status: 'success',
316-
node: { host: '127.0.0.1', port: 7002 },
322+
node: { host: '127.0.0.1', port: 7002, slot: 6918 },
317323
},
318324
]
319325
const responsePayload = { data, status: 200 }
@@ -329,7 +335,9 @@ describe('cliOutput slice', () => {
329335
sendCliCommand(),
330336
sendCliCommandSuccess(),
331337
concatToOutput(
332-
cliParseTextResponseWithOffset(first(data)?.response, command, first(data)?.status)
338+
cliParseTextResponseWithRedirect(
339+
first(data)?.response, command, first(data)?.status, first(data)?.node
340+
)
333341
),
334342
]
335343
expect(clearStoreActions(store.getActions())).toEqual(clearStoreActions(expectedActions))

redisinsight/ui/src/utils/cliHelper.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
66
import { resetOutput, updateCliCommandHistory } from 'uiSrc/slices/cli/cli-output'
77
import { BrowserStorageItem } from 'uiSrc/constants'
88
import { ModuleCommandPrefix } from 'uiSrc/pages/workbench/constants'
9-
import { RedisDefaultModules } from 'uiSrc/slices/interfaces'
9+
import { ClusterNode, RedisDefaultModules } from 'uiSrc/slices/interfaces'
1010

1111
import { RedisModuleDto } from 'apiSrc/modules/instances/dto/database-instance.dto'
1212
import { Nullable } from './types'
@@ -17,6 +17,20 @@ export enum CliPrefix {
1717
QueryCard = 'query-card',
1818
}
1919

20+
const cliParseTextResponseWithRedirect = (
21+
text: string = '',
22+
command: string = '',
23+
status: CommandExecutionStatus = CommandExecutionStatus.Success,
24+
redirectTo: ClusterNode | undefined,
25+
) => {
26+
let redirectMessage = ''
27+
if (redirectTo) {
28+
const { host, port, slot } = redirectTo
29+
redirectMessage = `-> Redirected to slot [${slot}] located at ${host}:${port}`
30+
}
31+
return [redirectMessage, '\n', cliParseTextResponse(text, command, status), '\n\n']
32+
}
33+
2034
const cliParseTextResponseWithOffset = (
2135
text: string = '',
2236
command: string = '',
@@ -110,6 +124,7 @@ const checkUnsupportedModuleCommand = (loadedModules: RedisModuleDto[], commandL
110124
export {
111125
cliParseTextResponse,
112126
cliParseTextResponseWithOffset,
127+
cliParseTextResponseWithRedirect,
113128
cliCommandOutput,
114129
bashTextValue,
115130
cliCommandWrapper,

redisinsight/ui/src/utils/cliTextFormatter.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
} from 'lodash'
44
import { bulkReplyCommands } from 'uiSrc/constants'
55

6-
const formatToText = (reply: any, command: string = '', redirectedTo: any = false): string => {
6+
const formatToText = (reply: any, command: string = ''): string => {
77
let result
88
if (isNull(reply)) {
99
result = '(nil)'
@@ -19,10 +19,6 @@ const formatToText = (reply: any, command: string = '', redirectedTo: any = fals
1919
result = reply
2020
}
2121

22-
if (redirectedTo) {
23-
const { slot, address } = redirectedTo
24-
result = `-> Redirected to slot [${slot}] located at ${address}\n${result}`
25-
}
2622
return result
2723
}
2824

0 commit comments

Comments
 (0)