All files / src/services/db category.db.ts

84.54% Statements 93/110
62.5% Branches 10/16
100% Functions 6/6
88.04% Lines 81/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 932x 2x 2x 2x 2x 2x 2x 2x 2x 2x 7x 7x 7x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 4x 2x 4x 4x 1x 1x 1x 1x 1x 4x 1x 1x 1x 1x 1x 1x 1x       1x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 2x 1x 1x 1x           2x 2x 2x 2x 2x 2x 2x 6x 2x 6x 6x 5x 5x 5x 5x 5x 5x 6x 5x 5x 5x        
// src/services/db/category.db.ts
import { Logger } from 'pino';
import { getPool } from './connection.db';
import { handleDbError } from './errors.db';
 
export interface Category {
  category_id: number;
  name: string;
  created_at: Date;
  updated_at: Date;
}
 
/**
 * Database service for category operations.
 * Categories are predefined grocery item categories (e.g., "Dairy & Eggs", "Fruits & Vegetables").
 */
export class CategoryDbService {
  /**
   * Get all categories ordered by name.
   * This endpoint is used for populating category dropdowns in the UI.
   *
   * @param logger - Pino logger instance
   * @returns Promise resolving to array of categories
   */
  static async getAllCategories(logger: Logger): Promise<Category[]> {
    const pool = getPool();
 
    try {
      const result = await pool.query<Category>(
        `SELECT category_id, name, created_at, updated_at
         FROM public.categories
         ORDER BY name ASC`,
      );
 
      return result.rows;
    } catch (error) {
      handleDbError(error, logger, 'Error fetching all categories', {});
      throw error;
    }
  }
 
  /**
   * Get a specific category by its ID.
   *
   * @param categoryId - The category ID to retrieve
   * @param logger - Pino logger instance
   * @returns Promise resolving to category or null if not found
   */
  static async getCategoryById(categoryId: number, logger: Logger): Promise<Category | null> {
    const pool = getPool();
 
    try {
      const result = await pool.query<Category>(
        `SELECT category_id, name, created_at, updated_at
         FROM public.categories
         WHERE category_id = $1`,
        [categoryId],
      );
 
      return result.rows[0] || null;
    } catch (error) {
      handleDbError(error, logger, 'Error fetching category by ID', { categoryId });
      throw error;
    }
  }

  /**
   * Get a category by its name (case-insensitive).
   * This is primarily used for migration support to allow clients to lookup category IDs by name.
   *
   * @param name - The category name to search for
   * @param logger - Pino logger instance
   * @returns Promise resolving to category or null if not found
   */
  static async getCategoryByName(name: string, logger: Logger): Promise<Category | null> {
    const pool = getPool();
 
    try {
      const result = await pool.query<Category>(
        `SELECT category_id, name, created_at, updated_at
         FROM public.categories
         WHERE LOWER(name) = LOWER($1)`,
        [name],
      );
 
      return result.rows[0] || null;
    } catch (error) {
      handleDbError(error, logger, 'Error fetching category by name', { name });
      throw error;
    }
  }
}