diff --git a/src/modules/investments/instruments/instruments.module.ts b/src/modules/investments/instruments/instruments.module.ts index c0b5a0c..92513cf 100644 --- a/src/modules/investments/instruments/instruments.module.ts +++ b/src/modules/investments/instruments/instruments.module.ts @@ -1,6 +1,5 @@ import { Module } from '@nestjs/common'; -import { TbankAccessService } from '../../../integrations/tbank/tbank-access.service'; import { TbankModule } from '../../../integrations/tbank/tbank.module'; import { AuthModule } from '../../auth/auth.module'; import { PortfolioModule } from '../portfolio/portfolio.module'; @@ -21,7 +20,6 @@ import { StockController } from './stocks/presentation/stock.controller'; controllers: [StockController, BondController], exports: [StockIncomeService, BondIncomeService], providers: [ - { provide: 'TBANK_ACCESS', useExisting: TbankAccessService }, BondIncomeService, BondMarketService, GetStockCompanyUseCase, diff --git a/src/modules/investments/instruments/stocks/application/stock-screener.service.spec.ts b/src/modules/investments/instruments/stocks/application/stock-screener.service.spec.ts index 34b3323..f9c264d 100644 --- a/src/modules/investments/instruments/stocks/application/stock-screener.service.spec.ts +++ b/src/modules/investments/instruments/stocks/application/stock-screener.service.spec.ts @@ -1,3 +1,7 @@ +jest.mock('../../../../../integrations/tbank/tbank.api', () => ({ + TbankAccessService: class {}, +})); + import { NotFoundException, ServiceUnavailableException } from '@nestjs/common'; import type { TbankShare } from '../../../../../integrations/tbank/tbank.api'; @@ -36,7 +40,7 @@ describe('StockScreenerService', () => { client.getAllShares.mockResolvedValue( Array.from({ length: pageSize + 1 }, (_, index) => share(index)), ); - service = new StockScreenerService(access, client as never, logger as never); + service = new StockScreenerService(access as never, client as never, logger as never); }); it('stores the full selected catalog and returns stable non-overlapping pages', async () => { diff --git a/src/modules/investments/instruments/stocks/application/stock-screener.service.ts b/src/modules/investments/instruments/stocks/application/stock-screener.service.ts index e069ff2..a36f680 100644 --- a/src/modules/investments/instruments/stocks/application/stock-screener.service.ts +++ b/src/modules/investments/instruments/stocks/application/stock-screener.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, - Inject, Injectable, NotFoundException, ServiceUnavailableException, @@ -10,6 +9,7 @@ import { randomUUID } from 'node:crypto'; import type { TbankShare } from '../../../../../integrations/tbank/tbank-market.types'; import { StructuredLogger } from '../../../../../common/logging/structured-logger.service'; +import { TbankAccessService } from '../../../../../integrations/tbank/tbank.api'; import { TbankClient } from '../../../../../integrations/tbank/tbank.client'; import { selectStockScreenerInstruments, @@ -47,17 +47,13 @@ type StockScreenerPage = { searchId: string; }; type Snapshot = { asOf: string; expiresAt: number; items: StockScreenerItem[]; revision: string; userId: string }; -type TbankAccess = { - local(userId: string): Promise<{ revision: string }>; - online(userId: string, run: (connection: { revision: string; token: string }) => Promise): Promise; -}; @Injectable() export class StockScreenerService { private readonly snapshots = new Map(); constructor( - @Inject('TBANK_ACCESS') private readonly access: TbankAccess, + private readonly access: TbankAccessService, private readonly client: TbankClient, private readonly logger: StructuredLogger, ) {} diff --git a/src/modules/investments/instruments/stocks/presentation/stock.controller.ts b/src/modules/investments/instruments/stocks/presentation/stock.controller.ts index 3c5e11b..70202bd 100644 --- a/src/modules/investments/instruments/stocks/presentation/stock.controller.ts +++ b/src/modules/investments/instruments/stocks/presentation/stock.controller.ts @@ -91,6 +91,7 @@ export class StockController { @Body(new ValidationPipe({ forbidNonWhitelisted: true, forbidUnknownValues: true, whitelist: true })) criteria: StockScreenerCriteriaDto, ) { + // The global whitelist pipe strips unknown keys before this param pipe runs, so only the raw body can reject them. const allowed = new Set(['countryOfRisk', 'currency', 'maxLot', 'query', 'realExchange', 'sector', 'shareType']); if (Object.keys(request.body as object).some((key) => !allowed.has(key))) { throw new BadRequestException();