Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
467d1eb
#RI-2409 switch db index. base implementation
Dec 28, 2022
646e087
fix db > 0 logic
Dec 28, 2022
cbf9412
#RI-3949 - initial fe implementation for changing db index
rsergeenko Dec 28, 2022
b58588a
fix switch
Dec 28, 2022
09eab73
Merge branch 'be/feature/RI-2409-change_database_index' into fe/featu…
rsergeenko Dec 28, 2022
36458f4
#RI-3949 - fix db index from session storage
rsergeenko Dec 28, 2022
2132c24
#RI-2409 workaround for cli clients
Dec 28, 2022
e79d6cb
Merge branch 'be/feature/RI-2409-change_database_index' into fe/featu…
rsergeenko Dec 28, 2022
49913f1
#RI-2409 tests
Dec 29, 2022
8aab9bd
ix tests
Dec 29, 2022
ecdb85d
remove ioredis-mock usage + forceExit at the end
Dec 29, 2022
9dd5767
Merge pull request #1535 from RedisInsight/be/feature/RI-2409-change_…
Dec 29, 2022
db050e2
#RI-3949 - add tests, info request
rsergeenko Dec 29, 2022
d48aaaa
#RI-3949 - remove unused import
rsergeenko Dec 29, 2022
477ed3f
Merge pull request #1536 from RedisInsight/fe/feature/RI-3949_change-…
rsergeenko Dec 30, 2022
8e58457
#RI-3983 - fix loading message
rsergeenko Jan 3, 2023
6aeb632
#RI-3980, #RI-3981, #RI-3982 bugfixes
Jan 3, 2023
e05284c
#RI-3980, #RI-3981, #RI-3982 pass db to client creation
Jan 3, 2023
2e294bf
#RI-3982 - fix bulk delete index
rsergeenko Jan 3, 2023
a839ca4
add tests for change database index
vlad-dargel Jan 3, 2023
1e0c8ea
Merge pull request #1552 from RedisInsight/fe/bugfix/RI-3983
vlad-dargel Jan 3, 2023
d77ae12
Merge pull request #1555 from RedisInsight/fe/bugfix/RI-3982_index-fo…
vlad-dargel Jan 3, 2023
0a48c52
#RI-3986 - fix loading data for redisearch
rsergeenko Jan 4, 2023
3e40c2f
#RI-3986 - change condition
rsergeenko Jan 4, 2023
3f1f786
Merge pull request #1557 from RedisInsight/fe/bugfix/RI-3986
vlad-dargel Jan 4, 2023
71728a2
#RI-3986 - fix refresh keys after change index
rsergeenko Jan 4, 2023
ab18e33
Merge pull request #1559 from RedisInsight/fe/bugfix/RI-3986
rsergeenko Jan 4, 2023
f25a3ec
updates for failed tests
vlad-dargel Jan 4, 2023
066a59c
Merge branch 'feature/RI-2409-change_database_index' into e2e/feature…
vlad-dargel Jan 4, 2023
d0f2f05
fix
vlad-dargel Jan 4, 2023
cebc037
#RI-3986 possible fix to deal with concurrent requests (graceful shut…
Jan 4, 2023
5c8a099
#RI-3986 possible fix to deal with concurrent requests (reuse existing)
Jan 4, 2023
1a9d9e2
#RI-3988 fix sentinel connection issue
Jan 4, 2023
fded78d
#RI-2409 - cover disabled state for button by tests
rsergeenko Jan 5, 2023
44276a3
updates for tests
vlad-dargel Jan 5, 2023
78fad15
Merge branch 'feature/RI-2409-change_database_index' into e2e/feature…
vlad-dargel Jan 5, 2023
c699491
fix
vlad-dargel Jan 9, 2023
d850620
Merge pull request #1564 from RedisInsight/e2e/feature/RI-2409-change…
vlad-dargel Jan 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion redisinsight/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"start:prod": "cross-env NODE_ENV=production node dist/src/main",
"test": "cross-env NODE_ENV=test ./node_modules/.bin/jest -w 1",
"test:watch": "cross-env NODE_ENV=test jest --watch -w 1",
"test:cov": "cross-env NODE_ENV=test ./node_modules/.bin/jest --coverage -w 1",
"test:cov": "cross-env NODE_ENV=test ./node_modules/.bin/jest --forceExit --coverage -w 1",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand -w 1",
"test:e2e": "jest --config ./test/jest-e2e.json -w 1",
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -d ./config/ormconfig.ts",
Expand Down
1 change: 1 addition & 0 deletions redisinsight/api/src/__mocks__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from './server';
export * from './redis-enterprise';
export * from './redis-sentinel';
export * from './database-import';
export * from './redis-client';
18 changes: 18 additions & 0 deletions redisinsight/api/src/__mocks__/redis-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { IRedisClientInstance, RedisService } from 'src/modules/redis/redis.service';
import { mockCommonClientMetadata } from 'src/__mocks__/common';
import { mockIORedisClient } from 'src/__mocks__/redis';
import { ClientMetadata } from 'src/common/models';

