Skip to content

Commit 30d8853

Browse files
author
Artem
committed
Merge branch 'main' into build/rstack
2 parents 15906c0 + 95fae3c commit 30d8853

File tree

90 files changed

+1458
-698
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+1458
-698
lines changed

.github/redisinsight_browser.png

463 KB
Loading

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ RedisInsight is an intuitive and efficient GUI for Redis, allowing you to intera
2323
* Browse, filter and visualise your key-value Redis data structures
2424
* CRUD support for Lists, Hashes, Strings, Sets, Sorted Sets
2525
* CRUD support for [RedisJSON](https://oss.redis.com/redisjson/)
26+
* Profiler - analyze every command sent to Redis in real-time
2627
* Introducing Workbench - advanced command line interface with intelligent command auto-complete and complex data visualizations
2728
* Command auto-complete support for [RediSearch](https://oss.redis.com/redisearch/), [RedisJSON](https://oss.redis.com/redisjson/), [RedisGraph](https://oss.redis.com/redisgraph/), [RedisTimeSeries](https://oss.redis.com/redistimeseries/), [RedisAI](https://oss.redis.com/redisai/)
2829
* Visualizations of your [RediSearch](https://oss.redis.com/redisearch/) index, queries, and aggregations

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@
228228
},
229229
"dependencies": {
230230
"@elastic/datemath": "^5.0.3",
231-
"@elastic/eui": "36.0.0",
231+
"@elastic/eui": "34.6.0",
232232
"@reduxjs/toolkit": "^1.6.2",
233233
"axios": "^0.25.0",
234234
"classnames": "^2.3.1",

redisinsight/api/src/modules/cli/services/cli-business/output-formatter/strategies/raw-formatter.strategy.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('Cli RawFormatterStrategy', () => {
4444
Buffer.from('"quoted key"'),
4545
],
4646
];
47-
const mockResponse = ['0', ['key', '"quoted""key"', '"quoted key"']];
47+
const mockResponse = ['0', ['key', '\\"quoted\\"\\"key\\"', '\\"quoted key\\"']];
4848
const output = strategy.format(input);
4949

5050
expect(output).toEqual(mockResponse);
@@ -69,7 +69,7 @@ describe('Cli RawFormatterStrategy', () => {
6969
const input = Buffer.from(JSON.stringify(object));
7070
const output = strategy.format(input);
7171

72-
expect(output).toEqual(JSON.stringify(object));
72+
expect(output).toEqual('{\\"key\\":\\"value\\"}');
7373
});
7474
});
7575
});

redisinsight/api/src/modules/cli/services/cli-business/output-formatter/strategies/raw-formatter.strategy.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { isArray, isObject } from 'lodash';
2+
import { getASCIISafeStringFromBuffer } from 'src/utils/cli-helper';
23
import { IOutputFormatterStrategy } from '../output-formatter.interface';
34

