Skip to content

Commit 6fada36

Browse files
#RI-2509-add wb group mode (#1122)
* #RI-2509-add wb group mode
1 parent 1fb6264 commit 6fada36

File tree

22 files changed

+307
-33
lines changed

22 files changed

+307
-33
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {MigrationInterface, QueryRunner} from "typeorm";
2+
3+
export class workbenchSummary1662523634724 implements MigrationInterface {
4+
name = 'workbenchSummary1662523634724'
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`DROP INDEX "IDX_5cd90dd6def1fd7c521e53fb2c"`);
8+
await queryRunner.query(`CREATE TABLE "temporary_command_execution" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "command" text NOT NULL, "result" text NOT NULL, "role" varchar, "nodeOptions" varchar, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "mode" varchar, "summary" varchar, CONSTRAINT "FK_ea8adfe9aceceb79212142206b8" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
9+
await queryRunner.query(`INSERT INTO "temporary_command_execution"("id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode" FROM "command_execution"`);
10+
await queryRunner.query(`DROP TABLE "command_execution"`);
11+
await queryRunner.query(`ALTER TABLE "temporary_command_execution" RENAME TO "command_execution"`);
12+
await queryRunner.query(`CREATE INDEX "IDX_5cd90dd6def1fd7c521e53fb2c" ON "command_execution" ("createdAt") `);
13+
}
14+
15+
public async down(queryRunner: QueryRunner): Promise<void> {
16+
await queryRunner.query(`DROP INDEX "IDX_5cd90dd6def1fd7c521e53fb2c"`);
17+
await queryRunner.query(`ALTER TABLE "command_execution" RENAME TO "temporary_command_execution"`);
18+
await queryRunner.query(`CREATE TABLE "command_execution" ("id" varchar PRIMARY KEY NOT NULL, "databaseId" varchar NOT NULL, "command" text NOT NULL, "result" text NOT NULL, "role" varchar, "nodeOptions" varchar, "encryption" varchar, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "mode" varchar, CONSTRAINT "FK_ea8adfe9aceceb79212142206b8" FOREIGN KEY ("databaseId") REFERENCES "database_instance" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`);
19+
await queryRunner.query(`INSERT INTO "command_execution"("id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode") SELECT "id", "databaseId", "command", "result", "role", "nodeOptions", "encryption", "createdAt", "mode" FROM "temporary_command_execution"`);
20+
await queryRunner.query(`DROP TABLE "temporary_command_execution"`);
21+
await queryRunner.query(`CREATE INDEX "IDX_5cd90dd6def1fd7c521e53fb2c" ON "command_execution" ("createdAt") `);
22+
}
23+
24+
}

redisinsight/api/migration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { sni1650278664000 } from "./1650278664000-sni";
1616
import { notification1655821010349 } from './1655821010349-notification';
1717
import { notificationCategory1659687030433 } from './1659687030433-notification-category';
1818
import { workbenchMode1660664717573 } from './1660664717573-workbench-mode';
19+
import { workbenchSummary1662523634724 } from './1662523634724-workbench-summary';
1920

2021
export default [
2122
initialMigration1614164490968,
@@ -36,4 +37,5 @@ export default [
3637
notification1655821010349,
3738
notificationCategory1659687030433,
3839
workbenchMode1660664717573,
40+
workbenchSummary1662523634724,
3941
];

redisinsight/api/src/modules/workbench/dto/create-command-execution.dto.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
22
import {
3-
IsEnum, IsNotEmpty, IsNotEmptyObject, IsOptional, IsString, ValidateNested,
3+
IsBoolean, IsEnum, IsNotEmpty, IsNotEmptyObject, IsOptional, IsString, ValidateNested,
44
} from 'class-validator';
55
import { Type } from 'class-transformer';
66
import { ClusterSingleNodeOptions } from 'src/modules/cli/dto/cli.dto';
@@ -38,6 +38,14 @@ export class CreateCommandExecutionDto {
3838
})
3939
mode?: RunQueryMode = RunQueryMode.ASCII;
4040

41+
@ApiPropertyOptional({
42+
description: 'Workbench group mode',
43+
default: false,
44+
})
45+
@IsOptional()
46+
@IsBoolean()
47+
isGroupMode?: boolean;
48+
4149
@ApiPropertyOptional({
4250
description: 'Execute command for nodes with defined role',
4351
default: ClusterNodeRole.All,

redisinsight/api/src/modules/workbench/dto/create-command-executions.dto.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
22
import {
3-
IsEnum, IsArray, IsNotEmptyObject, IsOptional, ArrayNotEmpty, ValidateNested,
3+
IsBoolean, IsEnum, IsArray, IsNotEmptyObject, IsOptional, ArrayNotEmpty, ValidateNested,
44
} from 'class-validator';
55
import { Type } from 'class-transformer';
66
import { ClusterSingleNodeOptions } from 'src/modules/cli/dto/cli.dto';
@@ -29,6 +29,14 @@ export class CreateCommandExecutionsDto {
2929
})
3030
mode?: RunQueryMode = RunQueryMode.ASCII;
3131

32+
@ApiPropertyOptional({
33+
description: 'Workbench group mode',
34+
default: false,
35+
})
36+
@IsOptional()
37+
@IsBoolean()
38+
isGroupMode?: boolean;
39+
3240
@ApiPropertyOptional({
3341
description: 'Execute command for nodes with defined role',
3442
default: ClusterNodeRole.All,

redisinsight/api/src/modules/workbench/entities/command-execution.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export class CommandExecutionEntity {
4343
@Column({ nullable: true })
4444
role?: string;
4545

46+
@Column({ nullable: true })
47+
summary?: string;
48+
4649
@Column({ nullable: true })
4750
@Transform((object) => JSON.stringify(object), { toClassOnly: true })
4851
@Transform((string) => {

redisinsight/api/src/modules/workbench/models/command-execution.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ export class CommandExecution {
3434
@Expose()
3535
mode?: RunQueryMode = RunQueryMode.ASCII;
3636

37+
@ApiPropertyOptional({
38+
description: 'Workbench executions summary',
39+
type: String,
40+
})
41+
@Expose()
42+
summary?: string;
43+
3744
@ApiProperty({
3845
description: 'Command execution result',
3946
type: () => CommandExecutionResult,

redisinsight/api/src/modules/workbench/providers/command-execution.provider.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,17 @@ export class CommandExecutionProvider {
8181
const entities = await this.commandExecutionRepository
8282
.createQueryBuilder('e')
8383
.where({ databaseId })
84-
.select(['e.id', 'e.command', 'e.databaseId', 'e.createdAt', 'e.encryption', 'e.role', 'e.nodeOptions', 'e.mode'])
84+
.select([
85+
'e.id',
86+
'e.command',
87+
'e.databaseId',
88+
'e.createdAt',
89+
'e.encryption',
90+
'e.role',
91+
'e.nodeOptions',
92+
'e.mode',
93+
'e.summary',
94+
])
8595
.orderBy('e.createdAt', 'DESC')
8696
.limit(WORKBENCH_CONFIG.maxItemsPerDb)
8797
.getMany();

redisinsight/api/src/modules/workbench/workbench.service.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,57 @@ export class WorkbenchService {
5252
return commandExecution;
5353
}
5454

55+
/**
56+
* Send redis command from workbench and save history
57+
*
58+
* @param clientOptions
59+
* @param dto
60+
*/
61+
async createCommandsExecution(
62+
clientOptions: IFindRedisClientInstanceByOptions,
63+
dto: Partial<CreateCommandExecutionDto>,
64+
commands: string[],
65+
): Promise<Partial<CommandExecution>> {
66+
const commandExecution: Partial<CommandExecution> = {
67+
...dto,
68+
databaseId: clientOptions.instanceId,
69+
};
70+
71+
const executionResults = await Promise.all(commands.map(async (singleCommand) => {
72+
const command = multilineCommandToOneLine(singleCommand);
73+
const deprecatedCommand = this.findCommandInBlackList(command);
74+
if (deprecatedCommand) {
75+
return {
76+
[command]: [
77+
{
78+
response: ERROR_MESSAGES.WORKBENCH_COMMAND_NOT_SUPPORTED(deprecatedCommand.toUpperCase()),
79+
status: CommandExecutionStatus.Fail,
80+
},
81+
],
82+
};
83+
}
84+
return ({ [command]: await this.commandsExecutor.sendCommand(clientOptions, { ...dto, command }) });
85+
}));
86+
87+
const successCommands = executionResults.filter(
88+
(command) => Object.values(command)[0][0].status === CommandExecutionStatus.Success,
89+
);
90+
const failedCommands = executionResults.filter(
91+
(command) => Object.values(command)[0][0].status === CommandExecutionStatus.Fail,
92+
);
93+
94+
commandExecution.summary = `
95+
${executionResults.length} Commands - ${successCommands.length} success, ${failedCommands.length} errors
96+
`;
97+
commandExecution.command = commands.join('\r\n');
98+
commandExecution.result = [{
99+
status: CommandExecutionStatus.Success,
100+
response: executionResults,
101+
}];
102+
103+
return commandExecution;
104+
}
105+
55106
/**
56107
* Send redis command from workbench and save history
57108
*
@@ -62,6 +113,11 @@ export class WorkbenchService {
62113
clientOptions: IFindRedisClientInstanceByOptions,
63114
dto: CreateCommandExecutionsDto,
64115
): Promise<CommandExecution[]> {
116+
if (dto.isGroupMode) {
117+
return this.commandExecutionProvider.createMany(
118+
[await this.createCommandsExecution(clientOptions, dto, dto.commands)],
119+
);
120+
}
65121
// todo: rework to support pipeline
66122
// prepare and execute commands
67123
const commandExecutions = await Promise.all(
Lines changed: 6 additions & 0 deletions
Loading

redisinsight/ui/src/components/query-card/QueryCard.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface Props {
3030
result: Maybe<CommandExecutionResult[]>
3131
activeMode: RunQueryMode
3232
mode: RunQueryMode
33+
summary?: string
3334
createdAt?: Date
3435
loading?: boolean
3536
onQueryDelete: () => void
@@ -47,6 +48,7 @@ const QueryCard = (props: Props) => {
4748
result,
4849
activeMode,
4950
mode,
51+
summary,
5052
isOpen,
5153
createdAt,
5254
onQueryOpen,
@@ -146,6 +148,7 @@ const QueryCard = (props: Props) => {
146148
selectedValue={selectedViewValue}
147149
activeMode={activeMode}
148150
mode={mode}
151+
summary={summary}
149152
toggleOpen={toggleOpen}
150153
toggleFullScreen={toggleFullScreen}
151154
setSelectedValue={changeViewTypeSelected}
@@ -179,6 +182,7 @@ const QueryCard = (props: Props) => {
179182
<QueryCardCliResult
180183
loading={loading}
181184
query={command}
185+
summary={summary}
182186
result={result}
183187
/>
184188
)}

0 commit comments

Comments
 (0)