Files
attune/work-summary/sessions/2026-01-27-sensor-webhook-schema-fix.md
2026-02-04 17:46:30 -06:00

5.5 KiB

Sensor Service Webhook Schema Migration Fix

Date: 2026-01-27
Status: Complete
Related: Webhook schema consolidation (migration 20260127000001)

Problem

After consolidating the webhook configuration from 12 separate columns into a single webhook_config JSONB column, the sensor service failed to compile with 20+ compilation errors:

error[E0560]: struct `attune_common::models::Trigger` has no field named `webhook_secret`
error[E0560]: struct `attune_common::models::Trigger` has no field named `webhook_hmac_enabled`
...

The sensor service was directly querying the database using sqlx::query_as!() macros with hardcoded column names that no longer existed in the schema.

Root Cause

The sensor service (crates/sensor/) was not following the repository pattern used by other services. It contained direct SQL queries in three files:

  1. sensor_manager.rs - Direct queries for triggers, runtimes, and sensors
  2. service.rs - Direct queries for sensors and triggers
  3. rule_matcher.rs - Direct queries for rules, enforcements, and packs

These queries explicitly listed all webhook columns by name, which broke when the schema changed.

Solution

1. Refactored to Use Repository Pattern

Updated all three files to use the repository layer instead of direct SQL:

sensor_manager.rs:

  • Replaced load_trigger() SQL query → TriggerRepository::find_by_id()
  • Replaced load_runtime() SQL query → RuntimeRepository::find_by_id()
  • Replaced load_enabled_sensors() SQL query → SensorRepository::list()

service.rs:

  • Replaced load_timer_triggers() sensor query → SensorRepository::list()
  • Replaced trigger lookup → TriggerRepository::find_by_id()
  • Fixed TimerManager::new() callback signature (2 args instead of 1)

rule_matcher.rs:

  • Replaced find_matching_rules() SQL query → RuleRepository::list()
  • Replaced enforcement creation SQL → EnforcementRepository::create()
  • Replaced pack config lookup SQL → PackRepository::find_by_ref()

2. Used Static Repository Trait Methods

The Attune repository layer uses static trait methods (not instance methods):

// ❌ Old (doesn't exist)
let repo = TriggerRepository::new(db.clone());
let trigger = repo.get_by_id(id).await?;

// ✅ New (correct pattern)
use attune_common::repositories::{TriggerRepository, FindById};
let trigger = TriggerRepository::find_by_id(&db, id).await?;

Key traits used:

  • FindById::find_by_id() - Find entity by ID
  • FindByRef::find_by_ref() - Find entity by reference string
  • List::list() - List all entities
  • Create::create() - Create new entity

3. Updated Test Fixtures

Updated test helper functions in sensor_manager.rs:

  • Removed 9 obsolete webhook field assignments
  • Added single webhook_config: None field

Files Changed

crates/sensor/src/sensor_manager.rs  - 70 lines changed
crates/sensor/src/service.rs         - 45 lines changed
crates/sensor/src/rule_matcher.rs    - 60 lines changed

Code Quality Improvements

  1. Consistency - Sensor service now follows the same repository pattern as API, Executor, and Worker services
  2. Maintainability - Schema changes only need updates in repository layer, not scattered SQL queries
  3. Testability - Services can now be tested with mock repositories
  4. Separation of Concerns - Services don't directly couple to database schema

Verification

# Compile sensor service
cargo build -p attune-sensor
# ✅ Success - no errors

# Compile entire workspace  
cargo build
# ✅ Success - all packages compile

# Rebuild and restart API service
cargo build -p attune-api
kill <old_pid>
nohup ./target/debug/attune-api > /tmp/attune-api.log 2>&1 &
# ✅ API service restarted successfully

# Run E2E tests
cd tests && ./venvs/e2e/bin/pytest test_e2e_basic.py -v
# ✅ 5 passed, 1 skipped

# Run quick test
./venvs/e2e/bin/python quick_test.py
# ✅ All 5 tests passed

Build Output:

  • 0 compilation errors
  • 4 warnings (unused variables/fields - not critical)
  • Build time: ~15 seconds (sensor), ~32 seconds (API)

Test Results:

  • E2E pytest suite: 5/5 passed, 1 skipped (expected)
  • Quick test: 5/5 passed
  • All trigger/rule creation tests working correctly with new schema

Impact

  • Sensor service compiles successfully
  • All workspace packages build without errors
  • API service restarted with updated code
  • All E2E tests passing (5/5) with new webhook schema
  • Trigger and rule creation working correctly
  • Maintains architectural consistency across services
  • Future schema changes won't break sensor service
  • Ready for integration testing with other services

Lessons Learned

  1. Repository pattern is mandatory - Direct SQL queries create tight coupling and break on schema changes
  2. SQLx macros need DATABASE_URL - Without it, offline mode requires .sqlx cache files
  3. Static trait methods - Attune repositories use static methods, not instance methods with new()
  4. Compilation before migration - Always ensure all services compile before major schema changes

Next Steps

  1. Continue with sensor service integration testing
  2. Verify timer triggers work with new webhook schema
  3. Test rule matching and enforcement creation
  4. Integration test with executor and worker services
  • docs/architecture.md - Repository pattern documentation
  • migrations/20260127000001_consolidate_webhook_config.sql - Schema migration
  • CHANGELOG.md - Updated with this fix
  • work-summary/2026-01-27-e2e-test-improvements.md - E2E testing session