45
export class RawFormatterStrategy implements IOutputFormatterStrategy {
56
public format(reply: any): any {
67
if (reply instanceof Buffer) {
7-
return this.formatRedisBufferReply(reply);
8+
return getASCIISafeStringFromBuffer(reply);
89
}
910
if (isArray(reply)) {
1011
return this.formatRedisArrayReply(reply);
@@ -29,10 +30,6 @@ export class RawFormatterStrategy implements IOutputFormatterStrategy {
2930
return result;
3031
}
3132

32-
private formatRedisBufferReply(reply: Buffer): string {
33-
return reply.toString();
34-
}
35-
3633
private formatRedisObjectReply(reply: Object): object {
3734
const result = {};
3835
Object.keys(reply).forEach((key) => {

redisinsight/api/src/modules/core/services/analytics/analytics.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ describe('AnalyticsService', () => {
115115
});
116116

117117
expect(mockAnalyticsTrack).toHaveBeenCalledWith({
118-
anonymousId: NON_TRACKING_ANONYMOUS_ID,
118+
anonymousId: mockAnonymousId,
119119
integrations: { Amplitude: { session_id: sessionId } },
120120
event: TelemetryEvents.ApplicationStarted,
121121
properties: {},

redisinsight/api/src/modules/core/services/analytics/analytics.service.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class AnalyticsService {
6161
'agreements.analytics',
6262
false,
6363
);
64-
if (isAnalyticsGranted) {
64+
if (isAnalyticsGranted || nonTracking) {
6565
this.analytics.track({
6666
anonymousId: this.anonymousId,
6767
integrations: { Amplitude: { session_id: this.sessionId } },
@@ -70,15 +70,6 @@ export class AnalyticsService {
7070
...eventData,
7171
},
7272
});
73-
} else if (nonTracking) {
74-
this.analytics.track({
75-
anonymousId: NON_TRACKING_ANONYMOUS_ID,
76-
integrations: { Amplitude: { session_id: this.sessionId } },
77-
event,
78-
properties: {
79-
...eventData,
80-
},
81-
});
8273
}
8374
} catch (e) {
8475
// continue regardless of error

redisinsight/api/src/modules/workbench/providers/workbench-commands.executor.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import {
1616
import { CommandExecutionResult } from 'src/modules/workbench/models/command-execution-result';
1717
import { CreateCommandExecutionDto } from 'src/modules/workbench/dto/create-command-execution.dto';
1818
import { RedisToolService } from 'src/modules/shared/services/base/redis-tool.service';
19+
import { RawFormatterStrategy } from 'src/modules/cli/services/cli-business/output-formatter/strategies/raw-formatter.strategy';
1920
import { WorkbenchAnalyticsService } from '../services/workbench-analytics/workbench-analytics.service';
2021

2122
@Injectable()
2223
export class WorkbenchCommandsExecutor {
2324
private logger = new Logger('WorkbenchCommandsExecutor');
2425

26+
private formatter = new RawFormatterStrategy();
27+
2528
constructor(
2629
private redisTool: RedisToolService,
2730
private analyticsService: WorkbenchAnalyticsService,
@@ -60,7 +63,10 @@ export class WorkbenchCommandsExecutor {
6063

6164
try {
6265
const [command, ...args] = splitCliCommandLine(commandLine);
63-
const response = await this.redisTool.execCommand(clientOptions, command, args, 'utf-8');
66+
const response = this.formatter.format(
67+
await this.redisTool.execCommand(clientOptions, command, args),
68+
);
69+
6470
this.logger.log('Succeed to execute workbench command.');
6571

6672
const result = { response, status: CommandExecutionStatus.Success };
@@ -100,7 +106,6 @@ export class WorkbenchCommandsExecutor {
100106
args,
101107
role,
102108
nodeAddress,
103-
'utf-8',
104109
);
105110
if (result.error && checkRedirectionError(result.error) && nodeOptions.enableRedirection) {
106111
const { slot, address } = parseRedirectionError(result.error);
@@ -110,7 +115,6 @@ export class WorkbenchCommandsExecutor {
110115
args,
111116
role,
112117
address,
113-
'utf-8',
114118
);
115119
result.slot = parseInt(slot, 10);
116120
}
@@ -119,7 +123,12 @@ export class WorkbenchCommandsExecutor {
119123
const {
120124
host, port, error, slot, ...rest
121125
} = result;
122-
return { ...rest, node: { host, port, slot } };
126+
127+
return {
128+
...rest,
129+
response: this.formatter.format(rest.response),
130+
node: { host, port, slot },
131+
};
123132
} catch (error) {
124133
this.logger.error('Failed to execute redis.cluster CLI command.', error);
125134
const result = { response: error.message, status: CommandExecutionStatus.Fail };
@@ -148,12 +157,16 @@ export class WorkbenchCommandsExecutor {
148157
const [command, ...args] = splitCliCommandLine(commandLine);
149158

150159
return (
151-
await this.redisTool.execCommandForNodes(clientOptions, command, args, role, 'utf-8')
160+
await this.redisTool.execCommandForNodes(clientOptions, command, args, role)
152161
).map((nodeExecReply) => {
153162
const {
154163
response, status, host, port,
155164
} = nodeExecReply;
156-
const result = { response, status, node: { host, port } };
165+
const result = {
166+
response: this.formatter.format(response),
167+
status,
168+
node: { host, port },
169+
};
157170
this.analyticsService.sendCommandExecutedEvent(clientOptions.instanceId, result, { command });
158171
return result;
159172
});

redisinsight/api/src/utils/auto-discovery-helper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const getRunningProcesses = async (): Promise<string[]> => new Promise((r
3636
stdoutData += data.toString();
3737
});
3838

39-
proc.stdout.on('error', (e) => {
39+
proc.on('error', (e) => {
4040
reject(e);
4141
});
4242

redisinsight/api/src/utils/cli-helper.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ERROR_MESSAGES from 'src/constants/error-messages';
44
import { CommandParsingError, RedirectionParsingError } from 'src/modules/cli/constants/errors';
55
import { ReplyError } from 'src/models';
66
import { IRedirectionInfo } from 'src/modules/cli/services/cli-business/output-formatter/output-formatter.interface';
7+
import { IS_NON_PRINTABLE_ASCII_CHARACTER } from 'src/constants';
78

89
const LOGGER_CONFIG = config.get('logger');
910
const BLANK_LINE_REGEX = /^\s*\n/gm;
@@ -219,3 +220,43 @@ export const multilineCommandToOneLine = (text: string = '') => text
219220
.split(/(\r\n|\n|\r)+\s+/gm)
220221
.filter((line: string) => !(BLANK_LINE_REGEX.test(line) || isEmpty(line)))
221222
.join(' ');
223+
224+
/**
225+
* Produces an escaped string representation of a byte string.
226+
* Ported from sdscatrepr() function in sds.c from Redis source code.
227+
* This is the function redis-cli uses to escape strings for output.
228+
* @param reply
229+
*/
230+
export const getASCIISafeStringFromBuffer = (reply: Buffer): string => {
231+
let result = '';
232+
reply.forEach((byte: number) => {
233+
const char = Buffer.from([byte]).toString();
234+
if (IS_NON_PRINTABLE_ASCII_CHARACTER.test(char)) {
235+
result += `\\x${decimalToHexString(byte)}`;
236+
} else {
237+
switch (char) {
238+
case '\u0007': // Bell character
239+
result += '\\a';
240+
break;
241+
case '"':
242+
result += `\\${char}`;
243+
break;
244+
case '\b':
245+
result += '\\b';
246+
break;
247+
case '\t':
248+
result += '\\t';
249+
break;
250+
case '\n':
251+
result += '\\n';
252+
break;
253+
case '\r':
254+
result += '\\r';
255+
break;
256+
default:
257+
result += char;
258+
}
259+
}
260+
});
261+
return result;
262+
};

0 commit comments

Comments
 (0)