Skip to content

Feature Implementation Plan: disable-coffee-bean

📋 Todo Checklist

  • [ ] Add DISABLED status to CoffeeBeanStatus enum.
  • [ ] Update CoffeeBeanRepository to exclude disabled beans from API queries.
  • [ ] Update CrawlUrlRepository to exclude URLs of disabled beans from crawling.
  • [ ] Update the Admin CRUD controllers so that beans can be disabled/enabled from the UI.
  • [ ] Create a Symfony console command to disable a bean.
  • [ ] Write tests for the new functionality.
  • [ ] Final Review and Testing.

🔍 Analysis & Investigation

Codebase Structure

  • Entity: src/Entity/CoffeeBean.php is the core entity. It already has a status field linked to the CoffeeBeanStatus enum.
  • Enum: src/Enum/CoffeeBeanStatus.php defines the possible states for a CoffeeBean. This is the ideal place to add a DISABLED state.
  • Repository: src/Repository/CoffeeBeanRepository.php is responsible for all database queries related to CoffeeBean entities. Key methods like findByRequest and findDtoById must be updated to filter out disabled beans.
  • Crawler Logic: src/Repository/CrawlUrlRepository.php contains the method findUrlsToCrawl, which is the choke point for selecting which URLs get scheduled for crawling. This is the best place to prevent re-crawling.
  • Admin Interface: There is no dedicated admin UI. A Symfony console command is the most appropriate and straightforward way to provide administrators with the ability to disable a bean.

Current Architecture

The system uses a standard Symfony setup with Doctrine ORM. API responses are driven by repositories that fetch entities and map them to DTOs. A Symfony Messenger-based queue system handles crawling, with CrawlerScheduler dispatching messages and CrawlUrlRepository providing the URLs to be crawled.

The most effective and non-destructive way to "disable" a bean is to extend the existing status system. Adding a DISABLED status to the CoffeeBeanStatus enum is a clean, logical extension of the current architecture. This approach preserves all data for historical purposes while effectively hiding the disabled entity from public view and preventing it from being re-processed.

Dependencies & Integration Points

  • Doctrine ORM: The repositories will need updated DQL queries to filter by the new status.
  • Symfony Console: To create the admin command for disabling beans.
  • API Endpoints: All public-facing API endpoints that return CoffeeBean data will be implicitly affected by the repository changes.

Considerations & Challenges

  • Comprehensive Filtering: It is critical to identify all public-facing query methods in CoffeeBeanRepository and ensure they are updated to exclude disabled beans. Missing one could lead to data leakage.
  • Crawler Exclusion: The logic to prevent re-crawling must be robust. The CrawlUrlRepository should be modified to join the CoffeeBean entity and check its status.
  • Idempotency: The disable command should be idempotent. Running it multiple times on the same bean should have no adverse effects.

📝 Implementation Plan

Prerequisites

  • None. This plan builds on existing application structure.

Step-by-Step Implementation

  1. Step 1: Extend CoffeeBeanStatus Enum

    • File to modify: src/Enum/CoffeeBeanStatus.php
    • Changes needed: Add a new DISABLED case to the enum.
      enum CoffeeBeanStatus: string
      {
          // ... existing cases
          case DISABLED = 'disabled';
      }
      
  2. Step 2: Update CoffeeBeanRepository to Exclude Disabled Beans

    • File to modify: src/Repository/CoffeeBeanRepository.php
    • Changes needed: Add a condition to all public query-building methods to filter out beans with the DISABLED status.
      • In executeCoffeeBeanQuery (which powers findByRequest), add the following andWhere clause to both the $idsQb and $countQb query builders:
        $qb->andWhere('cb.status != :disabledStatus OR cb.status IS NULL')
           ->setParameter('disabledStatus', CoffeeBeanStatus::DISABLED);
        
      • In findDtoById, add the same andWhere clause to the query builder to ensure a disabled bean cannot be fetched directly via the API.
      • In findSimilarityCandidates, add the same andWhere clause.
  3. Step 3: Update CrawlUrlRepository to Exclude Disabled URLs

    • File to modify: src/Repository/CrawlUrlRepository.php
    • Changes needed: Modify the findUrlsToCrawl method to join the CoffeeBean entity and exclude URLs where the associated bean is disabled.
      // Inside findUrlsToCrawl method
      $qb = $this->createQueryBuilder('cu')
                 ->leftJoin('cu.coffeeBean', 'cb') // Add this join
                 ->where('cu.roasterCrawlConfig = :config')
                 // Add this condition
                 ->andWhere('cb.status IS NULL OR cb.status != :disabledStatus')
                 ->setParameter('config', $config)
                 ->setParameter('disabledStatus', CoffeeBeanStatus::DISABLED);
      
  4. Step 4: Create Admin Command to Disable a CoffeeBean

    • File to create: src/Command/DisableCoffeeBeanCommand.php
    • Changes needed:
      • Create a new Symfony command named app:coffee-bean:disable.
      • The command should accept one argument: the UUID of the CoffeeBean to disable.
      • Inject the CoffeeBeanRepository and EntityManagerInterface.
      • In the execute method:
        • Find the CoffeeBean by its ID.
        • If not found, output an error.
        • Set the bean's status: $coffeeBean->setStatus(CoffeeBeanStatus::DISABLED);
        • Persist the change: $this->entityManager->flush();
        • Output a success message.

Testing Strategy

  1. Unit/Integration Tests:
    • CoffeeBeanRepositoryTest:
      • Create a disabled CoffeeBean in the test database.
      • Assert that findByRequest and findDtoById do not return the disabled bean.
    • CrawlUrlRepositoryTest:
      • Create a CrawlUrl linked to a disabled CoffeeBean.
      • Assert that findUrlsToCrawl does not return this URL.
    • DisableCoffeeBeanCommandTest:
      • Write a command test that executes app:coffee-bean:disable.
      • Assert that the command runs successfully and that the CoffeeBean's status is updated to DISABLED in the database.
  2. Manual Testing:
    • Run the app:coffee-bean:disable command on a known CoffeeBean ID.
    • Verify via the API that the disabled bean no longer appears in lists or when queried by ID.
    • Trigger the crawler scheduler and verify in the logs that the URL associated with the disabled bean is not scheduled for crawling.

🎯 Success Criteria

  • An administrator can successfully disable a CoffeeBean using the UI.
  • Disabled CoffeeBean entities are no longer returned by any public-facing API endpoints.
  • The CrawlUrl associated with a disabled CoffeeBean is no longer included in scheduled crawls.
  • No automation changes the data for disabled beans.
  • All data related to the disabled bean is preserved in the database.
  • The new functionality is covered by automated tests.