Skip to content

# fix: implement graphspace-scoped auth routing in Python client#325

Merged
imbajin merged 11 commits intoapache:mainfrom
Muawiya-contact:fix/graphspace-auth-routing
May 8, 2026
Merged

# fix: implement graphspace-scoped auth routing in Python client#325
imbajin merged 11 commits intoapache:mainfrom
Muawiya-contact:fix/graphspace-auth-routing

Conversation

@Muawiya-contact
Copy link
Copy Markdown
Contributor

What does this PR do?

Auth endpoints in the Python client were using absolute paths (/auth/users, etc.) that only worked because of a temporary PathFilter compatibility layer in HugeGraph 1.7.0. Once PathFilter is removed, those endpoints would break.

This PR implements the same dual-path strategy as the Java client's AuthAPI.java — graphspace-scoped paths when a graphspace is available, server-level fallback when it isn't.

Changes

src/pyhugegraph/utils/huge_router.py
Path resolution moved to runtime — prefers explicit graphspace arg, then session.cfg.graphspace when gs_supported is true, falls back to server-level /auth/... otherwise.

src/pyhugegraph/api/auth.py

  • users, accesses, belongs, targetsgraphspaces/{graphspace}/auth/...
  • groups/auth/groups (server-level, unchanged)

src/tests/api/test_auth_routing.py (new)
Unit tests mocking HGraphSession — asserts correct URL resolution for both graphspace and fallback cases. 9 tests, all passing.

src/tests/api/test_auth.py
Integration tests now skip gracefully when no live server is reachable — local runs won't fail without a HugeGraph instance.

Backward Compatibility

Public method signatures are unchanged. Behavior only differs when a graphspace is configured — requests use the scoped path automatically. No user action required.

Related

Closes #322
Reference: AuthAPI.java (Java client dual-path strategy)

/cc @imbajin

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label May 7, 2026
@Muawiya-contact
Copy link
Copy Markdown
Contributor Author

@imbajin PR #325 is ready for review — implements the dual-path auth routing strategy discussed in this issue.

Quick summary of what's in it:

  • huge_router.py — graphspace resolved at runtime with server-level fallback
  • auth.pyusers, accesses, belongs, targets scoped to graphspaces/{graphspace}/auth/...; groups stays server-level
  • Unit tests added, integration tests skip gracefully when no server is running

Happy to adjust anything based on your feedback.

This comment was marked as low quality.

Copy link
Copy Markdown
Member

@imbajin imbajin left a comment

Choose a reason for hiding this comment

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

Dual-path routing mirrors the Java client correctly and the approach is sound. A few issues to address before merging: a latent setdefault bug in the router that will bite any future method accepting an explicit graphspace kwarg, a fragile str.replace in the fallback, an integration test that silently accepts two response shapes, and a CI workflow change that's out of scope for this PR.

Comment thread hugegraph-python-client/src/pyhugegraph/utils/huge_router.py Outdated
Comment thread hugegraph-python-client/src/pyhugegraph/utils/huge_router.py Outdated
target_resources = target["target_resources"]
if isinstance(target_resources, dict):
target_resources = next(iter(target_resources.values()), [])
self.assertEqual(target_resources[0]["properties"]["city"], "Shanghai")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

⚠️ Accepting both dict and list shapes silently hides a real API contract divergence. If the server ever returns an unexpected shape due to a bug, the test still passes.

Better: pick the expected shape deterministically (e.g., branch on cls.client.cfg.version or gs_supported), or assert the shape explicitly so a drift surfaces as a test failure, not invisibly. At minimum, add a comment documenting which HugeGraph versions return which shape and link the server-side change — otherwise future maintainers can't tell whether the dict branch is compensating for a server bug or a legitimate format.

Comment thread hugegraph-python-client/src/tests/api/test_auth_routing.py
sess = DummySession(cfg)
auth = AuthManager(sess)
auth.list_groups()
assert "/auth/groups" in sess.last or "auth/groups" in sess.last
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🧹 "/auth/groups" in x or "auth/groups" in x is redundant — the first substring contains the second, so if the first matches the second always does, and if the second fails the first always fails. Just assert "auth/groups" in sess.last once (or use endswith("/auth/groups") if you want to pin the position).

Comment thread .github/workflows/ruff.yml
Copy link
Copy Markdown
Member

@imbajin imbajin left a comment

Choose a reason for hiding this comment

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

Follow-up from cross-checking against apache/hugegraph@1.7.0 source (server @Path annotations, PathFilter whitelist) and apache/hugegraph-toolchain AuthAPI.java: two additional observations about the PR's framing and the fallback branch.

