feat(node): number pool attach/detach, provenance and declared ranges

Merged
AJ
4d ago
+1219-92

Why

Infrahub 1.12 adds number-pool features that SDK users cannot use today:

  • Detaching a number from its pool, by sending from_pool: null on an update (IFC-3333). The SDK has no way to send it. Attaching a number the user already holds (value and from_pool together) has the same problem: on create, the SDK sends only from_pool, so the pool allocates a new number instead of attaching the given one.
  • Reading whether the pool allocated each number or a user provided it, from the provenance field of InfrahubResourcePoolAllocated (IFC-3367). get_pool_allocated_resources() returns only the nodes and drops every row field.
  • Creating a number pool with its ranges in one call (IFC-3369). The pool mutations take ranges as NumberPoolRangeInput (start, end, allocation_weight), but the SDK sends every relationship peer as a reference, so ranges=[{"start": 1, "end": 10}] reaches Infrahub as [{}]: one range with no start and no end.

Closes IFC-3333, IFC-3367, IFC-3369

What changed

Attach and detach a number (IFC-3333)

  • Number attributes have attach_to_pool(pool) and detach_from_pool(). Both are sent on the next save.
    • attach_to_pool() sends the number the attribute holds with from_pool, so the pool tracks it as provided. It raises ValueError when the attribute holds no number, or when it is given a node that is not a resource pool or has not been saved.
    • detach_from_pool() sends from_pool: null. The attribute keeps its number. On a node that is not saved yet, the number is sent with the detach so the node is created with it.
  • An attribute built with both value and from_pool now sends both, so the pool attaches the given number instead of allocating a new one.
  • A from_pool request is sent once. A private pending flag is cleared after a successful mutation, so the next update does not send it again. _from_pool stays set, so is_from_pool_attribute() still returns True after a save.
  • The Integer and IntegerOptional protocols declare the two methods for typed code.

List a pool's allocations with their provenance (IFC-3367)

  • Resource pool nodes have get_pool_allocations(resource), async and sync. It returns PoolAllocation entries (node ID, kind, branch, display label, identifier, provenance) without fetching the nodes.
  • provenance is PoolAllocationProvenance.ALLOCATED, PoolAllocationProvenance.PROVIDED, or None for IP pools.
  • get_pool_allocated_resources() is unchanged. It shares the paging loop but does not request provenance, so it keeps working against servers older than 1.12.

Create a number pool with its ranges (IFC-3369)

  • A relationship whose peer kind is CoreNumberPoolRange sends the peer's fields instead of a reference, matching how Infrahub picks NumberPoolRangeInput for that peer kind. The rule lives in one place: INLINE_INPUT_PEER_KINDS and RelatedNodeBase.
  • client.create(kind="CoreNumberPool", ..., ranges=[{"start": 100, "end": 199, "allocation_weight": 10}]) creates the pool and its ranges in one mutation. save(allow_upsert=True) sets an existing pool's ranges to the declared list.
  • Single ranges of an existing pool are added, changed or deleted through CoreNumberPoolRange nodes, which already worked. Infrahub refuses edits made through the pool's ranges relationship (Field 'id' is not defined by type 'NumberPoolRangeInput').

What stayed the same

  • No schema or protocol generation changes.
  • Every relationship other than one whose peer kind is CoreNumberPoolRange is serialized as before.

How to review

Suggested order, one commit per ticket:

  1. 1d55171b attach and detach: node/attribute.py and the two node.py hunks (clearing the pending flag in _process_mutation_result, and keeping value next to from_pool in _strip_unmodified_dict).
  2. ef09423f provenance: PoolAllocation, the shared query builder and paging loop in node.py.
  3. 3204d16c ranges: node/related_node.py and node/constants.py. It also removes a second self._relationship_metadata = None in RelatedNodeBase.__init__, which the constructor already sets just above.
  4. 2ed22456 integration test.

docs/docs/python-sdk/sdk_ref/ is generated.

Impact & rollout

  • Backward compatibility:
    • A save no longer resends from_pool after it succeeded. Before, the next update sent {"from_pool": ...} again, which Infrahub ignored.
    • ranges dicts on a CoreNumberPool are now sent as range definitions instead of empty objects.
  • Deployment notes: the new features need Infrahub 1.12 or later.

Checklist

  • Tests added/updated
  • Changelog entry added
  • External docs updated (resource manager guide, generated SDK reference)
  • Internal .md docs updated (none needed)
  • reviewed LLM-generated code

šŸ¤– Generated with Claude Code


Summary by cubic

Adds SDK support for the Infrahub 1.12 number-pool features: attaching or detaching a number from a pool, reading how each allocation came to be held, and declaring a pool's ranges on create or upsert.

  • Number attributes get attach_to_pool() and detach_from_pool(), both sent on the next save. Creating an attribute with both value and from_pool now sends both, so the pool tracks the given number instead of allocating a new one.
  • Resource pool nodes get get_pool_allocations(), returning PoolAllocation entries with provenance of ALLOCATED or PROVIDED (None for IP pools). get_pool_allocated_resources() still requests no provenance, so it keeps working against Infrahub before 1.12.
  • A CoreNumberPool can be created with ranges as dicts of start, end and allocation_weight; before, each range reached Infrahub as an empty object.

Behavior changes

  • A from_pool request is sent once instead of being resent on every update.
  • Saving with allow_upsert=True sets an existing pool's ranges to the declared list.
  • The new features require Infrahub 1.12 or later.