-
Notifications
You must be signed in to change notification settings - Fork 97
Split API endpoints by content type #6931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a new nested router for the Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Router
participant AuthMiddleware
participant WebSocketMiddleware
participant ServiceBuilder
participant APIHandler
Client->>Router: Request `/metrics`
Router->>AuthMiddleware: Apply Authentication
AuthMiddleware->>Router: Return authenticated request
Router->>WebSocketMiddleware: Check WebSocket upgrade
WebSocketMiddleware->>Router: Forward request
Router->>ServiceBuilder: Invoke tracing, CORS, etc.
ServiceBuilder->>APIHandler: Process request
APIHandler->>Router: Return response
Router->>Client: Deliver final response
Possibly related PRs
Suggested labels
Suggested reviewers
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (8)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Crate version bump missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned by @tolbrino in the chat, please, leave the layer in place, but create an exception with test/plain
for the metrics endpoint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
hoprd/rest-api/src/lib.rs (1)
265-292
: Implementation of a separate router for metrics route with text/plain content type looks good.This change correctly implements a dedicated router for the
/node/metrics
endpoint that acceptstext/plain
content type, which aligns with the PR objective of removing content type validation constraints. The metrics endpoint now properly accepts plain text data instead of requiring JSON format.While the implementation is correct, consider extracting the common middleware configuration patterns between this router and the main router to reduce code duplication. This would improve maintainability for future changes.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
hoprd/rest-api/src/lib.rs
(2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
hoprd/rest-api/src/lib.rs (1)
hoprd/rest-api/src/node.rs (1) (1)
metrics
(265-270)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: hoprd / docker
- GitHub Check: hopli / docker
- GitHub Check: Docs / Rust docs
- GitHub Check: tests-smoke-websocket
- GitHub Check: tests-smart-contracts
- GitHub Check: tests-unit-nightly
- GitHub Check: Linter
- GitHub Check: tests-unit
🔇 Additional comments (2)
hoprd/rest-api/src/lib.rs (2)
349-350
: Good use of cloning the inner state.Properly cloning the
inner_state
when passing it to the middleware function ensures that each middleware gets its own copy of the state, preventing any potential issues with shared mutable state.
353-366
: Consistent middleware configuration for the main router.The middleware stack for the main router is properly configured to maintain JSON validation, which provides a clean separation of concerns between the metrics endpoint (plain text) and all other endpoints (JSON).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to have some test verify this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
tests/test_rest_api.py (4)
41-53
: Consider optimizing the test function to reuse HTTP session.The test creates three separate
ClientSession
instances, which is inefficient. Consider reusing a single session for all requests in this test.Here's a more efficient implementation:
async def test_metric_endpoint_accepts_plain_text_header(peer: str, swarm7: dict[str, Node]): url = f"http://{swarm7[peer].host_addr}:{swarm7[peer].api_port}/metrics" headers = {"X-Auth-Token": swarm7[peer].api_token} - async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 200 - - async with aiohttp.ClientSession(headers=headers+{"accept": "text/plain"}) as s: - assert (await s.get(url)).status == 200 - - async with aiohttp.ClientSession(headers=headers+{"accept": "application/json"}) as s: - assert (await s.get(url)).status == 406 + async with aiohttp.ClientSession() as s: + # Test with default headers (no accept header) + assert (await s.get(url, headers=headers)).status == 200 + + # Test with text/plain accept header + plain_headers = {**headers, "accept": "text/plain"} + assert (await s.get(url, headers=plain_headers)).status == 200 + + # Test with application/json accept header + json_headers = {**headers, "accept": "application/json"} + assert (await s.get(url, headers=json_headers)).status == 406
41-53
: Enhance test assertions with descriptive messages.The assertions would be more helpful during debugging if they included descriptive messages explaining what's being tested.
Consider adding descriptive messages:
async def test_metric_endpoint_accepts_plain_text_header(peer: str, swarm7: dict[str, Node]): url = f"http://{swarm7[peer].host_addr}:{swarm7[peer].api_port}/metrics" headers = {"X-Auth-Token": swarm7[peer].api_token} async with aiohttp.ClientSession() as s: - assert (await s.get(url, headers=headers)).status == 200 + response = await s.get(url, headers=headers) + assert response.status == 200, f"Expected 200 OK for default request, got {response.status}" - assert (await s.get(url, headers={**headers, "accept": "text/plain"})).status == 200 + response = await s.get(url, headers={**headers, "accept": "text/plain"}) + assert response.status == 200, f"Expected 200 OK for text/plain request, got {response.status}" - assert (await s.get(url, headers={**headers, "accept": "application/json"})).status == 406 + response = await s.get(url, headers={**headers, "accept": "application/json"}) + assert response.status == 406, f"Expected 406 Not Acceptable for application/json request, got {response.status}"
41-53
: Consider validating response content type, not just status code.The test only checks status codes but doesn't verify that the response content type matches the requested format. This would make the test more robust.
Add content type verification:
async def test_metric_endpoint_accepts_plain_text_header(peer: str, swarm7: dict[str, Node]): url = f"http://{swarm7[peer].host_addr}:{swarm7[peer].api_port}/metrics" headers = {"X-Auth-Token": swarm7[peer].api_token} async with aiohttp.ClientSession() as s: - assert (await s.get(url, headers={**headers, "accept": "text/plain"})).status == 200 + response = await s.get(url, headers={**headers, "accept": "text/plain"}) + assert response.status == 200, "Expected 200 OK for text/plain request" + content_type = response.headers.get('Content-Type', '') + assert 'text/plain' in content_type, f"Expected text/plain content type, got {content_type}"
53-53
: Add a newline at the end of the file.The file is missing a newline at the end, which can cause issues with some linters and version control systems.
Add an empty line at the end of the file.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
tests/test_rest_api.py
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: hoprd / docker
- GitHub Check: hopli / docker
- GitHub Check: tests-unit
- GitHub Check: Docs / Rust docs
- GitHub Check: tests-unit-nightly
- GitHub Check: tests-smoke-websocket
- GitHub Check: Linter
- GitHub Check: tests-smart-contracts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
tests/test_rest_api.py (2)
48-54
: Suggestion: Consider reusing the ClientSessionFor improved efficiency, consider reusing the same ClientSession for all requests within the test function instead of creating a new one for each request.
- async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 200 - - headers["accept"] = "text/plain" - async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 200 - - headers["accept"] = "application/json" - async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 406 + async with aiohttp.ClientSession() as s: + # Test with default headers + assert (await s.get(url, headers=headers)).status == 200 + + # Test with text/plain accept header + headers_plain = {**headers, "accept": "text/plain"} + assert (await s.get(url, headers=headers_plain)).status == 200 + + # Test with application/json accept header + headers_json = {**headers, "accept": "application/json"} + assert (await s.get(url, headers=headers_json)).status == 406
66-72
: Apply the same session reuse pattern to this testFor consistency, consider applying the same ClientSession reuse pattern to this test function as well.
- headers["accept"] = "text/plain" - async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 406 - - headers["accept"] = "application/json" - async with aiohttp.ClientSession(headers=headers) as s: - assert (await s.get(url)).status == 200 + async with aiohttp.ClientSession() as s: + # Test with default headers + assert (await s.get(url, headers=headers)).status == 200 + + # Test with text/plain accept header + headers_plain = {**headers, "accept": "text/plain"} + assert (await s.get(url, headers=headers_plain)).status == 406 + + # Test with application/json accept header + headers_json = {**headers, "accept": "application/json"} + assert (await s.get(url, headers=headers_json)).status == 200
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
tests/test_rest_api.py
(1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
tests/test_rest_api.py (2)
tests/conftest.py (2) (2)
nodes_with_auth
(59-61)swarm7
(79-88)sdk/python/localcluster/node.py (1) (1)
Node
(35-266)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: tests-smoke-websocket
- GitHub Check: tests-smart-contracts
- GitHub Check: tests-unit
- GitHub Check: tests-unit-nightly
- GitHub Check: hopli / docker
- GitHub Check: hoprd / docker
- GitHub Check: Docs / Rust docs
- GitHub Check: Linter
🔇 Additional comments (2)
tests/test_rest_api.py (2)
39-55
: Test for metrics endpoint looks good!This test correctly verifies that the metrics endpoint:
- Returns 200 with default headers
- Returns 200 when explicitly requesting
text/plain
content- Returns 406 (Not Acceptable) when requesting
application/json
contentThis aligns with the PR objective of enforcing
text/plain
for the metrics endpoint.
57-72
: Test for info endpoint looks good!This test correctly verifies the opposite behavior for the info endpoint:
- Returns 200 with default headers
- Returns 406 (Not Acceptable) when requesting
text/plain
content- Returns 200 when requesting
application/json
contentThis complements the metrics endpoint test and provides good coverage of the content type enforcement changes.
Allows the metric endpoint to return response properly when the content type is en-forced to
text/plain
when doing the query.This fixes SwaggerUI not rendering the metrics.