6.9 KiB
6.9 KiB
Aether API V3 CRUD: Architecture and Learnings
This document summarizes the development of the V3 CRUD API, the architectural choices made, and the lessons learned during the process.
1. V3 CRUD Architecture
The V3 CRUD API (/v3/crud/) is designed to run in parallel with legacy V1 and V2 endpoints. It introduces a hierarchical, nested URL structure and leverages modern FastAPI features for better maintainability and performance.
Key Features:
- Nested URL Structure: Enforces parent-child relationships (e.g.,
/v3/crud/site/{site_id}/site_domain/). - Granular Dependencies: Instead of a monolithic common parameters object, V3 uses specialized, reusable dependencies from
app/lib_general_v3.py:AccountContext: Resolves account ID with clear precedence (Header > Query Token > Bypass Header).PaginationParams: Standardizeslimitandoffset.StatusFilterParams: Handlesenabledandhiddenstatus filtering.SerializationParams: Controls Pydantic serialization options (by_alias,exclude_unset).DelayParams: Facilitates optional latency simulation for testing.
- Non-blocking Delay: Uses
await asyncio.sleep()to simulate network latency without blocking the Gunicorn worker's event loop. - Data-Driven Configuration: Uses the modern format in
app/ae_obj_types_def.pyto map objects to tables and models. - Advanced Search (POST): Supports complex, nested filtering via
POST /v3/crud/{obj_type}/search.- Recursive AND/OR logic.
- Full operator support:
eq,ne,gt,gte,lt,lte,in,is_null,is_not_null,like,contains,startswith,endswith. - Safe parameterization using unique generated names (e.g.,
:sp_1) to prevent collisions.
2. Backward Compatibility Strategy
To ensure that the introduction of V3 doesn't break legacy V1 and V2 endpoints:
- Parallel Routes: All V3 logic is isolated in
app/routers/api_crud_v3.py. - Hybrid Configuration: The
obj_type_kv_lidictionary inapp/ae_obj_types_def.pyhas been updated to include both modern keys (e.g.,tbl,mdl) and legacy keys (e.g.,table_name,base_name). This allows V2 endpoints to continue functioning normally while V3 endpoints use the refined structure. - Stable Core Imports: Core modules like
app/lib_general.pyandapp/models/response_models.pyhave been refactored to maintain their existing exports (log,logging,mk_resp) while internally adopting more robust module-level logging.
3. Learnings and Best Practices
Logging and Startup Stability
- Isolate Loggers: Modules should instantiate their own module-level loggers (
logging.getLogger(__name__)) rather than importing a global instance. This breaks circular dependencies and improves traceability. - Robust Configuration: Logging configuration (
dictConfig) should be wrapped intry...exceptto prevent application crashes due to environment issues (e.g., missing log directories in Docker). - Explicit Imports: Always use
import logging.configbefore callinglogging.config.dictConfigto ensure the module is fully initialized.
FastAPI and Pydantic
- Dependency Injection: Use
response: Responseas a type hint for standard injection. AvoidDepends(Response)as it is not a valid dependency provider and can cause router initialization failures. - Python Parameter Order: In function signatures, non-default arguments (like
response: Response) must precede arguments with default values orDepends(). - Async Concurrency: Use
asyncio.sleep()instead oftime.sleep()in async endpoints. Blocking the event loop in a high-concurrency environment leads to worker timeouts and502 Bad Gatewayerrors. - Pydantic Compatibility: Ensure all new models and utility functions remain compatible with Pydantic v1.10 until a full project-wide migration to v2 is planned. Avoid V2-only features like
computed_fieldormodel_validator.
4. Current Migration Status
The following objects have been migrated to the modern V3 configuration and are supported by the V3 API:
journal,journal_entrysite,site_domainaccount,account_cfgaddress,contactorder,order_lineorganization,pagedata_store,activity_logarchive,archive_contenthosted_file,post,post_commentperson,userlu_country,lu_country_subdivision,lu_time_zone
5. Security and Data Isolation
Implemented in Jan 2026, the V3 architecture enforces strict data isolation and role-aware authorization.
Role-Aware Authorization (AuthContext)
- Deferred User Lookup: When a JWT is provided, the API performs a deferred database lookup via
load_user_objto populateadministrator,manager, andsuperflags. - Role Scoping: These flags are used to bypass account isolation for support and system maintenance tasks.
Multi-Tenant Isolation
- Fail-Closed Strategy (Feb 2026): The API enforces a strict "Fail-Closed" mindset. If an authentication context or account ID is missing, the system defaults to a blocking filter (e.g.,
account_id IS NULL). It no longer allows "Fail-Open" behavior where missing context returns all records. - IDAA Privacy Baseline: Based on the International Drunk Doctors Anonymous requirement, the system defaults to Maximum Isolation. No object (Events, Files, Posts, Meetings) is public by default. Every object requires an
x-account-idcontext, with the sole exception ofsite_domainused for bootstrapping. - Forced Account Filtering: For all non-super users, the API automatically injects an
account_idfilter into every list and search query. This is enforced at the SQL level viaapply_forced_account_filter. - Detailed 403 Feedback: Security failures now return descriptive error messages (e.g., "API Key expired", "Account context required") to assist developers in debugging header/context issues while maintaining strict isolation.
- Post-Retrieval Verification: Single object retrievals (
GET), updates (PATCH), and deletions (DELETE) include a secondary ownership check (check_account_access). If a user attempts to access an ID belonging to another account, they receive a403 Forbiddenresponse. - Hierarchical Verification: Child/Nested endpoints verify the ownership of the parent object before allowing operations on children, preventing cross-tenant "sideways" traversal.
- Creation Guard: When creating records (
POST), the user'saccount_idis automatically forced onto the new record, preventing a user from creating data for another account.
Bypass and Utility Access
- X-No-Account-ID: A specialized header used by development utilities and administrative scripts to bypass standard account isolation. This header grants
superpermissions and should only be used in trusted internal environments. - JWT Query Parameter: Supported for direct file downloads and sharing links where custom headers cannot be provided.