ockam_api 0.93.0

Ockam's request-response API
use crate::control_api::protocol::common::ErrorResponse;
use crate::control_api::ControlApiError;
use bytes::Bytes;
use http::{Method, StatusCode};
use http_body_util::Full;
use minicbor::{CborLen, Decode, Encode};
use ockam_core::errcode::{Kind, Origin};
use ockam_core::Error;
use serde::Serialize;

#[derive(Debug, Encode, Decode, CborLen)]
#[rustfmt::skip]
pub struct ControlApiHttpRequest {
    #[n(0)] pub method: String,
    #[n(1)] pub uri: String,
    #[n(2)] pub body: Option<Vec<u8>>,
}

#[derive(Debug, Encode, Decode, CborLen)]
#[rustfmt::skip]
pub struct ControlApiHttpResponse {
    #[n(0)] pub status: u16,
    #[n(1)] pub body: Vec<u8>,
}

impl ControlApiHttpResponse {
    pub fn with_body<T: Serialize>(
        status_code: StatusCode,
        body: T,
    ) -> ockam_core::Result<ControlApiHttpResponse> {
        Ok(Self {
            status: status_code.as_u16(),
            body: serde_json::to_vec(&body).map_err(|_| {
                Error::new(
                    Origin::Api,
                    Kind::Internal,
                    "Failed to encode response body",
                )
            })?,
        })
    }

    pub fn without_body(status_code: StatusCode) -> ockam_core::Result<ControlApiHttpResponse> {
        Ok(Self {
            status: status_code.as_u16(),
            body: Vec::new(),
        })
    }

    pub fn invalid_body<T>() -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::BAD_REQUEST,
            ErrorResponse {
                message: "Invalid request body".to_string(),
            },
        )?
        .into())
    }

    pub fn missing_body<T>() -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::BAD_REQUEST,
            ErrorResponse {
                message: "Missing request body".to_string(),
            },
        )?
        .into())
    }

    pub fn bad_request<T>(message: &str) -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::BAD_REQUEST,
            ErrorResponse {
                message: format!("Bad request: {message}"),
            },
        )?
        .into())
    }

    pub fn not_found<T>(message: &str) -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::NOT_FOUND,
            ErrorResponse {
                message: message.to_string(),
            },
        )?
        .into())
    }

    pub fn conflict<T>(message: &str) -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::CONFLICT,
            ErrorResponse {
                message: message.to_string(),
            },
        )?
        .into())
    }

    pub fn internal_error<T>(error: &str) -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::INTERNAL_SERVER_ERROR,
            ErrorResponse {
                message: format!("Internal server error: {error}"),
            },
        )?
        .into())
    }

    pub fn invalid_method<T>(
        method: Method,
        allowed_methods: Vec<Method>,
    ) -> Result<T, ControlApiError> {
        Err(Self::with_body(
            StatusCode::METHOD_NOT_ALLOWED,
            ErrorResponse {
                message: format!(
                    "Invalid method {method} for this API. Supported methods are: {}",
                    allowed_methods
                        .iter()
                        .map(|m| m.as_str())
                        .collect::<Vec<&str>>()
                        .join(", ")
                ),
            },
        )?
        .into())
    }
}

pub fn build_error_body(message: &str) -> Full<Bytes> {
    let result = serde_json::to_vec(&ErrorResponse {
        message: message.to_string(),
    });

    match result {
        Ok(body) => Full::new(Bytes::from(body)),
        Err(error) => {
            error!("Failed to encode error response body: {error:?}");
            Full::new(Bytes::from("{\"message\": \"Internal server error\"}"))
        }
    }
}