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
3 changes: 3 additions & 0 deletions redisinsight/api/src/__mocks__/databases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export const mockDatabaseSentinelMasterPasswordEncrypted = 'database.sentinelMas

export const mockDatabaseSentinelMasterPasswordPlain = 'some sentinel pass';

export const mockDBSize = 1;

export const mockDatabase = Object.assign(new Database(), {
id: mockDatabaseId,
name: 'database-name',
Expand Down Expand Up @@ -275,6 +277,7 @@ export const mockDatabaseInfoProvider = jest.fn(() => ({
determineSentinelMasterGroups: jest.fn().mockReturnValue([mockSentinelMasterDto]),
determineClusterNodes: jest.fn().mockResolvedValue(mockClusterNodes),
getRedisGeneralInfo: jest.fn().mockResolvedValueOnce(mockRedisGeneralInfo),
getRedisDBSize: jest.fn().mockResolvedValue(mockDBSize),
getClientListInfo: jest.fn().mockReturnValue(mockRedisClientListResult),
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@ import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service'
BulkActionsAnalytics,
BulkImportService,
],
exports: [
BulkImportService,
BulkActionsAnalytics,
],
})
export class BulkActionsModule {}
8 changes: 8 additions & 0 deletions redisinsight/api/src/modules/cloud/job/cloud-job.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { CloudDatabaseAnalytics } from 'src/modules/cloud/database/cloud-databas
import { CloudRequestUtm } from 'src/modules/cloud/common/models';
import { CloudCapiKeyService } from 'src/modules/cloud/capi-key/cloud-capi-key.service';
import { CloudSubscriptionApiService } from 'src/modules/cloud/subscription/cloud-subscription.api.service';
import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service';
import { DatabaseInfoService } from 'src/modules/database/database-info.service';

@Injectable()
export class CloudJobFactory {
Expand All @@ -26,6 +28,8 @@ export class CloudJobFactory {
private readonly cloudTaskCapiService: CloudTaskCapiService,
private readonly cloudDatabaseAnalytics: CloudDatabaseAnalytics,
private readonly databaseService: DatabaseService,
private readonly databaseInfoService: DatabaseInfoService,
private readonly bulkImportService: BulkImportService,
private readonly cloudCapiKeyService: CloudCapiKeyService,
private readonly cloudSubscriptionApiService: CloudSubscriptionApiService,
) {}
Expand Down Expand Up @@ -54,6 +58,8 @@ export class CloudJobFactory {
cloudTaskCapiService: this.cloudTaskCapiService,
cloudDatabaseAnalytics: this.cloudDatabaseAnalytics,
databaseService: this.databaseService,
databaseInfoService: this.databaseInfoService,
bulkImportService: this.bulkImportService,
cloudCapiKeyService: this.cloudCapiKeyService,
cloudSubscriptionApiService: this.cloudSubscriptionApiService,
},
Expand All @@ -71,6 +77,8 @@ export class CloudJobFactory {
cloudTaskCapiService: this.cloudTaskCapiService,
cloudDatabaseAnalytics: this.cloudDatabaseAnalytics,
databaseService: this.databaseService,
databaseInfoService: this.databaseInfoService,
bulkImportService: this.bulkImportService,
cloudCapiKeyService: this.cloudCapiKeyService,
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { CloudJobController } from 'src/modules/cloud/job/cloud-job.controller';
import { CloudJobService } from 'src/modules/cloud/job/cloud-job.service';
import { CloudSubscriptionModule } from 'src/modules/cloud/subscription/cloud-subscription.module';
import { CloudDatabaseModule } from 'src/modules/cloud/database/cloud-database.module';
import { CloudUserModule } from 'src/modules/cloud/user/cloud-user.module';
import { CloudJobFactory } from 'src/modules/cloud/job/cloud-job.factory';
import { CloudJobProvider } from 'src/modules/cloud/job/cloud-job.provider';
import { CloudJobGateway } from 'src/modules/cloud/job/cloud-job.gateway';
import { CloudCapiKeyModule } from 'src/modules/cloud/capi-key/cloud-capi-key.module';
import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service';
import { BulkActionsAnalytics } from 'src/modules/bulk-actions/bulk-actions.analytics';

@Module({
imports: [
Expand All @@ -22,6 +23,8 @@ import { CloudCapiKeyModule } from 'src/modules/cloud/capi-key/cloud-capi-key.mo
CloudJobFactory,
CloudJobProvider,
CloudJobGateway,
BulkImportService,
BulkActionsAnalytics,
],
controllers: [CloudJobController],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import { Database } from 'src/modules/database/models/database';
import config from 'src/utils/config';
import { CloudDatabaseAnalytics } from 'src/modules/cloud/database/cloud-database.analytics';
import { CloudCapiKeyService } from 'src/modules/cloud/capi-key/cloud-capi-key.service';
import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service';
import { ClientContext } from 'src/common/models';
import { DatabaseInfoService } from 'src/modules/database/database-info.service';

const cloudConfig = config.get('cloud');

Expand All @@ -36,6 +39,8 @@ export class CreateFreeDatabaseCloudJob extends CloudJob {
cloudTaskCapiService: CloudTaskCapiService,
cloudDatabaseAnalytics: CloudDatabaseAnalytics,
databaseService: DatabaseService,
databaseInfoService: DatabaseInfoService,
bulkImportService: BulkImportService,
cloudCapiKeyService: CloudCapiKeyService,
},
) {
Expand Down Expand Up @@ -128,6 +133,22 @@ export class CreateFreeDatabaseCloudJob extends CloudJob {
timeout: cloudConfig.cloudDatabaseConnectionTimeout,
});

try {
const clientMetadata = {
databaseId: database.id,
sessionMetadata: this.options.sessionMetadata,
context: ClientContext.Common,
db: database.db,
};
const dbSize = await this.dependencies.databaseInfoService.getDBSize(clientMetadata);

if (dbSize === 0) {
this.dependencies.bulkImportService.importDefaultData(clientMetadata);
}
} catch (e) {
this.logger.error('Error when trying to feed the db with default data');
}

this.result = {
resourceId: database.id,
region: freeSubscription?.region,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Database } from 'src/modules/database/models/database';
import { CloudDatabaseAnalytics } from 'src/modules/cloud/database/cloud-database.analytics';
import { CloudCapiKeyService } from 'src/modules/cloud/capi-key/cloud-capi-key.service';
import { CloudSubscription } from 'src/modules/cloud/subscription/models';
import { DatabaseInfoService } from 'src/modules/database/database-info.service';
import { BulkImportService } from 'src/modules/bulk-actions/bulk-import.service';
import { CloudSubscriptionApiService } from '../../subscription/cloud-subscription.api.service';
import { CloudSubscriptionPlanResponse } from '../../subscription/dto';

Expand All @@ -31,6 +33,8 @@ export class CreateFreeSubscriptionAndDatabaseCloudJob extends CloudJob {
cloudTaskCapiService: CloudTaskCapiService,
cloudDatabaseAnalytics: CloudDatabaseAnalytics,
databaseService: DatabaseService,
databaseInfoService: DatabaseInfoService,
bulkImportService: BulkImportService,
cloudCapiKeyService: CloudCapiKeyService,
cloudSubscriptionApiService: CloudSubscriptionApiService,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
mockDatabaseOverviewProvider,
mockDatabaseRecommendationService,
mockDatabaseService,
mockDBSize,
mockRedisGeneralInfo,
mockStandaloneRedisClient,
MockType,
Expand Down Expand Up @@ -73,6 +74,12 @@ describe('DatabaseInfoService', () => {
});
});

describe('getDBSize', () => {
it('should create client and gets db size', async () => {
expect(await service.getDBSize(mockCommonClientMetadata)).toEqual(mockDBSize);
});
});

describe('getDatabaseIndex', () => {
it('should not return a new client', async () => {
expect(await service.getDatabaseIndex(mockCommonClientMetadata, 0)).toEqual(undefined);
Expand Down
11 changes: 11 additions & 0 deletions redisinsight/api/src/modules/database/database-info.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ export class DatabaseInfoService {
return this.databaseOverviewProvider.getOverview(clientMetadata, client);
}

/**
* Get redis database number of keys
*
* @param clientMetadata
*/
async getDBSize(clientMetadata: ClientMetadata): Promise<number> {
const client: RedisClient = await this.databaseClientFactory.getOrCreateClient(clientMetadata);

return this.databaseInfoProvider.getRedisDBSize(client);
}

/**
* Create connection to specified database index
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,29 @@ describe('DatabaseInfoProvider', () => {
});
});

describe('getRedisDBSize', () => {
it('get dbsize for redis standalone', async () => {
when(standaloneClient.sendCommand)
.calledWith(['dbsize'], { replyEncoding: 'utf8' })
.mockResolvedValue('1');

const result = await service.getRedisDBSize(standaloneClient);
expect(result).toEqual(1);
});

it('get general info for redis cluster', async () => {
clusterClient.nodes.mockResolvedValueOnce([standaloneClient, standaloneClient]);
when(standaloneClient.sendCommand)
.calledWith(['dbsize'], { replyEncoding: 'utf8' })
.mockResolvedValueOnce('1')
.mockResolvedValueOnce('2');

const result = await service.getRedisDBSize(clusterClient);

expect(result).toEqual(3);
});
});

describe('getRedisGeneralInfo', () => {
beforeEach(() => {
service.getDatabasesCount = jest.fn().mockResolvedValue(16);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ export class DatabaseInfoProvider {
return await this.filterRawModules(modules);
}

public async getRedisDBSize(client: RedisClient): Promise<number> {
if (client.getConnectionType() === RedisClientConnectionType.CLUSTER) {
const nodesResult: number[] = await Promise.all(
(await client.nodes()).map(async (node) => this.getRedisNodeDBSize(node)),
);
return nodesResult.reduce((ac, cur) => ac + cur, 0);
}
return await this.getRedisNodeDBSize(client);
}

public async getRedisGeneralInfo(
client: RedisClient,
): Promise<RedisDatabaseInfoResponse> {
Expand Down Expand Up @@ -238,4 +248,15 @@ export class DatabaseInfoProvider {
return undefined;
}
}

private async getRedisNodeDBSize(client: RedisClient): Promise<number> {
try {
const total = await client.sendCommand(['dbsize'], {
replyEncoding: 'utf8',
}) as string;
return parseInt(total, 10);
} catch (e) {
throw catchAclError(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import {
} from '@elastic/eui'
import { find } from 'lodash'
import cx from 'classnames'
import { CloudJobStep } from 'uiSrc/electron/constants'
import { CloudJobName, CloudJobStep } from 'uiSrc/electron/constants'
import ExternalLink from 'uiSrc/components/base/external-link'
import ChampagneIcon from 'uiSrc/assets/img/icons/champagne.svg'
import Divider from 'uiSrc/components/divider/Divider'
import { OAuthProviders } from 'uiSrc/components/oauth/oauth-select-plan/constants'

import { CloudSuccessResult } from 'uiSrc/slices/interfaces'

import { Maybe } from 'uiSrc/utils'
import styles from './styles.module.scss'

export enum InfiniteMessagesIds {
Expand Down Expand Up @@ -91,8 +92,11 @@ export const INFINITE_MESSAGES = {
</div>
)
}),
SUCCESS_CREATE_DB: (details: Omit<CloudSuccessResult, 'resourceId'>, onSuccess: () => void) => {
SUCCESS_CREATE_DB: (details: Omit<CloudSuccessResult, 'resourceId'>, onSuccess: () => void, jobName: Maybe<CloudJobName>) => {
const vendor = find(OAuthProviders, ({ id }) => id === details.provider)
const withFeed = jobName
&& [CloudJobName.CreateFreeDatabase, CloudJobName.CreateFreeSubscriptionAndDatabase].includes(jobName)
const text = `You can now use your Redis Stack database in Redis Cloud${withFeed ? ' with pre-loaded sample data' : ''}.`
return ({
id: InfiniteMessagesIds.oAuthSuccess,
className: 'wide',
Expand All @@ -110,7 +114,7 @@ export const INFINITE_MESSAGES = {
<EuiFlexItem grow>
<EuiTitle className="infiniteMessage__title"><span>Congratulations!</span></EuiTitle>
<EuiText size="xs">
You can now use your Redis Stack database in Redis Cloud.
{text}
<EuiSpacer size="s" />
<b>Notice:</b> the database will be deleted after 15 days of inactivity.
</EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { setSSOFlow } from 'uiSrc/slices/instances/cloud'
const OAuthJobs = () => {
const {
status,
name: jobName,
error,
step,
result,
Expand All @@ -44,7 +45,7 @@ const OAuthJobs = () => {
break

case CloudJobStatus.Finished:
dispatch(fetchInstancesAction(() => dispatch(createFreeDbSuccess(result, history))))
dispatch(fetchInstancesAction(() => dispatch(createFreeDbSuccess(result, history, jobName))))
dispatch(setJob({ id: '', name: CloudJobName.CreateFreeSubscriptionAndDatabase, status: '' }))
localStorageService.remove(BrowserStorageItem.OAuthJobId)
break
Expand Down
8 changes: 4 additions & 4 deletions redisinsight/ui/src/slices/oauth/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AxiosError } from 'axios'
import { remove } from 'lodash'
import { apiService, localStorageService } from 'uiSrc/services'
import { ApiEndpoints, BrowserStorageItem, Pages } from 'uiSrc/constants'
import { getApiErrorCode, getApiErrorMessage, getAxiosError, isStatusSuccessful, Nullable } from 'uiSrc/utils'
import { getApiErrorCode, getApiErrorMessage, getAxiosError, isStatusSuccessful, Maybe, Nullable } from 'uiSrc/utils'

import { CloudJobName, CloudJobStatus } from 'uiSrc/electron/constants'
import {
Expand Down Expand Up @@ -242,7 +242,7 @@ export const oauthCapiKeysSelector = (state: RootState) => state.oauth.cloud.cap
// The reducer
export default oauthCloudSlice.reducer

export function createFreeDbSuccess(result: CloudSuccessResult, history: any) {
export function createFreeDbSuccess(result: CloudSuccessResult, history: any, jobName: Maybe<CloudJobName>) {
return async (dispatch: AppDispatch, stateInit: () => RootState) => {
const { resourceId: id, ...details } = result
try {
Expand All @@ -257,12 +257,12 @@ export function createFreeDbSuccess(result: CloudSuccessResult, history: any) {
dispatch(checkConnectToInstanceAction(id))
}

history.push(Pages.workbench(id))
history.push(Pages.browser(id))
}

dispatch(showOAuthProgress(true))
dispatch(removeInfiniteNotification(InfiniteMessagesIds.oAuthProgress))
dispatch(addInfiniteNotification(INFINITE_MESSAGES.SUCCESS_CREATE_DB(details, onConnect)))
dispatch(addInfiniteNotification(INFINITE_MESSAGES.SUCCESS_CREATE_DB(details, onConnect, jobName)))
dispatch(setSelectAccountDialogState(false))
} catch (_err) {
const error = _err as AxiosError
Expand Down
4 changes: 2 additions & 2 deletions redisinsight/ui/src/slices/tests/oauth/cloud.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1024,13 +1024,13 @@ describe('oauth cloud slice', () => {
const onConnect = () => {}

// Act
await store.dispatch<any>(createFreeDbSuccess(result, {}))
await store.dispatch<any>(createFreeDbSuccess(result, {}, CloudJobName.CreateFreeDatabase))

// Assert
const expectedActions = [
showOAuthProgress(true),
removeInfiniteNotification(InfiniteMessagesIds.oAuthProgress),
addInfiniteNotification(INFINITE_MESSAGES.SUCCESS_CREATE_DB({}, onConnect)),
addInfiniteNotification(INFINITE_MESSAGES.SUCCESS_CREATE_DB({}, onConnect, CloudJobName.CreateFreeDatabase)),
setSelectAccountDialogState(false),
]
expect(clearStoreActions(store.getActions())).toEqual(clearStoreActions(expectedActions))
Expand Down