Skip to content

[ENG-580] feat: allow clearing facility org/org#3690

Open
Jacobjeevan wants to merge 4 commits into
ohcnetwork:developfrom
Jacobjeevan:ENG-580-tag-config
Open

[ENG-580] feat: allow clearing facility org/org#3690
Jacobjeevan wants to merge 4 commits into
ohcnetwork:developfrom
Jacobjeevan:ENG-580-tag-config

Conversation

@Jacobjeevan

@Jacobjeevan Jacobjeevan commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

  • ENG-580
  • Allow clearing facility organization/organization

Architecture changes

  • Remove this section if not used

Merge Checklist

  • Tests added/fixed
  • Update docs in /docs
  • Linting Complete
  • Any other necessary step

Only PR's with test cases included and passing lint and test pipelines will be reviewed

@ohcnetwork/care-backend-maintainers @ohcnetwork/care-backend-admins

Summary by CodeRabbit

Release Notes

  • Updates
    • Tag configuration update requests now correctly clear both organization and facility organization when those fields are not provided (or are set to null), instead of leaving previously stored values in place.
    • This improves consistency when applying partial updates to EMR tag settings.

@Jacobjeevan Jacobjeevan requested a review from a team as a code owner June 17, 2026 12:18
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3967a86e-543a-4a8b-9393-8a89cff29f7f

📥 Commits

Reviewing files that changed from the base of the PR and between 9dadc15 and e3f7a7d.

📒 Files selected for processing (1)
  • care/emr/resources/tag/config_spec.py

📝 Walkthrough

Walkthrough

TagConfigUpdateSpec.perform_extra_deserialization now explicitly assigns organization and facility_organization to None in else branches when the corresponding input values are not truthy, ensuring these fields are consistently cleared during update operations.

Changes

TagConfigUpdateSpec Deserialization Update

Layer / File(s) Summary
Explicitly assign None in deserialization
care/emr/resources/tag/config_spec.py
perform_extra_deserialization now includes else branches that set obj.organization = None and obj.facility_organization = None when input values are not provided, rather than relying on prior values to persist.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 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
Title check ✅ Passed The title clearly and specifically describes the main change: allowing the clearing of facility organization and organization fields, which directly matches the code modification.
Description check ✅ Passed The description follows the template structure with Proposed Changes and includes the linked issue, though the Associated Issue section lacks detail on how the solution addresses the problem.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds else branches to TagConfigUpdateSpec.perform_extra_deserialization so that sending null for organization or facility_organization clears those fields on the TagConfig record.

  • The else branches now unconditionally set obj.organization = None and obj.facility_organization = None when the corresponding field is falsy, but since both fields carry a = None default, any update payload that simply omits them will trigger the same clearing path — silently dropping existing associations.
  • TagConfigWriteSpec (the create path) is unchanged and does not have this defect.

Confidence Score: 3/5

Not safe to merge: the update handler will silently clear existing organization associations whenever a client omits those fields from the request body.

The else branches run whenever self.organization or self.facility_organization is falsy — which covers both an explicit null and a missing field (Pydantic defaults both to None). Any PATCH/update call that doesn't include these keys will wipe the saved DB links, which is a data-loss regression on the update path.

care/emr/resources/tag/config_spec.py — specifically TagConfigUpdateSpec.perform_extra_deserialization

Important Files Changed

Filename Overview
care/emr/resources/tag/config_spec.py Adds else branches to clear organization/facility_organization on update, but because both fields default to None, any update that omits them will silently clear existing DB associations.

