Skip to main content

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}