Skip to content

Conversation

@gmazoyer
Copy link
Contributor

@gmazoyer gmazoyer commented Aug 27, 2025

This change fixes the use of my_object.my_relationship = None by ensuring this is removing the link between an object and a related one tied together by an optional relationship of cardinality one.

Summary by CodeRabbit

  • New Features
    • Allow unsetting optional one-to-one relationships by assigning None.
  • Bug Fixes
    • Correctly serialize None to GraphQL null, preventing incorrect “None” strings.
  • Tests
    • Added integration coverage to verify nullifying a HFID-based relationship removes the link.
    • Updated unit tests to expect None for optional relationships in generated input data.
  • Documentation
    • Added changelog entry describing the ability to unset optional one-to-one relationships via None.

@coderabbitai
Copy link

coderabbitai bot commented Aug 27, 2025

Walkthrough

The change introduces explicit None handling across GraphQL conversion and node input generation. In infrahub_sdk/graphql.py, convert_to_graphql_as_string now accepts Any and converts None to null. In infrahub_sdk/node/node.py, optional one-to-one relationships that are uninitialized are set to None in generated input; strip logic preserves optional relationships while removing truly unmodified items. Tests are updated to expect None fields and to validate unsetting a HFID-based relationship via None. A changelog entry documents unsetting optional cardinality-one relationships by assigning None. No public API changes besides the relaxed function signature.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4880a12 and b49f915.

📒 Files selected for processing (1)
  • infrahub_sdk/graphql.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • infrahub_sdk/graphql.py
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gma-20250827-ihs152

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or Summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link

codecov bot commented Aug 27, 2025

Codecov Report

❌ Patch coverage is 86.66667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
infrahub_sdk/node/node.py 83.33% 0 Missing and 2 partials ⚠️
@@            Coverage Diff             @@
##           stable     #515      +/-   ##
==========================================
+ Coverage   75.74%   75.76%   +0.02%     
==========================================
  Files         100      100              
  Lines        8846     8858      +12     
  Branches     1732     1737       +5     
==========================================
+ Hits         6700     6711      +11     
  Misses       1670     1670              