Comment thread hugegraph-python-client/src/pyhugegraph/api/auth.py Outdated
Comment thread hugegraph-python-client/src/pyhugegraph/utils/huge_router.py Outdated
Copy link
Copy Markdown
Member

@imbajin imbajin left a comment

Choose a reason for hiding this comment

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

Final audit of test recovery after this PR's auth fix, but several defensive mechanisms introduced by PR #320 (the 1.7.0 upgrade) are still in place. Splitting them by scope:

In scope of this PR — recommend cleaning up before merge:

  1. test_auth.py::setUpClass skip-on-404 probe. This PR's core change makes the 404 branch unreachable against a correctly-configured HugeGraph 1.7.0+ server, which turns the probe into a silent-regression trap: if a future commit regresses the auth paths, tests will skip instead of failing, and CI will stay green with 5 skipped instead of 5 failed. Either (a) remove the probe entirely and let list_users() raise loudly on 404 (preferred — forces regressions to surface), or (b) convert the 404 branch to raise AssertionError("auth path regressed — server returned 404"). The new except requests.exceptions.RequestException: skip that this PR adds falls into the same anti-pattern and should likewise become an explicit error.

  2. test_auth.py::test_target_operations dict-or-list target_resources compat (covered in my earlier inline comment). Same reasoning: once the client pins HugeGraph 1.7.0+, there is one authoritative response shape — assert it and fail on the other, rather than silently accepting both.

Out of scope — file as separate follow-up issues:

  1. test_gremlin.py skip_gremlin_tests probe (L41-52), introduced by #320. Same dead-defense pattern as (1). Gremlin currently works in CI so the probe never fires, but it would mask a future regression the same way. Belongs in its own cleanup PR.

  2. test_metric.py::test_metrics_operations (L78-79) — the if "hugegraph" in backend_metrics: ... guard added by #320 effectively dropped the assertion on backend_metrics' shape for 1.7.0. Should be replaced with a version-gated assertion once 1.7.0's actual shape is documented.

  3. test_traverser.py dynamic edge-id — the original hardcoded S1:josh>2>>S2:lop was fragile regardless of server version; using edge_id.id is the correct fix, not a temporary patch. Leave as-is; no follow-up needed.

Recommend (a) addressing items 1–2 in this PR before merge, and (b) opening two tracking issues for items 3–4 so the 1.7.0 test debt from #320 is fully closed out and the boundary between this PR and the broader upgrade stays clean.

Comment thread hugegraph-python-client/src/tests/api/test_auth.py Outdated
@Muawiya-contact
Copy link
Copy Markdown
Contributor Author

@imbajin CI is all green now — the ruff format check on auth.py was the last failing check, fixed and pushed. All 13 checks passing. Ready to merge when you are!

removeprefix + concat was redundant (netted to `path` unchanged);
drop the dead fallback naming and the stale 'gracefully fall back'
comment now that the branch raises ValueError instead.
Copy link
Copy Markdown
Member

@imbajin imbajin left a comment

Choose a reason for hiding this comment

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

LGTM.

Non-blocking follow-up (please handle separately, do not re-open this PR)

File two tracking issues for leftover 1.7.0 test-suite debt from PR #320 (explicitly out of scope for #322/#325, but they share the same dead-defense anti-pattern this PR fixed for auth):

  • test_gremlin.py — the skip_gremlin_tests probe in setUpClass (L41-52) skips all 6 gremlin integration tests if gremlin.exec("1 + 1") returns 404/timeout/connection-refused. Gremlin works in current CI so the probe never fires, but a future regression on the gremlin endpoint would be hidden under 6 skipped rather than surfaced as 6 failed. Same remediation as test_auth.py: drop the probe, or convert the 404 branch into an explicit AssertionError.

  • test_metric.py::test_metrics_operations (L78-79) — the if "hugegraph" in backend_metrics: self.assertGreater(...) guard that replaced the original strict assertion effectively no-ops the backend-metrics check on 1.7.0. Pin down the actual 1.7.0 response shape (likely a per-graph map) and restore a version-gated assertion that fails when the shape drifts.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 8, 2026
@imbajin imbajin merged commit 168adf9 into apache:main May 8, 2026
13 checks passed
@Muawiya-contact Muawiya-contact deleted the fix/graphspace-auth-routing branch May 8, 2026 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer python-client size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement proper graphspace-scoped auth routing in Python client (without PathFilter dependency)

3 participants