Skip to content

Commit f429101

Browse files
committed
Merge branch 'main' into feature/RI-4815-portion-of-string
2 parents 6026ac8 + 3df175e commit f429101

File tree

55 files changed

+1960
-482
lines changed

Some content is hidden

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

55 files changed

+1960
-482
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ RUN apk add --no-cache --virtual .gyp \
55
make \
66
g++
77
WORKDIR /usr/src/app
8-
COPY package.json yarn.lock babel.config.js tsconfig.json ./
8+
COPY package.json yarn.lock babel.config.cjs tsconfig.json ./
99
RUN SKIP_POSTINSTALL=1 yarn install
1010
COPY configs ./configs
1111
COPY scripts ./scripts

redisinsight/api/src/__mocks__/ssh.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export const mockSshOptionsBasic = Object.assign(new SshOptions(), {
1919
port: 22,
2020
username: mockSshOptionsUsernamePlain,
2121
password: mockSshOptionsPasswordPlain,
22-
privateKey: null,
23-
passphrase: null,
22+
privateKey: undefined,
23+
passphrase: undefined,
2424
});
2525

2626
export const mockSshOptionsBasicEntity = Object.assign(new SshOptionsEntity(), {
@@ -32,7 +32,7 @@ export const mockSshOptionsBasicEntity = Object.assign(new SshOptionsEntity(), {
3232

3333
export const mockSshOptionsPrivateKey = Object.assign(new SshOptions(), {
3434
...mockSshOptionsBasic,
35-
password: null,
35+
password: undefined,
3636
privateKey: mockSshOptionsPrivateKeyPlain,
3737
passphrase: mockSshOptionsPassphrasePlain,
3838
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Transform } from 'class-transformer';
2+
3+
export function HiddenField(field: any): PropertyDecorator {
4+
return Transform((value: string) => (value ? field : undefined), {
5+
toPlainOnly: true,
6+
});
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './certificate-import.util';
22
export * from './errors.util';
3+
export * from './merge.util';
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { deepMerge } from 'src/common/utils/merge.util';
2+
3+
const deepMergeTests = [
4+
{ obj1: {}, obj2: {}, result: {} },
5+
{ obj1: { value: 1 }, obj2: { value: 2 }, result: { value: 2 } },
6+
{ obj1: { value: 1 }, obj2: { value: 1.0000001 }, result: { value: 1.0000001 } },
7+
{ obj1: { value: 1 }, obj2: { value: '2' }, result: { value: '2' } },
8+
{ obj1: { value: 1 }, obj2: { value: undefined }, result: { value: 1 } },
9+
{ obj1: { value: 1 }, obj2: { value: null }, result: { value: null } },
10+
{ obj1: { value: 0 }, obj2: { value: null }, result: { value: null } },
11+
{ obj1: { value: false }, obj2: { value: 1 }, result: { value: 1 } },
12+
{ obj1: { value: false }, obj2: { value: true }, result: { value: true } },
13+
{ obj1: { value: false }, obj2: { value: '1' }, result: { value: '1' } },
14+
{ obj1: { value: false }, obj2: { value: false }, result: { value: false } },
15+
{ obj1: { value: false }, obj2: { value: 1 }, result: { value: 1 } },
16+
{ obj1: { value: false }, obj2: { value: true }, result: { value: true } },
17+
{ obj1: { value: false }, obj2: { value: '1' }, result: { value: '1' } },
18+
{ obj1: { value: '1' }, obj2: { value: '2' }, result: { value: '2' } },
19+
{ obj1: { value: undefined }, obj2: { value: 2 }, result: { value: 2 } },
20+
{ obj1: { value: undefined }, obj2: { value: null }, result: { value: null } },
21+
{ obj1: { value: null }, obj2: { value: null }, result: { value: null } },
22+
{ obj1: {}, obj2: { value: 2 }, result: { value: 2 } },
23+
{ obj1: { value: {} }, obj2: { value: 1 }, result: { value: 1 } },
24+
{ obj1: { value: [] }, obj2: { value: 0 }, result: { value: 0 } },
25+
{ obj1: { value: [] }, obj2: { value: [1] }, result: { value: [1] } },
26+
{ obj1: { value: [] }, obj2: { value: undefined }, result: { value: [] } },
27+
{ obj1: { value: { name: 1 } }, obj2: [], result: { value: { name: 1 } } },
28+
{ obj1: { value: [] }, obj2: { value: { name: 1 } }, result: { value: { name: 1 } } },
29+
{ obj1: [1, 2, 3], obj2: [3, 5, 6], result: [3, 5, 6] },
30+
31+
{
32+
obj1: {
33+
value: 1, value2: 'string', value3: null, value4: undefined, nested: { nestedValue1: 1, nestedValue2: 2 },
34+
},
35+
obj2: {
36+
value2: undefined, value3: 1, value4: null, value5: 'new', nested: { nestedValue2: 4, nestedValue3: 'value' },
37+
},
38+
result: {
39+
value: 1,
40+
value2: 'string',
41+
value3: 1,
42+
value4: null,
43+
value5: 'new',
44+
nested: { nestedValue1: 1, nestedValue2: 4, nestedValue3: 'value' },
45+
},
46+
},
47+
{
48+
obj1: {
49+
value: 0,
50+
value2: '',
51+
value3: [],
52+
value4: {},
53+
nested: {
54+
nestedValue1: undefined,
55+
nestedValue2: {
56+
key1: null, key2: undefined, key3: {}, key4: [],
57+
},
58+
nestedValue3: 'value',
59+
},
60+
},
61+
obj2: {
62+
value: 'value',
63+
value3: { name: 1 },
64+
value4: [1, 2, 3],
65+
value5: null,
66+
nested: {
67+
nestedValue1: { key1: 0, key2: undefined, key3: null },
68+
nestedValue2: {
69+
key1: 'value', key3: { name: 1 }, key5: null, key6: 1,
70+
},
71+
nestedValue4: 1.2,
72+
},
73+
},
74+
result: {
75+
value: 'value',
76+
value2: '',
77+
value3: { name: 1 },
78+
value4: [1, 2, 3],
79+
value5: null,
80+
nested: {
81+
nestedValue1: { key1: 0, key2: undefined, key3: null },
82+
nestedValue2: {
83+
key1: 'value', key2: undefined, key3: { name: 1 }, key4: [], key5: null, key6: 1,
84+
},
85+
nestedValue3: 'value',
86+
nestedValue4: 1.2,
87+
},
88+
},
89+
},
90+
];
91+
92+
describe('deepMerge', () => {
93+
test.each(deepMergeTests)('%j', ({ obj1, obj2, result }) => {
94+
expect(
95+
JSON.parse(
96+
JSON.stringify(
97+
deepMerge(obj1, obj2),
98+
),
99+
),
100+
).toStrictEqual(
101+
JSON.parse(
102+
JSON.stringify(result),
103+
),
104+
);
105+
});
106+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {
2+
isObjectLike, isUndefined, mergeWith, isArray,
3+
} from 'lodash';
4+
5+
export const deepMerge = (target: object, source: object) => mergeWith(target, source, (targetValue, sourceValue) => {
6+
if (isUndefined(sourceValue)) {
7+
return targetValue;
8+
}
9+
10+
if (isObjectLike(sourceValue) && !isArray(sourceValue) && !isArray(targetValue)) {
11+
return deepMerge(targetValue, sourceValue);
12+
}
13+
14+
return sourceValue;
15+
});

redisinsight/api/src/modules/database/database-connection.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ describe('DatabaseConnectionService', () => {
158158
});
159159

160160
describe('createClient', () => {
161-
it('should create client for standalone datbaase', async () => {
161+
it('should create client for standalone database', async () => {
162162
expect(await service.createClient(mockCommonClientMetadata)).toEqual(mockIORedisClient);
163163
});
164164
it('should throw Unauthorized error in case of NOAUTH', async () => {

redisinsight/api/src/modules/database/database.analytics.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class DatabaseAnalytics extends TelemetryBaseService {
2323

2424
sendInstanceAddedEvent(
2525
instance: Database,
26-
additionalInfo: RedisDatabaseInfoResponse,
26+
additionalInfo?: RedisDatabaseInfoResponse,
2727
): void {
2828
try {
2929
const modulesSummary = getRedisModulesSummary(instance.modules);
@@ -45,8 +45,8 @@ export class DatabaseAnalytics extends TelemetryBaseService {
4545
version: additionalInfo?.version,
4646
numberOfKeys: additionalInfo?.totalKeys,
4747
numberOfKeysRange: getRangeForNumber(additionalInfo?.totalKeys, TOTAL_KEYS_BREAKPOINTS),
48-
totalMemory: additionalInfo.usedMemory,
49-
numberedDatabases: additionalInfo.databases,
48+
totalMemory: additionalInfo?.usedMemory,
49+
numberedDatabases: additionalInfo?.databases,
5050
numberOfModules: instance.modules?.length || 0,
5151
timeout: instance.timeout / 1_000, // milliseconds to seconds
5252
databaseIndex: instance.db || 0,

redisinsight/api/src/modules/database/database.controller.ts

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
Param,
99
Patch,
1010
Post,
11-
Put,
1211
UseInterceptors,
1312
UsePipes,
1413
ValidationPipe,
@@ -26,9 +25,10 @@ import { DeleteDatabasesDto } from 'src/modules/database/dto/delete.databases.dt
2625
import { DeleteDatabasesResponse } from 'src/modules/database/dto/delete.databases.response';
2726
import { ClientMetadataParam } from 'src/common/decorators';
2827
import { ClientMetadata } from 'src/common/models';
29-
import { ModifyDatabaseDto } from 'src/modules/database/dto/modify.database.dto';
3028
import { ExportDatabasesDto } from 'src/modules/database/dto/export.databases.dto';
3129
import { ExportDatabase } from 'src/modules/database/models/export-database';
30+
import { DatabaseResponse } from 'src/modules/database/dto/database.response';
31+
import { classToClass } from 'src/utils';
3232

3333
@ApiTags('Database')
3434
@Controller('databases')
@@ -64,14 +64,14 @@ export class DatabaseController {
6464
{
6565
status: 200,
6666
description: 'Database instance',
67-
type: Database,
67+
type: DatabaseResponse,
6868
},
6969
],
7070
})
7171
async get(
7272
@Param('id') id: string,
73-
): Promise<Database> {
74-
return await this.service.get(id);
73+
): Promise<DatabaseResponse> {
74+
return classToClass(DatabaseResponse, await this.service.get(id));
7575
}
7676

7777
@UseInterceptors(ClassSerializerInterceptor)
@@ -84,7 +84,7 @@ export class DatabaseController {
8484
{
8585
status: 201,
8686
description: 'Created database instance',
87-
type: Database,
87+
type: DatabaseResponse,
8888
},
8989
],
9090
})
@@ -97,21 +97,21 @@ export class DatabaseController {
9797
)
9898
async create(
9999
@Body() dto: CreateDatabaseDto,
100-
): Promise<Database> {
101-
return await this.service.create(dto, true);
100+
): Promise<DatabaseResponse> {
101+
return classToClass(DatabaseResponse, await this.service.create(dto, true));
102102
}
103103

104104
@UseInterceptors(ClassSerializerInterceptor)
105105
@UseInterceptors(new TimeoutInterceptor(ERROR_MESSAGES.CONNECTION_TIMEOUT))
106-
@Put(':id')
106+
@Patch(':id')
107107
@ApiEndpoint({
108108
description: 'Update database instance by id',
109109
statusCode: 200,
110110
responses: [
111111
{
112112
status: 200,
113113
description: 'Updated database instance\' response',
114-
type: Database,
114+
type: DatabaseResponse,
115115
},
116116
],
117117
})
@@ -125,21 +125,21 @@ export class DatabaseController {
125125
async update(
126126
@Param('id') id: string,
127127
@Body() database: UpdateDatabaseDto,
128-
): Promise<Database> {
129-
return await this.service.update(id, database, true);
128+
): Promise<DatabaseResponse> {
129+
return classToClass(DatabaseResponse, await this.service.update(id, database, true));
130130
}
131131

132132
@UseInterceptors(ClassSerializerInterceptor)
133133
@UseInterceptors(new TimeoutInterceptor(ERROR_MESSAGES.CONNECTION_TIMEOUT))
134-
@Patch(':id')
134+
@Post('clone/:id')
135135
@ApiEndpoint({
136136
description: 'Update database instance by id',
137137
statusCode: 200,
138138
responses: [
139139
{
140140
status: 200,
141141
description: 'Updated database instance\' response',
142-
type: Database,
142+
type: DatabaseResponse,
143143
},
144144
],
145145
})
@@ -150,11 +150,11 @@ export class DatabaseController {
150150
forbidNonWhitelisted: true,
151151
}),
152152
)
153-
async modify(
153+
async clone(
154154
@Param('id') id: string,
155-
@Body() database: ModifyDatabaseDto,
156-
): Promise<Database> {
157-
return await this.service.update(id, database, true);
155+
@Body() database: UpdateDatabaseDto,
156+
): Promise<DatabaseResponse> {
157+
return classToClass(DatabaseResponse, await this.service.clone(id, database));
158158
}
159159

160160
@UseInterceptors(ClassSerializerInterceptor)
@@ -175,9 +175,34 @@ export class DatabaseController {
175175
}),
176176
)
177177
async testConnection(
178-
@Body() database: CreateDatabaseDto,
178+
@Body() dto: CreateDatabaseDto,
179+
): Promise<void> {
180+
return await this.service.testConnection(dto);
181+
}
182+
183+
@UseInterceptors(ClassSerializerInterceptor)
184+
@Post('/test/:id')
185+
@ApiEndpoint({
186+
description: 'Test connection',
187+
statusCode: 200,
188+
responses: [
189+
{
190+
status: 200,
191+
},
192+
],
193+
})
194+
@UsePipes(
195+
new ValidationPipe({
196+
transform: true,
197+
whitelist: true,
198+
forbidNonWhitelisted: true,
199+
}),
200+
)
201+
async testExistConnection(
202+
@Param('id') id: string,
203+
@Body() dto: UpdateDatabaseDto,
179204
): Promise<void> {
180-
return await this.service.testConnection(database);
205+
return this.service.testConnection(dto, id);
181206
}
182207

183208
@Delete('/:id')

0 commit comments

Comments
 (0)