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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang

<!-- towncrier release notes start -->

## [1.9.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.9.0) - 2025-03-21

### Added

- Add 'schema_hash' parameter to client.schema.all to only optionally refresh the schema if the provided hash differs from what the client has already cached. ([#152](https://github.com/opsmill/infrahub-sdk-python/issues/152))

### Changed

- CoreStandardGroups created or updated by a generator in Infrahub are now stored as a member of the CoreGeneratorGroup. Previously they were being stored as children of the CoreGeneratorGroup.

### Fixed

- The SDK client query methods (get, filters, all) default behaviour has changed. The query methods will store the retrieved nodes in the internal store by default, where previously this behaviour had to be enabled explicitly using the `populate_store` argument. ([#15](https://github.com/opsmill/infrahub-sdk-python/issues/15))

## [1.8.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.8.0) - 2025-03-19

### Deprecated
Expand Down
44 changes: 31 additions & 13 deletions docs/docs/python-sdk/guides/store.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,39 @@ The store can also be used to store Nodes that we retrieve using the client quer

## Storing nodes in the store

Nodes retrieved from Infrahub using a query method, will be stored in the internal store when you set the `populate_store` argument to `True`.
Nodes stored in the store using this method can be retrieved using their `id` as the key in the store.
Nodes retrieved from Infrahub using a the SDK client's query methods, such as the `get` `filters` or `all` method, will be automatically stored in the internal store. Nodes stored in the store using this method can be retrieved using their `id` as the key in the store.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=True)
tag = await client.get(kind="BuiltinTag", name__value="RED")
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED")
```

</TabItem>
</Tabs>

This behaviour may not be desirable in all scenarios, therefor you can explicitly disable this behaviour by setting the `populate_store` argument to `False` when calling the query methods.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=True)
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
```

</TabItem>
Expand All @@ -42,15 +60,15 @@ You can store nodes in the object store manually using the `set` method. This ha
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED")
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
```

</TabItem>
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED")
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
```

Expand All @@ -60,13 +78,13 @@ You can store nodes in the object store manually using the `set` method. This ha
## Retrieving object from the store

Nodes can be retrieved from the internal store using the key that was used to store them.
For nodes that are stored using the `populate_store` argument on a query method, this will be their `id`.
For nodes that are stored by the client's query methods, this will be their `id`.

<Tabs groupId="async-sync">
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=True)
tag = await client.get(kind="BuiltinTag", name__value="RED")
tag_in_store = client.store.get(key=tag.id)
assert tag == tag_in_store
```
Expand All @@ -75,7 +93,7 @@ For nodes that are stored using the `populate_store` argument on a query method,
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=True)
tag = client.get(kind="BuiltinTag", name__value="RED")
tag_in_store = client.store.get(key=tag.id)
assert tag == tag_in_store
```
Expand All @@ -89,7 +107,7 @@ For nodes that have been added manually to the store, this will be the key that
<TabItem value="Async" default>

```python
tag = await client.get(kind="BuiltinTag", name__value="RED")
tag = await client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
tag_in_store = client.store.get(key=tag.name.value)
assert tag == tag_in_store
Expand All @@ -99,7 +117,7 @@ For nodes that have been added manually to the store, this will be the key that
<TabItem value="Sync" default>

```python
tag = client.get(kind="BuiltinTag", name__value="RED")
tag = client.get(kind="BuiltinTag", name__value="RED", populate_store=False)
client.store.set(key=tag.name.value, node=tag)
tag_in_store = client.store.get(key=tag.name.value)
assert tag == tag_in_store
Expand Down Expand Up @@ -142,7 +160,7 @@ You can use a custom store, outside of the Infrahub SDK client. Storing or retri
from infrahub_sdk.store import NodeStore
store = NodeStore()

device = await client.get(kind="TestDevice", name__value="atl1-edge1")
device = await client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=False)
store.set(key=device.name.value, node=device)
store.get(key=device.name.value)
```
Expand All @@ -154,7 +172,7 @@ You can use a custom store, outside of the Infrahub SDK client. Storing or retri
from infrahub_sdk.store import NodeStoreSync
store = NodeStoreSync()

device = await client.get(kind="TestDevice", name__value="atl1-edge1")
device = await client.get(kind="TestDevice", name__value="atl1-edge1", populate_store=False)
store.set(key=device.name.value, node=device)
store.get(key=device.name.value)
```
Expand Down
12 changes: 6 additions & 6 deletions infrahub_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ async def get(
hfid: list[str] | None = None,
include: list[str] | None = None,
exclude: list[str] | None = None,
populate_store: bool = False,
populate_store: bool = True,
fragment: bool = False,
prefetch_relationships: bool = False,
property: bool = False,
Expand Down Expand Up @@ -624,7 +624,7 @@ async def all(
at: Timestamp | None = None,
branch: str | None = None,
timeout: int | None = None,
populate_store: bool = False,
populate_store: bool = True,
offset: int | None = None,
limit: int | None = None,
include: list[str] | None = None,
Expand Down Expand Up @@ -720,7 +720,7 @@ async def filters(
at: Timestamp | None = None,
branch: str | None = None,
timeout: int | None = None,
populate_store: bool = False,
populate_store: bool = True,
offset: int | None = None,
limit: int | None = None,
include: list[str] | None = None,
Expand Down Expand Up @@ -1745,7 +1745,7 @@ def all(
at: Timestamp | None = None,
branch: str | None = None,
timeout: int | None = None,
populate_store: bool = False,
populate_store: bool = True,
offset: int | None = None,
limit: int | None = None,
include: list[str] | None = None,
Expand Down Expand Up @@ -1876,7 +1876,7 @@ def filters(
at: Timestamp | None = None,
branch: str | None = None,
timeout: int | None = None,
populate_store: bool = False,
populate_store: bool = True,
offset: int | None = None,
limit: int | None = None,
include: list[str] | None = None,
Expand Down Expand Up @@ -2129,7 +2129,7 @@ def get(
hfid: list[str] | None = None,
include: list[str] | None = None,
exclude: list[str] | None = None,
populate_store: bool = False,
populate_store: bool = True,
fragment: bool = False,
prefetch_relationships: bool = False,
property: bool = False,
Expand Down
41 changes: 6 additions & 35 deletions infrahub_sdk/query_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
self.related_node_ids: list[str] = []
self.related_group_ids: list[str] = []
self.unused_member_ids: list[str] | None = None
self.unused_child_ids: list[str] | None = None
self.previous_members: list[RelatedNodeBase] | None = None
self.previous_children: list[RelatedNodeBase] | None = None
self.identifier: str | None = None
Expand Down Expand Up @@ -88,15 +87,14 @@
async def get_group(self, store_peers: bool = False) -> InfrahubNode | None:
group_name = self._generate_group_name()
try:
group = await self.client.get(kind=self.group_type, name__value=group_name, include=["members", "children"])
group = await self.client.get(kind=self.group_type, name__value=group_name, include=["members"])

Check warning on line 90 in infrahub_sdk/query_groups.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/query_groups.py#L90

Added line #L90 was not covered by tests
except NodeNotFoundError:
return None

if not store_peers:
return group

self.previous_members = group.members.peers # type: ignore[attr-defined]
self.previous_children = group.children.peers # type: ignore[attr-defined]
return group

async def delete_unused(self) -> None:
Expand All @@ -105,11 +103,6 @@
if member.id in self.unused_member_ids and member.typename:
await self.client.delete(kind=member.typename, id=member.id)

if self.previous_children and self.unused_child_ids:
for child in self.previous_children:
if child.id in self.unused_child_ids and child.typename:
await self.client.delete(kind=child.typename, id=child.id)

async def add_related_nodes(self, ids: list[str], update_group_context: bool | None = None) -> None:
"""
Add related Nodes IDs to the context.
Expand Down Expand Up @@ -140,15 +133,9 @@
"""
Create or update (using upsert) a CoreStandardGroup to store all the Nodes and Groups used during an execution.
"""
children: list[str] = []
members: list[str] = []

if self.related_group_ids:
children = self.related_group_ids
if self.related_node_ids:
members = self.related_node_ids
members: list[str] = self.related_group_ids + self.related_node_ids

Check warning on line 136 in infrahub_sdk/query_groups.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/query_groups.py#L136

Added line #L136 was not covered by tests

if not children and not members:
if not members:
return

group_name = self._generate_group_name()
Expand All @@ -164,7 +151,6 @@
name=group_name,
description=description,
members=members,
children=children,
)
await group.save(allow_upsert=True, update_group_context=False)

Expand All @@ -173,7 +159,6 @@

# Calculate how many nodes should be deleted
self.unused_member_ids = set(existing_group.members.peer_ids) - set(members) # type: ignore
self.unused_child_ids = set(existing_group.children.peer_ids) - set(children) # type: ignore

if not self.delete_unused_nodes:
return
Expand All @@ -194,15 +179,14 @@
def get_group(self, store_peers: bool = False) -> InfrahubNodeSync | None:
group_name = self._generate_group_name()
try:
group = self.client.get(kind=self.group_type, name__value=group_name, include=["members", "children"])
group = self.client.get(kind=self.group_type, name__value=group_name, include=["members"])

Check warning on line 182 in infrahub_sdk/query_groups.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/query_groups.py#L182

Added line #L182 was not covered by tests
except NodeNotFoundError:
return None

if not store_peers:
return group

self.previous_members = group.members.peers # type: ignore[attr-defined]
self.previous_children = group.children.peers # type: ignore[attr-defined]
return group

def delete_unused(self) -> None:
Expand All @@ -211,11 +195,6 @@
if member.id in self.unused_member_ids and member.typename:
self.client.delete(kind=member.typename, id=member.id)

if self.previous_children and self.unused_child_ids:
for child in self.previous_children:
if child.id in self.unused_child_ids and child.typename:
self.client.delete(kind=child.typename, id=child.id)

def add_related_nodes(self, ids: list[str], update_group_context: bool | None = None) -> None:
"""
Add related Nodes IDs to the context.
Expand Down Expand Up @@ -246,15 +225,9 @@
"""
Create or update (using upsert) a CoreStandardGroup to store all the Nodes and Groups used during an execution.
"""
children: list[str] = []
members: list[str] = []

if self.related_group_ids:
children = self.related_group_ids
if self.related_node_ids:
members = self.related_node_ids
members: list[str] = self.related_node_ids + self.related_group_ids

Check warning on line 228 in infrahub_sdk/query_groups.py

View check run for this annotation

Codecov / codecov/patch

infrahub_sdk/query_groups.py#L228

Added line #L228 was not covered by tests

if not children and not members:
if not members:
return

group_name = self._generate_group_name()
Expand All @@ -270,7 +243,6 @@
name=group_name,
description=description,
members=members,
children=children,
)
group.save(allow_upsert=True, update_group_context=False)

Expand All @@ -279,7 +251,6 @@

# Calculate how many nodes should be deleted
self.unused_member_ids = set(existing_group.members.peer_ids) - set(members) # type: ignore
self.unused_child_ids = set(existing_group.children.peer_ids) - set(children) # type: ignore

if not self.delete_unused_nodes:
return
Expand Down
Loading