feat: add update_from_dict to update node fields from a dict - #1206
feat: add update_from_dict to update node fields from a dict#1206lancamat1 wants to merge 1 commit into
Conversation
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>
There was a problem hiding this comment.
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
|
|
||
| for name, value in data.items(): | ||
| if name in self._attributes: | ||
| attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value) |
There was a problem hiding this comment.
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>
| 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)) |
|
|
||
| for name, value in data.items(): | ||
| if name in self._attributes: | ||
| attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value) |
There was a problem hiding this comment.
is it necessary to create a new Attribute here? can this do self._get_attribute and then attr.value = value? I think that would also inherently handle self._attribute_data
|
|
||
| super().__setattr__(name, value) | ||
|
|
||
| def _update_relationship_from_dict(self, name: str, data: Any) -> None: |
There was a problem hiding this comment.
basically the same question for this method. is it necessary to create a new RelationshipManager[Sync]? can this use self._get_relationship_many or self._get_relationship_one and then all be handled within the InfrahubNodeBase class instead of different Sync/Async versions?
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
AttributeandRelationshipManagerrebuilding (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
update_from_dict()method onInfrahubNodeandInfrahubNodeSync:valueand optional properties, orfrom_poolallocations; updated attributes are marked mutated so partial updates vianode.update()include them (including clearing an optional attribute withNone).Noneclears them.[](orNone) clears them.ValueErrorbefore anything is touched, so a typo cannot half-apply an update.CoreNodeBaseprotocol so protocol-typed code can call it.Attribute,RelatedNode, andRelationshipManagermachinery.How to review
infrahub_sdk/node/node.py: shared logic and validation live onInfrahubNodeBase.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.mdxis regenerated;changelog/272.added.mdis the towncrier fragment.How to test
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
Checklist
🤖 Generated with Claude Code
Summary by cubic
Add
update_from_dict()toInfrahubNodeandInfrahubNodeSyncto 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.{ value, ...props }, orfrom_pool; changes are marked mutated andNoneclears optional values.Noneclears ONE and[]orNoneclears MANY.ValueErrorbefore any change is applied.CoreNodeBaseprotocol; existing save/update payload generation is unchanged.Written for commit b3376bc. Summary will update on new commits.