feat: Update AI service to use new Google Generative AI SDK
Some checks are pending
Deploy to Test Environment / deploy-to-test (push) Has started running

- Refactored AIService to integrate with the latest GoogleGenAI SDK, updating the generateContent method signature and response handling.
- Adjusted error handling and logging for improved clarity and consistency.
- Enhanced mock implementations in tests to align with the new SDK structure.
refactor: Modify Admin DB service to use Profile type
- Updated AdminRepository to replace User type with Profile in relevant methods.
- Enhanced test cases to utilize mock factories for creating Profile and AdminUserView objects.
fix: Improve error handling in BudgetRepository
- Implemented type-safe checks for PostgreSQL error codes to enhance error handling in createBudget method.
test: Refactor Deals DB tests for type safety
- Updated DealsRepository tests to use Pool type for mock instances, ensuring type safety.
chore: Add new mock factories for testing
- Introduced mock factories for UserWithPasswordHash, Profile, WatchedItemDeal, LeaderboardUser, and UnmatchedFlyerItem to streamline test data creation.
style: Clean up queue service tests
- Refactored queue service tests to improve readability and maintainability, including better handling of mock worker instances.
docs: Update types to include UserWithPasswordHash
- Added UserWithPasswordHash interface to types for better clarity on user authentication data structure.
chore: Remove deprecated Google AI SDK references
- Updated code and documentation to reflect the migration to the new Google Generative AI SDK, removing references to the deprecated SDK.
This commit is contained in:
2025-12-14 17:14:44 -08:00
parent eb0e183f61
commit f73b1422ab
42 changed files with 530 additions and 365 deletions

View File

