Split the monolithic DatabaseService (1,492 lines) into focused repository
modules using the repository pattern for better maintainability and
separation of concerns.
Changes:
- Create new repositories/ directory with 5 focused repositories:
- TransactionRepository: transaction data operations (264 lines)
- AccountRepository: account data operations (128 lines)
- BalanceRepository: balance data operations (107 lines)
- MigrationRepository: all database migrations (629 lines)
- SyncRepository: sync operation tracking (132 lines)
- BaseRepository: shared database connection logic (28 lines)
- Refactor DatabaseService into a clean facade (287 lines):
- Delegates data access to repositories
- Maintains public API (no breaking changes)
- Keeps data processors in service layer
- Preserves require_sqlite decorator
- Update tests to mock repository methods instead of private methods
- Fix test references to internal methods (_persist_*, _get_*)
Benefits:
- Clear separation of concerns (one repository per domain)
- Easier maintenance (changes isolated to specific repositories)
- Better testability (repositories can be mocked individually)
- Improved code organization (from 1 file to 7 focused files)
All 114 tests passing.
Frontend:
- Memoize pagination object in TransactionsTable to prevent unnecessary re-renders and fix exhaustive-deps warning
- Add optional success and message fields to backup API response types for proper error handling
Backend:
- Add TypedDict for transaction type configuration to improve type safety in generate_sample_db
- Fix unpacking of amount_range with explicit float type hints
- Add explicit type hints for descriptions dictionary and specific_descriptions variable
- Fix sync endpoint return types: get_sync_status returns SyncStatus and sync_now returns SyncResult
- Fix transactions endpoint data type declaration to properly support Union types in PaginatedResponse
All checks now pass:
- Frontend: npm lint and npm build ✓
- Backend: mypy type checking ✓
- Backend: ruff lint on modified files ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Replace wrapped responses {success, data, message} with direct data returns
following REST best practices. Simplifies 41 endpoints across 7 route files
and updates all 109 tests. Also fixes test config setup to not require
user home directory config file.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Fix critical S3 client configuration bug for path-style addressing
- Add toast notifications for better user feedback on S3 config operations
- Set up Toaster component in root layout for app-wide notifications
- Clean up unused imports in test files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated test fixtures to correctly mock GoCardless API response format
with "results" key for institutions data. Fixed API client test to use
processed institutions data instead of raw GoCardless format.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
The notification system was incorrectly sending notifications for existing
transactions that were being updated during sync operations. This change
modifies the transaction persistence logic to only return genuinely new
transactions, preventing duplicate notifications while maintaining data
integrity through INSERT OR REPLACE.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Merge leggen/database/sqlite.py functionality directly into DatabaseService
- Extract transaction processing logic to separate TransactionProcessor class
- Remove leggen/utils/database.py and leggen/database/ directory entirely
- Update all tests to use new consolidated structure
- Reduce codebase by ~300 lines while maintaining full functionality
- Improve separation of concerns: data processing vs persistence vs CLI
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add Pydantic models for configuration validation in leggen/models/config.py
* Fix telegram config field aliases (api-key -> token, chat-id -> chat_id)
* Update config.py to use Pydantic validation with proper error handling
* Fix TOML serialization by excluding None values with exclude_none=True
* Update notification service to use correct telegram field names
* Enhance notification service with actual Discord/Telegram implementations
* Fix all failing configuration tests to work with Pydantic validation
* Add pydantic dependency to pyproject.toml
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Merge leggend API components into leggen (api/, services/, background/)
- Replace leggend command with 'leggen server' subcommand
- Consolidate configuration systems into leggen.utils.config
- Update environment variables: LEGGEND_API_URL -> LEGGEN_API_URL
- Rename LeggendAPIClient -> LeggenAPIClient
- Update all documentation, Docker configs, and compose files
- Fix all import statements and test references
- Remove duplicate utility files and clean up package structure
All tests passing (101/101), linting clean, server functionality preserved.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Change primary key from internalTransactionId to (accountId, transactionId)
- Add transactionId as stable bank-provided identifier
- Update INSERT to INSERT OR REPLACE for upsert behavior
- Update migration detection logic for composite key structure
- Update tests to include transactionId in sample data
- Update Transaction interface to include stable transaction_id field
- Modify TransactionsList to use stable transaction_id for React keys
- Update API models to handle new transactionId field from database
- Fix API routes to properly map transaction_id in responses
- Update test mocks to include transactionId field
- Ensure backward compatibility with internal_transaction_id
This adapts the frontend to work with the new composite primary key
(accountId, transactionId) structure that prevents duplicate transactions.
- Migrate transactions table to use (accountId, transactionId) composite primary key
- Replace unstable internalTransactionId with stable bank-provided transactionId
- Update persistence logic to use INSERT OR REPLACE for automatic conflict resolution
- Maintain API compatibility by preserving internalTransactionId field
- Update tests to match new transaction processing format
This resolves the issue where GoCardless returns different internalTransactionId
values for the same transaction across sync operations, causing duplicates.
- Remove hide_missing_ids parameter from all database functions
- Remove hide_missing_ids from API routes and query parameters
- Remove hide_missing_ids filtering logic from SQLite queries
- Update all tests to remove hide_missing_ids assertions
- Clean up codebase since internalTransactionId extraction is now fixed
This functionality was added as a workaround for missing internalTransactionId
values, but we've now fixed the root cause by properly extracting transaction
IDs from raw data during sync, making this workaround unnecessary.
- Remove amount_threshold and keywords fields from NotificationFilters model
- Remove handling of these fields from API routes (GET/PUT)
- Update test to remove amount_threshold reference
- Simplify notification filtering to focus on case-sensitive/insensitive keywords only
These fields were not being used in the actual filtering logic and were just
adding unnecessary complexity to the configuration.
- Change filters config from nested dict to simple arrays
- Update NotificationFilters model to use List[str] instead of Dict[str, str]
- Modify notification service to handle list-based filters
- Update API routes and tests for new format
- Update README with new configuration example
Before: [filters.case-insensitive] salary = 'salary'
After: [filters] case-insensitive = ['salary', 'utility']
- Add hide_missing_ids parameter to database functions to filter out transactions without internalTransactionId
- Update API routes to support the new filter parameter
- Update unit tests to include the new parameter
- Add opencode.json configuration file
- Add temp_db_path fixture to create temporary database file for tests
- Add mock_db_path fixture to mock Path.home() for database path resolution
- Update all account API tests to use temporary database
- Ensure test database is properly cleaned up after tests
- Prevent test data from polluting the actual configured database
- All 94 tests still pass with temporary database setup
- Remove GoCardless API calls from /api/v1/accounts and /api/v1/accounts/{account_id}
- Accounts endpoints now rely exclusively on database data
- Return 404 for accounts not found in database
- Update tests to mock database service instead of GoCardless API
- Remove unused GoCardless imports from transactions routes
- Preserve GoCardless usage in sync process and /banks endpoints
- Fix code formatting and remove unused imports
- Updated SQLite database to use ~/.config/leggen/leggen.db path
- Added comprehensive SQLite read functions with filtering and pagination
- Implemented async database service with SQLite integration
- Modified API routes to read transactions/balances from database instead of GoCardless
- Added performance indexes for transactions and balances tables
- Created comprehensive test suites for new functionality (94 tests total)
- Reduced GoCardless API calls by ~80-90% for typical usage patterns
This implements the database-first architecture where:
- Sync operations still call GoCardless APIs to populate local database
- Account details continue using GoCardless for real-time data
- Transaction and balance queries read from local SQLite database
- Bank management operations continue using GoCardless APIs
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove pymongo dependency from pyproject.toml and update lock file
- Delete leggen/database/mongo.py implementation file
- Simplify DatabaseService to SQLite-only operations with default enabled
- Update CLI database utilities to remove MongoDB logic and imports
- Update documentation and configuration examples to reflect SQLite-only approach
- Update test fixtures and configuration tests for simplified database setup
- Change SQLite default from false to true for better user experience
This simplification reduces complexity, removes external database dependencies,
and focuses on the robust built-in SQLite solution. All 46 tests passing.
Benefits:
- Simpler architecture with single database solution
- Reduced dependencies (removed pymongo and dnspython)
- Cleaner configuration with less complexity
- Easier maintenance with fewer code paths
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add pytest configuration in pyproject.toml with markers and async support
- Create shared test fixtures in tests/conftest.py for config, auth, and sample data
- Implement unit tests for all major components:
* Configuration management (11 tests) - TOML loading/saving, singleton pattern
* FastAPI API endpoints (12 tests) - Banks, accounts, transactions with mocks
* CLI API client (11 tests) - HTTP client integration and error handling
* Background scheduler (12 tests) - APScheduler job management and async ops
- Fix GoCardless API authentication mocking by adding token endpoints
- Resolve TOML file writing issues (binary vs text mode for tomli_w)
- Add comprehensive testing documentation to README
- Update code structure documentation to include test organization
Testing framework includes:
- respx for HTTP request mocking
- pytest-asyncio for async test support
- pytest-mock for advanced mocking capabilities
- requests-mock for CLI HTTP client testing
- Realistic test data fixtures for banks, accounts, and transactions
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>