Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/+get_by_hfid.deprecated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The method `get_by_hfid` on the object Store has been deprecated, use `get(key=[hfid])` instead
1 change: 1 addition & 0 deletions changelog/+internal_id.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
All nodes generated by the SDK will now be assigned an `internal_id` (`_internal_id`). This ID has no significance outside of the SDK.
1 change: 1 addition & 0 deletions changelog/+store.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The object store has been refactored to support more use cases in the future and it now properly support branches.
1 change: 1 addition & 0 deletions changelog/+store_init.deprecated.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Using a Store without specifying a default branch is now deprecated and will be removed in a future version.
12 changes: 6 additions & 6 deletions infrahub_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
self.schema = InfrahubSchema(self)
self.branch = InfrahubBranchManager(self)
self.object_store = ObjectStore(self)
self.store = NodeStore()
self.store = NodeStore(default_branch=self.default_branch)
self.task = InfrahubTaskManager(self)
self.concurrent_execution_limit = asyncio.Semaphore(self.max_concurrent_execution)
self._request_method: AsyncRequester = self.config.requester or self._default_request_method
Expand Down Expand Up @@ -840,11 +840,11 @@
if populate_store:
for node in nodes:
if node.id:
self.store.set(key=node.id, node=node)
self.store.set(node=node)
related_nodes = list(set(related_nodes))
for node in related_nodes:
if node.id:
self.store.set(key=node.id, node=node)
self.store.set(node=node)
return nodes

def clone(self) -> InfrahubClient:
Expand Down Expand Up @@ -1529,7 +1529,7 @@
self.schema = InfrahubSchemaSync(self)
self.branch = InfrahubBranchManagerSync(self)
self.object_store = ObjectStoreSync(self)
self.store = NodeStoreSync()
self.store = NodeStoreSync(default_branch=self.default_branch)
self.task = InfrahubTaskManagerSync(self)
self._request_method: SyncRequester = self.config.sync_requester or self._default_request_method
self.group_context = InfrahubGroupContextSync(self)
Expand Down Expand Up @@ -1997,11 +1997,11 @@
if populate_store:
for node in nodes:
if node.id:
self.store.set(key=node.id, node=node)
self.store.set(node=node)
related_nodes = list(set(related_nodes))
for node in related_nodes:
if node.id:
self.store.set(key=node.id, node=node)
self.store.set(node=node)

Check warning on line 2004 in infrahub_sdk/client.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/client.py#L2004

Added line #L2004 was not covered by tests
return nodes

@overload
Expand Down
8 changes: 6 additions & 2 deletions infrahub_sdk/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ def __init__(self, message: str | None = None):
class NodeNotFoundError(Error):
def __init__(
self,
node_type: str,
identifier: Mapping[str, list[str]],
message: str = "Unable to find the node in the database.",
branch_name: str | None = None,
node_type: str | None = None,
):
self.node_type = node_type
self.node_type = node_type or "unknown"
self.identifier = identifier
self.branch_name = branch_name

Expand All @@ -88,6 +88,10 @@ def __str__(self) -> str:
"""


class NodeInvalidError(NodeNotFoundError):
pass


class ResourceNotDefinedError(Error):
"""Raised when trying to access a resource that hasn't been defined."""

Expand Down
2 changes: 1 addition & 1 deletion infrahub_sdk/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@

for node in self._nodes + self._related_nodes:
if node.id:
self._init_client.store.set(key=node.id, node=node)
self._init_client.store.set(node=node)

Check warning on line 140 in infrahub_sdk/generator.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/generator.py#L140

Added line #L140 was not covered by tests

@abstractmethod
async def generate(self, data: dict) -> None:
Expand Down
41 changes: 34 additions & 7 deletions infrahub_sdk/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
)
from .graphql import Mutation, Query
from .schema import GenericSchemaAPI, RelationshipCardinality, RelationshipKind
from .utils import compare_lists, get_flat_value
from .utils import compare_lists, generate_short_id, get_flat_value
from .uuidt import UUIDT

if TYPE_CHECKING:
Expand Down Expand Up @@ -43,6 +43,20 @@
"calling generate is only supported for CoreArtifactDefinition nodes"
)

HFID_STR_SEPARATOR = "__"


def parse_human_friendly_id(hfid: str | list[str]) -> tuple[str | None, list[str]]:
"""Parse a human friendly ID into a kind and an identifier."""
if isinstance(hfid, str):
hfid_parts = hfid.split(HFID_STR_SEPARATOR)
if len(hfid_parts) == 1:
return None, hfid_parts
return hfid_parts[0], hfid_parts[1:]
if isinstance(hfid, list):
return None, hfid
raise ValueError(f"Invalid human friendly ID: {hfid}")

