Skip to content

Commit db0b42c

Browse files
#RI-3612-add inifinity score in zset
1 parent b8e47ff commit db0b42c

File tree

6 files changed

+70
-24
lines changed

6 files changed

+70
-24
lines changed

redisinsight/api/src/modules/browser/__mocks__/z-set.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,21 @@ import { SortOrder } from 'src/constants';
88

99
export const mockZSetMemberDto: ZSetMemberDto = {
1010
name: Buffer.from('member1'),
11-
score: 0,
11+
score: '-inf',
1212
};
1313
export const mockZSetMemberDto2: ZSetMemberDto = {
1414
name: Buffer.from('member2'),
15+
score: 0,
16+
};
17+
export const mockZSetMemberDto3: ZSetMemberDto = {
18+
name: Buffer.from('member3'),
1519
score: 2,
1620
};
21+
22+
export const mockZSetMemberDto4: ZSetMemberDto = {
23+
name: Buffer.from('member4'),
24+
score: 'inf',
25+
};
1726
export const mockGetMembersDto: GetZSetMembersDto = {
1827
keyName: Buffer.from('zSet'),
1928
offset: 0,
@@ -28,7 +37,7 @@ export const mockSearchMembersDto: SearchZSetMembersDto = {
2837
};
2938
export const mockAddMembersDto: AddMembersToZSetDto = {
3039
keyName: mockGetMembersDto.keyName,
31-
members: [mockZSetMemberDto, mockZSetMemberDto2],
40+
members: [mockZSetMemberDto, mockZSetMemberDto2, mockZSetMemberDto3, mockZSetMemberDto4],
3241
};
3342
export const mockUpdateMemberDto: UpdateMemberInZSetDto = {
3443
keyName: mockGetMembersDto.keyName,
@@ -39,10 +48,14 @@ export const mockMembersForZAddCommand = [
3948
mockZSetMemberDto.name,
4049
mockZSetMemberDto2.score,
4150
mockZSetMemberDto2.name,
51+
mockZSetMemberDto3.score,
52+
mockZSetMemberDto3.name,
53+
mockZSetMemberDto4.score,
54+
mockZSetMemberDto4.name,
4255
];
4356
export const mockDeleteMembersDto: DeleteMembersFromZSetDto = {
4457
keyName: mockAddMembersDto.keyName,
45-
members: [mockZSetMemberDto.name, mockZSetMemberDto2.name],
58+
members: [mockZSetMemberDto.name, mockZSetMemberDto2.name, mockZSetMemberDto3.name, mockZSetMemberDto4.name],
4659
};
4760
export const getZSetMembersInAscResponse = {
4861
keyName: mockGetMembersDto.keyName,

redisinsight/api/src/modules/browser/dto/z-set.dto.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ValidateNested
1313
} from 'class-validator';
1414
import { Type } from 'class-transformer';
15+
import { isNumber } from 'lodash';
1516
import { SortOrder } from 'src/constants';
1617
import {
1718
DeleteMembersFromSetDto,
@@ -65,6 +66,24 @@ export class GetZSetMembersDto extends KeyDto {
6566
sortOrder: SortOrder;
6667
}
6768

69+
export class ScoreNumberValidation {
70+
@ApiProperty({
71+
description: 'Infinity score values',
72+
type: String,
73+
})
74+
@IsString()
75+
score: string;
76+
}
77+
78+
export class ScoreStringValidation {
79+
@ApiProperty({
80+
description: 'Common score value',
81+
type: Number,
82+
})
83+
@IsNumber({ maxDecimalPlaces: 15 })
84+
score: number;
85+
}
86+
6887
export class ZSetMemberDto {
6988
@ApiProperty({
7089
type: String,
@@ -77,12 +96,11 @@ export class ZSetMemberDto {
7796

7897
@ApiProperty({
7998
description: 'Member score value.',
80-
type: Number,
99+
type: Number || String,
81100
default: 1,
82101
})
83102
@IsDefined()
84-
@IsNumber({ maxDecimalPlaces: 15 })
85-
// @Type(() => Number)
103+
@Type((val) => isNumber(val) ? ScoreNumberValidation : ScoreStringValidation)
86104
score: number | string;
87105
}
88106

redisinsight/api/src/modules/browser/services/z-set-business/z-set-business.service.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ describe('ZSetBusinessService', () => {
184184
BrowserToolZSetCommands.ZRange,
185185
expect.anything(),
186186
)
187-
.mockResolvedValue(['member1', '0', 'member2', '2']);
187+
.mockResolvedValue(['member1', '-inf', 'member2', '0', 'member3', '2', 'member4', 'inf']);
188188

189189
const result = await service.getMembers(
190190
mockClientOptions,
@@ -199,7 +199,7 @@ describe('ZSetBusinessService', () => {
199199
BrowserToolZSetCommands.ZRevRange,
200200
expect.anything(),
201201
)
202-
.mockResolvedValue(['member2', '2', 'member1', '0']);
202+
.mockResolvedValue(['member4', 'inf', 'member3', '2', 'member2', '0', 'member1', '-inf']);
203203

204204
const result = await service.getMembers(mockClientOptions, {
205205
...mockGetMembersDto,
@@ -485,7 +485,7 @@ describe('ZSetBusinessService', () => {
485485
BrowserToolZSetCommands.ZScan,
486486
expect.anything(),
487487
)
488-
.mockResolvedValue([0, ['member1', '0', 'member2', '2']]);
488+
.mockResolvedValue([0, ['member1', '-inf', 'member2', '0', 'member3', '2', 'member4', 'inf']]);
489489

490490
const result = await service.searchMembers(
491491
mockClientOptions,

redisinsight/api/src/modules/browser/services/z-set-business/z-set-business.service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,10 @@ export class ZSetBusinessService {
277277
BrowserToolZSetCommands.ZScore,
278278
[keyName, member],
279279
);
280+
const formattedScore = isNaN(parseFloat(score)) ? String(score) : parseFloat(score);
281+
280282
if (!isNull(score)) {
281-
result.members.push(plainToClass(ZSetMemberDto, { name: member, score }));
283+
result.members.push(plainToClass(ZSetMemberDto, { name: member, score: formattedScore }));
282284
}
283285
} else {
284286
const scanResult = await this.scanZSet(clientOptions, dto);
@@ -405,7 +407,7 @@ export class ZSetBusinessService {
405407
const result: ZSetMemberDto[] = [];
406408
while (reply.length) {
407409
const member = reply.splice(0, 2);
408-
const score = isNaN(parseFloat(member[1])) ? member[1] : parseFloat(member[1]);
410+
const score = isNaN(parseFloat(member[1])) ? String(member[1]) : parseFloat(member[1]);
409411
result.push(plainToClass(ZSetMemberDto, {
410412
name: member[0],
411413
score,

redisinsight/ui/src/pages/browser/components/zset-details/ZSetDetails.spec.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ jest.mock('uiSrc/slices/browser/zset', () => {
1212
setZsetInitialState: jest.fn,
1313
zsetDataSelector: jest.fn().mockReturnValue({
1414
...defaultState,
15-
total: 3,
15+
total: 4,
1616
key: 'z',
1717
keyName: 'z',
1818
members: [
1919
{ name: { type: 'Buffer', data: [49] }, score: 1 },
2020
{ name: { type: 'Buffer', data: [50] }, score: 2 },
2121
{ name: { type: 'Buffer', data: [51] }, score: 3 },
22+
{ name: { type: 'Buffer', data: [52] }, score: 'inf' },
2223
],
2324
}),
2425
updateZsetScoreStateSelector: jest.fn().mockReturnValue(defaultState.updateScore),
@@ -52,6 +53,16 @@ describe('ZSetDetails', () => {
5253
expect(screen.getByTestId(/zset-edit-button-1/)).toBeInTheDocument()
5354
})
5455

56+
it('should render disabled edit button', () => {
57+
render(<ZSetDetails {...instance(mockedProps)} />)
58+
expect(screen.getByTestId(/zset-edit-button-4/)).toBeDisabled()
59+
})
60+
61+
it('should render enabled edit button', () => {
62+
render(<ZSetDetails {...instance(mockedProps)} />)
63+
expect(screen.getByTestId(/zset-edit-button-3/)).not.toBeDisabled()
64+
})
65+
5566
it('should render editor after click edit button and able to change value', () => {
5667
render(<ZSetDetails {...instance(mockedProps)} />)
5768
fireEvent.click(screen.getAllByTestId(/zset-edit-button/)[0])

redisinsight/ui/src/pages/browser/components/zset-details/ZSetDetails.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useCallback, useEffect, useRef, useState } from 'react'
22
import { useDispatch, useSelector } from 'react-redux'
3-
import { toNumber } from 'lodash'
3+
import { toNumber, isNumber } from 'lodash'
44
import cx from 'classnames'
55
import { EuiButtonIcon, EuiProgress, EuiText, EuiToolTip } from '@elastic/eui'
66
import { CellMeasurerCache } from 'react-virtualized'
@@ -39,8 +39,8 @@ import InlineItemEditor from 'uiSrc/components/inline-item-editor/InlineItemEdit
3939
import { IColumnSearchState, ITableColumn } from 'uiSrc/components/virtual-table/interfaces'
4040
import { StopPropagation } from 'uiSrc/components/virtual-table'
4141
import { getColumnWidth } from 'uiSrc/components/virtual-grid'
42-
import { AddMembersToZSetDto, SearchZSetMembersResponse } from 'apiSrc/modules/browser/dto'
4342
import { stringToBuffer } from 'uiSrc/utils/formatters/bufferFormatters'
43+
import { AddMembersToZSetDto, SearchZSetMembersResponse } from 'apiSrc/modules/browser/dto'
4444
import PopoverDelete from '../popover-delete/PopoverDelete'
4545

4646
import styles from './styles.module.scss'
@@ -326,20 +326,22 @@ const ZSetDetails = (props: Props) => {
326326
minWidth: 100,
327327
maxWidth: 100,
328328
absoluteWidth: 100,
329-
render: function Actions(_act: any, { name: nameItem }: IZsetMember) {
329+
render: function Actions(_act: any, { name: nameItem, score }: IZsetMember) {
330330
const name = bufferToString(nameItem, viewFormat)
331331
return (
332332
<StopPropagation>
333333
<div className="value-table-actions">
334-
<EuiButtonIcon
335-
iconType="pencil"
336-
aria-label="Edit field"
337-
className="editFieldBtn"
338-
color="primary"
339-
disabled={updateLoading}
340-
onClick={() => handleEditMember(nameItem, true)}
341-
data-testid={`zset-edit-button-${name}`}
342-
/>
334+
<EuiToolTip content={!isNumber(score) ? 'Use CLI or Workbench to edit the score' : null}>
335+
<EuiButtonIcon
336+
iconType="pencil"
337+
aria-label="Edit field"
338+
className="editFieldBtn"
339+
color="primary"
340+
disabled={updateLoading || !isNumber(score)}
341+
onClick={() => handleEditMember(nameItem, true)}
342+
data-testid={`zset-edit-button-${name}`}
343+
/>
344+
</EuiToolTip>
343345
<PopoverDelete
344346
header={createDeleteFieldHeader(nameItem)}
345347
text={createDeleteFieldMessage(key ?? '')}

0 commit comments

Comments
 (0)