Skip to content

feat: support service overrides from dependent applications #806

@hartym

Description

@hartym

Summary

Enable applications to override services defined by their dependencies. This unblocks the http_cache → http_client architecture (#784), where http_cache needs to override http_client's transport service to inject caching behavior.

Context

Current Problem:
Applications cannot override services from their dependencies. When app A depends on app B and tries to override B's services, validation fails with:

ValueError: Service with name <service> is not defined, but override flag is set.

Root Cause:
Validation happens too early during ServiceDefinitionCollection.__iter__ (harp/services/models.py:176-177), only checking services within a single YAML file rather than the full container context.

Current Workaround:
http_cache manually modifies http_client's service definition after loading (see harp_apps/http_cache/app.py:22-38).

Input

Service Definition YAML:

# http_cache/services.yml overriding http_client service
services:
  - name: "http_client"
    override: true  # or override: "merge"
    type: "httpx.AsyncClient"
    defaults:
      transport: !ref "http_cache.transport"

Output and Testing Scenarios

Expected Behavior:

  1. Happy Path: http_cache overrides http_client service → cache transport is injected successfully
  2. Chained Override: A → B → C where both A and B override same service → A's override takes precedence
  3. Sibling Conflict: A → C ← B where both try to override same service → clear error with dependency context
  4. Invalid Override: App tries to override non-existent service → helpful error message

Possible Implementation

Chosen Approach: Container-aware validation

Modify ServiceDefinitionCollection.__iter__ to accept an optional container parameter for validation context:

def __iter__(self, container=None):
    # When validating override flag:
    if service.override:
        # Check both collection AND container
        if service.name not in _map and (not container or service.name not in container):
            raise ValueError(f"Service {service.name} not defined...")

Benefits:

  • Minimal code change
  • Backward compatible (container parameter is optional)
  • Preserves existing validation flow

Override Semantics

Two modes will be supported:

  1. override: true (replacement mode)

    • Behavior to be determined after documenting current implementation
    • Either complete replacement or type & args only
  2. override: "merge" (merge mode)

    • Merges arguments and defaults with base service
    • Override values win in conflicts
    • Can change type if explicitly specified
    • Deep merge for nested dictionaries

Validation Rules

Override Direction: Unidirectional (dependent → dependency only)

  • ✅ http_cache can override http_client services
  • ❌ http_client cannot override http_cache services

Conflict Resolution:

  • Chained overrides allowed (A → B → C)
  • Sibling conflicts raise error with dependency graph context

Current Challenges

  1. Need to document existing override behavior before finalizing override: true semantics
  2. Container reference must be passed through the loading chain
  3. Error messages need to include dependency graph context for clarity

Acceptance Criteria

  • Document current override semantics in codebase
  • Implement container-aware validation in ServiceDefinitionCollection
  • http_cache can override http_client service without workaround
  • Chained overrides work correctly
  • Sibling conflicts raise clear errors with context
  • Both override: true and override: "merge" modes work as specified
  • Backward compatible with existing single-app service definitions
  • Enable skipped test in harp_apps/http_cache/tests/test_settings.py
  • Add comprehensive tests for cross-app overrides

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions