Skip to content

Fix file_object_content filters 404ing due to wrong URL prefix#956

Merged
gmazoyer merged 2 commits intoinfrahub-developfrom
fix/issue-954-file-object-urls
Apr 22, 2026
Merged

Fix file_object_content filters 404ing due to wrong URL prefix#956
gmazoyer merged 2 commits intoinfrahub-developfrom
fix/issue-954-file-object-urls

Conversation

@PhillSimonds
Copy link
Copy Markdown
Contributor

Why

The three file_object_content* Jinja2 filters shipped in #889 and #904 are 404ing at runtime. The SDK hardcodes URLs under /api/files/*, but the Infrahub backend routes those endpoints under /api/storage/files/*. Every call to file_object_content, file_object_content_by_id, or file_object_content_by_hfid raises JinjaFilterError: Client error '404 Not Found' and the artifact goes to Error.

Goal: restore the three filters to working order and add end-to-end-shaped unit tests so a URL contract drift like this one trips CI next time.

Non-goals: not changing the backend router surface, not adding new filter variants, not touching the computed-attribute gating.

Closes #954

What changed

  • Behavioural change users observe: file_object_content, file_object_content_by_id, and file_object_content_by_hfid now return the actual file content instead of raising a 404 wrapper.
  • Implementation: six URL strings in infrahub_sdk/object_store.py prefixed with /api/storage/files/ (async + sync). No other behaviour in ObjectStore changed.
  • Tests: updated FILE_BY_STORAGE_ID_URL mock constant; added two new constants FILE_BY_ID_URL and FILE_BY_HFID_URL; added three happy-path test cases for file_object_content_by_id, file_object_content_by_hfid (string argument), and file_object_content_by_hfid (list argument). The existing missing-kind, auth-error, and binary-content tests still cover their respective paths.
  • What stayed the same: SDK public API signatures, error types, existing test scenarios, and sync-vs-async parity all unchanged.

How to review

  • infrahub_sdk/object_store.py: six one-line URL changes on lines 105/110/116/191/196/202. Trivial.
  • tests/unit/sdk/test_infrahub_filters.py: the new happy-path tests at the bottom of TestClientDependentFilters are the interesting bits — note they hit per-filter URLs that match the new SDK paths 1:1.
  • changelog/954.fixed.md: towncrier fragment.