- Partials      476      477       +1     
Flag Coverage Δ
integration-tests 34.74% <80.00%> (+0.07%) ⬆️
python-3.10 48.25% <80.00%> (+0.03%) ⬆️
python-3.11 48.27% <80.00%> (+0.08%) ⬆️
python-3.12 48.22% <80.00%> (+0.03%) ⬆️
python-3.13 48.22% <80.00%> (+0.03%) ⬆️
python-3.9 46.92% <80.00%> (+0.03%) ⬆️
python-filler-3.12 25.06% <6.66%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
infrahub_sdk/graphql.py 99.10% <100.00%> (+0.01%) ⬆️
infrahub_sdk/node/node.py 76.13% <83.33%> (+0.17%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 27, 2025

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: b49f915
Status: ✅  Deploy successful!
Preview URL: https://94fa5ca1.infrahub-sdk-python.pages.dev
Branch Preview URL: https://gma-20250827-ihs152.infrahub-sdk-python.pages.dev

View logs

"id": "llllllll-llll-llll-llll-llllllllllll",
"name": {"value": "DFW"},
# "primary_tag": None,
"primary_tag": None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think having these should be alright. This does not seem to have a negative impact.

if item in data and (data_item in ({}, []) or (data_item is None and original_data_item_is_none)):
data.pop(item)

def _strip_unmodified(self, data: dict, variables: dict) -> tuple[dict, dict]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The whole "strip unmodified" code feels difficult to read. I think we probably want to take on the burden of trying to improve it to make it easier to maintain.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree, we should probably look at the schema of the node as an initial starting point when refactoring and figure out what we want this to look like.

@gmazoyer gmazoyer marked this pull request as ready for review August 27, 2025 13:33
@gmazoyer gmazoyer requested a review from a team August 27, 2025 13:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
infrahub_sdk/graphql.py (1)

11-13: Type hint nit: Any makes the union redundant

Including Any in the parameter type negates the benefit of the detailed union. Consider removing Any (or narrowing types) to improve static checking.

-def convert_to_graphql_as_string(  # noqa: PLR0911
-    value: str | bool | list | BaseModel | Enum | Any | None, convert_enum: bool = False
-) -> str:
+def convert_to_graphql_as_string(  # noqa: PLR0911
+    value: str | bool | int | float | list | BaseModel | Enum | None, convert_enum: bool = False
+) -> str:
infrahub_sdk/node/node.py (1)

313-314: Remove the informal aside in a TODO

The Thanos quote is funny but adds noise to already complex logic. Consider removing to keep focus.

-        # TODO: I do not feel _great_ about this
-        # -> I don't even know who you are (but this is not great indeed) -- gmazoyer (quoting Thanos)
+        # TODO: This logic could be simplified/clarified.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ab99599 and 406c2c1.

📒 Files selected for processing (4)
  • infrahub_sdk/graphql.py (1 hunks)
  • infrahub_sdk/node/node.py (3 hunks)
  • tests/integration/test_infrahub_client.py (1 hunks)
  • tests/unit/sdk/test_node.py (5 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

When implementing Infrahub checks, subclass InfrahubCheck and override validate(data); do not implement or rely on a check() method

Files:

  • tests/integration/test_infrahub_client.py
  • infrahub_sdk/graphql.py
  • tests/unit/sdk/test_node.py
  • infrahub_sdk/node/node.py
tests/integration/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Place and write integration tests under tests/integration/ (tests against real Infrahub instances)

Files:

  • tests/integration/test_infrahub_client.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use the custom pytest plugin (infrahub_sdk.pytest_plugin) fixtures for clients, configuration, and Infrahub-specific testing

Files:

  • tests/integration/test_infrahub_client.py
  • tests/unit/sdk/test_node.py
tests/unit/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Place and write unit tests under tests/unit/ (isolated component tests)

Files:

  • tests/unit/sdk/test_node.py
🧬 Code graph analysis (3)
tests/integration/test_infrahub_client.py (3)
infrahub_sdk/testing/schemas/animal.py (1)
  • person_sophia (161-164)
infrahub_sdk/node/node.py (2)
  • save (600-627)
  • save (1226-1249)
infrahub_sdk/client.py (14)
  • get (364-380)
  • get (383-399)
  • get (402-418)
  • get (421-437)
  • get (440-456)
  • get (459-475)
  • get (477-535)
  • get (2040-2056)
  • get (2059-2075)
  • get (2078-2094)
  • get (2097-2113)
  • get (2116-2132)
  • get (2135-2151)
  • get (2153-2211)
infrahub_sdk/graphql.py (1)
infrahub_sdk/protocols_base.py (1)
  • Enum (111-112)
infrahub_sdk/node/node.py (3)
infrahub_sdk/schema/main.py (1)
  • RelationshipCardinality (17-19)
infrahub_sdk/protocols_base.py (2)
  • initialized (27-27)
  • RelatedNodeBase (13-40)
infrahub_sdk/node/related_node.py (4)
  • initialized (107-108)
  • get (216-226)
  • get (263-273)
  • RelatedNodeBase (17-179)
⏰ 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). (1)
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (8)
infrahub_sdk/graphql.py (1)

11-15: Correctly serializing None to GraphQL null

Returning "null" for Python None is correct and unblocks unsetting one-to-one relationships in mutations. Looks good.

tests/integration/test_infrahub_client.py (1)

173-178: Integration coverage for nullifying one-to-one relationship

Good addition verifying that setting favorite_animal = None removes the link on save and re-fetch.

infrahub_sdk/node/node.py (2)

237-240: Emit explicit null for optional one-to-one when uninitialized

This is the right place to surface None so the mutation payload can unset the relationship. Matches the PR objective.


328-330: Keep optional uninitialized one-to-one entries during strip

This ensures None survives stripping for optional RelatedNodeBase, enabling proper unsetting on update. Good change.

tests/unit/sdk/test_node.py (4)

1333-1338: Expect None for optional one-to-one on create

Updating expected input to include primary_tag: None aligns tests with new input generation. Good.


1585-1585: Include interface: None for IPHost

Matches the new behavior for optional one-to-one relationships. Looks correct.


1599-1599: Include site: None for IPNetwork

Consistent with emitting explicit nulls for optional one-to-one. Good.


1797-1797: Assert primary_tag: None when clearing relationship

The expectations for empty-relationship updates now explicitly verify nulling the primary_tag. This is the right assertion for the new behavior.

Also applies to: 1806-1806

@dgarros
Copy link
Contributor

dgarros commented Aug 27, 2025

Might be good to fix it in stable to include it in 1.14.1

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
infrahub_sdk/node/node.py (1)

312-323: Tighten “original is none” detection and replace the snarky TODO

Consider also treating {"id": None} as “original is none” and swap the tongue-in-cheek comment for a clear note.

Apply:

-        # TODO: I do not feel _great_ about this
-        # -> I don't even know who you are (but this is not great indeed) -- gmazoyer (quoting Thanos)
+        # Treat original as "none" when it's None, when `node` is None, or when no `id` (or id is None) is present.
         original_data_item = original_data.get(item)
         original_data_item_is_none = original_data_item is None
         if isinstance(original_data_item, dict):
             if "node" in original_data_item:
                 original_data_item_is_none = original_data_item["node"] is None
-            elif "id" not in original_data_item:
+            elif "id" not in original_data_item or original_data_item.get("id") is None:
                 original_data_item_is_none = True

(Agree with prior feedback: this “strip unmodified” block is hard to follow; factoring into small helpers would aid maintainability.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 406c2c1 and 86ffa79.

📒 Files selected for processing (2)
  • changelog/479.fixed.md (1 hunks)
  • infrahub_sdk/node/node.py (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • changelog/479.fixed.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

When implementing Infrahub checks, subclass InfrahubCheck and override validate(data); do not implement or rely on a check() method

Files:

  • infrahub_sdk/node/node.py
🧬 Code graph analysis (1)
infrahub_sdk/node/node.py (2)
infrahub_sdk/schema/main.py (1)
  • RelationshipCardinality (17-19)
infrahub_sdk/node/related_node.py (4)
  • initialized (107-108)
  • get (216-226)
  • get (263-273)
  • RelatedNodeBase (17-179)
⏰ 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). (5)
  • GitHub Check: unit-tests (3.13)
  • GitHub Check: unit-tests (3.11)
  • GitHub Check: unit-tests (3.10)
  • GitHub Check: unit-tests (3.12)
  • GitHub Check: integration-tests-latest-infrahub
🔇 Additional comments (1)
infrahub_sdk/node/node.py (1)

331-333: All required attributes present on RelationshipManagerBase — resolved

Quick sanity check confirms that RelationshipManagerBase in infrahub_sdk/node/relationship.py defines both members used by the pruning logic:

  • In the constructor (around line 32), it sets
    self.initialized: bool = False
    and
    self._has_update: bool = False.
  • Shortly afterward (around line 57), it exposes a has_update property:
    @property def has_update(self) -> bool: return self._has_update.

Since both initialized and has_update exist on RelationshipManagerBase, there’s no risk of accidental AttributeError.

@gmazoyer gmazoyer force-pushed the gma-20250827-ihs152 branch from 86ffa79 to 4880a12 Compare August 28, 2025 08:56
@gmazoyer gmazoyer changed the base branch from develop to stable August 28, 2025 08:56
@gmazoyer gmazoyer merged commit 482c969 into stable Aug 28, 2025
21 checks passed
@gmazoyer gmazoyer deleted the gma-20250827-ihs152 branch August 28, 2025 09:20
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.

bug: Unable to delete relationship of cardinality one from an InfrahubNode object

4 participants