Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b8b0bc4
Merge pull request #666 from opsmill/develop
ogenstad Dec 2, 2025
dd7a05c
Merge pull request #678 from opsmill/develop
ogenstad Dec 3, 2025
c14225a
Merge pull request #685 from opsmill/develop
ogenstad Dec 10, 2025
32728be
IHS-173 Detect if a relationship is set from a profile (#686)
gmazoyer Dec 11, 2025
aa0164a
Merge pull request #691 from opsmill/develop
ogenstad Dec 11, 2025
20d349c
Remove deprecated 'background_execution' param from client.branch.cre…
ogenstad Dec 16, 2025
186e199
Bump astral-sh/setup-uv from 4 to 7
dependabot[bot] Dec 10, 2025
b67ec03
Bump actions/checkout from 5 to 6
dependabot[bot] Dec 10, 2025
4228100
Bump DavidAnson/markdownlint-cli2-action from 21 to 22
dependabot[bot] Dec 15, 2025
77c9d1d
Bump actions/cache from 4 to 5
dependabot[bot] Dec 15, 2025
668caaa
fix: py3.14 warnings due to iscoroutinefunction
fatih-acar Dec 11, 2025
f9bd5f7
fix: upgrade pytest-asyncio for py3.14 compat
fatih-acar Dec 11, 2025
b3fc3ac
chore: add newsfragment
fatih-acar Dec 16, 2025
fb52237
Add ability to order results by metadata created_at or updated_at
ogenstad Dec 17, 2025
31a7720
Merge pull request #696 from opsmill/pog-branch-background_execution
ogenstad Dec 18, 2025
d74e043
Merge pull request #704 from opsmill/develop
ogenstad Dec 19, 2025
077bcc2
Add ability to query for node metadata
ogenstad Dec 18, 2025
fde1504
Merge pull request #700 from opsmill/pog-query-metadata-IHS-172
ogenstad Dec 19, 2025
ea109ee
Fix invalid return types
ogenstad Dec 19, 2025
64ca67a
Upgrade ty and cleanup rules
ogenstad Dec 22, 2025
1925f37
Merge pull request #706 from opsmill/pog-upgrade-ty-with-cleanup
ogenstad Dec 22, 2025
bdc4ca9
Merge pull request #705 from opsmill/pog-invalid-return-types
ogenstad Dec 22, 2025
d0dd721
Fixes for test annotations and split up rules
ogenstad Dec 22, 2025
f3edb52
Add annotations for clients and client_type
ogenstad Dec 22, 2025
504cda7
Merge pull request #707 from opsmill/pog-test-annotations
ogenstad Dec 23, 2025
7f7ee7d
Merge pull request #708 from opsmill/pog-test-node-annotations
ogenstad Dec 23, 2025
da881b4
Merge 'develop' into 'infrahub-develop' with resolved conflicts
ogenstad Dec 30, 2025
1ba4af3
Fix annotations on CTL tests
ogenstad Dec 30, 2025
9e2c560
Fix unsupported operator on MainSchemaTypes
ogenstad Dec 30, 2025
09b9254
Merge pull request #717 from opsmill/pog-develop-to-infrahub-develop-…
ogenstad Dec 30, 2025
71477e4
Merge pull request #718 from opsmill/pog-ctl-test-annotations
ogenstad Dec 30, 2025
36e2de8
Upgrade ty=0.0.8
ogenstad Dec 30, 2025
af827c1
Use pathlib instead of os for Path operations
ogenstad Dec 30, 2025
38b9ece
Merge pull request #722 from opsmill/pog-prefer-pathlib
ogenstad Dec 30, 2025
9e5ce80
Merge pull request #721 from opsmill/pog-fix-unsupported-operator
ogenstad Dec 30, 2025
4dbf8fe
Merge pull request #723 from opsmill/pog-upgrade-ty
ogenstad Dec 30, 2025
1506d95
Fix test annotations on unit tests
ogenstad Dec 30, 2025
854412b
Merge pull request #720 from opsmill/pog-unit-test-annotations
ogenstad Dec 30, 2025
be324f6
Merge branch 'develop' into 'infrahub-develop' with resolved conflicts
ogenstad Dec 30, 2025
51d4feb
Use pathlib
ogenstad Dec 30, 2025
4907fa0
Merge pull request #728 from opsmill/pog-develop-to-infrahub-develop-…
ogenstad Jan 2, 2026
4b70399
Convert and enable integration tests (#730)
gmazoyer Jan 2, 2026
7346e5e
Merge pull request #698 from opsmill/pog-metadata-ordering-IHS-172
ogenstad Jan 5, 2026
f732b77
Merge pull request #738 from opsmill/develop
ogenstad Jan 8, 2026
927892c
Add docs for metadata and ordering (#735)
minitriga Jan 8, 2026
0da2e78
bump version to v1.18.0
wvandeun Jan 8, 2026
2017aa9
update CHANGELOG for 1.18.0 release
wvandeun Jan 8, 2026
391d41c
Merge pull request #739 from opsmill/wvd-20260108-prep-release-1.18.0
wvandeun Jan 8, 2026
263945d
Revert "Fix code for HFID casting of strings that aren't UUIDs (#732…
FragmentedPacket Jan 8, 2026
d116aa3
Bump to 1.18.1 and update changelog. (#742)
FragmentedPacket Jan 9, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
with:
version: "${{ needs.prepare-environment.outputs.UV_VERSION }}"
- name: Install dependencies
run: uv sync --group lint
run: uv sync --all-groups --all-extras
- name: "Linting: ruff check"
run: "uv run ruff check ."
- name: "Linting: ruff format"
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the changes for the upcoming release can be found in <https://github.com/opsmill/infrahub/tree/develop/infrahub/python_sdk/changelog/>.

<!-- towncrier release notes start -->
## [1.18.1](https://github.com/opsmill/infrahub-sdk-python/tree/v1.18.1) - 2026-01-08

### Fixed

- Reverted #723 (Fix code for HFID casting of strings that aren't UUIDs)

## [1.18.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.18.0) - 2026-01-08

### Added

- Add ability to query for metadata on nodes to include information such as creation and update timestamps, creator and last user to update an object.
- Added ability to order nodes by metadata created_at or updated_at fields

### Removed

- The previously deprecated 'background_execution' parameter under client.branch.create() was removed.

### Fixed

- Rewrite and re-enable integration tests ([#187](https://github.com/opsmill/infrahub-sdk-python/issues/187))
- Fixed SDK including explicit `null` values for uninitialized optional relationships when creating nodes with object templates, which prevented the backend from applying template defaults. ([#630](https://github.com/opsmill/infrahub-sdk-python/issues/630))

### Housekeeping

- Fixed Python 3.14 compatibility warnings. Testing now requires pytest>=9.

## [1.17.0](https://github.com/opsmill/infrahub-sdk-python/tree/v1.17.0) - 2025-12-11

Expand Down
1 change: 0 additions & 1 deletion changelog/+1b40f022.housekeeping.md

This file was deleted.

1 change: 0 additions & 1 deletion changelog/630.fixed.md

This file was deleted.

273 changes: 273 additions & 0 deletions docs/docs/python-sdk/guides/query_data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,279 @@ By default, the [meta data or properties]($(base_url)topics/metadata) of attribu
</TabItem>
</Tabs>

## Node metadata

Node metadata provides information about when a node was created or last updated, and by whom. This includes timestamps and references to the accounts that made the changes.

### Including node metadata in queries

By default, node metadata is not included in query results. You can include it using the `include_metadata` argument of the SDK client's `all`, `filters`, or `get` method.

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

```python
device = await client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)
```

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

```python
device = client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)
```

</TabItem>
</Tabs>

### Accessing node metadata

Once metadata is included in the query, you can access it using the `get_node_metadata()` method. The metadata object contains the following fields:

- `created_at`: Timestamp when the node was created
- `created_by`: Reference to the account that created the node
- `updated_at`: Timestamp when the node was last updated
- `updated_by`: Reference to the account that last updated the node

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

```python
device = await client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)

# Get the metadata object
metadata = device.get_node_metadata()

# Access creation metadata
print(metadata.created_at) # e.g., "2024-01-15T10:30:00Z"
print(metadata.created_by.display_label) # e.g., "admin"

# Access update metadata
print(metadata.updated_at) # e.g., "2024-01-20T14:45:00Z"
print(metadata.updated_by.display_label) # e.g., "admin"
```

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

```python
device = client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)

# Get the metadata object
metadata = device.get_node_metadata()

# Access creation metadata
print(metadata.created_at) # e.g., "2024-01-15T10:30:00Z"
print(metadata.created_by.display_label) # e.g., "admin"

# Access update metadata
print(metadata.updated_at) # e.g., "2024-01-20T14:45:00Z"
print(metadata.updated_by.display_label) # e.g., "admin"
```

</TabItem>
</Tabs>

The `created_by` and `updated_by` fields are `NodeProperty` objects that include:

- `id`: The unique identifier of the account
- `display_label`: A human-readable label for the account
- `typename`: The GraphQL type name of the account

## Relationship metadata

When querying with `include_metadata=True`, you can also access metadata about relationship edges themselves. This tells you when a specific relationship (the connection between two nodes) was last modified and by whom.

### Accessing relationship metadata

Use the `get_relationship_metadata()` method on a related node to access the relationship edge metadata. This is different from node metadata - it describes when the relationship itself was created or modified, not the connected node.

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

```python
device = await client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)

# For a cardinality-one relationship
rel_metadata = device.site.get_relationship_metadata()
if rel_metadata:
print(rel_metadata.updated_at) # e.g., "2024-01-17T08:00:00Z"
print(rel_metadata.updated_by.display_label) # e.g., "admin"

# For a cardinality-many relationship
for tag in device.tags.peers:
rel_metadata = tag.get_relationship_metadata()
if rel_metadata:
print(f"Tag relationship updated at: {rel_metadata.updated_at}")
print(f"Updated by: {rel_metadata.updated_by.display_label}")
```

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

```python
device = client.get(kind="TestDevice", name__value="atl1-edge1", include_metadata=True)

# For a cardinality-one relationship
rel_metadata = device.site.get_relationship_metadata()
if rel_metadata:
print(rel_metadata.updated_at) # e.g., "2024-01-17T08:00:00Z"
print(rel_metadata.updated_by.display_label) # e.g., "admin"

# For a cardinality-many relationship
for tag in device.tags.peers:
rel_metadata = tag.get_relationship_metadata()
if rel_metadata:
print(f"Tag relationship updated at: {rel_metadata.updated_at}")
print(f"Updated by: {rel_metadata.updated_by.display_label}")
```

</TabItem>
</Tabs>

The `RelationshipMetadata` object contains:

- `updated_at`: Timestamp when the relationship was last updated
- `updated_by`: Reference to the account that last updated the relationship (a `NodeProperty` object with `id`, `display_label`, and `typename`)

:::note
Relationship metadata tracks changes to the relationship edge itself (for example, when the relationship was created or when its properties were modified), not changes to the connected nodes. For node-level metadata, use `get_node_metadata()` on the node itself.
:::

## Ordering query results

You can control the order in which query results are returned using the `order` argument. This is particularly useful when you need results sorted by metadata fields like creation or update timestamps.

### Ordering by node metadata

Use the `Order` and `NodeMetaOrder` classes along with `OrderDirection` to specify how results should be ordered.

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

```python
from infrahub_sdk.enums import OrderDirection
from infrahub_sdk.types import NodeMetaOrder, Order

# Get devices ordered by creation time (oldest first)
devices = await client.all(
kind="TestDevice",
order=Order(node_metadata=NodeMetaOrder(created_at=OrderDirection.ASC))
)

# Get devices ordered by last update time (most recent first)
devices = await client.all(
kind="TestDevice",
order=Order(node_metadata=NodeMetaOrder(updated_at=OrderDirection.DESC))
)
```

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

```python
from infrahub_sdk.enums import OrderDirection
from infrahub_sdk.types import NodeMetaOrder, Order

# Get devices ordered by creation time (oldest first)
devices = client.all(
kind="TestDevice",
order=Order(node_metadata=NodeMetaOrder(created_at=OrderDirection.ASC))
)

# Get devices ordered by last update time (most recent first)
devices = client.all(
kind="TestDevice",
order=Order(node_metadata=NodeMetaOrder(updated_at=OrderDirection.DESC))
)
```

</TabItem>
</Tabs>

The available order directions are:

- `OrderDirection.ASC`: Ascending order (oldest/smallest first)
- `OrderDirection.DESC`: Descending order (newest/largest first)

:::note
You can only order by one metadata field at a time. Specifying both `created_at` and `updated_at` in the same `NodeMetaOrder` will raise a validation error, as they are mutually exclusive.
:::

### Disabling default ordering

For performance optimization, you can disable the default ordering behavior entirely:

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

```python
from infrahub_sdk.types import Order

# Disable ordering to improve query performance
devices = await client.all(kind="TestDevice", order=Order(disable=True))
```

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

```python
from infrahub_sdk.types import Order

# Disable ordering to improve query performance
devices = client.all(kind="TestDevice", order=Order(disable=True))
```

</TabItem>
</Tabs>

### Combining metadata and ordering

You can include metadata and order results in the same query:

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

```python
from infrahub_sdk.enums import OrderDirection
from infrahub_sdk.types import NodeMetaOrder, Order

# Get the 10 most recently updated devices with their metadata
devices = await client.filters(
kind="TestDevice",
limit=10,
include_metadata=True,
order=Order(node_metadata=NodeMetaOrder(updated_at=OrderDirection.DESC))
)

for device in devices:
metadata = device.get_node_metadata()
print(f"{device.name.value} - Last updated: {metadata.updated_at}")
```

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

```python
from infrahub_sdk.enums import OrderDirection
from infrahub_sdk.types import NodeMetaOrder, Order

# Get the 10 most recently updated devices with their metadata
devices = client.filters(
kind="TestDevice",
limit=10,
include_metadata=True,
order=Order(node_metadata=NodeMetaOrder(updated_at=OrderDirection.DESC))
)

for device in devices:
metadata = device.get_node_metadata()
print(f"{device.name.value} - Last updated: {metadata.updated_at}")
```

</TabItem>
</Tabs>

## Query a node(s) in a different branch

If you want to query a node(s) in a different branch than the default branch with which the SDK client was initiated, then you can use the `branch` argument of the query methods.
Expand Down
Loading
Loading