shopify_sdk/auth/associated_user.rs
1//! Associated user types for Shopify API online sessions.
2//!
3//! This module provides the [`AssociatedUser`] type for storing user information
4//! associated with online (user-specific) sessions.
5//!
6//! # Overview
7//!
8//! When a Shopify app uses online access tokens, the OAuth response includes
9//! information about the user who authorized the app. This user information
10//! is stored in the `AssociatedUser` struct.
11//!
12//! # Example
13//!
14//! ```rust
15//! use shopify_sdk::AssociatedUser;
16//!
17//! let user = AssociatedUser::new(
18//! 12345,
19//! "Jane".to_string(),
20//! "Doe".to_string(),
21//! "jane@example.com".to_string(),
22//! true, // email_verified
23//! true, // account_owner
24//! "en".to_string(),
25//! false, // collaborator
26//! );
27//!
28//! assert_eq!(user.id, 12345);
29//! assert_eq!(user.email, "jane@example.com");
30//! ```
31
32use serde::{Deserialize, Serialize};
33
34/// Represents a Shopify user associated with an online session.
35///
36/// This struct holds information about the user who authorized an app
37/// during the OAuth flow when using online access tokens.
38///
39/// # Thread Safety
40///
41/// `AssociatedUser` is `Send + Sync`, making it safe to share across threads.
42///
43/// # Serialization
44///
45/// The struct derives `Serialize` and `Deserialize` for easy storage and
46/// transmission in JSON format.
47///
48/// # Example
49///
50/// ```rust
51/// use shopify_sdk::AssociatedUser;
52///
53/// let user = AssociatedUser::new(
54/// 12345,
55/// "Jane".to_string(),
56/// "Doe".to_string(),
57/// "jane@example.com".to_string(),
58/// true,
59/// false,
60/// "en".to_string(),
61/// false,
62/// );
63///
64/// // Serialize to JSON
65/// let json = serde_json::to_string(&user).unwrap();
66/// assert!(json.contains("12345"));
67///
68/// // Deserialize from JSON
69/// let restored: AssociatedUser = serde_json::from_str(&json).unwrap();
70/// assert_eq!(user, restored);
71/// ```
72#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
73pub struct AssociatedUser {
74 /// The Shopify user ID (numeric).
75 pub id: u64,
76
77 /// The user's first name.
78 pub first_name: String,
79
80 /// The user's last name.
81 pub last_name: String,
82
83 /// The user's email address.
84 pub email: String,
85
86 /// Whether the user's email has been verified.
87 pub email_verified: bool,
88
89 /// Whether the user is the account owner.
90 pub account_owner: bool,
91
92 /// The user's locale preference (e.g., "en", "fr").
93 pub locale: String,
94
95 /// Whether the user is a collaborator.
96 pub collaborator: bool,
97}
98
99impl AssociatedUser {
100 /// Creates a new `AssociatedUser` with all required fields.
101 ///
102 /// # Arguments
103 ///
104 /// * `id` - The Shopify user ID
105 /// * `first_name` - The user's first name
106 /// * `last_name` - The user's last name
107 /// * `email` - The user's email address
108 /// * `email_verified` - Whether the email has been verified
109 /// * `account_owner` - Whether the user is the account owner
110 /// * `locale` - The user's locale preference
111 /// * `collaborator` - Whether the user is a collaborator
112 ///
113 /// # Example
114 ///
115 /// ```rust
116 /// use shopify_sdk::AssociatedUser;
117 ///
118 /// let user = AssociatedUser::new(
119 /// 12345,
120 /// "Jane".to_string(),
121 /// "Doe".to_string(),
122 /// "jane@example.com".to_string(),
123 /// true,
124 /// true,
125 /// "en".to_string(),
126 /// false,
127 /// );
128 /// ```
129 #[must_use]
130 #[allow(clippy::too_many_arguments)]
131 pub const fn new(
132 id: u64,
133 first_name: String,
134 last_name: String,
135 email: String,
136 email_verified: bool,
137 account_owner: bool,
138 locale: String,
139 collaborator: bool,
140 ) -> Self {
141 Self {
142 id,
143 first_name,
144 last_name,
145 email,
146 email_verified,
147 account_owner,
148 locale,
149 collaborator,
150 }
151 }
152}
153
154// Verify AssociatedUser is Send + Sync at compile time
155const _: fn() = || {
156 const fn assert_send_sync<T: Send + Sync>() {}
157 assert_send_sync::<AssociatedUser>();
158};
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 fn sample_user() -> AssociatedUser {
165 AssociatedUser::new(
166 12345,
167 "Jane".to_string(),
168 "Doe".to_string(),
169 "jane@example.com".to_string(),
170 true,
171 true,
172 "en".to_string(),
173 false,
174 )
175 }
176
177 #[test]
178 fn test_associated_user_creation_with_all_fields() {
179 let user = sample_user();
180
181 assert_eq!(user.id, 12345);
182 assert_eq!(user.first_name, "Jane");
183 assert_eq!(user.last_name, "Doe");
184 assert_eq!(user.email, "jane@example.com");
185 assert!(user.email_verified);
186 assert!(user.account_owner);
187 assert_eq!(user.locale, "en");
188 assert!(!user.collaborator);
189 }
190
191 #[test]
192 fn test_associated_user_serialization_to_json() {
193 let user = sample_user();
194 let json = serde_json::to_string(&user).unwrap();
195
196 assert!(json.contains("12345"));
197 assert!(json.contains("Jane"));
198 assert!(json.contains("Doe"));
199 assert!(json.contains("jane@example.com"));
200 assert!(json.contains("email_verified"));
201 assert!(json.contains("account_owner"));
202 assert!(json.contains("en"));
203 assert!(json.contains("collaborator"));
204 }
205
206 #[test]
207 fn test_associated_user_deserialization_from_json() {
208 let json = r#"{
209 "id": 67890,
210 "first_name": "John",
211 "last_name": "Smith",
212 "email": "john@example.com",
213 "email_verified": false,
214 "account_owner": false,
215 "locale": "fr",
216 "collaborator": true
217 }"#;
218
219 let user: AssociatedUser = serde_json::from_str(json).unwrap();
220
221 assert_eq!(user.id, 67890);
222 assert_eq!(user.first_name, "John");
223 assert_eq!(user.last_name, "Smith");
224 assert_eq!(user.email, "john@example.com");
225 assert!(!user.email_verified);
226 assert!(!user.account_owner);
227 assert_eq!(user.locale, "fr");
228 assert!(user.collaborator);
229 }
230
231 #[test]
232 fn test_associated_user_equality_comparison() {
233 let user1 = sample_user();
234 let user2 = sample_user();
235
236 assert_eq!(user1, user2);
237
238 // Different user should not be equal
239 let user3 = AssociatedUser::new(
240 99999,
241 "Jane".to_string(),
242 "Doe".to_string(),
243 "jane@example.com".to_string(),
244 true,
245 true,
246 "en".to_string(),
247 false,
248 );
249
250 assert_ne!(user1, user3);
251 }
252
253 #[test]
254 fn test_associated_user_clone_preserves_all_fields() {
255 let user = sample_user();
256 let cloned = user.clone();
257
258 assert_eq!(user.id, cloned.id);
259 assert_eq!(user.first_name, cloned.first_name);
260 assert_eq!(user.last_name, cloned.last_name);
261 assert_eq!(user.email, cloned.email);
262 assert_eq!(user.email_verified, cloned.email_verified);
263 assert_eq!(user.account_owner, cloned.account_owner);
264 assert_eq!(user.locale, cloned.locale);
265 assert_eq!(user.collaborator, cloned.collaborator);
266 assert_eq!(user, cloned);
267 }
268
269 #[test]
270 fn test_associated_user_is_send_sync() {
271 fn assert_send_sync<T: Send + Sync>() {}
272 assert_send_sync::<AssociatedUser>();
273 }
274}