arangors/
database.rs

1//! struct and enum pertain to arangoDB database
2//!
3//! AQL query are all executed in database level, so Database offers AQL query.
4use std::{collections::HashMap, fmt::Debug, sync::Arc};
5
6use log::trace;
7use maybe_async::maybe_async;
8use serde::{de::DeserializeOwned, Deserialize};
9use serde_json::value::Value;
10use url::Url;
11
12use crate::{
13    analyzer::{AnalyzerDescription, AnalyzerInfo},
14    aql::{AqlQuery, Cursor},
15    client::ClientExt,
16    collection::{
17        options::{CreateOptions, CreateParameters},
18        response::{Info, Properties},
19        Collection, CollectionType,
20    },
21    connection::Version,
22    graph::{Graph, GraphCollection, GraphResponse, GHARIAL_API_PATH},
23    index::{DeleteIndexResponse, Index, IndexCollection, INDEX_API_PATH},
24    response::{deserialize_response, ArangoResult},
25    transaction::{
26        ArangoTransaction, Transaction, TransactionList, TransactionSettings, TransactionState,
27        TRANSACTION_HEADER,
28    },
29    user::{
30        access_level_enum_to_str, DeleteUserResponse, User, UserAccessLevel,
31        UserDatabasesGetResponse, UserResponse,
32    },
33    view::{
34        ArangoSearchViewProperties, ArangoSearchViewPropertiesOptions, View, ViewDescription,
35        ViewOptions,
36    },
37    ClientError,
38};
39
40#[derive(Debug, Clone)]
41pub struct Database<C: ClientExt> {
42    name: String,
43    base_url: Url,
44    session: Arc<C>,
45}
46
47impl<'a, C: ClientExt> Database<C> {
48    pub(crate) fn new<T: Into<String>>(name: T, arango_url: &Url, session: Arc<C>) -> Database<C> {
49        let name = name.into();
50        let path = format!("/_db/{}/", name.as_str());
51        let url = arango_url.join(path.as_str()).unwrap();
52        Database {
53            name,
54            session,
55            base_url: url,
56        }
57    }
58
59    /// Retrieve all collections of this database.
60    ///
61    /// # Note
62    /// this function would make a request to arango server.
63    #[maybe_async]
64    pub async fn accessible_collections(&self) -> Result<Vec<Info>, ClientError> {
65        // an invalid arango_url should never running through initialization
66        // so we assume arango_url is a valid url
67        // When we pass an invalid path, it should panic to eliminate the bug
68        // in development.
69        let url = self.base_url.join("_api/collection").unwrap();
70        trace!(
71            "Retrieving collections from {:?}: {}",
72            self.name,
73            url.as_str()
74        );
75        let resp = self.session.get(url, "").await?;
76        let result: ArangoResult<Vec<Info>> = deserialize_response(resp.body())?;
77        trace!("Collections retrieved");
78        Ok(result.unwrap())
79    }
80
81    pub fn url(&self) -> &Url {
82        &self.base_url
83    }
84
85    pub fn name(&self) -> &str {
86        &self.name
87    }
88
89    pub fn session(&self) -> Arc<C> {
90        Arc::clone(&self.session)
91    }
92
93    /// Get collection object with name.
94    ///
95    /// # Note
96    /// this function would make a request to arango server.
97    #[maybe_async]
98    pub async fn collection(&self, name: &str) -> Result<Collection<C>, ClientError> {
99        let url = self
100            .base_url
101            .join(&format!("_api/collection/{}", name))
102            .unwrap();
103        let resp: Info = deserialize_response(self.session.get(url, "").await?.body())?;
104        Ok(Collection::from_response(self, &resp))
105    }
106
107    /// Create a collection via HTTP request with options.
108    ///
109    /// Return a collection object if success.
110    ///
111    /// # Note
112    /// this function would make a request to arango server.
113    #[maybe_async]
114    pub async fn create_collection_with_options<'f>(
115        &self,
116        options: CreateOptions<'f>,
117        parameters: CreateParameters,
118    ) -> Result<Collection<C>, ClientError> {
119        let mut url = self.base_url.join("_api/collection").unwrap();
120        let query = serde_qs::to_string(&parameters).unwrap();
121        url.set_query(Some(query.as_str()));
122
123        let resp = self
124            .session
125            .post(url, &serde_json::to_string(&options)?)
126            .await?;
127        let result: Properties = deserialize_response(resp.body())?;
128        self.collection(&result.info.name).await
129    }
130
131    /// Create a collection via HTTP request.
132    ///
133    /// Return a collection object if success.
134    ///
135    /// # Note
136    /// this function would make a request to arango server.
137    #[maybe_async]
138    pub async fn create_collection(&self, name: &str) -> Result<Collection<C>, ClientError> {
139        self.create_collection_with_options(
140            CreateOptions::builder().name(name).build(),
141            Default::default(),
142        )
143        .await
144    }
145
146    #[maybe_async]
147    pub async fn create_edge_collection(&self, name: &str) -> Result<Collection<C>, ClientError> {
148        self.create_collection_with_options(
149            CreateOptions::builder()
150                .name(name)
151                .collection_type(CollectionType::Edge)
152                .build(),
153            Default::default(),
154        )
155        .await
156    }
157
158    /// Drops a collection
159    ///
160    /// # Note
161    /// this function would make a request to arango server.
162    #[maybe_async]
163    pub async fn drop_collection(&self, name: &str) -> Result<String, ClientError> {
164        let url_path = format!("_api/collection/{}", name);
165        let url = self.base_url.join(&url_path).unwrap();
166
167        #[derive(Debug, Deserialize)]
168        struct DropCollectionResponse {
169            id: String,
170        }
171
172        let resp: DropCollectionResponse =
173            deserialize_response(self.session.delete(url, "").await?.body())?;
174        Ok(resp.id)
175    }
176
177    /// Get the version remote arango database server
178    ///
179    /// # Note
180    /// this function would make a request to arango server.
181    #[maybe_async]
182    pub async fn arango_version(&self) -> Result<Version, ClientError> {
183        let url = self.base_url.join("_api/version").unwrap();
184        let resp = self.session.get(url, "").await?;
185        let version: Version = serde_json::from_str(resp.body())?;
186        Ok(version)
187    }
188
189    /// Get information of current database.
190    ///
191    /// # Note
192    /// this function would make a request to arango server.
193    #[maybe_async]
194    pub async fn info(&self) -> Result<DatabaseDetails, ClientError> {
195        let url = self.base_url.join("_api/database/current").unwrap();
196        let resp = self.session.get(url, "").await?;
197        let res: ArangoResult<DatabaseDetails> = deserialize_response(resp.body())?;
198        Ok(res.unwrap())
199    }
200
201    /// Execute aql query, return a cursor if succeed. The major advantage of
202    /// batch query is that cursors contain more information and stats
203    /// about the AQL query, and users can fetch results in batch to save memory
204    /// resources on clients.
205    ///
206    /// # Note
207    /// this function would make a request to arango server.
208    #[maybe_async]
209    pub async fn aql_query_batch<R>(&self, aql: AqlQuery<'_>) -> Result<Cursor<R>, ClientError>
210    where
211        R: DeserializeOwned,
212    {
213        let url = self.base_url.join("_api/cursor").unwrap();
214        let resp = self
215            .session
216            .post(url, &serde_json::to_string(&aql)?)
217            .await?;
218        deserialize_response(resp.body())
219    }
220
221    /// Get next batch given the cursor id.
222    ///
223    /// # Note
224    /// this function would make a request to arango server.
225    #[maybe_async]
226    pub async fn aql_next_batch<R>(&self, cursor_id: &str) -> Result<Cursor<R>, ClientError>
227    where
228        R: DeserializeOwned,
229    {
230        let url = self
231            .base_url
232            .join(&format!("_api/cursor/{}", cursor_id))
233            .unwrap();
234        let resp = self.session.put(url, "").await?;
235        deserialize_response(resp.body())
236    }
237
238    #[maybe_async]
239    async fn aql_fetch_all<R>(&self, response: Cursor<R>) -> Result<Vec<R>, ClientError>
240    where
241        R: DeserializeOwned,
242    {
243        let mut response_cursor = response;
244        let mut results: Vec<R> = Vec::new();
245        loop {
246            results.extend(response_cursor.result.into_iter());
247            if response_cursor.more {
248                let id = response_cursor.id.unwrap().clone();
249                response_cursor = self.aql_next_batch(id.as_str()).await?;
250            } else {
251                break;
252            }
253        }
254        Ok(results)
255    }
256
257    /// Execute AQL query fetch all results.
258    ///
259    /// DO NOT do this when the count of results is too large that network or
260    /// memory resources cannot afford.
261    ///
262    /// DO NOT set a small batch size, otherwise clients will have to make many
263    /// HTTP requests.
264    ///
265    /// # Note
266    /// this function would make a request to arango server.
267    #[maybe_async]
268    pub async fn aql_query<R>(&self, aql: AqlQuery<'_>) -> Result<Vec<R>, ClientError>
269    where
270        R: DeserializeOwned,
271    {
272        let response = self.aql_query_batch(aql).await?;
273        if response.more {
274            self.aql_fetch_all(response).await
275        } else {
276            Ok(response.result)
277        }
278    }
279
280    /// Similar to `aql_query`, except that this method only accept a string of
281    /// AQL query.
282    ///
283    /// # Note
284    /// this function would make a request to arango server.
285    #[maybe_async]
286    pub async fn aql_str<R>(&self, query: &str) -> Result<Vec<R>, ClientError>
287    where
288        R: DeserializeOwned,
289    {
290        let aql = AqlQuery::builder().query(query).build();
291        self.aql_query(aql).await
292    }
293
294    /// Similar to `aql_query`, except that this method only accept a string of
295    /// AQL query, with additional bind vars.
296    ///
297    /// # Note
298    /// this function would make a request to arango server.
299    #[maybe_async]
300    pub async fn aql_bind_vars<R>(
301        &self,
302        query: &str,
303        bind_vars: HashMap<&str, Value>,
304    ) -> Result<Vec<R>, ClientError>
305    where
306        R: DeserializeOwned,
307    {
308        let aql = AqlQuery::builder()
309            .query(query)
310            .bind_vars(bind_vars)
311            .build();
312        self.aql_query(aql).await
313    }
314
315    /// Create a new index on a collection.
316    ///
317    /// # Note
318    /// this function would make a request to arango server.
319    #[maybe_async]
320    pub async fn create_index(
321        &self,
322        collection: &str,
323        index: &Index,
324    ) -> Result<Index, ClientError> {
325        let mut url = self.base_url.join(INDEX_API_PATH).unwrap();
326        url.set_query(Some(&format!("collection={}", collection)));
327
328        let resp = self
329            .session
330            .post(url, &serde_json::to_string(&index)?)
331            .await?;
332
333        let result: Index = deserialize_response::<Index>(resp.body())?;
334
335        Ok(result)
336    }
337
338    /// Retrieve an index by id
339    ///
340    /// # Note
341    /// this function would make a request to arango server.
342    #[maybe_async]
343    pub async fn index(&self, id: &str) -> Result<Index, ClientError> {
344        let url = self
345            .base_url
346            .join(&format!("{}/{}", INDEX_API_PATH, id))
347            .unwrap();
348
349        let resp = self.session.get(url, "").await?;
350
351        let result: Index = deserialize_response::<Index>(resp.body())?;
352
353        Ok(result)
354    }
355
356    /// Retrieve a list of indexes for a collection.
357    ///
358    /// # Note
359    /// this function would make a request to arango server.
360    #[maybe_async]
361    pub async fn indexes(&self, collection: &str) -> Result<IndexCollection, ClientError> {
362        let mut url = self.base_url.join(INDEX_API_PATH).unwrap();
363        url.set_query(Some(&format!("collection={}", collection)));
364
365        let resp = self.session.get(url, "").await?;
366
367        let result: IndexCollection = deserialize_response::<IndexCollection>(resp.body())?;
368
369        Ok(result)
370    }
371
372    /// Delete an index by id.
373    ///
374    /// # Note
375    /// this function would make a request to arango server.
376    #[maybe_async]
377    pub async fn delete_index(&self, id: &str) -> Result<DeleteIndexResponse, ClientError> {
378        let url = self
379            .base_url
380            .join(&format!("{}/{}", INDEX_API_PATH, id))
381            .unwrap();
382        let resp = self.session.delete(url, "").await?;
383
384        let result: DeleteIndexResponse = deserialize_response::<DeleteIndexResponse>(resp.body())?;
385
386        Ok(result)
387    }
388
389    /// Create a new graph in the graph module.
390    ///
391    /// # Arguments
392    /// * `graph` - The graph object to create, its name must be unique.
393    /// * `wait_for_sync` - define if the request should wait until everything
394    ///   is synced to disc.
395    ///
396    /// # Note
397    /// this function would make a request to arango server.
398    #[maybe_async]
399    pub async fn create_graph(
400        &self,
401        graph: Graph,
402        wait_for_sync: bool,
403    ) -> Result<Graph, ClientError> {
404        let mut url = self.base_url.join(GHARIAL_API_PATH).unwrap();
405        url.set_query(Some(&format!("waitForSync={}", wait_for_sync)));
406
407        let resp = self
408            .session
409            .post(url, &serde_json::to_string(&graph)?)
410            .await?;
411
412        let result: GraphResponse = deserialize_response::<GraphResponse>(resp.body())?;
413
414        Ok(result.graph)
415    }
416
417    /// Retrieve an graph by name
418    ///
419    /// # Note
420    /// this function would make a request to arango server.
421    #[maybe_async]
422    pub async fn graph(&self, name: &str) -> Result<Graph, ClientError> {
423        let url = self
424            .base_url
425            .join(&format!("{}/{}", GHARIAL_API_PATH, name))
426            .unwrap();
427
428        let resp = self.session.get(url, "").await?;
429
430        let result: GraphResponse = deserialize_response::<GraphResponse>(resp.body())?;
431
432        Ok(result.graph)
433    }
434
435    /// Retrieve the list of created graphs.
436    ///
437    /// # Note
438    /// this function would make a request to arango server.
439    #[maybe_async]
440    pub async fn graphs(&self) -> Result<GraphCollection, ClientError> {
441        let url = self.base_url.join(GHARIAL_API_PATH).unwrap();
442
443        let resp = self.session.get(url, "").await?;
444
445        let result: GraphCollection = deserialize_response::<GraphCollection>(resp.body())?;
446
447        Ok(result)
448    }
449
450    /// Drops an existing graph object by name. Optionally all collections not
451    /// used by other graphs can be dropped as well.
452    ///
453    /// # Arguments
454    /// * `name` - The name of the graph to drop
455    /// * `drop_collections`- if set to `true`, drops collections of this graph
456    ///   as well.
457    /// Collections will only be dropped if they are not used in other graphs.
458    ///
459    /// # Note
460    /// this function would make a request to arango server.
461    #[maybe_async]
462    pub async fn drop_graph(&self, name: &str, drop_collections: bool) -> Result<(), ClientError> {
463        let mut url = self
464            .base_url
465            .join(&format!("{}/{}", GHARIAL_API_PATH, name))
466            .unwrap();
467        url.set_query(Some(&format!("dropCollections={}", drop_collections)));
468
469        self.session.delete(url, "").await?;
470
471        Ok(())
472    }
473
474    /// Return the currently running server-side transactions
475    ///
476    /// # Note
477    /// this function would make a request to arango server.
478    #[maybe_async]
479    pub async fn list_transactions(&self) -> Result<Vec<TransactionState>, ClientError> {
480        let url = self.base_url.join("_api/transaction").unwrap();
481
482        let resp = self.session.get(url, "").await?;
483
484        let result: TransactionList = deserialize_response(resp.body())?;
485        Ok(result.transactions)
486    }
487
488    /// Begin a server-side transaction, the transaction settings should specify
489    /// at least collections to be updated through the write list
490    ///
491    /// # Note
492    /// this function would make a request to arango server.
493    #[maybe_async]
494    pub async fn begin_transaction(
495        &self,
496        transaction_settings: TransactionSettings,
497    ) -> Result<Transaction<C>, ClientError> {
498        let url = self.base_url.join("_api/transaction/begin").unwrap();
499
500        let resp = self
501            .session
502            .post(url, &serde_json::to_string(&transaction_settings)?)
503            .await?;
504
505        let result: ArangoResult<ArangoTransaction> = deserialize_response(resp.body())?;
506        let transaction = result.unwrap();
507        let tx_id = transaction.id.clone();
508
509        let mut session = (*self.session).clone();
510        session
511            .headers()
512            .insert(TRANSACTION_HEADER, tx_id.parse().unwrap());
513
514        Ok(Transaction::<C>::new(
515            transaction,
516            Arc::new(session),
517            self.base_url.clone(),
518        ))
519    }
520
521    /// Returns an object containing a listing of all Views in a database,
522    /// regardless of their typ
523    ///
524    /// # Note
525    /// this function would make a request to arango server.
526    #[maybe_async]
527    pub async fn list_views(&self) -> Result<Vec<ViewDescription>, ClientError> {
528        let url = self.base_url.join("_api/view").unwrap();
529
530        let resp = self.session.get(url, "").await?;
531
532        let result: ArangoResult<Vec<ViewDescription>> = deserialize_response(resp.body())?;
533        Ok(result.unwrap())
534    }
535
536    /// Creates an ArangoSearch View
537    ///
538    /// # Note
539    /// this function would make a request to arango server.
540    #[maybe_async]
541    pub async fn create_view(&self, view_options: ViewOptions) -> Result<View, ClientError> {
542        let url = self.base_url.join("_api/view").unwrap();
543
544        let resp = self
545            .session
546            .post(url, &serde_json::to_string(&view_options)?)
547            .await?;
548
549        let result: View = deserialize_response(resp.body())?;
550        Ok(result)
551    }
552
553    /// Return information about a View
554    ///
555    /// # Note
556    /// this function would make a request to arango server.
557    #[maybe_async]
558    pub async fn view(&self, view_name: &str) -> Result<ViewDescription, ClientError> {
559        let url = self
560            .base_url
561            .join(&format!("_api/view/{}", view_name))
562            .unwrap();
563
564        let resp = self.session.get(url, "").await?;
565
566        let result: ViewDescription = deserialize_response(resp.body())?;
567        Ok(result)
568    }
569
570    /// Read properties of a View
571    ///
572    /// # Note
573    /// this function would make a request to arango server.
574    #[maybe_async]
575    pub async fn view_properties(
576        &self,
577        view_name: &str,
578    ) -> Result<ArangoSearchViewProperties, ClientError> {
579        let url = self
580            .base_url
581            .join(&format!("_api/view/{}/properties", view_name))
582            .unwrap();
583
584        let resp = self.session.get(url, "").await?;
585
586        let result: ArangoSearchViewProperties = deserialize_response(resp.body())?;
587        Ok(result)
588    }
589
590    /// Changes all the properties of an ArangoSearch
591    ///
592    /// # Note
593    /// this function would make a request to arango server.
594    #[maybe_async]
595    pub async fn replace_view_properties(
596        &self,
597        view_name: &str,
598        properties: ArangoSearchViewPropertiesOptions,
599    ) -> Result<View, ClientError> {
600        let url = self
601            .base_url
602            .join(&format!("_api/view/{}/properties", view_name))
603            .unwrap();
604
605        let resp = self
606            .session
607            .put(url, &serde_json::to_string(&properties)?)
608            .await?;
609
610        let result: View = deserialize_response(resp.body())?;
611        Ok(result)
612    }
613
614    /// Partially changes properties of an ArangoSearch View
615    ///
616    /// # Note
617    /// this function would make a request to arango server.
618    #[maybe_async]
619    pub async fn update_view_properties(
620        &self,
621        view_name: &str,
622        properties: ArangoSearchViewPropertiesOptions,
623    ) -> Result<View, ClientError> {
624        let url = self
625            .base_url
626            .join(&format!("_api/view/{}/properties", view_name))
627            .unwrap();
628
629        let resp = self
630            .session
631            .patch(url, &serde_json::to_string(&properties)?)
632            .await?;
633
634        let result: View = deserialize_response(resp.body())?;
635        Ok(result)
636    }
637
638    /// Drops the View identified by view-name.
639    ///
640    /// # Note
641    /// this function would make a request to arango server.
642    #[maybe_async]
643    pub async fn drop_view(&self, view_name: &str) -> Result<bool, ClientError> {
644        let url = self
645            .base_url
646            .join(&format!("_api/view/{}", view_name))
647            .unwrap();
648
649        let resp = self.session.delete(url, "").await?;
650
651        let result: ArangoResult<bool> = deserialize_response(resp.body())?;
652        Ok(result.unwrap())
653    }
654
655    #[maybe_async]
656    pub async fn list_analyzers(&self) -> Result<Vec<AnalyzerInfo>, ClientError> {
657        let url = self.base_url.join("_api/analyzer").unwrap();
658
659        let resp = self.session.get(url, "").await?;
660
661        let result: ArangoResult<Vec<AnalyzerInfo>> = deserialize_response(resp.body())?;
662        Ok(result.unwrap())
663    }
664
665    /// Create an Analyzer with the supplied definition
666    ///
667    /// # Note
668    /// this function would make a request to arango server.
669    #[maybe_async]
670    pub async fn create_analyzer(
671        &self,
672        analyzer: AnalyzerInfo,
673    ) -> Result<AnalyzerInfo, ClientError> {
674        let url = self.base_url.join("_api/analyzer").unwrap();
675
676        let resp = self
677            .session
678            .post(url, &serde_json::to_string(&analyzer)?)
679            .await?;
680
681        let result: AnalyzerInfo = deserialize_response(resp.body())?;
682        Ok(result)
683    }
684
685    /// Return the Analyzer definition
686    ///
687    /// # Note
688    /// this function would make a request to arango server.
689    #[maybe_async]
690    pub async fn analyzer(&self, analyzer_name: &str) -> Result<AnalyzerInfo, ClientError> {
691        let url = self
692            .base_url
693            .join(&format!("_api/analyzer/{}", analyzer_name))
694            .unwrap();
695
696        let resp = self.session.get(url, "").await?;
697
698        let result: AnalyzerInfo = deserialize_response(resp.body())?;
699        Ok(result)
700    }
701
702    ///Removes an Analyzer configuration identified by analyzer_name.
703    ///
704    /// # Note
705    /// this function would make a request to arango server.
706    #[maybe_async]
707    pub async fn drop_analyzer(
708        &self,
709        analyzer_name: &str,
710    ) -> Result<AnalyzerDescription, ClientError> {
711        let url = self
712            .base_url
713            .join(&format!("_api/analyzer/{}", analyzer_name))
714            .unwrap();
715
716        let resp = self.session.delete(url, "").await?;
717
718        let result: AnalyzerDescription = deserialize_response(resp.body())?;
719        Ok(result)
720    }
721
722    /// List available users
723    ///
724    /// Fetches data about all users. You need the Administrate server access
725    /// level in order to execute this REST call. Otherwise, you will only
726    /// get information about yourself.
727    ///
728    /// # Note
729    /// this function would make a request to arango server.
730    #[maybe_async]
731    pub async fn users(&self) -> Result<Vec<User>, ClientError> {
732        let url = self.base_url.join(&format!("_api/user/")).unwrap();
733
734        let resp = self.session.get(url, "").await?;
735
736        let result: UserResponse = deserialize_response(resp.body())?;
737        Ok(result.result)
738    }
739
740    /// Create User
741    ///
742    /// # Note
743    /// this function would make a request to arango server.
744    #[maybe_async]
745    pub async fn create_user(&self, user: User) -> Result<User, ClientError> {
746        let url = self.base_url.join("_api/user").unwrap();
747
748        let resp = self
749            .session
750            .post(url, &serde_json::to_string(&user)?)
751            .await?;
752
753        let result = deserialize_response(resp.body())?;
754        Ok(result)
755    }
756
757    /// Create User
758    ///
759    /// # Note
760    /// this function would make a request to arango server.
761    #[maybe_async]
762    pub async fn update_user(&self, username: String, user: User) -> Result<User, ClientError> {
763        let url = self
764            .base_url
765            .join(&format!("_api/user/{}", username))
766            .unwrap();
767
768        let resp = self
769            .session
770            .put(url, &serde_json::to_string(&user)?)
771            .await?;
772
773        let result = deserialize_response(resp.body())?;
774        Ok(result)
775    }
776
777    /// Delete User
778    ///
779    /// # Note
780    /// this function would make a request to arango server.
781    #[maybe_async]
782    pub async fn delete_user(&self, username: String) -> Result<(), ClientError> {
783        let url = self
784            .base_url
785            .join(&format!("_api/user/{}", username))
786            .unwrap();
787
788        let resp = self.session.delete(url, "").await?;
789
790        let _: DeleteUserResponse = deserialize_response(resp.body())?;
791        Ok(())
792    }
793
794    /// Get user-accessible databases
795    ///
796    /// # Note
797    /// this function would make a request to arango server.
798    #[maybe_async]
799    pub async fn user_databases(
800        &self,
801        username: String,
802        full: bool,
803    ) -> Result<UserDatabasesGetResponse, ClientError> {
804        let url = self
805            .base_url
806            .join(&format!("_api/user/{username}/database/?full={full}"))
807            .unwrap();
808        let resp = self.session.get(url, "").await?;
809
810        let result = deserialize_response(resp.body())?;
811        Ok(result)
812    }
813
814    /// Get user-accessible databases
815    ///
816    /// # Note
817    /// this function would make a request to arango server.
818    #[maybe_async]
819    pub async fn user_db_access_level(
820        &self,
821        username: String,
822        db_name: String,
823    ) -> Result<UserDatabasesGetResponse, ClientError> {
824        let url = self
825            .base_url
826            .join(&format!("_api/user/{username}/database/{db_name}"))
827            .unwrap();
828        let resp = self.session.get(url, "").await?;
829
830        let result = deserialize_response(resp.body())?;
831        Ok(result)
832    }
833
834    /// Set user's databases access level
835    ///
836    /// # Note
837    /// this function would make a request to arango server.
838    #[maybe_async]
839    pub async fn user_db_access_put(
840        &self,
841        username: String,
842        db_name: String,
843        access_level: UserAccessLevel,
844    ) -> Result<Value, ClientError> {
845        let url = self
846            .base_url
847            .join(&format!("_api/user/{username}/database/{db_name}"))
848            .unwrap();
849        let resp = self
850            .session
851            .put(
852                url,
853                format!(
854                    "{{ \"grant\":\"{}\" }}",
855                    access_level_enum_to_str(access_level)
856                ),
857            )
858            .await?;
859
860        let result = deserialize_response(resp.body())?;
861        Ok(result)
862    }
863
864    /// Set user's databases access level
865    ///
866    /// # Note
867    /// this function would make a request to arango server.
868    #[maybe_async]
869    pub async fn user_db_collection_access(
870        &self,
871        username: String,
872        db_name: String,
873        collection: String,
874    ) -> Result<Value, ClientError> {
875        let url = self
876            .base_url
877            .join(&format!(
878                "_api/user/{username}/database/{db_name}/{collection}"
879            ))
880            .unwrap();
881        let resp = self.session.get(url, "").await?;
882
883        let result = deserialize_response(resp.body())?;
884        Ok(result)
885    }
886
887    /// Set user's databases access level
888    ///
889    /// # Note
890    /// this function would make a request to arango server.
891    #[maybe_async]
892    pub async fn user_db_collection_access_put(
893        &self,
894        username: String,
895        db_name: String,
896        collection: String,
897        access_level: UserAccessLevel,
898    ) -> Result<Value, ClientError> {
899        let url = self
900            .base_url
901            .join(&format!(
902                "_api/user/{username}/database/{db_name}/{collection}"
903            ))
904            .unwrap();
905        let resp = self
906            .session
907            .put(
908                url,
909                format!(
910                    "{{ \"grant\":\"{}\" }}",
911                    access_level_enum_to_str(access_level)
912                ),
913            )
914            .await?;
915
916        let result = deserialize_response(resp.body())?;
917        Ok(result)
918    }
919}
920
921#[derive(Debug, Deserialize)]
922#[serde(rename_all = "camelCase")]
923pub struct DatabaseDetails {
924    pub name: String,
925    pub id: String,
926    pub path: String,
927    pub is_system: bool,
928}