Skip to content

Commit 1f20f59

Browse files
committed
Merge branch 'develop' of github.com:humanprotocol/human-protocol into kb/3161
2 parents b4152ce + 7c17a23 commit 1f20f59

File tree

157 files changed

+3102
-2323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

157 files changed

+3102
-2323
lines changed

packages/apps/dashboard/client/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@emotion/react": "^11.11.4",
1717
"@emotion/styled": "^11.11.5",
1818
"@human-protocol/sdk": "*",
19-
"@mui/icons-material": "^6.4.6",
19+
"@mui/icons-material": "^7.0.1",
2020
"@mui/material": "^5.15.18",
2121
"@mui/styled-engine-sc": "6.4.0",
2222
"@mui/x-data-grid": "^7.23.2",
@@ -53,7 +53,7 @@
5353
"sass": "^1.85.0",
5454
"stylelint-prettier": "^5.0.0",
5555
"typescript": "^5.6.3",
56-
"vite": "^6.2.0",
56+
"vite": "^6.2.4",
5757
"vite-plugin-svgr": "^4.2.0"
5858
}
5959
}

packages/apps/faucet/client/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"license": "MIT",
77
"dependencies": {
88
"@human-protocol/sdk": "*",
9-
"@mui/icons-material": "^6.4.6",
9+
"@mui/icons-material": "^7.0.1",
1010
"@mui/material": "^5.16.7",
1111
"react": "^18.2.0",
1212
"react-dom": "^18.2.0",
@@ -22,7 +22,7 @@
2222
"eslint-plugin-import": "^2.29.0",
2323
"eslint-plugin-react": "^7.34.3",
2424
"eslint-plugin-react-hooks": "^5.1.0",
25-
"vite": "^6.2.0",
25+
"vite": "^6.2.4",
2626
"vite-plugin-node-polyfills": "^0.22.0"
2727
},
2828
"scripts": {

packages/apps/fortune/exchange-oracle/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"eslint-plugin-react-hooks": "^5.1.0",
5151
"eslint-plugin-react-refresh": "^0.4.11",
5252
"typescript": "^5.6.3",
53-
"vite": "^6.2.0"
53+
"vite": "^6.2.4"
5454
},
5555
"lint-staged": {
5656
"*.{ts,tsx}": [

packages/apps/fortune/exchange-oracle/server/src/common/constant/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export enum ErrorAssignment {
4545
export enum ErrorJob {
4646
AlreadyExists = 'Job already exists',
4747
InvalidAddress = 'Invalid address',
48+
InvalidStatus = 'Invalid job status',
4849
NotAssigned = 'User is not assigned to the job',
4950
SolutionAlreadySubmitted = 'User has already submitted a solution',
5051
JobCompleted = 'This job has already been completed',

packages/apps/fortune/exchange-oracle/server/src/common/enums/job.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export enum JobStatus {
22
ACTIVE = 'active',
3+
PAUSED = 'paused',
34
COMPLETED = 'completed',
45
CANCELED = 'canceled',
56
}

packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export enum EventType {
55
ESCROW_FAILED = 'escrow_failed',
66
SUBMISSION_REJECTED = 'submission_rejected',
77
SUBMISSION_IN_REVIEW = 'submission_in_review',
8+
ABUSE_DETECTED = 'abuse_detected',
9+
ABUSE_DISMISSED = 'abuse_dismissed',
810
}
911

1012
export enum WebhookStatus {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export class HandleAbuse1743412274647 implements MigrationInterface {
4+
name = 'HandleAbuse1743412274647';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`
8+
ALTER TYPE "hmt"."webhooks_event_type_enum"
9+
RENAME TO "webhooks_event_type_enum_old"
10+
`);
11+
await queryRunner.query(`
12+
CREATE TYPE "hmt"."webhooks_event_type_enum" AS ENUM(
13+
'escrow_created',
14+
'escrow_completed',
15+
'escrow_canceled',
16+
'escrow_failed',
17+
'submission_rejected',
18+
'submission_in_review',
19+
'abuse_detected',
20+
'abuse_dismissed'
21+
)
22+
`);
23+
await queryRunner.query(`
24+
ALTER TABLE "hmt"."webhooks"
25+
ALTER COLUMN "event_type" TYPE "hmt"."webhooks_event_type_enum" USING "event_type"::"text"::"hmt"."webhooks_event_type_enum"
26+
`);
27+
await queryRunner.query(`
28+
DROP TYPE "hmt"."webhooks_event_type_enum_old"
29+
`);
30+
await queryRunner.query(`
31+
ALTER TYPE "hmt"."jobs_status_enum"
32+
RENAME TO "jobs_status_enum_old"
33+
`);
34+
await queryRunner.query(`
35+
CREATE TYPE "hmt"."jobs_status_enum" AS ENUM('active', 'paused', 'completed', 'canceled')
36+
`);
37+
await queryRunner.query(`
38+
ALTER TABLE "hmt"."jobs"
39+
ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum" USING "status"::"text"::"hmt"."jobs_status_enum"
40+
`);
41+
await queryRunner.query(`
42+
DROP TYPE "hmt"."jobs_status_enum_old"
43+
`);
44+
}
45+
46+
public async down(queryRunner: QueryRunner): Promise<void> {
47+
await queryRunner.query(`
48+
CREATE TYPE "hmt"."jobs_status_enum_old" AS ENUM('active', 'completed', 'canceled')
49+
`);
50+
await queryRunner.query(`
51+
ALTER TABLE "hmt"."jobs"
52+
ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum_old" USING "status"::"text"::"hmt"."jobs_status_enum_old"
53+
`);
54+
await queryRunner.query(`
55+
DROP TYPE "hmt"."jobs_status_enum"
56+
`);
57+
await queryRunner.query(`
58+
ALTER TYPE "hmt"."jobs_status_enum_old"
59+
RENAME TO "jobs_status_enum"
60+
`);
61+
await queryRunner.query(`
62+
CREATE TYPE "hmt"."webhooks_event_type_enum_old" AS ENUM(
63+
'escrow_created',
64+
'escrow_completed',
65+
'escrow_canceled',
66+
'escrow_failed',
67+
'submission_rejected',
68+
'submission_in_review'
69+
)
70+
`);
71+
await queryRunner.query(`
72+
ALTER TABLE "hmt"."webhooks"
73+
ALTER COLUMN "event_type" TYPE "hmt"."webhooks_event_type_enum_old" USING "event_type"::"text"::"hmt"."webhooks_event_type_enum_old"
74+
`);
75+
await queryRunner.query(`
76+
DROP TYPE "hmt"."webhooks_event_type_enum"
77+
`);
78+
await queryRunner.query(`
79+
ALTER TYPE "hmt"."webhooks_event_type_enum_old"
80+
RENAME TO "webhooks_event_type_enum"
81+
`);
82+
}
83+
}

packages/apps/fortune/exchange-oracle/server/src/modules/assignment/assignment.service.spec.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
MOCK_MANIFEST_URL,
77
MOCK_PRIVATE_KEY,
88
} from '../../../test/constants';
9-
import { AssignmentStatus, JobType } from '../../common/enums/job';
9+
import { AssignmentStatus, JobStatus, JobType } from '../../common/enums/job';
1010
import { AssignmentRepository } from '../assignment/assignment.repository';
1111
import { AssignmentService } from '../assignment/assignment.service';
1212
import { ManifestDto } from '../job/job.dto';
@@ -18,7 +18,7 @@ import { Escrow__factory } from '@human-protocol/core/typechain-types';
1818
import { AssignmentSortField } from '../../common/enums/job';
1919
import { SortDirection } from '../../common/enums/collection';
2020
import { AssignmentEntity } from './assignment.entity';
21-
import { ErrorAssignment } from '../../common/constant/errors';
21+
import { ErrorAssignment, ErrorJob } from '../../common/constant/errors';
2222
import { BadRequestException } from '@nestjs/common';
2323
import { ServerConfigService } from '../../common/config/server-config.service';
2424

@@ -117,6 +117,7 @@ describe('AssignmentService', () => {
117117
id: 1,
118118
manifestUrl: MOCK_MANIFEST_URL,
119119
reputationNetwork: reputationNetwork,
120+
status: JobStatus.ACTIVE,
120121
} as any);
121122
jest
122123
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')
@@ -140,6 +141,7 @@ describe('AssignmentService', () => {
140141
id: 1,
141142
manifestUrl: MOCK_MANIFEST_URL,
142143
reputationNetwork: reputationNetwork,
144+
status: JobStatus.ACTIVE,
143145
},
144146
workerAddress: workerAddress,
145147
status: AssignmentStatus.ACTIVE,
@@ -160,6 +162,7 @@ describe('AssignmentService', () => {
160162
id: 1,
161163
manifestUrl: MOCK_MANIFEST_URL,
162164
reputationNetwork: reputationNetwork,
165+
status: JobStatus.ACTIVE,
163166
} as any);
164167
jest
165168
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')
@@ -194,6 +197,7 @@ describe('AssignmentService', () => {
194197
assignmentService.createAssignment(createAssignmentDto, {
195198
address: workerAddress,
196199
reputationNetwork: reputationNetwork,
200+
status: JobStatus.ACTIVE,
197201
} as any),
198202
).rejects.toThrow('Job not found');
199203
});
@@ -207,6 +211,7 @@ describe('AssignmentService', () => {
207211
id: 1,
208212
manifestUrl: MOCK_MANIFEST_URL,
209213
reputationNetwork: differentReputationNetwork,
214+
status: JobStatus.ACTIVE,
210215
} as any);
211216

212217
await expect(
@@ -217,13 +222,32 @@ describe('AssignmentService', () => {
217222
).rejects.toThrow('Requested job is not in your reputation network');
218223
});
219224

225+
it('should fail if job is not active', async () => {
226+
jest
227+
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
228+
.mockResolvedValue({
229+
id: 1,
230+
manifestUrl: MOCK_MANIFEST_URL,
231+
reputationNetwork: reputationNetwork,
232+
status: JobStatus.PAUSED,
233+
} as any);
234+
235+
await expect(
236+
assignmentService.createAssignment(createAssignmentDto, {
237+
address: workerAddress,
238+
reputationNetwork: reputationNetwork,
239+
} as any),
240+
).rejects.toThrow(ErrorJob.InvalidStatus);
241+
});
242+
220243
it('should fail if user already assigned', async () => {
221244
jest
222245
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
223246
.mockResolvedValue({
224247
id: 1,
225248
manifestUrl: MOCK_MANIFEST_URL,
226249
reputationNetwork: reputationNetwork,
250+
status: JobStatus.ACTIVE,
227251
} as any);
228252
jest
229253
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')
@@ -244,6 +268,7 @@ describe('AssignmentService', () => {
244268
id: 1,
245269
manifestUrl: MOCK_MANIFEST_URL,
246270
reputationNetwork: reputationNetwork,
271+
status: JobStatus.ACTIVE,
247272
} as any);
248273
jest
249274
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')
@@ -255,6 +280,7 @@ describe('AssignmentService', () => {
255280
assignmentService.createAssignment(createAssignmentDto, {
256281
address: workerAddress,
257282
reputationNetwork: reputationNetwork,
283+
status: JobStatus.ACTIVE,
258284
} as any),
259285
).rejects.toThrow('Fully assigned job');
260286
});
@@ -267,6 +293,7 @@ describe('AssignmentService', () => {
267293
id: 1,
268294
manifestUrl: MOCK_MANIFEST_URL,
269295
reputationNetwork: reputationNetwork,
296+
status: JobStatus.ACTIVE,
270297
} as any);
271298
jest
272299
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')
@@ -291,6 +318,7 @@ describe('AssignmentService', () => {
291318
id: 1,
292319
manifestUrl: MOCK_MANIFEST_URL,
293320
reputationNetwork: reputationNetwork,
321+
status: JobStatus.ACTIVE,
294322
} as any);
295323
jest
296324
.spyOn(assignmentRepository, 'findOneByJobIdAndWorker')

packages/apps/fortune/exchange-oracle/server/src/modules/assignment/assignment.service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
2-
import { AssignmentStatus, JobType } from '../../common/enums/job';
2+
import { AssignmentStatus, JobStatus, JobType } from '../../common/enums/job';
33
import { JwtUser } from '../../common/types/jwt';
44
import { JobRepository } from '../job/job.repository';
55
import {
@@ -13,7 +13,7 @@ import { PageDto } from '../../common/pagination/pagination.dto';
1313
import { JobService } from '../job/job.service';
1414
import { Escrow__factory } from '@human-protocol/core/typechain-types';
1515
import { Web3Service } from '../web3/web3.service';
16-
import { ErrorAssignment } from '../../common/constant/errors';
16+
import { ErrorAssignment, ErrorJob } from '../../common/constant/errors';
1717
import { ServerConfigService } from '../../common/config/server-config.service';
1818

1919
@Injectable()
@@ -40,6 +40,9 @@ export class AssignmentService {
4040
if (!jobEntity) {
4141
this.logger.log(ErrorAssignment.JobNotFound, AssignmentService.name);
4242
throw new BadRequestException(ErrorAssignment.JobNotFound);
43+
} else if (jobEntity.status !== JobStatus.ACTIVE) {
44+
this.logger.log(ErrorJob.InvalidStatus, AssignmentService.name);
45+
throw new BadRequestException(ErrorJob.InvalidStatus);
4346
} else if (jobEntity.reputationNetwork !== jwtUser.reputationNetwork) {
4447
this.logger.log(
4548
ErrorAssignment.ReputationNetworkMismatch,

packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ describe('JobService', () => {
450450
job: {
451451
escrowAddress,
452452
chainId,
453+
status: JobStatus.ACTIVE,
453454
},
454455
} as AssignmentEntity;
455456

@@ -666,4 +667,54 @@ describe('JobService', () => {
666667
).rejects.toThrow(`Solution not found in Escrow: ${escrowAddress}`);
667668
});
668669
});
670+
671+
describe('pauseJob', () => {
672+
const webhook: WebhookDto = {
673+
chainId,
674+
escrowAddress,
675+
eventType: EventType.ABUSE_DETECTED,
676+
};
677+
678+
it('should create a new job in the database', async () => {
679+
jest
680+
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
681+
.mockResolvedValue({
682+
chainId: chainId,
683+
escrowAddress: escrowAddress,
684+
status: JobStatus.ACTIVE,
685+
} as JobEntity);
686+
const result = await jobService.pauseJob(webhook);
687+
688+
expect(result).toEqual(undefined);
689+
expect(jobRepository.updateOne).toHaveBeenCalledWith({
690+
chainId: chainId,
691+
escrowAddress: escrowAddress,
692+
status: JobStatus.PAUSED,
693+
});
694+
});
695+
696+
it('should fail if job not exists', async () => {
697+
jest
698+
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
699+
.mockResolvedValue(null);
700+
701+
await expect(jobService.pauseJob(webhook)).rejects.toThrow(
702+
ErrorJob.NotFound,
703+
);
704+
});
705+
706+
it('should fail if job is not in Active status', async () => {
707+
jest
708+
.spyOn(jobRepository, 'findOneByChainIdAndEscrowAddress')
709+
.mockResolvedValue({
710+
chainId: chainId,
711+
escrowAddress: escrowAddress,
712+
status: JobStatus.CANCELED,
713+
} as JobEntity);
714+
715+
await expect(jobService.pauseJob(webhook)).rejects.toThrow(
716+
ErrorJob.InvalidStatus,
717+
);
718+
});
719+
});
669720
});

0 commit comments

Comments
 (0)