feat(sdk): client.get_many — batched multi-kind node fetch#1108
Draft
iddocohen wants to merge 1 commit into
Draft
feat(sdk): client.get_many — batched multi-kind node fetch#1108iddocohen wants to merge 1 commit into
iddocohen wants to merge 1 commit into
Conversation
Deploying infrahub-sdk-python with
|
| Latest commit: |
5b8047c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9c30417a.infrahub-sdk-python.pages.dev |
| Branch Preview URL: | https://ic-feat-batched-multi-kind-f.infrahub-sdk-python.pages.dev |
Codecov Report✅ All modified and coverable lines are covered by tests.
@@ Coverage Diff @@
## develop #1108 +/- ##
===========================================
- Coverage 82.15% 75.40% -6.76%
===========================================
Files 138 138
Lines 11897 11965 +68
Branches 1784 1798 +14
===========================================
- Hits 9774 9022 -752
- Misses 1575 2399 +824
+ Partials 548 544 -4
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 34 files with indirect coverage changes 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
2 issues found across 5 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
iddocohen
added a commit
that referenced
this pull request
Jun 28, 2026
`{"InfraDevice": {"ids": "dev-1"}}` and
`{"InfraDevice": {"ids": ["dev-1"], "attributes": "name"}}` are easy
mistakes — a caller with one id or one attribute reaches for the bare
value rather than wrapping it in a list. Strings and bytes are
iterable in Python, so the compiler used to walk them
character-by-character: the first form sent seven one-character
GraphQL ids, and the second built a selection set of
`n { value } a { value } m { value } e { value }` because each
character passed the GraphQL-identifier regex.
Reject `str`/`bytes` explicitly for both fields and surface a
descriptive `ValidationError` instead. The original list-of-id /
list-of-attribute path is unchanged.
Reported by the cubic AI code reviewer on #1108.
Adds InfrahubClient.get_many (and the sync twin) plus the
compile_get_many_query helper. Callers pass a kind -> {ids, attributes}
spec and get back one GraphQL operation with one aliased block per kind
(k0, k1, ...) and one [ID] variable per kind (ids_0, ids_1, ...). The
cost is a single round-trip regardless of how many kinds are in the
spec — the alternative today is one client.filters call per kind, or a
hand-rolled execute_graphql with manual alias plumbing.
The compile helper validates kind and attribute names as GraphQL
identifiers up front and raises ValidationError listing every problem
before any HTTP call is made. Scalar str/bytes passed where a list is
expected for ids or attributes are rejected explicitly — Python's
iteration over a bare string would otherwise turn one id or attribute
into N one-character entries, silently producing an invalid query.
Server-side rejections still propagate as GraphQLError. Hydration
reuses InfrahubNode.from_graphql so callers get typed attribute
access; populate_store=True mirrors client.get.
Reuses existing primitives end-to-end:
- execute_graphql for the network call
- InfrahubNode.from_graphql / InfrahubNodeSync.from_graphql for hydration
- client.store.set for store population
- ValidationError / GraphQLError from infrahub_sdk.exceptions
No new exception classes, no new result types, no new dependencies.
The return is dict[str, list[InfrahubNode]].
1db243e to
5b8047c
Compare
ogenstad
reviewed
Jun 29, 2026
ogenstad
left a comment
Contributor
There was a problem hiding this comment.
As mentioned in Slack I think we should have an extra round of design for this before moving forward.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
InfrahubClient.get_many(and the sync twin): one GraphQLoperation, one aliased block per kind, one round-trip.
Today this shape costs N round-trips with per-kind
client.filters(each pulling the full default selection set), or a hand-rolledexecute_graphqlwith manual alias plumbing and a hand-written hydration loop. After this change, it's a single call.Why this is needed
The pattern — read attributes off a heterogeneous set of
(kind, id)tuples in one shot — recurs across the Infrahub ecosystem. Concrete domains that will adoptget_manyimmediately:infrahub-managing-generatorsskill, opsmill demos)client.get/client.filterscalls.execute_graphql.get_manycollapses a multi-kind sweep to one call.(kind, id)tuples for changed nodes. To render a human report you need attributes per id — current options are N gets or hand-built GraphQL.The litmus test for adoption is simple: search the opsmill org for
execute_graphql(and look at what those queries do. Whenever the query is a hand-built alias-block multi-fetch (rather than a genuinely custom GraphQL operation the SDK can't express), that's a future caller ofget_many. The canonical example today is_fetch_attributesin opsmill/infrahub-demo-reachability-check — that method drops from ~70 lines of query construction + hydration to ~10 lines of domain logic onceget_manylands.What ships
infrahub_sdk.client.compile_get_many_query— pure helper that turns a kind→{ids, attributes} spec into(query, variables, kinds_ordered). Validates kind and attribute names as GraphQL identifiers up front and collects every problem before raisingValidationError.InfrahubClient.get_many/InfrahubClientSync.get_many— async and sync. Reuseexecute_graphqlfor transport,InfrahubNode.from_graphqlfor hydration, andclient.store.setfor store population.populate_store=Truematchesclient.getsemantics.Reuses existing primitives end-to-end. No new exception classes, no new result types, no new dependencies. The return is
dict[str, list[InfrahubNode]].Error handling
Compile-time problems (empty spec, missing/empty
ids, malformed kind/attribute identifiers, scalarstr/bytespassed where a list is expected) raiseValidationErrorwith a per-problem message list before any HTTP call is made. Server-side rejections (unknown kind or attribute on the loaded schema) propagate asGraphQLErrorfromexecute_graphql.Back-compat
Additive only.
get,filters,all,traverse_paths,execute_graphql, andPathNode.fetch()are untouched.Test plan
compile_get_many_query: query shape, alias allocation, variable allocation, id/attribute dedup + sort, kind-order preservation, every compile-error path, scalarstr/bytesrejection foridsandattributeshttpx_mockparametrized over[""standard"", ""sync""]: hydration,populate_store=True/False, compile errors short-circuit before the HTTP calluv run invoke format lint-codeclean (ruff, ty, mypy)integration-tests-latest-infrahub) passing in CIRelated issues
Follow-ups (not in this PR)
PathTraversalResult.fetch_hop_attributes(spec)sugar — one-liner overget_manyfor the reachability-check shape.include_attributes=keyword ontraverse_paths— sugar that calls the above before returning.