Check warning on line 58 in infrahub_sdk/node.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/node.py#L57-L58

Added lines #L57 - L58 were not covered by tests


class Attribute:
"""Represents an attribute of a Node, including its schema, value, and properties."""
Expand Down Expand Up @@ -340,10 +354,10 @@
return self._peer # type: ignore[return-value]

if self.id and self.typename:
return self._client.store.get(key=self.id, kind=self.typename) # type: ignore[return-value]
return self._client.store.get(key=self.id, kind=self.typename, branch=self._branch) # type: ignore[return-value]

if self.hfid_str:
return self._client.store.get_by_hfid(key=self.hfid_str) # type: ignore[return-value]
return self._client.store.get(key=self.hfid_str, branch=self._branch) # type: ignore[return-value]

Check warning on line 360 in infrahub_sdk/node.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/node.py#L360

Added line #L360 was not covered by tests

raise ValueError("Node must have at least one identifier (ID or HFID) to query it.")

Expand Down Expand Up @@ -387,10 +401,10 @@
return self._peer # type: ignore[return-value]

if self.id and self.typename:
return self._client.store.get(key=self.id, kind=self.typename) # type: ignore[return-value]
return self._client.store.get(key=self.id, kind=self.typename, branch=self._branch) # type: ignore[return-value]

if self.hfid_str:
return self._client.store.get_by_hfid(key=self.hfid_str) # type: ignore[return-value]
return self._client.store.get(key=self.hfid_str, branch=self._branch) # type: ignore[return-value]

Check warning on line 407 in infrahub_sdk/node.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/node.py#L407

Added line #L407 was not covered by tests

raise ValueError("Node must have at least one identifier (ID or HFID) to query it.")

Expand Down Expand Up @@ -678,6 +692,11 @@
self._branch = branch
self._existing: bool = True

# Generate a unique ID only to be used inside the SDK
# The format if this ID is purposely different from the ID used by the API
# This is done to avoid confusion and potential conflicts between the IDs
self._internal_id = generate_short_id()

self.id = data.get("id", None) if isinstance(data, dict) else None
self.display_label: str | None = data.get("display_label", None) if isinstance(data, dict) else None
self.typename: str | None = data.get("__typename", schema.kind) if isinstance(data, dict) else schema.kind
Expand All @@ -694,6 +713,9 @@
self._init_attributes(data)
self._init_relationships(data)

def get_branch(self) -> str:
return self._branch

def get_path_value(self, path: str) -> Any:
path_parts = path.split("__")
return_value = None
Expand Down Expand Up @@ -794,6 +816,11 @@
def get_kind(self) -> str:
return self._schema.kind

def get_all_kinds(self) -> list[str]:
if hasattr(self._schema, "inherit_from"):
return [self._schema.kind] + self._schema.inherit_from
return [self._schema.kind]

Check warning on line 822 in infrahub_sdk/node.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/node.py#L822

Added line #L822 was not covered by tests

def is_ip_prefix(self) -> bool:
builtin_ipprefix_kind = "BuiltinIPPrefix"
return self.get_kind() == builtin_ipprefix_kind or builtin_ipprefix_kind in self._schema.inherit_from # type: ignore[union-attr]
Expand Down Expand Up @@ -1201,7 +1228,7 @@
else:
await self._client.group_context.add_related_nodes(ids=[self.id], update_group_context=update_group_context)

self._client.store.set(key=self.id, node=self)
self._client.store.set(node=self)

async def generate_query_data(
self,
Expand Down Expand Up @@ -1726,7 +1753,7 @@
else:
self._client.group_context.add_related_nodes(ids=[self.id], update_group_context=update_group_context)

self._client.store.set(key=self.id, node=self)
self._client.store.set(node=self)

def generate_query_data(
self,
Expand Down
9 changes: 8 additions & 1 deletion infrahub_sdk/protocols_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ class AnyAttributeOptional(Attribute):
@runtime_checkable
class CoreNodeBase(Protocol):
_schema: MainSchemaTypes
id: str
_internal_id: str
id: str # NOTE this is incorrect, should be str | None
display_label: str | None

@property
Expand All @@ -153,10 +154,16 @@ def hfid(self) -> list[str] | None: ...
@property
def hfid_str(self) -> str | None: ...

def get_human_friendly_id(self) -> list[str] | None: ...

def get_human_friendly_id_as_string(self, include_kind: bool = False) -> str | None: ...

def get_kind(self) -> str: ...

def get_all_kinds(self) -> list[str]: ...

def get_branch(self) -> str: ...

def is_ip_prefix(self) -> bool: ...

def is_ip_address(self) -> bool: ...
Expand Down
Loading