export const mockRedisClientInstance: IRedisClientInstance = {
id: RedisService.generateId(mockCommonClientMetadata),
clientMetadata: mockCommonClientMetadata,
client: mockIORedisClient,
lastTimeUsed: 1619791508019,
};

export const generateMockRedisClientInstance = (clientMetadata: Partial<ClientMetadata>): IRedisClientInstance => ({
id: RedisService.generateId(clientMetadata as ClientMetadata),
clientMetadata: clientMetadata as ClientMetadata,
client: mockIORedisClient,
lastTimeUsed: Date.now(),
});
17 changes: 12 additions & 5 deletions redisinsight/api/src/__mocks__/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,18 @@ export const mockRedisService = jest.fn(() => ({
getClientInstance: jest.fn().mockResolvedValue({
client: mockIORedisClient,
}),
setClientInstance: jest.fn(),
setClientInstance: jest.fn().mockReturnValue({
client: mockIORedisClient,
}),
isClientConnected: jest.fn().mockReturnValue(true),
connectToDatabaseInstance: jest.fn().mockResolvedValue(mockIORedisClient),
createStandaloneClient: jest.fn().mockResolvedValue(mockIORedisClient),
createSentinelClient: jest.fn().mockResolvedValue(mockIORedisSentinel),
createClusterClient: jest.fn().mockResolvedValue(mockIORedisCluster),
removeClientInstance: jest.fn(),
removeClientInstances: jest.fn(),
findClientInstances: jest.fn(),
}));

export const mockRedisConnectionFactory = jest.fn(() => ({
createRedisConnection: jest.fn().mockResolvedValue(mockIORedisClient),
createStandaloneConnection: jest.fn().mockResolvedValue(mockIORedisClient),
createSentinelConnection: jest.fn().mockResolvedValue(mockIORedisSentinel),
createClusterConnection: jest.fn().mockResolvedValue(mockIORedisCluster),
}));
1 change: 1 addition & 0 deletions redisinsight/api/src/common/constants/api.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const API_PARAM_DATABASE_ID = 'dbInstance';
export const API_HEADER_DATABASE_INDEX = 'ri-db-index';
export const API_PARAM_CLI_CLIENT_ID = 'uuid';
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,40 @@ import { plainToClass } from 'class-transformer';
import { ClientContext, ClientMetadata } from 'src/common/models';
import { sessionFromRequestFactory } from 'src/common/decorators';
import { Validator } from 'class-validator';
import { API_PARAM_DATABASE_ID } from 'src/common/constants';
import { API_HEADER_DATABASE_INDEX, API_PARAM_DATABASE_ID } from 'src/common/constants';
import { ApiHeader, ApiParam } from '@nestjs/swagger';

const validator = new Validator();

export interface IClientMetadataParamOptions {
databaseIdParam?: string,
uniqueIdParam?: string,
context?: ClientContext,
ignoreDbIndex?: boolean,
}

