kdb-connection 0.1.0

kDB connection.
Documentation
use std::time::Duration;

use crate::{connection, prelude::*};

#[derive(Clone)]
pub struct Connection
{
    url: String,
}

impl Connection
{
    /// Create a new http connection to a `kDB` store at the given URL.
    pub fn new(url: impl Into<String>) -> Connection
    {
        Connection { url: url.into() }
    }
}

impl connection::ConnectionPrivate for Connection {}

impl connection::Connection for Connection
{
    async fn is_connected(&self) -> bool
    {
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(5))
            .build()
            .unwrap();

        client.get(&self.url).send().await.is_ok()
    }
    fn execute_query<TQuery>(
        &self,
        query: TQuery,
    ) -> Result<impl std::future::Future<Output = Result<dbc::Result>>>
    where
        TQuery: TryInto<dbc::Query>,
        crate::Error: From<<TQuery as TryInto<dbc::Query>>::Error>,
    {
        let query = query.try_into()?;
        Ok(async move {
            let params = [
                ("query", query.query),
                ("Accept", "application/sparql-results+json".to_string()),
            ];
            let client = reqwest::Client::new();
            let uri_type = match query.query_type
            {
                dbc::QueryType::KDQL => "kdql",
                dbc::QueryType::KRQL => "krql",
                dbc::QueryType::SPARQL => "sparql",
                dbc::QueryType::SQL => "sql",
            };
            let res = client
                .post(format!("{}/{}.html", self.url, uri_type))
                .form(&params)
                .send()
                .await?;

            Ok(res.json::<crate::dbc::Result>().await?)
        })
    }
}

#[cfg(test)]
mod tests
{
    use crate::prelude::*;
    use queries::{kdql, krql};

    #[tokio::test(flavor = "current_thread")]
    async fn it_can_run_kr_query()
    {
        let mut store =
            crate::test::create_store(crate::test::create_store_configuration().set_web_port(8888));
        store.start().unwrap();
        let q = krql::Test {
            return_value: Some(12.into()),
            ..Default::default()
        };
        let c = crate::http::Connection::new("http://localhost:8888");
        crate::test::wait_for_connection(&c).await;

        let f = c.execute_query(&q).unwrap();
        let r = f.await.unwrap();
        assert_eq!(r.metadata.error, String::new());
        assert_eq!(
            r.metadata.query,
            "test:\n  return:\n    type: literal\n    datatype: \"http://www.w3.org/2001/XMLSchema#long\"\n    value: 12\n"
        );
        assert!(r.metadata.success);
        assert_eq!(r.head.vars, ["data"]);
        assert_eq!(r.results.bindings.len(), 1);
        let v0 = r.results.bindings.first().unwrap();
        assert_eq!(v0.len(), 1);
        let v0data = v0.get("data").unwrap();
        assert_eq!(*v0data, 12.into());
    }

    #[tokio::test(flavor = "current_thread")]
    async fn it_can_run_kd_query()
    {
        let mut store = crate::test::create_store(
            crate::test::create_store_configuration()
                .set_web_port(8888)
                .load_extension("kDBDocuments"),
        );
        store.start().unwrap();

        let c = crate::http::Connection::new("http://localhost:8888");
        crate::test::wait_for_connection(&c).await;

        // Create some documents
        let q = kdql::KDQLQuery::Create {
            into: "test".into(),
            documents: vec![
                crate::krql_value_map!("name" => "a", "age" => 12),
                crate::krql_value_map!("name" => "b", "age" => 42),
            ],
        };
        let f = c.execute_query(&q).unwrap();
        let r = f.await.unwrap();
        assert_eq!(r.metadata.error, String::new());

        // Retrieve the "b" document
        let q = kdql::KDQLQuery::Retrieve {
            what: kdql::What::All,
            from: "test".into(),
            matches: crate::krql_value_map!("name" => "b"),
        };
        let f = c.execute_query(q).unwrap();
        let r = f.await.unwrap();
        assert_eq!(r.metadata.error, String::new());

        assert!(r.metadata.success);
        assert_eq!(r.head.vars, ["*"]);
        assert_eq!(r.results.bindings.len(), 1);
        let v0 = r.results.bindings.first().unwrap();
        assert_eq!(v0.len(), 1);
        let v0data = v0.get("*").unwrap();
        assert_eq!(
            *v0data,
            crate::value_hash!("name" => "b", "age" => 42).into()
        );

        // Retrieve the age of "b" document
        let q = kdql::KDQLQuery::Retrieve {
            what: vec!["age"].into(),
            from: "test".into(),
            matches: crate::krql_value_map!("name" => "b"),
        };
        let f = c.execute_query(q).unwrap();
        let r = f.await.unwrap();
        assert_eq!(r.metadata.error, String::new());

        assert!(r.metadata.success);
        assert_eq!(r.head.vars, ["age"]);
        assert_eq!(r.results.bindings.len(), 1);
        let v0 = r.results.bindings.first().unwrap();
        assert_eq!(v0.len(), 1);
        let v0data = v0.get("age").unwrap();
        assert_eq!(*v0data, 42.into());
    }
}