Skip to main content

flams_router_dashboard/
query.rs

1use flams_router_base::maybe_lazy;
2use ftml_dom::utils::css::inject_css;
3use leptos::prelude::*;
4
5#[server(QueryApi,
6  prefix="/api/backend",
7  endpoint="query",
8  input=server_fn::codec::PostUrl,
9  output=server_fn::codec::Json
10)]
11#[cfg_attr(
12    feature = "ssr",
13    tracing::instrument(level = "info", name = "query", target = "query", skip_all)
14)]
15pub async fn query_api(
16    query: String,
17    decode_uris: Option<bool>,
18) -> Result<flams_backend_types::sparql::SparqlResult, ServerFnError<String>> {
19    use flams_math_archives::backend::GlobalBackend;
20    use flams_math_archives::triple_store::sparql::QueryResult;
21    use flams_system::TokioEngine;
22    tracing::info!("Query: {query} (decode_uris: {decode_uris:?}");
23    let decode_uris = decode_uris.unwrap_or(true);
24    let r = tokio::task::spawn_blocking(move || {
25        GlobalBackend
26            .triple_store()
27            .query_str::<TokioEngine>(&query)
28            .map(|q| QueryResult::into_json(q, decode_uris))
29    })
30    .await; //.in_current_span().await;
31    match r {
32        Ok(Ok(r)) => Ok(r),
33        //Ok(Ok(Err(e))) => Err(ServerFnError::WrappedServerError(e.to_string())),
34        Ok(Err(e)) => Err(ServerFnError::WrappedServerError(e.to_string())),
35        Err(e) => Err(ServerFnError::WrappedServerError(e.to_string())),
36    }
37}
38
39const QUERY: &str = r"SELECT ?x ?y WHERE {
40  ?x rdf:type ulo:declaration .
41  ?y rdf:type ulo:notation .
42  ?y ulo:notation-for ?x.
43}";
44
45maybe_lazy!(Query = query());
46
47fn query() -> AnyView {
48    use ftml_component_utils::Checkbox;
49    use ftml_component_utils::{Code, Input, Text};
50    use leptos::form::ActionForm;
51    inject_css("flams-query", include_str!("query.css"));
52
53    let action = ServerAction::<QueryApi>::new();
54    let rf = NodeRef::<leptos::html::Div>::new();
55    let pretty_print = RwSignal::new(false);
56    let result = Memo::new(move |_| {
57        action.value().get().map(|result| match result {
58            Ok(r) => {
59                if pretty_print.get() {
60                    serde_json::to_string_pretty(&r)//from_str::<serde_json::Value>(&r)
61                        .map_or_else(|e| format!("Error: {e}"), |v| format!("{v:#}"))
62                } else {
63                    serde_json::to_string(&r).expect("infallible?")
64                }
65            }
66            Err(e) => format!("Error: {e}"),
67        })
68    });
69    let uri = RwSignal::new(String::new());
70
71    view! {
72      <div>
73        <h1>Query</h1>
74            <p>"Note that FTML URIs need to be partially URL-encoded to be valid RDF-IRIs"</p>
75            <div>
76                <Text>"Encode URI: "</Text>
77                <Input value=uri/>
78                <Code>{
79                    move || {
80                        ftml_uris::rdf_encode(&uri.get()).unwrap_or_else(|| "(invalid URI)".to_string())
81                    }
82                }</Code>
83            </div>
84        <ActionForm action>
85            <span class="flams-query-container">
86                <textarea name="query" class="flams-query-inner">{QUERY.to_string()}</textarea>
87            </span>
88            <br/><input type="submit" value="Query"/>
89        </ActionForm>
90        <Checkbox checked=pretty_print label="pretty printed"/>
91        <div node_ref=rf style="text-align:left;margin:10px;font-family:monospace;white-space:pre;border:var(--strokeWidthThickest) solid var(--colorNeutralStroke1);text-wrap:pretty;">
92            {move || result.get().unwrap_or_default()}
93        </div>
94      </div>
95    }.into_any()
96}