actor_core_client/
client.rs

1use anyhow::Result;
2use serde_json::{json, Value};
3
4use crate::drivers::TransportKind;
5use crate::encoding::EncodingKind;
6use crate::handle::{ActorHandle, ActorHandleInner};
7
8type ActorTags = Vec<(String, String)>;
9
10pub struct CreateRequestMetadata {
11    pub tags: ActorTags,
12    pub region: Option<String>,
13}
14
15impl Default for CreateRequestMetadata {
16    fn default() -> Self {
17        Self {
18            tags: vec![],
19            region: None,
20        }
21    }
22}
23
24pub struct PartialCreateRequestMetadata {
25    pub tags: Option<ActorTags>,
26    pub region: Option<String>,
27}
28
29pub struct GetWithIdOptions {
30    pub params: Option<serde_json::Value>,
31}
32
33impl Default for GetWithIdOptions {
34    fn default() -> Self {
35        Self { params: None }
36    }
37}
38
39pub struct GetOptions {
40    pub tags: Option<ActorTags>,
41    pub params: Option<serde_json::Value>,
42    pub no_create: Option<bool>,
43    pub create: Option<PartialCreateRequestMetadata>,
44}
45
46impl Default for GetOptions {
47    fn default() -> Self {
48        Self {
49            tags: None,
50            params: None,
51            no_create: None,
52            create: None,
53        }
54    }
55}
56
57pub struct CreateOptions {
58    pub params: Option<serde_json::Value>,
59    pub create: CreateRequestMetadata,
60}
61
62impl Default for CreateOptions {
63    fn default() -> Self {
64        Self {
65            params: None,
66            create: CreateRequestMetadata::default()
67        }
68    }
69}
70
71pub struct Client {
72    manager_endpoint: String,
73    encoding_kind: EncodingKind,
74    transport_kind: TransportKind,
75}
76
77impl Client {
78    pub fn new(
79        manager_endpoint: String,
80        transport_kind: TransportKind,
81        encoding_kind: EncodingKind,
82    ) -> Self {
83        Self {
84            manager_endpoint,
85            encoding_kind,
86            transport_kind,
87        }
88    }
89
90    async fn post_manager_endpoint(&self, path: &str, body: Value) -> Result<Value> {
91        let client = reqwest::Client::new();
92        let req = client.post(format!("{}{}", self.manager_endpoint, path));
93        let req = req.header("Content-Type", "application/json");
94        let req = req.body(serde_json::to_string(&body)?);
95        let res = req.send().await?;
96        let body = res.text().await?;
97
98        let body: Value = serde_json::from_str(&body)?;
99
100        Ok(body)
101    }
102
103    #[allow(dead_code)]
104    async fn get_manager_endpoint(&self, path: &str) -> Result<Value> {
105        let client = reqwest::Client::new();
106        let req = client.get(format!("{}{}", self.manager_endpoint, path));
107        let res = req.send().await?;
108        let body = res.text().await?;
109        let body: Value = serde_json::from_str(&body)?;
110
111        Ok(body)
112    }
113
114    pub async fn get(&self, name: &str, opts: GetOptions) -> Result<ActorHandle> {
115        // Convert tags to a map for JSON
116        let tags_map: serde_json::Map<String, Value> = opts.tags
117            .unwrap_or_default()
118            .into_iter()
119            .map(|(k, v)| (k, json!(v)))
120            .collect();
121            
122        // Build create object if no_create is false
123        let create = if !opts.no_create.unwrap_or(false) {
124            // Start with create options if provided
125            if let Some(create_opts) = &opts.create {
126                // Build tags map - use create.tags if provided, otherwise fall back to query tags
127                let create_tags = if let Some(tags) = &create_opts.tags {
128                    tags.iter()
129                        .map(|(k, v)| (k.clone(), json!(v.clone())))
130                        .collect()
131                } else {
132                    tags_map.clone()
133                };
134                
135                // Build create object with name, tags, and optional region
136                let mut create_obj = json!({
137                    "name": name,
138                    "tags": create_tags
139                });
140                
141                if let Some(region) = &create_opts.region {
142                    create_obj["region"] = json!(region.clone());
143                }
144                
145                Some(create_obj)
146            } else {
147                // Create with just the name and query tags
148                Some(json!({
149                    "name": name,
150                    "tags": tags_map
151                }))
152            }
153        } else {
154            None
155        };
156        
157        // Build the request body
158        let body = json!({
159            "query": {
160                "getOrCreateForTags": {
161                    "name": name,
162                    "tags": tags_map,
163                    "create": create
164                }
165            }
166        });
167        let res_json = self.post_manager_endpoint("/manager/actors", body).await?;
168        let Some(endpoint) = res_json["endpoint"].as_str() else {
169            return Err(anyhow::anyhow!(
170                "No endpoint returned. Request failed? {:?}",
171                res_json
172            ));
173        };
174
175        let handle = ActorHandleInner::new(
176            endpoint.to_string(),
177            self.transport_kind,
178            self.encoding_kind,
179            opts.params,
180        )?;
181        handle.start_connection().await;
182
183        Ok(handle)
184    }
185
186    pub async fn get_with_id(&self, actor_id: &str, opts: GetWithIdOptions) -> Result<ActorHandle> {
187        let body = json!({
188            "query": {
189                "getForId": {
190                    "actorId": actor_id,
191                }
192            },
193        });
194        let res_json = self.post_manager_endpoint("/manager/actors", body).await?;
195        let Some(endpoint) = res_json["endpoint"].as_str() else {
196            return Err(anyhow::anyhow!(
197                "No endpoint returned. Request failed? {:?}",
198                res_json
199            ));
200        };
201
202        let handle = ActorHandleInner::new(
203            endpoint.to_string(),
204            self.transport_kind,
205            self.encoding_kind,
206            opts.params,
207        )?;
208        handle.start_connection().await;
209
210        Ok(handle)
211    }
212
213    pub async fn create(&self, name: &str, opts: CreateOptions) -> Result<ActorHandle> {
214        let mut tag_map = serde_json::Map::new();
215
216        for (key, value) in opts.create.tags {
217            tag_map.insert(key, json!(value));
218        }
219
220        let mut req_body = serde_json::Map::new();
221        req_body.insert("name".to_string(), json!(name.to_string()));
222        req_body.insert("tags".to_string(), json!(tag_map));
223        if let Some(region) = opts.create.region {
224            req_body.insert("region".to_string(), json!(region));
225        }
226
227        let body = json!({
228            "query": {
229                "create": req_body
230            },
231        });
232        let res_json = self.post_manager_endpoint("/manager/actors", body).await?;
233        let Some(endpoint) = res_json["endpoint"].as_str() else {
234            return Err(anyhow::anyhow!(
235                "No endpoint returned. Request failed? {:?}",
236                res_json
237            ));
238        };
239
240        let handle = ActorHandleInner::new(
241            endpoint.to_string(),
242            self.transport_kind,
243            self.encoding_kind,
244            opts.params,
245        )?;
246        handle.start_connection().await;
247
248        Ok(handle)
249    }
250}