@@ -149,10 +149,10 @@ router.get('/stats/daily', async (req, res, next: NextFunction) => {
});
router.post('/corrections/:id/approve', validateRequest(numericIdParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
type ApproveCorrectionRequest = z.infer<typeof updateCorrectionSchema>;
const { params } = req as unknown as ApproveCorrectionRequest;
// Apply ADR-003 pattern for type safety
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
try {
await db.adminRepo.approveCorrection(params.id, req.log);
await db.adminRepo.approveCorrection(params.id, req.log); // params.id is now safely typed as number
res.status(200).json({ message: 'Correction approved successfully.' });
} catch (error) {
next(error);
@@ -160,10 +160,10 @@ router.post('/corrections/:id/approve', validateRequest(numericIdParamSchema('id
});
router.post('/corrections/:id/reject', validateRequest(numericIdParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
type RejectCorrectionRequest = z.infer<typeof updateCorrectionSchema>;
const { params } = req as unknown as RejectCorrectionRequest;
// Apply ADR-003 pattern for type safety
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
try {
await db.adminRepo.rejectCorrection(params.id, req.log);
await db.adminRepo.rejectCorrection(params.id, req.log); // params.id is now safely typed as number
res.status(200).json({ message: 'Correction rejected successfully.' });
} catch (error) {
next(error);
@@ -171,8 +171,8 @@ router.post('/corrections/:id/reject', validateRequest(numericIdParamSchema('id'
});
router.put('/corrections/:id', validateRequest(updateCorrectionSchema), async (req: Request, res: Response, next: NextFunction) => {
type UpdateCorrectionRequest = z.infer<typeof updateCorrectionSchema>;
const { params, body } = req as unknown as UpdateCorrectionRequest;
// Apply ADR-003 pattern for type safety
const { params, body } = req as unknown as z.infer<typeof updateCorrectionSchema>;
try {
const updatedCorrection = await db.adminRepo.updateSuggestedCorrection(params.id, body.suggested_value, req.log);
res.status(200).json(updatedCorrection);
@@ -182,8 +182,8 @@ router.put('/corrections/:id', validateRequest(updateCorrectionSchema), async (r
});
router.put('/recipes/:id/status', validateRequest(updateRecipeStatusSchema), async (req: Request, res: Response, next: NextFunction) => {
type UpdateRecipeStatusRequest = z.infer<typeof updateRecipeStatusSchema>;
const { params, body } = req as unknown as UpdateRecipeStatusRequest;
// Apply ADR-003 pattern for type safety
const { params, body } = req as unknown as z.infer<typeof updateRecipeStatusSchema>;
try {
const updatedRecipe = await db.adminRepo.updateRecipeStatus(params.id, body.status, req.log); // This is still a standalone function in admin.db.ts
res.status(200).json(updatedRecipe);
@@ -193,6 +193,7 @@ router.put('/recipes/:id/status', validateRequest(updateRecipeStatusSchema), asy
});
router.post('/brands/:id/logo', validateRequest(numericIdParamSchema('id')), upload.single('logoImage'), requireFileUpload('logoImage'), async (req: Request, res: Response, next: NextFunction) => {
// Apply ADR-003 pattern for type safety
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
try {
// Although requireFileUpload middleware should ensure the file exists,
@@ -223,8 +224,10 @@ router.get('/unmatched-items', async (req, res, next: NextFunction) => {
* DELETE /api/admin/recipes/:recipeId - Admin endpoint to delete any recipe.
*/
router.delete('/recipes/:recipeId', validateRequest(numericIdParamSchema('recipeId')), async (req: Request, res: Response, next: NextFunction) => {
// Define schema locally to simplify type inference
const schema = numericIdParamSchema('recipeId');
const adminUser = req.user as UserProfile;
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
const { params } = req as unknown as z.infer<typeof schema>;
try {
// The isAdmin flag bypasses the ownership check in the repository method.
await db.recipeRepo.deleteRecipe(params.recipeId, adminUser.user_id, true, req.log);
@@ -238,7 +241,9 @@ router.delete('/recipes/:recipeId', validateRequest(numericIdParamSchema('recipe
* DELETE /api/admin/flyers/:flyerId - Admin endpoint to delete a flyer and its items.
*/
router.delete('/flyers/:flyerId', validateRequest(numericIdParamSchema('flyerId')), async (req: Request, res: Response, next: NextFunction) => {
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
// Define schema locally to simplify type inference
const schema = numericIdParamSchema('flyerId');
const { params } = req as unknown as z.infer<typeof schema>;
try {
await db.flyerRepo.deleteFlyer(params.flyerId, req.log);
res.status(204).send();
@@ -248,8 +253,8 @@ router.delete('/flyers/:flyerId', validateRequest(numericIdParamSchema('flyerId'
});
router.put('/comments/:id/status', validateRequest(updateCommentStatusSchema), async (req: Request, res: Response, next: NextFunction) => {
type UpdateCommentStatusRequest = z.infer<typeof updateCommentStatusSchema>;
const { params, body } = req as unknown as UpdateCommentStatusRequest;
// Apply ADR-003 pattern for type safety
const { params, body } = req as unknown as z.infer<typeof updateCommentStatusSchema>;
try {
const updatedComment = await db.adminRepo.updateRecipeCommentStatus(params.id, body.status, req.log); // This is still a standalone function in admin.db.ts
res.status(200).json(updatedComment);
@@ -268,8 +273,8 @@ router.get('/users', async (req, res, next: NextFunction) => {
});
router.get('/activity-log', validateRequest(activityLogSchema), async (req: Request, res: Response, next: NextFunction) => {
type ActivityLogRequest = z.infer<typeof activityLogSchema>;
const { query } = req as unknown as ActivityLogRequest;
// Apply ADR-003 pattern for type safety
const { query } = req as unknown as z.infer<typeof activityLogSchema>;
try {
const logs = await db.adminRepo.getActivityLog(query.limit, query.offset, req.log);
res.json(logs);
@@ -279,7 +284,8 @@ router.get('/activity-log', validateRequest(activityLogSchema), async (req: Requ
});
router.get('/users/:id', validateRequest(uuidParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
const { params } = req as z.infer<ReturnType<typeof uuidParamSchema>>;
// Apply ADR-003 pattern for type safety
const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
try {
const user = await db.userRepo.findUserProfileById(params.id, req.log);
res.json(user);
@@ -289,8 +295,8 @@ router.get('/users/:id', validateRequest(uuidParamSchema('id')), async (req: Req
});
router.put('/users/:id', validateRequest(updateUserRoleSchema), async (req: Request, res: Response, next: NextFunction) => {
type UpdateUserRoleRequest = z.infer<typeof updateUserRoleSchema>;
const { params, body } = req as unknown as UpdateUserRoleRequest;
// Apply ADR-003 pattern for type safety
const { params, body } = req as unknown as z.infer<typeof updateUserRoleSchema>;
try {
const updatedUser = await db.adminRepo.updateUserRole(params.id, body.role, req.log);
res.json(updatedUser);
@@ -302,7 +308,8 @@ router.put('/users/:id', validateRequest(updateUserRoleSchema), async (req: Requ
router.delete('/users/:id', validateRequest(uuidParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
const adminUser = req.user as UserProfile;
const { params } = req as z.infer<ReturnType<typeof uuidParamSchema>>;
// Apply ADR-003 pattern for type safety
const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
try {
if (adminUser.user.user_id === params.id) {
throw new ValidationError([], 'Admins cannot delete their own account.');
@@ -360,8 +367,10 @@ router.post('/trigger/analytics-report', async (req: Request, res: Response, nex
* This is triggered by an admin after they have verified the flyer processing was successful.
*/
router.post('/flyers/:flyerId/cleanup', validateRequest(numericIdParamSchema('flyerId')), async (req: Request, res: Response, next: NextFunction) => {
// Define schema locally to simplify type inference
const schema = numericIdParamSchema('flyerId');
const adminUser = req.user as UserProfile;
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
const { params } = req as unknown as z.infer<typeof schema>;
logger.info(`[Admin] Manual trigger for flyer file cleanup received from user: ${adminUser.user_id} for flyer ID: ${params.flyerId}`);
// Enqueue the cleanup job. The worker will handle the file deletion.