vortex_sdk/
types.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// User type for JWT generation
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct User {
8    pub id: String,
9    pub email: String,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub admin_scopes: Option<Vec<String>>,
12}
13
14impl User {
15    pub fn new(id: &str, email: &str) -> Self {
16        Self {
17            id: id.to_string(),
18            email: email.to_string(),
19            admin_scopes: None,
20        }
21    }
22
23    pub fn with_admin_scopes(mut self, scopes: Vec<String>) -> Self {
24        self.admin_scopes = Some(scopes);
25        self
26    }
27}
28
29/// Identifier for a user (email, sms, etc.)
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Identifier {
32    #[serde(rename = "type")]
33    pub identifier_type: String,
34    pub value: String,
35}
36
37impl Identifier {
38    pub fn new(identifier_type: &str, value: &str) -> Self {
39        Self {
40            identifier_type: identifier_type.to_string(),
41            value: value.to_string(),
42        }
43    }
44}
45
46/// Group information for JWT generation (input)
47/// Supports both 'id' (legacy) and 'groupId' (preferred) for backward compatibility
48#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct Group {
51    #[serde(rename = "type")]
52    pub group_type: String,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub id: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub group_id: Option<String>,
57    pub name: String,
58}
59
60impl Group {
61    pub fn new(group_type: &str, name: &str) -> Self {
62        Self {
63            group_type: group_type.to_string(),
64            id: None,
65            group_id: None,
66            name: name.to_string(),
67        }
68    }
69
70    pub fn with_id(mut self, id: &str) -> Self {
71        self.id = Some(id.to_string());
72        self
73    }
74
75    pub fn with_group_id(mut self, group_id: &str) -> Self {
76        self.group_id = Some(group_id.to_string());
77        self
78    }
79}
80
81/// Invitation group from API responses
82/// This matches the MemberGroups table structure from the API
83#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct InvitationGroup {
86    /// Vortex internal UUID
87    pub id: String,
88    /// Vortex account ID
89    pub account_id: String,
90    /// Customer's group ID (the ID they provided to Vortex)
91    pub group_id: String,
92    /// Group type (e.g., "workspace", "team")
93    #[serde(rename = "type")]
94    pub group_type: String,
95    /// Group name
96    pub name: String,
97    /// ISO 8601 timestamp when the group was created
98    pub created_at: String,
99}
100
101/// Invitation target (email or sms) - DEPRECATED
102/// Use AcceptUser instead for accepting invitations
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct InvitationTarget {
105    #[serde(rename = "type")]
106    pub target_type: String,
107    pub value: String,
108}
109
110impl InvitationTarget {
111    pub fn new(target_type: &str, value: &str) -> Self {
112        Self {
113            target_type: target_type.to_string(),
114            value: value.to_string(),
115        }
116    }
117}
118
119/// User data for accepting invitations (preferred format)
120///
121/// At least one of email or phone must be provided.
122///
123/// # Example
124///
125/// ```
126/// use vortex_sdk::AcceptUser;
127///
128/// // With email only
129/// let user = AcceptUser::new().with_email("user@example.com");
130///
131/// // With email and name
132/// let user = AcceptUser::new()
133///     .with_email("user@example.com")
134///     .with_name("John Doe");
135///
136/// // With all fields
137/// let user = AcceptUser::new()
138///     .with_email("user@example.com")
139///     .with_phone("+1234567890")
140///     .with_name("John Doe");
141/// ```
142#[derive(Debug, Clone, Serialize, Deserialize, Default)]
143pub struct AcceptUser {
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub email: Option<String>,
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub phone: Option<String>,
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub name: Option<String>,
150}
151
152impl AcceptUser {
153    pub fn new() -> Self {
154        Self::default()
155    }
156
157    pub fn with_email(mut self, email: &str) -> Self {
158        self.email = Some(email.to_string());
159        self
160    }
161
162    pub fn with_phone(mut self, phone: &str) -> Self {
163        self.phone = Some(phone.to_string());
164        self
165    }
166
167    pub fn with_name(mut self, name: &str) -> Self {
168        self.name = Some(name.to_string());
169        self
170    }
171}
172
173/// Invitation acceptance information
174#[derive(Debug, Clone, Serialize, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct InvitationAcceptance {
177    pub id: Option<String>,
178    pub account_id: Option<String>,
179    pub project_id: Option<String>,
180    pub accepted_at: Option<String>,
181    pub target: Option<InvitationTarget>,
182}
183
184/// Full invitation details
185#[derive(Debug, Clone, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187pub struct Invitation {
188    #[serde(default)]
189    pub id: String,
190    #[serde(default)]
191    pub account_id: String,
192    #[serde(default)]
193    pub click_throughs: u32,
194    pub configuration_attributes: Option<HashMap<String, serde_json::Value>>,
195    pub attributes: Option<HashMap<String, serde_json::Value>>,
196    #[serde(default)]
197    pub created_at: String,
198    #[serde(default)]
199    pub deactivated: bool,
200    #[serde(default)]
201    pub delivery_count: u32,
202    #[serde(default)]
203    pub delivery_types: Vec<String>,
204    #[serde(default)]
205    pub foreign_creator_id: String,
206    #[serde(default)]
207    pub invitation_type: String,
208    pub modified_at: Option<String>,
209    #[serde(default)]
210    pub status: String,
211    #[serde(default)]
212    pub target: Vec<InvitationTarget>,
213    #[serde(default)]
214    pub views: u32,
215    #[serde(default)]
216    pub widget_configuration_id: String,
217    #[serde(default)]
218    pub project_id: String,
219    #[serde(default)]
220    pub groups: Vec<InvitationGroup>,
221    #[serde(default)]
222    pub accepts: Vec<InvitationAcceptance>,
223    pub expired: bool,
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub expires: Option<String>,
226}
227
228/// Response containing multiple invitations
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct InvitationsResponse {
231    pub invitations: Option<Vec<Invitation>>,
232}
233
234/// Accept invitation parameter - supports both new User format and legacy Target format
235#[derive(Debug, Clone)]
236pub enum AcceptInvitationParam {
237    /// New User format (preferred)
238    User(AcceptUser),
239    /// Legacy target format (deprecated)
240    Target(InvitationTarget),
241    /// Legacy multiple targets format (deprecated)
242    Targets(Vec<InvitationTarget>),
243}
244
245impl From<AcceptUser> for AcceptInvitationParam {
246    fn from(user: AcceptUser) -> Self {
247        AcceptInvitationParam::User(user)
248    }
249}
250
251impl From<InvitationTarget> for AcceptInvitationParam {
252    fn from(target: InvitationTarget) -> Self {
253        AcceptInvitationParam::Target(target)
254    }
255}
256
257impl From<Vec<InvitationTarget>> for AcceptInvitationParam {
258    fn from(targets: Vec<InvitationTarget>) -> Self {
259        AcceptInvitationParam::Targets(targets)
260    }
261}