Skip to content

Add gRPC user agent interceptor - #2172

Closed
malsomesh9 wants to merge 1 commit into
hiero-ledger:mainfrom
malsomesh9:codex/grpc-user-agent-header
Closed

Add gRPC user agent interceptor#2172
malsomesh9 wants to merge 1 commit into
hiero-ledger:mainfrom
malsomesh9:codex/grpc-user-agent-header

Conversation

@malsomesh9

Copy link
Copy Markdown

Summary

  • add a gRPC client interceptor that appends SDK identification metadata to outgoing calls
  • wrap both consensus node and mirror node channels with the interceptor
  • add unit coverage for version lookup, metadata injection, metadata preservation, channel wrapping, and a real local gRPC metadata call

Details

The interceptor sends x-user-agent: hiero-sdk-python/{version}. The lowercase metadata key is required by gRPC Python/HTTP2 while preserving the same X-User-Agent header semantics expected by the consensus node.

Fixes #2169

Validation

  • /tmp/hiero-sdk-python-venv/bin/python -m pytest tests/unit/user_agent_interceptor_test.py -q
  • /tmp/hiero-sdk-python-venv/bin/python -m pytest tests/unit/ -q
  • /tmp/hiero-sdk-python-venv/bin/python -m ruff check src/hiero_sdk_python/user_agent_interceptor.py src/hiero_sdk_python/node.py src/hiero_sdk_python/client/client.py tests/unit/user_agent_interceptor_test.py
  • /tmp/hiero-sdk-python-venv/bin/python -m ruff format --check src/hiero_sdk_python/user_agent_interceptor.py src/hiero_sdk_python/node.py src/hiero_sdk_python/client/client.py tests/unit/user_agent_interceptor_test.py

Signed-off-by: Somesh Mal <malsomesh9@gmail.com>
@malsomesh9
malsomesh9 requested review from a team as code owners April 18, 2026 11:12
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 20 complexity

Metric Results
Complexity 20

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The PR implements a gRPC client interceptor that injects an x-user-agent header containing the SDK version into all outgoing gRPC calls. The interceptor is applied to both consensus node and mirror node channels, with fallback to "dev" when the package version is unavailable.

Changes

Cohort / File(s) Summary
User-Agent Interceptor Implementation
src/hiero_sdk_python/user_agent_interceptor.py
New module implementing a gRPC client interceptor that appends x-user-agent header with format hiero-sdk-python/{version} to unary-unary and unary-stream RPCs. Includes version detection via importlib.metadata.version() with fallback to "dev", a _ClientCallDetails wrapper, and _apply_user_agent_interceptor() function for channel wrapping.
Channel Integration
src/hiero_sdk_python/client/client.py, src/hiero_sdk_python/node.py
Minimal integration of the user-agent interceptor into existing channel creation logic. Each file adds import and applies _apply_user_agent_interceptor() to wrap the channel immediately after channel selection, preserving all existing TLS and credential configuration.
Interceptor Tests
tests/unit/user_agent_interceptor_test.py
Comprehensive unit and integration tests verifying version detection and fallback, header injection in unary-unary and unary-stream calls, preservation of existing metadata, and channel wrapping behavior. Integration test validates header injection through an in-process gRPC server.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a gRPC user agent interceptor to the SDK.
Description check ✅ Passed The description is directly related to the changeset, explaining the interceptor implementation, channels being wrapped, unit test coverage, and validation performed.
Linked Issues check ✅ Passed The PR implements all requirements from issue #2169: interceptor with x-user-agent header, version lookup with dev fallback, support for unary RPC types, application to consensus and mirror node channels, and comprehensive unit tests.
Out of Scope Changes check ✅ Passed All changes are within scope: new user agent interceptor module, wrapping of existing channels, and comprehensive unit tests—all directly aligned with issue #2169 requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #2169

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bb099e42-243d-4032-8910-378d5693cca1

📥 Commits

Reviewing files that changed from the base of the PR and between 15032e1 and 7ba6106.

📒 Files selected for processing (4)
  • src/hiero_sdk_python/client/client.py
  • src/hiero_sdk_python/node.py
  • src/hiero_sdk_python/user_agent_interceptor.py
  • tests/unit/user_agent_interceptor_test.py

)


pytestmark = pytest.mark.unit

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Move the real gRPC server check out of the unit test set.

Line 19 marks the whole module as unit tests, but Line 102 starts a real gRPC server and Line 113 opens a real loopback channel. Keep this coverage, but move it to an integration test module or remove the module-level unit marker and explicitly mark only the pure tests as unit.

Suggested marking split if this test remains in the same file
-pytestmark = pytest.mark.unit
-
 
+@pytest.mark.unit
 def test_get_sdk_version_returns_installed_version():
     assert _get_sdk_version()
 
 
+@pytest.mark.unit
 `@patch`("hiero_sdk_python.user_agent_interceptor.importlib_metadata.version")
 def test_get_sdk_version_falls_back_to_dev(mock_version):
     mock_version.side_effect = importlib_metadata.PackageNotFoundError
 
     assert _get_sdk_version() == "dev"
 
 
+@pytest.mark.unit
 `@patch`("hiero_sdk_python.user_agent_interceptor._get_sdk_version", return_value="1.2.3")
 def test_unary_unary_interceptor_adds_user_agent_header(mock_get_version):
     interceptor = _UserAgentInterceptor()
     continuation = Mock(return_value="response")
 
 
+@pytest.mark.unit
 `@patch`("hiero_sdk_python.user_agent_interceptor._get_sdk_version", return_value="1.2.3")
 def test_unary_stream_interceptor_adds_user_agent_header(mock_get_version):
     interceptor = _UserAgentInterceptor()
     continuation = Mock(return_value=iter(["response"]))
 
 
+@pytest.mark.unit
 `@patch`("hiero_sdk_python.user_agent_interceptor._get_sdk_version", return_value="1.2.3")
 def test_interceptor_preserves_existing_metadata(mock_get_version):
     interceptor = _UserAgentInterceptor()
     continuation = Mock(return_value="response")
 
 
+@pytest.mark.unit
 `@patch`("grpc.intercept_channel")
 def test_apply_user_agent_interceptor_wraps_channel(mock_intercept_channel):
     channel = Mock(spec=grpc.Channel)
     intercepted_channel = Mock(spec=grpc.Channel)
 
 
+@pytest.mark.integration
 def test_interceptor_sends_valid_grpc_metadata():
     received_metadata = []

As per coding guidelines, "No network calls or external dependencies (unit tests are isolated)."

Also applies to: 95-126

@Akshat8510

Copy link
Copy Markdown
Member

Closing this PR for now since the contributor is not assigned to the issue, and there are also some concerns around whether this may be an automated/bot-generated contribution.

@Akshat8510 Akshat8510 closed this Apr 18, 2026
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.

Implement gRPC X-User-Agent Header for SDK Identification

2 participants