export const clientMetadataParamFactory = (
options: IClientMetadataParamOptions,
ctx: ExecutionContext,
): ClientMetadata => {
const opts: IClientMetadataParamOptions = {
context: ClientContext.Common,
databaseIdParam: API_PARAM_DATABASE_ID,
...options,
};

const req = ctx.switchToHttp().getRequest();

let databaseId;
if (opts?.databaseIdParam) {
databaseId = req.params?.[opts.databaseIdParam];
if (options?.databaseIdParam) {
databaseId = req.params?.[options.databaseIdParam];
}

let uniqueId;
if (opts?.uniqueIdParam) {
uniqueId = req.params?.[opts.uniqueIdParam];
if (options?.uniqueIdParam) {
uniqueId = req.params?.[options.uniqueIdParam];
}

const clientMetadata = plainToClass(ClientMetadata, {
session: sessionFromRequestFactory(undefined, ctx),
databaseId,
uniqueId,
context: opts?.context || ClientContext.Common,
context: options?.context || ClientContext.Common,
db: options?.ignoreDbIndex ? undefined : req?.headers?.[API_HEADER_DATABASE_INDEX],
});

const errors = validator.validateSync(clientMetadata, {
Expand All @@ -53,4 +50,35 @@ export const clientMetadataParamFactory = (
return clientMetadata;
};

export const ClientMetadataParam = createParamDecorator(clientMetadataParamFactory);
export const ClientMetadataParam = (
options?: IClientMetadataParamOptions,
) => {
const opts: IClientMetadataParamOptions = {
context: ClientContext.Common,
databaseIdParam: API_PARAM_DATABASE_ID,
ignoreDbIndex: false,
...options,
};

return createParamDecorator(clientMetadataParamFactory, [
(target: any, key: string) => {
// Here it is. Use the `@ApiQuery` decorator purely as a function to define the meta only once here.
ApiParam({
name: opts.databaseIdParam,
schema: { type: 'string' },
required: true,
})(target, key, Object.getOwnPropertyDescriptor(target, key));
if (!opts.ignoreDbIndex) {
ApiHeader({
name: API_HEADER_DATABASE_INDEX,
schema: {
default: undefined,
type: 'number',
minimum: 0,
},
required: false,
})(target, key, Object.getOwnPropertyDescriptor(target, key));
}
},
])(opts);
};
9 changes: 8 additions & 1 deletion redisinsight/api/src/common/models/client-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Session } from 'src/common/models/session';
import { Type } from 'class-transformer';
import {
IsEnum, IsNotEmpty, IsOptional, IsString,
IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min,
} from 'class-validator';

export enum ClientContext {
Expand All @@ -27,4 +27,11 @@ export class ClientMetadata {
@IsOptional()
@IsString()
uniqueId?: string;

@IsOptional()
@IsNumber()
@Type(() => Number)
@Min(0)
@Max(2147483647)
db?: number;
}
4 changes: 4 additions & 0 deletions redisinsight/api/src/common/models/database-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { PickType } from '@nestjs/swagger';
import { ClientMetadata } from 'src/common/models/client-metadata';

export class DatabaseIndex extends PickType(ClientMetadata, ['db'] as const) {}
1 change: 1 addition & 0 deletions redisinsight/api/src/common/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './common';
export * from './endpoint';
export * from './session';
export * from './client-metadata';
export * from './database-index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ArgumentMetadata, ValidationPipe } from '@nestjs/common';

export class DbIndexValidationPipe extends ValidationPipe {
async transform(db, metadata: ArgumentMetadata) {
return super.transform({ db }, metadata);
}
}
1 change: 1 addition & 0 deletions redisinsight/api/src/common/pipes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './database-index.validation.pipe';
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import { Test, TestingModule } from '@nestjs/testing';
import {
mockAutodiscoveryEndpoint,
mockDatabaseService,
mockIORedisClient,
mockIORedisClient, mockRedisConnectionFactory,
mockRedisServerInfoResponse,
mockRedisService, mockServerConfig,
mockServerConfig,
mockSettingsService,
MockType,
} from 'src/__mocks__';
import { SettingsService } from 'src/modules/settings/settings.service';
import { AutodiscoveryService } from 'src/modules/autodiscovery/autodiscovery.service';
import { DatabaseService } from 'src/modules/database/database.service';
import { RedisService } from 'src/modules/redis/redis.service';
import { mocked } from 'ts-jest/utils';
import { RedisConnectionFactory } from 'src/modules/redis/redis-connection.factory';

jest.mock(
'src/modules/autodiscovery/utils/autodiscovery.util',
Expand All @@ -38,7 +38,7 @@ describe('AutodiscoveryService', () => {
let service: AutodiscoveryService;
let settingsService: MockType<SettingsService>;
let databaseService: MockType<DatabaseService>;
let redisService: MockType<RedisService>;
let redisConnectionFactory: MockType<RedisConnectionFactory>;

beforeEach(async () => {
jest.clearAllMocks();
Expand All @@ -54,18 +54,17 @@ describe('AutodiscoveryService', () => {
useFactory: mockDatabaseService,
},
{
provide: RedisService,
useFactory: mockRedisService,
provide: RedisConnectionFactory,
useFactory: mockRedisConnectionFactory,
},
],
}).compile();

service = module.get(AutodiscoveryService);
settingsService = module.get(SettingsService);
databaseService = module.get(DatabaseService);
redisService = await module.get(RedisService);
redisConnectionFactory = await module.get(RedisConnectionFactory);

redisService.createStandaloneClient.mockResolvedValue(mockIORedisClient);
mockIORedisClient.info.mockResolvedValue(mockRedisServerInfoResponse);

mocked(utils.convertRedisInfoReplyToObject).mockReturnValue({
Expand Down Expand Up @@ -187,7 +186,7 @@ describe('AutodiscoveryService', () => {
});

it('should not fail in case of an error', async () => {
redisService.createStandaloneClient.mockRejectedValue(new Error());
redisConnectionFactory.createStandaloneConnection.mockRejectedValue(new Error());

await service['addRedisDatabase'](mockAutodiscoveryEndpoint);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import {
Injectable, Logger, OnModuleInit,
} from '@nestjs/common';
import { RedisService } from 'src/modules/redis/redis.service';
import { AppTool } from 'src/models';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { getAvailableEndpoints } from 'src/modules/autodiscovery/utils/autodiscovery.util';
import { convertRedisInfoReplyToObject } from 'src/utils';
import config from 'src/utils/config';
import { SettingsService } from 'src/modules/settings/settings.service';
import { Database } from 'src/modules/database/models/database';
import { DatabaseService } from 'src/modules/database/database.service';
import { ClientContext } from 'src/common/models';
import { ClientContext, ClientMetadata } from 'src/common/models';
import { RedisConnectionFactory } from 'src/modules/redis/redis-connection.factory';

const SERVER_CONFIG = config.get('server');

Expand All @@ -19,7 +16,7 @@ export class AutodiscoveryService implements OnModuleInit {

constructor(
private settingsService: SettingsService,
private redisService: RedisService,
private redisConnectionFactory: RedisConnectionFactory,
private databaseService: DatabaseService,
) {}

Expand Down Expand Up @@ -73,11 +70,12 @@ export class AutodiscoveryService implements OnModuleInit {
*/
private async addRedisDatabase(endpoint: { host: string, port: number }) {
try {
const client = await this.redisService.createStandaloneClient(
const client = await this.redisConnectionFactory.createStandaloneConnection(
{
context: ClientContext.Common,
} as ClientMetadata,
endpoint as Database,
ClientContext.Common,
false,
'redisinsight-auto-discovery',
{ useRetry: false, connectionName: 'redisinsight-auto-discovery' },
);

const info = convertRedisInfoReplyToObject(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { API_PARAM_DATABASE_ID } from 'src/common/constants';
import { createParamDecorator } from '@nestjs/common';
import { ClientContext } from 'src/common/models';
import { clientMetadataParamFactory } from 'src/common/decorators';
import { ClientMetadataParam } from 'src/common/decorators';

export const BrowserClientMetadata = (
databaseIdParam = API_PARAM_DATABASE_ID,
) => createParamDecorator(clientMetadataParamFactory)({
) => ClientMetadataParam({
context: ClientContext.Browser,
databaseIdParam,
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
import { ClusterNodeNotFoundError } from 'src/modules/cli/constants/errors';
import ERROR_MESSAGES from 'src/constants/error-messages';
import { DatabaseService } from 'src/modules/database/database.service';
import { mockBrowserClientMetadata } from 'src/__mocks__';
import { mockBrowserClientMetadata, mockRedisConnectionFactory } from 'src/__mocks__';
import { RedisConnectionFactory } from 'src/modules/redis/redis-connection.factory';

const mockClient = new Redis();
const mockCluster = new Redis.Cluster([]);
Expand All @@ -42,6 +43,10 @@ describe('BrowserToolClusterService', () => {
provide: RedisService,
useFactory: () => ({}),
},
{
provide: RedisConnectionFactory,
useFactory: mockRedisConnectionFactory,
},
{
provide: DatabaseService,
useFactory: () => ({}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ERROR_MESSAGES from 'src/constants/error-messages';
import { getRedisPipelineSummary } from 'src/utils/cli-helper';
import { getConnectionName } from 'src/utils/redis-connection-helper';
import { DatabaseService } from 'src/modules/database/database.service';
import { RedisConnectionFactory } from 'src/modules/redis/redis-connection.factory';

export interface IExecCommandFromClusterNode {
host: string;
Expand All @@ -24,9 +25,10 @@ export class BrowserToolClusterService extends RedisConsumerAbstractService {

constructor(
protected redisService: RedisService,
protected redisConnectionFactory: RedisConnectionFactory,
protected databaseService: DatabaseService,
) {
super(ClientContext.Browser, redisService, databaseService);
super(ClientContext.Browser, redisService, redisConnectionFactory, databaseService);
}

async execCommand(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import * as IORedis from 'ioredis';
import * as Redis from 'ioredis-mock';
import { mockBrowserClientMetadata } from 'src/__mocks__';
import { mockBrowserClientMetadata, mockRedisConnectionFactory } from 'src/__mocks__';
import {
RedisService,
} from 'src/modules/redis/redis.service';
Expand All @@ -15,6 +15,7 @@ import { InternalServerErrorException } from '@nestjs/common';
import { mockKeyDto } from 'src/modules/browser/__mocks__';
import { RedisString } from 'src/common/constants';
import { DatabaseService } from 'src/modules/database/database.service';
import { RedisConnectionFactory } from 'src/modules/redis/redis-connection.factory';

const mockClient = new Redis();
const mockConnectionErrorMessage = 'Could not connect to localhost, please check the connection details.';
Expand All @@ -34,6 +35,10 @@ describe('BrowserToolService', () => {
provide: RedisService,
useFactory: () => ({}),
},
{
provide: RedisConnectionFactory,
useFactory: mockRedisConnectionFactory,
},
{
provide: DatabaseService,
useFactory: () => ({}),
Expand Down
Loading