nylas_types/
grant.rs

1//! Grant types for Nylas API v3.
2//!
3//! Grants represent authenticated connections to email and calendar providers.
4
5use serde::{Deserialize, Serialize};
6
7use crate::common::{GrantId, Provider};
8
9/// Grant status.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum GrantStatus {
13    /// Grant is valid and active.
14    Valid,
15    /// Grant is invalid or expired.
16    Invalid,
17}
18
19/// Grant model.
20///
21/// Represents a Nylas grant which provides access to a user's account.
22///
23/// # Example
24///
25/// ```
26/// # use nylas_types::{Grant, GrantId, Provider, GrantStatus};
27/// let grant = Grant {
28///     id: GrantId::new("grant_123"),
29///     provider: Provider::Google,
30///     grant_status: Some(GrantStatus::Valid),
31///     email: Some("user@example.com".to_string()),
32///     scope: Some(vec!["https://www.googleapis.com/auth/gmail.readonly".to_string()]),
33///     user_timezone: None,
34///     created_at: Some(1234567890),
35///     updated_at: Some(1234567890),
36///     provider_user_id: None,
37///     ip: None,
38///     state: None,
39///     user_agent: None,
40///     settings: None,
41///     metadata: None,
42/// };
43/// ```
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct Grant {
46    /// Unique identifier for the grant.
47    pub id: GrantId,
48
49    /// Provider for this grant.
50    pub provider: Provider,
51
52    /// Grant status.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub grant_status: Option<GrantStatus>,
55
56    /// Email address associated with this grant.
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub email: Option<String>,
59
60    /// OAuth scopes granted to this connection.
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub scope: Option<Vec<String>>,
63
64    /// User's timezone.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub user_timezone: Option<String>,
67
68    /// Created timestamp (Unix time).
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub created_at: Option<i64>,
71
72    /// Updated timestamp (Unix time).
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub updated_at: Option<i64>,
75
76    /// Provider-specific user identifier.
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub provider_user_id: Option<String>,
79
80    /// IP address from the authentication request.
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub ip: Option<String>,
83
84    /// OAuth state parameter.
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub state: Option<String>,
87
88    /// User agent from the authentication request.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub user_agent: Option<String>,
91
92    /// Provider-specific settings.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub settings: Option<serde_json::Value>,
95
96    /// Custom metadata (key-value pairs).
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub metadata: Option<serde_json::Value>,
99}
100
101/// Request to create a new grant.
102///
103/// Note: Grant creation typically happens through OAuth flow,
104/// not directly via API. This is for advanced use cases.
105#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct CreateGrantRequest {
107    /// Provider type.
108    pub provider: Provider,
109
110    /// Provider-specific settings (e.g., IMAP credentials).
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub settings: Option<serde_json::Value>,
113
114    /// OAuth scopes to request.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub scope: Option<Vec<String>>,
117
118    /// OAuth state parameter.
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub state: Option<String>,
121
122    /// Custom metadata.
123    #[serde(skip_serializing_if = "Option::is_none")]
124    pub metadata: Option<serde_json::Value>,
125}
126
127impl CreateGrantRequest {
128    /// Create a builder for CreateGrantRequest.
129    pub fn builder(provider: Provider) -> CreateGrantRequestBuilder {
130        CreateGrantRequestBuilder::new(provider)
131    }
132}
133
134/// Builder for CreateGrantRequest.
135#[derive(Debug, Clone)]
136pub struct CreateGrantRequestBuilder {
137    provider: Provider,
138    settings: Option<serde_json::Value>,
139    scope: Option<Vec<String>>,
140    state: Option<String>,
141    metadata: Option<serde_json::Value>,
142}
143
144impl CreateGrantRequestBuilder {
145    /// Create a new builder.
146    pub fn new(provider: Provider) -> Self {
147        Self {
148            provider,
149            settings: None,
150            scope: None,
151            state: None,
152            metadata: None,
153        }
154    }
155
156    /// Set provider-specific settings.
157    pub fn settings(mut self, settings: serde_json::Value) -> Self {
158        self.settings = Some(settings);
159        self
160    }
161
162    /// Set OAuth scopes.
163    pub fn scope(mut self, scope: Vec<String>) -> Self {
164        self.scope = Some(scope);
165        self
166    }
167
168    /// Set OAuth state parameter.
169    pub fn state(mut self, state: impl Into<String>) -> Self {
170        self.state = Some(state.into());
171        self
172    }
173
174    /// Set custom metadata.
175    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
176        self.metadata = Some(metadata);
177        self
178    }
179
180    /// Build the CreateGrantRequest.
181    pub fn build(self) -> CreateGrantRequest {
182        CreateGrantRequest {
183            provider: self.provider,
184            settings: self.settings,
185            scope: self.scope,
186            state: self.state,
187            metadata: self.metadata,
188        }
189    }
190}
191
192/// Request to update a grant.
193#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
194pub struct UpdateGrantRequest {
195    /// Update provider-specific settings.
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub settings: Option<serde_json::Value>,
198
199    /// Update OAuth scopes.
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub scope: Option<Vec<String>>,
202
203    /// Update custom metadata.
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub metadata: Option<serde_json::Value>,
206}
207
208impl UpdateGrantRequest {
209    /// Create a builder for UpdateGrantRequest.
210    pub fn builder() -> UpdateGrantRequestBuilder {
211        UpdateGrantRequestBuilder::default()
212    }
213}
214
215/// Builder for UpdateGrantRequest.
216#[derive(Debug, Clone, Default)]
217pub struct UpdateGrantRequestBuilder {
218    settings: Option<serde_json::Value>,
219    scope: Option<Vec<String>>,
220    metadata: Option<serde_json::Value>,
221}
222
223impl UpdateGrantRequestBuilder {
224    /// Set provider-specific settings.
225    pub fn settings(mut self, settings: serde_json::Value) -> Self {
226        self.settings = Some(settings);
227        self
228    }
229
230    /// Set OAuth scopes.
231    pub fn scope(mut self, scope: Vec<String>) -> Self {
232        self.scope = Some(scope);
233        self
234    }
235
236    /// Set custom metadata.
237    pub fn metadata(mut self, metadata: serde_json::Value) -> Self {
238        self.metadata = Some(metadata);
239        self
240    }
241
242    /// Build the UpdateGrantRequest.
243    pub fn build(self) -> UpdateGrantRequest {
244        UpdateGrantRequest {
245            settings: self.settings,
246            scope: self.scope,
247            metadata: self.metadata,
248        }
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn test_grant_serialization() {
258        let grant = Grant {
259            id: GrantId::new("grant_123"),
260            provider: Provider::Google,
261            grant_status: Some(GrantStatus::Valid),
262            email: Some("user@example.com".to_string()),
263            scope: Some(vec![
264                "https://www.googleapis.com/auth/gmail.readonly".to_string()
265            ]),
266            user_timezone: None,
267            created_at: Some(1234567890),
268            updated_at: Some(1234567890),
269            provider_user_id: None,
270            ip: None,
271            state: None,
272            user_agent: None,
273            settings: None,
274            metadata: None,
275        };
276
277        let json = serde_json::to_string(&grant).unwrap();
278        assert!(json.contains("grant_123"));
279        assert!(json.contains("google"));
280        assert!(json.contains("user@example.com"));
281    }
282
283    #[test]
284    fn test_grant_deserialization() {
285        let json = r#"{
286            "id": "grant_123",
287            "provider": "google",
288            "grant_status": "valid",
289            "email": "user@example.com",
290            "scope": ["https://www.googleapis.com/auth/gmail.readonly"],
291            "created_at": 1234567890,
292            "updated_at": 1234567890
293        }"#;
294
295        let grant: Grant = serde_json::from_str(json).unwrap();
296        assert_eq!(grant.id.as_str(), "grant_123");
297        assert_eq!(grant.provider, Provider::Google);
298        assert_eq!(grant.grant_status, Some(GrantStatus::Valid));
299        assert_eq!(grant.email, Some("user@example.com".to_string()));
300    }
301
302    #[test]
303    fn test_grant_status_serialization() {
304        assert_eq!(
305            serde_json::to_string(&GrantStatus::Valid).unwrap(),
306            r#""valid""#
307        );
308        assert_eq!(
309            serde_json::to_string(&GrantStatus::Invalid).unwrap(),
310            r#""invalid""#
311        );
312    }
313
314    #[test]
315    fn test_create_grant_request_builder() {
316        let request = CreateGrantRequest::builder(Provider::Google)
317            .scope(vec![
318                "https://www.googleapis.com/auth/gmail.readonly".to_string()
319            ])
320            .state("test_state")
321            .build();
322
323        assert_eq!(request.provider, Provider::Google);
324        assert!(request.scope.is_some());
325        assert_eq!(request.state, Some("test_state".to_string()));
326        assert!(request.settings.is_none());
327        assert!(request.metadata.is_none());
328    }
329
330    #[test]
331    fn test_create_grant_request_with_metadata() {
332        let metadata = serde_json::json!({"team": "engineering"});
333        let request = CreateGrantRequest::builder(Provider::Microsoft)
334            .metadata(metadata.clone())
335            .build();
336
337        assert_eq!(request.provider, Provider::Microsoft);
338        assert_eq!(request.metadata, Some(metadata));
339    }
340
341    #[test]
342    fn test_update_grant_request_builder() {
343        let metadata = serde_json::json!({"updated": true});
344        let request = UpdateGrantRequest::builder()
345            .metadata(metadata.clone())
346            .scope(vec!["new_scope".to_string()])
347            .build();
348
349        assert_eq!(request.metadata, Some(metadata));
350        assert_eq!(request.scope, Some(vec!["new_scope".to_string()]));
351        assert!(request.settings.is_none());
352    }
353
354    #[test]
355    fn test_update_grant_request_default() {
356        let request = UpdateGrantRequest::default();
357        assert!(request.settings.is_none());
358        assert!(request.scope.is_none());
359        assert!(request.metadata.is_none());
360    }
361}