forked from humanprotocol/human-protocol
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into HUM-61-dm-filters-on-mobile
- Loading branch information
Showing
172 changed files
with
4,841 additions
and
1,043 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
packages/apps/fortune/exchange-oracle/server/src/common/decorators/enums.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { | ||
registerDecorator, | ||
ValidationOptions, | ||
ValidationArguments, | ||
} from 'class-validator'; | ||
import 'reflect-metadata'; | ||
|
||
export function IsEnumCaseInsensitive( | ||
enumType: any, | ||
validationOptions?: ValidationOptions, | ||
) { | ||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
return function (object: Object, propertyName: string) { | ||
// Attach enum metadata to the property | ||
Reflect.defineMetadata('custom:enum', enumType, object, propertyName); | ||
|
||
// Register the validation logic using class-validator | ||
registerDecorator({ | ||
name: 'isEnumWithMetadata', | ||
target: object.constructor, | ||
propertyName: propertyName, | ||
options: validationOptions, | ||
validator: { | ||
validate(value: any, args: ValidationArguments) { | ||
// Retrieve enum type from metadata | ||
const enumType = Reflect.getMetadata( | ||
'custom:enum', | ||
args.object, | ||
args.property, | ||
); | ||
if (!enumType) { | ||
return false; // If no enum metadata is found, validation fails | ||
} | ||
|
||
// Validate value is part of the enum | ||
const enumValues = Object.values(enumType); | ||
return enumValues.includes(value); | ||
}, | ||
defaultMessage(args: ValidationArguments) { | ||
// Default message if validation fails | ||
const enumType = Reflect.getMetadata( | ||
'custom:enum', | ||
args.object, | ||
args.property, | ||
); | ||
const enumValues = Object.values(enumType).join(', '); | ||
return `${args.property} must be a valid enum value. Valid values: [${enumValues}]`; | ||
}, | ||
}, | ||
}); | ||
}; | ||
} |
1 change: 1 addition & 0 deletions
1
packages/apps/fortune/exchange-oracle/server/src/common/decorators/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './public'; | ||
export * from './enums'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
...fortune/exchange-oracle/server/src/common/interceptors/transform-enum.interceptor.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { TransformEnumInterceptor } from './transform-enum.interceptor'; | ||
import { | ||
ExecutionContext, | ||
CallHandler, | ||
BadRequestException, | ||
} from '@nestjs/common'; | ||
import { of } from 'rxjs'; | ||
import { IsNumber, IsString, Min } from 'class-validator'; | ||
import { JobType } from '../../common/enums/job'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsEnumCaseInsensitive } from '../decorators/enums'; | ||
|
||
export class MockDto { | ||
@ApiProperty({ | ||
enum: JobType, | ||
}) | ||
@IsEnumCaseInsensitive(JobType) | ||
public jobType: JobType; | ||
|
||
@ApiProperty() | ||
@IsNumber() | ||
@Min(0.5) | ||
public amount: number; | ||
|
||
@ApiProperty() | ||
@IsString() | ||
public address: string; | ||
} | ||
|
||
describe('TransformEnumInterceptor', () => { | ||
let interceptor: TransformEnumInterceptor; | ||
let executionContext: ExecutionContext; | ||
let callHandler: CallHandler; | ||
|
||
beforeEach(() => { | ||
interceptor = new TransformEnumInterceptor(); | ||
|
||
// Mocking ExecutionContext and CallHandler | ||
executionContext = { | ||
switchToHttp: jest.fn().mockReturnValue({ | ||
getRequest: jest.fn().mockReturnValue({ | ||
body: { | ||
jobType: 'FORTUNE', | ||
amount: 5, | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}, | ||
}), | ||
}), | ||
getHandler: jest.fn().mockReturnValue({ | ||
name: 'create', // Assume the handler is named 'create' | ||
}), | ||
getClass: jest.fn().mockReturnValue({ | ||
prototype: {}, | ||
}), | ||
} as unknown as ExecutionContext; | ||
|
||
callHandler = { | ||
handle: jest.fn().mockReturnValue(of({})), | ||
}; | ||
|
||
// Mock Reflect.getMetadata to return DTO and Enum types | ||
Reflect.getMetadata = jest.fn((metadataKey, target, propertyKey) => { | ||
// Mock design:paramtypes to return MockDto as the parameter type | ||
if (metadataKey === 'design:paramtypes') { | ||
return [MockDto]; | ||
} | ||
|
||
// Mock custom:enum to return the corresponding enum for each property | ||
if (metadataKey === 'custom:enum' && propertyKey === 'jobType') { | ||
return JobType; | ||
} | ||
return undefined; // For non-enum properties, return undefined | ||
}) as any; | ||
}); | ||
|
||
it('should transform enum values to lowercase', async () => { | ||
// Run the interceptor | ||
await interceptor.intercept(executionContext, callHandler).toPromise(); | ||
|
||
// Access the modified request body | ||
const request = executionContext.switchToHttp().getRequest(); | ||
|
||
// Expectations | ||
expect(request.body.jobType).toBe('fortune'); // Should be transformed to lowercase | ||
expect(request.body).toEqual({ | ||
jobType: 'fortune', | ||
amount: 5, | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}); | ||
expect(callHandler.handle).toBeCalled(); // Ensure the handler is called | ||
}); | ||
|
||
it('should throw an error if the value is not a valid enum', async () => { | ||
// Modify the request body to have an invalid enum value for jobType | ||
executionContext.switchToHttp = jest.fn().mockReturnValue({ | ||
getRequest: jest.fn().mockReturnValue({ | ||
body: { | ||
jobType: 'invalidEnum', // Invalid enum value for jobType | ||
amount: 5, | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}, | ||
}), | ||
}); | ||
|
||
try { | ||
// Run the interceptor | ||
await interceptor.intercept(executionContext, callHandler).toPromise(); | ||
} catch (err) { | ||
// Expect an error to be thrown | ||
expect(err).toBeInstanceOf(BadRequestException); | ||
expect(err.response.statusCode).toBe(400); | ||
expect(err.response.message).toContain('Validation failed'); | ||
} | ||
}); | ||
|
||
it('should not transform non-enum properties', async () => { | ||
// Run the interceptor with a non-enum property (amount and address) | ||
await interceptor.intercept(executionContext, callHandler).toPromise(); | ||
|
||
// Access the modified request body | ||
const request = executionContext.switchToHttp().getRequest(); | ||
|
||
// Expectations | ||
expect(request.body.amount).toBe(5); // Non-enum property should remain unchanged | ||
expect(request.body.address).toBe( | ||
'0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
); // Non-enum string should remain unchanged | ||
expect(callHandler.handle).toBeCalled(); | ||
}); | ||
|
||
it('should handle nested objects with enums', async () => { | ||
// Modify the request body to have a nested object with enum value | ||
executionContext.switchToHttp = jest.fn().mockReturnValue({ | ||
getRequest: jest.fn().mockReturnValue({ | ||
body: { | ||
transaction: { | ||
jobType: 'FORTUNE', | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}, | ||
amount: 5, | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}, | ||
}), | ||
}); | ||
|
||
// Run the interceptor | ||
await interceptor.intercept(executionContext, callHandler).toPromise(); | ||
|
||
// Access the modified request body | ||
const request = executionContext.switchToHttp().getRequest(); | ||
|
||
// Expectations | ||
expect(request.body.transaction.jobType).toBe('fortune'); | ||
expect(request.body).toEqual({ | ||
transaction: { | ||
jobType: 'fortune', | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}, | ||
amount: 5, | ||
address: '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e', | ||
}); | ||
expect(callHandler.handle).toHaveBeenCalled(); | ||
}); | ||
}); |
Oops, something went wrong.