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