Skip to content

Commit 65fbe73

Browse files
author
Artem
committed
resolve merge conflicts
1 parent f96392b commit 65fbe73

File tree

8 files changed

+188
-14
lines changed

8 files changed

+188
-14
lines changed

redisinsight/api/src/__mocks__/database-import.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ export const mockDatabaseImportAnalytics = jest.fn(() => ({
6262
sendImportResults: jest.fn(),
6363
sendImportFailed: jest.fn(),
6464
}));
65+
66+
export const mockCertificateImportService = jest.fn(() => {
67+
68+
});

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

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { pick } from 'lodash';
22
import { DatabaseImportService } from 'src/modules/database-import/database-import.service';
33
import {
4+
mockCertificateImportService,
45
mockDatabase,
56
mockDatabaseImportAnalytics,
67
mockDatabaseImportFile,
@@ -14,9 +15,11 @@ import { ConnectionType } from 'src/modules/database/entities/database.entity';
1415
import { BadRequestException, ForbiddenException } from '@nestjs/common';
1516
import { ValidationError } from 'class-validator';
1617
import {
17-
NoDatabaseImportFileProvidedException, SizeLimitExceededDatabaseImportFileException,
18+
NoDatabaseImportFileProvidedException,
19+
SizeLimitExceededDatabaseImportFileException,
1820
UnableToParseDatabaseImportFileException,
1921
} from 'src/modules/database-import/exceptions';
22+
import { CertificateImportService } from 'src/modules/database-import/certificate-import.service';
2023

2124
describe('DatabaseImportService', () => {
2225
let service: DatabaseImportService;
@@ -36,6 +39,10 @@ describe('DatabaseImportService', () => {
3639
create: jest.fn().mockResolvedValue(mockDatabase),
3740
})),
3841
},
42+
{
43+
provide: CertificateImportService,
44+
useFactory: mockCertificateImportService,
45+
},
3946
{
4047
provide: DatabaseImportAnalytics,
4148
useFactory: mockDatabaseImportAnalytics,
@@ -154,6 +161,7 @@ describe('DatabaseImportService', () => {
154161
it('should create cluster database', async () => {
155162
await service['createDatabase']({
156163
...mockDatabase,
164+
connectionType: undefined,
157165
cluster: true,
158166
}, 0);
159167

@@ -163,4 +171,64 @@ describe('DatabaseImportService', () => {
163171
});
164172
});
165173
});
174+
175+
describe('determineConnectionType', () => {
176+
const tcs = [
177+
// common
178+
{ input: {}, output: ConnectionType.NOT_CONNECTED },
179+
// isCluster
180+
{ input: { isCluster: true }, output: ConnectionType.CLUSTER },
181+
{ input: { isCluster: false }, output: ConnectionType.NOT_CONNECTED },
182+
{ input: { isCluster: undefined }, output: ConnectionType.NOT_CONNECTED },
183+
// sentinelMasterName
184+
{ input: { sentinelMasterName: 'some name' }, output: ConnectionType.SENTINEL },
185+
// connectionType
186+
{ input: { connectionType: ConnectionType.STANDALONE }, output: ConnectionType.STANDALONE },
187+
{ input: { connectionType: ConnectionType.CLUSTER }, output: ConnectionType.CLUSTER },
188+
{ input: { connectionType: ConnectionType.SENTINEL }, output: ConnectionType.SENTINEL },
189+
{ input: { connectionType: 'something not supported' }, output: ConnectionType.NOT_CONNECTED },
190+
// type
191+
{ input: { type: 'standalone' }, output: ConnectionType.STANDALONE },
192+
{ input: { type: 'cluster' }, output: ConnectionType.CLUSTER },
193+
{ input: { type: 'sentinel' }, output: ConnectionType.SENTINEL },
194+
{ input: { type: 'something not supported' }, output: ConnectionType.NOT_CONNECTED },
195+
// priority tests
196+
{
197+
input: {
198+
connectionType: ConnectionType.SENTINEL,
199+
type: 'standalone',
200+
isCluster: true,
201+
sentinelMasterName: 'some name',
202+
},
203+
output: ConnectionType.SENTINEL,
204+
},
205+
{
206+
input: {
207+
type: 'standalone',
208+
isCluster: true,
209+
sentinelMasterName: 'some name',
210+
},
211+
output: ConnectionType.STANDALONE,
212+
},
213+
{
214+
input: {
215+
isCluster: true,
216+
sentinelMasterName: 'some name',
217+
},
218+
output: ConnectionType.CLUSTER,
219+
},
220+
{
221+
input: {
222+
sentinelMasterName: 'some name',
223+
},
224+
output: ConnectionType.SENTINEL,
225+
},
226+
];
227+
228+
tcs.forEach((tc) => {
229+
it(`should return ${tc.output} when called with ${JSON.stringify(tc.input)}`, () => {
230+
expect(DatabaseImportService.determineConnectionType(tc.input)).toEqual(tc.output);
231+
});
232+
});
233+
});
166234
});

