Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default {
},
],
redisStack: {
id: process.env.REDIS_STACK_DATABASE_ID,
id: process.env.BUILD_TYPE === 'REDIS_STACK' ? process.env.REDIS_STACK_DATABASE_ID || 'redis-stack' : undefined,
name: process.env.REDIS_STACK_DATABASE_NAME,
host: process.env.REDIS_STACK_DATABASE_HOST,
port: process.env.REDIS_STACK_DATABASE_PORT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,24 @@ import {
Injectable,
Logger,
NotFoundException,
OnApplicationBootstrap,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { merge } from 'lodash';
import { Repository } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import ERROR_MESSAGES from 'src/constants/error-messages';
import config from 'src/utils/config';
import { EncryptionService } from 'src/modules/core/encryption/encryption.service';
import { DatabaseInstanceEntity } from 'src/modules/core/models/database-instance.entity';

@Injectable()
export class DatabasesProvider implements OnApplicationBootstrap {
private logger = new Logger('DatabaseProvider');
export class DatabasesProvider {
protected logger = new Logger('DatabaseProvider');

constructor(
@InjectRepository(DatabaseInstanceEntity)
private readonly databasesRepository: Repository<DatabaseInstanceEntity>,
private readonly encryptionService: EncryptionService,
protected readonly databasesRepository: Repository<DatabaseInstanceEntity>,
protected readonly encryptionService: EncryptionService,
) {}

async onApplicationBootstrap() {
const REDIS_STACK_CONFIG = config.get('redisStack');
if (REDIS_STACK_CONFIG?.id) {
await this.setPredefinedDatabase(merge({
name: 'Redis Stack',
host: 'localhost',
port: '6379',
}, REDIS_STACK_CONFIG));
}
}

/**
* Fast check if database exists.
* No need to retrieve any fields.
Expand Down Expand Up @@ -207,32 +193,4 @@ export class DatabasesProvider implements OnApplicationBootstrap {
sentinelMasterPassword,
};
}

private async setPredefinedDatabase(
options: { id: string; name: string; host: string; port: string; },
): Promise<void> {
try {
const {
id, name, host, port,
} = options;
const isExist = await this.exists(id);
if (!isExist) {
const database: any = this.databasesRepository.create({
id,
host,
port: parseInt(port, 10),
name,
username: null,
password: null,
tls: false,
verifyServerCert: false,
db: 0,
});
await this.save(database);
}
this.logger.log(`Succeed to set predefined database ${id}`);
} catch (error) {
this.logger.error('Failed to set predefined database', error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import {
mockDataToEncrypt,
mockEncryptionService,
mockEncryptResult,
mockRepository,
MockType,
} from 'src/__mocks__';
import { EncryptionService } from 'src/modules/core/encryption/encryption.service';
import { DatabaseInstanceEntity } from 'src/modules/core/models/database-instance.entity';
import { StackDatabasesProvider } from './stack.databases.provider';

describe('StackDatabasesProvider', () => {
let service: StackDatabasesProvider;
let encryptionService: MockType<EncryptionService>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
StackDatabasesProvider,
{
provide: EncryptionService,
useFactory: mockEncryptionService,
},
{
provide: getRepositoryToken(DatabaseInstanceEntity),
useFactory: mockRepository,
},
],
}).compile();

service = module.get(StackDatabasesProvider);
encryptionService = module.get(EncryptionService);

encryptionService.decrypt.mockReturnValue(mockDataToEncrypt);
encryptionService.encrypt.mockReturnValue(mockEncryptResult);
});

describe('onApplicationBootstrap', () => {
beforeEach(() => {
service.save = jest.fn();
});
it('should save database if it is not exist', async () => {
service.exists = jest.fn().mockResolvedValue(false);

await service.onApplicationBootstrap();

expect(service.save).toHaveBeenCalled();
});
it('should not save database if it is exist', async () => {
service.exists = jest.fn().mockResolvedValue(true);

await service.onApplicationBootstrap();

expect(service.save).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
Injectable,
Logger,
OnApplicationBootstrap,
} from '@nestjs/common';
import { merge } from 'lodash';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import config from 'src/utils/config';
import { DatabaseInstanceEntity } from 'src/modules/core/models/database-instance.entity';
import { DatabasesProvider } from 'src/modules/shared/services/instances-business/databases.provider';

const REDIS_STACK_CONFIG = config.get('redisStack');

@Injectable()
export class StackDatabasesProvider extends DatabasesProvider implements OnApplicationBootstrap {
protected logger = new Logger('StackDatabasesProvider');

async onApplicationBootstrap() {
await this.setPredefinedDatabase(merge({
name: 'Redis Stack',
host: 'localhost',
port: '6379',
}, REDIS_STACK_CONFIG));
}

/**
* Check if database for Stack exists.
*/
async exists(): Promise<boolean> {
return super.exists(REDIS_STACK_CONFIG.id);
}

/**
* Get list of databases from the local db
*/
async getAll(): Promise<DatabaseInstanceEntity[]> {
this.logger.log('Getting databases list');
return [await this.getOneById(REDIS_STACK_CONFIG.id)];
}

/**
* Get single database by id from the local db
* @throws NotFoundException in case when no database found
*/
async getOneById(
id: string,
ignoreEncryptionErrors: boolean = false,
): Promise<DatabaseInstanceEntity> {
return super.getOneById(REDIS_STACK_CONFIG.id, ignoreEncryptionErrors);
}

/**
* Save entire entity
* @param database
*/
async save(database: DatabaseInstanceEntity): Promise<DatabaseInstanceEntity> {
return super.save(new DatabaseInstanceEntity({
...database,
id: REDIS_STACK_CONFIG.id,
}));
}

/**
* Update database field(s) without encryption logic
* @param id
* @param data
* @throws BadRequestException error when try to update password or sentinelMasterPassword fields
*/
async patch(id: string, data: QueryDeepPartialEntity<DatabaseInstanceEntity>) {
return super.patch(REDIS_STACK_CONFIG.id, {
...data,
id: REDIS_STACK_CONFIG.id,
});
}

/**
* Update entire database entity with fields encryption logic
*
* @param id
* @param data
*/
async update(id: string, data: DatabaseInstanceEntity) {
return super.update(REDIS_STACK_CONFIG.id, new DatabaseInstanceEntity({
...data,
id: REDIS_STACK_CONFIG.id,
}));
}

/**
* Save database entity for Stack
*
* @param options
*/
private async setPredefinedDatabase(
options: { id: string; name: string; host: string; port: string; },
): Promise<void> {
try {
const {
id, name, host, port,
} = options;
const isExist = await this.exists();
if (!isExist) {
const database: any = this.databasesRepository.create({
id,
host,
port: parseInt(port, 10),
name,
tls: false,
verifyServerCert: false,
});
await this.save(database);
}
this.logger.log(`Succeed to set predefined database ${id}`);
} catch (error) {
this.logger.error('Failed to set predefined database', error);
}
}
}
6 changes: 5 additions & 1 deletion redisinsight/api/src/modules/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { DatabasesProvider } from 'src/modules/shared/services/instances-business/databases.provider';
import { OverviewService } from 'src/modules/shared/services/instances-business/overview.service';
import { RedisToolFactory } from 'src/modules/shared/services/base/redis-tool.factory';
import { StackDatabasesProvider } from 'src/modules/shared/services/instances-business/stack.databases.provider';
import { InstancesBusinessService } from './services/instances-business/instances-business.service';
import { RedisEnterpriseBusinessService } from './services/redis-enterprise-business/redis-enterprise-business.service';
import { RedisCloudBusinessService } from './services/redis-cloud-business/redis-cloud-business.service';
Expand All @@ -28,7 +29,10 @@ const SERVER_CONFIG = config.get('server');
TypeOrmModule.forFeature([DatabaseInstanceEntity]),
],
providers: [
DatabasesProvider,
{
provide: DatabasesProvider,
useClass: SERVER_CONFIG.buildType === 'REDIS_STACK' ? StackDatabasesProvider : DatabasesProvider,
},
InstancesBusinessService,
InstancesAnalyticsService,
RedisEnterpriseBusinessService,
Expand Down