Skip to content

feat: add update_from_dict to update node fields from a dict#1206

Open
lancamat1 wants to merge 1 commit into
stablefrom
feat/update-node-from-dict
Open

feat: add update_from_dict to update node fields from a dict#1206
lancamat1 wants to merge 1 commit into
stablefrom
feat/update-node-from-dict

Conversation

@lancamat1

@lancamat1 lancamat1 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Why

Creating a node accepts a dict of attributes and relationships, but updating an existing node requires assigning each field one by one — integrations that receive external data as dicts end up hand-rolling Attribute and RelationshipManager rebuilding (as shown in the linked issue).

This PR adds update_from_dict() so an existing node can be updated from a dict in one call, mirroring the data formats accepted at creation time.

Non-goals: hierarchical relationships (parent/children) are not addressed; they are rejected as unknown fields.

Closes #272

What changed

  • New update_from_dict() method on InfrahubNode and InfrahubNodeSync:
    • Attributes accept scalars, dicts with value and optional properties, or from_pool allocations; updated attributes are marked mutated so partial updates via node.update() include them (including clearing an optional attribute with None).
    • Cardinality-one relationships accept an ID, HFID list, dict, or node object; None clears them.
    • Cardinality-many relationships replace the peer list from a list of IDs/dicts/node objects; [] (or None) clears them.
    • Unknown field names raise ValueError before anything is touched, so a typo cannot half-apply an update.
  • Method declared on the CoreNodeBase protocol so protocol-typed code can call it.
  • No changes to existing save/update payload generation — the method reuses the existing Attribute, RelatedNode, and RelationshipManager machinery.

How to review

  • infrahub_sdk/node/node.py: shared logic and validation live on InfrahubNodeBase.update_from_dict(); each client flavor implements _update_relationship_from_dict() (cardinality-one reuses the existing __setattr__ path, cardinality-many rebuilds the manager with _has_update = True).
  • docs/.../node.mdx is regenerated; changelog/272.added.md is the towncrier fragment.

How to test

uv run pytest tests/unit/sdk/test_node.py -k update_from_dict

10 new test cases (5 scenarios × async/sync) covering attribute scalars, attribute dicts with properties, both relationship cardinalities, clearing relationships, and the unknown-field error with payload assertions via _generate_input_data().

Impact & rollout

  • Backward compatibility: purely additive, no changes to existing behavior.

Checklist

  • Tests added/updated
  • Changelog entry added
  • External docs updated (generated SDK reference)

🤖 Generated with Claude Code


Summary by cubic

Add update_from_dict() to InfrahubNode and InfrahubNodeSync to update multiple attributes and relationships from a dict in one call, matching create-time payloads. This simplifies integrations and validates keys up front to prevent partial updates.

  • New Features
    • Attributes accept scalars, { value, ...props }, or from_pool; changes are marked mutated and None clears optional values.
    • Relationships accept an ID, dict, node object, or lists for cardinality-many; None clears ONE and [] or None clears MANY.
    • Unknown fields raise ValueError before any change is applied.
    • Method added to the CoreNodeBase protocol; existing save/update payload generation is unchanged.

Written for commit b3376bc. Summary will update on new commits.

Review in cubic

Allow updating several attributes and relationships of an existing
InfrahubNode or InfrahubNodeSync at once from a dict, mirroring the
data format accepted when creating a node. Attribute values accept
scalars, dicts with value and properties, or from_pool allocations;
relationships accept IDs, dicts, node objects, or lists of those for
cardinality-many. None clears a cardinality-one relationship and []
clears a cardinality-many one. Unknown field names raise ValueError
and leave the node unmodified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lancamat1
lancamat1 requested a review from a team as a code owner July 22, 2026 13:27
@lancamat1 lancamat1 added the type/feature New feature or request label Jul 22, 2026
@github-actions github-actions Bot added the type/documentation Improvements or additions to documentation label Jul 22, 2026

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infrahub_sdk/node/node.py">

<violation number="1" location="infrahub_sdk/node/node.py:324">
P2: Reusing a `from_pool` update payload mutates it on the first call, so later calls no longer allocate from the pool. Copy the supplied value before constructing `Attribute` so this public update helper does not alter caller data.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread infrahub_sdk/node/node.py

for name, value in data.items():
if name in self._attributes:
attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value)

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.

P2: Reusing a from_pool update payload mutates it on the first call, so later calls no longer allocate from the pool. Copy the supplied value before constructing Attribute so this public update helper does not alter caller data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At infrahub_sdk/node/node.py, line 324:

<comment>Reusing a `from_pool` update payload mutates it on the first call, so later calls no longer allocate from the pool. Copy the supplied value before constructing `Attribute` so this public update helper does not alter caller data.</comment>

<file context>
@@ -294,6 +294,42 @@ def _get_request_context(self, request_context: RequestContext | None = None) ->
+
+        for name, value in data.items():
+            if name in self._attributes:
+                attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value)
+                attribute.value_has_been_mutated = True
+                self._attribute_data[name] = attribute
</file context>
Suggested change
attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value)
attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=deepcopy(value))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/documentation Improvements or additions to documentation type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feature: Be able to update different attributes and relationships of a node with a dict

1 participant