Skip to content

Align endpoint error JSON to Weblate/DRF conventions#165

Merged
wpak-ai merged 4 commits into
cppalliance:developfrom
whisper67265:fix/endpoint-error-DRF
Jul 9, 2026
Merged

Align endpoint error JSON to Weblate/DRF conventions#165
wpak-ai merged 4 commits into
cppalliance:developfrom
whisper67265:fix/endpoint-error-DRF

Conversation

@whisper67265

@whisper67265 whisper67265 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Close #161.

Summary by CodeRabbit

  • New Features
    • Validation errors now return standardized entries with code, detail, and optional attr, including language-scoped, flattened field paths.
    • Add-or-update request validation exposes flattened, DRF-like details via standardized_errors.
  • Bug Fixes
    • Invalid requests now consistently use the serializer’s exception flow for uniform 400 responses.
    • Task/internal error serialization now follows the same detail/attr shape.
  • Documentation
    • Updated error response and error-handling examples to match the new format.
  • Tests
    • Updated validation, task failure, and extension validation expectations to the new error contract.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 438ffc33-9b02-4292-a229-0764ed5459e3

📥 Commits

Reviewing files that changed from the base of the PR and between 3e6590b and 50a546d.

📒 Files selected for processing (1)
  • tests/postgres/test_endpoint_trust_boundary.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/postgres/test_endpoint_trust_boundary.py

📝 Walkthrough

Walkthrough

Error serialization, serializer validation, and view handling now emit DRF-style code, detail, and attr error entries. The serializer exposes standardized_errors, and the API docs and tests were updated to match the new validation error envelope.

Changes

DRF-standard error payload refactor

Layer / File(s) Summary
Error entry contracts and helpers
src/boost_weblate/endpoint/errors.py, src/boost_weblate/endpoint/tasks.py
Adds attr_for_field, to_error_entry, flatten_validation_detail, and updates to_dict, to_error_dict, append_error, and boost_validation_errors to build code/detail/attr entries; the task module docstring is updated to describe the error shapes.
Serializer validation raises ErrorDetail-based errors
src/boost_weblate/endpoint/serializers.py, src/boost_weblate/endpoint/views.py
Replaces structured_errors with standardized_errors, adds _boost_code_for_drf_error and _BOOST_ERROR_CODES, updates validation methods to raise ValidationError using ErrorDetail, and switches AddOrUpdateView.post to serializer.is_valid(raise_exception=True).
Error helper and serializer tests
tests/endpoint/test_errors.py, tests/endpoint/test_serializers.py, tests/endpoint/test_views.py
Tests now assert the standardized code/detail/attr shape, including flattened validation detail, standardized_errors, and extension validation cases.
API docs and endpoint integration checks
docs/boost-endpoint-api.md, tests/postgres/test_endpoint_trust_boundary.py
The API reference and trust-boundary test now expect the validation_error envelope and standardized error entries for HTTP validation failures.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested reviewers: henry0816191, wpak-ai

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change to endpoint error JSON conventions.
Linked Issues check ✅ Passed The PR appears to satisfy the linked issue by standardizing 400 validation errors, preserving metadata for service errors, and exposing flattened serializer errors.
Out of Scope Changes check ✅ Passed The changes stay focused on endpoint error formatting plus the necessary serializer, view, test, and documentation updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/endpoint/test_errors.py (1)

145-165: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test locks in the attr_for_field trailing-dot bug.

errors[0]["attr"] == "add_or_update." asserts the malformed trailing-dot output produced by attr_for_field when language="" (see comment on errors.py lines 62-73). Once that helper is fixed to treat empty-string language as absent, this assertion should be updated to "add_or_update".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/endpoint/test_errors.py` around lines 145 - 165, The test in
boost_validation_errors_builds_list is asserting the malformed trailing-dot attr
produced by attr_for_field when language is empty. Update the expectation for
the INVALID_LANGUAGE_CODE case so it matches the fixed behavior of
attr_for_field in errors.py, using "add_or_update" instead of "add_or_update.",
while keeping the INVALID_SUBMODULE_LIST assertion unchanged.
🧹 Nitpick comments (2)
tests/endpoint/test_errors.py (1)

167-190: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Missing coverage for raw DRF error codes not present in BoostEndpointErrorCode.

This test only uses codes already defined on the enum (required_field, invalid_submodule_list). It doesn't cover the case where an ErrorDetail.code is a native DRF code (e.g. required, invalid) not in the enum, which would raise ValueError inside flatten_validation_detail (see the corresponding comment in errors.py). Consider adding a case with an ErrorDetail(..., code="required") to catch this regression once addressed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/endpoint/test_errors.py` around lines 167 - 190, Add coverage in
test_flatten_validation_detail_matches_drf_shape for a raw DRF ErrorDetail.code
such as "required" or "invalid" that is not present in BoostEndpointErrorCode.
Update the existing flatten_validation_detail test in test_errors.py to include
one such ErrorDetail so the behavior in errors.py is exercised and the
regression where enum casting raises ValueError is caught.
tests/endpoint/test_serializers.py (1)

340-350: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for non-string extensions elements.

