1use crate::{
2 error::Result,
3 http::Requester,
4 pagination::PageStream,
5 params::wrap_params,
6 resources::{
7 communication_channel::CommunicationChannel,
8 content_migration::{ContentMigration, Migrator},
9 course::Course,
10 enrollment::Enrollment,
11 file::File,
12 folder::Folder,
13 },
14};
15
16#[derive(Debug, Default, Clone, serde::Serialize)]
18pub struct EditUserParams {
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub name: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub short_name: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub sortable_name: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub time_zone: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub email: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub locale: Option<String>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub avatar_url: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub bio: Option<String>,
35}
36use chrono::{DateTime, Utc};
37use serde::{Deserialize, Serialize};
38use std::sync::Arc;
39
40#[derive(Debug, Clone, Deserialize, Serialize, canvas_lms_api_derive::CanvasResource)]
42pub struct User {
43 pub id: u64,
44 pub name: Option<String>,
45 pub sortable_name: Option<String>,
46 pub short_name: Option<String>,
47 pub sis_user_id: Option<String>,
48 pub login_id: Option<String>,
49 pub email: Option<String>,
50 pub avatar_url: Option<String>,
51 pub locale: Option<String>,
52 pub last_login: Option<DateTime<Utc>>,
53 pub time_zone: Option<String>,
54 pub bio: Option<String>,
55
56 #[serde(skip)]
57 pub(crate) requester: Option<Arc<Requester>>,
58}
59
60impl User {
61 pub fn get_courses(&self) -> PageStream<Course> {
66 PageStream::new(
67 Arc::clone(self.req()),
68 &format!("users/{}/courses", self.id),
69 vec![],
70 )
71 }
72
73 pub fn get_enrollments(&self) -> PageStream<Enrollment> {
78 PageStream::new(
79 Arc::clone(self.req()),
80 &format!("users/{}/enrollments", self.id),
81 vec![],
82 )
83 }
84
85 pub fn get_communication_channels(&self) -> PageStream<CommunicationChannel> {
90 let user_id = self.id;
91 PageStream::new_with_injector(
92 Arc::clone(self.req()),
93 &format!("users/{user_id}/communication_channels"),
94 vec![],
95 |mut c: CommunicationChannel, req| {
96 c.requester = Some(Arc::clone(&req));
97 c
98 },
99 )
100 }
101
102 fn propagate(&self, u: &mut User) {
103 u.requester = self.requester.clone();
104 }
105
106 pub async fn edit(&self, params: EditUserParams) -> Result<User> {
111 let form = wrap_params("user", ¶ms);
112 let mut u: User = self.req().put(&format!("users/{}", self.id), &form).await?;
113 self.propagate(&mut u);
114 Ok(u)
115 }
116
117 pub async fn get_profile(&self) -> Result<serde_json::Value> {
122 self.req()
123 .get(&format!("users/{}/profile", self.id), &[])
124 .await
125 }
126
127 pub async fn terminate_sessions(&self) -> Result<()> {
132 self.req()
133 .delete_void(&format!("users/{}/sessions", self.id))
134 .await
135 }
136
137 pub async fn merge_into(&self, destination_user_id: u64) -> Result<User> {
142 let mut u: User = self
143 .req()
144 .put(
145 &format!("users/{}/merge_into/{destination_user_id}", self.id),
146 &[],
147 )
148 .await?;
149 self.propagate(&mut u);
150 Ok(u)
151 }
152
153 pub fn get_avatars(&self) -> PageStream<serde_json::Value> {
158 PageStream::new(
159 Arc::clone(self.req()),
160 &format!("users/{}/avatars", self.id),
161 vec![],
162 )
163 }
164
165 pub fn get_page_views(&self) -> PageStream<serde_json::Value> {
170 PageStream::new(
171 Arc::clone(self.req()),
172 &format!("users/{}/page_views", self.id),
173 vec![],
174 )
175 }
176
177 pub fn get_observees(&self) -> PageStream<User> {
182 PageStream::new(
183 Arc::clone(self.req()),
184 &format!("users/{}/observees", self.id),
185 vec![],
186 )
187 }
188
189 pub async fn add_observee(&self, observee_id: u64) -> Result<User> {
194 let mut u: User = self
195 .req()
196 .put(&format!("users/{}/observees/{observee_id}", self.id), &[])
197 .await?;
198 self.propagate(&mut u);
199 Ok(u)
200 }
201
202 pub async fn remove_observee(&self, observee_id: u64) -> Result<User> {
207 let mut u: User = self
208 .req()
209 .delete(&format!("users/{}/observees/{observee_id}", self.id), &[])
210 .await?;
211 self.propagate(&mut u);
212 Ok(u)
213 }
214
215 pub async fn show_observee(&self, observee_id: u64) -> Result<User> {
220 let mut u: User = self
221 .req()
222 .get(&format!("users/{}/observees/{observee_id}", self.id), &[])
223 .await?;
224 self.propagate(&mut u);
225 Ok(u)
226 }
227
228 pub fn get_observers(&self) -> PageStream<User> {
233 PageStream::new(
234 Arc::clone(self.req()),
235 &format!("users/{}/observers", self.id),
236 vec![],
237 )
238 }
239
240 pub async fn get_colors(&self) -> Result<serde_json::Value> {
245 self.req()
246 .get(&format!("users/{}/colors", self.id), &[])
247 .await
248 }
249
250 pub async fn get_color(&self, asset_string: &str) -> Result<serde_json::Value> {
255 self.req()
256 .get(&format!("users/{}/colors/{asset_string}", self.id), &[])
257 .await
258 }
259
260 pub async fn update_color(
265 &self,
266 asset_string: &str,
267 hexcode: &str,
268 ) -> Result<serde_json::Value> {
269 let params = vec![("hexcode".to_string(), hexcode.to_string())];
270 self.req()
271 .put(&format!("users/{}/colors/{asset_string}", self.id), ¶ms)
272 .await
273 }
274
275 pub fn get_missing_submissions(&self) -> PageStream<serde_json::Value> {
280 PageStream::new(
281 Arc::clone(self.req()),
282 &format!("users/{}/missing_submissions", self.id),
283 vec![],
284 )
285 }
286
287 pub fn get_files(&self) -> PageStream<File> {
292 PageStream::new_with_injector(
293 Arc::clone(self.req()),
294 &format!("users/{}/files", self.id),
295 vec![],
296 move |mut f: File, req| {
297 f.requester = Some(Arc::clone(&req));
298 f
299 },
300 )
301 }
302
303 pub fn get_folders(&self) -> PageStream<Folder> {
308 PageStream::new_with_injector(
309 Arc::clone(self.req()),
310 &format!("users/{}/folders", self.id),
311 vec![],
312 move |mut f: Folder, req| {
313 f.requester = Some(Arc::clone(&req));
314 f
315 },
316 )
317 }
318
319 pub async fn create_folder(&self, name: &str) -> Result<Folder> {
324 let params = vec![("name".to_string(), name.to_string())];
325 let mut f: Folder = self
326 .req()
327 .post(&format!("users/{}/folders", self.id), ¶ms)
328 .await?;
329 f.requester = self.requester.clone();
330 Ok(f)
331 }
332
333 pub async fn get_file_quota(&self) -> Result<serde_json::Value> {
338 self.req()
339 .get(&format!("users/{}/files/quota", self.id), &[])
340 .await
341 }
342
343 pub fn get_user_logins(&self) -> PageStream<serde_json::Value> {
348 PageStream::new(
349 Arc::clone(self.req()),
350 &format!("users/{}/logins", self.id),
351 vec![],
352 )
353 }
354
355 pub async fn get_settings(&self) -> Result<serde_json::Value> {
360 self.req()
361 .get(&format!("users/{}/settings", self.id), &[])
362 .await
363 }
364
365 pub async fn update_settings(&self, params: &[(String, String)]) -> Result<serde_json::Value> {
370 self.req()
371 .put(&format!("users/{}/settings", self.id), params)
372 .await
373 }
374
375 pub async fn create_communication_channel(
383 &self,
384 address: &str,
385 channel_type: &str,
386 ) -> Result<CommunicationChannel> {
387 let params = vec![
388 (
389 "communication_channel[address]".to_string(),
390 address.to_string(),
391 ),
392 (
393 "communication_channel[type]".to_string(),
394 channel_type.to_string(),
395 ),
396 ];
397 let mut channel: CommunicationChannel = self
398 .req()
399 .post(
400 &format!("users/{}/communication_channels", self.id),
401 ¶ms,
402 )
403 .await?;
404 channel.requester = self.requester.clone();
405 Ok(channel)
406 }
407
408 pub async fn create_pairing_code(&self) -> Result<serde_json::Value> {
413 self.req()
414 .post(&format!("users/{}/observer_pairing_codes", self.id), &[])
415 .await
416 }
417
418 pub fn get_authentication_events(&self) -> PageStream<serde_json::Value> {
423 PageStream::new(
424 Arc::clone(self.req()),
425 &format!("audit/authentication/users/{}", self.id),
426 vec![],
427 )
428 }
429
430 pub fn get_features(&self) -> PageStream<serde_json::Value> {
435 PageStream::new(
436 Arc::clone(self.req()),
437 &format!("users/{}/features", self.id),
438 vec![],
439 )
440 }
441
442 pub async fn get_enabled_features(&self) -> Result<Vec<String>> {
447 self.req()
448 .get(&format!("users/{}/features/enabled", self.id), &[])
449 .await
450 }
451
452 pub async fn export_content(&self, export_type: &str) -> Result<serde_json::Value> {
457 let params = vec![("export_type".to_string(), export_type.to_string())];
458 self.req()
459 .post(&format!("users/{}/content_exports", self.id), ¶ms)
460 .await
461 }
462
463 pub fn get_content_exports(&self) -> PageStream<serde_json::Value> {
468 PageStream::new(
469 Arc::clone(self.req()),
470 &format!("users/{}/content_exports", self.id),
471 vec![],
472 )
473 }
474
475 pub fn get_eportfolios(&self) -> PageStream<serde_json::Value> {
480 PageStream::new(
481 Arc::clone(self.req()),
482 &format!("users/{}/eportfolios", self.id),
483 vec![],
484 )
485 }
486
487 pub fn get_open_poll_sessions(&self) -> PageStream<serde_json::Value> {
492 PageStream::new(
493 Arc::clone(self.req()),
494 &format!("users/{}/poll_sessions/opened", self.id),
495 vec![],
496 )
497 }
498
499 pub fn get_closed_poll_sessions(&self) -> PageStream<serde_json::Value> {
504 PageStream::new(
505 Arc::clone(self.req()),
506 &format!("users/{}/poll_sessions/closed", self.id),
507 vec![],
508 )
509 }
510
511 pub async fn get_file(&self, file_id: u64) -> Result<File> {
516 let mut f: File = self
517 .req()
518 .get(&format!("users/{}/files/{file_id}", self.id), &[])
519 .await?;
520 f.requester = self.requester.clone();
521 Ok(f)
522 }
523
524 pub async fn get_folder(&self, folder_id: u64) -> Result<Folder> {
529 let mut f: Folder = self
530 .req()
531 .get(&format!("users/{}/folders/{folder_id}", self.id), &[])
532 .await?;
533 f.requester = self.requester.clone();
534 Ok(f)
535 }
536
537 pub fn resolve_path(&self, full_path: Option<&str>) -> PageStream<Folder> {
545 let endpoint = match full_path {
546 Some(p) if !p.is_empty() => {
547 format!("users/{}/folders/by_path/{p}", self.id)
548 }
549 _ => format!("users/{}/folders/by_path", self.id),
550 };
551 PageStream::new_with_injector(
552 Arc::clone(self.req()),
553 &endpoint,
554 vec![],
555 |mut f: Folder, req| {
556 f.requester = Some(Arc::clone(&req));
557 f
558 },
559 )
560 }
561
562 pub fn get_grade_change_events_for_grader(&self) -> PageStream<serde_json::Value> {
567 PageStream::new(
568 Arc::clone(self.req()),
569 &format!("audit/grade_change/graders/{}", self.id),
570 vec![],
571 )
572 }
573
574 pub fn get_grade_change_events_for_student(&self) -> PageStream<serde_json::Value> {
579 PageStream::new(
580 Arc::clone(self.req()),
581 &format!("audit/grade_change/students/{}", self.id),
582 vec![],
583 )
584 }
585
586 pub async fn get_content_migration(&self, migration_id: u64) -> Result<ContentMigration> {
591 let mut m: ContentMigration = self
592 .req()
593 .get(
594 &format!("users/{}/content_migrations/{migration_id}", self.id),
595 &[],
596 )
597 .await?;
598 m.requester = self.requester.clone();
599 Ok(m)
600 }
601
602 pub fn get_content_migrations(&self) -> PageStream<ContentMigration> {
607 let user_id = self.id;
608 PageStream::new_with_injector(
609 Arc::clone(self.req()),
610 &format!("users/{user_id}/content_migrations"),
611 vec![],
612 move |mut m: ContentMigration, req| {
613 m.requester = Some(Arc::clone(&req));
614 m.user_id = Some(user_id);
615 m
616 },
617 )
618 }
619
620 pub async fn create_content_migration(&self, migration_type: &str) -> Result<ContentMigration> {
625 let params = vec![("migration_type".to_string(), migration_type.to_string())];
626 let mut m: ContentMigration = self
627 .req()
628 .post(&format!("users/{}/content_migrations", self.id), ¶ms)
629 .await?;
630 m.requester = self.requester.clone();
631 m.user_id = Some(self.id);
632 Ok(m)
633 }
634
635 pub fn get_migration_systems(&self) -> PageStream<Migrator> {
640 PageStream::new(
641 Arc::clone(self.req()),
642 &format!("users/{}/content_migrations/migrators", self.id),
643 vec![],
644 )
645 }
646
647 pub async fn get_feature_flag(&self, feature: &str) -> Result<serde_json::Value> {
652 self.req()
653 .get(&format!("users/{}/features/flags/{feature}", self.id), &[])
654 .await
655 }
656
657 pub async fn upload_file(
665 &self,
666 request: crate::upload::UploadRequest,
667 data: Vec<u8>,
668 ) -> Result<File> {
669 crate::upload::initiate_and_upload(
670 self.req(),
671 &format!("users/{}/files", self.id),
672 request,
673 data,
674 )
675 .await
676 }
677
678 pub async fn add_observee_with_credentials(&self, params: &[(String, String)]) -> Result<User> {
683 let mut u: User = self
684 .req()
685 .post(&format!("users/{}/observees", self.id), params)
686 .await?;
687 u.requester = self.requester.clone();
688 Ok(u)
689 }
690
691 pub fn get_calendar_events(&self) -> PageStream<serde_json::Value> {
696 PageStream::new(
697 Arc::clone(self.req()),
698 &format!("users/{}/calendar_events", self.id),
699 vec![],
700 )
701 }
702
703 pub async fn get_content_export(&self, export_id: u64) -> Result<serde_json::Value> {
708 self.req()
709 .get(
710 &format!("users/{}/content_exports/{export_id}", self.id),
711 &[],
712 )
713 .await
714 }
715
716 pub fn get_licenses(&self) -> PageStream<serde_json::Value> {
721 PageStream::new(
722 Arc::clone(self.req()),
723 &format!("users/{}/content_licenses", self.id),
724 vec![],
725 )
726 }
727
728 pub async fn set_usage_rights(&self, params: &[(String, String)]) -> Result<serde_json::Value> {
733 self.req()
734 .put(&format!("users/{}/usage_rights", self.id), params)
735 .await
736 }
737
738 pub async fn remove_usage_rights(
743 &self,
744 params: &[(String, String)],
745 ) -> Result<serde_json::Value> {
746 self.req()
747 .delete(&format!("users/{}/usage_rights", self.id), params)
748 .await
749 }
750}
751
752#[derive(Debug, Clone, Deserialize, Serialize)]
754pub struct CurrentUser {
755 pub id: u64,
756 pub name: Option<String>,
757 pub sortable_name: Option<String>,
758 pub short_name: Option<String>,
759 pub sis_user_id: Option<String>,
760 pub login_id: Option<String>,
761 pub email: Option<String>,
762 pub avatar_url: Option<String>,
763 pub locale: Option<String>,
764 pub last_login: Option<DateTime<Utc>>,
765 pub time_zone: Option<String>,
766 pub bio: Option<String>,
767 pub effective_locale: Option<String>,
768}
769
770#[derive(Debug, Clone, Deserialize, Serialize)]
772pub struct UserDisplay {
773 pub id: u64,
774 pub display_name: Option<String>,
775 pub avatar_image_url: Option<String>,
776 pub html_url: Option<String>,
777}
778
779pub enum UserId {
781 Id(u64),
782 Current,
783}
784
785impl UserId {
786 pub(crate) fn to_path_segment(&self) -> String {
787 match self {
788 UserId::Id(id) => id.to_string(),
789 UserId::Current => "self".to_string(),
790 }
791 }
792}