use crate::{
allow_cors_with_origin, check, generate_action_templates, HttpReply, HttpRequest,
ProcessActionStruct, ToServiceSchema, WithActionStruct,
};
use async_graphql::{
http::{receive_body, GraphiQLSource},
EmptyMutation, EmptySubscription,
};
use futures::executor::block_on;
const SIMPLE_UI: &[u8] = br#"<html><div id="root" class="ui container"></div><script src="/common/SimpleUI.mjs" type="module"></script></html>"#;
pub fn serve_simple_ui<Wrapper: WithActionStruct + ToServiceSchema>(
request: &HttpRequest,
) -> Option<HttpReply> {
None.or_else(|| serve_simple_index(request))
.or_else(|| serve_action_templates::<Wrapper>(request))
.or_else(|| serve_pack_action::<Wrapper>(request))
}
pub fn serve_simple_index(request: &HttpRequest) -> Option<HttpReply> {
if request.method == "GET" && (request.target == "/" || request.target == "/index.html") {
Some(HttpReply {
status: 200,
contentType: "text/html".into(),
body: SIMPLE_UI.to_vec().into(),
headers: allow_cors_with_origin("*"),
})
} else {
None
}
}
pub fn serve_action_templates<Wrapper: ToServiceSchema>(
request: &HttpRequest,
) -> Option<HttpReply> {
if request.method == "GET" && request.target == "/action_templates" {
Some(HttpReply {
status: 200,
contentType: "text/html".into(),
body: generate_action_templates::<Wrapper>().into(),
headers: allow_cors_with_origin("*"),
})
} else {
None
}
}
pub fn serve_pack_action<Wrapper: WithActionStruct>(request: &HttpRequest) -> Option<HttpReply> {
struct PackAction<'a>(&'a [u8]);
impl<'a> ProcessActionStruct for PackAction<'a> {
type Output = HttpReply;
fn process<
Return: serde::Serialize + serde::de::DeserializeOwned,
ArgStruct: fracpack::Pack + serde::Serialize + serde::de::DeserializeOwned,
>(
self,
) -> Self::Output {
let arg_struct_result = serde_json::from_slice::<ArgStruct>(self.0);
if let Err(err) = &arg_struct_result {
check(false, &format!("err parsing action args json {}", err));
}
HttpReply {
status: 200,
contentType: "application/octet-stream".into(),
body: arg_struct_result.unwrap().packed().into(),
headers: allow_cors_with_origin("*"),
}
}
}
if request.method == "POST" && request.target.starts_with("/pack_action/") {
Wrapper::with_action_struct(&request.target[13..], PackAction(&request.body))
} else {
None
}
}
pub fn serve_graphql<Query: async_graphql::ObjectType + 'static>(
request: &HttpRequest,
query: Query,
) -> Option<HttpReply> {
let (base, args) = if let Some((b, q)) = request.target.split_once('?') {
(b, q)
} else {
(request.target.as_ref(), "")
};
if base != "/graphql" || request.method != "GET" && request.method != "POST" {
return None;
}
block_on(async move {
let schema = async_graphql::Schema::new(query, EmptyMutation, EmptySubscription);
if let Some(request) = args.strip_prefix("?query=") {
let res = schema.execute(request).await;
Some(HttpReply {
status: 200,
contentType: "application/json".into(),
body: serde_json::to_vec(&res).unwrap().into(),
headers: allow_cors_with_origin("*"),
})
} else if request.method == "GET" {
Some(HttpReply {
status: 200,
contentType: "text".into(), body: schema.sdl().into_bytes().into(),
headers: allow_cors_with_origin("*"),
})
} else if request.contentType == "application/graphql" {
let res = schema
.execute(std::str::from_utf8(&request.body.0).unwrap())
.await;
Some(HttpReply {
status: 200,
contentType: "application/json".into(),
body: serde_json::to_vec(&res).unwrap().into(),
headers: allow_cors_with_origin("*"),
})
} else {
let request_result = receive_body(
Some(&request.contentType),
request.body.as_ref(),
Default::default(),
)
.await;
if let Err(err) = &request_result {
check(false, &format!("err parsing graphql query {}", err));
}
let res = schema.execute(request_result.unwrap()).await;
Some(HttpReply {
status: 200,
contentType: "application/json".into(),
body: serde_json::to_vec(&res).unwrap().into(),
headers: allow_cors_with_origin("*"),
})
}
})
}
pub fn serve_graphiql(request: &HttpRequest) -> Option<HttpReply> {
if request.method == "GET"
&& (request.target == "/graphiql.html" || request.target == "/graphiql")
{
Some(HttpReply {
status: 200,
contentType: "text/html".into(),
body: GraphiQLSource::build().endpoint("/graphql").finish().into(),
headers: allow_cors_with_origin("*"),
})
} else {
None
}
}