Only the "extensions is a dict" case is tested here. A case like "extensions": ["ok", 1] would exercise validate_extensions's non-string-element branch (serializers.py Line 234-237), which currently maps to the wrong Boost code (INVALID_SUBMODULE_LIST instead of an extensions-specific code) — see the related comment on serializers.py. Adding this test would have caught that.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/endpoint/test_serializers.py` around lines 340 - 350, Add a test in
test_add_or_update_serializer_rejects_extensions_dict or a new test in
test_serializers for AddOrUpdateRequestSerializer that passes a non-string
element inside extensions, such as a mixed list, and asserts validation fails
with an extensions error. Use this to cover the validate_extensions branch that
checks element types, and verify the standardized error is attributed to
extensions rather than only testing the dict-shape rejection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/boost_weblate/endpoint/errors.py`:
- Around line 62-73: Fix attr_for_field so an empty language string is treated
like no language and does not append a trailing dot; update the language check
in attr_for_field accordingly and keep the existing behavior only when language
has a real value. Also update the related boost_validation_errors expectation in
test_errors.py so the empty-language case asserts the base field path rather
than the malformed dotted attr.

In `@src/boost_weblate/endpoint/serializers.py`:
- Around line 234-237: The `extensions` validation branch is reusing the wrong
DRF error code and enum form, causing non-string extension items to map to the
same Boost code as a non-list language value. Update the serializer logic around
`extensions` so it uses the string-valued DRF code consistently and ensure
`_code_for_drf_error` differentiates by field instead of mapping `NOT_A_LIST`
unconditionally. Check the `extensions` validation path in `serializers.py` and
add/adjust a test to assert the returned Boost error code for non-string
extension elements is field-appropriate.

---

Outside diff comments:
In `@tests/endpoint/test_errors.py`:
- Around line 145-165: The test in boost_validation_errors_builds_list is
asserting the malformed trailing-dot attr produced by attr_for_field when
language is empty. Update the expectation for the INVALID_LANGUAGE_CODE case so
it matches the fixed behavior of attr_for_field in errors.py, using
"add_or_update" instead of "add_or_update.", while keeping the
INVALID_SUBMODULE_LIST assertion unchanged.

---

Nitpick comments:
In `@tests/endpoint/test_errors.py`:
- Around line 167-190: Add coverage in
test_flatten_validation_detail_matches_drf_shape for a raw DRF ErrorDetail.code
such as "required" or "invalid" that is not present in BoostEndpointErrorCode.
Update the existing flatten_validation_detail test in test_errors.py to include
one such ErrorDetail so the behavior in errors.py is exercised and the
regression where enum casting raises ValueError is caught.

In `@tests/endpoint/test_serializers.py`:
- Around line 340-350: Add a test in
test_add_or_update_serializer_rejects_extensions_dict or a new test in
test_serializers for AddOrUpdateRequestSerializer that passes a non-string
element inside extensions, such as a mixed list, and asserts validation fails
with an extensions error. Use this to cover the validate_extensions branch that
checks element types, and verify the standardized error is attributed to
extensions rather than only testing the dict-shape rejection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12e240e6-fbcf-47c3-9874-627085bf42dc

📥 Commits

Reviewing files that changed from the base of the PR and between 9d29d33 and ab059e0.

📒 Files selected for processing (7)
  • src/boost_weblate/endpoint/errors.py
  • src/boost_weblate/endpoint/serializers.py
  • src/boost_weblate/endpoint/tasks.py
  • src/boost_weblate/endpoint/views.py
  • tests/endpoint/test_errors.py
  • tests/endpoint/test_serializers.py
  • tests/endpoint/test_views.py

Comment thread src/boost_weblate/endpoint/errors.py
Comment thread src/boost_weblate/endpoint/serializers.py
@whisper67265 whisper67265 requested a review from henry0816191 July 9, 2026 17:45
@henry0816191

Copy link
Copy Markdown
Collaborator

Location: docs/boost-endpoint-api.md — Response (400 Bad Request) — lines ~140–161; Error handling — lines ~384–400

Issue: The doc still shows HTTP 400 as {"errors": [{"code", "message", "metadata"}, …]} and states validation errors use metadata.field / metadata.language / metadata.drf_code. After this PR, AddOrUpdateView returns DRF standardized validation errors: {"type": "validation_error", "errors": [{"code", "detail", "attr"}, …]}. Submodule/Celery service results still use to_error_dict with metadata, but the HTTP layer no longer does. Clients and operators following the doc will mis-parse 400 responses. Same documentation gap pattern as PR #154 finding 1.

Recommendation: Update the 400 JSON example, the 400 table, and the Error handling section to distinguish HTTP validation (type, detail, attr) from service/submodule errors (detail + optional metadata bag).

@henry0816191

Copy link
Copy Markdown
Collaborator

Location: tests/postgres/test_endpoint_trust_boundary.pytest_adversarial_payload_rejected_without_db_rows_or_enqueue — lines ~43–44

Issue: The integration test only checks "errors" in response.data on HTTP 400. Unit test_add_or_update_validation_error now asserts type == "validation_error" and code/detail/attr keys. The postgres tier is the real-DB complement to trust-boundary tests; a regression in DRF envelope wiring would not be caught there.

Recommendation: Mirror the unit test assertions on response.data["type"] and error entry keys for the adversarial case.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/postgres/test_endpoint_trust_boundary.py`:
- Around line 46-52: The test only verifies that INVALID_CLONE_URL appears, but
it does not check the matching error’s attr path. Update the assertions in the
response validation test to locate the error entry for
BoostEndpointErrorCode.INVALID_CLONE_URL.value and assert its attr is the
expected organization path, using the existing response.data["errors"] structure
and the code/attr fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f7138bb-f212-4911-8588-67a68e3b7c6a

📥 Commits

Reviewing files that changed from the base of the PR and between 38ffb57 and 3e6590b.

📒 Files selected for processing (2)
  • docs/boost-endpoint-api.md
  • tests/postgres/test_endpoint_trust_boundary.py

Comment thread tests/postgres/test_endpoint_trust_boundary.py
@henry0816191 henry0816191 requested a review from wpak-ai July 9, 2026 18:05
@wpak-ai wpak-ai merged commit 7a67d61 into cppalliance:develop Jul 9, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align endpoint error JSON to Weblate/DRF conventions

3 participants