1use 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 #[maybe_async]
64 pub async fn accessible_collections(&self) -> Result<Vec<Info>, ClientError> {
65 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 #[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 #[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(¶meters).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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}