Skip to content

bug: artifact_fetch() returns a string but documents a parsed object for JSON artifacts #1312

Description

@BeArchiTek

Component

Python SDK

Infrahub SDK version

1.23.1 (verified on develop @ 9b39ab4)

Current Behavior

InfrahubNode.artifact_fetch() documents that it parses JSON-typed artifacts, but it always returns a string.

infrahub_sdk/node/node.py, sync at :2261 (async at :1026):

def artifact_fetch(self, name: str) -> str | dict[str, Any]:
    """Fetch the stored content of a named artifact for this node.

    Returns:
        str | dict[str, Any]: The artifact content. Returns a parsed object for
        JSON-typed artifacts and a string for text-typed artifacts.
    ...
    """
    self._validate_artifact_support(ARTIFACT_FETCH_FEATURE_NOT_SUPPORTED_MESSAGE)
    artifact = self._client.get(kind="CoreArtifact", name__value=name, object__ids=[self.id])
    return self._client.object_store.get(identifier=artifact._get_attribute(name="storage_id").value)

No parsing happens. ObjectStore.get() is declared -> str (infrahub_sdk/object_store.py:131) and ends with return resp.text. The artifact's content_type is never consulted, so a application/json artifact comes back as a raw JSON string while the docstring and the str | dict[str, Any] return type both promise otherwise.

Expected Behavior

The docstring, the annotation and the behaviour should agree. Two ways to get there, and the choice is a product decision rather than an obvious fix:

  • Make the implementation match the docs — read the artifact's content_type and json.loads() the body for JSON-typed artifacts. This is the behaviour the docstring describes and, in our experience, the behaviour callers want; it keeps the existing str | dict[str, Any] annotation honest.
  • Make the docs match the implementation — narrow the return to -> str and drop the parsing claim, leaving the caller to parse.

Worth noting that the artifact node is already fetched in the method, so content_type is available without an extra request — the parsing option costs nothing in round trips.

We would suggest the first: the str | dict[str, Any] signature indicates parsing was the intent, and a caller who has to parse anyway gains nothing from the union type.

Steps to Reproduce

client = InfrahubClientSync(address="http://localhost:8000")
node = client.get(kind="MyArtifactTarget", id="<node id>")
content = node.artifact_fetch(name="<name of a JSON artifact>")

print(type(content))   # <class 'str'>  — docstring says a parsed object

Additional Information

This has a concrete downstream effect. The opsmill.infrahub Ansible collection currently fetches artifacts through hand-built REST calls and does branch on content_type to parse JSON. It is being moved onto this supported method (opsmill/infrahub-ansible#37), and taking the docstring at face value would silently change the module's return shape from parsed JSON to a string — so the discrepancy is a migration hazard, not only a documentation nit.

Adjacent: #1195 (enforce docstring↔signature consistency rules, incl. DOC201) would not catch this, since the docstring does document a Returns section — it is the content that is wrong, not the presence.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/lowThis issue should be completed in a couple of hoursstate/need-triageThis issue needs to be triagedtype/bugSomething isn't working as expected

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions