From e56060594a11657adf5e36e12548834708f26628 Mon Sep 17 00:00:00 2001 From: pricelesschap Date: Thu, 5 Jun 2025 17:01:32 +0100 Subject: [PATCH] feat(currency-hub): add search and pagination for currency data --- .../currency-hub/currency-hub.controller.ts | 6 ++++ .../currency-hub/currency-hub.service.spec.ts | 9 ++++++ .../src/currency-hub/currency-hub.service.ts | 29 +++++++++++++++++++ .../currency-hub/dto/search-currency.dto.ts | 20 +++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 backend/src/currency-hub/dto/search-currency.dto.ts diff --git a/backend/src/currency-hub/currency-hub.controller.ts b/backend/src/currency-hub/currency-hub.controller.ts index 1252f08..d3de5ee 100644 --- a/backend/src/currency-hub/currency-hub.controller.ts +++ b/backend/src/currency-hub/currency-hub.controller.ts @@ -1,3 +1,4 @@ +import { SearchCurrencyDto } from './dto/search-currency.dto'; import { Controller, Get, @@ -19,6 +20,11 @@ import { QueryCurrencyHubDto } from './dto/query-currency-hub.dto'; export class CurrencyHubController { constructor(private readonly currencyHubService: CurrencyHubService) {} + @Get('search') +searchWithPagination(@Query() dto: SearchCurrencyDto) { + return this.currencyHubService.searchAndPaginateCurrencies(dto); +} + @Post() create(@Body() createCurrencyHubDto: CreateCurrencyHubDto) { return this.currencyHubService.create(createCurrencyHubDto); diff --git a/backend/src/currency-hub/currency-hub.service.spec.ts b/backend/src/currency-hub/currency-hub.service.spec.ts index 6c301d8..3f028d2 100644 --- a/backend/src/currency-hub/currency-hub.service.spec.ts +++ b/backend/src/currency-hub/currency-hub.service.spec.ts @@ -407,4 +407,13 @@ describe('CurrencyHubService', () => { ).rejects.toThrow(NotFoundException); }); }); + + describe('searchAndPaginateCurrencies', () => { + it('returns filtered and paginated results', async () => { + const result = await service.searchAndPaginateCurrencies({ search: 'usd', page: 1, limit: 5 }); + expect(result).toHaveProperty('data'); + expect(result).toHaveProperty('total'); + expect(Array.isArray(result.data)).toBe(true); + }); +}); }); diff --git a/backend/src/currency-hub/currency-hub.service.ts b/backend/src/currency-hub/currency-hub.service.ts index 3ea23ea..3c779bf 100644 --- a/backend/src/currency-hub/currency-hub.service.ts +++ b/backend/src/currency-hub/currency-hub.service.ts @@ -1,4 +1,5 @@ // src/currency-hub/currency-hub.service.ts +import { SearchCurrencyDto } from './dto/search-currency.dto'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, Between, FindOptionsWhere } from 'typeorm'; @@ -238,4 +239,32 @@ export class CurrencyHubService { // For demonstration purposes, we'll just log a message console.log('Updating exchange rates from external sources...'); } + + // Added Filter + Pagination + async searchAndPaginateCurrencies( + dto: SearchCurrencyDto, +): Promise<{ + data: CurrencyHub[]; + total: number; + page: number; + limit: number; +}> { + const { search, page = 1, limit = 10 } = dto; + + const qb = this.currencyHubRepository.createQueryBuilder('currency'); + + if (search) { + qb.where('currency.baseCurrencyCode ILIKE :search', { search: `%${search}%` }) + .orWhere('currency.targetCurrencyCode ILIKE :search', { search: `%${search}%` }) + .orWhere('currency.provider ILIKE :search', { search: `%${search}%` }); + } + + const [data, total] = await qb + .skip((page - 1) * limit) + .take(limit) + .orderBy('currency.createdAt', 'DESC') + .getManyAndCount(); + + return { data, total, page, limit }; +} } diff --git a/backend/src/currency-hub/dto/search-currency.dto.ts b/backend/src/currency-hub/dto/search-currency.dto.ts new file mode 100644 index 0000000..9473512 --- /dev/null +++ b/backend/src/currency-hub/dto/search-currency.dto.ts @@ -0,0 +1,20 @@ +import { IsOptional, IsString, IsNumber, Min } from 'class-validator'; +import { Type } from 'class-transformer'; + +export class SearchCurrencyDto { + @IsOptional() + @IsString() + search?: string; + + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + page?: number = 1; + + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + limit?: number = 10; +}