Skip to main content

rust_filen/v1/
events.rs

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/// Type of an user event.
42#[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/// Determines which events to filter out.
107#[derive(Clone, Debug, Eq, Hash, PartialEq)]
108pub enum UserEventFilter {
109    /// All events are fine.
110    All,
111    /// Only specific event type is fine.
112    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/// Holds IP and User-Agent.
176#[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    /// General event for event infos containing only IP and user agent.
188    PlainUserEvent<UserFingerprint>
189);
190
191/// Generic file event data for a downloadable file.
192#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
193pub struct DownloadableFileEventInfo {
194    /// File ID; hyphenated lowercased UUID V4.
195    pub uuid: Uuid,
196
197    /// Filen file storage info.
198    #[serde(flatten)]
199    pub storage: FileStorageInfo,
200
201    /// File metadata.
202    pub metadata: String,
203
204    /// Random alphanumeric string associated with the file. Used for deleting and versioning.
205    pub rm: String,
206
207    /// File creation time, as Unix timestamp in seconds.
208    pub timestamp: u64,
209
210    /// ID of the folder which contains this file.
211    pub parent: Uuid,
212
213    /// Determines how file bytes should be encrypted/decrypted.
214    /// File is encrypted using roughly the same algorithm as metadata encryption,
215    /// use [crypto::encrypt_file_data] and [crypto::decrypt_file_data] for the task.
216    pub version: u32,
217
218    /// User's IP and User-Agent.
219    #[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/// Generic file event data.
243#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
244pub struct FileParentlessEventInfo {
245    /// File ID; hyphenated lowercased UUID V4.
246    pub uuid: Uuid,
247
248    /// File metadata.
249    pub metadata: String,
250
251    /// User's IP and User-Agent.
252    #[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    /// Folder ID; hyphenated lowercased UUID V4.
266    pub uuid: Uuid,
267
268    /// Folder name metadata.
269    #[serde(rename = "name")]
270    pub name_metadata: String,
271
272    /// Parent folder ID; hyphenated lowercased UUID V4.
273    pub parent: Uuid,
274
275    /// Folder creation time, as Unix timestamp in seconds.
276    pub timestamp: u64,
277
278    /// User's IP and User-Agent.
279    #[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/// Used for requests to `USER_EVENTS_PATH` endpoint.
297#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
298pub struct UserEventsRequestPayload<'user_events> {
299    /// User-associated Filen API key.
300    #[serde(rename = "apiKey")]
301    pub api_key: &'user_events SecUtf8,
302
303    pub id: u64,
304
305    /// Determines which events to return.
306    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    /// Folder ID; hyphenated lowercased UUID V4.
313    pub uuid: Uuid,
314
315    /// Folder name metadata.
316    #[serde(rename = "name")]
317    pub name_metadata: String,
318
319    /// Folder creation time, as Unix timestamp in seconds.
320    pub timestamp: u64,
321
322    /// User's IP and User-Agent.
323    #[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    /// Event emitted after a base folder was created.
342    BaseFolderCreatedUserEvent<BaseFolderCreatedEventInfo>
343);
344
345#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
346pub struct CodeRedeemedEventInfo {
347    /// Redeemed code.
348    pub code: String,
349
350    /// User's IP and User-Agent.
351    #[serde(flatten)]
352    pub fingerprint: UserFingerprint,
353}
354utils::display_from_json!(CodeRedeemedEventInfo);
355
356user_event_struct!(
357    /// Event emitted after user has redeemed a promocode.
358    CodeRedeemedUserEvent<CodeRedeemedEventInfo>
359);
360
361#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
362pub struct EmailChangeAttemptInfo {
363    /// New email.
364    pub email: String,
365
366    /// User's IP and User-Agent.
367    #[serde(flatten)]
368    pub fingerprint: UserFingerprint,
369}
370utils::display_from_json!(EmailChangeAttemptInfo);
371
372user_event_struct!(
373    /// Event emitted after email change was requested, but before the new email was confirmed.
374    EmailChangeAttemptUserEvent<EmailChangeAttemptInfo>
375);
376
377#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
378pub struct EmailChangedInfo {
379    /// New email.
380    pub email: String,
381
382    /// User's IP and User-Agent.
383    #[serde(flatten)]
384    pub fingerprint: UserFingerprint,
385}
386utils::display_from_json!(EmailChangedInfo);
387
388user_event_struct!(
389    /// Event emitted after the new email was confirmed.
390    EmailChangedUserEvent<EmailChangedInfo>
391);
392
393#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
394pub struct FileLinkEditedInfo {
395    /// File ID; hyphenated lowercased UUID V4.
396    pub uuid: Uuid,
397
398    /// Link ID; hyphenated lowercased UUID V4.
399    #[serde(rename = "linkUUID")]
400    pub link_uuid: Uuid,
401
402    /// File metadata.
403    pub metadata: String,
404
405    /// User's IP and User-Agent.
406    #[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    /// Event emitted after a file link was edited.
419    FileLinkEditedUserEvent<FileLinkEditedInfo>
420);
421
422user_event_struct!(
423    /// Event emitted after a file was moved.
424    FileMovedUserEvent<DownloadableFileEventInfo>
425);
426
427user_event_struct!(
428    /// Event emitted after a file was restored from 'trash'.
429    FileRestoredUserEvent<DownloadableFileEventInfo>
430);
431
432#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
433pub struct FileRenamedInfo {
434    /// File ID; hyphenated lowercased UUID V4.
435    pub uuid: Uuid,
436
437    /// File metadata.
438    pub metadata: String,
439
440    /// Previous file metadata.
441    #[serde(rename = "oldMetadata")]
442    pub old_metadata: String,
443
444    /// User's IP and User-Agent.
445    #[serde(flatten)]
446    pub fingerprint: UserFingerprint,
447}
448utils::display_from_json!(FileRenamedInfo);
449
450impl FileRenamedInfo {
451    /// Decrypts old file metadata string.
452    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    /// Event emitted after a file was renamed.
465    FileRenamedUserEvent<FileRenamedInfo>
466);
467
468user_event_struct!(
469    /// Event emitted after a file was deleted.
470    FileRmUserEvent<FileParentlessEventInfo>
471);
472
473#[skip_serializing_none]
474#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
475pub struct FileSharedInfo {
476    /// File ID; hyphenated lowercased UUID V4.
477    pub uuid: Uuid,
478
479    /// Email of the user the file is shared with.
480    #[serde(rename = "receiverEmail")]
481    pub receiver_email: String,
482
483    /// File metadata.
484    pub metadata: String,
485
486    /// Parent folder ID; hyphenated lowercased UUID V4.
487    pub parent: Option<Uuid>,
488
489    /// User's IP and User-Agent.
490    #[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    /// Event emitted after a file was shared.
503    FileSharedUserEvent<FileSharedInfo>
504);
505
506user_event_struct!(
507    /// Event emitted after a file was versioned.
508    FileVersionedUserEvent<FileParentlessEventInfo>
509);
510
511user_event_struct!(
512    /// Event emitted after a file was moved to 'trash'.
513    FileTrashUserEvent<FileParentlessEventInfo>
514);
515
516user_event_struct!(
517    /// Event emitted after a file was uploaded.
518    FileUploadedUserEvent<DownloadableFileEventInfo>
519);
520
521#[skip_serializing_none]
522#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
523pub struct FolderColorChangedInfo {
524    /// Folder ID; hyphenated lowercased UUID V4.
525    pub uuid: Uuid,
526
527    /// Folder name metadata.
528    #[serde(rename = "name")]
529    pub name_metadata: String,
530
531    /// Folder color name.
532    pub color: LocationColor,
533
534    /// Previous folder color name. None means default yellow color.
535    #[serde(rename = "oldColor")]
536    pub old_color: Option<LocationColor>,
537
538    /// User's IP and User-Agent.
539    #[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    /// Event emitted after a folder color was changed.
558    FolderColorChangedUserEvent<FolderColorChangedInfo>
559);
560
561#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
562pub struct FolderLinkEditedInfo {
563    /// Folder ID; hyphenated lowercased UUID V4.
564    pub uuid: Uuid,
565
566    /// Link ID; hyphenated lowercased UUID V4.
567    #[serde(rename = "linkUUID")]
568    pub link_uuid: Uuid,
569
570    /// User's IP and User-Agent.
571    #[serde(flatten)]
572    pub fingerprint: UserFingerprint,
573}
574utils::display_from_json!(FolderLinkEditedInfo);
575
576user_event_struct!(
577    /// Event emitted after a folder link was edited.
578    FolderLinkEditedUserEvent<FolderLinkEditedInfo>
579);
580
581user_event_struct!(
582    /// Event emitted after a folder was moved.
583    FolderMovedUserEvent<FolderEventInfo>
584);
585
586#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
587pub struct FolderRenamedInfo {
588    /// Folder ID; hyphenated lowercased UUID V4.
589    pub uuid: Uuid,
590
591    /// Folder name metadata.
592    #[serde(rename = "name")]
593    pub name_metadata: String,
594
595    /// Previous folder name metadata.
596    #[serde(rename = "oldName")]
597    pub old_name_metadata: String,
598
599    /// User's IP and User-Agent.
600    #[serde(flatten)]
601    pub fingerprint: UserFingerprint,
602}
603utils::display_from_json!(FolderRenamedInfo);
604
605impl FolderRenamedInfo {
606    /// Decrypts old name metadata into a location name.
607    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    /// Event emitted after a folder was renamed.
626    FolderRenamedUserEvent<FolderRenamedInfo>
627);
628
629user_event_struct!(
630    /// Event emitted after a folder was restored.
631    FolderRestoredUserEvent<FolderEventInfo>
632);
633
634#[skip_serializing_none]
635#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
636pub struct FolderSharedEventInfo {
637    /// Folder ID; hyphenated lowercased UUID V4.
638    pub uuid: Uuid,
639
640    /// Email of the user the folder is shared with.
641    #[serde(rename = "receiverEmail")]
642    pub receiver_email: String,
643
644    /// Folder name metadata.
645    #[serde(rename = "name")]
646    pub name_metadata: String,
647
648    /// Parent folder ID; hyphenated lowercased UUID V4.
649    pub parent: Option<Uuid>,
650
651    /// User's IP and User-Agent.
652    #[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    /// Event emitted after a folder was shared.
671    FolderSharedUserEvent<FolderSharedEventInfo>
672);
673
674#[skip_serializing_none]
675#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
676pub struct FolderTrashEventInfo {
677    /// Folder ID; hyphenated lowercased UUID V4.
678    pub uuid: Uuid,
679
680    /// Folder name metadata.
681    #[serde(rename = "name")]
682    pub name_metadata: String,
683
684    /// Parent folder ID; hyphenated lowercased UUID V4.
685    pub parent: Option<Uuid>,
686
687    /// User's IP and User-Agent.
688    #[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    /// Event emitted after a folder was moved to 'trash'.
707    FolderTrashUserEvent<FolderTrashEventInfo>
708);
709
710#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
711pub struct ItemFavoriteEventInfo {
712    /// Item ID; hyphenated lowercased UUID V4.
713    pub uuid: Uuid,
714
715    /// What is favorited: a "file" or "folder"?
716    #[serde(rename = "type")]
717    pub item_type: ItemKind,
718
719    /// 0 means item was unfavorited, 1 means item was favorited.
720    #[serde(deserialize_with = "bool_from_int", serialize_with = "bool_to_int")]
721    pub value: bool,
722
723    /// File metadata.
724    pub metadata: String,
725
726    /// User's IP and User-Agent.
727    #[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    /// Event emitted after a file or folder favorite status was changed.
740    ItemFavoriteUserEvent<ItemFavoriteEventInfo>
741);
742
743#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
744pub struct RemovedSharedInItemsInfo {
745    /// Email of the user who shared this item.
746    #[serde(rename = "sharerEmail")]
747    pub sharer_email: String,
748
749    /// Removed items count.
750    pub count: u32,
751
752    /// User's IP and User-Agent.
753    #[serde(flatten)]
754    pub fingerprint: UserFingerprint,
755}
756utils::display_from_json!(RemovedSharedInItemsInfo);
757
758user_event_struct!(
759    /// Event emitted after some shared-in (from another user) items were removed.
760    RemovedSharedInItemsUserEvent<RemovedSharedInItemsInfo>
761);
762
763#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
764pub struct RemovedSharedOutItemsInfo {
765    /// Email of the user with whom the item is shared.
766    #[serde(rename = "receiverEmail")]
767    pub receiver_email: String,
768
769    /// Removed items count.
770    pub count: u32,
771
772    /// User's IP and User-Agent.
773    #[serde(flatten)]
774    pub fingerprint: UserFingerprint,
775}
776utils::display_from_json!(RemovedSharedOutItemsInfo);
777
778user_event_struct!(
779    /// Event emitted after some shared-out items were removed.
780    RemovedSharedOutItemsUserEvent<RemovedSharedOutItemsInfo>
781);
782
783user_event_struct!(
784    /// Event emitted after a subfolder was created.
785    SubFolderCreatedUserEvent<FolderEventInfo>
786);
787
788user_event_struct!(
789    /// Event emitted after a versioned file was restored.
790    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    // I know this deserialization is ugly, but I cannot think of better alternative
845    #[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/// Response data for `USER_EVENTS_PATH` endpoint.
962#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
963pub struct UserEventsResponseData {
964    /// List of filtered user events.
965    pub events: Vec<UserEvent>,
966
967    /// Filtered events count.
968    pub limit: u32,
969}
970utils::display_from_json!(UserEventsResponseData);
971
972response_payload!(
973    /// Response for `USER_EVENTS_PATH` endpoint.
974    UserEventsResponsePayload<UserEventsResponseData>
975);
976
977/// Used for requests to `USER_EVENTS_GET_PATH` endpoint.
978#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
979pub struct UserEventsGetRequestPayload<'user_events_get> {
980    /// User-associated Filen API key.
981    #[serde(rename = "apiKey")]
982    pub api_key: &'user_events_get SecUtf8,
983
984    /// Event UUID; hyphenated lowercased UUID V4.
985    pub uuid: Uuid,
986}
987utils::display_from_json_with_lifetime!('user_events_get, UserEventsGetRequestPayload);
988
989response_payload!(
990    /// Response for `USER_EVENTS_GET_PATH` endpoint.
991    UserEventsGetResponsePayload<UserEvent>
992);
993
994/// Calls `USER_EVENTS_PATH` endpoint.
995pub 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/// Calls `USER_EVENTS_PATH` endpoint asynchronously.
1003#[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
1013/// Calls `USER_EVENTS_GET_PATH` endpoint.
1014pub 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/// Calls `USER_EVENTS_GET_PATH` endpoint asynchronously.
1022#[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            // Event ID; Filen-incremented counter.
1042            pub id: u64,
1043
1044            /// Event UUID; hyphenated lowercased UUID V4.
1045            pub uuid: Uuid,
1046
1047            /// Event kind.
1048            #[serde(rename = "type")]
1049            pub event_type: UserEventKind,
1050
1051            /// Time when the event has occured, as Unix timestamp in seconds.
1052            pub timestamp: u64,
1053
1054            /// Data, associated with the event.
1055            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}