Reviews (4): Last reviewed commit: "chore: allow none default" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@care/emr/resources/tag/config_spec.py`:
- Around line 71-72: The truthy checks on the organization and
facility_organization fields prevent explicit None values from clearing the
database fields. Replace the truthy checks (such as `if organization:` and `if
facility_organization:`) with explicit `is not None` checks throughout the
update logic (lines 75-84) so that passing `organization=None` or
`facility_organization=None` will properly clear the existing DB values instead
of leaving them unchanged.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6b57f861-64da-4dfb-bda4-b5492166a35e

📥 Commits

Reviewing files that changed from the base of the PR and between c4d5582 and 97229ea.

📒 Files selected for processing (1)
  • care/emr/resources/tag/config_spec.py

Comment thread care/emr/resources/tag/config_spec.py Outdated
Comment thread care/emr/resources/tag/config_spec.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
care/emr/resources/tag/config_spec.py (1)

75-88: 💤 Low value

Truthy checks work here, but explicit is not None would be clearer

The else branches successfully address the clearing functionality—which is great, that's the whole point of ENG-580. However, the truthy checks (if self.organization:) differ from the past review's suggestion to use explicit is not None checks.

For UUID4 | None, this is functionally equivalent since Pydantic validates the type. But using is not None would make the intent more obvious to future readers who might wonder if there's some edge case being handled.

Optional: Use explicit None checks for clarity
     def perform_extra_deserialization(self, is_update, obj):
-        if self.organization:
+        if self.organization is not None:
             obj.organization = get_object_or_404(
                 Organization.objects.only("id"), external_id=self.organization
             )
         else:
             obj.organization = None
-        if self.facility_organization:
+        if self.facility_organization is not None:
             obj.facility_organization = get_object_or_404(
                 FacilityOrganization.objects.only("id"),
                 external_id=self.facility_organization,
                 facility=obj.facility,
             )
         else:
             obj.facility_organization = None
🤖 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 `@care/emr/resources/tag/config_spec.py` around lines 75 - 88, Replace the
truthy checks for self.organization and self.facility_organization with explicit
is not None checks to make the intent clearer. Change the if conditions from
checking truthiness (if self.organization:) to explicitly checking against None
(if self.organization is not None:) in both the organization assignment block
and the facility_organization assignment block to improve code clarity for
future readers.
🤖 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.

Nitpick comments:
In `@care/emr/resources/tag/config_spec.py`:
- Around line 75-88: Replace the truthy checks for self.organization and
self.facility_organization with explicit is not None checks to make the intent
clearer. Change the if conditions from checking truthiness (if
self.organization:) to explicitly checking against None (if self.organization is
not None:) in both the organization assignment block and the
facility_organization assignment block to improve code clarity for future
readers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8c70e6b6-2b47-474f-a893-2f486a2145aa

📥 Commits

Reviewing files that changed from the base of the PR and between 97229ea and 9dadc15.

📒 Files selected for processing (1)
  • care/emr/resources/tag/config_spec.py

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Want your agent to iterate on Greptile's feedback? Try greploops.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@care/emr/resources/tag/config_spec.py`:
- Around line 71-72: The facility_organization and organization fields both
default to None, making omitted fields indistinguishable from explicitly nulled
fields. This causes the else branches (lines 79 and 87) to unconditionally clear
relations even when those fields were not provided in the PATCH request. To fix
this, use Pydantic's model_fields_set attribute to check whether each field was
actually included in the request before executing the else branch logic that
clears the relation. Only clear the relation if the field was explicitly present
in the request data, not when it was omitted.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3967a86e-543a-4a8b-9393-8a89cff29f7f

📥 Commits

Reviewing files that changed from the base of the PR and between 9dadc15 and e3f7a7d.

📒 Files selected for processing (1)
  • care/emr/resources/tag/config_spec.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

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 `@care/emr/resources/tag/config_spec.py`:
- Around line 71-72: The facility_organization and organization fields both
default to None, making omitted fields indistinguishable from explicitly nulled
fields. This causes the else branches (lines 79 and 87) to unconditionally clear
relations even when those fields were not provided in the PATCH request. To fix
this, use Pydantic's model_fields_set attribute to check whether each field was
actually included in the request before executing the else branch logic that
clears the relation. Only clear the relation if the field was explicitly present
in the request data, not when it was omitted.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3967a86e-543a-4a8b-9393-8a89cff29f7f

📥 Commits

Reviewing files that changed from the base of the PR and between 9dadc15 and e3f7a7d.

📒 Files selected for processing (1)
  • care/emr/resources/tag/config_spec.py
🛑 Comments failed to post (1)
care/emr/resources/tag/config_spec.py (1)

71-72: 🗄️ Data Integrity & Integration | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Verify update semantics (PATCH/partial vs full update) =="
rg -n --type=py -C3 'class\s+EMRUpdateMixin|def\s+update\(|def\s+partial_update\(' care/emr

echo
echo "== Confirm TagConfig update model wiring =="
rg -n --type=py -C2 'pydantic_update_model\s*=\s*TagConfigUpdateSpec' care/emr

echo
echo "== Check whether tests cover omitted-vs-null behavior for tag config updates =="
rg -n --type=py -C2 'TagConfigUpdateSpec|facility_organization|organization' care/emr -g '*test*'

echo
echo "== Detect Pydantic major version from manifests/locks =="
fd -HI 'pyproject.toml' . -x rg -n 'pydantic|requires-python' {}
fd -HI 'requirements*.txt' . -x rg -n 'pydantic' {}
fd -HI 'poetry.lock|uv.lock|Pipfile.lock' . -x rg -n 'pydantic' {}

Repository: ohcnetwork/care

Length of output: 50372


🏁 Script executed:

