Files
flyer-crawler.projectium.com/src/services/flyerPersistenceService.server.ts
Torben Sorensen 4e22213cd1
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 15m54s
all the new shiny things
2026-01-11 02:04:52 -08:00

75 lines
2.6 KiB
TypeScript

// src/services/flyerPersistenceService.server.ts
import type { Logger } from 'pino';
import type { PoolClient } from 'pg';
import { withTransaction as defaultWithTransaction } from './db/connection.db';
import { createFlyerAndItems } from './db/flyer.db';
import { AdminRepository } from './db/admin.db';
import { GamificationRepository } from './db/gamification.db';
import { cacheService } from './cacheService.server';
import type { FlyerInsert, FlyerItemInsert, Flyer } from '../types';
export type WithTransactionFn = <T>(callback: (client: PoolClient) => Promise<T>) => Promise<T>;
export class FlyerPersistenceService {
private withTransaction: WithTransactionFn;
constructor(withTransactionFn: WithTransactionFn = defaultWithTransaction) {
this.withTransaction = withTransactionFn;
}
/**
* Allows replacing the withTransaction function at runtime.
* This is primarily used for testing to inject mock implementations.
* Pass null to reset to the default implementation.
* @internal
*/
_setWithTransaction(fn: WithTransactionFn | null): void {
this.withTransaction = fn ?? defaultWithTransaction;
}
/**
* Saves the flyer and its items to the database within a transaction.
* Also logs the activity and invalidates related cache entries.
*/
async saveFlyer(
flyerData: FlyerInsert,
itemsForDb: FlyerItemInsert[],
userId: string | undefined,
logger: Logger,
): Promise<Flyer> {
const flyer = await this.withTransaction(async (client) => {
const { flyer, items } = await createFlyerAndItems(flyerData, itemsForDb, logger, client);
logger.info(
`Successfully processed flyer: ${flyer.file_name} (ID: ${flyer.flyer_id}) with ${items.length} items.`,
);
// Log activity if a user uploaded it
if (userId) {
const transactionalAdminRepo = new AdminRepository(client);
await transactionalAdminRepo.logActivity(
{
userId: userId,
action: 'flyer_processed',
displayText: `Processed a new flyer for ${flyerData.store_name}.`,
details: { flyerId: flyer.flyer_id, storeName: flyerData.store_name },
},
logger,
);
// Award 'First-Upload' achievement
const gamificationRepo = new GamificationRepository(client);
await gamificationRepo.awardAchievement(userId, 'First-Upload', logger);
}
return flyer;
});
// Invalidate flyer list cache after successful creation (fire-and-forget)
cacheService.invalidateFlyers(logger).catch(() => {
// Error already logged in invalidateFlyers
});
return flyer;
}
}