Skip to content

Commit

Permalink
Get exchange rates (#6392)
Browse files Browse the repository at this point in the history
* Get exchange rates

* name fix

* typo

* enable disabled cronjob

* Update services/121-service/src/exchange-rates/exchange-rates.controller.ts

Co-authored-by: Peter Smallenbroek <[email protected]>

---------

Co-authored-by: Ruben <[email protected]>
Co-authored-by: Peter Smallenbroek <[email protected]>
  • Loading branch information
3 people authored Jan 16, 2025
1 parent b336a14 commit 22f8b7b
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 31 deletions.
4 changes: 2 additions & 2 deletions services/121-service/module-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

```mermaid
graph LR
CronjobModule-->ExchangeRateModule
ExchangeRateModule-->UserModule
CronjobModule-->ExchangeRatesModule
ExchangeRatesModule-->UserModule
UserModule-->EmailsModule
MessageTemplateModule-->UserModule
MessageTemplateModule-->ProgramAttributesModule
Expand Down
4 changes: 2 additions & 2 deletions services/121-service/src/cronjob/cronjob.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Module } from '@nestjs/common';

import { CronjobService } from '@121-service/src/cronjob/cronjob.service';
import { ExchangeRateModule } from '@121-service/src/exchange-rate/exchange-rate.module';
import { ExchangeRatesModule } from '@121-service/src/exchange-rates/exchange-rates.module';

@Module({
imports: [ExchangeRateModule],
imports: [ExchangeRatesModule],
providers: [CronjobService],
controllers: [],
exports: [CronjobService],
Expand Down
6 changes: 3 additions & 3 deletions services/121-service/src/cronjob/cronjob.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

import { ExchangeRateService } from '@121-service/src/exchange-rate/exchange-rate.service';
import { ExchangeRatesService } from '@121-service/src/exchange-rates/exchange-rates.service';
import { CustomHttpService } from '@121-service/src/shared/services/custom-http.service';
import { AxiosCallsService } from '@121-service/src/utils/axios/axios-calls.service';

Expand All @@ -15,7 +15,7 @@ export class CronjobService {
private httpService = new CustomHttpService(new HttpService());
private axiosCallsService = new AxiosCallsService();

constructor(private exchangeRateService: ExchangeRateService) {}
constructor(private exchangeRatesService: ExchangeRatesService) {}

@Cron(CronExpression.EVERY_10_MINUTES, {
disabled: !shouldBeEnabled(
Expand Down Expand Up @@ -90,7 +90,7 @@ export class CronjobService {
public async getDailyExchangeRates(): Promise<void> {
console.info('CronjobService - Started: getDailyExchangeRates');

await this.exchangeRateService.getAndStoreProgramsExchangeRates();
await this.exchangeRatesService.getAndStoreProgramsExchangeRates();

console.info('CronjobService - Complete: getDailyExchangeRates');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';

export class GetExchangeRateDto {
@ApiProperty({ example: 'KES', type: 'string' })
public currency: string;

@ApiProperty({ example: '0.00744368', type: 'number' })
public euroExchangeRate: number;

@ApiProperty({
example: '2025-01-15T23:59:59Z',
type: 'string',
nullable: true,
})
public closeTime: string | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface ExchangeRateApiResponse {
}

@Injectable()
export class ExchangeRateApiService {
export class ExchangeRatesApiService {
public constructor(private readonly httpService: CustomHttpService) {}

public async retrieveExchangeRate(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Controller, UseGuards } from '@nestjs/common';
import { Get, HttpStatus } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';

import { GetExchangeRateDto } from '@121-service/src/exchange-rates/dtos/get-exchange-rate.dto';
import { ExchangeRatesService } from '@121-service/src/exchange-rates/exchange-rates.service';
import { AuthenticatedUser } from '@121-service/src/guards/authenticated-user.decorator';
import { AuthenticatedUserGuard } from '@121-service/src/guards/authenticated-user.guard';

@UseGuards(AuthenticatedUserGuard)
@ApiTags('exchange-rates')
@Controller('exchange-rates')
export class ExchangeRatesController {
public constructor(
private readonly exchangeRateService: ExchangeRatesService,
) {}

@AuthenticatedUser({ isAdmin: true })
@ApiOperation({
summary: 'Get all exchange rates',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Gets all exchange rates for a program',
type: [GetExchangeRateDto],
})
@Get()
public async getAll(): Promise<GetExchangeRateDto[]> {
return this.exchangeRateService.getAll();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { ExchangeRateApiService } from '@121-service/src/exchange-rate/exchange-rate.api.service';
import { ExchangeRateEntity } from '@121-service/src/exchange-rate/exchange-rate.entity';
import { ExchangeRateService } from '@121-service/src/exchange-rate/exchange-rate.service';
import { ExchangeRateEntity } from '@121-service/src/exchange-rates/exchange-rate.entity';
import { ExchangeRatesApiService } from '@121-service/src/exchange-rates/exchange-rates.api.service';
import { ExchangeRatesController } from '@121-service/src/exchange-rates/exchange-rates.controller';
import { ExchangeRatesService } from '@121-service/src/exchange-rates/exchange-rates.service';
import { ProgramEntity } from '@121-service/src/programs/program.entity';
import { CustomHttpService } from '@121-service/src/shared/services/custom-http.service';
import { UserModule } from '@121-service/src/user/user.module';
Expand All @@ -15,8 +16,8 @@ import { UserModule } from '@121-service/src/user/user.module';
UserModule,
HttpModule,
],
providers: [ExchangeRateService, CustomHttpService, ExchangeRateApiService],
controllers: [],
exports: [ExchangeRateService, ExchangeRateApiService],
providers: [ExchangeRatesService, CustomHttpService, ExchangeRatesApiService],
controllers: [ExchangeRatesController],
exports: [ExchangeRatesService, ExchangeRatesApiService],
})
export class ExchangeRateModule {}
export class ExchangeRatesModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import { Test } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { ExchangeRateApiService } from '@121-service/src/exchange-rate/exchange-rate.api.service';
import { ExchangeRateEntity } from '@121-service/src/exchange-rate/exchange-rate.entity';
import { ExchangeRateService } from '@121-service/src/exchange-rate/exchange-rate.service';
import { ExchangeRateEntity } from '@121-service/src/exchange-rates/exchange-rate.entity';
import { ExchangeRatesApiService } from '@121-service/src/exchange-rates/exchange-rates.api.service';
import { ExchangeRatesService as ExchangeRatesService } from '@121-service/src/exchange-rates/exchange-rates.service';
import { ProgramEntity } from '@121-service/src/programs/program.entity';

// Mock for ExchangeRateApiService
const mockExchangeRateApiService = {
const mockExchangeRatesApiService = {
retrieveExchangeRate: jest.fn(),
};

describe('ExchangeRateService', () => {
let exchangeRateService: ExchangeRateService;
describe('ExchangeRatesService', () => {
let exchangeRateService: ExchangeRatesService;
let mockExchangeRateRepository: jest.Mocked<Repository<ExchangeRateEntity>>;

beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
ExchangeRateService,
ExchangeRatesService,
{
provide: getRepositoryToken(ExchangeRateEntity),
useValue: {
Expand All @@ -43,14 +43,14 @@ describe('ExchangeRateService', () => {
},
},
{
provide: ExchangeRateApiService,
useValue: mockExchangeRateApiService,
provide: ExchangeRatesApiService,
useValue: mockExchangeRatesApiService,
},
],
}).compile();

exchangeRateService =
moduleRef.get<ExchangeRateService>(ExchangeRateService);
moduleRef.get<ExchangeRatesService>(ExchangeRatesService);
mockExchangeRateRepository = moduleRef.get<
jest.Mocked<Repository<ExchangeRateEntity>>
>(getRepositoryToken(ExchangeRateEntity));
Expand All @@ -65,11 +65,11 @@ describe('ExchangeRateService', () => {
const GBP_closeTime = '2024-01-30';

// Mocking API responses for retrieveExchangeRate
mockExchangeRateApiService.retrieveExchangeRate!.mockResolvedValueOnce({
mockExchangeRatesApiService.retrieveExchangeRate!.mockResolvedValueOnce({
rate: USD_euroExchangeRate.toString(),
closeTime: USD_closeTime,
});
mockExchangeRateApiService.retrieveExchangeRate!.mockResolvedValue({
mockExchangeRatesApiService.retrieveExchangeRate!.mockResolvedValue({
rate: GBP_euroExchangeRate.toString(),
closeTime: GBP_closeTime,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,28 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { ExchangeRateApiService } from '@121-service/src/exchange-rate/exchange-rate.api.service';
import { ExchangeRateEntity } from '@121-service/src/exchange-rate/exchange-rate.entity';
import { GetExchangeRateDto } from '@121-service/src/exchange-rates/dtos/get-exchange-rate.dto';
import { ExchangeRateEntity } from '@121-service/src/exchange-rates/exchange-rate.entity';
import { ExchangeRatesApiService } from '@121-service/src/exchange-rates/exchange-rates.api.service';
import { ProgramEntity } from '@121-service/src/programs/program.entity';

@Injectable()
export class ExchangeRateService {
export class ExchangeRatesService {
@InjectRepository(ExchangeRateEntity)
private exchangeRateRepository: Repository<ExchangeRateEntity>;
@InjectRepository(ProgramEntity)
public programRepository: Repository<ProgramEntity>;

public constructor(
private readonly exchangeRateApiService: ExchangeRateApiService,
private readonly exchangeRateApiService: ExchangeRatesApiService,
) {}

public async getAll(): Promise<GetExchangeRateDto[]> {
return await this.exchangeRateRepository.find({
select: ['currency', 'euroExchangeRate', 'closeTime'],
});
}

public async getAndStoreProgramsExchangeRates(): Promise<void> {
const currencies = await this.getAllProgramCurrencies();

Expand Down
5 changes: 5 additions & 0 deletions services/121-service/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
"path": "/api/health/version",
"params": []
},
{
"method": "get",
"path": "/api/exchange-rates",
"params": []
},
{
"method": "get",
"path": "/api/roles",
Expand Down

0 comments on commit 22f8b7b

Please sign in to comment.