shared/domain/
admin.rs

1//! Types for admin routes.
2use crate::api::endpoints::PathPart;
3use crate::domain::billing::{Account, AccountUser, AdminSchool, SchoolNameId};
4use crate::domain::user::UserId;
5use crate::domain::{billing::SchoolId, ItemCount, Page, PageLimit};
6use chrono::Utc;
7use macros::make_path_parts;
8use serde::{Deserialize, Serialize};
9use strum_macros::{Display, EnumIter};
10
11make_path_parts!(AdminUserExportPath => "/v1/admin/export/users");
12/// Request to export data
13#[derive(Serialize, Deserialize, Debug, Clone)]
14pub struct AdminUserExportRequest {
15    ///
16    pub date_filter_type: DateFilterType,
17    /// Optionally the date to export data from
18    pub from_date: Option<chrono::DateTime<Utc>>,
19    /// Optionally the date to export data to
20    pub to_date: Option<chrono::DateTime<Utc>>,
21}
22
23make_path_parts!(AdminJigExportPath => "/v1/admin/export/jigs");
24
25make_path_parts!(AdminPlaylistExportPath => "/v1/admin/export/playlists");
26
27/// Type of filter to apply for the date ranges
28#[derive(Display, EnumIter, Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
29#[serde(rename_all = "lowercase")]
30pub enum DateFilterType {
31    /// Only filter on new records
32    #[strum(serialize = "Only new records")]
33    OnlyNew,
34    /// Only filter on updated records
35    #[strum(serialize = "Only updated records")]
36    OnlyUpdated,
37    /// Filter by either new or updated records
38    #[strum(serialize = "New or updated records")]
39    Either,
40}
41
42impl Default for DateFilterType {
43    fn default() -> Self {
44        Self::OnlyNew
45    }
46}
47
48make_path_parts!(AdminSchoolsPath => "/v1/admin/schools");
49
50/// Request to list school names
51#[derive(Default, Serialize, Deserialize, Debug, Clone)]
52pub struct SearchSchoolsParams {
53    /// String to search school names by
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub q: Option<String>,
56    /// If `Some` then whether to filter by verified or unverified, otherwise return all schools
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub verified: Option<bool>,
59    /// Current page of results
60    #[serde(default)]
61    pub page: Page,
62    /// Total schools per page to return
63    #[serde(default)]
64    pub page_limit: PageLimit,
65}
66
67/// List of school names and their associated schools
68#[derive(Serialize, Deserialize, Debug, Clone)]
69pub struct SearchSchoolsResponse {
70    /// List of schools
71    pub schools: Vec<AdminSchool>,
72    /// Count of pages
73    pub pages: ItemCount,
74    /// Total count of schools for this query
75    pub total_schools_count: ItemCount,
76}
77
78make_path_parts!(AdminSchoolAccountPath => "/v1/admin/schools/{}" => SchoolId);
79
80/// Request to create a new school account
81#[derive(Debug, Serialize, Deserialize, Clone)]
82pub struct GetAdminSchoolAccountResponse {
83    /// School name
84    pub school: AdminSchool,
85    /// Account associated with the school
86    pub account: Account,
87    /// School location
88    pub users: Vec<AccountUser>,
89}
90
91make_path_parts!(AdminVerifySchoolPath => "/v1/admin/schools/verify");
92
93/// Request to update verification of a `SchoolName`
94#[derive(Serialize, Deserialize, Debug, Clone)]
95pub struct VerifySchoolRequest {
96    /// The ID of the school to update verification
97    pub school_id: SchoolId,
98    /// Whether this school should be marked verified or not
99    pub verified: bool,
100}
101
102make_path_parts!(ImportSchoolNamesPath => "/v1/admin/import-school-names");
103
104make_path_parts!(InviteSchoolUsersPath => "/v1/admin/invite-users");
105
106/// Request to invite users to a school by ID. The data is a newline separated list
107/// of user emails.
108#[derive(Serialize, Deserialize, Debug, Clone)]
109pub struct InviteSchoolUsersRequest {
110    /// School ID to invite users to
111    pub school_id: SchoolId,
112    /// Newline-separated list of user emails
113    pub data: String,
114}
115
116/// Response holding list of failed emails and the reasons
117#[derive(Serialize, Deserialize, Debug, Clone)]
118pub struct InviteSchoolUsersResponse {
119    /// List of failed invites
120    pub failures: Vec<InviteSchoolUserFailure>,
121}
122
123/// Represents a failed invited user
124#[derive(Serialize, Deserialize, Debug, Clone)]
125pub struct InviteSchoolUserFailure {
126    /// The users email
127    pub email: String,
128    /// The reason the user could not be associated
129    pub reason: InviteFailedReason,
130}
131
132/// Possible invite failure reasons
133#[derive(Display, Serialize, Deserialize, Debug, Clone)]
134pub enum InviteFailedReason {
135    /// The user already has an individual account
136    #[strum(serialize = "Has individual account")]
137    HasIndividualAccount,
138    /// The user is already associated with another school
139    #[strum(serialize = "Associated with another school")]
140    AssociatedWithSchool,
141    /// The user could not be found (not registered yet)
142    #[strum(serialize = "Not found")]
143    UserNotFound,
144    /// The user hasn't completed setting up their profile
145    #[strum(serialize = "Incomplete profile")]
146    IncompleteProfile,
147}
148
149make_path_parts!(SchoolNamesPath => "/v1/admin/school-names");
150
151make_path_parts!(UpdateSchoolNamePath => "/v1/admin/school-names/{}" => SchoolNameId);
152
153make_path_parts!(SetInternalSchoolNamePath => "/v1/admin/schools/{}/school-name" => SchoolId);
154
155make_path_parts!(SetAccountTierOverridePath => "/v1/admin/users/{}/tier-override" => UserId);
156
157make_path_parts!(DeleteUserAccountPath => "/v1/admin/users/{}/account" => UserId);
158
159make_path_parts!(RemoveUserFromSchoolPath => "/v1/admin/schools/{}/users" => SchoolId);