plane icon indicating copy to clipboard operation
plane copied to clipboard

[SILO-663] chore: enhance error handling in ComplexFilterBackend with DRFValidationError

Open dheeru0198 opened this issue 1 month ago β€’ 2 comments

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.

dheeru0198 avatar Nov 10 '25 12:11 dheeru0198

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

makeplane[bot] avatar Nov 10 '25 12:11 makeplane[bot]

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, missing build_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 DRFValidationError and log_exception aligns 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_validation hook 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_conditions hook 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_class and 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.detail which 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:

  1. Sanitizing or summarizing the error details
  2. Only including detailed errors in development/debug mode
  3. 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_q for 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.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 10 '25 12:11 coderabbitai[bot]