Skip to content

Commit c5832c8

Browse files
authored
feat(acp-nats): add request_permission client handler (#24)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent c53444d commit c5832c8

8 files changed

Lines changed: 985 additions & 139 deletions

File tree

.github/workflows/ci-rust.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
diff: true
5757
diff-branch: main
5858
diff-storage: _xml_coverage_reports
59-
uncovered-statements-increase-failure: true
60-
new-uncovered-statements-failure: true
59+
uncovered-statements-increase-failure: true # DO NOT CHANGE THIS, ADD TESTS
60+
new-uncovered-statements-failure: true # DO NOT CHANGE THIS, ADD TESTS
6161
coverage-rate-reduction-failure: true
6262
togglable-report: true

rsworkspace/crates/acp-nats/src/client/fs_read_text_file.rs

Lines changed: 8 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,14 @@
1+
use crate::client::rpc_reply;
12
use crate::jsonrpc::extract_request_id;
2-
use crate::nats::{FlushClient, PublishClient, headers_with_trace_context};
3+
use crate::nats::{FlushClient, PublishClient};
34
use agent_client_protocol::{
4-
Client, Error, ErrorCode, ReadTextFileRequest, ReadTextFileResponse, Request, RequestId,
5-
Response,
5+
Client, ErrorCode, ReadTextFileRequest, ReadTextFileResponse, Request, Response,
66
};
77
use bytes::Bytes;
88
use serde::de::Error as SerdeDeError;
99
use tracing::{instrument, warn};
1010
use trogon_std::JsonSerialize;
1111

12-
const CONTENT_TYPE_JSON: &str = "application/json";
13-
const CONTENT_TYPE_PLAIN: &str = "text/plain";
14-
15-
fn error_response_fallback_bytes<S: JsonSerialize>(serializer: &S) -> (Bytes, &'static str) {
16-
match serializer.to_vec(&Response::<()>::Error {
17-
id: RequestId::Null,
18-
error: Error::new(-32603, "Internal error"),
19-
}) {
20-
Ok(v) => (Bytes::from(v), CONTENT_TYPE_JSON),
21-
Err(e) => {
22-
warn!(
23-
error = %e,
24-
"Fallback JSON serialization failed, response may not be valid JSON-RPC"
25-
);
26-
(Bytes::from("Internal error"), CONTENT_TYPE_PLAIN)
27-
}
28-
}
29-
}
30-
31-
async fn publish_reply<N: PublishClient + FlushClient>(
32-
nats: &N,
33-
reply_to: &str,
34-
bytes: Bytes,
35-
content_type: &str,
36-
context: &str,
37-
) {
38-
let mut headers = headers_with_trace_context();
39-
headers.insert("Content-Type", content_type);
40-
if let Err(e) = nats
41-
.publish_with_headers(reply_to.to_string(), headers, bytes)
42-
.await
43-
{
44-
warn!(error = %e, "Failed to publish {}", context);
45-
}
46-
if let Err(e) = nats.flush().await {
47-
warn!(error = %e, "Failed to flush {}", context);
48-
}
49-
}
50-
51-
fn error_response_bytes<S: JsonSerialize>(
52-
serializer: &S,
53-
request_id: RequestId,
54-
code: ErrorCode,
55-
message: &str,
56-
) -> (Bytes, &'static str) {
57-
let response = Response::<()>::Error {
58-
id: request_id,
59-
error: Error::new(i32::from(code), message),
60-
};
61-
match serializer.to_vec(&response) {
62-
Ok(v) => (Bytes::from(v), CONTENT_TYPE_JSON),
63-
Err(e) => {
64-
warn!(error = %e, "JSON serialization failed, using fallback error");
65-
error_response_fallback_bytes(serializer)
66-
}
67-
}
68-
}
69-
7012
#[derive(Debug)]
7113
pub enum FsReadTextFileError {
7214
InvalidRequest(serde_json::Error),
@@ -134,17 +76,17 @@ pub async fn handle<N: PublishClient + FlushClient, C: Client, S: JsonSerialize>
13476
id: request_id.clone(),
13577
result: response,
13678
})
137-
.map(|v| (Bytes::from(v), CONTENT_TYPE_JSON))
79+
.map(|v| (Bytes::from(v), rpc_reply::CONTENT_TYPE_JSON))
13880
.unwrap_or_else(|e| {
13981
warn!(error = %e, "JSON serialization of response failed, sending error reply");
140-
error_response_bytes(
82+
rpc_reply::error_response_bytes(
14183
serializer,
14284
request_id,
14385
ErrorCode::InternalError,
14486
&format!("Failed to serialize response: {}", e),
14587
)
14688
});
147-
publish_reply(
89+
rpc_reply::publish_reply(
14890
nats,
14991
reply_to,
15092
response_bytes,
@@ -161,8 +103,8 @@ pub async fn handle<N: PublishClient + FlushClient, C: Client, S: JsonSerialize>
161103
"Failed to handle fs_read_text_file"
162104
);
163105
let (bytes, content_type) =
164-
error_response_bytes(serializer, request_id, code, &message);
165-
publish_reply(
106+
rpc_reply::error_response_bytes(serializer, request_id, code, &message);
107+
rpc_reply::publish_reply(
166108
nats,
167109
reply_to,
168110
bytes,
@@ -634,21 +576,6 @@ mod tests {
634576
assert!(fs_err.source().is_some());
635577
}
636578

637-
#[test]
638-
fn error_response_bytes_first_fallback_uses_null_id() {
639-
let mock = FailNextSerialize::new(1);
640-
let (bytes, content_type) = error_response_bytes(
641-
&mock,
642-
RequestId::Number(42),
643-
ErrorCode::InvalidParams,
644-
"test message",
645-
);
646-
assert_eq!(content_type, "application/json");
647-
let parsed: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
648-
assert_eq!(parsed["id"], serde_json::Value::Null);
649-
assert_eq!(parsed["error"]["code"], -32603);
650-
}
651-
652579
#[tokio::test]
653580
async fn mock_client_request_permission_returns_err() {
654581
let client = MockClient::new("x");
@@ -674,22 +601,4 @@ mod tests {
674601
let result = client.request_permission(req).await;
675602
assert!(result.is_err());
676603
}
677-
678-
#[test]
679-
fn error_response_bytes_last_resort_returns_plain_text() {
680-
let mock = FailNextSerialize::new(2);
681-
let (bytes, content_type) =
682-
error_response_bytes(&mock, RequestId::Number(1), ErrorCode::InternalError, "msg");
683-
assert_eq!(content_type, "text/plain");
684-
assert_eq!(bytes.as_ref(), b"Internal error");
685-
}
686-
687-
#[test]
688-
fn error_response_fallback_bytes_std_serializer_returns_json() {
689-
let (bytes, content_type) = error_response_fallback_bytes(&StdJsonSerialize);
690-
assert_eq!(content_type, "application/json");
691-
let parsed: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
692-
assert_eq!(parsed["id"], serde_json::Value::Null);
693-
assert_eq!(parsed["error"]["code"], -32603);
694-
}
695604
}

0 commit comments

Comments
 (0)