keygen_rs/
entitlement.rs

1#[cfg(feature = "token")]
2use crate::client::Client;
3#[cfg(feature = "token")]
4use crate::errors::Error;
5use crate::KeygenResponseData;
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct EntitlementAttributes {
12    pub name: Option<String>,
13    pub code: String,
14    pub metadata: Option<HashMap<String, serde_json::Value>>,
15    pub created: DateTime<Utc>,
16    pub updated: DateTime<Utc>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub(crate) struct EntitlementResponse {
21    pub data: KeygenResponseData<EntitlementAttributes>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub(crate) struct EntitlementsResponse {
26    pub data: Vec<KeygenResponseData<EntitlementAttributes>>,
27}
28
29#[cfg(feature = "token")]
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct CreateEntitlementRequest {
32    pub name: Option<String>,
33    pub code: String,
34    pub metadata: Option<HashMap<String, serde_json::Value>>,
35}
36
37#[cfg(feature = "token")]
38#[derive(Debug, Clone, Default, Serialize, Deserialize)]
39pub struct ListEntitlementsOptions {
40    pub limit: Option<u32>,
41    #[serde(rename = "page[size]")]
42    pub page_size: Option<u32>,
43    #[serde(rename = "page[number]")]
44    pub page_number: Option<u32>,
45}
46
47#[cfg(feature = "token")]
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct UpdateEntitlementRequest {
50    pub name: Option<String>,
51    pub code: Option<String>,
52    pub metadata: Option<HashMap<String, serde_json::Value>>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct Entitlement {
57    pub id: String,
58    pub name: Option<String>,
59    pub code: String,
60    pub metadata: Option<HashMap<String, serde_json::Value>>,
61    pub created: DateTime<Utc>,
62    pub updated: DateTime<Utc>,
63    pub account_id: Option<String>,
64}
65
66impl Entitlement {
67    pub(crate) fn from(data: KeygenResponseData<EntitlementAttributes>) -> Entitlement {
68        Entitlement {
69            id: data.id,
70            name: data.attributes.name,
71            code: data.attributes.code,
72            metadata: data.attributes.metadata,
73            created: data.attributes.created,
74            updated: data.attributes.updated,
75            account_id: data
76                .relationships
77                .account
78                .as_ref()
79                .and_then(|a| a.data.as_ref().map(|d| d.id.clone())),
80        }
81    }
82
83    /// Create a new entitlement
84    #[cfg(feature = "token")]
85    pub async fn create(request: CreateEntitlementRequest) -> Result<Entitlement, Error> {
86        let client = Client::default()?;
87
88        let mut attributes = serde_json::Map::new();
89        if let Some(name) = request.name {
90            attributes.insert("name".to_string(), serde_json::Value::String(name));
91        }
92        attributes.insert("code".to_string(), serde_json::Value::String(request.code));
93        if let Some(metadata) = request.metadata {
94            attributes.insert("metadata".to_string(), serde_json::to_value(metadata)?);
95        }
96
97        let body = serde_json::json!({
98            "data": {
99                "type": "entitlements",
100                "attributes": attributes
101            }
102        });
103
104        let response = client
105            .post("entitlements", Some(&body), None::<&()>)
106            .await?;
107        let entitlement_response: EntitlementResponse = serde_json::from_value(response.body)?;
108        Ok(Entitlement::from(entitlement_response.data))
109    }
110
111    /// List entitlements with optional pagination and filtering
112    #[cfg(feature = "token")]
113    pub async fn list(options: Option<ListEntitlementsOptions>) -> Result<Vec<Entitlement>, Error> {
114        let client = Client::default()?;
115        let response = client.get("entitlements", options.as_ref()).await?;
116        let entitlements_response: EntitlementsResponse = serde_json::from_value(response.body)?;
117        Ok(entitlements_response
118            .data
119            .into_iter()
120            .map(Entitlement::from)
121            .collect())
122    }
123
124    /// Get an entitlement by ID
125    #[cfg(feature = "token")]
126    pub async fn get(id: &str) -> Result<Entitlement, Error> {
127        let client = Client::default()?;
128        let endpoint = format!("entitlements/{id}");
129        let response = client.get(&endpoint, None::<&()>).await?;
130        let entitlement_response: EntitlementResponse = serde_json::from_value(response.body)?;
131        Ok(Entitlement::from(entitlement_response.data))
132    }
133
134    /// Update an entitlement
135    #[cfg(feature = "token")]
136    pub async fn update(&self, request: UpdateEntitlementRequest) -> Result<Entitlement, Error> {
137        let client = Client::default()?;
138        let endpoint = format!("entitlements/{}", self.id);
139
140        let mut attributes = serde_json::Map::new();
141        if let Some(name) = request.name {
142            attributes.insert("name".to_string(), serde_json::Value::String(name));
143        }
144        if let Some(code) = request.code {
145            attributes.insert("code".to_string(), serde_json::Value::String(code));
146        }
147        if let Some(metadata) = request.metadata {
148            attributes.insert("metadata".to_string(), serde_json::to_value(metadata)?);
149        }
150
151        let body = serde_json::json!({
152            "data": {
153                "type": "entitlements",
154                "attributes": attributes
155            }
156        });
157
158        let response = client.patch(&endpoint, Some(&body), None::<&()>).await?;
159        let entitlement_response: EntitlementResponse = serde_json::from_value(response.body)?;
160        Ok(Entitlement::from(entitlement_response.data))
161    }
162
163    /// Delete an entitlement
164    #[cfg(feature = "token")]
165    pub async fn delete(&self) -> Result<(), Error> {
166        let client = Client::default()?;
167        let endpoint = format!("entitlements/{}", self.id);
168        client.delete::<(), ()>(&endpoint, None::<&()>).await?;
169        Ok(())
170    }
171}
172
173#[cfg(all(test, feature = "token"))]
174mod tests {
175    use super::*;
176    use crate::{
177        KeygenRelationship, KeygenRelationshipData, KeygenRelationships, KeygenResponseData,
178    };
179
180    #[test]
181    fn test_entitlement_account_relationship() {
182        let entitlement_data = KeygenResponseData {
183            id: "test-entitlement-id".to_string(),
184            r#type: "entitlements".to_string(),
185            attributes: EntitlementAttributes {
186                name: Some("Premium Feature".to_string()),
187                code: "premium".to_string(),
188                metadata: Some(HashMap::new()),
189                created: "2023-01-01T00:00:00Z".parse().unwrap(),
190                updated: "2023-01-01T00:00:00Z".parse().unwrap(),
191            },
192            relationships: KeygenRelationships {
193                account: Some(KeygenRelationship {
194                    data: Some(KeygenRelationshipData {
195                        r#type: "accounts".to_string(),
196                        id: "test-account-id".to_string(),
197                    }),
198                    links: None,
199                }),
200                ..Default::default()
201            },
202        };
203
204        let entitlement = Entitlement::from(entitlement_data);
205
206        assert_eq!(entitlement.account_id, Some("test-account-id".to_string()));
207        assert_eq!(entitlement.id, "test-entitlement-id");
208        assert_eq!(entitlement.name, Some("Premium Feature".to_string()));
209        assert_eq!(entitlement.code, "premium");
210    }
211
212    #[test]
213    fn test_entitlement_without_account_relationship() {
214        let entitlement_data = KeygenResponseData {
215            id: "test-entitlement-id".to_string(),
216            r#type: "entitlements".to_string(),
217            attributes: EntitlementAttributes {
218                name: None,
219                code: "basic".to_string(),
220                metadata: None,
221                created: "2023-01-01T00:00:00Z".parse().unwrap(),
222                updated: "2023-01-01T00:00:00Z".parse().unwrap(),
223            },
224            relationships: KeygenRelationships::default(),
225        };
226
227        let entitlement = Entitlement::from(entitlement_data);
228
229        assert_eq!(entitlement.account_id, None);
230        assert_eq!(entitlement.name, None);
231        assert_eq!(entitlement.metadata, None);
232    }
233
234    #[test]
235    fn test_create_entitlement_request_serialization() {
236        let mut metadata = HashMap::new();
237        metadata.insert(
238            "feature_level".to_string(),
239            serde_json::Value::String("premium".to_string()),
240        );
241
242        let request = CreateEntitlementRequest {
243            name: Some("Advanced Features".to_string()),
244            code: "advanced".to_string(),
245            metadata: Some(metadata),
246        };
247
248        let serialized = serde_json::to_string(&request).unwrap();
249        assert!(serialized.contains("\"code\":\"advanced\""));
250        assert!(serialized.contains("\"name\":\"Advanced Features\""));
251        assert!(serialized.contains("\"metadata\""));
252    }
253
254    #[test]
255    fn test_list_entitlements_options_serialization() {
256        let options = ListEntitlementsOptions {
257            limit: Some(10),
258            page_size: Some(5),
259            page_number: Some(2),
260        };
261
262        let serialized = serde_json::to_string(&options).unwrap();
263        assert!(serialized.contains("\"limit\":10"));
264        assert!(serialized.contains("\"page[size]\":5"));
265        assert!(serialized.contains("\"page[number]\":2"));
266    }
267}