redisinsight/api/src/modules/database-import/database-import.service.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,20 @@ export class DatabaseImportService {
3737
['port', ['port']],
3838
['db', ['db']],
3939
['isCluster', ['cluster']],
40+
['type', ['type']],
41+
['connectionType', ['connectionType']],
4042
['tls', ['tls', 'ssl']],
41-
['tlsServername', ['tlsServername', 'sni_name', 'sni_server_name']],
43+
['tlsServername', ['tlsServername']],
4244
['tlsCaName', ['caCert.name']],
43-
['tlsCaCert', ['caCert.certificate', 'sslOptions.ca', 'ssl_ca_cert_path']],
45+
['tlsCaCert', ['caCert.certificate', 'caCert', 'sslOptions.ca', 'ssl_ca_cert_path']],
4446
['tlsClientName', ['clientCert.name']],
45-
['tlsClientCert', ['clientCert.certificate', 'sslOptions.cert', 'ssl_local_cert_path']],
46-
['tlsClientKey', ['clientCert.key', 'sslOptions.key', 'ssl_private_key_path']],
47+
['tlsClientCert', ['clientCert.certificate', 'certificate', 'sslOptions.cert', 'ssl_local_cert_path']],
48+
['tlsClientKey', ['clientCert.key', 'keyFile', 'sslOptions.key', 'ssl_private_key_path']],
49+
['sentinelMasterName', ['sentinelMaster.name', 'sentinelOptions.masterName', 'sentinelOptions.name']],
50+
['sentinelMasterUsername', ['sentinelMaster.username']],
51+
['sentinelMasterPassword', [
52+
'sentinelMaster.password', 'sentinelOptions.nodePassword', 'sentinelOptions.sentinelPassword',
53+
]],
4754
];
4855

