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