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
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {

import { ConnectionType, InstanceType, } from 'uiSrc/slices/interfaces'
import { getRedisModulesSummary, sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { getDiffKeysOfObjectValues, checkRediStackModules } from 'uiSrc/utils'
import { getDiffKeysOfObjectValues, isRediStack } from 'uiSrc/utils'
import { BuildType } from 'uiSrc/constants/env'

import {
Expand Down Expand Up @@ -129,6 +129,7 @@ const AddStandaloneForm = (props: Props) => {
ssh,
sshPassType = SshPassType.Password,
sshOptions,
version,
},
initialValues: initialValuesProp,
width,
Expand Down Expand Up @@ -510,7 +511,7 @@ const AddStandaloneForm = (props: Props) => {
{isEditMode && name && (
<div className="fluid" style={{ marginBottom: 15 }}>
<DatabaseAlias
isRediStack={checkRediStackModules(modules)}
isRediStack={isRediStack(modules, version)}
isCloneMode={isCloneMode}
alias={name}
database={db}
Expand Down
1 change: 1 addition & 0 deletions redisinsight/ui/src/slices/interfaces/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Instance extends DatabaseInstanceResponse {
isDeleting?: boolean
sentinelMaster?: SentinelMaster
modules: AdditionalRedisModule[]
version: Nullable<string>
isRediStack?: boolean
visible?: boolean
loading?: boolean
Expand Down
90 changes: 59 additions & 31 deletions redisinsight/ui/src/utils/redistack.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,75 @@
import { isArray, map } from 'lodash'
import { isArray, map, concat } from 'lodash'
import { Instance, RedisDefaultModules } from 'uiSrc/slices/interfaces'
import { isVersionHigherOrEquals, Nullable } from 'uiSrc/utils'

export const REDISTACK_PORT = 6379
export const REDISTACK_MODULES: Array<string | Array<string>> = [
const REDISTACK_LOW_VERSION = '6.2.6'
const REDISTACK_HIGH_VERSION = '7.2'

const REDISTACK_LOW_VERSION_REQUIRE_MODULES: Array<string | Array<string>> = [
RedisDefaultModules.ReJSON,
RedisDefaultModules.Bloom,
RedisDefaultModules.Graph,
[RedisDefaultModules.Search, RedisDefaultModules.SearchLight],
RedisDefaultModules.TimeSeries,
]

const checkRediStackModules = (modules: any[]) => {
if (!modules?.length || modules.length !== REDISTACK_MODULES.length) return false
const REDISTACK_HIGH_VERSION_REQUIRE_MODULES: Array<string | Array<string>> = [
RedisDefaultModules.ReJSON,
RedisDefaultModules.Bloom,
[RedisDefaultModules.Search, RedisDefaultModules.SearchLight],
RedisDefaultModules.TimeSeries,
]

const REDISTACK_HIGH_VERSION_OPTIONAL_MODULES: Array<string> = [
RedisDefaultModules.Gears,
]

const checkRediStackModules = (modules: any[], required: any[], optional: any[] = []) => {
if (!modules?.length) return false

return map(modules, 'name')
.sort()
.every((m, index) => (isArray(REDISTACK_MODULES[index])
? (REDISTACK_MODULES[index] as Array<string>).some((rm) => rm === m)
: REDISTACK_MODULES[index] === m))
if (modules.length === required.length) {
return map(modules, 'name')
.sort()
.every((m, index) => (isArray(required[index])
? (required[index] as Array<string>).some((rm) => rm === m)
: required[index] === m))
}

if (modules.length === (required.length + optional.length)) {
const rediStackModules = concat(required, optional).sort()
return map(modules, 'name')
.sort()
.every((m, index) => (isArray(rediStackModules[index])
? (rediStackModules[index] as Array<string>).some((rm) => rm === m)
: rediStackModules[index] === m))
}

return false
}

const checkRediStack = (instances: Instance[]): Instance[] => {
let isRediStackCheck = false

let newInstances = instances.map((instance) => {
const isRediStack = +instance.port === REDISTACK_PORT && checkRediStackModules(instance.modules)

isRediStackCheck = isRediStackCheck || isRediStack
return {
...instance,
isRediStack
}
})

// if no any database with redistack on port 6379 - mark others as redistack (with modules check)
if (!isRediStackCheck) {
newInstances = newInstances.map((instance) => ({
...instance,
isRediStack: checkRediStackModules(instance.modules)
}))
const isRediStack = (modules: any[], version?: Nullable<string>): boolean => {
if (!version) {
return checkRediStackModules(modules, REDISTACK_LOW_VERSION_REQUIRE_MODULES)
}

return newInstances
if (isVersionHigherOrEquals(version, REDISTACK_HIGH_VERSION)) {
return checkRediStackModules(
modules,
REDISTACK_HIGH_VERSION_REQUIRE_MODULES,
REDISTACK_HIGH_VERSION_OPTIONAL_MODULES
)
}

if (isVersionHigherOrEquals(version, REDISTACK_LOW_VERSION)) {
return checkRediStackModules(modules, REDISTACK_LOW_VERSION_REQUIRE_MODULES)
}

return false
}

export { checkRediStack, checkRediStackModules }
const checkRediStack = (instances: Instance[]): Instance[] => (instances.map((instance) => ({
...instance,
isRediStack: isRediStack(instance.modules)
})))

export { checkRediStack, isRediStack }
92 changes: 30 additions & 62 deletions redisinsight/ui/src/utils/tests/redistack.spec.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,37 @@
/* eslint-disable max-len */
import { checkRediStack, REDISTACK_PORT } from 'uiSrc/utils'
import { RedisDefaultModules } from 'uiSrc/slices/interfaces'
import { isRediStack } from 'uiSrc/utils'

const unmapWithName = (arr: any[]) => arr.map((item) => ({ name: item }))

const REDISTACK_MODULE_DEFAULT = unmapWithName([
RedisDefaultModules.ReJSON,
RedisDefaultModules.Graph,
RedisDefaultModules.TimeSeries,
RedisDefaultModules.Search,
RedisDefaultModules.Bloom,
].sort())

const getOutputCheckRediStackTests: any[] = [
[
[{ port: REDISTACK_PORT, modules: REDISTACK_MODULE_DEFAULT }, { port: 12000, modules: REDISTACK_MODULE_DEFAULT }],
[{ port: REDISTACK_PORT, modules: REDISTACK_MODULE_DEFAULT, isRediStack: true }, { port: 12000, modules: REDISTACK_MODULE_DEFAULT, isRediStack: false }]
],
[
[{ port: REDISTACK_PORT, modules: REDISTACK_MODULE_DEFAULT }],
[{ port: REDISTACK_PORT, modules: REDISTACK_MODULE_DEFAULT, isRediStack: true }]
],
[
[{ port: REDISTACK_PORT, modules: unmapWithName(['']) }],
[{ port: REDISTACK_PORT, modules: unmapWithName(['']), isRediStack: false }]
],
[
[{ port: REDISTACK_PORT, modules: unmapWithName(['search']) }],
[{ port: REDISTACK_PORT, modules: unmapWithName(['search']), isRediStack: false }]
],
[
[{ port: REDISTACK_PORT, modules: unmapWithName(['bf', 'search', 'timeseries']) }],
[{ port: REDISTACK_PORT, modules: unmapWithName(['bf', 'search', 'timeseries']), isRediStack: false }]
],
[
[{ port: 12000, modules: REDISTACK_MODULE_DEFAULT }],
[{ port: 12000, modules: REDISTACK_MODULE_DEFAULT, isRediStack: true }]
],
[
[{ port: 12000, modules: unmapWithName(['search']) }],
[{ port: 12000, modules: unmapWithName(['search']), isRediStack: false }]
],
// check searchlight - should be also marked as RediStack
[
[{ port: 12000, modules: unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']) }],
[{ port: 12000, modules: unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']), isRediStack: true }]
],
[
[{ port: 12000, modules: [] }],
[{ port: 12000, modules: [], isRediStack: false }]
],
[
[{ port: 12000, modules: unmapWithName(['ReJSON']) }],
[{ port: 12000, modules: unmapWithName(['ReJSON']), isRediStack: false }]
],
[
[{ port: 12000, modules: unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph', 'custom']) }],
[{ port: 12000, modules: unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph', 'custom']), isRediStack: false }]
],
const isRediStackTests = [
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph', 'custom']), '6.2.6'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search', 'graph', 'custom']), '6.2.6'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']), '6.2.6'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph'])], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']), null], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search']), null], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'rg', 'search'])], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search', 'graph']), '6.2.6'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']), '6.2.5'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search', 'graph']), '6.2.5'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight', 'graph']), '7.2'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search', 'graph']), '7.2'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'rg', 'searchlight']), '7.2'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'rg', 'search']), '7.2'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'searchlight']), '7.2'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search']), '7.2'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search']), '7.2'], expected: true },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'search', 'custom']), '7.2'], expected: false },
{ input: [unmapWithName(['bf', 'timeseries', 'ReJSON', 'seasearchlightrch', 'custom']), '7.2'], expected: false },
]

describe('checkRediStack', () => {
it.each(getOutputCheckRediStackTests)('for input: %s (reply), should be output: %s',
(reply, expected) => {
const result = checkRediStack(reply)
expect(result).toStrictEqual(expected)
})
describe('isRediStack', () => {
test.each(isRediStackTests)(
'%j',
({ input, expected }) => {
// @ts-ignore
const result = isRediStack(...input)
expect(result).toEqual(expected)
}
)
})