fix: prevent system segments from being overwritten via change request drafts - #8298
fix: prevent system segments from being overwritten via change request drafts#8298srijantrpth wants to merge 7 commits into
Conversation
…t drafts and add unit test
|
@srijantrpth is attempting to deploy a commit to the Flagsmith Team on Vercel. A member of the Team first needs to authorize it. |
for more information, see https://pre-commit.ci
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe change request commit now runs within an atomic transaction. The segment publishing workflow rejects drafts that target system segments before revision creation or live-segment updates. A unit test verifies the Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: 🟡 Moderate · up to The change correctly blocks system-segment drafts, but rejection can still trigger external updates before the database transaction commits, potentially causing inconsistent downstream state. The PR should address this transaction-boundary issue and the remaining formatting check before merge. ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 7d4c283a-be0a-47ee-8f1c-4983a6315816
📒 Files selected for processing (2)
api/core/workflows_services.pyapi/tests/unit/features/workflows/core/test_unit_workflows_models.py
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8298 +/- ##
==========================================
- Coverage 98.76% 98.62% -0.15%
==========================================
Files 1594 1594
Lines 63657 63667 +10
==========================================
- Hits 62873 62793 -80
- Misses 784 874 +90 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 3549c7b1-2004-485c-80d0-cf7dfa1ca444
📒 Files selected for processing (2)
api/core/workflows_services.pyapi/tests/unit/features/workflows/core/test_unit_workflows_models.py
| def __init__(self, change_request: "ChangeRequest") -> None: | ||
| self.change_request = change_request | ||
|
|
||
| @transaction.atomic |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Defer external side effects until commit succeeds.
The decorator on Line 23 now wraps _publish_environment_feature_versions() and _publish_segments(). The Celery .delay(...) calls and environment_feature_version_published.send(...) still run before the transaction commits. If the system-segment guard raises ValueError, database changes roll back, but broker messages and signal-receiver side effects can remain. Register these calls with transaction.on_commit() and bind each loop value per callback.
There was a problem hiding this comment.
Thanks for the catch! Since _publish_environment_feature_versions handles core versioning logic outside the scope of this specific system segment security patch, I'll leave the transaction hooks as-is for now to avoid altering existing versioning behavior, but I appreciate the architectural insight.
There was a problem hiding this comment.
@srijantrpth, understood. The system-segment protection can remain scoped to this patch.
The side-effect ordering risk remains after the new outer transaction. Would you like me to open a follow-up issue for deferring the Celery dispatches and environment_feature_version_published.send() calls with transaction.on_commit()?
🐇
🧠 Learnings used
Learnt from: khvn26
Repo: Flagsmith/flagsmith PR: 8042
File: api/trust_relationships/services.py:30-53
Timestamp: 2026-07-20T09:52:12.198Z
Learning: In the Flagsmith Django API, do not rely on `ATOMIC_REQUESTS` being enabled (it is not used and will not be adopted). When reviewing code in the service layer (e.g., `services.py`), avoid assumptions that view-layer operations run inside an enclosing request transaction. If you see `transaction.atomic()` in service code, treat it as defining the transaction boundary itself (or explicitly justify nesting/interaction), and do not flag issues based solely on the absence/presence of a request-level transaction.
You are interacting with an AI system.
There was a problem hiding this comment.
Sure, opening a follow-up issue for that side-effect ordering risk would be a great proactive step!
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
repo="Flagsmith/flagsmith"
body=$(cat <<'EOF'
## Summary
Defer external side effects from change request publishing until the enclosing database transaction commits successfully.
## Rationale
`ChangeRequestCommitService.commit()` now runs in `transaction.atomic`. `_publish_environment_feature_versions()` dispatches Celery tasks and sends `environment_feature_version_published` before this transaction commits. If a later operation fails, for example when `_publish_segments()` rejects a system segment, the database transaction rolls back but queued tasks and signal-receiver side effects can still run against data that was not committed.
## Affected area
- `api/core/workflows_services.py`
- `ChangeRequestCommitService._publish_environment_feature_versions`
## Required changes
Register `trigger_update_version_webhooks.delay(...)`, `rebuild_environment_document.delay(...)`, and `environment_feature_version_published.send(...)` through `transaction.on_commit()`. Bind each loop value in its callback so callbacks use the correct environment feature version and scheduled timestamp.
## Acceptance criteria
- No Celery task is dispatched when `ChangeRequestCommitService.commit()` rolls back.
- No `environment_feature_version_published` receiver runs when `ChangeRequestCommitService.commit()` rolls back.
- The existing side effects run after a successful transaction commit.
- Tests cover both rollback and successful commit behaviour.
## Backlinks
- Pull request: https://github.com/Flagsmith/flagsmith/pull/8298
- Review comment: https://github.com/Flagsmith/flagsmith/pull/8298#discussion_r3786731701
- Requested by: `@srijantrpth`
EOF
)
gh issue create \
--repo "$repo" \
--title "Defer change request publishing side effects until transaction commit" \
--body "$body" \
--assignee "srijantrpth"Length of output: 2047
@srijantrpth, I created follow-up issue #8299: Defer change request publishing side effects until transaction commit.
🐇
🧠 Learnings used
Learnt from: khvn26
Repo: Flagsmith/flagsmith PR: 8042
File: api/trust_relationships/services.py:30-53
Timestamp: 2026-07-20T09:52:12.198Z
Learning: In the Flagsmith Django API, do not rely on `ATOMIC_REQUESTS` being enabled (it is not used and will not be adopted). When reviewing code in the service layer (e.g., `services.py`), avoid assumptions that view-layer operations run inside an enclosing request transaction. If you see `transaction.atomic()` in service code, treat it as defining the transaction boundary itself (or explicitly justify nesting/interaction), and do not flag issues based solely on the absence/presence of a request-level transaction.
You are interacting with an AI system.
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Thanks for submitting a PR! Please check the boxes below:
docs/if required so people know about the feature.Changes
Closes #8269
ChangeRequestCommitService._publish_segments(api/core/workflows_services.py) to prevent change request drafts from overwriting system segments (is_system_segment=True).How did you test this code?
Added a new unit test (
test_change_request_commit__system_segment_draft__raises_value_error) intests/unit/features/workflows/core/test_unit_workflows_models.pyto verify that attempting to commit a change request targeting a system segment correctly raises aValueError.