1
1
//! Tests invoking an API defined in a custom backend.
2

            
3
use bonsaidb::{
4
    client::{url::Url, Client},
5
    core::{
6
        async_trait::async_trait,
7
        custom_api::{CustomApi, Infallible},
8
        permissions::{Actionable, Dispatcher, Permissions},
9
        test_util::{Basic, TestDirectory},
10
    },
11
    local::config::Builder,
12
    server::{
13
        Backend, BackendError, ConnectedClient, CustomApiDispatcher, CustomServer,
14
        DefaultPermissions, ServerConfiguration,
15
    },
16
};
17
use serde::{Deserialize, Serialize};
18

            
19
4
#[derive(Debug, Dispatcher)]
20
#[dispatcher(input = CustomRequest, actionable = bonsaidb::core::actionable)]
21
struct CustomBackend {
22
    client: ConnectedClient<Self>,
23
}
24

            
25
impl Backend for CustomBackend {
26
    type CustomApi = Self;
27
    type CustomApiDispatcher = Self;
28
    type ClientData = u64;
29
}
30

            
31
impl CustomApiDispatcher<Self> for CustomBackend {
32
2
    fn new(_server: &CustomServer<Self>, client: &ConnectedClient<Self>) -> Self {
33
2
        Self {
34
2
            client: client.clone(),
35
2
        }
36
2
    }
37
}
38

            
39
impl CustomApi for CustomBackend {
40
    type Request = CustomRequest;
41
    type Response = CustomResponse;
42
    type Error = Infallible;
43
}
44

            
45
4
#[derive(Serialize, Deserialize, Debug, Actionable)]
46
#[actionable(actionable = bonsaidb_core::actionable)]
47
enum CustomRequest {
48
    #[actionable(protection = "none")]
49
    SetValue(u64),
50
}
51

            
52
4
#[derive(Serialize, Deserialize, Debug, Clone)]
53
enum CustomResponse {
54
    ExistingValue(Option<u64>),
55
}
56

            
57
1
#[tokio::test]
58
1
async fn custom_api() -> anyhow::Result<()> {
59
1
    let dir = TestDirectory::new("custom_api.bonsaidb");
60
1
    let server = CustomServer::<CustomBackend>::open(
61
1
        ServerConfiguration::new(&dir)
62
1
            .default_permissions(DefaultPermissions::AllowAll)
63
1
            .with_schema::<Basic>()?,
64
18
    )
65
18
    .await?;
66
8
    server.install_self_signed_certificate(false).await?;
67
1
    let certificate = server
68
1
        .certificate_chain()
69
1
        .await?
70
1
        .into_end_entity_certificate();
71
3
    tokio::spawn(async move { server.listen_on(12346).await });
72

            
73
1
    let client = Client::build(Url::parse("bonsaidb://localhost:12346")?)
74
1
        .with_custom_api::<CustomBackend>()
75
1
        .with_certificate(certificate)
76
1
        .finish()
77
        .await?;
78

            
79
1
    let CustomResponse::ExistingValue(old_data) =
80
1
        client.send_api_request(CustomRequest::SetValue(1)).await?;
81
1
    assert_eq!(old_data, None);
82
1
    let CustomResponse::ExistingValue(old_data) =
83
1
        client.send_api_request(CustomRequest::SetValue(2)).await?;
84
1
    assert_eq!(old_data, Some(1));
85

            
86
1
    Ok(())
87
1
}
88

            
89
impl CustomRequestDispatcher for CustomBackend {
90
    type Output = CustomResponse;
91
    type Error = BackendError<Infallible>;
92
}
93

            
94
#[async_trait]
95
impl SetValueHandler for CustomBackend {
96
2
    async fn handle(
97
2
        &self,
98
2
        _permissions: &Permissions,
99
2
        value: u64,
100
2
    ) -> Result<CustomResponse, BackendError<Infallible>> {
101
2
        let mut data = self.client.client_data().await;
102
2
        let existing_value = data.replace(value);
103
2
        Ok(CustomResponse::ExistingValue(existing_value))
104
4
    }
105
}