[SILO-663] chore: enhance error handling in ComplexFilterBackend with DRFValidationError
Description
This PR enhances error handling in ComplexFilterBackend by replacing Django's ValidationError with Django REST Framework's DRFValidationError and introducing structured error responses with error codes.
Type of Change
- [x] Improvement (change that would cause existing functionality to not work as expected)
- [x] Code refactoring
Screenshots and Media (if applicable)
Test Scenarios
- Existing functionality is working as expected.
References
SILO-663
Summary by CodeRabbit
-
Bug Fixes
- Standardized API validation responses with explicit error codes and clearer messages across filter operations, improving error clarity and consistency.
- Improved defensive handling for missing/disabled or misconfigured filter components with actionable feedback.
-
New Features
- Added hooks for advanced filter preprocessing and field-name transformation to support extensible/customizable filter behavior.
Walkthrough
Replaced ValidationError-based handling with DRFValidationError carrying structured payloads and explicit error codes across the complex filter backend. Added two hooks: _preprocess_leaf_conditions() and _transform_field_name_for_validation() and propagated DRFValidationError through normalization, leaf building, validation, and evaluation flows.
Changes
| Cohort / File(s) | Summary |
|---|---|
Filter backend core apps/api/plane/utils/filters/filter_backend.py |
Replaced all ValidationError usage with DRFValidationError and structured payloads (message + code). Added/updated explicit error codes for many failure paths (e.g., invalid_filter_type, invalid_json, filtering_not_enabled, invalid_filter_field, missing_build_combined_q, invalid_filterset, max_depth_exceeded, invalid_leaf, operator_in_leaf, etc.). Improved logging and replaced generic exception handling with logged raises. |
Extensibility hooks & field handling apps/api/plane/utils/filters/filter_backend.py |
Added two public hooks on ComplexFilterBackend: _preprocess_leaf_conditions(leaf_conditions, view, queryset) and _transform_field_name_for_validation(field_name). Applied _transform_field_name_for_validation during field extraction and used _preprocess_leaf_conditions before building leaf Q objects. |
Validation & build flow apps/api/plane/utils/filters/filter_backend.py |
Strengthened _normalize_filter_data, _apply_json_filter, _build_leaf_q, _validate_fields, _evaluate_node, and _get_max_depth to propagate DRFValidationError with explicit codes and include structured details for invalid filtersets and leaf/structure validation errors. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant Client
participant View
participant ComplexFilterBackend
participant Filterset / ORM
Client->>View: request with filter payload
View->>ComplexFilterBackend: apply_filters(queryset, request)
ComplexFilterBackend->>ComplexFilterBackend: _normalize_filter_data(payload)
alt invalid JSON / type
ComplexFilterBackend-->>View: raise DRFValidationError (invalid_json / invalid_filter_type)
else valid
ComplexFilterBackend->>ComplexFilterBackend: _apply_json_filter(...)
ComplexFilterBackend->>ComplexFilterBackend: _preprocess_leaf_conditions(leafs, view, queryset)
ComplexFilterBackend->>ComplexFilterBackend: _transform_field_name_for_validation(field_name)
ComplexFilterBackend->>ComplexFilterBackend: _build_leaf_q(leafs)
alt validation errors
ComplexFilterBackend-->>View: raise DRFValidationError (codes e.g., invalid_filterset, invalid_leaf, max_depth_exceeded)
else built Q
ComplexFilterBackend->>Filterset / ORM: build_combined_q(...) / apply Q
Filterset / ORM-->>ComplexFilterBackend: queryset
ComplexFilterBackend-->>View: filtered queryset
end
end
Estimated code review effort
π― 4 (Complex) | β±οΈ ~45 minutes
- Areas to focus:
- All new DRFValidationError code paths and message payload consistency.
- Correct invocation and defaults of
_preprocess_leaf_conditions()and_transform_field_name_for_validation()for subclasses. - Edge cases: missing
filterset_class, missingbuild_combined_q, deep nesting and mixed operator/leaf structures. - Logging vs. raised error coverage to ensure no swallowed exceptions.
Poem
π° I nudge the filters, tidy and neat,
With codes that hum and hops that beat.
Leafs preprocessed, names transformed too,
Errors speak clearly β a rabbitβs view.
Hop on, dear backend, the checks pass through.
Pre-merge checks and finishing touches
β Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | β Passed | The title clearly and specifically summarizes the main change: enhancing error handling by replacing ValidationError with DRFValidationError in ComplexFilterBackend. |
| Description check | β Passed | The description includes all required sections from the template with meaningful content: detailed description of changes, type of change clearly selected, test scenarios provided, and references linked. |
| Docstring Coverage | β Passed | Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%. |
β¨ Finishing touches
- [ ] π Generate docstrings
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
chore-improve-filter-errors
π Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π₯ Commits
Reviewing files that changed from the base of the PR and between f0fb916dc1e613e7f541612f7a2fbc0510b0f580 and 878c4fd0c60bb619f179114d3b3cce13ce610951.
π Files selected for processing (1)
apps/api/plane/utils/filters/filter_backend.py(12 hunks)
π§° Additional context used
𧬠Code graph analysis (1)
apps/api/plane/utils/filters/filter_backend.py (1)
apps/api/plane/utils/exception_logger.py (1)
log_exception(9-20)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Agent
- GitHub Check: Analyze (javascript)
- GitHub Check: Analyze (python)
- GitHub Check: Build and lint web apps
π Additional comments (11)
apps/api/plane/utils/filters/filter_backend.py (11)
1-13: LGTM! Imports are appropriate for enhanced error handling.The addition of
DRFValidationErrorandlog_exceptionaligns well with the PR's objective to improve error handling with structured responses.
51-74: LGTM! Well-structured error handling with clear error codes.The method now returns consistent, structured error responses with specific error codes (
invalid_filter_type,invalid_json) that API clients can programmatically handle.
96-123: LGTM! Field validation with clear, actionable error messages.The validation properly enforces that only fields declared in the FilterSet are allowed, and provides structured errors (
filtering_not_enabled,invalid_filter_field) when violations occur.
125-136: LGTM! Well-designed extensibility hook.The
_transform_field_name_for_validationhook provides a clean way for subclasses to handle custom field naming conventions. The docstring clearly explains its purpose and contract.
154-156: LGTM! Proper integration of the transformation hook.The transformation is correctly applied only to field names (not logical operators) before validation, ensuring custom field naming conventions are handled consistently.
214-229: LGTM! Valuable extensibility hook for custom filter transformations.The
_preprocess_leaf_conditionshook enables subclasses to transform filter conditions before Q object construction. The docstring provides a concrete example (custom property filters), making the intended use case clear.
246-254: LGTM! Proper error handling and hook integration.The method correctly validates the presence of
filterset_classand applies the preprocessing hook before building the QueryDict, enabling flexible transformation of filter conditions.
274-281: Verify if exposing detailed filterset validation errors is acceptable.The error response includes
ve.detailwhich contains validation errors from the FilterSet. While this provides helpful debugging information, it may expose internal field names, lookup types, or data structure details to API clients.If this level of detail is intentional for developer experience, this is fine. However, if you want to prevent potential information disclosure, consider either:
- Sanitizing or summarizing the error details
- Only including detailed errors in development/debug mode
- Mapping internal field names to public API field names
Based on learnings: If your API is intended for internal use or you want rich validation feedback, exposing these details is often acceptable. For public APIs, more caution may be warranted.
285-290: LGTM! Clear error message for missing required method.The error appropriately indicates that the FilterSet must implement
build_combined_qfor complex filtering to work.
309-402: LGTM! Comprehensive structure validation with consistent error codes.The method enforces all structural constraints (depth limits, operator rules, nesting requirements) and provides specific, actionable error codes for each validation failure. The error messages clearly explain what went wrong.
407-452: LGTM! Thorough leaf validation with precise error codes.The method validates leaf filter objects comprehensively, ensuring operators don't appear in leaves, list values are non-empty and contain only scalars, and all values are of acceptable types. Each validation failure has a specific error code.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.