1use crate::collections::error::SchemaError;
2use crate::collections::schema::{
3 Class, Classes, Property, Shard, ShardStatus, Shards, Tenant, Tenants,
4};
5use reqwest::Url;
6use std::error::Error;
7use std::sync::Arc;
8
9#[derive(Debug)]
12pub struct Schema {
13 endpoint: Url,
14 client: Arc<reqwest::Client>,
15}
16
17impl Schema {
18 pub(super) fn new(url: &Url, client: Arc<reqwest::Client>) -> Result<Self, Box<dyn Error>> {
21 let endpoint = url.join("/v1/schema/")?;
22 Ok(Schema { endpoint, client })
23 }
24
25 pub async fn get_class(&self, class_name: &str) -> Result<Class, Box<dyn Error>> {
40 let endpoint = self.endpoint.join(class_name)?;
41 let res = self.client.get(endpoint).send().await?;
42
43 match res.status() {
44 reqwest::StatusCode::OK => {
45 let res: Class = res.json().await?;
46 Ok(res)
47 },
48 _ => Err(self.get_err_msg("get class", res).await),
49 }
50 }
51
52 pub async fn get(&self) -> Result<Classes, Box<dyn Error>> {
67 let res = self.client.get(self.endpoint.clone()).send().await?;
68 match res.status() {
69 reqwest::StatusCode::OK => {
70 let res: Classes = res.json().await?;
71 Ok(res)
72 }
73 _ => Err(self.get_err_msg("get schema", res).await),
74 }
75 }
76
77 pub async fn create_class(&self, class: &Class) -> Result<Class, Box<dyn Error>> {
98 let payload = serde_json::to_value(&class).unwrap();
99 let res = self
100 .client
101 .post(self.endpoint.clone())
102 .json(&payload)
103 .send()
104 .await?;
105 match res.status() {
106 reqwest::StatusCode::OK => {
107 let res: Class = res.json().await?;
108 Ok(res)
109 }
110 _ => Err(self.get_err_msg("create class", res).await),
111 }
112 }
113
114 pub async fn delete(&self, class_name: &str) -> Result<bool, Box<dyn Error>> {
131 let endpoint = self.endpoint.join(class_name)?;
132 let res = self.client.delete(endpoint).send().await?;
133 match res.status() {
134 reqwest::StatusCode::OK => Ok(true),
135 _ => Err(self.get_err_msg("delete class", res).await),
136 }
137 }
138
139 pub async fn update(&self, class: &Class) -> Result<Class, Box<dyn Error>> {
153 let endpoint = self.endpoint.join(&class.class)?;
154 let payload = serde_json::to_value(&class)?;
155 let res = self.client.put(endpoint).json(&payload).send().await?;
156 match res.status() {
157 reqwest::StatusCode::OK => {
158 let res: Class = res.json().await?;
159 Ok(res)
160 }
161 _ => Err(self.get_err_msg("update class", res).await),
162 }
163 }
164
165 pub async fn add_property(
169 &self,
170 class_name: &str,
171 property: &Property,
172 ) -> Result<Property, Box<dyn Error>> {
173 let mut endpoint = class_name.to_string();
174 endpoint.push_str("/properties");
175 let endpoint = self.endpoint.join(&endpoint)?;
176 let payload = serde_json::to_value(&property)?;
177 let res = self.client.post(endpoint).json(&payload).send().await?;
178 match res.status() {
179 reqwest::StatusCode::OK => {
180 let res: Property = res.json().await?;
181 Ok(res)
182 }
183 _ => Err(self.get_err_msg("add property", res).await),
184 }
185 }
186
187 pub async fn get_shards(&self, class_name: &str) -> Result<Shards, Box<dyn Error>> {
191 let mut endpoint = class_name.to_string();
192 endpoint.push_str("/shards");
193 let endpoint = self.endpoint.join(&endpoint)?;
194 let res = self.client.get(endpoint).send().await?;
195 match res.status() {
196 reqwest::StatusCode::OK => {
197 let shards = res.json::<Vec<Shard>>().await?;
198 let shards = Shards { shards };
199 Ok(shards)
200 }
201 _ => Err(self.get_err_msg("get shards", res).await),
202 }
203 }
204
205 pub async fn update_class_shard(
209 &self,
210 class_name: &str,
211 shard_name: &str,
212 status: ShardStatus,
213 ) -> Result<Shard, Box<dyn Error>> {
214 let mut endpoint = class_name.to_string();
215 endpoint.push_str("/shards/");
216 endpoint.push_str(shard_name);
217 let endpoint = self.endpoint.join(&endpoint)?;
218 let payload = serde_json::json!({ "status": status });
219 let res = self.client.put(endpoint).json(&payload).send().await?;
220 match res.status() {
221 reqwest::StatusCode::OK => Ok(Shard {
222 name: shard_name.into(),
223 status,
224 }),
225 _ => Err(self.get_err_msg("update class shard", res).await),
226 }
227 }
228
229 pub async fn list_tenants(&self, class_name: &str) -> Result<Tenants, Box<dyn Error>> {
233 let mut endpoint = class_name.to_string();
234 endpoint.push_str("/tenants");
235 let endpoint = self.endpoint.join(&endpoint)?;
236 let res = self.client.get(endpoint).send().await?;
237 match res.status() {
238 reqwest::StatusCode::OK => {
239 let tenants = res.json::<Vec<Tenant>>().await?;
240 let tenants = Tenants { tenants };
241 Ok(tenants)
242 }
243 _ => Err(self.get_err_msg("list tenants", res).await),
244 }
245 }
246
247 pub async fn add_tenants(
251 &self,
252 class_name: &str,
253 tenants: &Tenants,
254 ) -> Result<Tenants, Box<dyn Error>> {
255 let mut endpoint = class_name.to_string();
256 endpoint.push_str("/tenants");
257 let endpoint = self.endpoint.join(&endpoint)?;
258 let payload = serde_json::to_value(&tenants.tenants)?;
259 let res = self.client.post(endpoint).json(&payload).send().await?;
260 match res.status() {
261 reqwest::StatusCode::OK => {
262 let tenants = res.json::<Vec<Tenant>>().await?;
263 let tenants = Tenants { tenants };
264 Ok(tenants)
265 }
266 _ => Err(self.get_err_msg("add tenants", res).await),
267 }
268 }
269
270 pub async fn remove_tenants(
274 &self,
275 class_name: &str,
276 tenants: &Vec<&str>,
277 ) -> Result<bool, Box<dyn Error>> {
278 let mut endpoint = class_name.to_string();
279 endpoint.push_str("/tenants");
280 let endpoint = self.endpoint.join(&endpoint)?;
281 let payload = serde_json::to_value(&tenants)?;
282 let res = self.client.delete(endpoint).json(&payload).send().await?;
283 match res.status() {
284 reqwest::StatusCode::OK => Ok(true),
285 _ => Err(self.get_err_msg("remove tenants", res).await),
286 }
287 }
288
289 pub async fn update_tenants(
297 &self,
298 class_name: &str,
299 tenants: &Tenants,
300 ) -> Result<Tenants, Box<dyn Error>> {
301 let mut endpoint = class_name.to_string();
302 endpoint.push_str("/tenants");
303 let endpoint = self.endpoint.join(&endpoint)?;
304 let payload = serde_json::to_value(&tenants.tenants)?;
305 let res = self.client.put(endpoint).json(&payload).send().await?;
306 match res.status() {
307 reqwest::StatusCode::OK => {
308 let tenants = res.json::<Vec<Tenant>>().await?;
309 let tenants = Tenants { tenants };
310 Ok(tenants)
311 }
312 _ => Err(self.get_err_msg("update tenants", res).await),
313 }
314 }
315
316 async fn get_err_msg(&self, endpoint: &str, res: reqwest::Response) -> Box<SchemaError> {
320 let status_code = res.status();
321 let msg: Result<serde_json::Value, reqwest::Error> = res.json().await;
322 let r_str: String;
323 if let Ok(json) = msg {
324 r_str = format!(
325 "Status code `{}` received when calling {} endpoint. Response: {}",
326 status_code,
327 endpoint,
328 json,
329 );
330 } else {
331 r_str = format!(
332 "Status code `{}` received when calling {} endpoint.",
333 status_code,
334 endpoint
335 );
336 }
337 Box::new(SchemaError(r_str))
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use crate::collections::schema::{
347 ActivityStatus, Class, ClassBuilder, Classes, Property, Shard, ShardStatus, Shards, Tenant,
348 Tenants,
349 };
350 use crate::WeaviateClient;
351
352 fn test_class(class_name: &str) -> Class {
354 ClassBuilder::new(class_name)
355 .with_description("Test")
356 .build()
357 }
358
359 fn test_classes() -> Classes {
360 let class_a = test_class("Test1");
361 let class_b = test_class("Test1");
362 Classes::new(vec![class_a, class_b])
363 }
364
365 fn test_shard() -> Shard {
366 Shard::new("abcd", ShardStatus::READY)
367 }
368
369 fn test_property(property_name: &str) -> Property {
371 Property::builder(property_name, vec!["boolean"])
372 .with_description("test property")
373 .build()
374 }
375
376 fn test_tenants() -> Tenants {
378 Tenants::new(vec![
379 Tenant::builder("TENANT_A").build(),
380 Tenant::builder("TENANT_B")
381 .with_activity_status(ActivityStatus::COLD)
382 .build(),
383 ])
384 }
385
386 fn test_shards() -> Shards {
387 Shards::new(vec![Shard::new("1D3PBjtz9W7r", ShardStatus::READY)])
388 }
389
390 fn get_test_harness() -> (mockito::ServerGuard, WeaviateClient) {
391 let mock_server = mockito::Server::new();
392 let mut host = "http://".to_string();
393 host.push_str(&mock_server.host_with_port());
394 let client = WeaviateClient::builder(&host).build().unwrap();
395 (mock_server, client)
396 }
397
398 fn mock_post(
399 server: &mut mockito::ServerGuard,
400 endpoint: &str,
401 status_code: usize,
402 body: &str,
403 ) -> mockito::Mock {
404 server
405 .mock("POST", endpoint)
406 .with_status(status_code)
407 .with_header("content-type", "application/json")
408 .with_body(body)
409 .create()
410 }
411
412 fn mock_put(
413 server: &mut mockito::ServerGuard,
414 endpoint: &str,
415 status_code: usize,
416 body: &str,
417 ) -> mockito::Mock {
418 server
419 .mock("PUT", endpoint)
420 .with_status(status_code)
421 .with_header("content-type", "application/json")
422 .with_body(body)
423 .create()
424 }
425
426 fn mock_get(
427 server: &mut mockito::ServerGuard,
428 endpoint: &str,
429 status_code: usize,
430 body: &str,
431 ) -> mockito::Mock {
432 server
433 .mock("GET", endpoint)
434 .with_status(status_code)
435 .with_header("content-type", "application/json")
436 .with_body(body)
437 .create()
438 }
439
440 fn mock_delete(
441 server: &mut mockito::ServerGuard,
442 endpoint: &str,
443 status_code: usize,
444 ) -> mockito::Mock {
445 server
446 .mock("DELETE", endpoint)
447 .with_status(status_code)
448 .create()
449 }
450
451 #[tokio::test]
452 async fn test_create_class_ok() {
453 let class = test_class("UnitClass");
454 let class_str = serde_json::to_string(&class).unwrap();
455 let (mut mock_server, client) = get_test_harness();
456 let mock = mock_post(&mut mock_server, "/v1/schema/", 200, &class_str);
457 let res = client.schema.create_class(&class).await;
458 mock.assert();
459 assert!(res.is_ok());
460 assert_eq!(class.class, res.unwrap().class);
461 }
462
463 #[tokio::test]
464 async fn test_create_class_err() {
465 let class = test_class("UnitClass");
466 let (mut mock_server, client) = get_test_harness();
467 let mock = mock_post(&mut mock_server, "/v1/schema/", 401, "");
468 let res = client.schema.create_class(&class).await;
469 mock.assert();
470 assert!(res.is_err());
471 }
472
473 #[tokio::test]
474 async fn test_get_all_classes_ok() {
475 let classes = test_classes();
476 let class_str = serde_json::to_string(&classes).unwrap();
477 let (mut mock_server, client) = get_test_harness();
478 let mock = mock_get(&mut mock_server, "/v1/schema/", 200, &class_str);
479 let res = client.schema.get().await;
480 mock.assert();
481 assert!(res.is_ok());
482 assert_eq!(classes.classes[0].class, res.unwrap().classes[0].class);
483 }
484
485 #[tokio::test]
486 async fn test_get_all_classes_err() {
487 let (mut mock_server, client) = get_test_harness();
488 let mock = mock_get(&mut mock_server, "/v1/schema/", 401, "");
489 let class = client.schema.get().await;
490 mock.assert();
491 assert!(class.is_err());
492 }
493
494 #[tokio::test]
495 async fn test_get_single_class_ok() {
496 let class = test_class("Test");
497 let class_str = serde_json::to_string(&class).unwrap();
498 let (mut mock_server, client) = get_test_harness();
499 let mock = mock_get(&mut mock_server, "/v1/schema/Test", 200, &class_str);
500 let res = client.schema.get_class("Test").await;
501 mock.assert();
502 assert!(res.is_ok());
503 assert_eq!(class.class, res.unwrap().class);
504 }
505
506 #[tokio::test]
507 async fn test_get_single_class_err() {
508 let (mut mock_server, client) = get_test_harness();
509 let mock = mock_get(&mut mock_server, "/v1/schema/Test", 401, "");
510 let class = client.schema.get_class("Test").await;
511 mock.assert();
512 assert!(class.is_err());
513 }
514
515 #[tokio::test]
516 async fn test_get_delete_class_ok() {
517 let (mut mock_server, client) = get_test_harness();
518 let mock = mock_delete(&mut mock_server, "/v1/schema/Test", 200);
519 let res = client.schema.delete("Test").await;
520 mock.assert();
521 assert!(res.is_ok());
522 assert!(res.unwrap());
523 }
524
525 #[tokio::test]
526 async fn test_get_delete_class_err() {
527 let (mut mock_server, client) = get_test_harness();
528 let mock = mock_delete(&mut mock_server, "/v1/schema/Test", 401);
529 let class = client.schema.delete("Test").await;
530 mock.assert();
531 assert!(class.is_err());
532 }
533
534 #[tokio::test]
535 async fn test_update_class_ok() {
536 let class = test_class("Test");
537 let class_str = serde_json::to_string(&class).unwrap();
538 let (mut mock_server, client) = get_test_harness();
539 let mock = mock_put(&mut mock_server, "/v1/schema/Test", 200, &class_str);
540 let res = client.schema.update(&class).await;
541 mock.assert();
542 assert!(res.is_ok());
543 assert_eq!(class.class, res.unwrap().class);
544 }
545
546 #[tokio::test]
547 async fn test_update_class_err() {
548 let class = test_class("Test");
549 let (mut mock_server, client) = get_test_harness();
550 let mock = mock_put(&mut mock_server, "/v1/schema/Test", 401, "");
551 let res = client.schema.update(&class).await;
552 mock.assert();
553 assert!(res.is_err());
554 }
555
556 #[tokio::test]
557 async fn test_add_property_ok() {
558 let property = test_property("Test");
559 let property_str = serde_json::to_string(&property).unwrap();
560 let (mut mock_server, client) = get_test_harness();
561 let mock = mock_post(
562 &mut mock_server,
563 "/v1/schema/TestClass/properties",
564 200,
565 &property_str,
566 );
567 let res = client.schema.add_property("TestClass", &property).await;
568 mock.assert();
569 assert!(res.is_ok());
570 assert_eq!(property.name, res.unwrap().name);
571 }
572
573 #[tokio::test]
574 async fn test_add_property_err() {
575 let property = test_property("Test");
576 let (mut mock_server, client) = get_test_harness();
577 let mock = mock_post(&mut mock_server, "/v1/schema/TestClass/properties", 401, "");
578 let res = client.schema.add_property("TestClass", &property).await;
579 mock.assert();
580 assert!(res.is_err());
581 }
582
583 #[tokio::test]
584 async fn test_get_shards_ok() {
585 let shards = test_shards();
586 let shards_str = serde_json::to_string(&shards.shards).unwrap();
587 let (mut mock_server, client) = get_test_harness();
588 let mock = mock_get(&mut mock_server, "/v1/schema/Test/shards", 200, &shards_str);
589 let res = client.schema.get_shards("Test").await;
590 mock.assert();
591 assert!(res.is_ok());
592 assert_eq!(shards.shards[0].name, res.unwrap().shards[0].name);
593 }
594
595 #[tokio::test]
596 async fn test_get_shards_err() {
597 let (mut mock_server, client) = get_test_harness();
598 let mock = mock_get(&mut mock_server, "/v1/schema/Test/shards", 401, "");
599 let res = client.schema.get_shards("Test").await;
600 mock.assert();
601 assert!(res.is_err());
602 }
603
604 #[tokio::test]
605 async fn test_update_class_shard_ok() {
606 let shard = test_shard();
607 let shard_str = serde_json::to_string(&shard).unwrap();
608 let (mut mock_server, client) = get_test_harness();
609 let mock = mock_put(
610 &mut mock_server,
611 "/v1/schema/Test/shards/abcd",
612 200,
613 &shard_str,
614 );
615 let res = client
616 .schema
617 .update_class_shard("Test", "abcd", ShardStatus::READONLY)
618 .await;
619 mock.assert();
620 assert!(res.is_ok());
621 assert_eq!(shard.name, res.unwrap().name);
622 }
623
624 #[tokio::test]
625 async fn test_update_class_shard_err() {
626 let (mut mock_server, client) = get_test_harness();
627 let mock = mock_put(&mut mock_server, "/v1/schema/Test/shards/abcd", 401, "");
628 let res = client
629 .schema
630 .update_class_shard("Test", "abcd", ShardStatus::READONLY)
631 .await;
632 mock.assert();
633 assert!(res.is_err());
634 }
635
636 #[tokio::test]
637 async fn test_list_tenants_ok() {
638 let tenants = test_tenants();
639 let tenants_str = serde_json::to_string(&tenants.tenants).unwrap();
640 let (mut mock_server, client) = get_test_harness();
641 let mock = mock_get(
642 &mut mock_server,
643 "/v1/schema/Test/tenants",
644 200,
645 &tenants_str,
646 );
647 let res = client.schema.list_tenants("Test").await;
648 mock.assert();
649 assert!(res.is_ok());
650 assert_eq!(tenants.tenants[0].name, res.unwrap().tenants[0].name);
651 }
652
653 #[tokio::test]
654 async fn test_list_tenants_err() {
655 let (mut mock_server, client) = get_test_harness();
656 let mock = mock_get(&mut mock_server, "/v1/schema/Test/tenants", 422, "");
657 let res = client.schema.list_tenants("Test").await;
658 mock.assert();
659 assert!(res.is_err());
660 }
661
662 #[tokio::test]
663 async fn test_add_tenants_ok() {
664 let tenants = test_tenants();
665 let tenants_str = serde_json::to_string(&tenants.tenants).unwrap();
666 let (mut mock_server, client) = get_test_harness();
667 let mock = mock_post(
668 &mut mock_server,
669 "/v1/schema/Test/tenants",
670 200,
671 &tenants_str,
672 );
673 let res = client.schema.add_tenants("Test", &tenants).await;
674 mock.assert();
675 assert!(res.is_ok());
676 assert_eq!(tenants.tenants[0].name, res.unwrap().tenants[0].name);
677 }
678
679 #[tokio::test]
680 async fn test_add_tenants_err() {
681 let tenants = test_tenants();
682 let (mut mock_server, client) = get_test_harness();
683 let mock = mock_post(&mut mock_server, "/v1/schema/Test/tenants", 422, "");
684 let res = client.schema.add_tenants("Test", &tenants).await;
685 mock.assert();
686 assert!(res.is_err());
687 }
688
689 #[tokio::test]
690 async fn test_remove_tenants_ok() {
691 let (mut mock_server, client) = get_test_harness();
692 let mock = mock_delete(&mut mock_server, "/v1/schema/Test/tenants", 200);
693 let res = client
694 .schema
695 .remove_tenants("Test", &vec!["TestTenant"])
696 .await;
697 mock.assert();
698 assert!(res.is_ok());
699 assert!(res.unwrap());
700 }
701
702 #[tokio::test]
703 async fn test_remove_tenants_err() {
704 let (mut mock_server, client) = get_test_harness();
705 let mock = mock_delete(&mut mock_server, "/v1/schema/Test/tenants", 422);
706 let res = client
707 .schema
708 .remove_tenants("Test", &vec!["TestTenant"])
709 .await;
710 mock.assert();
711 assert!(res.is_err());
712 }
713
714 #[tokio::test]
715 async fn test_update_tenants_ok() {
716 let tenants = test_tenants();
717 let tenants_str = serde_json::to_string(&tenants.tenants).unwrap();
718 let (mut mock_server, client) = get_test_harness();
719 let mock = mock_put(
720 &mut mock_server,
721 "/v1/schema/Test/tenants",
722 200,
723 &tenants_str,
724 );
725 let res = client.schema.update_tenants("Test", &tenants).await;
726 mock.assert();
727 assert!(res.is_ok());
728 assert_eq!(tenants.tenants[0].name, res.unwrap().tenants[0].name);
729 }
730
731 #[tokio::test]
732 async fn test_update_tenants_err() {
733 let tenants = test_tenants();
734 let (mut mock_server, client) = get_test_harness();
735 let mock = mock_put(&mut mock_server, "/v1/schema/Test/tenants", 422, "");
736 let res = client.schema.update_tenants("Test", &tenants).await;
737 mock.assert();
738 assert!(res.is_err());
739 }
740}