Skip to content

Commit e836c53

Browse files
authored
Merge pull request #61 from GeneralMagicio/add-option-checking-to-setVote
fix setVote issues
2 parents 37dee98 + f69fddb commit e836c53

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

src/user/user.dto.ts

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,132 @@
11
import {
22
IsNotEmpty,
33
IsNumber,
4-
IsObject,
54
IsOptional,
65
IsString,
6+
IsEnum,
7+
IsDate,
8+
Validate,
9+
ValidatorConstraint,
10+
ValidatorConstraintInterface,
711
} from 'class-validator';
812
import { ActionType } from '@prisma/client';
913

1014
export class GetUserDataDto {
15+
@IsString()
16+
@IsNotEmpty()
1117
worldID: string;
1218
}
1319

1420
export class UserDataResponseDto {
21+
@IsNumber()
22+
@IsNotEmpty()
1523
pollsCreated: number;
24+
25+
@IsNumber()
26+
@IsNotEmpty()
1627
pollsParticipated: number;
28+
29+
@IsString()
30+
@IsNotEmpty()
1731
worldID: string;
32+
33+
@IsString()
34+
@IsOptional()
1835
worldProfilePic?: string | null;
1936
}
2037

2138
export class GetUserActivitiesDto {
39+
@IsString()
40+
@IsNotEmpty()
2241
worldID: string;
42+
43+
@IsEnum(['active', 'inactive', 'created', 'participated'])
44+
@IsOptional()
2345
filter?: 'active' | 'inactive' | 'created' | 'participated';
2446
}
2547

2648
export class UserActionDto {
49+
@IsNumber()
50+
@IsNotEmpty()
2751
id: number;
52+
53+
@IsEnum([ActionType.CREATED, ActionType.VOTED])
54+
@IsNotEmpty()
2855
type: ActionType;
56+
57+
@IsNumber()
58+
@IsNotEmpty()
2959
pollId: number;
60+
61+
@IsString()
62+
@IsNotEmpty()
3063
pollTitle: string;
64+
65+
@IsString()
66+
@IsNotEmpty()
3167
pollDescription: string;
68+
69+
@IsDate()
70+
@IsNotEmpty()
3271
endDate: Date;
72+
73+
@IsNumber()
74+
@IsNotEmpty()
3375
votersParticipated: number;
76+
77+
@IsString()
78+
@IsNotEmpty()
3479
authorWorldId: string;
80+
81+
@IsDate()
82+
@IsNotEmpty()
3583
createdAt: Date;
3684
}
3785

3886
export class UserActivitiesResponseDto {
87+
@IsNotEmpty()
3988
userActions: UserActionDto[];
4089
}
4190

4291
export class GetUserVotesDto {
92+
@IsNumber()
93+
@IsNotEmpty()
4394
pollId: number;
95+
96+
@IsString()
97+
@IsNotEmpty()
4498
worldID: string;
4599
}
46100

101+
@ValidatorConstraint({ name: 'IsRecordStringNumber', async: false })
102+
export class IsRecordStringNumberConstraint
103+
implements ValidatorConstraintInterface
104+
{
105+
validate(value: any): boolean {
106+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
107+
return false;
108+
}
109+
return Object.entries(value).every(
110+
([key, val]) => typeof key === 'string' && typeof val === 'number',
111+
);
112+
}
113+
114+
defaultMessage(): string {
115+
return 'weightDistribution must be a record with string keys and number values';
116+
}
117+
}
118+
47119
export class UserVotesResponseDto {
120+
@IsNotEmpty()
121+
@IsString({ each: true })
48122
options: string[];
123+
124+
@IsNumber()
125+
@IsNotEmpty()
49126
votingPower: number;
127+
128+
@Validate(IsRecordStringNumberConstraint)
129+
@IsOptional()
50130
weightDistribution?: Record<string, number>;
51131
}
52132

@@ -60,22 +140,37 @@ export class SetVoteDto {
60140
worldID: string;
61141

62142
@IsNotEmpty()
63-
@IsObject()
143+
@Validate(IsRecordStringNumberConstraint)
64144
weightDistribution: Record<string, number>;
65145
}
66146

67147
export class SetVoteResponseDto {
148+
@IsNotEmpty()
149+
@IsString()
68150
voteID: string;
151+
152+
@IsNotEmpty()
153+
@IsNumber()
69154
actionId: number;
70155
}
71156

72157
export class EditVoteDto {
158+
@IsNotEmpty()
159+
@IsNumber()
73160
userId: number;
161+
162+
@IsNotEmpty()
163+
@IsString()
74164
voteID: string;
165+
166+
@IsNotEmpty()
167+
@Validate(IsRecordStringNumberConstraint)
75168
weightDistribution: Record<string, number>;
76169
}
77170

78171
export class EditVoteResponseDto {
172+
@IsNotEmpty()
173+
@IsNumber()
79174
actionId: number;
80175
}
81176

@@ -94,5 +189,7 @@ export class CreateUserDto {
94189
}
95190

96191
export class CreateUserResponseDto {
192+
@IsNotEmpty()
193+
@IsNumber()
97194
userId: number;
98195
}

src/user/user.service.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,24 @@ export class UserService {
176176
if (!poll || poll.endDate < new Date()) {
177177
throw new Error('Poll is not active or does not exist');
178178
}
179+
const dtoWeightKeys = Object.keys(dto.weightDistribution);
180+
const areWeightKeysMatching =
181+
dtoWeightKeys.length === poll.options.length &&
182+
dtoWeightKeys.every((key) => poll.options.includes(key));
183+
if (!areWeightKeysMatching) {
184+
throw new Error(
185+
'Weight distribution keys do not match poll options exactly',
186+
);
187+
}
188+
const totalWeight = Object.values(dto.weightDistribution).reduce(
189+
(acc, weight) => acc + weight,
190+
0,
191+
);
192+
if (totalWeight > votingPower) {
193+
throw new Error(
194+
`Total weight distribution must be equal or lower than the voting power of ${votingPower}`,
195+
);
196+
}
179197
const existingVote = await this.databaseService.vote.findFirst({
180198
where: {
181199
pollId: dto.pollId,
@@ -191,7 +209,7 @@ export class UserService {
191209
pollId: dto.pollId,
192210
votingPower,
193211
weightDistribution: dto.weightDistribution,
194-
proof: '', // TODO implement Bandada proof later
212+
proof: '', // Implement Bandada proof in next phase
195213
},
196214
});
197215
const action = await this.databaseService.userAction.create({

0 commit comments

Comments
 (0)