use async_graphql::{ObjectType, SubscriptionType};
use aws_lambda_events::encodings::Body;
use netlify_lambda_http::{Request, Response};
use std::any::Any;
use diana::{DianaHandler, DianaResponse, Options};
pub type AwsError = Box<dyn std::error::Error + Send + Sync + 'static>;
enum AwsReqData {
Valid((String, Option<String>)),
Invalid(Response<String>), }
fn get_data_from_aws_req(req: Request) -> Result<AwsReqData, AwsError> {
let body = req.body();
let body = match body {
Body::Text(body_str) => body_str.to_string(),
Body::Binary(body_binary) => {
let body_str = std::str::from_utf8(&body_binary);
match body_str {
Ok(body_str) => body_str.to_string(),
Err(_) => {
let res = Response::builder()
.status(400) .body(
"Found binary body that couldn't be serialized to string".to_string(),
)?;
return Ok(AwsReqData::Invalid(res));
}
}
}
Body::Empty => {
let res = Response::builder()
.status(400) .body("Found empty body, expected string".to_string())?;
return Ok(AwsReqData::Invalid(res));
}
};
let auth_header = req.headers().get("Authorization");
let auth_header = match auth_header {
Some(auth_header) => {
let header_str = auth_header.to_str();
match header_str {
Ok(header_str) => Some(header_str.to_string()),
Err(_) => {
let res = Response::builder()
.status(400) .body("Couldn't parse authorization header as string".to_string())?;
return Ok(AwsReqData::Invalid(res));
}
}
}
None => None,
};
Ok(AwsReqData::Valid((body, auth_header)))
}
fn parse_aws_res(res: DianaResponse) -> Result<Response<String>, AwsError> {
let res = match res {
DianaResponse::Success(gql_res_str) => Response::builder()
.status(200) .body(gql_res_str)?,
DianaResponse::Blocked => Response::builder()
.status(403) .body("Request blocked due to invalid or insufficient authentication".to_string())?,
DianaResponse::Error(_) => Response::builder()
.status(500) .body("An internal server error occurred".to_string())?,
};
Ok(res)
}
pub async fn run_aws_req<C, Q, M, S>(
req: Request,
opts: Options<C, Q, M, S>,
) -> Result<Response<String>, AwsError>
where
C: Any + Send + Sync + Clone,
Q: Clone + ObjectType + 'static,
M: Clone + ObjectType + 'static,
S: Clone + SubscriptionType + 'static,
{
let diana_handler = DianaHandler::new(opts.clone()).map_err(|err| err.to_string())?;
let req_data = get_data_from_aws_req(req)?;
let (body, auth_header) = match req_data {
AwsReqData::Valid(data) => data,
AwsReqData::Invalid(http_res) => return Ok(http_res), };
let res = diana_handler
.run_stateless_without_subscriptions(body, auth_header.as_deref(), None)
.await;
let http_res = parse_aws_res(res)?;
Ok(http_res)
}