Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 15, 2025

Fix bug where creating objects with object templates fails to set optional attributes

Problem

When creating a new object with an object template, the SDK includes null values for uninitialized optional relationships with cardinality ONE. This prevents the backend from applying values from the object template.

Root Cause

In infrahub_sdk/node/node.py, the _generate_input_data() method always adds None for optional, uninitialized relationships (lines 237-239), even for new objects. The backend interprets these explicit None values as overrides, preventing object template defaults from being applied.

Solution Plan

Changes Made

1. Core Logic Change (infrahub_sdk/node/node.py)

Modified _generate_input_data() method to conditionally include None for optional relationships:

  • NEW nodes (self._existing == False): Omit optional relationship fields entirely, allowing object template defaults to be applied
  • EXISTING nodes (self._existing == True): Include None for optional relationships, preserving ability to clear relationships (PR IHS-152 Allow unsetting one-to-one relationship #515)

2. Test Updates (tests/unit/sdk/test_node.py)

  • Updated 4 existing tests to remove expectation of None for new nodes
  • Added new test to verify existing nodes still include None
  • Added proper type annotations to test parameters (review feedback)
  • Added from __future__ import annotations for cleaner type hints
  • Moved type-only imports to TYPE_CHECKING block
  • Fixed import path after merge

3. Changelog (changelog/630.fixed.md)

  • Added towncrier changelog entry documenting the fix

Test Results

✅ All 154 node unit tests pass
✅ All linting checks pass (ruff, mypy, yamllint)
✅ CodeQL security scan: 0 alerts

Security Summary

No security vulnerabilities were introduced or discovered by this change. CodeQL analysis found 0 alerts.

Original prompt

This section details on the original issue you should resolve

<issue_title>bug: Creating an object with an object template with optional attributes fails to set the optional attributes</issue_title>
<issue_description>### Component

Python SDK

Infrahub SDK version

1.15.1

Current Behavior

Currently if you have a optional relationship of cardinality one and try to use the object templates the SDK will provide an override value to the backend this causes the backend not to apply the value provided by object template.

The UI provides a mutation like so:

mutation {
  DcimDeviceCreate(
    data: {object_template: {id: "18779f71-9564-3911-3bc2-1065c8cd8363"}, name: {value: "123123"}, location: {id: "18779f71-e8aa-55c1-3bc0-1065ebd40353"}}
  ) {
    object {
      id
      display_label
      hfid
      __typename
    }
    __typename
  }
}

However the SDK would provide a graphql mutation which looks like the following:

mutation {
    DcimDeviceUpsert(
        data: {
            status: {
                value: "active"
            }
            name: {
                value: "123123"
            }
            index: {
                value: 1
            }
            mlag_domain: null
            object_template: {
                id: "18779f71-55f1-390f-3bcf-10652e6ca9d3"
            }
            platform: {
                id: "test"
            }
            primary_address: null
            location: {
                id: "18779f72-1586-2058-3bc3-106527497af8"
            }
            device_type: null
        }
    ){
        ok
        object {
            id
        }
    }
}

You can see there are many null fields being passed in. This also happens when using object files to load data.

Expected Behavior

I would expect optional fields to be ignored and not instantiated by the client when creating a node the backend does not require them so no need to include them.

Steps to Reproduce

  1. Load infrahub with the base schema from the Schema library https://github.com/opsmill/schema-library/blob/main/base/dcim.yml#L87
  2. Create some manufactures and device_types
  3. Create a device type using an object file
---
apiVersion: infrahub.app/v1
kind: Object
spec:
  kind: DcimDevice
  data:
    # Paris HQ Building - Cisco Core Routers
    - name: CoreRouter1
      object_template: CISCO-ASR1006-CORE
      location: hq-01
      status: active
      index: 1
    - name: CoreRouter2
      object_template: CISCO-ASR1006-CORE
      location: hq-01
      status: active
      index: 2

Additional Information

No response</issue_description>

Comments on the Issue (you are @copilot in this section)

@minitriga Have to be careful we don't impact this change https://github.com//pull/515

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@coderabbitai
Copy link

coderabbitai bot commented Nov 15, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.


Comment @coderabbitai help to get the list of available commands and usage tips.

- Modified _generate_input_data() to skip adding None for optional relationships on new nodes
- For existing nodes, still include None to allow clearing relationships (preserves PR #515 behavior)
- Updated tests to reflect new behavior
- Added test to verify existing nodes still work correctly

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 15, 2025

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 15beefe
Status: ✅  Deploy successful!
Preview URL: https://59bec498.infrahub-sdk-python.pages.dev
Branch Preview URL: https://copilot-fix-optional-attribu.infrahub-sdk-python.pages.dev

View logs

Copilot AI changed the title [WIP] Fix bug with optional attributes in object templates Fix optional relationships preventing object template defaults from applying Nov 15, 2025
Copilot AI requested a review from minitriga November 15, 2025 00:59
@minitriga minitriga marked this pull request as ready for review November 15, 2025 01:03
@codecov
Copy link

codecov bot commented Nov 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

@@           Coverage Diff           @@
##           stable     #634   +/-   ##
=======================================
  Coverage   76.03%   76.03%           
=======================================
  Files         113      113           
  Lines        9743     9744    +1     
  Branches     1490     1491    +1     
=======================================
+ Hits         7408     7409    +1     
  Misses       1840     1840           
  Partials      495      495           
Flag Coverage Δ
integration-tests 34.64% <66.66%> (+<0.01%) ⬆️
python-3.10 49.95% <66.66%> (+<0.01%) ⬆️
python-3.11 49.95% <66.66%> (-0.02%) ⬇️
python-3.12 49.91% <66.66%> (-0.04%) ⬇️
python-3.13 49.93% <66.66%> (-0.02%) ⬇️
python-3.14 51.57% <66.66%> (-0.02%) ⬇️
python-filler-3.12 23.94% <33.33%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
infrahub_sdk/node/node.py 79.19% <100.00%> (+0.02%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@minitriga
Copy link
Contributor

@copilot would this PR cause a regression of the changes made in #479

Copy link
Contributor Author

Copilot AI commented Dec 19, 2025

@copilot would this PR cause a regression of the changes made in #479

No, this PR does not cause a regression for #479.

Issue #479 was about allowing users to explicitly set optional cardinality-one relationships to None to clear them. This fix preserves that behavior for existing nodes (nodes with an id).

The key distinction in this PR is:

I verified this by running test_update_input_data_empty_relationship which tests the #479 scenario - it passes successfully (commit: f0ee3cd).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/opsmill/infrahub-sdk-python/issues/479
    • Triggering command: /usr/bin/curl curl -s REDACTED (http block)

If you need me to access, download, or install something from one of these locations, you can either:


@pytest.mark.parametrize("client_type", client_types)
async def test_update_input_data_existing_node_with_optional_relationship(
client, location_schema: NodeSchemaAPI, client_type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good if all of these parameters included the type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Fix this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit dd24d0f. Added type annotations for all parameters:

  • clients: "BothClients" (using string annotation to avoid runtime import issues)
  • location_schema: NodeSchemaAPI
  • client_type: str

Also switched from the client fixture to the clients fixture which properly supports both sync and async clients via clients.standard and clients.sync.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot instead of "BothClients" can we use from future import annotations so we can use clients: BothClients? Can you also ensure there is a changelog for this change called 630.fixed.md

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit de39974:

  • Added from __future__ import annotations to enable postponed evaluation
  • Changed clients: "BothClients" to clients: BothClients (no string quotes)
  • Moved GenericSchema, NodeSchemaAPI, and HTTPXMock imports to TYPE_CHECKING block per ruff TC rules
  • Created changelog/630.fixed.md with the fix description

if client_type == "standard":
node = InfrahubNode(client=client, schema=location_schema, data=data)
else:
node = InfrahubNodeSync(client=client, schema=location_schema, data=data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As client doesn't have a type as per above we don't see the type violation here as InfrahubNodeSync doesn't accept an async client. We have a fixture called both_clients (or similar) which supports both sync and async.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Fix this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit dd24d0f. The test now uses the clients fixture (which is a BothClients instance) instead of client, and correctly accesses clients.standard for async and clients.sync for sync clients. This prevents the type violation where InfrahubNodeSync would receive an async client.

- Added BothClients import to TYPE_CHECKING block
- Updated test_update_input_data_existing_node_with_optional_relationship to use clients fixture with proper type annotations
- Changed from client fixture to clients fixture (clients.standard and clients.sync)
- Added type annotation for client_type parameter

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>
- Added `from __future__ import annotations` to enable postponed evaluation
- Changed `clients: "BothClients"` to `clients: BothClients` without string quotes
- Moved GenericSchema, NodeSchemaAPI, and HTTPXMock imports to TYPE_CHECKING block
- Added changelog/630.fixed.md documenting the fix

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>
@minitriga
Copy link
Contributor

@copilot can you fix linting

Changed `from tests.unit.sdk.conftest import BothClients` to `from .conftest import BothClients` to fix ruff import sorting error after merge from stable branch.

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 22, 2025

@copilot can you fix linting

Fixed in commit 15beefe. Corrected the import path in the TYPE_CHECKING block from tests.unit.sdk.conftest to .conftest to resolve ruff import sorting error introduced during the merge from stable branch.

@minitriga minitriga merged commit 1f1cb0c into stable Dec 23, 2025
21 checks passed
@minitriga minitriga deleted the copilot/fix-optional-attributes-bug branch December 23, 2025 00:00
minitriga added a commit that referenced this pull request Dec 23, 2025
…pplying (#634)

* Initial plan

* Fix optional attributes not being set when using object templates

- Modified _generate_input_data() to skip adding None for optional relationships on new nodes
- For existing nodes, still include None to allow clearing relationships (preserves PR #515 behavior)
- Updated tests to reflect new behavior
- Added test to verify existing nodes still work correctly

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>

* Add type annotations to test parameters and use BothClients fixture

- Added BothClients import to TYPE_CHECKING block
- Updated test_update_input_data_existing_node_with_optional_relationship to use clients fixture with proper type annotations
- Changed from client fixture to clients fixture (clients.standard and clients.sync)
- Added type annotation for client_type parameter

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>

* Use __future__ annotations and add changelog for issue 630

- Added `from __future__ import annotations` to enable postponed evaluation
- Changed `clients: "BothClients"` to `clients: BothClients` without string quotes
- Moved GenericSchema, NodeSchemaAPI, and HTTPXMock imports to TYPE_CHECKING block
- Added changelog/630.fixed.md documenting the fix

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>

* Fix linting: correct import path in TYPE_CHECKING block

Changed `from tests.unit.sdk.conftest import BothClients` to `from .conftest import BothClients` to fix ruff import sorting error after merge from stable branch.

Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: minitriga <26367336+minitriga@users.noreply.github.com>
Co-authored-by: Alex Gittings <agitting96@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Creating an object with an object template with optional attributes fails to set the optional attributes

3 participants