Skip to content

Commit

Permalink
feat(rust): node control api: bat tests for all apis and relative fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
davide-baldo committed Feb 12, 2025
1 parent 0769c2b commit c338ad2
Show file tree
Hide file tree
Showing 19 changed files with 876 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ impl HttpControlNodeApiBackend {
handle_authority_member_remove(context, &self.node_manager, body, id).await
}
},
_ => ControlApiHttpResponse::invalid_method(),
_ => {
warn!("Invalid method: {method}");
ControlApiHttpResponse::invalid_method()
}
}
}
}
Expand Down Expand Up @@ -78,7 +81,7 @@ async fn handle_authority_member_add_or_update(
};

let authority_client =
match create_authority_client(node_manager, request.authority, &request.identity).await? {
match create_authority_client(node_manager, &request.authority, &request.identity).await? {
Ok(authority_client) => authority_client,
Err(direct_response) => {
return Ok(direct_response);
Expand All @@ -88,6 +91,7 @@ async fn handle_authority_member_add_or_update(
let member_identity = if let Ok(identifier) = Identifier::from_str(member_identity) {
identifier
} else {
warn!("Invalid member identity");
return ControlApiHttpResponse::bad_request("Invalid member identity");
};

Expand All @@ -96,7 +100,10 @@ async fn handle_authority_member_add_or_update(
.await;
match result {
Ok(_) => ControlApiHttpResponse::without_body(StatusCode::CREATED),
Err(error) => ControlApiHttpResponse::internal_error(error),
Err(error) => {
warn!("Error adding member: {error}");
ControlApiHttpResponse::internal_error("Adding member failed")
}
}
}

Expand All @@ -123,13 +130,13 @@ async fn handle_authority_member_list(
node_manager: &Arc<NodeManager>,
body: Option<Vec<u8>>,
) -> ockam_core::Result<ControlApiHttpResponse> {
let request: ListAuthorityMembersRequest = match common::parse_request_body(body) {
let request: ListAuthorityMembersRequest = match common::parse_optional_request_body(body) {
Ok(value) => value,
Err(value) => return value,
};

let authority_client =
match create_authority_client(node_manager, request.authority, &request.identity).await? {
match create_authority_client(node_manager, &request.authority, &request.identity).await? {
Ok(authority_client) => authority_client,
Err(direct_response) => {
return Ok(direct_response);
Expand All @@ -148,7 +155,10 @@ async fn handle_authority_member_list(
.collect();
ControlApiHttpResponse::with_body(StatusCode::OK, members)
}
Err(error) => ControlApiHttpResponse::internal_error(error),
Err(error) => {
warn!("Error listing members: {error}");
ControlApiHttpResponse::internal_error("Listing members failed")
}
}
}

Expand All @@ -165,20 +175,25 @@ async fn handle_authority_member_list(
("node" = String, description = "Destination node name"),
("member" = String, description = "Member identity", example = "Id3b788c6a89de8b1f2fd13743eb3123178cf6ec7c9253be8ddcf7e154abe016a"),
),
request_body(
content = GetAuthorityMemberRequest,
content_type = "application/json",
description = "Get member request"
)
)]
async fn handle_authority_member_get(
context: &Context,
node_manager: &Arc<NodeManager>,
body: Option<Vec<u8>>,
resource_id: &str,
) -> ockam_core::Result<ControlApiHttpResponse> {
let request: GetAuthorityMemberRequest = match common::parse_request_body(body) {
let request: GetAuthorityMemberRequest = match common::parse_optional_request_body(body) {
Ok(value) => value,
Err(value) => return value,
};

let authority_client =
match create_authority_client(node_manager, request.authority, &request.identity).await? {
match create_authority_client(node_manager, &request.authority, &request.identity).await? {
Ok(authority_client) => authority_client,
Err(direct_response) => {
return Ok(direct_response);
Expand All @@ -188,6 +203,7 @@ async fn handle_authority_member_get(
let member_identity = if let Ok(identifier) = Identifier::from_str(resource_id) {
identifier
} else {
warn!("Invalid member identity");
return ControlApiHttpResponse::bad_request("Invalid member identity");
};

Expand All @@ -204,7 +220,8 @@ async fn handle_authority_member_get(
),
Err(error) => {
//TODO: handle not found
ControlApiHttpResponse::internal_error(error)
warn!("Error getting member: {error}");
ControlApiHttpResponse::internal_error("Getting member failed")
}
}
}
Expand Down Expand Up @@ -234,13 +251,13 @@ async fn handle_authority_member_remove(
body: Option<Vec<u8>>,
resource_id: &str,
) -> ockam_core::Result<ControlApiHttpResponse> {
let request: RemoveAuthorityMemberRequest = match common::parse_request_body(body) {
let request: RemoveAuthorityMemberRequest = match common::parse_optional_request_body(body) {
Ok(value) => value,
Err(value) => return value,
};

let authority_client =
match create_authority_client(node_manager, request.authority, &request.identity).await? {
match create_authority_client(node_manager, &request.authority, &request.identity).await? {
Ok(authority_client) => authority_client,
Err(direct_response) => {
return Ok(direct_response);
Expand All @@ -260,7 +277,8 @@ async fn handle_authority_member_remove(
Ok(_) => ControlApiHttpResponse::without_body(StatusCode::NO_CONTENT),
Err(error) => {
//TODO: handle not found
ControlApiHttpResponse::internal_error(error)
warn!("Error removing member: {error}");
ControlApiHttpResponse::internal_error("Deleting member failed")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::sync::Arc;

pub async fn create_authority_client(
node_manager: &Arc<NodeManager>,
authority: Authority,
authority: &Authority,
caller_identifier: &Option<String>,
) -> ockam_core::Result<Result<AuthorityNodeClient, ControlApiHttpResponse>> {
let caller_identifier = if let Some(identity) = caller_identifier {
Expand All @@ -26,12 +26,13 @@ pub async fn create_authority_client(
if let Ok(project) = node_manager
.cli_state
.projects()
.get_project_by_name(&name)
.get_project_by_name(name)
.await
{
create_project_authority_with_project(node_manager, &project, &caller_identifier)
.await?
} else {
warn!("Project {name} not found");
return Ok(Err(ControlApiHttpResponse::not_found("Project not found")?));
}
}
Expand All @@ -45,24 +46,27 @@ pub async fn create_authority_client(
create_project_authority_with_project(node_manager, &project, &caller_identifier)
.await?
} else {
warn!("No default project");
return Ok(Err(ControlApiHttpResponse::bad_request(
"Missing default project",
"No default project",
)?));
}
}

Authority::Node { route, identity } => {
Authority::Provided { route, identity } => {
let route = if let Ok(route) = MultiAddr::try_from(route.as_str()) {
route
} else {
warn!("Invalid authority route");
return Ok(Err(ControlApiHttpResponse::bad_request(
"Invalid authority route",
)?));
};

let identifier = if let Ok(identifier) = Identifier::from_str(&identity) {
let identifier = if let Ok(identifier) = Identifier::from_str(identity) {
identifier
} else {
warn!("Invalid identity");
return Ok(Err(ControlApiHttpResponse::bad_request(
"Invalid identity",
)?));
Expand Down Expand Up @@ -113,14 +117,33 @@ pub async fn create_project_authority_with_project(
.await
}

pub fn parse_optional_request_body<T: DeserializeOwned + Default>(
body: Option<Vec<u8>>,
) -> Result<T, ockam_core::Result<ControlApiHttpResponse>> {
if let Some(body) = body {
if body.is_empty() {
Ok(T::default())
} else {
match serde_json::from_slice(&body) {
Ok(request) => Ok(request),
Err(error) => {
warn!("Invalid request body: {error:?}");
Err(ControlApiHttpResponse::invalid_body())
}
}
}
} else {
Ok(T::default())
}
}
pub fn parse_request_body<T: DeserializeOwned>(
body: Option<Vec<u8>>,
) -> Result<T, ockam_core::Result<ControlApiHttpResponse>> {
let request: T = if let Some(body) = body {
match serde_json::from_slice(&body) {
Ok(request) => request,
Err(_error) => {
warn!("Invalid request body");
Err(error) => {
warn!("Invalid request body: {error:?}");
return Err(ControlApiHttpResponse::invalid_body());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ impl Worker for HttpControlNodeApiBackend {
message: Routed<Self::Message>,
) -> ockam_core::Result<()> {
let request: ControlApiHttpRequest = minicbor::decode(message.payload())?;
info!(
"Received Node Control API {} request for {}",
request.method, request.uri
);

let uri = Uri::from_str(&request.uri).unwrap();
// The syntax for restful API is:
Expand All @@ -42,6 +46,7 @@ impl Worker for HttpControlNodeApiBackend {

let path = uri.path().split('/').collect::<Vec<&str>>();
if path.len() < 3 {
warn!("Invalid URI: {uri}");
return context
.send(
message.return_route().clone(),
Expand Down Expand Up @@ -88,32 +93,41 @@ impl Worker for HttpControlNodeApiBackend {
)
.await
}
_ => ControlApiHttpResponse::with_body(
StatusCode::BAD_REQUEST,
ErrorResponse {
message: "Unknown resource kind".to_string(),
},
),
};

let response = match result {
Ok(response) => response,
Err(error) => match error.code().kind {
// We make an assumption that every parsing error originates from the
// client; This is not necessarily always true, but it's a good approximation
Kind::Parse => ControlApiHttpResponse::with_body(
_ => {
warn!("Invalid resource kind: {resource_kind}");
ControlApiHttpResponse::with_body(
StatusCode::BAD_REQUEST,
ErrorResponse {
message: error.to_string(),
},
)?,
_ => ControlApiHttpResponse::with_body(
StatusCode::INTERNAL_SERVER_ERROR,
ErrorResponse {
message: error.to_string(),
message: "Unknown resource kind".to_string(),
},
)?,
},
)
}
};

let response = match result {
Ok(response) => {
info!("Processed request for {}", request.uri);
response
}
Err(error) => {
warn!("Error processing request: {error:?}");
match error.code().kind {
// We make an assumption that every parsing error originates from the
// client; This is not necessarily always true, but it's a good approximation
Kind::Parse => ControlApiHttpResponse::with_body(
StatusCode::BAD_REQUEST,
ErrorResponse {
message: error.to_string(),
},
)?,
_ => ControlApiHttpResponse::with_body(
StatusCode::INTERNAL_SERVER_ERROR,
ErrorResponse {
message: error.to_string(),
},
)?,
}
}
};

let response = minicbor::to_vec(&response)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ impl HttpControlNodeApiBackend {
None => ControlApiHttpResponse::missing_resource_id(),
Some(id) => handle_tcp_inlet_delete(&self.node_manager, id).await,
},
_ => ControlApiHttpResponse::invalid_method(),
_ => {
warn!("Invalid method: {method}");
ControlApiHttpResponse::invalid_method()
}
}
}
}
Expand Down Expand Up @@ -159,7 +162,7 @@ async fn handle_tcp_inlet_create(
// name already exists
// port already bound
warn!("Failed to create tcp inlet: {:?}", error);
ControlApiHttpResponse::internal_error(error)
ControlApiHttpResponse::internal_error("Failed to create tcp inlet")
}
}
}
Expand Down Expand Up @@ -276,7 +279,7 @@ async fn handle_tcp_inlet_delete(
Ok(_) => ControlApiHttpResponse::without_body(StatusCode::NO_CONTENT),
Err(error) => {
warn!("Failed to delete tcp inlet: {:?}", error);
ControlApiHttpResponse::internal_error(error)
ControlApiHttpResponse::internal_error("Failed to delete tcp inlet")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod authority_member;
pub(super) mod authority_member;
mod common;
mod entrypoint;
pub(super) mod inlet;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ impl HttpControlNodeApiBackend {
None => ControlApiHttpResponse::missing_resource_id(),
Some(id) => handle_tcp_outlet_delete(&self.node_manager, id).await,
},
_ => ControlApiHttpResponse::invalid_method(),
_ => {
warn!("Invalid method: {method}");
ControlApiHttpResponse::invalid_method()
}
}
}
}
Expand Down Expand Up @@ -67,8 +70,8 @@ async fn handle_tcp_outlet_create(
let request: CreateOutletRequest = if let Some(body) = body {
match serde_json::from_slice(&body) {
Ok(request) => request,
Err(_error) => {
warn!("Invalid request body");
Err(error) => {
warn!("Invalid request body: {:?}", error);
return ControlApiHttpResponse::invalid_body();
}
}
Expand Down Expand Up @@ -118,7 +121,7 @@ async fn handle_tcp_outlet_create(
message: error.to_string(),
},
),
_ => ControlApiHttpResponse::internal_error(error),
_ => ControlApiHttpResponse::internal_error("Failed to create outlet"),
},
}
}
Expand Down
Loading

0 comments on commit c338ad2

Please sign in to comment.