All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 15m54s
75 lines
2.6 KiB
TypeScript
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;
|
|
}
|
|
}
|