The bug was introduced in the original filter PRs (#889, #904) and has been present on every branch since. git diff 44a7ccc..origin/infrahub-develop -- infrahub_sdk/object_store.py returns empty — the wrong URLs never changed.

How to test

All should pass. The three new happy-path tests (test_file_object_content_by_id_happy_path, test_file_object_content_by_hfid_happy_path_string, test_file_object_content_by_hfid_happy_path_list) each assert the SDK hits http://mock/api/storage/files/... — if the URL regresses, these fail immediately.

End-to-end reproduction (against a live Infrahub 1.9 stack)

Start an Infrahub 1.9 instance per the usual invoke demo.start flow. Grab the admin token (default 06438eb2-8019-4776-878c-0941b1f1d1ec in dev builds) and set:

export TOKEN=06438eb2-8019-4776-878c-0941b1f1d1ec
export INFRAHUB=http://localhost:8000

1. Load a schema with a TestingFileBlob node inheriting from CoreFileObject:

# /tmp/testing-fileblob.yml
---
version: "1.0"
nodes:
  - name: FileBlob
    namespace: Testing
    include_in_menu: true
    label: FileBlob
    default_filter: label__value
    human_friendly_id: ["label__value"]
    inherit_from: ["CoreFileObject"]
    attributes:
      - name: label
        kind: Text
        unique: true

Load it:

infrahubctl schema load /tmp/testing-fileblob.yml --wait 30

2. Upload a file object via the GraphQL multipart mutation:

echo "hello from blob A" > /tmp/blob_a.txt

OP='{"query": "mutation CreateBlob($file: Upload!) { TestingFileBlobCreate(data: {label: {value: \"blob_a\"}}, file: $file) { ok object { id hfid storage_id { value } } } }", "variables": {"file": null}}'

curl -s -X POST $INFRAHUB/graphql -H "X-INFRAHUB-KEY: $TOKEN" \
  -F "operations=$OP" \
  -F 'map={"0": ["variables.file"]}' \
  -F "0=@/tmp/blob_a.txt"

Note the returned id (node UUID), hfid[0] (blob_a), and storage_id.value — you'll plug these into the templates below.

3. Create a TestingPerson schema + data to act as the artifact target:

# /tmp/testing-person.yml
---
version: "1.0"
nodes:
  - name: Person
    namespace: Testing
    include_in_menu: true
    label: Person
    default_filter: name__value
    human_friendly_id: ["name__value"]
    inherit_from: ["CoreArtifactTarget"]
    attributes:
      - name: name
        kind: Text
        unique: true
infrahubctl schema load /tmp/testing-person.yml --wait 30

# Create a person + a group holding it
curl -s -X POST $INFRAHUB/graphql -H "X-INFRAHUB-KEY: $TOKEN" -H "Content-Type: application/json" \
  -d '{"query": "mutation { p: TestingPersonCreate(data: {name: {value: \"John Doe\"}}) { object { id } } }"}'
# use the returned id for members:[{id: "..."}] in the group create

curl -s -X POST $INFRAHUB/graphql -H "X-INFRAHUB-KEY: $TOKEN" -H "Content-Type: application/json" \
  -d '{"query": "mutation { CoreStandardGroupCreate(data: {name: {value: \"people\"}, members: [{id: \"<person-id>\"}]}) { object { id } } }"}'

4. Prepare a Git repo with three templates exercising each filter variant. Inside a worker container (or any directory reachable by Infrahub as a git remote):

mkdir -p /remote/file-filter-tests/templates
cd /remote/file-filter-tests

# Common query
cat > templates/person.gql <<'EOF'
query Person($name: String!) {
  TestingPerson(name__value: $name) { edges { node { name { value } } } }
}
EOF

# file_object_content by storage_id
cat > templates/by_sid.j2 <<EOF
[by_sid] {{ '<blob_a_storage_id>' | file_object_content }}
EOF

# file_object_content_by_id (node UUID)
cat > templates/by_id.j2 <<EOF
[by_id] {{ '<blob_a_node_id>' | file_object_content_by_id }}
EOF

# file_object_content_by_hfid (needs kind=...)
cat > templates/by_hfid.j2 <<EOF
[by_hfid] {{ 'blob_a' | file_object_content_by_hfid(kind='TestingFileBlob') }}
EOF

cp templates/person.gql templates/by_sid.gql
cp templates/person.gql templates/by_id.gql
cp templates/person.gql templates/by_hfid.gql

cat > .infrahub.yml <<'EOF'
---
queries:
  - { name: by_sid_q, file_path: templates/by_sid.gql }
  - { name: by_id_q, file_path: templates/by_id.gql }
  - { name: by_hfid_q, file_path: templates/by_hfid.gql }
jinja2_transforms:
  - { name: by_sid_t, query: by_sid_q, template_path: templates/by_sid.j2 }
  - { name: by_id_t, query: by_id_q, template_path: templates/by_id.j2 }
  - { name: by_hfid_t, query: by_hfid_q, template_path: templates/by_hfid.j2 }
artifact_definitions:
  - { name: By Sid, artifact_name: by-sid, parameters: { name: name__value }, content_type: text/plain, targets: people, transformation: by_sid_t }
  - { name: By Id, artifact_name: by-id, parameters: { name: name__value }, content_type: text/plain, targets: people, transformation: by_id_t }
  - { name: By Hfid, artifact_name: by-hfid, parameters: { name: name__value }, content_type: text/plain, targets: people, transformation: by_hfid_t }
EOF

git init -q --initial-branch=main && git add -A && git -c user.email=test@test -c user.name=test commit -q -m init

Substitute the real storage_id / node id into by_sid.j2 and by_id.j2 before committing.

5. Register the repo:

infrahubctl repository add file-filter-tests file:///remote/file-filter-tests

Wait ~15 seconds for the artifact pipeline to run.

6. Observe results:

curl -s -X POST $INFRAHUB/graphql -H "X-INFRAHUB-KEY: $TOKEN" -H "Content-Type: application/json" \
  -d '{"query":"{ CoreArtifact { edges { node { name { value } status { value } storage_id { value } } } } }"}' | python3 -m json.tool
  • Before this PR: the three by-sid / by-id / by-hfid artifacts have status Error. Worker logs contain Filter 'file_object_content*': failed to retrieve content ... Client error '404 Not Found' for url 'http://server:8000/api/files/...'.
  • After this PR: all three artifacts have status Ready. Fetching their content returns e.g. [by_sid] hello from blob A. No 404 errors anywhere in worker logs.

Quick direct HTTP verification independent of the filters:

# Before this PR, both should 404:
curl -s -w "\n%{http_code}\n" "$INFRAHUB/api/files/by-storage-id/<sid>" -H "X-INFRAHUB-KEY: $TOKEN"

# After this PR, the SDK now hits the correct path (which already works):
curl -s -w "\n%{http_code}\n" "$INFRAHUB/api/storage/files/by-storage-id/<sid>" -H "X-INFRAHUB-KEY: $TOKEN"
# → 200 + "hello from blob A"

Impact & rollout

  • Backward compatibility: none broken — these filters did not work before, so no caller is depending on the current (error) behaviour. Callers that previously saw JinjaFilterError from these filters will now succeed.
  • Performance: none.
  • Config/env changes: none.
  • Deployment notes: safe to deploy. Users who have worked around the bug by avoiding these filters can now adopt them.

Checklist

  • Tests added/updated (3 new happy-path tests covering all three filter URL variants)
  • Changelog entry added (changelog/954.fixed.md)
  • External docs updated — no user-facing doc changes required; the filters are already documented assuming they work.
  • Internal .md docs updated — none needed; no architectural change.

The SDK constructed URLs under /api/files/* for the three
file_object_content* Jinja2 filters, but the Infrahub backend serves
these endpoints under /api/storage/files/*. Every call 404ed. Updated
all six URLs (async + sync) in object_store.py to the correct prefix.

Added happy-path unit tests for file_object_content_by_id and
file_object_content_by_hfid (string and list forms) so similar
contract drift with the backend router is caught in CI.

Fixes #954
@PhillSimonds PhillSimonds requested a review from a team as a code owner April 22, 2026 00:26
@github-actions github-actions Bot added the type/documentation Improvements or additions to documentation label Apr 22, 2026
@PhillSimonds PhillSimonds changed the base branch from develop to infrahub-develop April 22, 2026 00:28
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
infrahub_sdk/object_store.py 50.00% 3 Missing ⚠️
@@                 Coverage Diff                  @@
##           infrahub-develop     #956      +/-   ##
====================================================
+ Coverage             81.11%   81.19%   +0.08%     
====================================================
  Files                   134      134              
  Lines                 11314    11315       +1     
  Branches               1693     1693              
====================================================
+ Hits                   9177     9187      +10     
+ Misses                 1594     1585       -9     
  Partials                543      543              
Flag Coverage Δ
integration-tests 41.83% <0.00%> (-0.01%) ⬇️
python-3.10 54.10% <50.00%> (+0.07%) ⬆️
python-3.11 54.10% <50.00%> (+0.07%) ⬆️
python-3.12 54.08% <50.00%> (+0.07%) ⬆️
python-3.13 54.08% <50.00%> (+0.05%) ⬆️
python-3.14 55.71% <50.00%> (+0.09%) ⬆️
python-filler-3.12 22.79% <0.00%> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
infrahub_sdk/object_store.py 61.74% <50.00%> (+3.35%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PhillSimonds PhillSimonds changed the title Fix/issue 954 file object urls bug: Fix file_object_content filters 404ing due to wrong URL prefix Apr 22, 2026
@PhillSimonds PhillSimonds changed the title bug: Fix file_object_content filters 404ing due to wrong URL prefix Fix file_object_content filters 404ing due to wrong URL prefix Apr 22, 2026
Copy link
Copy Markdown
Contributor

@gmazoyer gmazoyer left a comment

Choose a reason for hiding this comment

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

Nice catch. I don't think you need the changelog record though, since this is still unreleased code, this would appear in the changelog as a fix for something introduced in the same release.

The bug was introduced in the same release cycle, so a changelog
record would appear as a fix for something that was never released.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 22, 2026

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6fe555a
Status: ✅  Deploy successful!
Preview URL: https://84958bda.infrahub-sdk-python.pages.dev
Branch Preview URL: https://fix-issue-954-file-object-ur.infrahub-sdk-python.pages.dev

View logs

@github-actions github-actions Bot removed the type/documentation Improvements or additions to documentation label Apr 22, 2026
@gmazoyer gmazoyer merged commit e317d93 into infrahub-develop Apr 22, 2026
20 checks passed
@gmazoyer gmazoyer deleted the fix/issue-954-file-object-urls branch April 22, 2026 08:24
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: file_object_content* filters fail with 404 due to /api/files URL path mismatch

2 participants