From 36f811d6293117a73ccf06005e2634040accfa7d Mon Sep 17 00:00:00 2001 From: pd-redis Date: Mon, 5 May 2025 09:36:13 +0300 Subject: [PATCH 01/20] add additional info to getRedisNodeGeneralInfo --- .../modules/database/dto/redis-info.dto.ts | 6 ++++ .../providers/database-info.provider.ts | 31 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/redisinsight/api/src/modules/database/dto/redis-info.dto.ts b/redisinsight/api/src/modules/database/dto/redis-info.dto.ts index 8fda151ed9..f80683bdc9 100644 --- a/redisinsight/api/src/modules/database/dto/redis-info.dto.ts +++ b/redisinsight/api/src/modules/database/dto/redis-info.dto.ts @@ -22,6 +22,12 @@ export class RedisNodeInfoResponse { }) server?: any; + @ApiPropertyOptional({ + description: 'Various Redis stats', + type: Object, + }) + stats?: any; + @ApiPropertyOptional({ description: 'The number of Redis databases', type: Number, diff --git a/redisinsight/api/src/modules/database/providers/database-info.provider.ts b/redisinsight/api/src/modules/database/providers/database-info.provider.ts index ec4607e036..671a97ed8d 100644 --- a/redisinsight/api/src/modules/database/providers/database-info.provider.ts +++ b/redisinsight/api/src/modules/database/providers/database-info.provider.ts @@ -3,6 +3,8 @@ import { calculateRedisHitRatio, catchAclError, convertIntToSemanticVersion, + getRangeForNumber, + TOTAL_KEYS_BREAKPOINTS, } from 'src/utils'; import { AdditionalRedisModule } from 'src/modules/database/models/additional.redis.module'; import { REDIS_MODULES_COMMANDS, SUPPORTED_REDIS_MODULES } from 'src/constants'; @@ -162,11 +164,23 @@ export class DatabaseInfoProvider { const statsInfo = info['stats']; const replicationInfo = info['replication']; const databases = await this.getDatabasesCount(client, keyspaceInfo); + /* +redis_version +uptime_in_days +used_memory +connected_clients +maxmemory_policy +instantaneous_ops_per_sec +instantaneous_input_kbps: +instantaneous_output_kbps +Number of keys (grouped and actual value) - the same as for CONFIG_DATABASES_DATABASE_ADDED + */ + const totalKeys = this.getRedisNodeTotalKeysCount(keyspaceInfo); return { version: serverInfo?.redis_version, databases, role: get(replicationInfo, 'role') || undefined, - totalKeys: this.getRedisNodeTotalKeysCount(keyspaceInfo), + totalKeys, usedMemory: parseInt(get(memoryInfo, 'used_memory'), 10) || undefined, connectedClients: parseInt(get(clientsInfo, 'connected_clients'), 10) || undefined, @@ -177,6 +191,21 @@ export class DatabaseInfoProvider { parseInt(get(memoryInfo, 'number_of_cached_scripts'), 10) || undefined, server: serverInfo, + stats: { + instantaneous_ops_per_sec: + get(statsInfo, 'instantaneous_ops_per_sec') || undefined, + instantaneous_input_kbps: + get(statsInfo, 'instantaneous_input_kbps') || undefined, + instantaneous_output_kbps: + get(statsInfo, 'instantaneous_output_kbps') || undefined, + uptime_in_days: get(serverInfo, 'uptime_in_days') || undefined, + maxmemory_policy: + parseInt(get(memoryInfo, 'maxmemory_policy'), 10) || undefined, + numberOfKeysRange: getRangeForNumber( + totalKeys, + TOTAL_KEYS_BREAKPOINTS, + ), + }, }; } catch (error) { throw catchAclError(error); From 70fb4698d72fa9dd9c0cd7f4a8f2b66817213d39 Mon Sep 17 00:00:00 2001 From: pd-redis Date: Mon, 5 May 2025 12:40:12 +0300 Subject: [PATCH 02/20] refactor calls to api in instances.ts, add instancesService.ts, add info to InstancesList.tsx goToInstance --- .../providers/database-info.provider.ts | 14 +- .../instances-list/InstancesList.spec.tsx | 35 +++- .../instances-list/InstancesList.tsx | 7 +- .../src/services/database/instancesService.ts | 142 ++++++++++++++++ redisinsight/ui/src/services/index.ts | 1 + .../ui/src/slices/instances/instances.ts | 153 +++++------------- .../ui/src/telemetry/telemetryUtils.ts | 23 +++ 7 files changed, 243 insertions(+), 132 deletions(-) create mode 100644 redisinsight/ui/src/services/database/instancesService.ts diff --git a/redisinsight/api/src/modules/database/providers/database-info.provider.ts b/redisinsight/api/src/modules/database/providers/database-info.provider.ts index 671a97ed8d..becb792212 100644 --- a/redisinsight/api/src/modules/database/providers/database-info.provider.ts +++ b/redisinsight/api/src/modules/database/providers/database-info.provider.ts @@ -164,17 +164,6 @@ export class DatabaseInfoProvider { const statsInfo = info['stats']; const replicationInfo = info['replication']; const databases = await this.getDatabasesCount(client, keyspaceInfo); - /* -redis_version -uptime_in_days -used_memory -connected_clients -maxmemory_policy -instantaneous_ops_per_sec -instantaneous_input_kbps: -instantaneous_output_kbps -Number of keys (grouped and actual value) - the same as for CONFIG_DATABASES_DATABASE_ADDED - */ const totalKeys = this.getRedisNodeTotalKeysCount(keyspaceInfo); return { version: serverInfo?.redis_version, @@ -199,8 +188,7 @@ Number of keys (grouped and actual value) - the same as for CONFIG_DATABASES_DAT instantaneous_output_kbps: get(statsInfo, 'instantaneous_output_kbps') || undefined, uptime_in_days: get(serverInfo, 'uptime_in_days') || undefined, - maxmemory_policy: - parseInt(get(memoryInfo, 'maxmemory_policy'), 10) || undefined, + maxmemory_policy: get(memoryInfo, 'maxmemory_policy') || undefined, numberOfKeysRange: getRangeForNumber( totalKeys, TOTAL_KEYS_BREAKPOINTS, diff --git a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx index 0fcf938075..49b39eb28a 100644 --- a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx +++ b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx @@ -10,6 +10,8 @@ import { screen, } from 'uiSrc/utils/test-utils' import { TelemetryEvent, sendEventTelemetry } from 'uiSrc/telemetry' +import { apiService } from 'uiSrc/services' +import { Instance, RdiInstance } from 'uiSrc/slices/interfaces' import InstancesList, { InstancesListProps } from './InstancesList' import { InstancesTabs } from '../../InstancesNavigationPopover' @@ -22,25 +24,39 @@ beforeEach(() => { store.clearActions() }) -const mockRdis = [ +const mockRdis: RdiInstance[] = [ { id: 'rdiDB_1', name: 'RdiDB_1', + loading: false, + error: '', + url: '', }, { id: 'rdiDB_2', name: 'RdiDB_2', + loading: false, + error: '', + url: '', }, ] -const mockDbs = [ +const mockDbs: Instance[] = [ { id: 'db_1', name: 'DB_1', + host: 'localhost', + port: 6379, + modules: [], + version: '7.0.0', }, { id: 'db_2', name: 'DB_2', + host: 'localhost', + port: 6379, + modules: [], + version: '7.0.0', }, ] @@ -85,7 +101,7 @@ describe('InstancesList', () => { />, ) - expect(screen.getByText(mockDbs[0].name)).toBeInTheDocument() + expect(screen.getByText(mockDbs[0].name!)).toBeInTheDocument() }) it('should render rdi instances when selected tab is rdi', () => { @@ -99,11 +115,18 @@ describe('InstancesList', () => { expect(screen.getByText(mockRdis[0].name)).toBeInTheDocument() }) - it('should send event telemetry', () => { + it('should send event telemetry', async () => { const sendEventTelemetryMock = jest.fn() ;(sendEventTelemetry as jest.Mock).mockImplementation( () => sendEventTelemetryMock, ) + jest + .spyOn(apiService, 'get') + .mockImplementation( + jest + .fn() + .mockResolvedValue({ data: { version: '7.4.0' }, status: 200 }), + ) render( { ) const listItem = screen.getByTestId(`instance-item-${mockDbs[1].id}`) - act(() => { + await act(() => { fireEvent.click(listItem) }) - expect(sendEventTelemetry).toBeCalledWith({ + expect(sendEventTelemetry).toHaveBeenCalledWith({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: expect.any(Object), }) diff --git a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.tsx b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.tsx index da15aba394..6f8a01dccf 100644 --- a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.tsx +++ b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.tsx @@ -13,6 +13,7 @@ import { TelemetryEvent, getRedisModulesSummary, sendEventTelemetry, + getRedisInfoSummary, } from 'uiSrc/telemetry' import { getDbIndex } from 'uiSrc/utils' import { @@ -56,20 +57,22 @@ const InstancesList = ({ history.push(Pages.browser(id)) } - const goToInstance = (instance: Instance) => { + const goToInstance = async (instance: Instance) => { if (instanceId === instance.id) { // already connected so do nothing return } setLoading(true) const modulesSummary = getRedisModulesSummary(instance.modules) - sendEventTelemetry({ + const infoData = await getRedisInfoSummary(instance.id) + await sendEventTelemetry({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: { databaseId: instance.id, source: 'navigation_panel', provider: instance.provider, ...modulesSummary, + ...infoData, }, }) dispatch( diff --git a/redisinsight/ui/src/services/database/instancesService.ts b/redisinsight/ui/src/services/database/instancesService.ts new file mode 100644 index 0000000000..7afd2d7622 --- /dev/null +++ b/redisinsight/ui/src/services/database/instancesService.ts @@ -0,0 +1,142 @@ +import { RedisNodeInfoResponse } from 'src/modules/database/dto/redis-info.dto' +import { Database as DatabaseInstanceResponse } from 'src/modules/database/models/database' +import { ExportDatabase } from 'src/modules/database/models/export-database' +import axios, { CancelTokenSource } from 'axios' +import { ApiEndpoints } from 'uiSrc/constants' +import { apiService } from 'uiSrc/services' +import { isStatusSuccessful, Nullable } from 'uiSrc/utils' +import { Instance } from 'uiSrc/slices/interfaces' +import { PartialInstance } from 'uiSrc/slices/instances/instances' + +const endpoint = ApiEndpoints.DATABASES + +export async function getInstanceInfo(id: string) { + const { data, status } = await apiService.get( + `${endpoint}/${id}/info`, + ) + + return isStatusSuccessful(status) ? data : null +} +export async function getInstanceOverview(id: string) { + const { data, status } = await apiService.get( + `${endpoint}/${id}/overview`, + ) + + return isStatusSuccessful(status) ? data : null +} + +export const sourceInstance: { + source: Nullable +} = { + source: null, +} + +export async function updateInstanceAlias(id: string, name: string) { + sourceInstance.source?.cancel?.() + const { CancelToken } = axios + sourceInstance.source = CancelToken.source() + + const { status } = await apiService.patch( + `${endpoint}/${id}`, + { name }, + { cancelToken: sourceInstance.source.token }, + ) + + sourceInstance.source = null + + return isStatusSuccessful(status) +} + +export async function checkInstanceDbIndex(id: string, index: number) { + const { status } = await apiService.get(`${endpoint}/${id}/db/${index}`) + + return isStatusSuccessful(status) +} + +export async function importInstances(importData: FormData) { + const { status, data } = await apiService.post( + ApiEndpoints.DATABASES_IMPORT, + importData, + { + headers: { + Accept: 'application/json', + 'Content-Type': 'multipart/form-data', + }, + }, + ) + + return isStatusSuccessful(status) ? data : null +} + +export async function testInstanceConnection( + id?: string, + payload?: PartialInstance, +) { + const url = id + ? `${ApiEndpoints.DATABASES_TEST_CONNECTION}/${id}` + : `${ApiEndpoints.DATABASES_TEST_CONNECTION}` + const { status } = await apiService.post(url, payload) + + return isStatusSuccessful(status) +} + +export async function getInstance(id: string) { + const { data, status } = await apiService.get(`${endpoint}/${id}`) + + return isStatusSuccessful(status) ? data : null +} + +export async function connectInstance(id: string) { + const { status } = await apiService.get(`${endpoint}/${id}/connect`) + + return isStatusSuccessful(status) +} + +export async function listDatabases() { + const { data, status } = + await apiService.get(endpoint) + + return isStatusSuccessful(status) ? data : null +} + +export async function createInstance(instance: Instance) { + const { data, status } = await apiService.post(endpoint, instance) + + return isStatusSuccessful(status) ? data : null +} + +export async function updateInstance( + id: string, + instance: Partial | PartialInstance, +) { + const { status } = await apiService.patch(`${endpoint}/${id}`, instance) + + return isStatusSuccessful(status) +} + +export async function cloneInstance(id: string, instance: Partial) { + const { status, data } = await apiService.post( + `${endpoint}/clone/${id}`, + instance, + ) + + return isStatusSuccessful(status) ? data : null +} + +export async function deleteInstances(databasesIds: string[]) { + const { status } = await apiService.delete(endpoint, { + data: { ids: databasesIds }, + }) + return isStatusSuccessful(status) +} + +export async function exportInstances(ids: string[], withSecrets: boolean) { + const { data, status } = await apiService.post( + ApiEndpoints.DATABASES_EXPORT, + { + ids, + withSecrets, + }, + ) + return isStatusSuccessful(status) ? data : null +} diff --git a/redisinsight/ui/src/services/index.ts b/redisinsight/ui/src/services/index.ts index bbbe00b420..9f4191ee29 100644 --- a/redisinsight/ui/src/services/index.ts +++ b/redisinsight/ui/src/services/index.ts @@ -12,3 +12,4 @@ export * from './hooks' export * from './capability' export { apiService, resourcesService } export { WorkbenchStorage } from 'uiSrc/services/workbenchStorage' +export * as instancesService from 'uiSrc/services/database/instancesService' diff --git a/redisinsight/ui/src/slices/instances/instances.ts b/redisinsight/ui/src/slices/instances/instances.ts index bd94dbf5c7..b2369bcaa8 100644 --- a/redisinsight/ui/src/slices/instances/instances.ts +++ b/redisinsight/ui/src/slices/instances/instances.ts @@ -1,15 +1,14 @@ -import { first, isNull, map, filter, orderBy, get } from 'lodash' +import { filter, first, get, isNull, map, orderBy } from 'lodash' import { createSlice } from '@reduxjs/toolkit' -import axios, { AxiosError, CancelTokenSource } from 'axios' +import axios, { AxiosError } from 'axios' import ApiErrors from 'uiSrc/constants/apiErrors' import { - apiService, + instancesService, localStorageService, sessionStorageService, } from 'uiSrc/services' import { - ApiEndpoints, BrowserStorageItem, COLUMN_FIELD_NAME_MAP, CustomErrorCodes, @@ -18,12 +17,7 @@ import { import { setAppContextInitialState } from 'uiSrc/slices/app/context' import { resetKeys } from 'uiSrc/slices/browser/keys' import successMessages from 'uiSrc/components/notifications/success-messages' -import { - checkRediStack, - getApiErrorMessage, - isStatusSuccessful, - Nullable, -} from 'uiSrc/utils' +import { checkRediStack, getApiErrorMessage, Nullable } from 'uiSrc/utils' import { INFINITE_MESSAGES, InfiniteMessagesIds, @@ -41,11 +35,7 @@ import { addMessageNotification, removeInfiniteNotification, } from '../app/notifications' -import { - Instance, - InitialStateInstances, - ConnectionType, -} from '../interfaces' +import { ConnectionType, InitialStateInstances, Instance } from '../interfaces' const HIDE_CREATING_DB_DELAY_MS = 500 @@ -378,9 +368,6 @@ export const importInstancesSelector = (state: RootState) => // The reducer export default instancesSlice.reducer -// eslint-disable-next-line import/no-mutable-exports -export let sourceInstance: Nullable = null - // Asynchronous thunk action export function fetchInstancesAction(onSuccess?: (data: Instance[]) => void) { return async (dispatch: AppDispatch, stateInit: () => RootState) => { @@ -399,11 +386,9 @@ export function fetchInstancesAction(onSuccess?: (data: Instance[]) => void) { dispatch(loadInstances()) try { - const { data, status } = await apiService.get( - `${ApiEndpoints.DATABASES}`, - ) + const data = await instancesService.listDatabases() - if (isStatusSuccessful(status)) { + if (data !== null) { localStorageService.set(BrowserStorageItem.instancesCount, data?.length) onSuccess?.(data as Instance[]) dispatch(loadInstancesSuccess(data)) @@ -429,12 +414,9 @@ export function createInstanceStandaloneAction( dispatch(defaultInstanceChanging()) try { - const { data, status } = await apiService.post( - `${ApiEndpoints.DATABASES}`, - payload, - ) + const data = await instancesService.createInstance(payload) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(defaultInstanceChangingSuccess()) dispatch(fetchInstancesAction()) @@ -500,13 +482,10 @@ export function autoCreateAndConnectToInstanceAction( ) try { - const { status, data } = await apiService.post( - `${ApiEndpoints.DATABASES}`, - payload, - ) + const data = await instancesService.createInstance(payload) - if (isStatusSuccessful(status)) { - dispatch( + if (data !== null) { + await dispatch( autoCreateAndConnectToInstanceActionSuccess( data?.id, successMessages.ADDED_NEW_INSTANCE(data?.name), @@ -581,7 +560,7 @@ function autoCreateAndConnectToInstanceActionSuccess( } } -type PartialInstance = Partial> & { +export type PartialInstance = Partial> & { tags?: { key: string value: string @@ -597,12 +576,9 @@ export function updateInstanceAction( dispatch(defaultInstanceChanging()) try { - const { status } = await apiService.patch( - `${ApiEndpoints.DATABASES}/${id}`, - payload, - ) + const result = await instancesService.updateInstance(id!, payload) - if (isStatusSuccessful(status)) { + if (result) { dispatch(defaultInstanceChangingSuccess()) dispatch(fetchInstancesAction()) if (payload.tags) { @@ -628,12 +604,9 @@ export function cloneInstanceAction( dispatch(defaultInstanceChanging()) try { - const { status, data } = await apiService.post( - `${ApiEndpoints.DATABASES}/clone/${id}`, - payload, - ) + const data = await instancesService.cloneInstance(id!, payload) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(defaultInstanceChangingSuccess()) dispatch(fetchInstancesAction()) @@ -664,11 +637,9 @@ export function deleteInstancesAction( try { const state = stateInit() const databasesIds = map(instances, 'id') - const { status } = await apiService.delete(ApiEndpoints.DATABASES, { - data: { ids: databasesIds }, - }) + const result = await instancesService.deleteInstances(databasesIds) - if (isStatusSuccessful(status)) { + if (result) { dispatch(setDefaultInstanceSuccess()) dispatch(fetchInstancesAction()) dispatch(fetchTags()) @@ -714,15 +685,9 @@ export function exportInstancesAction( dispatch(setDefaultInstance()) try { - const { data, status } = await apiService.post( - ApiEndpoints.DATABASES_EXPORT, - { - ids, - withSecrets, - }, - ) + const data = await instancesService.exportInstances(ids, withSecrets) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(setDefaultInstanceSuccess()) onSuccess?.(data) @@ -748,11 +713,9 @@ export function fetchConnectedInstanceAction( dispatch(setConnectedInstance()) try { - const { data, status } = await apiService.get( - `${ApiEndpoints.DATABASES}/${id}`, - ) + const data = await instancesService.getInstance(id) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(setConnectedInstanceSuccess(data)) dispatch(setDefaultInstanceSuccess()) } @@ -777,13 +740,13 @@ export function fetchConnectedInstanceInfoAction( dispatch(setConnectedInfoInstance()) try { - const { data, status } = await apiService.get( - `${ApiEndpoints.DATABASES}/${id}/info`, - ) + const data = await instancesService.getInstanceInfo(id) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(setConnectedInfoInstanceSuccess(data)) onSuccess?.() + } else { + onFail?.() } } catch (error) { onFail?.() @@ -801,11 +764,9 @@ export function fetchEditedInstanceAction( dispatch(setEditedInstance(instance)) try { - const { data, status } = await apiService.get( - `${ApiEndpoints.DATABASES}/${instance.id}`, - ) + const data = await instancesService.getInstance(instance.id) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(updateEditedInstance(data)) dispatch(setDefaultInstanceSuccess()) } @@ -832,11 +793,9 @@ export function checkConnectToInstanceAction( dispatch(setDefaultInstance()) resetInstance && dispatch(resetConnectedInstance()) try { - const { status } = await apiService.get( - `${ApiEndpoints.DATABASES}/${id}/connect`, - ) + const result = await instancesService.connectInstance(id) - if (isStatusSuccessful(status)) { + if (result) { dispatch(setDefaultInstanceSuccess()) onSuccessAction?.(id) } @@ -873,11 +832,9 @@ export function getDatabaseConfigInfoAction( return async (dispatch: AppDispatch) => { dispatch(getDatabaseConfigInfo()) try { - const { status, data } = await apiService.get( - `${ApiEndpoints.DATABASES}/${id}/overview`, - ) + const data = await instancesService.getInstanceOverview(id) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(getDatabaseConfigInfoSuccess(data)) onSuccessAction?.(id) } @@ -901,18 +858,8 @@ export function changeInstanceAliasAction( dispatch(changeInstanceAlias()) try { - sourceInstance?.cancel?.() - const { CancelToken } = axios - sourceInstance = CancelToken.source() - - const { status } = await apiService.patch( - `${ApiEndpoints.DATABASES}/${id}`, - { name }, - { cancelToken: sourceInstance.token }, - ) - - sourceInstance = null - if (isStatusSuccessful(status)) { + const result = await instancesService.updateInstanceAlias(id, name) + if (result) { dispatch(changeInstanceAliasSuccess({ id, name })) onSuccessAction?.() } @@ -938,11 +885,9 @@ export function checkDatabaseIndexAction( dispatch(checkDatabaseIndex()) try { - const { status } = await apiService.get( - `${ApiEndpoints.DATABASES}/${id}/db/${index}`, - ) + const result = await instancesService.checkInstanceDbIndex(id, index) - if (isStatusSuccessful(status)) { + if (result) { dispatch(checkDatabaseIndexSuccess(index)) onSuccessAction?.() } @@ -958,7 +903,7 @@ export function checkDatabaseIndexAction( export function resetInstanceUpdateAction() { return async (dispatch: AppDispatch) => { dispatch(resetInstanceUpdate()) - sourceInstance?.cancel?.() + instancesService.sourceInstance?.source?.cancel?.() } } @@ -972,18 +917,9 @@ export function uploadInstancesFile( dispatch(importInstancesFromFile()) try { - const { status, data } = await apiService.post( - ApiEndpoints.DATABASES_IMPORT, - file, - { - headers: { - Accept: 'application/json', - 'Content-Type': 'multipart/form-data', - }, - }, - ) + const data = await instancesService.importInstances(file) - if (isStatusSuccessful(status)) { + if (data !== null) { dispatch(fetchTags()) dispatch(importInstancesFromFileSuccess(data)) onSuccessAction?.(data) @@ -1004,14 +940,9 @@ export function testInstanceStandaloneAction( ) { return async (dispatch: AppDispatch) => { dispatch(testConnection()) - const url = id - ? `${ApiEndpoints.DATABASES_TEST_CONNECTION}/${id}` - : `${ApiEndpoints.DATABASES_TEST_CONNECTION}` - try { - const { status } = await apiService.post(url, payload) - - if (isStatusSuccessful(status)) { + const result = await instancesService.testInstanceConnection(id, payload) + if (result) { dispatch(testConnectionSuccess()) dispatch(addMessageNotification(successMessages.TEST_CONNECTION())) diff --git a/redisinsight/ui/src/telemetry/telemetryUtils.ts b/redisinsight/ui/src/telemetry/telemetryUtils.ts index 59bf0a534d..8eedbb9b1f 100644 --- a/redisinsight/ui/src/telemetry/telemetryUtils.ts +++ b/redisinsight/ui/src/telemetry/telemetryUtils.ts @@ -16,6 +16,7 @@ import { } from 'uiSrc/telemetry/interfaces' import { apiService } from 'uiSrc/services' import { store } from 'uiSrc/slices/store' +import { getInstanceInfo } from 'uiSrc/services/database/instancesService' import { AdditionalRedisModule } from 'apiSrc/modules/database/models/additional.redis.module' import { IRedisModulesSummary, MatchType, RedisModules } from './interfaces' import { TelemetryEvent } from './events' @@ -237,7 +238,28 @@ const getModuleSummaryToSent = ( version: module.version, semanticVersion: module.semanticVersion, }) +const getRedisInfoSummary = async (id: string) => { + let infoData: any = {} + try { + const info = await getInstanceInfo(id) + infoData = { + redis_version: info?.version, + uptime_in_days: info?.stats?.uptime_in_days, + used_memory: info?.usedMemory, + connected_clients: info?.connectedClients, + maxmemory_policy: info?.stats?.maxmemory_policy, + instantaneous_ops_per_sec: info?.stats?.instantaneous_ops_per_sec, + instantaneous_input_kbps: info?.stats?.instantaneous_input_kbps, + instantaneous_output_kbps: info?.stats?.instantaneous_output_kbps, + numberOfKeysRange: info?.stats?.numberOfKeysRange, + totalKeys: info?.totalKeys, + } + } catch (e) { + // continue regardless of error + } + return infoData +} const getRedisModulesSummary = ( modules: AdditionalRedisModule[] = [], ): IRedisModulesSummary => { @@ -279,4 +301,5 @@ export { getMatchType, getRedisModulesSummary, getFreeDbFlag, + getRedisInfoSummary, } From c78474d87e0eb716c2a2ffeef1badc0cdb39b9ba Mon Sep 17 00:00:00 2001 From: pd-redis Date: Mon, 5 May 2025 13:54:55 +0300 Subject: [PATCH 03/20] updated tests --- .../instances-list/InstancesList.spec.tsx | 18 +++---------- .../OAuthConnectFreeDb.spec.tsx | 20 +++++++++++--- .../OAuthConnectFreeDb.tsx | 9 ++++--- redisinsight/ui/src/mocks/data/instances.ts | 27 +++++++++++++++++++ .../handlers/instances/instancesHandlers.ts | 16 ++++++++--- .../DatabasesListWrapper.test.tsx | 18 ++++++------- .../DatabasesListWrapper.tsx | 5 +++- .../src/services/database/instancesService.ts | 1 - .../slices/tests/instances/instances.spec.ts | 3 ++- 9 files changed, 80 insertions(+), 37 deletions(-) create mode 100644 redisinsight/ui/src/mocks/data/instances.ts diff --git a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx index 49b39eb28a..54d423b8b4 100644 --- a/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx +++ b/redisinsight/ui/src/components/instance-header/components/instances-navigation-popover/components/instances-list/InstancesList.spec.tsx @@ -9,8 +9,7 @@ import { render, screen, } from 'uiSrc/utils/test-utils' -import { TelemetryEvent, sendEventTelemetry } from 'uiSrc/telemetry' -import { apiService } from 'uiSrc/services' +import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry' import { Instance, RdiInstance } from 'uiSrc/slices/interfaces' import InstancesList, { InstancesListProps } from './InstancesList' import { InstancesTabs } from '../../InstancesNavigationPopover' @@ -120,15 +119,8 @@ describe('InstancesList', () => { ;(sendEventTelemetry as jest.Mock).mockImplementation( () => sendEventTelemetryMock, ) - jest - .spyOn(apiService, 'get') - .mockImplementation( - jest - .fn() - .mockResolvedValue({ data: { version: '7.4.0' }, status: 200 }), - ) - render( + const { getByTestId } = render( { />, ) - const listItem = screen.getByTestId(`instance-item-${mockDbs[1].id}`) - await act(() => { - fireEvent.click(listItem) - }) + const listItem = getByTestId(`instance-item-${mockDbs[1].id}`) + await act(() => fireEvent.click(listItem)) expect(sendEventTelemetry).toHaveBeenCalledWith({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, diff --git a/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.spec.tsx b/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.spec.tsx index f3987f8b5a..356f3d3777 100644 --- a/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.spec.tsx +++ b/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.spec.tsx @@ -1,10 +1,16 @@ import React from 'react' import { cloneDeep } from 'lodash' -import { cleanup, fireEvent, mockedStore, render } from 'uiSrc/utils/test-utils' import { - TelemetryEvent, + act, + cleanup, + fireEvent, + mockedStore, + render, +} from 'uiSrc/utils/test-utils' +import { getRedisModulesSummary, sendEventTelemetry, + TelemetryEvent, } from 'uiSrc/telemetry' import { freeInstancesSelector, @@ -12,6 +18,7 @@ import { } from 'uiSrc/slices/instances/instances' import { OAuthSocialSource } from 'uiSrc/slices/interfaces' import { setCapability } from 'uiSrc/slices/app/context' +import { MOCK_ADDITIONAL_INFO } from 'uiSrc/mocks/data/instances' import OAuthConnectFreeDb from './OAuthConnectFreeDb' jest.mock('uiSrc/telemetry', () => ({ @@ -56,15 +63,20 @@ describe('OAuthConnectFreeDb', () => { const { queryByTestId } = render() - fireEvent.click(queryByTestId('connect-free-db-btn') as HTMLButtonElement) + await act(() => + fireEvent.click( + queryByTestId('connect-free-db-btn') as HTMLButtonElement, + ), + ) - expect(sendEventTelemetry).toBeCalledWith({ + expect(sendEventTelemetry).toHaveBeenCalledWith({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: { databaseId: 'providedId', provider: undefined, source: OAuthSocialSource.ListOfDatabases, ...getRedisModulesSummary(), + ...MOCK_ADDITIONAL_INFO, }, }) diff --git a/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.tsx b/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.tsx index ff046c0841..7ec6569fad 100644 --- a/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.tsx +++ b/redisinsight/ui/src/components/oauth/oauth-connect-free-db/OAuthConnectFreeDb.tsx @@ -8,6 +8,7 @@ import { TelemetryEvent, getRedisModulesSummary, sendEventTelemetry, + getRedisInfoSummary, } from 'uiSrc/telemetry' import { OAuthSocialSource } from 'uiSrc/slices/interfaces' import { @@ -48,8 +49,9 @@ const OAuthConnectFreeDb = ({ return null } - const sendTelemetry = () => { + const sendTelemetry = async () => { const modulesSummary = getRedisModulesSummary(modules) + const infoData = await getRedisInfoSummary(targetDatabaseId) sendEventTelemetry({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: { @@ -57,6 +59,7 @@ const OAuthConnectFreeDb = ({ provider, source, ...modulesSummary, + ...infoData, }, }) } @@ -67,8 +70,8 @@ const OAuthConnectFreeDb = ({ openNewWindowDatabase(Pages.browser(targetDatabaseId) + search) } - const handleCheckConnectToInstance = () => { - sendTelemetry() + const handleCheckConnectToInstance = async () => { + await sendTelemetry() dispatch(setCapability({ source, tutorialPopoverShown: false })) dispatch( checkConnectToInstanceAction( diff --git a/redisinsight/ui/src/mocks/data/instances.ts b/redisinsight/ui/src/mocks/data/instances.ts new file mode 100644 index 0000000000..7ac97990e3 --- /dev/null +++ b/redisinsight/ui/src/mocks/data/instances.ts @@ -0,0 +1,27 @@ +export const MOCK_INFO_API_RESPONSE = { + version: '7.4.0', + usedMemory: 1234, + connectedClients: 2, + totalKeys: 123, + stats: { + uptime_in_days: '2', + maxmemory_policy: 'allkeys-lru', + instantaneous_ops_per_sec: 123, + instantaneous_input_kbps: 123, + instantaneous_output_kbps: 123, + numberOfKeysRange: '0-123', + }, +} + +export const MOCK_ADDITIONAL_INFO = { + connected_clients: 2, + instantaneous_input_kbps: 123, + instantaneous_ops_per_sec: 123, + instantaneous_output_kbps: 123, + maxmemory_policy: 'allkeys-lru', + numberOfKeysRange: '0-123', + redis_version: '7.4.0', + totalKeys: 123, + uptime_in_days: '2', + used_memory: 1234, +} diff --git a/redisinsight/ui/src/mocks/handlers/instances/instancesHandlers.ts b/redisinsight/ui/src/mocks/handlers/instances/instancesHandlers.ts index 6b369033e4..e856652241 100644 --- a/redisinsight/ui/src/mocks/handlers/instances/instancesHandlers.ts +++ b/redisinsight/ui/src/mocks/handlers/instances/instancesHandlers.ts @@ -1,8 +1,10 @@ import { rest, RestHandler } from 'msw' +import { RedisNodeInfoResponse } from 'src/modules/database/dto/redis-info.dto' import { ApiEndpoints } from 'uiSrc/constants' import { ConnectionType, Instance } from 'uiSrc/slices/interfaces' import { getMswURL } from 'uiSrc/utils/test-utils' import { getUrl } from 'uiSrc/utils' +import { MOCK_INFO_API_RESPONSE } from 'uiSrc/mocks/data/instances' import { Database as DatabaseInstanceResponse } from 'apiSrc/modules/database/models/database' import { ExportDatabase } from 'apiSrc/modules/database/models/export-database' @@ -10,6 +12,7 @@ export const INSTANCE_ID_MOCK = 'instanceId' export const INSTANCES_MOCK: Instance[] = [ { id: INSTANCE_ID_MOCK, + version: '6.2.6', host: 'localhost', port: 6379, name: 'localhost', @@ -18,7 +21,7 @@ export const INSTANCES_MOCK: Instance[] = [ connectionType: ConnectionType.Standalone, nameFromProvider: null, modules: [], - uoeu: 123, + db: 123, lastConnection: new Date('2021-04-22T09:03:56.917Z'), }, { @@ -39,6 +42,7 @@ export const INSTANCES_MOCK: Instance[] = [ }, { id: 'b83a3932-e95f-4f09-9d8a-55079f400186', + version: '6.2.6', host: 'localhost', port: 5005, name: 'sentinel', @@ -66,7 +70,7 @@ export const INSTANCES_MOCK: Instance[] = [ export const getDatabasesApiSpy = jest .fn() - .mockImplementation(async (req, res, ctx) => + .mockImplementation(async (_req, res, ctx) => res(ctx.status(200), ctx.json(INSTANCES_MOCK)), ) @@ -78,12 +82,18 @@ const handlers: RestHandler[] = [ ), rest.post( getMswURL(ApiEndpoints.DATABASES_EXPORT), - async (req, res, ctx) => res(ctx.status(200), ctx.json(INSTANCES_MOCK)), + async (_req, res, ctx) => res(ctx.status(200), ctx.json(INSTANCES_MOCK)), ), rest.get( getMswURL(getUrl(INSTANCE_ID_MOCK)), async (_req, res, ctx) => res(ctx.status(200), ctx.json(INSTANCES_MOCK[0])), ), + rest.get( + getMswURL(`/${ApiEndpoints.DATABASES}/:id/info`), + // getMswURL(getUrl(INSTANCE_ID_MOCK, 'info')), + async (_req, res, ctx) => + res(ctx.status(200), ctx.json(MOCK_INFO_API_RESPONSE)), + ), ] // rest.post(`${ApiEndpoints.INSTANCE}`, (req, res, ctx) => { diff --git a/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.test.tsx b/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.test.tsx index 325ef1c0b1..35a8a41c31 100644 --- a/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.test.tsx +++ b/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.test.tsx @@ -23,6 +23,7 @@ import { CREATE_CLOUD_DB_ID } from 'uiSrc/pages/home/constants' import { setSSOFlow } from 'uiSrc/slices/instances/cloud' import { setSocialDialogState } from 'uiSrc/slices/oauth/cloud' import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features' +import { MOCK_ADDITIONAL_INFO } from 'uiSrc/mocks/data/instances' import DatabasesListWrapper, { Props } from './DatabasesListWrapper' const mockedProps = mock() @@ -208,26 +209,22 @@ describe('DatabasesListWrapper', () => { it('should call proper telemetry on open database', async () => { const sendEventTelemetryMock = jest.fn() - ;(sendEventTelemetry as jest.Mock).mockImplementation( () => sendEventTelemetryMock, ) - render( + const { getByTestId } = render( , ) - await act(() => { + await act(() => fireEvent.click( - screen.getByTestId( - 'instance-name-e37cc441-a4f2-402c-8bdb-fc2413cbbaff', - ), - ) - }) - - expect(sendEventTelemetry).toBeCalledWith({ + getByTestId('instance-name-e37cc441-a4f2-402c-8bdb-fc2413cbbaff'), + ), + ) + expect(sendEventTelemetry).toHaveBeenCalledWith({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: { databaseId: 'e37cc441-a4f2-402c-8bdb-fc2413cbbaff', @@ -255,6 +252,7 @@ describe('DatabasesListWrapper', () => { loaded: false, }, customModules: [], + ...MOCK_ADDITIONAL_INFO, }, }) ;(sendEventTelemetry as jest.Mock).mockRestore() diff --git a/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.tsx b/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.tsx index 0279755ec6..a0bb549912 100644 --- a/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.tsx +++ b/redisinsight/ui/src/pages/home/components/database-list-component/DatabasesListWrapper.tsx @@ -65,6 +65,7 @@ import { OAuthSocialSource, } from 'uiSrc/slices/interfaces' import { + getRedisInfoSummary, getRedisModulesSummary, sendEventTelemetry, TelemetryEvent, @@ -184,12 +185,13 @@ const DatabasesListWrapper = (props: Props) => { history.push(Pages.browser(id)) } - const handleCheckConnectToInstance = ( + const handleCheckConnectToInstance = async ( event: React.MouseEvent | React.KeyboardEvent, { id, provider, modules }: Instance, ) => { event.preventDefault() const modulesSummary = getRedisModulesSummary(modules) + const infoData = await getRedisInfoSummary(id) sendEventTelemetry({ event: TelemetryEvent.CONFIG_DATABASES_OPEN_DATABASE, eventData: { @@ -197,6 +199,7 @@ const DatabasesListWrapper = (props: Props) => { provider, source: 'db_list', ...modulesSummary, + ...infoData, }, }) dispatch( diff --git a/redisinsight/ui/src/services/database/instancesService.ts b/redisinsight/ui/src/services/database/instancesService.ts index 7afd2d7622..81b3b1d422 100644 --- a/redisinsight/ui/src/services/database/instancesService.ts +++ b/redisinsight/ui/src/services/database/instancesService.ts @@ -14,7 +14,6 @@ export async function getInstanceInfo(id: string) { const { data, status } = await apiService.get( `${endpoint}/${id}/info`, ) - return isStatusSuccessful(status) ? data : null } export async function getInstanceOverview(id: string) { diff --git a/redisinsight/ui/src/slices/tests/instances/instances.spec.ts b/redisinsight/ui/src/slices/tests/instances/instances.spec.ts index 93d9d86ccf..4671ef8e98 100644 --- a/redisinsight/ui/src/slices/tests/instances/instances.spec.ts +++ b/redisinsight/ui/src/slices/tests/instances/instances.spec.ts @@ -79,7 +79,6 @@ import { Instance, } from '../../interfaces' import { loadMastersSentinel } from '../../instances/sentinel' -import { fetchTags } from 'uiSrc/slices/instances/tags' jest.mock('uiSrc/services', () => ({ ...jest.requireActual('uiSrc/services'), @@ -96,6 +95,7 @@ beforeEach(() => { instances = [ { id: 'e37cc441-a4f2-402c-8bdb-fc2413cbbaff', + version: '6.2.6', host: 'localhost', port: 6379, name: 'localhost', @@ -124,6 +124,7 @@ beforeEach(() => { }, { id: 'b83a3932-e95f-4f09-9d8a-55079f400186', + version: '6.2.6', host: 'localhost', port: 5005, name: 'sentinel', From ba483e427c23cb7bad5cdbdaed6d0028bc4c82ab Mon Sep 17 00:00:00 2001 From: pd-redis Date: Mon, 5 May 2025 16:26:09 +0300 Subject: [PATCH 04/20] fix double onClick handling --- .../src/components/base/layout/list/Item.tsx | 1 - .../pages/rdi/instance/InstancePage.spec.tsx | 30 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/redisinsight/ui/src/components/base/layout/list/Item.tsx b/redisinsight/ui/src/components/base/layout/list/Item.tsx index 7715afa509..dff5a9d921 100644 --- a/redisinsight/ui/src/components/base/layout/list/Item.tsx +++ b/redisinsight/ui/src/components/base/layout/list/Item.tsx @@ -118,7 +118,6 @@ const Item = ({ $isActive={isActive} $isDisabled={isDisabled} $color={color} - onClick={onClick} className={cx(ListClassNames.listItem, className, { [ListClassNames.listItemActive]: isActive, [ListClassNames.listItemDisabled]: isDisabled, diff --git a/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx b/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx index 556323324f..ecefacff91 100644 --- a/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx +++ b/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx @@ -91,13 +91,13 @@ describe('InstancePage', () => { ;(appContextSelector as jest.Mock).mockReturnValue({ contextRdiInstanceId: '', }) - await act(() => { + await act(() => render( , - ) - }) + ), + ) const resetContextActions = [ resetKeys(), @@ -144,13 +144,13 @@ describe('InstancePage', () => { contextRdiInstanceId: 'prevId', }) - await act(() => { + await act(() => render( , - ) - }) + ), + ) const expectedActions = [ setConfigValidationErrors(['Error: unknown error']), @@ -183,15 +183,15 @@ describe('InstancePage', () => { .fn() .mockReturnValue({ pathname: Pages.rdiPipeline(RDI_INSTANCE_ID_MOCK) }) - await act(() => { + await act(() => render( , - ) - }) + ), + ) - expect(pushMock).toBeCalledWith( + expect(pushMock).toHaveBeenCalledWith( Pages.rdiPipelineManagement(RDI_INSTANCE_ID_MOCK), ) }) @@ -212,14 +212,16 @@ describe('InstancePage', () => { .fn() .mockReturnValue({ pathname: Pages.rdiPipeline(RDI_INSTANCE_ID_MOCK) }) - await act(() => { + await act(() => render( , - ) - }) + ), + ) - expect(pushMock).toBeCalledWith(Pages.rdiStatistics(RDI_INSTANCE_ID_MOCK)) + expect(pushMock).toHaveBeenCalledWith( + Pages.rdiStatistics(RDI_INSTANCE_ID_MOCK), + ) }) }) From 4aca70ad3cb119ad680c87b22eab49ae4e081bef Mon Sep 17 00:00:00 2001 From: Krum Tyukenov Date: Mon, 19 May 2025 10:22:18 +0300 Subject: [PATCH 05/20] RI-7129: fix Enterprise s3 upload path --- .github/workflows/aws-upload-enterprise.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aws-upload-enterprise.yml b/.github/workflows/aws-upload-enterprise.yml index b4bc7c51af..e5c6721111 100644 --- a/.github/workflows/aws-upload-enterprise.yml +++ b/.github/workflows/aws-upload-enterprise.yml @@ -64,7 +64,7 @@ jobs: - name: Upload vendor plugins to s3 bucket if: steps.download-vendor.outcome == 'success' run: | - aws s3 cp vendor/ s3://${SELECTED_AWS_BUCKET_NAME}/public/plugins/static/ --recursive + aws s3 cp vendor/ s3://${AWS_BUCKET_NAME_TEST}/public/plugins/static/ --recursive - name: Generate job summary run: | From 84c25c24edc5d6bc762e8da30a4665e464aa4ea6 Mon Sep 17 00:00:00 2001 From: Krum Tyukenov Date: Mon, 19 May 2025 10:44:55 +0300 Subject: [PATCH 06/20] RI-7129: upload Enterprise statics for test builds only --- .github/workflows/aws-upload-enterprise.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/aws-upload-enterprise.yml b/.github/workflows/aws-upload-enterprise.yml index e5c6721111..03f2d70561 100644 --- a/.github/workflows/aws-upload-enterprise.yml +++ b/.github/workflows/aws-upload-enterprise.yml @@ -62,7 +62,7 @@ jobs: path: vendor - name: Upload vendor plugins to s3 bucket - if: steps.download-vendor.outcome == 'success' + if: steps.download-vendor.outcome == 'success' && ${{ inputs.environment != 'production' }} run: | aws s3 cp vendor/ s3://${AWS_BUCKET_NAME_TEST}/public/plugins/static/ --recursive From 285c913a41ed1fafbd7914e49053256c9612508b Mon Sep 17 00:00:00 2001 From: Krum Tyukenov Date: Mon, 19 May 2025 11:04:11 +0300 Subject: [PATCH 07/20] RI-7129: remove vendor plugins for Enterprise builds --- .github/workflows/aws-upload-enterprise.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/aws-upload-enterprise.yml b/.github/workflows/aws-upload-enterprise.yml index 03f2d70561..18e3a4bd11 100644 --- a/.github/workflows/aws-upload-enterprise.yml +++ b/.github/workflows/aws-upload-enterprise.yml @@ -53,19 +53,6 @@ jobs: aws s3 cp release/ s3://${AWS_BUCKET_NAME_PROD}/releases/${APP_VERSION} --recursive aws s3 cp release/ s3://${AWS_BUCKET_NAME_PROD}/latest --recursive - - name: Download vendor plugins - uses: actions/download-artifact@v4 - id: download-vendor - continue-on-error: true - with: - name: 'vendor-plugins' - path: vendor - - - name: Upload vendor plugins to s3 bucket - if: steps.download-vendor.outcome == 'success' && ${{ inputs.environment != 'production' }} - run: | - aws s3 cp vendor/ s3://${AWS_BUCKET_NAME_TEST}/public/plugins/static/ --recursive - - name: Generate job summary run: | node ./.github/generate-build-summary.js From af05519e26b456fc11bf53083c8c868a1d5dd65e Mon Sep 17 00:00:00 2001 From: Kristiyan Ivanov Date: Mon, 19 May 2025 11:20:53 +0300 Subject: [PATCH 08/20] version bump --- .github/build/release-docker.sh | 2 +- redisinsight/api/config/default.ts | 2 +- redisinsight/api/config/swagger.ts | 2 +- redisinsight/api/package.json | 2 +- redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts | 2 +- redisinsight/package.json | 2 +- scripts/update-version.js | 3 +-- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/build/release-docker.sh b/.github/build/release-docker.sh index f4fc68f651..671ab90e0b 100755 --- a/.github/build/release-docker.sh +++ b/.github/build/release-docker.sh @@ -2,7 +2,7 @@ set -e HELP="Args: --v - Semver (2.66.0) +-v - Semver (2.70.0) -d - Build image repository (Ex: -d redisinsight) -r - Target repository (Ex: -r redis/redisinsight) " diff --git a/redisinsight/api/config/default.ts b/redisinsight/api/config/default.ts index 1fab943816..4a2f482a62 100644 --- a/redisinsight/api/config/default.ts +++ b/redisinsight/api/config/default.ts @@ -105,7 +105,7 @@ export default { : true, buildType: process.env.RI_BUILD_TYPE || 'DOCKER_ON_PREMISE', appType: process.env.RI_APP_TYPE, - appVersion: process.env.RI_APP_VERSION || '2.68.0', + appVersion: process.env.RI_APP_VERSION || '2.70.0', requestTimeout: parseInt(process.env.RI_REQUEST_TIMEOUT, 10) || 25000, excludeRoutes: [], excludeAuthRoutes: [], diff --git a/redisinsight/api/config/swagger.ts b/redisinsight/api/config/swagger.ts index 52e8f13b43..e5230f0d94 100644 --- a/redisinsight/api/config/swagger.ts +++ b/redisinsight/api/config/swagger.ts @@ -5,7 +5,7 @@ const SWAGGER_CONFIG: Omit = { info: { title: 'Redis Insight Backend API', description: 'Redis Insight Backend API', - version: '2.68.0', + version: '2.70.0', }, tags: [], }; diff --git a/redisinsight/api/package.json b/redisinsight/api/package.json index 167800402a..4e0d873ac5 100644 --- a/redisinsight/api/package.json +++ b/redisinsight/api/package.json @@ -1,6 +1,6 @@ { "name": "redisinsight-api", - "version": "2.68.0", + "version": "2.70.0", "description": "Redis Insight API", "private": true, "author": { diff --git a/redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts b/redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts index dd328ec878..ab0efc0ec0 100644 --- a/redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts +++ b/redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts @@ -7,7 +7,7 @@ const ICON_PATH = app.isPackaged : path.join(__dirname, '../resources', 'icon.png') const appVersionPrefix = config.isEnterprise ? 'Enterprise - ' : '' -const appVersion = app.getVersion() || '2.68.0' +const appVersion = app.getVersion() || '2.70.0' const appVersionSuffix = !config.isProduction ? `-dev-${process.getCreationTime()}` : '' diff --git a/redisinsight/package.json b/redisinsight/package.json index a14180d832..5a2902f2f5 100644 --- a/redisinsight/package.json +++ b/redisinsight/package.json @@ -3,7 +3,7 @@ "appName": "Redis Insight", "productName": "RedisInsight", "private": true, - "version": "2.68.0", + "version": "2.70.0", "description": "Redis Insight", "main": "./dist/main/main.js", "author": { diff --git a/scripts/update-version.js b/scripts/update-version.js index 11e69abca8..f9d2bb8614 100644 --- a/scripts/update-version.js +++ b/scripts/update-version.js @@ -51,8 +51,7 @@ const filesToUpdate = [ __dirname, '../redisinsight/desktop/src/lib/aboutPanel/aboutPanel.ts', ), - regex: - /applicationVersion:\s*`\${app\.getVersion\(\)\s*\|\|\s*'([^']+)'}\${/, + regex: /app\.getVersion\(\)\s*\|\|\s*'([^']+)'/, replacement: (match, p1) => match.replace(p1, newVersion), }, { From 21148bf77e7445f67ff10810ab8e9a05a6d9730e Mon Sep 17 00:00:00 2001 From: Krum Tyukenov Date: Tue, 20 May 2025 11:05:09 +0300 Subject: [PATCH 09/20] RI-7138: fix suggested tag values (#4565) --- .../TagSuggestions.spec.tsx | 13 +++++++++++++ .../database-manage-tags-modal/TagSuggestions.tsx | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.spec.tsx b/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.spec.tsx index 10d6c4777e..b838cd9d67 100644 --- a/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.spec.tsx +++ b/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.spec.tsx @@ -91,4 +91,17 @@ describe('TagSuggestions', () => { expect(tagElements.length).toBe(7) }) + + it('should display correct number of value suggestions when duplicated tag keys are present', () => { + mockSelector.mockReturnValue({ + data: presetTagSuggestions, + }) + + renderComponent({ + targetKey: 'environment', + }) + const tagElements = screen.getAllByRole('option') + + expect(tagElements.length).toBe(3) + }) }) diff --git a/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.tsx b/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.tsx index 86919e895b..2de19997f5 100644 --- a/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.tsx +++ b/redisinsight/ui/src/pages/home/components/database-manage-tags-modal/TagSuggestions.tsx @@ -22,7 +22,9 @@ export const TagSuggestions = ({ const { data: allTags } = useSelector(tagsSelector) const tagsSuggestions: EuiSelectableOption<{ value: string }>[] = useMemo(() => { - const options = uniqBy(presetTagSuggestions.concat(allTags), 'key') + const options = uniqBy(presetTagSuggestions.concat(allTags), (tag) => + targetKey ? tag.value : tag.key, + ) .filter(({ key, value }) => { if (targetKey !== undefined) { return ( From b298468859607b53aef8423e9dab6325d26c69c1 Mon Sep 17 00:00:00 2001 From: Pavel Angelov Date: Tue, 20 May 2025 13:20:24 +0300 Subject: [PATCH 10/20] RI-7139: Fix json editor styles (#4566) --- .../rejson-details/RejsonDetailsWrapper.tsx | 6 +++++- .../rejson-details/monaco-editor/MonacoEditor.tsx | 6 +++++- .../components/rejson-details/styles.module.scss | 14 +++++++++++--- .../browser/modules/key-details/styles.module.scss | 9 +++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/RejsonDetailsWrapper.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/RejsonDetailsWrapper.tsx index 5cde468055..4e478425f8 100644 --- a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/RejsonDetailsWrapper.tsx +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/RejsonDetailsWrapper.tsx @@ -123,11 +123,15 @@ const RejsonDetailsWrapper = (props: Props) => { const shouldShowTextEditor = !isUndefined(updatedData) && editorType === EditorType.Text + const keyDetailsBodyName = shouldShowDefaultEditor + ? 'key-details-body' + : 'key-details-body-monaco-editor' + return (
-
+
{loading && ( diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx index 9b3e9fa540..4bfcc097e6 100644 --- a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx @@ -39,7 +39,11 @@ const MonacoEditor = (props: BaseProps) => { } return ( -
+
Date: Tue, 20 May 2025 15:00:10 +0300 Subject: [PATCH 11/20] RI-7137: Support Big integers in Monaco editor (#4563) * add test --- .../monaco-editor/MonacoEditor.spec.tsx | 45 +++++++++++++++++++ .../monaco-editor/MonacoEditor.tsx | 8 +++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.spec.tsx diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.spec.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.spec.tsx new file mode 100644 index 0000000000..e6fe514993 --- /dev/null +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.spec.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import { configureStore } from '@reduxjs/toolkit' +import { Provider } from 'react-redux' + +import MonacoEditor from './MonacoEditor' + +const mockStore = configureStore({ + reducer: () => ({ + browser: { + rejson: {}, + }, + }), +}) + +const renderWithRedux = (ui: React.ReactNode) => + render({ui}) + +const commonProps = { + length: 1, + selectedKey: 'key' as any, + dataType: 'ReJSON-RL', + isDownloaded: true, + onJsonKeyExpandAndCollapse: () => {}, + expandedRows: new Set(['someKey']), +} + +jest.mock('uiSrc/components/monaco-editor/useMonacoValidation', () => ({ + __esModule: true, + default: () => ({ + isValid: true, + isValidating: false, + }), +})) + +it('should preserve large numbers in Monaco editor', async () => { + const bigNumber = '245343644508855571' + const data = { huge: BigInt(bigNumber) } + + renderWithRedux() + + const editor = await screen.findByTestId('json-data-editor') + + expect(editor.textContent).toContain(`${bigNumber}`) +}) diff --git a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx index 4bfcc097e6..e87157869c 100644 --- a/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx +++ b/redisinsight/ui/src/pages/browser/modules/key-details/components/rejson-details/monaco-editor/MonacoEditor.tsx @@ -2,6 +2,7 @@ import React, { useRef, useState } from 'react' import { useDispatch } from 'react-redux' import { EuiButton, EuiFlexItem } from '@elastic/eui' import { monaco } from 'react-monaco-editor' +import JSONbig from 'json-bigint' import { MonacoEditor as Editor, @@ -15,7 +16,12 @@ import styles from '../styles.module.scss' const ROOT_PATH = '$' -const jsonToReadableString = (data: any) => JSON.stringify(data, null, 2) +// We use `storeAsString: true` to ensure large numbers are serialized as strings. +// This avoids precision loss for values larger than Number.MAX_SAFE_INTEGER (2^53 - 1), +// which would otherwise be inaccurately represented in JavaScript. +// Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER +const jsonToReadableString = (data: any) => + JSONbig({ storeAsString: true }).stringify(data, null, 2) const MonacoEditor = (props: BaseProps) => { const { data, length, selectedKey } = props From e7a6eb56852a27b402245ab17d0a1a83cea05152 Mon Sep 17 00:00:00 2001 From: pd-redis Date: Thu, 22 May 2025 17:52:43 +0300 Subject: [PATCH 12/20] fix InstancePage.spec.tsx --- redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx b/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx index a28fddafc8..465a57e873 100644 --- a/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx +++ b/redisinsight/ui/src/pages/rdi/instance/InstancePage.spec.tsx @@ -92,7 +92,7 @@ describe('InstancePage', () => { contextRdiInstanceId: '', }) - await act(async () => { + await act(async () => render( @@ -151,7 +151,7 @@ describe('InstancePage', () => { }) // this MUST be awaited, in order for all effects to happen and all actions to be dispatched - await act(async () => { + await act(async () => render( From ab41382e6918c1cde878abf9000891996de5bc16 Mon Sep 17 00:00:00 2001 From: pd-redis Date: Fri, 23 May 2025 11:42:11 +0300 Subject: [PATCH 13/20] fix database-info.provider.spec.ts --- .../providers/database-info.provider.spec.ts | 17 +++++++++++++++ .../providers/database-info.provider.ts | 21 +++++++++++-------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/redisinsight/api/src/modules/database/providers/database-info.provider.spec.ts b/redisinsight/api/src/modules/database/providers/database-info.provider.spec.ts index 5c0a26f473..9c95970426 100644 --- a/redisinsight/api/src/modules/database/providers/database-info.provider.spec.ts +++ b/redisinsight/api/src/modules/database/providers/database-info.provider.spec.ts @@ -32,12 +32,21 @@ const mockRedisServerInfoDto = { tcp_port: '11113', uptime_in_seconds: '1000', }; +const mockRedisStatsDto = { + instantaneous_input_kbps: undefined, + instantaneous_ops_per_sec: undefined, + instantaneous_output_kbps: undefined, + maxmemory_policy: undefined, + numberOfKeysRange: '0 - 500 000', + uptime_in_days: undefined, +}; const mockRedisGeneralInfo: RedisDatabaseInfoResponse = { version: mockRedisServerInfoDto.redis_version, databases: 16, role: 'master', server: mockRedisServerInfoDto, + stats: mockRedisStatsDto, usedMemory: 1000000, totalKeys: 1, connectedClients: 1, @@ -441,6 +450,10 @@ describe('DatabaseInfoProvider', () => { expect(result).toEqual({ ...mockRedisGeneralInfo, + stats: { + ...mockRedisStatsDto, + numberOfKeysRange: undefined, + }, totalKeys: undefined, usedMemory: undefined, hitRatio: undefined, @@ -481,6 +494,10 @@ describe('DatabaseInfoProvider', () => { expect(result).toEqual({ ...mockRedisGeneralInfo, + stats: { + ...mockRedisStatsDto, + numberOfKeysRange: undefined, + }, server: { redis_mode: mockRedisServerInfoDto.redis_mode, redis_version: mockRedisGeneralInfo.version, diff --git a/redisinsight/api/src/modules/database/providers/database-info.provider.ts b/redisinsight/api/src/modules/database/providers/database-info.provider.ts index becb792212..ce573a9e36 100644 --- a/redisinsight/api/src/modules/database/providers/database-info.provider.ts +++ b/redisinsight/api/src/modules/database/providers/database-info.provider.ts @@ -168,7 +168,7 @@ export class DatabaseInfoProvider { return { version: serverInfo?.redis_version, databases, - role: get(replicationInfo, 'role') || undefined, + role: get(replicationInfo, 'role'), totalKeys, usedMemory: parseInt(get(memoryInfo, 'used_memory'), 10) || undefined, connectedClients: @@ -181,14 +181,17 @@ export class DatabaseInfoProvider { undefined, server: serverInfo, stats: { - instantaneous_ops_per_sec: - get(statsInfo, 'instantaneous_ops_per_sec') || undefined, - instantaneous_input_kbps: - get(statsInfo, 'instantaneous_input_kbps') || undefined, - instantaneous_output_kbps: - get(statsInfo, 'instantaneous_output_kbps') || undefined, - uptime_in_days: get(serverInfo, 'uptime_in_days') || undefined, - maxmemory_policy: get(memoryInfo, 'maxmemory_policy') || undefined, + instantaneous_ops_per_sec: get( + statsInfo, + 'instantaneous_ops_per_sec', + ), + instantaneous_input_kbps: get(statsInfo, 'instantaneous_input_kbps'), + instantaneous_output_kbps: get( + statsInfo, + 'instantaneous_output_kbps', + ), + uptime_in_days: get(serverInfo, 'uptime_in_days', undefined), + maxmemory_policy: get(memoryInfo, 'maxmemory_policy', undefined), numberOfKeysRange: getRangeForNumber( totalKeys, TOTAL_KEYS_BREAKPOINTS, From 17298ba714e4b8e0e21ba1c557ecfaeca85c8c8f Mon Sep 17 00:00:00 2001 From: pd-redis Date: Fri, 23 May 2025 11:49:16 +0300 Subject: [PATCH 14/20] added RedisDatabaseStatsDto --- .../modules/database/dto/redis-info.dto.ts | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/redisinsight/api/src/modules/database/dto/redis-info.dto.ts b/redisinsight/api/src/modules/database/dto/redis-info.dto.ts index f80683bdc9..ef55901b20 100644 --- a/redisinsight/api/src/modules/database/dto/redis-info.dto.ts +++ b/redisinsight/api/src/modules/database/dto/redis-info.dto.ts @@ -1,5 +1,39 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +class RedisDatabaseStatsDto { + @ApiProperty({ + type: String, + }) + instantaneous_input_kbps: string | undefined; + + @ApiProperty({ + type: String, + }) + instantaneous_ops_per_sec: string | undefined; + + @ApiProperty({ + type: String, + }) + instantaneous_output_kbps: string | undefined; + + @ApiProperty({ + type: Number, + }) + maxmemory_policy: string | undefined; + + @ApiProperty({ + description: 'Redis database mode', + type: String, + }) + numberOfKeysRange: string | undefined; + + @ApiProperty({ + description: 'Redis database role', + type: String, + }) + uptime_in_days: string | undefined; +} + export class RedisNodeInfoResponse { @ApiProperty({ description: 'Redis database version', @@ -24,9 +58,9 @@ export class RedisNodeInfoResponse { @ApiPropertyOptional({ description: 'Various Redis stats', - type: Object, + type: RedisDatabaseStatsDto, }) - stats?: any; + stats?: RedisDatabaseStatsDto; @ApiPropertyOptional({ description: 'The number of Redis databases', From 6257a352b3f8488ab41a525b1699ef1d2219550e Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 09:01:55 +0300 Subject: [PATCH 15/20] RI-7141 add db number to env --- redisinsight/api/src/__mocks__/database-discovery.ts | 4 +++- .../database-discovery/utils/pre-setup.discovery.util.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/redisinsight/api/src/__mocks__/database-discovery.ts b/redisinsight/api/src/__mocks__/database-discovery.ts index 56a021b712..c443b73e01 100644 --- a/redisinsight/api/src/__mocks__/database-discovery.ts +++ b/redisinsight/api/src/__mocks__/database-discovery.ts @@ -7,7 +7,7 @@ export const mockDefaultDatabaseFields = { modules: [], caCert: null, clientCert: null, - db: null, + db: 0, nameFromProvider: null, password: null, provider: null, @@ -24,6 +24,7 @@ export const mockDatabaseToImportFromEnvsInput = { host: 'localhost', port: 6379, name: 'local database', + db: 0, } as Partial; export const mockDatabaseToImportFromEnvsPrepared = { @@ -45,6 +46,7 @@ export const mockDatabaseToImportWithCertsFromEnvsInput = { key: 'User key', }, verifyServerCert: true, + db: 0, } as Partial; export const mockDatabaseToImportWithCertsFromEnvsPrepared = { diff --git a/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts b/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts index 18b4476034..41d56c8e50 100644 --- a/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts +++ b/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts @@ -126,6 +126,7 @@ export const prepareDatabaseFromEnvs = async ( id: id || '0', host: process.env[hostEnv], port: parseInt(process.env[`RI_REDIS_PORT${id}`], 10) || 6379, + db: parseInt(process.env[`RI_REDIS_DB${id}`], 10) || 0, name: process.env[`RI_REDIS_ALIAS${id}`], username: process.env[`RI_REDIS_USERNAME${id}`], password: process.env[`RI_REDIS_PASSWORD${id}`], From 3100e73c8cacf563712d9b416776c0a4975a6fcd Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 09:24:17 +0300 Subject: [PATCH 16/20] RI-7145 fix db id undefined state --- .../database-discovery/utils/pre-setup.discovery.util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts b/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts index 41d56c8e50..0da328d40b 100644 --- a/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts +++ b/redisinsight/api/src/modules/database-discovery/utils/pre-setup.discovery.util.ts @@ -1,3 +1,4 @@ +import { v4 as uuidv4 } from 'uuid'; import { pathExists, readFile } from 'fs-extra'; import { Database } from 'src/modules/database/models/database'; import { @@ -34,7 +35,7 @@ export const populateDefaultValues = ( database: Partial, ): Database => { const { - id, + id = uuidv4(), host, port = 6379, name = `${host}:${port}`, From 9abe1fc58eff18d4795eae8588e26c5a6aedef18 Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 10:03:48 +0300 Subject: [PATCH 17/20] RI-7145 fix /db/info ITests --- .../api/test/api/database/GET-databases-id-info.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/redisinsight/api/test/api/database/GET-databases-id-info.test.ts b/redisinsight/api/test/api/database/GET-databases-id-info.test.ts index b8972efd85..e9ced14a8f 100644 --- a/redisinsight/api/test/api/database/GET-databases-id-info.test.ts +++ b/redisinsight/api/test/api/database/GET-databases-id-info.test.ts @@ -38,6 +38,14 @@ const responseSchema = Joi.object() server: Joi.object().required(), }), ), + stats: Joi.object().keys({ + instantaneous_ops_per_sec: Joi.string(), + instantaneous_input_kbps: Joi.string(), + instantaneous_output_kbps: Joi.string(), + uptime_in_days: Joi.string(), + maxmemory_policy: Joi.string(), + numberOfKeysRange: Joi.string(), + }), }) .required() .strict(); From 820edf729d5c11e314582fcd9901a3ae9eebf152 Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 10:17:50 +0300 Subject: [PATCH 18/20] RI-7145 fix /db/info ITests for cluster --- redisinsight/api/test/api/.mocharc.yml | 2 +- .../api/test/api/database/GET-databases-id-info.test.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/redisinsight/api/test/api/.mocharc.yml b/redisinsight/api/test/api/.mocharc.yml index 2522bfc111..69c24d0ccb 100644 --- a/redisinsight/api/test/api/.mocharc.yml +++ b/redisinsight/api/test/api/.mocharc.yml @@ -1,5 +1,5 @@ spec: - - 'test/**/*.test.ts' + - 'test/api/database/**/*.test.ts' require: 'test/api/api.deps.init.ts' project: ./test/api/api.tsconfig.json retries: 2 diff --git a/redisinsight/api/test/api/database/GET-databases-id-info.test.ts b/redisinsight/api/test/api/database/GET-databases-id-info.test.ts index e9ced14a8f..9d8a9c4c9e 100644 --- a/redisinsight/api/test/api/database/GET-databases-id-info.test.ts +++ b/redisinsight/api/test/api/database/GET-databases-id-info.test.ts @@ -36,6 +36,14 @@ const responseSchema = Joi.object() hitRatio: Joi.number().required(), cashedScripts: Joi.number(), server: Joi.object().required(), + stats: Joi.object().keys({ + instantaneous_ops_per_sec: Joi.string(), + instantaneous_input_kbps: Joi.string(), + instantaneous_output_kbps: Joi.string(), + uptime_in_days: Joi.string(), + maxmemory_policy: Joi.string(), + numberOfKeysRange: Joi.string(), + }), }), ), stats: Joi.object().keys({ From 1690af062e02a231d477ac071e794a39f8d6f56c Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 10:21:43 +0300 Subject: [PATCH 19/20] RI-7145 fix /db/info ITests for cluster --- redisinsight/api/test/api/.mocharc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redisinsight/api/test/api/.mocharc.yml b/redisinsight/api/test/api/.mocharc.yml index 69c24d0ccb..2522bfc111 100644 --- a/redisinsight/api/test/api/.mocharc.yml +++ b/redisinsight/api/test/api/.mocharc.yml @@ -1,5 +1,5 @@ spec: - - 'test/api/database/**/*.test.ts' + - 'test/**/*.test.ts' require: 'test/api/api.deps.init.ts' project: ./test/api/api.tsconfig.json retries: 2 From 4aa7d21b2c920a214eb0592e63385b54ee48832c Mon Sep 17 00:00:00 2001 From: ArtemHoruzhenko Date: Tue, 27 May 2025 16:58:15 +0300 Subject: [PATCH 20/20] RI-000 fix api url for web builds --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7310ef097..4f5f1a7ef4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dev:electron:api": "cross-env RI_APP_PORT=5540 RI_APP_TYPE=ELECTRON NODE_ENV=development USE_TCP_CLOUD_AUTH=true yarn --cwd redisinsight/api start:dev", "dev:electron": "cross-env RI_APP_TYPE=ELECTRON RI_AUTO_BOOTSTRAP=false NODE_ENV=development USE_TCP_CLOUD_AUTH=true yarn --cwd redisinsight/desktop dev", "dev:desktop": "concurrently \"yarn dev:electron:api\" \"yarn dev:electron:ui\" \"yarn dev:electron\"", - "build:ui": "cross-env NODE_ENV=production RI_APP_TYPE=WEB yarn --cwd redisinsight/ui build", + "build:ui": "cross-env NODE_ENV=production RI_APP_TYPE=web yarn --cwd redisinsight/ui build", "build:renderer": "cross-env NODE_ENV=production RI_APP_TYPE=ELECTRON yarn --cwd redisinsight/ui build --emptyOutDir && copyfiles ./redisinsight/desktop/splash.html ./redisinsight/dist/renderer -f", "stats:ui": "yarn --cwd redisinsight/ui stats", "build": "cross-env NODE_ENV=development concurrently \"yarn build:main\" \"yarn build:renderer\"",