1#![allow(clippy::redundant_pub_crate)]
2
3use crate::{
4 queries, utils,
5 v1::{
6 bool_from_int, bool_to_int, files, fs, response_payload, FileProperties, FileStorageInfo, HasFileLocation,
7 HasFileMetadata, HasLocationName, HasUuid, ItemKind, LocationColor, LocationNameMetadata,
8 },
9 FilenSettings,
10};
11use secstr::SecUtf8;
12use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
13use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
14use snafu::{Backtrace, ResultExt, Snafu};
15use std::fmt;
16use std::net::Ipv4Addr;
17use std::str::FromStr;
18use strum::{Display, EnumString};
19use uuid::Uuid;
20
21type Result<T, E = Error> = std::result::Result<T, E>;
22
23const USER_EVENTS_PATH: &str = "/v1/user/events";
24const USER_EVENTS_GET_PATH: &str = "/v1/user/events/get";
25
26#[derive(Snafu, Debug)]
27pub enum Error {
28 #[snafu(display(
29 "Expected \"all\" or specific event type, got unknown string of length: {}",
30 string_length
31 ))]
32 CannotParseUserEventFilterFromString { string_length: usize, backtrace: Backtrace },
33
34 #[snafu(display("{} query failed: {}", USER_EVENTS_PATH, source))]
35 UserEventsQueryFailed { source: queries::Error },
36
37 #[snafu(display("{} query failed: {}", USER_EVENTS_GET_PATH, source))]
38 UserEventsGetQueryFailed { source: queries::Error },
39}
40
41#[derive(Clone, Debug, Display, EnumString, Eq, Hash, PartialEq)]
43#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
44pub enum UserEventKind {
45 BaseFolderCreated,
46 CodeRedeemed,
47 DeleteAll,
48 DeleteUnfinished,
49 DeleteVersioned,
50 Disabled2FA,
51 EmailChangeAttempt,
52 EmailChanged,
53 Enabled2FA,
54 FileLinkEdited,
55 FileMoved,
56 FileRenamed,
57 FileRestored,
58 FileRm,
59 FileShared,
60 FileTrash,
61 FileUploaded,
62 FileVersioned,
63 FolderColorChanged,
64 FolderLinkEdited,
65 FolderMoved,
66 FolderRenamed,
67 FolderRestored,
68 FolderShared,
69 FolderTrash,
70 ItemFavorite,
71 Login,
72 PasswordChanged,
73 RemovedSharedInItems,
74 RemovedSharedOutItems,
75 SubFolderCreated,
76 RequestAccountDeletion,
77 TrashEmptied,
78 VersionedFileRestored,
79 #[strum(default)]
80 Unknown(String),
81}
82
83impl<'de> Deserialize<'de> for UserEventKind {
84 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85 where
86 D: Deserializer<'de>,
87 {
88 let s = String::deserialize(deserializer)?;
89 Ok(Self::from_str(&s).unwrap_or(Self::Unknown(s)))
90 }
91}
92
93impl Serialize for UserEventKind {
94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: Serializer,
97 {
98 #[allow(clippy::wildcard_enum_match_arm)]
99 match self {
100 &UserEventKind::Unknown(ref value) => serializer.serialize_str(value),
101 other => serializer.serialize_str(&other.to_string()),
102 }
103 }
104}
105
106#[derive(Clone, Debug, Eq, Hash, PartialEq)]
108pub enum UserEventFilter {
109 All,
111 Specific(UserEventKind),
113}
114
115impl FromStr for UserEventFilter {
116 type Err = Error;
117
118 fn from_str(all_or_event_kind: &str) -> Result<Self, Self::Err> {
119 if all_or_event_kind.eq_ignore_ascii_case("all") {
120 Ok(Self::All)
121 } else {
122 match UserEventKind::from_str(all_or_event_kind) {
123 Ok(user_event_kind) => Ok(Self::Specific(user_event_kind)),
124 Err(_) => CannotParseUserEventFilterFromStringSnafu {
125 string_length: all_or_event_kind.len(),
126 }
127 .fail(),
128 }
129 }
130 }
131}
132
133impl fmt::Display for UserEventFilter {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 match *self {
136 UserEventFilter::All => write!(f, "all"),
137 UserEventFilter::Specific(ref user_event_kind) => user_event_kind.fmt(f),
138 }
139 }
140}
141
142impl<'de> Deserialize<'de> for UserEventFilter {
143 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144 where
145 D: Deserializer<'de>,
146 {
147 let all_or_event_kind = String::deserialize(deserializer)?;
148
149 if all_or_event_kind.eq_ignore_ascii_case("all") {
150 Ok(Self::All)
151 } else {
152 match UserEventKind::from_str(&all_or_event_kind) {
153 Ok(user_event_kind) => Ok(Self::Specific(user_event_kind)),
154 Err(_) => Err(de::Error::invalid_value(
155 de::Unexpected::Str(&all_or_event_kind),
156 &"\"all\" or specific event type",
157 )),
158 }
159 }
160 }
161}
162
163impl Serialize for UserEventFilter {
164 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165 where
166 S: Serializer,
167 {
168 match *self {
169 UserEventFilter::All => serializer.serialize_str("all"),
170 UserEventFilter::Specific(ref user_event_kind) => user_event_kind.serialize(serializer),
171 }
172 }
173}
174
175#[serde_as]
177#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
178pub struct UserFingerprint {
179 #[serde_as(as = "DisplayFromStr")]
180 pub ip: Ipv4Addr,
181
182 #[serde(rename = "userAgent")]
183 pub user_agent: String,
184}
185
186user_event_struct!(
187 PlainUserEvent<UserFingerprint>
189);
190
191#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
193pub struct DownloadableFileEventInfo {
194 pub uuid: Uuid,
196
197 #[serde(flatten)]
199 pub storage: FileStorageInfo,
200
201 pub metadata: String,
203
204 pub rm: String,
206
207 pub timestamp: u64,
209
210 pub parent: Uuid,
212
213 pub version: u32,
217
218 #[serde(flatten)]
220 pub fingerprint: UserFingerprint,
221}
222utils::display_from_json!(DownloadableFileEventInfo);
223
224impl HasFileMetadata for DownloadableFileEventInfo {
225 fn file_metadata_ref(&self) -> &str {
226 &self.metadata
227 }
228}
229
230impl HasFileLocation for DownloadableFileEventInfo {
231 fn file_storage_ref(&self) -> &FileStorageInfo {
232 &self.storage
233 }
234}
235
236impl HasUuid for DownloadableFileEventInfo {
237 fn uuid_ref(&self) -> &Uuid {
238 &self.uuid
239 }
240}
241
242#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
244pub struct FileParentlessEventInfo {
245 pub uuid: Uuid,
247
248 pub metadata: String,
250
251 #[serde(flatten)]
253 pub fingerprint: UserFingerprint,
254}
255utils::display_from_json!(FileParentlessEventInfo);
256
257impl HasFileMetadata for FileParentlessEventInfo {
258 fn file_metadata_ref(&self) -> &str {
259 &self.metadata
260 }
261}
262
263#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
264pub struct FolderEventInfo {
265 pub uuid: Uuid,
267
268 #[serde(rename = "name")]
270 pub name_metadata: String,
271
272 pub parent: Uuid,
274
275 pub timestamp: u64,
277
278 #[serde(flatten)]
280 pub fingerprint: UserFingerprint,
281}
282utils::display_from_json!(FolderEventInfo);
283
284impl HasLocationName for FolderEventInfo {
285 fn name_metadata_ref(&self) -> &str {
286 &self.name_metadata
287 }
288}
289
290impl HasUuid for FolderEventInfo {
291 fn uuid_ref(&self) -> &Uuid {
292 &self.uuid
293 }
294}
295
296#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
298pub struct UserEventsRequestPayload<'user_events> {
299 #[serde(rename = "apiKey")]
301 pub api_key: &'user_events SecUtf8,
302
303 pub id: u64,
304
305 pub filter: UserEventFilter,
307}
308utils::display_from_json_with_lifetime!('user_events, UserEventsRequestPayload);
309
310#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
311pub struct BaseFolderCreatedEventInfo {
312 pub uuid: Uuid,
314
315 #[serde(rename = "name")]
317 pub name_metadata: String,
318
319 pub timestamp: u64,
321
322 #[serde(flatten)]
324 pub fingerprint: UserFingerprint,
325}
326utils::display_from_json!(BaseFolderCreatedEventInfo);
327
328impl HasLocationName for BaseFolderCreatedEventInfo {
329 fn name_metadata_ref(&self) -> &str {
330 &self.name_metadata
331 }
332}
333
334impl HasUuid for BaseFolderCreatedEventInfo {
335 fn uuid_ref(&self) -> &Uuid {
336 &self.uuid
337 }
338}
339
340user_event_struct!(
341 BaseFolderCreatedUserEvent<BaseFolderCreatedEventInfo>
343);
344
345#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
346pub struct CodeRedeemedEventInfo {
347 pub code: String,
349
350 #[serde(flatten)]
352 pub fingerprint: UserFingerprint,
353}
354utils::display_from_json!(CodeRedeemedEventInfo);
355
356user_event_struct!(
357 CodeRedeemedUserEvent<CodeRedeemedEventInfo>
359);
360
361#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
362pub struct EmailChangeAttemptInfo {
363 pub email: String,
365
366 #[serde(flatten)]
368 pub fingerprint: UserFingerprint,
369}
370utils::display_from_json!(EmailChangeAttemptInfo);
371
372user_event_struct!(
373 EmailChangeAttemptUserEvent<EmailChangeAttemptInfo>
375);
376
377#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
378pub struct EmailChangedInfo {
379 pub email: String,
381
382 #[serde(flatten)]
384 pub fingerprint: UserFingerprint,
385}
386utils::display_from_json!(EmailChangedInfo);
387
388user_event_struct!(
389 EmailChangedUserEvent<EmailChangedInfo>
391);
392
393#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
394pub struct FileLinkEditedInfo {
395 pub uuid: Uuid,
397
398 #[serde(rename = "linkUUID")]
400 pub link_uuid: Uuid,
401
402 pub metadata: String,
404
405 #[serde(flatten)]
407 pub fingerprint: UserFingerprint,
408}
409utils::display_from_json!(FileLinkEditedInfo);
410
411impl HasFileMetadata for FileLinkEditedInfo {
412 fn file_metadata_ref(&self) -> &str {
413 &self.metadata
414 }
415}
416
417user_event_struct!(
418 FileLinkEditedUserEvent<FileLinkEditedInfo>
420);
421
422user_event_struct!(
423 FileMovedUserEvent<DownloadableFileEventInfo>
425);
426
427user_event_struct!(
428 FileRestoredUserEvent<DownloadableFileEventInfo>
430);
431
432#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
433pub struct FileRenamedInfo {
434 pub uuid: Uuid,
436
437 pub metadata: String,
439
440 #[serde(rename = "oldMetadata")]
442 pub old_metadata: String,
443
444 #[serde(flatten)]
446 pub fingerprint: UserFingerprint,
447}
448utils::display_from_json!(FileRenamedInfo);
449
450impl FileRenamedInfo {
451 pub fn decrypt_old_file_metadata(&self, master_keys: &[SecUtf8]) -> Result<FileProperties, files::Error> {
453 FileProperties::decrypt_file_metadata(&self.old_metadata, master_keys)
454 }
455}
456
457impl HasFileMetadata for FileRenamedInfo {
458 fn file_metadata_ref(&self) -> &str {
459 &self.metadata
460 }
461}
462
463user_event_struct!(
464 FileRenamedUserEvent<FileRenamedInfo>
466);
467
468user_event_struct!(
469 FileRmUserEvent<FileParentlessEventInfo>
471);
472
473#[skip_serializing_none]
474#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
475pub struct FileSharedInfo {
476 pub uuid: Uuid,
478
479 #[serde(rename = "receiverEmail")]
481 pub receiver_email: String,
482
483 pub metadata: String,
485
486 pub parent: Option<Uuid>,
488
489 #[serde(flatten)]
491 pub fingerprint: UserFingerprint,
492}
493utils::display_from_json!(FileSharedInfo);
494
495impl HasFileMetadata for FileSharedInfo {
496 fn file_metadata_ref(&self) -> &str {
497 &self.metadata
498 }
499}
500
501user_event_struct!(
502 FileSharedUserEvent<FileSharedInfo>
504);
505
506user_event_struct!(
507 FileVersionedUserEvent<FileParentlessEventInfo>
509);
510
511user_event_struct!(
512 FileTrashUserEvent<FileParentlessEventInfo>
514);
515
516user_event_struct!(
517 FileUploadedUserEvent<DownloadableFileEventInfo>
519);
520
521#[skip_serializing_none]
522#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
523pub struct FolderColorChangedInfo {
524 pub uuid: Uuid,
526
527 #[serde(rename = "name")]
529 pub name_metadata: String,
530
531 pub color: LocationColor,
533
534 #[serde(rename = "oldColor")]
536 pub old_color: Option<LocationColor>,
537
538 #[serde(flatten)]
540 pub fingerprint: UserFingerprint,
541}
542utils::display_from_json!(FolderColorChangedInfo);
543
544impl HasLocationName for FolderColorChangedInfo {
545 fn name_metadata_ref(&self) -> &str {
546 &self.name_metadata
547 }
548}
549
550impl HasUuid for FolderColorChangedInfo {
551 fn uuid_ref(&self) -> &Uuid {
552 &self.uuid
553 }
554}
555
556user_event_struct!(
557 FolderColorChangedUserEvent<FolderColorChangedInfo>
559);
560
561#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
562pub struct FolderLinkEditedInfo {
563 pub uuid: Uuid,
565
566 #[serde(rename = "linkUUID")]
568 pub link_uuid: Uuid,
569
570 #[serde(flatten)]
572 pub fingerprint: UserFingerprint,
573}
574utils::display_from_json!(FolderLinkEditedInfo);
575
576user_event_struct!(
577 FolderLinkEditedUserEvent<FolderLinkEditedInfo>
579);
580
581user_event_struct!(
582 FolderMovedUserEvent<FolderEventInfo>
584);
585
586#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
587pub struct FolderRenamedInfo {
588 pub uuid: Uuid,
590
591 #[serde(rename = "name")]
593 pub name_metadata: String,
594
595 #[serde(rename = "oldName")]
597 pub old_name_metadata: String,
598
599 #[serde(flatten)]
601 pub fingerprint: UserFingerprint,
602}
603utils::display_from_json!(FolderRenamedInfo);
604
605impl FolderRenamedInfo {
606 pub fn decrypt_old_name_metadata(&self, master_keys: &[SecUtf8]) -> Result<String, fs::Error> {
608 LocationNameMetadata::decrypt_name_from_metadata(&self.old_name_metadata, master_keys)
609 }
610}
611
612impl HasLocationName for FolderRenamedInfo {
613 fn name_metadata_ref(&self) -> &str {
614 &self.name_metadata
615 }
616}
617
618impl HasUuid for FolderRenamedInfo {
619 fn uuid_ref(&self) -> &Uuid {
620 &self.uuid
621 }
622}
623
624user_event_struct!(
625 FolderRenamedUserEvent<FolderRenamedInfo>
627);
628
629user_event_struct!(
630 FolderRestoredUserEvent<FolderEventInfo>
632);
633
634#[skip_serializing_none]
635#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
636pub struct FolderSharedEventInfo {
637 pub uuid: Uuid,
639
640 #[serde(rename = "receiverEmail")]
642 pub receiver_email: String,
643
644 #[serde(rename = "name")]
646 pub name_metadata: String,
647
648 pub parent: Option<Uuid>,
650
651 #[serde(flatten)]
653 pub fingerprint: UserFingerprint,
654}
655utils::display_from_json!(FolderSharedEventInfo);
656
657impl HasLocationName for FolderSharedEventInfo {
658 fn name_metadata_ref(&self) -> &str {
659 &self.name_metadata
660 }
661}
662
663impl HasUuid for FolderSharedEventInfo {
664 fn uuid_ref(&self) -> &Uuid {
665 &self.uuid
666 }
667}
668
669user_event_struct!(
670 FolderSharedUserEvent<FolderSharedEventInfo>
672);
673
674#[skip_serializing_none]
675#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
676pub struct FolderTrashEventInfo {
677 pub uuid: Uuid,
679
680 #[serde(rename = "name")]
682 pub name_metadata: String,
683
684 pub parent: Option<Uuid>,
686
687 #[serde(flatten)]
689 pub fingerprint: UserFingerprint,
690}
691utils::display_from_json!(FolderTrashEventInfo);
692
693impl HasLocationName for FolderTrashEventInfo {
694 fn name_metadata_ref(&self) -> &str {
695 &self.name_metadata
696 }
697}
698
699impl HasUuid for FolderTrashEventInfo {
700 fn uuid_ref(&self) -> &Uuid {
701 &self.uuid
702 }
703}
704
705user_event_struct!(
706 FolderTrashUserEvent<FolderTrashEventInfo>
708);
709
710#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
711pub struct ItemFavoriteEventInfo {
712 pub uuid: Uuid,
714
715 #[serde(rename = "type")]
717 pub item_type: ItemKind,
718
719 #[serde(deserialize_with = "bool_from_int", serialize_with = "bool_to_int")]
721 pub value: bool,
722
723 pub metadata: String,
725
726 #[serde(flatten)]
728 pub fingerprint: UserFingerprint,
729}
730utils::display_from_json!(ItemFavoriteEventInfo);
731
732impl HasFileMetadata for ItemFavoriteEventInfo {
733 fn file_metadata_ref(&self) -> &str {
734 &self.metadata
735 }
736}
737
738user_event_struct!(
739 ItemFavoriteUserEvent<ItemFavoriteEventInfo>
741);
742
743#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
744pub struct RemovedSharedInItemsInfo {
745 #[serde(rename = "sharerEmail")]
747 pub sharer_email: String,
748
749 pub count: u32,
751
752 #[serde(flatten)]
754 pub fingerprint: UserFingerprint,
755}
756utils::display_from_json!(RemovedSharedInItemsInfo);
757
758user_event_struct!(
759 RemovedSharedInItemsUserEvent<RemovedSharedInItemsInfo>
761);
762
763#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
764pub struct RemovedSharedOutItemsInfo {
765 #[serde(rename = "receiverEmail")]
767 pub receiver_email: String,
768
769 pub count: u32,
771
772 #[serde(flatten)]
774 pub fingerprint: UserFingerprint,
775}
776utils::display_from_json!(RemovedSharedOutItemsInfo);
777
778user_event_struct!(
779 RemovedSharedOutItemsUserEvent<RemovedSharedOutItemsInfo>
781);
782
783user_event_struct!(
784 SubFolderCreatedUserEvent<FolderEventInfo>
786);
787
788user_event_struct!(
789 VersionedFileRestoredUserEvent<DownloadableFileEventInfo>
791);
792
793#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
794#[serde(untagged)]
795pub enum UserEvent {
796 BaseFolderCreated(BaseFolderCreatedUserEvent),
797 CodeRedeemed(CodeRedeemedUserEvent),
798 DeleteAll(PlainUserEvent),
799 DeleteUnfinished(PlainUserEvent),
800 DeleteVersioned(PlainUserEvent),
801 Disabled2FA(PlainUserEvent),
802 EmailChangeAttempt(EmailChangeAttemptUserEvent),
803 EmailChanged(EmailChangedUserEvent),
804 Enabled2FA(PlainUserEvent),
805 FileLinkEdited(FileLinkEditedUserEvent),
806 FileMoved(FileMovedUserEvent),
807 FileRenamed(FileRenamedUserEvent),
808 FileRestored(FileRestoredUserEvent),
809 FileRm(FileRmUserEvent),
810 FileShared(FileSharedUserEvent),
811 FileTrash(FileTrashUserEvent),
812 FileUploaded(FileUploadedUserEvent),
813 FileVersioned(FileVersionedUserEvent),
814 FolderColorChanged(FolderColorChangedUserEvent),
815 FolderLinkEdited(FolderLinkEditedUserEvent),
816 FolderMoved(FolderMovedUserEvent),
817 FolderRenamed(FolderRenamedUserEvent),
818 FolderRestored(FolderRestoredUserEvent),
819 FolderShared(FolderSharedUserEvent),
820 FolderTrash(FolderTrashUserEvent),
821 ItemFavorite(ItemFavoriteUserEvent),
822 Login(PlainUserEvent),
823 PasswordChanged(PlainUserEvent),
824 RemovedSharedInItems(RemovedSharedInItemsUserEvent),
825 RemovedSharedOutItems(RemovedSharedOutItemsUserEvent),
826 SubFolderCreated(SubFolderCreatedUserEvent),
827 RequestAccountDeletion(PlainUserEvent),
828 TrashEmptied(PlainUserEvent),
829 VersionedFileRestored(VersionedFileRestoredUserEvent),
830 Unknown(PlainUserEvent),
831}
832
833#[derive(Deserialize)]
834pub(crate) struct UserEventDeserializeHelper {
835 pub id: u64,
836 pub uuid: Uuid,
837 #[serde(rename = "type")]
838 pub event_type: UserEventKind,
839 pub info: serde_json::Value,
840 pub timestamp: u64,
841}
842
843impl<'de> Deserialize<'de> for UserEvent {
844 #[allow(clippy::too_many_lines)]
846 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
847 where
848 D: Deserializer<'de>,
849 {
850 let helper = UserEventDeserializeHelper::deserialize(deserializer)?;
851 match helper.event_type {
852 UserEventKind::BaseFolderCreated => BaseFolderCreatedEventInfo::deserialize(&helper.info)
853 .map(|ei| Self::BaseFolderCreated(BaseFolderCreatedUserEvent::from_helper_and_info(helper, ei)))
854 .map_err(de::Error::custom),
855 UserEventKind::CodeRedeemed => CodeRedeemedEventInfo::deserialize(&helper.info)
856 .map(|ei| Self::CodeRedeemed(CodeRedeemedUserEvent::from_helper_and_info(helper, ei)))
857 .map_err(de::Error::custom),
858 UserEventKind::DeleteAll => UserFingerprint::deserialize(&helper.info)
859 .map(|ei| Self::DeleteAll(PlainUserEvent::from_helper_and_info(helper, ei)))
860 .map_err(de::Error::custom),
861 UserEventKind::DeleteUnfinished => UserFingerprint::deserialize(&helper.info)
862 .map(|ei| Self::DeleteUnfinished(PlainUserEvent::from_helper_and_info(helper, ei)))
863 .map_err(de::Error::custom),
864 UserEventKind::DeleteVersioned => UserFingerprint::deserialize(&helper.info)
865 .map(|ei| Self::DeleteVersioned(PlainUserEvent::from_helper_and_info(helper, ei)))
866 .map_err(de::Error::custom),
867 UserEventKind::Disabled2FA => UserFingerprint::deserialize(&helper.info)
868 .map(|ei| Self::Disabled2FA(PlainUserEvent::from_helper_and_info(helper, ei)))
869 .map_err(de::Error::custom),
870 UserEventKind::EmailChangeAttempt => EmailChangeAttemptInfo::deserialize(&helper.info)
871 .map(|ei| Self::EmailChangeAttempt(EmailChangeAttemptUserEvent::from_helper_and_info(helper, ei)))
872 .map_err(de::Error::custom),
873 UserEventKind::EmailChanged => EmailChangedInfo::deserialize(&helper.info)
874 .map(|ei| Self::EmailChanged(EmailChangedUserEvent::from_helper_and_info(helper, ei)))
875 .map_err(de::Error::custom),
876 UserEventKind::Enabled2FA => UserFingerprint::deserialize(&helper.info)
877 .map(|ei| Self::Enabled2FA(PlainUserEvent::from_helper_and_info(helper, ei)))
878 .map_err(de::Error::custom),
879 UserEventKind::FileLinkEdited => FileLinkEditedInfo::deserialize(&helper.info)
880 .map(|ei| Self::FileLinkEdited(FileLinkEditedUserEvent::from_helper_and_info(helper, ei)))
881 .map_err(de::Error::custom),
882 UserEventKind::FileMoved => DownloadableFileEventInfo::deserialize(&helper.info)
883 .map(|ei| Self::FileMoved(FileMovedUserEvent::from_helper_and_info(helper, ei)))
884 .map_err(de::Error::custom),
885 UserEventKind::FileRenamed => FileRenamedInfo::deserialize(&helper.info)
886 .map(|ei| Self::FileRenamed(FileRenamedUserEvent::from_helper_and_info(helper, ei)))
887 .map_err(de::Error::custom),
888 UserEventKind::FileRestored => DownloadableFileEventInfo::deserialize(&helper.info)
889 .map(|ei| Self::FileRestored(FileRestoredUserEvent::from_helper_and_info(helper, ei)))
890 .map_err(de::Error::custom),
891 UserEventKind::FileRm => FileParentlessEventInfo::deserialize(&helper.info)
892 .map(|ei| Self::FileRm(FileRmUserEvent::from_helper_and_info(helper, ei)))
893 .map_err(de::Error::custom),
894 UserEventKind::FileShared => FileSharedInfo::deserialize(&helper.info)
895 .map(|ei| Self::FileShared(FileSharedUserEvent::from_helper_and_info(helper, ei)))
896 .map_err(de::Error::custom),
897 UserEventKind::FileTrash => FileParentlessEventInfo::deserialize(&helper.info)
898 .map(|ei| Self::FileTrash(FileTrashUserEvent::from_helper_and_info(helper, ei)))
899 .map_err(de::Error::custom),
900 UserEventKind::FileUploaded => DownloadableFileEventInfo::deserialize(&helper.info)
901 .map(|ei| Self::FileUploaded(FileUploadedUserEvent::from_helper_and_info(helper, ei)))
902 .map_err(de::Error::custom),
903 UserEventKind::FileVersioned => FileParentlessEventInfo::deserialize(&helper.info)
904 .map(|ei| Self::FileVersioned(FileVersionedUserEvent::from_helper_and_info(helper, ei)))
905 .map_err(de::Error::custom),
906 UserEventKind::FolderColorChanged => FolderColorChangedInfo::deserialize(&helper.info)
907 .map(|ei| Self::FolderColorChanged(FolderColorChangedUserEvent::from_helper_and_info(helper, ei)))
908 .map_err(de::Error::custom),
909 UserEventKind::FolderLinkEdited => FolderLinkEditedInfo::deserialize(&helper.info)
910 .map(|ei| Self::FolderLinkEdited(FolderLinkEditedUserEvent::from_helper_and_info(helper, ei)))
911 .map_err(de::Error::custom),
912 UserEventKind::FolderMoved => FolderEventInfo::deserialize(&helper.info)
913 .map(|ei| Self::FolderMoved(FolderMovedUserEvent::from_helper_and_info(helper, ei)))
914 .map_err(de::Error::custom),
915 UserEventKind::FolderRenamed => FolderRenamedInfo::deserialize(&helper.info)
916 .map(|ei| Self::FolderRenamed(FolderRenamedUserEvent::from_helper_and_info(helper, ei)))
917 .map_err(de::Error::custom),
918 UserEventKind::FolderRestored => FolderEventInfo::deserialize(&helper.info)
919 .map(|ei| Self::FolderRestored(FolderRestoredUserEvent::from_helper_and_info(helper, ei)))
920 .map_err(de::Error::custom),
921 UserEventKind::FolderShared => FolderSharedEventInfo::deserialize(&helper.info)
922 .map(|ei| Self::FolderShared(FolderSharedUserEvent::from_helper_and_info(helper, ei)))
923 .map_err(de::Error::custom),
924 UserEventKind::FolderTrash => FolderTrashEventInfo::deserialize(&helper.info)
925 .map(|ei| Self::FolderTrash(FolderTrashUserEvent::from_helper_and_info(helper, ei)))
926 .map_err(de::Error::custom),
927 UserEventKind::ItemFavorite => ItemFavoriteEventInfo::deserialize(&helper.info)
928 .map(|ei| Self::ItemFavorite(ItemFavoriteUserEvent::from_helper_and_info(helper, ei)))
929 .map_err(de::Error::custom),
930 UserEventKind::Login => UserFingerprint::deserialize(&helper.info)
931 .map(|ei| Self::Login(PlainUserEvent::from_helper_and_info(helper, ei)))
932 .map_err(de::Error::custom),
933 UserEventKind::PasswordChanged => UserFingerprint::deserialize(&helper.info)
934 .map(|ei| Self::PasswordChanged(PlainUserEvent::from_helper_and_info(helper, ei)))
935 .map_err(de::Error::custom),
936 UserEventKind::RemovedSharedInItems => RemovedSharedInItemsInfo::deserialize(&helper.info)
937 .map(|ei| Self::RemovedSharedInItems(RemovedSharedInItemsUserEvent::from_helper_and_info(helper, ei)))
938 .map_err(de::Error::custom),
939 UserEventKind::RemovedSharedOutItems => RemovedSharedOutItemsInfo::deserialize(&helper.info)
940 .map(|ei| Self::RemovedSharedOutItems(RemovedSharedOutItemsUserEvent::from_helper_and_info(helper, ei)))
941 .map_err(de::Error::custom),
942 UserEventKind::SubFolderCreated => FolderEventInfo::deserialize(&helper.info)
943 .map(|ei| Self::SubFolderCreated(SubFolderCreatedUserEvent::from_helper_and_info(helper, ei)))
944 .map_err(de::Error::custom),
945 UserEventKind::RequestAccountDeletion => UserFingerprint::deserialize(&helper.info)
946 .map(|ei| Self::RequestAccountDeletion(PlainUserEvent::from_helper_and_info(helper, ei)))
947 .map_err(de::Error::custom),
948 UserEventKind::TrashEmptied => UserFingerprint::deserialize(&helper.info)
949 .map(|ei| Self::TrashEmptied(PlainUserEvent::from_helper_and_info(helper, ei)))
950 .map_err(de::Error::custom),
951 UserEventKind::VersionedFileRestored => DownloadableFileEventInfo::deserialize(&helper.info)
952 .map(|ei| Self::VersionedFileRestored(VersionedFileRestoredUserEvent::from_helper_and_info(helper, ei)))
953 .map_err(de::Error::custom),
954 UserEventKind::Unknown(_) => UserFingerprint::deserialize(&helper.info)
955 .map(|ei| Self::Unknown(PlainUserEvent::from_helper_and_info(helper, ei)))
956 .map_err(de::Error::custom),
957 }
958 }
959}
960
961#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
963pub struct UserEventsResponseData {
964 pub events: Vec<UserEvent>,
966
967 pub limit: u32,
969}
970utils::display_from_json!(UserEventsResponseData);
971
972response_payload!(
973 UserEventsResponsePayload<UserEventsResponseData>
975);
976
977#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
979pub struct UserEventsGetRequestPayload<'user_events_get> {
980 #[serde(rename = "apiKey")]
982 pub api_key: &'user_events_get SecUtf8,
983
984 pub uuid: Uuid,
986}
987utils::display_from_json_with_lifetime!('user_events_get, UserEventsGetRequestPayload);
988
989response_payload!(
990 UserEventsGetResponsePayload<UserEvent>
992);
993
994pub fn user_events_request(
996 payload: &UserEventsRequestPayload,
997 filen_settings: &FilenSettings,
998) -> Result<UserEventsResponsePayload> {
999 queries::query_filen_api(USER_EVENTS_PATH, payload, filen_settings).context(UserEventsQueryFailedSnafu {})
1000}
1001
1002#[cfg(feature = "async")]
1004pub async fn user_events_request_async(
1005 payload: &UserEventsRequestPayload<'_>,
1006 filen_settings: &FilenSettings,
1007) -> Result<UserEventsResponsePayload> {
1008 queries::query_filen_api_async(USER_EVENTS_PATH, payload, filen_settings)
1009 .await
1010 .context(UserEventsQueryFailedSnafu {})
1011}
1012
1013pub fn user_events_get_request(
1015 payload: &UserEventsGetRequestPayload,
1016 filen_settings: &FilenSettings,
1017) -> Result<UserEventsGetResponsePayload> {
1018 queries::query_filen_api(USER_EVENTS_GET_PATH, payload, filen_settings).context(UserEventsGetQueryFailedSnafu {})
1019}
1020
1021#[cfg(feature = "async")]
1023pub async fn user_events_get_request_async(
1024 payload: &UserEventsGetRequestPayload<'_>,
1025 filen_settings: &FilenSettings,
1026) -> Result<UserEventsGetResponsePayload> {
1027 queries::query_filen_api_async(USER_EVENTS_GET_PATH, payload, filen_settings)
1028 .await
1029 .context(UserEventsGetQueryFailedSnafu {})
1030}
1031
1032macro_rules! user_event_struct {
1033 (
1034 $(#[$meta:meta])*
1035 $struct_name:ident<$event_data_type:ty>
1036 ) => {
1037 $(#[$meta])*
1038 #[serde_with::skip_serializing_none]
1039 #[derive(Clone, Debug, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)]
1040 pub struct $struct_name {
1041 pub id: u64,
1043
1044 pub uuid: Uuid,
1046
1047 #[serde(rename = "type")]
1049 pub event_type: UserEventKind,
1050
1051 pub timestamp: u64,
1053
1054 pub info: $event_data_type,
1056 }
1057
1058 crate::utils::display_from_json!($struct_name);
1059
1060 impl $struct_name {
1061 #[allow(clippy::missing_const_for_fn)]
1062 pub(crate) fn from_helper_and_info(helper: UserEventDeserializeHelper, info: $event_data_type) -> $struct_name {
1063 $struct_name {
1064 id: helper.id,
1065 uuid: helper.uuid,
1066 event_type: helper.event_type,
1067 timestamp: helper.timestamp,
1068 info,
1069 }
1070 }
1071 }
1072 }
1073}
1074pub(crate) use user_event_struct;
1075
1076#[cfg(test)]
1077mod tests {
1078 use super::*;
1079 use crate::test_utils::validate_contract;
1080 #[cfg(feature = "async")]
1081 use crate::test_utils::validate_contract_async;
1082 use once_cell::sync::Lazy;
1083 use pretty_assertions::assert_eq;
1084 use secstr::SecUtf8;
1085
1086 static API_KEY: Lazy<SecUtf8> =
1087 Lazy::new(|| SecUtf8::from("bYZmrwdVEbHJSqeA1RfnPtKiBcXzUpRdKGRkjw9m1o1eqSGP1s6DM11CDnklpFq6"));
1088
1089 #[test]
1090 fn known_user_event_kind_should_be_serialized() {
1091 let expected = r#""deleteAll""#;
1092
1093 let serialized = serde_json::to_string(&UserEventKind::DeleteAll).unwrap();
1094
1095 assert_eq!(serialized, expected);
1096 }
1097
1098 #[test]
1099 fn unknown_user_event_kind_should_be_serialized() {
1100 let expected = r#""this is some unknown value""#;
1101
1102 let serialized_user_event_kind =
1103 serde_json::to_string(&UserEventKind::Unknown("this is some unknown value".to_owned())).unwrap();
1104
1105 assert_eq!(serialized_user_event_kind, expected);
1106 }
1107
1108 #[test]
1109 fn known_user_event_kind_should_be_deserialized() {
1110 let expected = UserEventKind::DeleteAll;
1111
1112 let deserialized_user_event_kind = serde_json::from_str::<UserEventKind>(r#""deleteAll""#).unwrap();
1113
1114 assert_eq!(deserialized_user_event_kind, expected);
1115 }
1116
1117 #[test]
1118 fn unknown_user_event_kind_should_be_deserialized() {
1119 let expected = UserEventKind::Unknown("this is some unknown value".to_owned());
1120
1121 let deserialized_user_event_kind =
1122 serde_json::from_str::<UserEventKind>(r#""this is some unknown value""#).unwrap();
1123
1124 assert_eq!(deserialized_user_event_kind, expected);
1125 }
1126
1127 #[test]
1128 fn user_events_request_should_have_proper_contract() {
1129 let request_payload = UserEventsRequestPayload {
1130 api_key: &API_KEY,
1131 id: 0,
1132 filter: UserEventFilter::All,
1133 };
1134 validate_contract(
1135 USER_EVENTS_PATH,
1136 request_payload,
1137 "tests/resources/responses/user_events.json",
1138 |request_payload, filen_settings| user_events_request(&request_payload, &filen_settings),
1139 );
1140 }
1141
1142 #[cfg(feature = "async")]
1143 #[tokio::test]
1144 async fn user_events_request_async_should_have_proper_contract() {
1145 let request_payload = UserEventsRequestPayload {
1146 api_key: &API_KEY,
1147 id: 0,
1148 filter: UserEventFilter::All,
1149 };
1150 validate_contract_async(
1151 USER_EVENTS_PATH,
1152 request_payload,
1153 "tests/resources/responses/user_events.json",
1154 |request_payload, filen_settings| async move {
1155 user_events_request_async(&request_payload, &filen_settings).await
1156 },
1157 )
1158 .await;
1159 }
1160
1161 #[test]
1162 fn user_events_get_request_should_have_proper_contract() {
1163 let request_payload = UserEventsGetRequestPayload {
1164 api_key: &API_KEY,
1165 uuid: Uuid::nil(),
1166 };
1167 validate_contract(
1168 USER_EVENTS_GET_PATH,
1169 request_payload,
1170 "tests/resources/responses/user_events_get.json",
1171 |request_payload, filen_settings| user_events_get_request(&request_payload, &filen_settings),
1172 );
1173 }
1174
1175 #[cfg(feature = "async")]
1176 #[tokio::test]
1177 async fn user_events_get_request_async_should_have_proper_contract() {
1178 let request_payload = UserEventsGetRequestPayload {
1179 api_key: &API_KEY,
1180 uuid: Uuid::nil(),
1181 };
1182 validate_contract_async(
1183 USER_EVENTS_GET_PATH,
1184 request_payload,
1185 "tests/resources/responses/user_events_get.json",
1186 |request_payload, filen_settings| async move {
1187 user_events_get_request_async(&request_payload, &filen_settings).await
1188 },
1189 )
1190 .await;
1191 }
1192}