From 22f8b7bd988ffaf7a2882b879417df5c979de0ad Mon Sep 17 00:00:00 2001 From: RubenGeo <34537157+RubenGeo@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:24:15 +0100 Subject: [PATCH] Get exchange rates (#6392) * 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 --------- Co-authored-by: Ruben Co-authored-by: Peter Smallenbroek --- services/121-service/module-dependencies.md | 4 +-- .../121-service/src/cronjob/cronjob.module.ts | 4 +-- .../src/cronjob/cronjob.service.ts | 6 ++-- .../dtos/get-exchange-rate.dto.ts | 16 ++++++++++ .../exchange-rate.entity.ts | 0 .../exchange-rates.api.service.ts} | 2 +- .../exchange-rates.controller.ts | 32 +++++++++++++++++++ .../exchange-rates.module.ts} | 15 +++++---- .../exchange-rates.service.spec.ts} | 24 +++++++------- .../exchange-rates.service.ts} | 15 ++++++--- services/121-service/swagger.json | 5 +++ 11 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 services/121-service/src/exchange-rates/dtos/get-exchange-rate.dto.ts rename services/121-service/src/{exchange-rate => exchange-rates}/exchange-rate.entity.ts (100%) rename services/121-service/src/{exchange-rate/exchange-rate.api.service.ts => exchange-rates/exchange-rates.api.service.ts} (97%) create mode 100644 services/121-service/src/exchange-rates/exchange-rates.controller.ts rename services/121-service/src/{exchange-rate/exchange-rate.module.ts => exchange-rates/exchange-rates.module.ts} (50%) rename services/121-service/src/{exchange-rate/exchange-rate.service.spec.ts => exchange-rates/exchange-rates.service.spec.ts} (78%) rename services/121-service/src/{exchange-rate/exchange-rate.service.ts => exchange-rates/exchange-rates.service.ts} (75%) diff --git a/services/121-service/module-dependencies.md b/services/121-service/module-dependencies.md index f813cade61..9dbde5d174 100644 --- a/services/121-service/module-dependencies.md +++ b/services/121-service/module-dependencies.md @@ -2,8 +2,8 @@ ```mermaid graph LR - CronjobModule-->ExchangeRateModule - ExchangeRateModule-->UserModule + CronjobModule-->ExchangeRatesModule + ExchangeRatesModule-->UserModule UserModule-->EmailsModule MessageTemplateModule-->UserModule MessageTemplateModule-->ProgramAttributesModule diff --git a/services/121-service/src/cronjob/cronjob.module.ts b/services/121-service/src/cronjob/cronjob.module.ts index 77a44d41f8..08bad28745 100644 --- a/services/121-service/src/cronjob/cronjob.module.ts +++ b/services/121-service/src/cronjob/cronjob.module.ts @@ -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], diff --git a/services/121-service/src/cronjob/cronjob.service.ts b/services/121-service/src/cronjob/cronjob.service.ts index 9fe01a076f..3ac9b4fdb6 100644 --- a/services/121-service/src/cronjob/cronjob.service.ts +++ b/services/121-service/src/cronjob/cronjob.service.ts @@ -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'; @@ -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( @@ -90,7 +90,7 @@ export class CronjobService { public async getDailyExchangeRates(): Promise { console.info('CronjobService - Started: getDailyExchangeRates'); - await this.exchangeRateService.getAndStoreProgramsExchangeRates(); + await this.exchangeRatesService.getAndStoreProgramsExchangeRates(); console.info('CronjobService - Complete: getDailyExchangeRates'); } diff --git a/services/121-service/src/exchange-rates/dtos/get-exchange-rate.dto.ts b/services/121-service/src/exchange-rates/dtos/get-exchange-rate.dto.ts new file mode 100644 index 0000000000..0a5500815e --- /dev/null +++ b/services/121-service/src/exchange-rates/dtos/get-exchange-rate.dto.ts @@ -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; +} diff --git a/services/121-service/src/exchange-rate/exchange-rate.entity.ts b/services/121-service/src/exchange-rates/exchange-rate.entity.ts similarity index 100% rename from services/121-service/src/exchange-rate/exchange-rate.entity.ts rename to services/121-service/src/exchange-rates/exchange-rate.entity.ts diff --git a/services/121-service/src/exchange-rate/exchange-rate.api.service.ts b/services/121-service/src/exchange-rates/exchange-rates.api.service.ts similarity index 97% rename from services/121-service/src/exchange-rate/exchange-rate.api.service.ts rename to services/121-service/src/exchange-rates/exchange-rates.api.service.ts index f95d7febce..bed75ae4bd 100644 --- a/services/121-service/src/exchange-rate/exchange-rate.api.service.ts +++ b/services/121-service/src/exchange-rates/exchange-rates.api.service.ts @@ -11,7 +11,7 @@ interface ExchangeRateApiResponse { } @Injectable() -export class ExchangeRateApiService { +export class ExchangeRatesApiService { public constructor(private readonly httpService: CustomHttpService) {} public async retrieveExchangeRate( diff --git a/services/121-service/src/exchange-rates/exchange-rates.controller.ts b/services/121-service/src/exchange-rates/exchange-rates.controller.ts new file mode 100644 index 0000000000..5d062ebb4d --- /dev/null +++ b/services/121-service/src/exchange-rates/exchange-rates.controller.ts @@ -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 { + return this.exchangeRateService.getAll(); + } +} diff --git a/services/121-service/src/exchange-rate/exchange-rate.module.ts b/services/121-service/src/exchange-rates/exchange-rates.module.ts similarity index 50% rename from services/121-service/src/exchange-rate/exchange-rate.module.ts rename to services/121-service/src/exchange-rates/exchange-rates.module.ts index 4b3097ef91..e5d8c0f246 100644 --- a/services/121-service/src/exchange-rate/exchange-rate.module.ts +++ b/services/121-service/src/exchange-rates/exchange-rates.module.ts @@ -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'; @@ -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 {} diff --git a/services/121-service/src/exchange-rate/exchange-rate.service.spec.ts b/services/121-service/src/exchange-rates/exchange-rates.service.spec.ts similarity index 78% rename from services/121-service/src/exchange-rate/exchange-rate.service.spec.ts rename to services/121-service/src/exchange-rates/exchange-rates.service.spec.ts index deefeaa336..fd4d0e5a7e 100644 --- a/services/121-service/src/exchange-rate/exchange-rate.service.spec.ts +++ b/services/121-service/src/exchange-rates/exchange-rates.service.spec.ts @@ -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>; beforeEach(async () => { const moduleRef = await Test.createTestingModule({ providers: [ - ExchangeRateService, + ExchangeRatesService, { provide: getRepositoryToken(ExchangeRateEntity), useValue: { @@ -43,14 +43,14 @@ describe('ExchangeRateService', () => { }, }, { - provide: ExchangeRateApiService, - useValue: mockExchangeRateApiService, + provide: ExchangeRatesApiService, + useValue: mockExchangeRatesApiService, }, ], }).compile(); exchangeRateService = - moduleRef.get(ExchangeRateService); + moduleRef.get(ExchangeRatesService); mockExchangeRateRepository = moduleRef.get< jest.Mocked> >(getRepositoryToken(ExchangeRateEntity)); @@ -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, }); diff --git a/services/121-service/src/exchange-rate/exchange-rate.service.ts b/services/121-service/src/exchange-rates/exchange-rates.service.ts similarity index 75% rename from services/121-service/src/exchange-rate/exchange-rate.service.ts rename to services/121-service/src/exchange-rates/exchange-rates.service.ts index 9f696a5ab5..86fcffc822 100644 --- a/services/121-service/src/exchange-rate/exchange-rate.service.ts +++ b/services/121-service/src/exchange-rates/exchange-rates.service.ts @@ -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; @InjectRepository(ProgramEntity) public programRepository: Repository; public constructor( - private readonly exchangeRateApiService: ExchangeRateApiService, + private readonly exchangeRateApiService: ExchangeRatesApiService, ) {} + public async getAll(): Promise { + return await this.exchangeRateRepository.find({ + select: ['currency', 'euroExchangeRate', 'closeTime'], + }); + } + public async getAndStoreProgramsExchangeRates(): Promise { const currencies = await this.getAllProgramCurrencies(); diff --git a/services/121-service/swagger.json b/services/121-service/swagger.json index 85c4ac7fc9..4bac83f0a3 100644 --- a/services/121-service/swagger.json +++ b/services/121-service/swagger.json @@ -9,6 +9,11 @@ "path": "/api/health/version", "params": [] }, + { + "method": "get", + "path": "/api/exchange-rates", + "params": [] + }, { "method": "get", "path": "/api/roles",