feat: Implement deals repository and routes for fetching best watched item prices
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled

- Added a new DealsRepository class to interact with the database for fetching the best sale prices of watched items.
- Created a new route `/api/users/deals/best-watched-prices` to handle requests for the best prices of items the authenticated user is watching.
- Enhanced logging in the FlyerDataTransformer and FlyerProcessingService for better traceability.
- Updated tests to ensure proper logging and functionality in the FlyerProcessingService.
- Refactored logger client to support structured logging for better consistency across the application.
This commit is contained in:
2025-12-13 20:02:18 -08:00
parent 2affda25dc
commit 424cbaf0d4
22 changed files with 409 additions and 241 deletions

View File

@@ -0,0 +1,61 @@
// src/services/db/deals.repository.ts
import { getPool } from './connection.db';
import { WatchedItemDeal } from '../../types';
import { Pool } from 'pg';
export class DealsRepository {
private pool: Pool;
constructor() {
this.pool = getPool();
}
/**
* Finds the best current sale price for each of a user's watched items.
*
* @param userId - The ID of the user whose watched items are being checked.
* @returns A promise that resolves to an array of WatchedItemDeal objects.
*/
async findBestPricesForWatchedItems(userId: string): Promise<WatchedItemDeal[]> {
const query = `
WITH UserWatchedItems AS (
-- Select all items the user is watching
SELECT master_item_id FROM watched_items WHERE user_id = $1
),
RankedPrices AS (
-- Find all current sale prices for those items and rank them
SELECT
fi.master_item_id,
mgi.name AS item_name,
fi.price_in_cents,
s.name AS store_name,
f.flyer_id,
f.valid_to,
ROW_NUMBER() OVER(PARTITION BY fi.master_item_id ORDER BY fi.price_in_cents ASC, f.valid_to DESC) as rn
FROM flyer_items fi
JOIN flyers f ON fi.flyer_id = f.flyer_id
JOIN stores s ON f.store_id = s.store_id
JOIN master_grocery_items mgi ON fi.master_item_id = mgi.master_grocery_item_id
WHERE
fi.master_item_id IN (SELECT master_item_id FROM UserWatchedItems)
AND f.valid_to >= CURRENT_DATE -- Only consider active flyers
AND fi.price_in_cents IS NOT NULL
)
-- Select only the #1 ranked (lowest) price for each item
SELECT
master_item_id,
item_name,
price_in_cents AS best_price_in_cents,
store_name,
flyer_id,
valid_to
FROM RankedPrices
WHERE rn = 1
ORDER BY item_name;
`;
const { rows } = await this.pool.query(query, [userId]);
return rows;
}
}
export const dealsRepo = new DealsRepository();