4956
constructor(
@@ -83,6 +90,8 @@ export class DatabaseImportService {
8390
fail: [],
8491
};
8592

93+
console.log('___ items', items);
94+
8695
// it is very important to insert databases on-by-one to avoid db constraint errors
8796
await items.reduce((prev, item, index) => prev.finally(() => this.createDatabase(item, index)
8897
.then((result) => {
@@ -144,11 +153,18 @@ export class DatabaseImportService {
144153
data.name = `${data.host}:${data.port}`;
145154
}
146155

147-
// determine database type
148-
if (data.isCluster) {
149-
data.connectionType = ConnectionType.CLUSTER;
150-
} else {
151-
data.connectionType = ConnectionType.STANDALONE;
156+
data.connectionType = DatabaseImportService.determineConnectionType(data);
157+
158+
if (data?.sentinelMasterName) {
159+
data.sentinelMaster = {
160+
name: data.sentinelMasterName,
161+
username: data.sentinelMasterUsername,
162+
password: data.sentinelMasterPassword,
163+
};
164+
data.nodes = [{
165+
host: data.host,
166+
port: parseInt(data.port, 10),
167+
}];
152168
}
153169

154170
if (data?.tlsCaCert) {
@@ -237,6 +253,49 @@ export class DatabaseImportService {
237253
}
238254
}
239255

256+
/**
257+
* Try to determine connection type based on input data
258+
* Should return NOT_CONNECTED when it is not possible
259+
* @param data
260+
*/
261+
static determineConnectionType(data: any = {}): ConnectionType {
262+
if (data?.connectionType) {
263+
switch (data.connectionType) {
264+
case ConnectionType.CLUSTER:
265+
return ConnectionType.CLUSTER;
266+
case ConnectionType.SENTINEL:
267+
return ConnectionType.SENTINEL;
268+
case ConnectionType.STANDALONE:
269+
return ConnectionType.STANDALONE;
270+
default:
271+
return ConnectionType.NOT_CONNECTED;
272+
}
273+
}
274+
275+
if (data?.type) {
276+
switch (data.type) {
277+
case 'cluster':
278+
return ConnectionType.CLUSTER;
279+
case 'sentinel':
280+
return ConnectionType.SENTINEL;
281+
case 'standalone':
282+
return ConnectionType.STANDALONE;
283+
default:
284+
return ConnectionType.NOT_CONNECTED;
285+
}
286+
}
287+
288+
if (data?.isCluster === true) {
289+
return ConnectionType.CLUSTER;
290+
}
291+
292+
if (data?.sentinelMasterName) {
293+
return ConnectionType.SENTINEL;
294+
}
295+
296+
return ConnectionType.NOT_CONNECTED;
297+
}
298+
240299
/**
241300
* Try to parse file based on mimetype and known\supported formats
242301
* @param file

redisinsight/api/src/modules/database-import/dto/import.database.dto.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { UseClientCertificateDto } from 'src/modules/certificate/dto/use.client-
1313

1414
export class ImportDatabaseDto extends PickType(Database, [
1515
'host', 'port', 'name', 'db', 'username', 'password',
16-
'connectionType', 'tls', 'verifyServerCert',
16+
'connectionType', 'tls', 'verifyServerCert', 'sentinelMaster', 'nodes',
1717
] as const) {
1818
@Expose()
1919
@IsNotEmpty()
@@ -43,5 +43,4 @@ export class ImportDatabaseDto extends PickType(Database, [
4343
@Type(clientCertTransformer)
4444
@ValidateNested()
4545
clientCert?: CreateClientCertificateDto | UseClientCertificateDto;
46-
4746
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ClientMetadata } from 'src/modules/redis/models/client-metadata';
99
import { DatabaseService } from 'src/modules/database/database.service';
1010
import { DatabaseInfoProvider } from 'src/modules/database/providers/database-info.provider';
1111
import { Database } from 'src/modules/database/models/database';
12+
import { ConnectionType } from 'src/modules/database/entities/database.entity';
1213

1314
@Injectable()
1415
export class DatabaseConnectionService {
@@ -111,11 +112,29 @@ export class DatabaseConnectionService {
111112
const connectionName = generateRedisConnectionName(clientMetadata.namespace, clientMetadata.databaseId);
112113

113114
try {
114-
return await this.redisService.connectToDatabaseInstance(
115+
const client = await this.redisService.connectToDatabaseInstance(
115116
database,
116117
clientMetadata.namespace,
117118
connectionName,
118119
);
120+
121+
if (database.connectionType === ConnectionType.NOT_CONNECTED) {
122+
let connectionType = ConnectionType.STANDALONE;
123+
124+
// cluster check
125+
if (client.isCluster) {
126+
connectionType = ConnectionType.CLUSTER;
127+
}
128+
129+
// sentinel check
130+
if (client?.options?.['sentinels']?.length) {
131+
connectionType = ConnectionType.SENTINEL;
132+
}
133+
134+
await this.repository.update(database.id, { connectionType });
135+
}
136+
137+
return client;
119138
} catch (error) {
120139
this.logger.error('Failed to create database client', error);
121140
const exception = getRedisConnectionException(

redisinsight/api/src/modules/database/entities/database.entity.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export enum ConnectionType {
2121
STANDALONE = 'STANDALONE',
2222
CLUSTER = 'CLUSTER',
2323
SENTINEL = 'SENTINEL',
24+
NOT_CONNECTED = 'NOT CONNECTED',
2425
}
2526

2627
@Entity('database_instance')

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ export class Database {
142142
type: Endpoint,
143143
isArray: true,
144144
})
145+
@IsOptional()
146+
@Type(() => Endpoint)
145147
@Expose()
146148
nodes?: Endpoint[];
147149

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,34 @@ export class RedisService {
195195
client = await this.createSentinelClient(database, nodes, tool, true, connectionName);
196196
break;
197197
default:
198-
client = await this.createStandaloneClient(database, tool, true, connectionName);
198+
// AUTO
199+
client = await this.createClientAutomatically(database, tool, connectionName);
199200
}
200201

201202
return client;
202203
}
203204

205+
public async createClientAutomatically(database: Database, tool: AppTool, connectionName) {
206+
// try sentinel connection
207+
if (database?.sentinelMaster) {
208+
try {
209+
return await this.createSentinelClient(database, database.nodes, tool, true, connectionName);
210+
} catch (e) {
211+
// ignore error
212+
}
213+
}
214+
215+
// try cluster connection
216+
try {
217+
return await this.createClusterClient(database, database.nodes, true, connectionName);
218+
} catch (e) {
219+
// ignore error
220+
}
221+
222+
// Standalone in any other case
223+
return this.createStandaloneClient(database, tool, true, connectionName);
224+
}
225+
204226
public isClientConnected(client: Redis | Cluster): boolean {
205227
try {
206228
return client.status === 'ready';

0 commit comments

Comments
 (0)