Skip to content

Commit b38a31a

Browse files
authored
Merge pull request #1285 from RedisInsight/bugfix/RI-3612_infinity_score_zset
#RI-3612-zset infinity score
2 parents 4fad02f + ec9d6a5 commit b38a31a

File tree

16 files changed

+103
-38
lines changed

16 files changed

+103
-38
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './redis-string';
2+
export * from './zset-score';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './zset-score.decorator';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {
2+
registerDecorator,
3+
ValidationOptions,
4+
} from 'class-validator';
5+
import { ZSetScoreValidator } from 'src/common/validators';
6+
7+
export function isZSetScore(validationOptions?: ValidationOptions) {
8+
return (object: any, propertyName: string) => {
9+
registerDecorator({
10+
name: 'isZSetScore',
11+
target: object.constructor,
12+
propertyName,
13+
options: validationOptions,
14+
validator: ZSetScoreValidator,
15+
});
16+
};
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './redis-string.validator';
2+
export * from './zset-score.validator';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { isNumber } from 'lodash';
2+
import {
3+
ValidationArguments,
4+
ValidatorConstraint,
5+
ValidatorConstraintInterface,
6+
} from 'class-validator';
7+
8+
@ValidatorConstraint({ name: 'RedisStringValidator', async: true })
9+
export class ZSetScoreValidator implements ValidatorConstraintInterface {
10+
async validate(value: any) {
11+
return value === 'inf' || value === '-inf' || isNumber(value);
12+
}
13+
14+
defaultMessage(args: ValidationArguments) {
15+
return `${args.property || 'field'} must be a string or a number`;
16+
}
17+
}

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: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
IsNotEmptyObject,
1010
IsNumber, IsString,
1111
Min,
12-
ValidateNested
12+
ValidateNested,
1313
} from 'class-validator';
1414
import { Type } from 'class-transformer';
1515
import { SortOrder } from 'src/constants';
@@ -18,7 +18,7 @@ import {
1818
DeleteMembersFromSetResponse,
1919
} from 'src/modules/browser/dto/set.dto';
2020
import { RedisString } from 'src/common/constants';
21-
import { IsRedisString, RedisStringType } from 'src/common/decorators';
21+
import { IsRedisString, RedisStringType, isZSetScore } from 'src/common/decorators';
2222
import {
2323
KeyDto, KeyResponse, KeyWithExpireDto, ScanDataTypeDto,
2424
} from './keys.dto';
@@ -77,13 +77,12 @@ export class ZSetMemberDto {
7777

7878
@ApiProperty({
7979
description: 'Member score value.',
80-
type: Number,
80+
type: Number || String,
8181
default: 1,
8282
})
8383
@IsDefined()
84-
@IsNumber({ maxDecimalPlaces: 15 })
85-
@Type(() => Number)
86-
score: number;
84+
@isZSetScore()
85+
score: number | 'inf' | '-inf';
8786
}
8887

8988
export class AddMembersToZSetDto extends KeyDto {

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: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Logger,
66
NotFoundException,
77
} from '@nestjs/common';
8-
import { isNull } from 'lodash';
8+
import { isNull, isNaN } from 'lodash';
99
import * as isGlob from 'is-glob';
1010
import config from 'src/utils/config';
1111
import { catchAclError, catchTransactionError, unescapeGlob } from 'src/utils';
@@ -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);
@@ -375,7 +377,6 @@ export class ZSetBusinessService {
375377
nextCursor: null,
376378
members: [],
377379
};
378-
379380
while (result.nextCursor !== 0 && result.members.length < count) {
380381
const scanResult = await this.browserTool.execCommand(
381382
clientOptions,
@@ -406,11 +407,13 @@ export class ZSetBusinessService {
406407
const result: ZSetMemberDto[] = [];
407408
while (reply.length) {
408409
const member = reply.splice(0, 2);
410+
const score = isNaN(parseFloat(member[1])) ? String(member[1]) : parseFloat(member[1]);
409411
result.push(plainToClass(ZSetMemberDto, {
410412
name: member[0],
411-
score: parseFloat(member[1]),
413+
score,
412414
}));
413415
}
416+
414417
return result;
415418
}
416419

redisinsight/api/test/api/z-set/PATCH-instance-id-zSet.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ const dataSchema = Joi.object({
2222
member: Joi.object().keys({
2323
name: Joi.string().required(),
2424
// todo: allow(true) - is incorrect but will be transformed to number by BE. Investigate/fix it
25-
score: Joi.number().required().allow(true),
25+
score: Joi.number().required().allow('inf', '-inf').label('.score'),
2626
}).messages({
27-
'number.base': '{#lavel} must be a number',
27+
'number.base': '{#lavel} must be a string or a number',
2828
}),
2929
}).strict();
3030

0 commit comments

Comments
 (0)