Skip to content

Commit

Permalink
Changes logic of calling exchange oracle: url is obtained based on th…
Browse files Browse the repository at this point in the history
…e address provided in the enpoint. Adds CORS
  • Loading branch information
macnablocky committed Apr 17, 2024
1 parent 030ef6d commit 413a3e5
Show file tree
Hide file tree
Showing 27 changed files with 514 additions and 123 deletions.
2 changes: 1 addition & 1 deletion packages/apps/human-app/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ E2E_TESTING_PASSWORD=
E2E_TESTING_EXCHANGE_ORACLE_URL=
E2E_TESTING_ESCROW_ADDRESS=
E2E_TESTING_ESCROW_CHAIN_ID=
POLYGON_KVSTORE_ADDRESS=
RPC_URL=
2 changes: 2 additions & 0 deletions packages/apps/human-app/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { JobAssignmentModule } from './modules/job-assignment/job-assignment.mod
import { StatisticsModule } from './modules/statistics/statistics.module';
import { StatisticsController } from './modules/statistics/statistics.controller';
import { ExchangeOracleModule } from './integrations/exchange-oracle/exchange-oracle.module';
import { KvStoreModule } from './integrations/kv-store/kv-store.module';

@Module({
imports: [
Expand All @@ -44,6 +45,7 @@ import { ExchangeOracleModule } from './integrations/exchange-oracle/exchange-or
CommonConfigModule,
OracleDiscoveryModule,
StatisticsModule,
KvStoreModule,
],
controllers: [
AppController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,8 @@ export class EnvironmentConfigService {
DEFAULT_CACHE_TTL_ORACLE_DISCOVERY,
);
}
get polygonKVStoreAddress(): string {
return this.configService.get<string>(
'POLYGON_KVSTORE_ADDRESS',
DEFAULT_KVSTORE_ADDRESS
)
get rpcUrl(): string {
return this.configService.get<string>('RPC_URL', '') // @TODO: add default
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ import { Injectable } from '@nestjs/common';
import { AxiosRequestConfig } from 'axios';
import { lastValueFrom } from 'rxjs';
import {
UserStatisticsCommand,
UserStatisticsDetails,
UserStatisticsResponse,
} from '../../modules/statistics/model/user-statistics.model';
import { HttpService } from '@nestjs/axios';
import {
OracleStatisticsCommand,
OracleStatisticsDetails,
OracleStatisticsResponse,
} from '../../modules/statistics/model/oracle-statistics.model';
import {
JobAssignmentCommand,
JobAssignmentData,
JobAssignmentDetails,
JobAssignmentParams,
JobAssignmentResponse,
JobsFetchParams,
JobsFetchParamsCommand,
JobsFetchParamsData,
JobsFetchParamsDetails,
JobsFetchResponse,
} from '../../modules/job-assignment/model/job-assignment.model';
import {
JobsDiscoveryParams,
JobsDiscoveryParamsCommand,
JobsDiscoveryParamsData,
JobsDiscoveryParamsDetails,
JobsDiscoveryResponse,
} from '../../modules/jobs-discovery/model/jobs-discovery.model';
import { Mapper } from '@automapper/core';
Expand Down Expand Up @@ -54,72 +54,72 @@ export class ExchangeOracleGateway {
return response.data;
}
async fetchUserStatistics(
command: UserStatisticsCommand,
details: UserStatisticsDetails,
): Promise<UserStatisticsResponse> {
const options: AxiosRequestConfig = {
method: HttpMethod.GET,
url: `${command.exchangeOracleUrl}/stats/assignment`,
url: `${details.exchangeOracleUrl}/stats/assignment`,
headers: {
Authorization: command.token,
Authorization: details.token,
},
};
return this.callExternalHttpUtilRequest<UserStatisticsResponse>(options);
}
async fetchOracleStatistics(
command: OracleStatisticsCommand,
details: OracleStatisticsDetails,
): Promise<OracleStatisticsResponse> {
const options: AxiosRequestConfig = {
method: HttpMethod.GET,
url: `${command.exchangeOracleUrl}/stats`,
url: `${details.exchangeOracleUrl}/stats`,
};
return this.callExternalHttpUtilRequest<OracleStatisticsResponse>(options);
}
async fetchAssignedJobs(
command: JobsFetchParamsCommand,
details: JobsFetchParamsDetails,
): Promise<JobsFetchResponse> {
const jobFetchParamsData = this.mapper.map(
command.data,
details.data,
JobsFetchParams,
JobsFetchParamsData,
);
const reducedParams = this.toCleanObjParams(jobFetchParamsData);
const options: AxiosRequestConfig = {
method: HttpMethod.GET,
url: `${command.exchangeOracleUrl}/assignment`,
url: `${details.exchangeOracleUrl}/assignment`,
params: reducedParams,
};
return this.callExternalHttpUtilRequest<JobsFetchResponse>(options);
}
async postNewJobAssignment(
command: JobAssignmentCommand,
details: JobAssignmentDetails,
): Promise<JobAssignmentResponse> {
const options: AxiosRequestConfig = {
method: HttpMethod.POST,
url: `${command.exchangeOracleUrl}/assignment`,
url: `${details.exchangeOracleUrl}/assignment`,
data: this.mapper.map(
command.data,
details.data,
JobAssignmentParams,
JobAssignmentData,
),
headers: {
Authorization: command.token,
Authorization: details.token,
},
};
return this.callExternalHttpUtilRequest<JobAssignmentResponse>(options);
}
async fetchDiscoveredJobs(command: JobsDiscoveryParamsCommand) {
async fetchJobs(details: JobsDiscoveryParamsDetails) {
const jobsDiscoveryParamsData = this.mapper.map(
command.data,
details.data,
JobsDiscoveryParams,
JobsDiscoveryParamsData,
);
const reducedParams = this.toCleanObjParams(jobsDiscoveryParamsData);
const options: AxiosRequestConfig = {
method: HttpMethod.GET,
url: `${command.exchangeOracleUrl}/job`,
url: `${details.exchangeOracleUrl}/job`,
params: reducedParams,
headers: {
Authorization: command.token,
Authorization: details.token,
Accept: 'application/json',
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@ import { Test, TestingModule } from '@nestjs/testing';
import { HttpService } from '@nestjs/axios';
import { ExchangeOracleGateway } from '../exchange-oracle.gateway';
import {
oracleStatsCommandFixture,
statisticsExchangeOracleUrl,
userStatsCommandFixture,
oracleStatsDetailsFixture,
statisticsExchangeOracleAddress, statisticsExchangeOracleUrl,
userStatsDetailsFixture,
} from '../../../modules/statistics/spec/statistics.fixtures';
import { AutomapperModule } from '@automapper/nestjs';
import { classes } from '@automapper/classes';
import nock, { RequestBodyMatcher } from 'nock';
import { of, throwError } from 'rxjs';
import {
jobAssignmentCommandFixture,
jobAssignmentDataFixture,
jobAssignmentDetailsFixture,
jobAssignmentOracleUrl,
jobsFetchParamsCommandFixture,
jobsFetchParamsDataFixtureAsString,
jobsFetchParamsDetailsFixture,
} from '../../../modules/job-assignment/spec/job-assignment.fixtures';
import { ExchangeOracleProfile } from '../exchange-oracle.mapper';
import {
jobsDiscoveryParamsCommandFixture,
jobsDiscoveryParamsDetailsFixture,
paramsDataFixtureAsString,
} from '../../../modules/jobs-discovery/spec/jobs-discovery.fixtures';
import { GoneException, HttpException } from '@nestjs/common';
import { UserStatisticsDetails } from '../../../modules/statistics/model/user-statistics.model';
import { HttpMethod } from '../../../common/enums/http-method';

describe('ExchangeOracleApiGateway', () => {
let gateway: ExchangeOracleGateway;
Expand Down Expand Up @@ -53,87 +55,113 @@ describe('ExchangeOracleApiGateway', () => {

it('should be defined', () => {
expect(gateway).toBeDefined();
expect(httpService).toBeDefined();
});

describe('fetchUserStatistics', () => {
it('should successfully call the requested url for user statistics', async () => {
const command = userStatsCommandFixture;
const details = userStatsDetailsFixture;
nock(statisticsExchangeOracleUrl)
.get('/stats/assignment')
.matchHeader('Authorization', `Bearer ${command.token}`)
.matchHeader('Authorization', `Bearer ${details.token}`)
.reply(200);
await gateway.fetchUserStatistics(command);
expect(httpService.request).toHaveBeenCalled();
await gateway.fetchUserStatistics(details);
expect(httpService.request).toHaveBeenCalledWith(
expect.objectContaining({
url: details.exchangeOracleUrl + '/stats/assignment',
method: HttpMethod.GET,
}),
);
});
it('should handle errors on fetchUserStatistics', async () => {
const command = {
const details = {
exchangeOracleUrl: 'https://example.com',
token: 'dummyToken',
};
} as UserStatisticsDetails;
jest
.spyOn(httpService, 'request')
.mockReturnValue(
throwError(() => new HttpException('Service Unavailable', 503)),
);

await expect(gateway.fetchUserStatistics(command)).rejects.toThrow(
await expect(gateway.fetchUserStatistics(details)).rejects.toThrow(
HttpException,
);
});
});
describe('fetchOracleStatistics', () => {
it('should successfully call the requested url for oracle statistics', async () => {
const command = oracleStatsCommandFixture;
const details = oracleStatsDetailsFixture;
nock(statisticsExchangeOracleUrl).get('/stats').reply(200);
await gateway.fetchOracleStatistics(command);
expect(httpService.request).toHaveBeenCalled();
await gateway.fetchOracleStatistics(details);
expect(httpService.request).toHaveBeenCalledWith(
expect.objectContaining({
url: details.exchangeOracleUrl + '/stats',
method: HttpMethod.GET,
}),
);
});
it('should handle errors on fetchOracleStatistics', async () => {
const command = {
const details = {
exchangeOracleUrl: 'https://example.com',
token: 'dummyToken',
};
} as UserStatisticsDetails;
jest
.spyOn(httpService, 'request')
.mockReturnValue(throwError(() => new GoneException()));

await expect(gateway.fetchUserStatistics(command)).rejects.toThrow(
await expect(gateway.fetchUserStatistics(details)).rejects.toThrow(
GoneException,
);
});
});
describe('fetchAssignedJobs', () => {
it('should successfully call get assigned jobs', async () => {
const command = jobsFetchParamsCommandFixture;
const details = jobsFetchParamsDetailsFixture;
nock(jobAssignmentOracleUrl)
.get(`/assignment${jobsFetchParamsDataFixtureAsString}`)
.reply(200);
await gateway.fetchAssignedJobs(command);
expect(httpService.request).toHaveBeenCalled();
await gateway.fetchAssignedJobs(details);
expect(httpService.request).toHaveBeenCalledWith(
expect.objectContaining({
url: details.exchangeOracleUrl + '/assignment',
method: HttpMethod.GET,
}),
);
});
});
describe('postNewJobAssignment', () => {
it('should successfully post new job assignment', async () => {
const command = jobAssignmentCommandFixture;
const details = jobAssignmentDetailsFixture;
const data = jobAssignmentDataFixture;
const matcher: RequestBodyMatcher = {
escrowAddress: data.escrow_address,
chainId: data.chain_id,
};
nock(jobAssignmentOracleUrl).post('/assignment', matcher).reply(200);
await gateway.postNewJobAssignment(command);
expect(httpService.request).toHaveBeenCalled();
await gateway.postNewJobAssignment(details);
expect(httpService.request).toHaveBeenCalledWith(
expect.objectContaining({
url: details.exchangeOracleUrl + '/assignment',
method: HttpMethod.POST,
}),
);
});
});

describe('fetchDiscoveredJobs', () => {
it('should successfully call get discovered jobs', async () => {
const command = jobsDiscoveryParamsCommandFixture;
const details = jobsDiscoveryParamsDetailsFixture;
nock(jobAssignmentOracleUrl)
.get(`/assignment${paramsDataFixtureAsString}`)
.reply(200);
await gateway.fetchDiscoveredJobs(command);
expect(httpService.request).toHaveBeenCalled();
await gateway.fetchJobs(details);
expect(httpService.request).toHaveBeenCalledWith(
expect.objectContaining({
url: details.exchangeOracleUrl + '/job',
method: HttpMethod.GET,
}),
);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { EnvironmentConfigService } from '../../common/config/environment-config.service';
import { ethers } from 'ethers';
import { KVStoreClient } from '@human-protocol/sdk';

@Injectable()
export class KvStoreGateway {
private URL_KEY = 'url';
private kvStoreClient: KVStoreClient;
constructor(private environmentConfig: EnvironmentConfigService) {}
async onModuleInit(): Promise<void> {
this.kvStoreClient = await KVStoreClient.build(
new ethers.JsonRpcProvider(this.environmentConfig.rpcUrl),
);
}
async getExchangeOracleUrlByAddress(address: string): Promise<string> {
return this.kvStoreClient.get(address, this.URL_KEY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { KvStoreGateway } from './kv-store-gateway.service';

@Module({
providers: [KvStoreGateway],
exports: [KvStoreGateway],
})
export class KvStoreModule {}
Loading

0 comments on commit 413a3e5

Please sign in to comment.