netsblox_api/
lib.rs

1pub mod common;
2pub mod error;
3
4use crate::common::*;
5use futures_util::SinkExt;
6use netsblox_api_common::{
7    CreateGroupData, CreateMagicLinkData, ServiceHostScope, UpdateGroupData, UpdateUserData,
8};
9use reqwest::{self, Method, RequestBuilder, Response};
10use serde::{Deserialize, Serialize};
11pub use serde_json;
12use serde_json::{json, Value};
13use tokio::net::TcpStream;
14use tokio_tungstenite::tungstenite::Message;
15use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
16
17#[derive(Serialize, Deserialize, Debug, Clone)]
18pub struct Config {
19    pub app_id: Option<AppId>,
20    pub url: String,
21    pub token: Option<String>,
22    pub username: Option<String>,
23}
24
25impl Default for Config {
26    fn default() -> Self {
27        Self {
28            app_id: None,
29            username: None,
30            token: None,
31            url: "https://cloud.netsblox.org".to_owned(),
32        }
33    }
34}
35
36async fn check_response(response: Response) -> Result<Response, error::Error> {
37    let status_code = response.status().as_u16();
38    let is_error = status_code > 399;
39    if is_error {
40        let msg = response.text().await.map_err(error::Error::RequestError)?;
41
42        match status_code {
43            400 => Err(error::Error::BadRequestError(msg)),
44            401 => Err(error::Error::LoginRequiredError),
45            403 => Err(error::Error::PermissionsError(msg)),
46            404 => Err(error::Error::NotFoundError(msg)),
47            500 => Err(error::Error::InternalServerError),
48            _ => panic!("Unknown status code: {:?}", status_code), // FIXME: Use error instead?
49        }
50    } else {
51        Ok(response)
52    }
53}
54
55pub type Token = String;
56pub async fn login(mut cfg: Config, credentials: &LoginRequest) -> Result<Config, error::Error> {
57    let client = reqwest::Client::new();
58    let response = client
59        .post(format!("{}/users/login", cfg.url))
60        .json(&credentials)
61        .send()
62        .await
63        .map_err(error::Error::RequestError)?;
64
65    let response = check_response(response).await?;
66    let cookie = response
67        .cookies()
68        .find(|cookie| cookie.name() == "netsblox")
69        .ok_or("No cookie received.")
70        .unwrap();
71
72    let token = cookie.value().to_owned();
73
74    let user = response.json::<User>().await.unwrap();
75    cfg.username = Some(user.username);
76    cfg.token = Some(token);
77    Ok(cfg)
78}
79
80#[derive(Serialize)]
81struct UserData<'a> {
82    username: &'a str,
83    email: &'a str,
84    role: &'a UserRole,
85    group_id: Option<&'a GroupId>,
86    password: Option<&'a str>,
87}
88
89#[derive(Clone)]
90pub struct Client {
91    cfg: Config,
92}
93
94impl Client {
95    pub fn new(cfg: Config) -> Self {
96        Client { cfg }
97    }
98
99    fn request(&self, method: Method, path: &str) -> RequestBuilder {
100        let client = reqwest::Client::new();
101        let empty = "".to_owned();
102        let token = self.cfg.token.as_ref().unwrap_or(&empty);
103        client
104            .request(method, format!("{}{}", self.cfg.url, path))
105            .header("Cookie", format!("netsblox={}", token))
106    }
107
108    // User management
109    pub async fn create_user(
110        &self,
111        name: &str,
112        email: &str,
113        password: Option<&str>, // TODO: Make these CreateUserOptions
114        group_id: Option<&GroupId>,
115        role: UserRole,
116    ) -> Result<(), error::Error> {
117        let user_data = NewUser {
118            username: name.to_owned(),
119            email: email.to_owned(),
120            role: Some(role),
121            group_id: group_id.map(|id| id.to_owned()),
122            password: password.map(|pwd| pwd.to_owned()),
123        };
124
125        let response = self
126            .request(Method::POST, "/users/create")
127            .json(&user_data)
128            .send()
129            .await
130            .map_err(error::Error::RequestError)?;
131
132        println!(
133            "status {} {}",
134            response.status(),
135            response.text().await.unwrap()
136        );
137        Ok(())
138    }
139
140    pub async fn list_users(&self) -> Result<Vec<User>, error::Error> {
141        let response = self
142            .request(Method::GET, "/users/")
143            .send()
144            .await
145            .map_err(error::Error::RequestError)?;
146
147        let response = check_response(response).await?;
148        Ok(response.json::<Vec<User>>().await.unwrap())
149    }
150
151    /// Send an email containing all usernames associated with the given
152    /// address to the email address.
153    pub async fn forgot_username(&self, email: &str) -> Result<(), error::Error> {
154        let response = self
155            .request(Method::POST, "/users/forgot-username")
156            .json(&email)
157            .send()
158            .await
159            .map_err(error::Error::RequestError)?;
160
161        check_response(response).await?;
162        Ok(())
163    }
164
165    pub async fn delete_user(&self, username: &str) -> Result<(), error::Error> {
166        let response = self
167            .request(Method::POST, &format!("/users/{}/delete", username))
168            .send()
169            .await
170            .map_err(error::Error::RequestError)?;
171
172        check_response(response).await?;
173        Ok(())
174    }
175
176    pub async fn view_user(&self, username: &str) -> Result<User, error::Error> {
177        let response = self
178            .request(Method::GET, &format!("/users/{}", username))
179            .send()
180            .await
181            .map_err(error::Error::RequestError)?;
182
183        let response = check_response(response).await?;
184        Ok(response.json::<User>().await.unwrap())
185    }
186
187    pub async fn update_user(
188        &self,
189        username: &str,
190        update: &UpdateUserData,
191    ) -> Result<User, error::Error> {
192        let path = format!("/users/{}", username);
193        let response = self
194            .request(Method::PATCH, &path)
195            .json(&update)
196            .send()
197            .await
198            .map_err(error::Error::RequestError)?;
199
200        let response = check_response(response).await?;
201        Ok(response.json::<User>().await.unwrap())
202    }
203
204    pub async fn set_password(&self, username: &str, password: &str) -> Result<(), error::Error> {
205        let path = format!("/users/{}/password", username);
206        let response = self
207            .request(Method::PATCH, &path)
208            .json(&password)
209            .send()
210            .await
211            .map_err(error::Error::RequestError)?;
212
213        check_response(response).await?;
214        Ok(())
215    }
216
217    pub async fn link_account(
218        &self,
219        username: &str,
220        credentials: &Credentials,
221    ) -> Result<(), error::Error> {
222        let response = self
223            .request(Method::POST, &format!("/users/{}/link/", username))
224            .json(&credentials)
225            .send()
226            .await
227            .map_err(error::Error::RequestError)?;
228
229        check_response(response).await?;
230        Ok(())
231    }
232
233    pub async fn unlink_account(
234        &self,
235        username: &str,
236        account: &LinkedAccount,
237    ) -> Result<(), error::Error> {
238        let response = self
239            .request(Method::POST, &format!("/users/{}/unlink", username))
240            .json(&account)
241            .send()
242            .await
243            .map_err(error::Error::RequestError)?;
244
245        check_response(response).await?;
246        Ok(())
247    }
248
249    pub async fn ban_user(&self, username: &str) -> Result<BannedAccount, error::Error> {
250        let response = self
251            .request(Method::POST, &format!("/users/{}/ban", username))
252            .send()
253            .await
254            .map_err(error::Error::RequestError)?;
255
256        let response = check_response(response).await?;
257        Ok(response.json::<BannedAccount>().await.unwrap())
258    }
259
260    pub async fn unban_user(&self, username: &str) -> Result<BannedAccount, error::Error> {
261        let response = self
262            .request(Method::POST, &format!("/users/{}/unban", username))
263            .send()
264            .await
265            .map_err(error::Error::RequestError)?;
266
267        let response = check_response(response).await?;
268        Ok(response.json::<BannedAccount>().await.unwrap())
269    }
270
271    /// Send a magic link to the given email address. Usable for any user associated with the
272    /// address.
273    pub async fn send_magic_link(&self, data: &CreateMagicLinkData) -> Result<(), error::Error> {
274        let response = self
275            .request(Method::POST, "/magic-links/")
276            .json(data)
277            .send()
278            .await
279            .map_err(error::Error::RequestError)?;
280
281        check_response(response).await?;
282        Ok(())
283    }
284
285    // Project management
286    pub async fn create_project(
287        &self,
288        data: &CreateProjectData,
289    ) -> Result<ProjectMetadata, error::Error> {
290        // TODO: what should the method signature look like for this? Probably should accept CreateProjectData
291        let response = self
292            .request(Method::POST, "/projects/")
293            .json(data)
294            .send()
295            .await
296            .map_err(error::Error::RequestError)?;
297
298        let response = check_response(response).await?;
299        Ok(response.json::<ProjectMetadata>().await.unwrap())
300    }
301
302    pub async fn list_projects(&self, owner: &str) -> Result<Vec<ProjectMetadata>, error::Error> {
303        let response = self
304            .request(Method::GET, &format!("/projects/user/{}", &owner))
305            .send()
306            .await
307            .map_err(error::Error::RequestError)?;
308
309        let response = check_response(response).await?;
310
311        Ok(response.json::<Vec<ProjectMetadata>>().await.unwrap())
312    }
313
314    pub async fn list_shared_projects(
315        &self,
316        owner: &str,
317    ) -> Result<Vec<ProjectMetadata>, error::Error> {
318        let response = self
319            .request(Method::GET, &format!("/projects/shared/{}", &owner))
320            .send()
321            .await
322            .map_err(error::Error::RequestError)?;
323
324        let response = check_response(response).await?;
325
326        Ok(response.json::<Vec<ProjectMetadata>>().await.unwrap())
327    }
328
329    pub async fn get_project_metadata(
330        &self,
331        owner: &str,
332        name: &str,
333    ) -> Result<ProjectMetadata, error::Error> {
334        let response = self
335            .request(
336                Method::GET,
337                &format!("/projects/user/{}/{}/metadata", &owner, name),
338            )
339            .send()
340            .await
341            .map_err(error::Error::RequestError)?;
342
343        let response = check_response(response).await?;
344
345        Ok(response.json::<ProjectMetadata>().await.unwrap())
346    }
347
348    pub async fn rename_project(&self, id: &ProjectId, name: &str) -> Result<(), error::Error> {
349        let response = self
350            .request(Method::PATCH, &format!("/projects/id/{}", &id))
351            .json(&UpdateProjectData {
352                name: name.to_owned(),
353                client_id: None,
354            })
355            .send()
356            .await
357            .map_err(error::Error::RequestError)?;
358
359        check_response(response).await?;
360
361        Ok(())
362    }
363
364    pub async fn rename_role(
365        &self,
366        id: &ProjectId,
367        role_id: &RoleId,
368        name: &str,
369    ) -> Result<(), error::Error> {
370        let response = self
371            .request(Method::PATCH, &format!("/projects/id/{}/{}", &id, &role_id))
372            .json(&UpdateRoleData {
373                name: name.to_owned(),
374                client_id: None,
375            })
376            .send()
377            .await
378            .map_err(error::Error::RequestError)?;
379
380        check_response(response).await?;
381
382        Ok(())
383    }
384
385    pub async fn delete_project(&self, id: &ProjectId) -> Result<(), error::Error> {
386        let response = self
387            .request(Method::DELETE, &format!("/projects/id/{}", id))
388            .send()
389            .await
390            .map_err(error::Error::RequestError)?;
391
392        check_response(response).await?;
393
394        Ok(())
395    }
396
397    pub async fn delete_role(&self, id: &ProjectId, role_id: &RoleId) -> Result<(), error::Error> {
398        let response = self
399            .request(Method::DELETE, &format!("/projects/id/{}/{}", id, role_id))
400            .send()
401            .await
402            .map_err(error::Error::RequestError)?;
403
404        check_response(response).await?;
405
406        Ok(())
407    }
408
409    pub async fn publish_project(&self, id: &ProjectId) -> Result<PublishState, error::Error> {
410        let response = self
411            .request(Method::POST, &format!("/projects/id/{}/publish", id))
412            .send()
413            .await
414            .map_err(error::Error::RequestError)?;
415
416        let response = check_response(response).await?;
417
418        Ok(response.json::<PublishState>().await.unwrap())
419    }
420
421    pub async fn unpublish_project(&self, id: &ProjectId) -> Result<(), error::Error> {
422        let response = self
423            .request(Method::POST, &format!("/projects/id/{}/unpublish", id))
424            .send()
425            .await
426            .map_err(error::Error::RequestError)?;
427
428        check_response(response).await?;
429
430        Ok(())
431    }
432
433    pub async fn get_project(
434        &self,
435        id: &ProjectId,
436        latest: &bool,
437    ) -> Result<Project, error::Error> {
438        let path = if *latest {
439            format!("/projects/id/{}/latest", id)
440        } else {
441            format!("/projects/id/{}", id)
442        };
443        let response = self
444            .request(Method::GET, &path)
445            .send()
446            .await
447            .map_err(error::Error::RequestError)?;
448
449        let response = check_response(response).await?;
450
451        Ok(response.json::<Project>().await.unwrap())
452    }
453
454    pub async fn get_role(
455        &self,
456        id: &ProjectId,
457        role_id: &RoleId,
458        latest: &bool,
459    ) -> Result<RoleData, error::Error> {
460        let path = if *latest {
461            format!("/projects/id/{}/{}/latest", id, role_id)
462        } else {
463            format!("/projects/id/{}/{}", id, role_id)
464        };
465        let response = self
466            .request(Method::GET, &path)
467            .send()
468            .await
469            .map_err(error::Error::RequestError)?;
470
471        let response = check_response(response).await?;
472
473        Ok(response.json::<RoleData>().await.unwrap())
474    }
475
476    // Project collaborators
477    pub async fn list_collaborators(&self, project_id: &str) -> Result<Vec<String>, error::Error> {
478        let response = self
479            .request(Method::GET, &format!("/id/{}/collaborators/", project_id))
480            .send()
481            .await
482            .map_err(error::Error::RequestError)?;
483
484        let response = check_response(response).await?;
485
486        Ok(response.json::<Vec<String>>().await.unwrap())
487    }
488
489    pub async fn remove_collaborator(
490        &self,
491        project_id: &ProjectId,
492        username: &str,
493    ) -> Result<(), error::Error> {
494        let response = self
495            .request(
496                Method::DELETE,
497                &format!("/projects/id/{}/collaborators/{}", project_id, username),
498            )
499            .send()
500            .await
501            .map_err(error::Error::RequestError)?;
502
503        check_response(response).await?;
504
505        Ok(())
506    }
507
508    pub async fn list_collaboration_invites(
509        &self,
510        username: &str,
511    ) -> Result<Vec<CollaborationInvite>, error::Error> {
512        let response = self
513            .request(
514                Method::GET,
515                &format!("/collaboration-invites/user/{}/", username),
516            )
517            .send()
518            .await
519            .map_err(error::Error::RequestError)?;
520
521        let response = check_response(response).await?;
522
523        Ok(response.json::<Vec<CollaborationInvite>>().await.unwrap())
524    }
525
526    pub async fn invite_collaborator(
527        &self,
528        id: &ProjectId,
529        username: &str,
530    ) -> Result<(), error::Error> {
531        let response = self
532            .request(
533                Method::POST,
534                &format!("/collaboration-invites/{}/invite/{}", id, username),
535            )
536            .send()
537            .await
538            .map_err(error::Error::RequestError)?;
539
540        check_response(response).await?;
541        Ok(())
542    }
543
544    pub async fn respond_to_collaboration_invite(
545        &self,
546        id: &InvitationId,
547        state: &InvitationState,
548    ) -> Result<(), error::Error> {
549        let response = self
550            .request(Method::POST, &format!("/collaboration-invites/id/{}", id))
551            .json(state)
552            .send()
553            .await
554            .map_err(error::Error::RequestError)?;
555
556        check_response(response).await?;
557        Ok(())
558    }
559
560    // Friend capabilities
561    pub async fn list_friends(&self, username: &str) -> Result<Vec<String>, error::Error> {
562        let path = &format!("/friends/{}/", username);
563        let response = self
564            .request(Method::GET, path)
565            .send()
566            .await
567            .map_err(error::Error::RequestError)?;
568
569        let response = check_response(response).await?;
570        Ok(response.json::<Vec<String>>().await.unwrap())
571    }
572
573    pub async fn list_online_friends(&self, username: &str) -> Result<Vec<String>, error::Error> {
574        let path = &format!("/friends/{}/online", username);
575        let response = self
576            .request(Method::GET, path)
577            .send()
578            .await
579            .map_err(error::Error::RequestError)?;
580
581        let response = check_response(response).await?;
582        Ok(response.json::<Vec<String>>().await.unwrap())
583    }
584
585    pub async fn list_friend_invites(
586        &self,
587        username: &str,
588    ) -> Result<Vec<FriendInvite>, error::Error> {
589        let path = &format!("/friends/{}/invites/", username);
590        let response = self
591            .request(Method::GET, path)
592            .send()
593            .await
594            .map_err(error::Error::RequestError)?;
595
596        let response = check_response(response).await?;
597        Ok(response.json::<Vec<FriendInvite>>().await.unwrap())
598    }
599
600    pub async fn send_friend_invite(
601        &self,
602        username: &str,
603        recipient: &str,
604    ) -> Result<(), error::Error> {
605        let path = &format!("/friends/{}/invite/", username);
606        let response = self
607            .request(Method::POST, path)
608            .json(recipient)
609            .send()
610            .await
611            .map_err(error::Error::RequestError)?;
612
613        check_response(response).await?;
614        Ok(())
615    }
616
617    pub async fn respond_to_friend_invite(
618        &self,
619        recipient: &str,
620        sender: &str,
621        state: FriendLinkState,
622    ) -> Result<(), error::Error> {
623        let path = format!("/friends/{}/invites/{}", recipient, sender);
624        let response = self
625            .request(Method::POST, &path)
626            .json(&state)
627            .send()
628            .await
629            .map_err(error::Error::RequestError)?;
630
631        check_response(response).await?;
632        Ok(())
633    }
634
635    pub async fn unfriend(&self, username: &str, friend: &str) -> Result<(), error::Error> {
636        let path = format!("/friends/{}/unfriend/{}", username, friend);
637        let response = self
638            .request(Method::POST, &path)
639            .send()
640            .await
641            .map_err(error::Error::RequestError)?;
642
643        check_response(response).await?;
644        Ok(())
645    }
646
647    pub async fn block_user(&self, username: &str, other_user: &str) -> Result<(), error::Error> {
648        let path = format!("/friends/{}/block/{}", username, other_user);
649        let response = self
650            .request(Method::POST, &path)
651            .send()
652            .await
653            .map_err(error::Error::RequestError)?;
654
655        check_response(response).await?;
656        Ok(())
657    }
658
659    pub async fn unblock_user(&self, username: &str, other_user: &str) -> Result<(), error::Error> {
660        let path = format!("/friends/{}/unblock/{}", username, other_user);
661        let response = self
662            .request(Method::POST, &path)
663            .send()
664            .await
665            .map_err(error::Error::RequestError)?;
666
667        check_response(response).await?;
668        Ok(())
669    }
670
671    // Library capabilities
672    pub async fn get_libraries(
673        &self,
674        username: &str,
675    ) -> Result<Vec<LibraryMetadata>, error::Error> {
676        let path = format!("/libraries/user/{}/", username);
677        let response = self
678            .request(Method::GET, &path)
679            .send()
680            .await
681            .map_err(error::Error::RequestError)?;
682
683        let response = check_response(response).await?;
684        Ok(response.json::<Vec<LibraryMetadata>>().await.unwrap())
685    }
686
687    pub async fn get_submitted_libraries(&self) -> Result<Vec<LibraryMetadata>, error::Error> {
688        let response = self
689            .request(Method::GET, "/libraries/mod/pending")
690            .send()
691            .await
692            .map_err(error::Error::RequestError)?;
693
694        let response = check_response(response).await?;
695
696        Ok(response.json::<Vec<LibraryMetadata>>().await.unwrap())
697    }
698
699    pub async fn get_public_libraries(&self) -> Result<Vec<LibraryMetadata>, error::Error> {
700        let response = self
701            .request(Method::GET, "/libraries/community/")
702            .send()
703            .await
704            .map_err(error::Error::RequestError)?;
705
706        let response = check_response(response).await?;
707
708        Ok(response.json::<Vec<LibraryMetadata>>().await.unwrap())
709    }
710
711    pub async fn get_library(&self, username: &str, name: &str) -> Result<String, error::Error> {
712        let path = format!("/libraries/user/{}/{}", username, name); // TODO: URI escape?
713        let response = self
714            .request(Method::GET, &path)
715            .send()
716            .await
717            .map_err(error::Error::RequestError)?;
718
719        let response = check_response(response).await?;
720
721        Ok(response.text().await.unwrap())
722    }
723
724    pub async fn save_library(
725        &self,
726        username: &str,
727        name: &str,
728        blocks: &str,
729        notes: &str,
730    ) -> Result<(), error::Error> {
731        let path = format!("/libraries/user/{}/", username);
732        let response = self
733            .request(Method::POST, &path)
734            .json(&CreateLibraryData {
735                name: name.to_owned(),
736                blocks: blocks.to_owned(),
737                notes: notes.to_owned(),
738            })
739            .send()
740            .await
741            .map_err(error::Error::RequestError)?;
742
743        check_response(response).await?;
744        Ok(())
745    }
746
747    pub async fn delete_library(&self, username: &str, library: &str) -> Result<(), error::Error> {
748        let path = format!("/libraries/user/{}/{}", username, library);
749        let response = self
750            .request(Method::DELETE, &path)
751            .send()
752            .await
753            .map_err(error::Error::RequestError)?;
754
755        check_response(response).await?;
756        Ok(())
757    }
758
759    pub async fn publish_library(&self, username: &str, library: &str) -> Result<(), error::Error> {
760        let path = format!("/libraries/user/{}/{}/publish", username, library);
761        let response = self
762            .request(Method::POST, &path)
763            .send()
764            .await
765            .map_err(error::Error::RequestError)?;
766
767        check_response(response).await?;
768        Ok(())
769    }
770
771    pub async fn unpublish_library(
772        &self,
773        username: &str,
774        library: &str,
775    ) -> Result<(), error::Error> {
776        let path = format!("/libraries/user/{}/{}/unpublish", username, library);
777        let response = self
778            .request(Method::POST, &path)
779            .send()
780            .await
781            .map_err(error::Error::RequestError)?;
782
783        check_response(response).await?;
784        Ok(())
785    }
786
787    pub async fn approve_library(
788        &self,
789        username: &str,
790        library: &str,
791        state: &PublishState,
792    ) -> Result<(), error::Error> {
793        let path = format!("/libraries/mod/{}/{}", username, library);
794        let response = self
795            .request(Method::POST, &path)
796            .json(&state)
797            .send()
798            .await
799            .map_err(error::Error::RequestError)?;
800
801        check_response(response).await?;
802        Ok(())
803    }
804
805    // Group management
806    pub async fn list_groups(&self, username: &str) -> Result<Vec<Group>, error::Error> {
807        let path = format!("/groups/user/{}/", username);
808        let response = self
809            .request(Method::GET, &path)
810            .send()
811            .await
812            .map_err(error::Error::RequestError)?;
813
814        let response = check_response(response).await?;
815
816        Ok(response.json::<Vec<Group>>().await.unwrap())
817    }
818
819    pub async fn create_group(&self, owner: &str, name: &str) -> Result<(), error::Error> {
820        let path = format!("/groups/user/{}/", owner);
821        let group = CreateGroupData {
822            name: name.to_owned(),
823            services_hosts: None,
824        };
825        let response = self
826            .request(Method::POST, &path)
827            .json(&group)
828            .send()
829            .await
830            .map_err(error::Error::RequestError)?;
831
832        check_response(response).await?;
833        Ok(())
834    }
835
836    pub async fn delete_group(&self, id: &GroupId) -> Result<(), error::Error> {
837        let path = format!("/groups/id/{}", id);
838        let response = self
839            .request(Method::DELETE, &path)
840            .send()
841            .await
842            .map_err(error::Error::RequestError)?;
843
844        check_response(response).await?;
845        Ok(())
846    }
847
848    pub async fn list_members(&self, id: &GroupId) -> Result<Vec<User>, error::Error> {
849        let path = format!("/groups/id/{}/members", id);
850        let response = self
851            .request(Method::GET, &path)
852            .send()
853            .await
854            .map_err(error::Error::RequestError)?;
855
856        let response = check_response(response).await?;
857        Ok(response.json::<Vec<User>>().await.unwrap())
858    }
859
860    pub async fn rename_group(&self, id: &GroupId, name: &str) -> Result<(), error::Error> {
861        let path = format!("/groups/id/{}", id);
862        let response = self
863            .request(Method::PATCH, &path)
864            .json(&UpdateGroupData {
865                name: name.to_owned(),
866            })
867            .send()
868            .await
869            .map_err(error::Error::RequestError)?;
870
871        check_response(response).await?;
872        Ok(())
873    }
874
875    pub async fn view_group(&self, id: &GroupId) -> Result<Group, error::Error> {
876        let path = format!("/groups/id/{}", id);
877        let response = self
878            .request(Method::GET, &path)
879            .send()
880            .await
881            .map_err(error::Error::RequestError)?;
882
883        let response = check_response(response).await?;
884
885        Ok(response.json::<Group>().await.unwrap())
886    }
887
888    // Service host management
889    pub async fn list_user_hosts(&self, username: &str) -> Result<Vec<ServiceHost>, error::Error> {
890        let response = self
891            .request(Method::GET, &format!("/services/hosts/user/{}", username))
892            .send()
893            .await
894            .map_err(error::Error::RequestError)?;
895
896        let response = check_response(response).await?;
897
898        Ok(response.json::<Vec<ServiceHost>>().await.unwrap())
899    }
900
901    pub async fn list_group_hosts(
902        &self,
903        group_id: &GroupId,
904    ) -> Result<Vec<ServiceHost>, error::Error> {
905        let response = self
906            .request(Method::GET, &format!("/services/hosts/group/{}", group_id))
907            .send()
908            .await
909            .map_err(error::Error::RequestError)?;
910
911        let response = check_response(response).await?;
912
913        Ok(response.json::<Vec<ServiceHost>>().await.unwrap())
914    }
915
916    pub async fn list_hosts(&self, username: &str) -> Result<Vec<ServiceHost>, error::Error> {
917        let response = self
918            .request(Method::GET, &format!("/services/hosts/all/{}", username))
919            .send()
920            .await
921            .map_err(error::Error::RequestError)?;
922
923        let response = check_response(response).await?;
924
925        Ok(response.json::<Vec<ServiceHost>>().await.unwrap())
926    }
927
928    pub async fn set_user_hosts(
929        &self,
930        username: &str,
931        hosts: Vec<ServiceHost>,
932    ) -> Result<(), error::Error> {
933        let response = self
934            .request(Method::POST, &format!("/services/hosts/user/{}", username))
935            .json(&hosts)
936            .send()
937            .await
938            .map_err(error::Error::RequestError)?;
939
940        check_response(response).await?;
941        Ok(())
942    }
943
944    pub async fn set_group_hosts(
945        &self,
946        group_id: &GroupId,
947        hosts: Vec<ServiceHost>,
948    ) -> Result<(), error::Error> {
949        let response = self
950            .request(Method::POST, &format!("/services/hosts/group/{}", group_id))
951            .json(&hosts)
952            .send()
953            .await
954            .map_err(error::Error::RequestError)?;
955
956        check_response(response).await?;
957        Ok(())
958    }
959
960    pub async fn authorize_host(
961        &self,
962        url: &str,
963        id: &str,
964        visibility: ServiceHostScope,
965    ) -> Result<String, error::Error> {
966        let host = AuthorizedServiceHost {
967            url: url.to_owned(),
968            id: id.to_owned(),
969            visibility,
970        };
971        let response = self
972            .request(Method::POST, "/services/hosts/authorized/")
973            .json(&host)
974            .send()
975            .await
976            .map_err(error::Error::RequestError)?;
977
978        let response = check_response(response).await?;
979        Ok(response.json::<String>().await.unwrap())
980    }
981
982    pub async fn unauthorize_host(&self, id: &str) -> Result<(), error::Error> {
983        let response = self
984            .request(
985                Method::DELETE,
986                &format!("/services/hosts/authorized/{}", id),
987            )
988            .send()
989            .await
990            .map_err(error::Error::RequestError)?;
991
992        check_response(response).await?;
993        Ok(())
994    }
995
996    pub async fn list_authorized_hosts(&self) -> Result<Vec<AuthorizedServiceHost>, error::Error> {
997        let response = self
998            .request(Method::GET, "/services/hosts/authorized/")
999            .send()
1000            .await
1001            .map_err(error::Error::RequestError)?;
1002
1003        let response = check_response(response).await?;
1004        Ok(response.json::<Vec<AuthorizedServiceHost>>().await.unwrap())
1005    }
1006
1007    // Service settings management
1008    pub async fn list_group_settings(
1009        &self,
1010        group_id: &GroupId,
1011    ) -> Result<Vec<String>, error::Error> {
1012        let response = self
1013            .request(
1014                Method::GET,
1015                &format!("/services/settings/group/{}/", group_id),
1016            )
1017            .send()
1018            .await
1019            .map_err(error::Error::RequestError)?;
1020
1021        let response = check_response(response).await?;
1022        Ok(response.json::<Vec<String>>().await.unwrap())
1023    }
1024
1025    pub async fn list_user_settings(&self, username: &str) -> Result<Vec<String>, error::Error> {
1026        let response = self
1027            .request(
1028                Method::GET,
1029                &format!("/services/settings/user/{}/", username),
1030            )
1031            .send()
1032            .await
1033            .map_err(error::Error::RequestError)?;
1034
1035        let response = check_response(response).await?;
1036        Ok(response.json::<Vec<String>>().await.unwrap())
1037    }
1038
1039    pub async fn get_all_settings(
1040        &self,
1041        username: &str,
1042        service_id: &str,
1043    ) -> Result<ServiceSettings, error::Error> {
1044        let response = self
1045            .request(
1046                Method::GET,
1047                &format!("/services/settings/user/{}/{}/all", username, service_id),
1048            )
1049            .send()
1050            .await
1051            .map_err(error::Error::RequestError)?;
1052
1053        let response = check_response(response).await?;
1054        Ok(response.json::<ServiceSettings>().await.unwrap())
1055    }
1056
1057    pub async fn get_group_settings(
1058        &self,
1059        group_id: &GroupId,
1060        service_id: &str,
1061    ) -> Result<String, error::Error> {
1062        let response = self
1063            .request(
1064                Method::GET,
1065                &format!("/services/settings/group/{}/{}", group_id, service_id),
1066            )
1067            .send()
1068            .await
1069            .map_err(error::Error::RequestError)?;
1070
1071        let response = check_response(response).await?;
1072        Ok(response.text().await.unwrap())
1073    }
1074
1075    pub async fn get_user_settings(
1076        &self,
1077        username: &str,
1078        service_id: &str,
1079    ) -> Result<String, error::Error> {
1080        let response = self
1081            .request(
1082                Method::GET,
1083                &format!("/services/settings/user/{}/{}", username, service_id),
1084            )
1085            .send()
1086            .await
1087            .map_err(error::Error::RequestError)?;
1088
1089        let response = check_response(response).await?;
1090        Ok(response.text().await.unwrap())
1091    }
1092
1093    pub async fn set_user_settings(
1094        &self,
1095        username: &str,
1096        service_id: &str,
1097        settings: String,
1098    ) -> Result<String, error::Error> {
1099        let response = self
1100            .request(
1101                Method::POST,
1102                &format!("/services/settings/user/{}/{}", username, service_id),
1103            )
1104            .body(settings)
1105            .send()
1106            .await
1107            .map_err(error::Error::RequestError)?;
1108
1109        let response = check_response(response).await?;
1110        Ok(response.text().await.unwrap())
1111    }
1112
1113    pub async fn set_group_settings(
1114        &self,
1115        group_id: &GroupId,
1116        service_id: &str,
1117        settings: String,
1118    ) -> Result<String, error::Error> {
1119        let response = self
1120            .request(
1121                Method::POST,
1122                &format!("/services/settings/group/{}/{}", group_id, service_id),
1123            )
1124            .body(settings)
1125            .send()
1126            .await
1127            .map_err(error::Error::RequestError)?;
1128
1129        let response = check_response(response).await?;
1130        Ok(response.text().await.unwrap())
1131    }
1132
1133    pub async fn delete_user_settings(
1134        &self,
1135        username: &str,
1136        service_id: &str,
1137    ) -> Result<String, error::Error> {
1138        let response = self
1139            .request(
1140                Method::DELETE,
1141                &format!("/services/settings/user/{}/{}", username, service_id),
1142            )
1143            .send()
1144            .await
1145            .map_err(error::Error::RequestError)?;
1146
1147        let response = check_response(response).await?;
1148        Ok(response.text().await.unwrap())
1149    }
1150
1151    pub async fn delete_group_settings(
1152        &self,
1153        group_id: &GroupId,
1154        service_id: &str,
1155    ) -> Result<String, error::Error> {
1156        let response = self
1157            .request(
1158                Method::DELETE,
1159                &format!("/services/settings/group/{}/{}", group_id, service_id),
1160            )
1161            .send()
1162            .await
1163            .map_err(error::Error::RequestError)?;
1164
1165        let response = check_response(response).await?;
1166        Ok(response.text().await.unwrap())
1167    }
1168    // NetsBlox network capabilities
1169    pub async fn list_external_clients(&self) -> Result<Vec<ExternalClient>, error::Error> {
1170        let response = self
1171            .request(Method::GET, "/network/external")
1172            .send()
1173            .await
1174            .map_err(error::Error::RequestError)?;
1175
1176        let response = check_response(response).await?;
1177
1178        Ok(response.json::<Vec<ExternalClient>>().await.unwrap())
1179    }
1180
1181    pub async fn list_networks(&self) -> Result<Vec<ProjectId>, error::Error> {
1182        let response = self
1183            .request(Method::GET, "/network/")
1184            .send()
1185            .await
1186            .map_err(error::Error::RequestError)?;
1187
1188        let response = check_response(response).await?;
1189
1190        Ok(response.json::<Vec<ProjectId>>().await.unwrap())
1191    }
1192
1193    pub async fn get_room_state(&self, id: &ProjectId) -> Result<RoomState, error::Error> {
1194        let response = self
1195            .request(Method::GET, &format!("/network/id/{}", id))
1196            .send()
1197            .await
1198            .map_err(error::Error::RequestError)?;
1199
1200        let response = check_response(response).await?;
1201
1202        Ok(response.json::<RoomState>().await.unwrap())
1203    }
1204
1205    pub async fn get_client_state(&self, client_id: &ClientId) -> Result<ClientInfo, error::Error> {
1206        let response = self
1207            .request(
1208                Method::GET,
1209                &format!("/network/{}/state", client_id.as_str()),
1210            )
1211            .send()
1212            .await
1213            .map_err(error::Error::RequestError)?;
1214
1215        let response = check_response(response).await?;
1216
1217        Ok(response.json::<ClientInfo>().await.unwrap())
1218    }
1219
1220    pub async fn evict_occupant(&self, client_id: &ClientId) -> Result<(), error::Error> {
1221        let response = self
1222            .request(
1223                Method::POST,
1224                &format!("/network/clients/{}/evict", client_id.as_str()),
1225            )
1226            .send()
1227            .await
1228            .map_err(error::Error::RequestError)?;
1229
1230        check_response(response).await?;
1231        Ok(())
1232    }
1233
1234    pub async fn connect(&self, address: &str) -> Result<MessageChannel, error::Error> {
1235        let response = self
1236            .request(Method::GET, "/configuration")
1237            .send()
1238            .await
1239            .map_err(error::Error::RequestError)?;
1240
1241        let response = check_response(response).await?;
1242
1243        let config = response.json::<ClientConfig>().await.unwrap();
1244
1245        let url = format!(
1246            "{}/network/{}/connect",
1247            self.cfg.url.replace("http", "ws"),
1248            config.client_id
1249        );
1250        let (ws_stream, _) = connect_async(&url).await.unwrap();
1251
1252        let state = ClientStateData {
1253            state: ClientState::External(ExternalClientState {
1254                address: address.to_owned(),
1255                app_id: self.cfg.app_id.as_ref().unwrap().clone(),
1256            }),
1257        };
1258
1259        let response = self
1260            .request(
1261                Method::POST,
1262                &format!("/network/{}/state", config.client_id),
1263            )
1264            .json(&state)
1265            .send()
1266            .await
1267            .map_err(error::Error::RequestError)?;
1268
1269        check_response(response).await?;
1270
1271        Ok(MessageChannel {
1272            id: config.client_id,
1273            stream: ws_stream,
1274        })
1275    }
1276
1277    // NetsBlox OAuth capabilities
1278    pub async fn add_oauth_client(
1279        &self,
1280        client: &oauth::CreateClientData,
1281    ) -> Result<oauth::CreatedClientData, error::Error> {
1282        let response = self
1283            .request(Method::POST, "/oauth/clients/")
1284            .json(&client)
1285            .send()
1286            .await
1287            .map_err(error::Error::RequestError)?;
1288
1289        let response = check_response(response).await?;
1290
1291        Ok(response.json::<oauth::CreatedClientData>().await.unwrap())
1292    }
1293
1294    pub async fn remove_oauth_client(&self, id: &oauth::ClientId) -> Result<(), error::Error> {
1295        let response = self
1296            .request(Method::DELETE, &format!("/oauth/clients/{}", id))
1297            .send()
1298            .await
1299            .map_err(error::Error::RequestError)?;
1300
1301        check_response(response).await?;
1302        Ok(())
1303    }
1304
1305    pub async fn list_oauth_clients(&self) -> Result<Vec<oauth::Client>, error::Error> {
1306        let response = self
1307            .request(Method::GET, "/oauth/clients/")
1308            .send()
1309            .await
1310            .map_err(error::Error::RequestError)?;
1311
1312        let response = check_response(response).await?;
1313
1314        Ok(response.json::<Vec<oauth::Client>>().await.unwrap())
1315    }
1316}
1317
1318pub struct MessageChannel {
1319    pub id: String,
1320    pub stream: WebSocketStream<MaybeTlsStream<TcpStream>>,
1321}
1322
1323impl MessageChannel {
1324    // TODO: do we need a method for sending other types?
1325    // TODO: sending a generic struct (implementing Deserialize)
1326    pub async fn send_json(
1327        &mut self,
1328        addr: &str,
1329        r#type: &str,
1330        data: &Value,
1331    ) -> Result<(), error::Error> {
1332        let msg = json!({
1333            "type": "message",
1334            "dstId": addr,
1335            "msgType": r#type,
1336            "content": data
1337        });
1338        let msg_text = serde_json::to_string(&msg).unwrap();
1339        self.stream
1340            .send(Message::Text(msg_text))
1341            .await
1342            .map_err(error::Error::WebSocketSendError)?;
1343
1344        Ok(())
1345    }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350
1351    #[test]
1352    fn it_works() {
1353        let result = 2 + 2;
1354        assert_eq!(result, 4);
1355    }
1356}