# Read the actual TagConfigUpdateSpec implementation
ast-grep outline care/emr/resources/tag/config_spec.py

Repository: ohcnetwork/care

Length of output: 1566


🏁 Script executed:

# Extract Pydantic version more efficiently
grep -i "pydantic" pyproject.toml 2>/dev/null || echo "No pyproject.toml"
grep -i "pydantic" requirements*.txt 2>/dev/null | head -5 || echo "No requirements files"
find . -name "poetry.lock" -o -name "uv.lock" -o -name "Pipfile.lock" | head -1 | xargs grep -A5 "name = \"pydantic\"" 2>/dev/null | head -10

Repository: ohcnetwork/care

Length of output: 171


🏁 Script executed:

# Check if TagConfigUpdateSpec update endpoint uses PATCH or PUT semantics
rg -n "class.*TagConfigViewSet|def (update|partial_update)" care/emr -A 5 --type=py

Repository: ohcnetwork/care

Length of output: 7672


🏁 Script executed:

# Read the actual implementation of TagConfigUpdateSpec including perform_extra_deserialization
sed -n '70,90p' care/emr/resources/tag/config_spec.py

Repository: ohcnetwork/care

Length of output: 894


🏁 Script executed:

# Find Pydantic version in requirements files
find . -name "*.txt" -o -name "*.lock" | xargs grep -l "pydantic" 2>/dev/null | head -3

Repository: ohcnetwork/care

Length of output: 168


🏁 Script executed:

# Extract Pydantic version from Pipfile.lock
rg -A 10 '"pydantic"' Pipfile.lock | head -20

Repository: ohcnetwork/care

Length of output: 570


🏁 Script executed:

# Verify whether update endpoints support partial_update (PATCH semantics)
rg -B 5 -A 15 'class TagConfigViewSet' care/emr/api/viewsets/tag_config.py

Repository: ohcnetwork/care

Length of output: 1019


🏁 Script executed:

# Check if there are tests for tag config updates with omitted vs null fields
rg -n 'TagConfig.*update|facility_organization.*update|organization.*update' care/emr/tests -g '*tag*' -B 2 -A 5

Repository: ohcnetwork/care

Length of output: 153


Update omission and explicit null are indistinguishable, risking silent data loss.

With both fields defaulting to None on lines 71–72, a PATCH request that omits organization and facility_organization is treated identically to one that explicitly nulls them. The else branches on lines 79 and 87 then unconditionally clear those relations, wiping out existing data in unrelated updates.

Pydantic 2.12.5 has model_fields_set available—use it to gate assignment by actual field presence:

Suggested fix
 class TagConfigUpdateSpec(TagConfigBaseSpec):
     facility_organization: UUID4 | None = None
     organization: UUID4 | None = None

     def perform_extra_deserialization(self, is_update, obj):
-        if self.organization:
-            obj.organization = get_object_or_404(
-                Organization.objects.only("id"), external_id=self.organization
-            )
-        else:
-            obj.organization = None
-        if self.facility_organization:
-            obj.facility_organization = get_object_or_404(
-                FacilityOrganization.objects.only("id"),
-                external_id=self.facility_organization,
-                facility=obj.facility,
-            )
-        else:
-            obj.facility_organization = None
+        if "organization" in self.model_fields_set:
+            if self.organization is None:
+                obj.organization = None
+            else:
+                obj.organization = get_object_or_404(
+                    Organization.objects.only("id"), external_id=self.organization
+                )
+
+        if "facility_organization" in self.model_fields_set:
+            if self.facility_organization is None:
+                obj.facility_organization = None
+            else:
+                obj.facility_organization = get_object_or_404(
+                    FacilityOrganization.objects.only("id"),
+                    external_id=self.facility_organization,
+                    facility=obj.facility,
+                )
🤖 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 `@care/emr/resources/tag/config_spec.py` around lines 71 - 72, The
facility_organization and organization fields both default to None, making
omitted fields indistinguishable from explicitly nulled fields. This causes the
else branches (lines 79 and 87) to unconditionally clear relations even when
those fields were not provided in the PATCH request. To fix this, use Pydantic's
model_fields_set attribute to check whether each field was actually included in
the request before executing the else branch logic that clears the relation.
Only clear the relation if the field was explicitly present in the request data,
not when it was omitted.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.56%. Comparing base (c8baced) to head (e3f7a7d).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #3690   +/-   ##
========================================
  Coverage    79.55%   79.56%           
========================================
  Files          479      479           
  Lines        22996    22998    +2     
  Branches      2378     2378           
========================================
+ Hits         18295    18298    +3     
+ Misses        4096     4095    -1     
  Partials       605      605           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

2 participants