-
Notifications
You must be signed in to change notification settings - Fork 11
feat: add update_from_dict to update node fields from a dict #1206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: stable
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Added a new `update_from_dict()` method on `InfrahubNode` and `InfrahubNodeSync` to update several attributes and relationships of an existing node 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. Passing `None` clears a cardinality-one relationship and `[]` clears a cardinality-many relationship. Unknown field names raise a `ValueError` and leave the node unmodified. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -294,6 +294,42 @@ def _get_request_context(self, request_context: RequestContext | None = None) -> | |
| def _init_relationships(self, data: dict | None = None) -> None: | ||
| pass | ||
|
|
||
| def update_from_dict(self, data: dict[str, Any]) -> None: | ||
| """Update attributes and relationships of this node from a dict. | ||
|
|
||
| Values accept the same formats as node creation: for attributes, a scalar value, | ||
| a dict with a ``value`` key and optional properties, or a dict with a | ||
| ``from_pool`` key; for relationships, an ID, a dict describing the peer, a node | ||
| object, or a list of those for cardinality-many. Pass ``None`` to clear a | ||
| cardinality-one relationship and ``[]`` (or ``None``) to clear a | ||
| cardinality-many relationship. | ||
|
|
||
| Args: | ||
| data (dict[str, Any]): Mapping of attribute and relationship names to their | ||
| new values. | ||
|
|
||
| Raises: | ||
| ValueError: If a key does not match any attribute or relationship of the | ||
| schema. The node is left unmodified in that case. | ||
|
|
||
| """ | ||
| unknown_fields = [key for key in data if key not in self._attributes and key not in self._relationships] | ||
| if unknown_fields: | ||
| raise ValueError( | ||
| f"Unable to update {self._schema.kind}, unknown field(s): {', '.join(sorted(unknown_fields))}" | ||
| ) | ||
|
|
||
| for name, value in data.items(): | ||
| if name in self._attributes: | ||
| attribute = Attribute(name=name, schema=self._schema.get_attribute(name=name), data=value) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it necessary to create a new |
||
| attribute.value_has_been_mutated = True | ||
| self._attribute_data[name] = attribute | ||
| else: | ||
| self._update_relationship_from_dict(name=name, data=value) | ||
|
|
||
| def _update_relationship_from_dict(self, name: str, data: Any) -> None: | ||
| raise NotImplementedError | ||
|
|
||
| def __repr__(self) -> str: | ||
| if self.display_label: | ||
| return self.display_label | ||
|
|
@@ -980,6 +1016,23 @@ def __setattr__(self, name: str, value: Any) -> None: | |
|
|
||
| super().__setattr__(name, value) | ||
|
|
||
| def _update_relationship_from_dict(self, name: str, data: Any) -> None: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. basically the same question for this method. is it necessary to create a new |
||
| rel_schema = self._schema.get_relationship(name=name) | ||
| if rel_schema.cardinality == RelationshipCardinality.ONE: | ||
| setattr(self, name, data) | ||
| return | ||
|
|
||
| manager = RelationshipManager( | ||
| name=name, | ||
| client=self._client, | ||
| node=self, | ||
| branch=self._branch, | ||
| schema=rel_schema, | ||
| data=data if data is not None else [], | ||
| ) | ||
| manager._has_update = True | ||
| self._relationship_cardinality_many_data[name] = manager | ||
|
|
||
| async def generate(self, nodes: list[str] | None = None) -> None: | ||
| """Trigger artifact generation for this artifact definition. | ||
|
|
||
|
|
@@ -2171,6 +2224,23 @@ def __setattr__(self, name: str, value: Any) -> None: | |
|
|
||
| super().__setattr__(name, value) | ||
|
|
||
| def _update_relationship_from_dict(self, name: str, data: Any) -> None: | ||
| rel_schema = self._schema.get_relationship(name=name) | ||
| if rel_schema.cardinality == RelationshipCardinality.ONE: | ||
| setattr(self, name, data) | ||
| return | ||
|
|
||
| manager = RelationshipManagerSync( | ||
| name=name, | ||
| client=self._client, | ||
| node=self, | ||
| branch=self._branch, | ||
| schema=rel_schema, | ||
| data=data if data is not None else [], | ||
| ) | ||
| manager._has_update = True | ||
| self._relationship_cardinality_many_data[name] = manager | ||
|
|
||
| def generate(self, nodes: list[str] | None = None) -> None: | ||
| """Trigger artifact generation for this artifact definition. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Reusing a
from_poolupdate payload mutates it on the first call, so later calls no longer allocate from the pool. Copy the supplied value before constructingAttributeso this public update helper does not alter caller data.Prompt for AI agents