Skip to content

Commit 7bf0f87

Browse files
Merge pull request #3340 from RedisInsight/be/feature/CR-27-enhance-redis-module
initial implementation of session metadata checks
2 parents 05ecd6b + 1e9a8f6 commit 7bf0f87

15 files changed

+600
-28
lines changed

redisinsight/api/src/__mocks__/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ export const mockSessionMetadata: SessionMetadata = {
5858
sessionId: uuidv4(),
5959
};
6060

61+
export const mockClientMetadata: ClientMetadata = {
62+
sessionMetadata: mockSessionMetadata,
63+
databaseId: mockDatabase.id,
64+
context: ClientContext.Common,
65+
};
66+
6167
export const mockCliClientMetadata: ClientMetadata = {
6268
sessionMetadata: mockSessionMetadata,
6369
databaseId: mockDatabase.id,

redisinsight/api/src/__mocks__/databases.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { mockSentinelMasterDto } from 'src/__mocks__/redis-sentinel';
88
import { pick } from 'lodash';
99
import { RedisDatabaseInfoResponse } from 'src/modules/database/dto/redis-info.dto';
1010
import { DatabaseOverview } from 'src/modules/database/models/database-overview';
11-
import { ClientContext, ClientMetadata } from 'src/common/models';
1211
import {
1312
mockSshOptionsBasic,
1413
mockSshOptionsBasicEntity,
@@ -205,12 +204,6 @@ export const mockNewDatabase = Object.assign(new Database(), {
205204
new: true,
206205
});
207206

208-
export const mockClientMetadata: ClientMetadata = {
209-
sessionMetadata: undefined,
210-
databaseId: mockDatabase.id,
211-
context: ClientContext.Common,
212-
};
213-
214207
export const mockDatabaseOverview: DatabaseOverview = {
215208
version: '6.2.4',
216209
usedMemory: 1,

redisinsight/api/src/__mocks__/redis-client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { ClientMetadata } from 'src/common/models';
22
import { RedisClient, RedisClientConnectionType } from 'src/modules/redis/client';
33
import { RedisClientLib } from 'src/modules/redis/redis.client.factory';
44
import { mockCommonClientMetadata } from 'src/__mocks__/common';
5+
import { BadRequestException } from '@nestjs/common';
6+
import ERROR_MESSAGES from 'src/constants/error-messages';
57

68
export interface IRedisClientInstance {
79
id: string,
@@ -10,6 +12,9 @@ export interface IRedisClientInstance {
1012
lastTimeUsed: number;
1113
}
1214

15+
export const mockInvalidClientMetadataError = new BadRequestException(ERROR_MESSAGES.INVALID_CLIENT_METADATA);
16+
export const mockInvalidSessionMetadataError = new BadRequestException(ERROR_MESSAGES.INVALID_SESSION_METADATA);
17+
1318
// todo: NEW. remove everything above
1419
export class MockRedisClient extends RedisClient {
1520
constructor(clientMetadata: ClientMetadata, client: any = jest.fn(), options = {}) {

redisinsight/api/src/common/models/client-metadata.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Type } from 'class-transformer';
33
import {
44
IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min,
55
} from 'class-validator';
6+
import { BadRequestException } from '@nestjs/common';
7+
import ERROR_MESSAGES from 'src/constants/error-messages';
68

79
export enum ClientContext {
810
Common = 'Common',
@@ -40,4 +42,21 @@ export class ClientMetadata {
4042
@Min(0)
4143
@Max(2147483647)
4244
db?: number;
45+
46+
/**
47+
* Validates client metadata required properties to be defined
48+
* Must be used in all the places that works with clients
49+
* @param clientMetadata
50+
*/
51+
static validate(clientMetadata: ClientMetadata) {
52+
// validate session metadata
53+
SessionMetadata.validate(clientMetadata?.sessionMetadata);
54+
55+
if (
56+
!clientMetadata?.databaseId
57+
|| !clientMetadata?.context
58+
) {
59+
throw new BadRequestException(ERROR_MESSAGES.INVALID_CLIENT_METADATA);
60+
}
61+
}
4362
}

redisinsight/api/src/common/models/session.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
IsNotEmpty, IsObject, IsOptional, IsString,
33
} from 'class-validator';
4+
import { BadRequestException } from '@nestjs/common';
5+
import ERROR_MESSAGES from 'src/constants/error-messages';
46

57
export interface ISessionMetadata {
68
userId: string;
@@ -20,6 +22,20 @@ export class SessionMetadata implements ISessionMetadata {
2022
@IsOptional()
2123
@IsString()
2224
uniqueId?: string;
25+
26+
/**
27+
* Validates session metadata required properties to be defined
28+
* Must be used in all the places that works with clients
29+
* @param sessionMetadata
30+
*/
31+
static validate(sessionMetadata: SessionMetadata) {
32+
if (
33+
!sessionMetadata?.sessionId
34+
|| !sessionMetadata?.userId
35+
) {
36+
throw new BadRequestException(ERROR_MESSAGES.INVALID_SESSION_METADATA);
37+
}
38+
}
2339
}
2440

2541
export class Session {

redisinsight/api/src/constants/error-messages.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export default {
66
NOT_FOUND: 'Resource was not found',
77
INTERNAL_SERVER_ERROR: 'Server error',
88

9+
INVALID_CLIENT_METADATA: 'Client metadata missed required properties',
10+
INVALID_SESSION_METADATA: 'Session metadata missed required properties',
11+
912
INVALID_DATABASE_INSTANCE_ID: 'Invalid database instance id.',
1013
COMMAND_EXECUTION_NOT_FOUND: 'Command execution was not found.',
1114
DATABASE_ANALYSIS_NOT_FOUND: 'Database analysis was not found.',

redisinsight/api/src/modules/database/providers/database.factory.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
2+
import { v4 as uuidv4 } from 'uuid';
23
import { ConnectionType, HostingProvider } from 'src/modules/database/entities/database.entity';
34
import { catchRedisConnectionError, getHostingProvider } from 'src/utils';
45
import { Database } from 'src/modules/database/models/database';
@@ -35,7 +36,7 @@ export class DatabaseFactory {
3536
const client = await this.redisClientFactory.getConnectionStrategy().createStandaloneClient(
3637
{
3738
sessionMetadata,
38-
databaseId: database.id,
39+
databaseId: database.id || uuidv4(), // we assume that if no database id defined we are in creation process
3940
context: ClientContext.Common,
4041
},
4142
database,
@@ -110,7 +111,7 @@ export class DatabaseFactory {
110111
const clusterClient = await this.redisClientFactory.getConnectionStrategy().createClusterClient(
111112
{
112113
sessionMetadata,
113-
databaseId: model.id,
114+
databaseId: model.id || uuidv4(), // we assume that if no database id defined we are in creation process
114115
context: ClientContext.Common,
115116
},
116117
model,
@@ -157,7 +158,7 @@ export class DatabaseFactory {
157158
const sentinelClient = await this.redisClientFactory.getConnectionStrategy().createSentinelClient(
158159
{
159160
sessionMetadata,
160-
databaseId: model.id,
161+
databaseId: model.id || uuidv4(), // we assume that if no database id defined we are in creation process
161162
context: ClientContext.Common,
162163
},
163164
model,

redisinsight/api/src/modules/redis-sentinel/redis-sentinel.service.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Test, TestingModule } from '@nestjs/testing';
99
import { BadRequestException } from '@nestjs/common';
1010
import ERROR_MESSAGES from 'src/constants/error-messages';
1111
import {
12+
mockConstantsProvider,
1213
mockDatabaseFactory,
1314
mockDatabaseService,
1415
mockIORedisClient,
@@ -24,6 +25,7 @@ import { RedisSentinelAnalytics } from 'src/modules/redis-sentinel/redis-sentine
2425
import { DatabaseService } from 'src/modules/database/database.service';
2526
import { DatabaseFactory } from 'src/modules/database/providers/database.factory';
2627
import { RedisClientFactory } from 'src/modules/redis/redis.client.factory';
28+
import { ConstantsProvider } from 'src/modules/constants/providers/constants.provider';
2729

2830
describe('RedisSentinelService', () => {
2931
let service: RedisSentinelService;
@@ -49,6 +51,10 @@ describe('RedisSentinelService', () => {
4951
provide: DatabaseFactory,
5052
useFactory: mockDatabaseFactory,
5153
},
54+
{
55+
provide: ConstantsProvider,
56+
useFactory: mockConstantsProvider,
57+
},
5258
],
5359
}).compile();
5460

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
HttpException, Injectable, Logger,
33
} from '@nestjs/common';
4+
import { v4 as uuidv4 } from 'uuid';
45
import { CreateSentinelDatabaseResponse } from 'src/modules/redis-sentinel/dto/create.sentinel.database.response';
56
import { CreateSentinelDatabasesDto } from 'src/modules/redis-sentinel/dto/create.sentinel.databases.dto';
67
import { Database } from 'src/modules/database/models/database';
@@ -12,6 +13,7 @@ import { RedisSentinelAnalytics } from 'src/modules/redis-sentinel/redis-sentine
1213
import { DatabaseFactory } from 'src/modules/database/providers/database.factory';
1314
import { discoverSentinelMasterGroups } from 'src/modules/redis/utils';
1415
import { RedisClientFactory } from 'src/modules/redis/redis.client.factory';
16+
import { ConstantsProvider } from 'src/modules/constants/providers/constants.provider';
1517

1618
@Injectable()
1719
export class RedisSentinelService {
@@ -22,6 +24,7 @@ export class RedisSentinelService {
2224
private readonly databaseService: DatabaseService,
2325
private readonly databaseFactory: DatabaseFactory,
2426
private readonly redisSentinelAnalytics: RedisSentinelAnalytics,
27+
private readonly constantsProvider: ConstantsProvider,
2528
) {}
2629

2730
/**
@@ -118,8 +121,8 @@ export class RedisSentinelService {
118121
try {
119122
const database = await this.databaseFactory.createStandaloneDatabaseModel(dto);
120123
const client = await this.redisClientFactory.getConnectionStrategy().createStandaloneClient({
121-
sessionMetadata: {} as SessionMetadata,
122-
databaseId: database.id,
124+
sessionMetadata: this.constantsProvider.getSystemSessionMetadata(),
125+
databaseId: database.id || uuidv4(),
123126
context: ClientContext.Common,
124127
}, database, { useRetry: false });
125128
result = await discoverSentinelMasterGroups(client);

redisinsight/api/src/modules/redis/connection/ioredis.redis.connection.strategy.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ export class IoredisRedisConnectionStrategy extends RedisConnectionStrategy {
156156
): Promise<RedisClient> {
157157
this.logger.debug('Creating ioredis standalone client');
158158

159+
// Additional validation
160+
ClientMetadata.validate(clientMetadata);
161+
159162
let tnl: SshTunnel;
160163

161164
try {
@@ -215,6 +218,9 @@ export class IoredisRedisConnectionStrategy extends RedisConnectionStrategy {
215218
database: Database,
216219
options: IRedisConnectionOptions,
217220
): Promise<RedisClient> {
221+
// Additional validation
222+
ClientMetadata.validate(clientMetadata);
223+
218224
let tnls: SshTunnel[] = [];
219225
let standaloneClient: RedisClient;
220226
let rootNodes = [{
@@ -291,6 +297,9 @@ export class IoredisRedisConnectionStrategy extends RedisConnectionStrategy {
291297
database: Database,
292298
options: IRedisConnectionOptions,
293299
): Promise<RedisClient> {
300+
// Additional validation
301+
ClientMetadata.validate(clientMetadata);
302+
294303
const config = await this.getRedisSentinelOptions(clientMetadata, database, options);
295304

296305
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)