Skip to main content

winterbaume_chatbot/
state.rs

1use std::collections::HashMap;
2
3use uuid::Uuid;
4
5use crate::types::{ChimeConfig, SlackConfig, TeamsConfig};
6
7#[derive(Debug, Default)]
8pub struct ChatbotState {
9    pub slack_configs: HashMap<String, SlackConfig>,
10    pub chime_configs: HashMap<String, ChimeConfig>,
11    pub teams_configs: HashMap<String, TeamsConfig>,
12}
13
14#[derive(Debug, thiserror::Error)]
15pub enum ChatbotError {
16    #[error("Configuration not found: {0}")]
17    ConfigurationNotFound(String),
18    #[error("Resource not found: {0}")]
19    ResourceNotFound(String),
20}
21
22impl ChatbotState {
23    #[allow(clippy::too_many_arguments)]
24    pub fn create_slack_channel_configuration(
25        &mut self,
26        account_id: &str,
27        region: &str,
28        configuration_name: &str,
29        slack_team_id: &str,
30        slack_channel_id: &str,
31        slack_channel_name: Option<String>,
32        iam_role_arn: &str,
33        sns_topic_arns: Vec<String>,
34        logging_level: Option<String>,
35        guardrail_policy_arns: Vec<String>,
36        user_authorization_required: Option<bool>,
37        tags: HashMap<String, String>,
38    ) -> Result<SlackConfig, ChatbotError> {
39        let arn = format!(
40            "arn:aws:chatbot:{}:{}:chat-configuration/slack-channel/{}",
41            region, account_id, configuration_name
42        );
43        let config = SlackConfig {
44            arn: arn.clone(),
45            configuration_name: configuration_name.to_string(),
46            slack_team_id: slack_team_id.to_string(),
47            slack_channel_id: slack_channel_id.to_string(),
48            slack_channel_name,
49            iam_role_arn: iam_role_arn.to_string(),
50            sns_topic_arns,
51            logging_level,
52            guardrail_policy_arns,
53            user_authorization_required,
54            tags,
55        };
56        self.slack_configs.insert(arn, config.clone());
57        Ok(config)
58    }
59
60    pub fn describe_slack_channel_configurations(
61        &self,
62        filter_arn: Option<&str>,
63    ) -> Vec<&SlackConfig> {
64        if let Some(arn) = filter_arn {
65            self.slack_configs.get(arn).into_iter().collect()
66        } else {
67            self.slack_configs.values().collect()
68        }
69    }
70
71    #[allow(clippy::too_many_arguments)]
72    pub fn update_slack_channel_configuration(
73        &mut self,
74        chat_configuration_arn: &str,
75        slack_channel_id: &str,
76        slack_channel_name: Option<String>,
77        iam_role_arn: Option<String>,
78        sns_topic_arns: Option<Vec<String>>,
79        logging_level: Option<String>,
80        guardrail_policy_arns: Option<Vec<String>>,
81        user_authorization_required: Option<bool>,
82    ) -> Result<SlackConfig, ChatbotError> {
83        let config = self
84            .slack_configs
85            .get_mut(chat_configuration_arn)
86            .ok_or_else(|| {
87                ChatbotError::ConfigurationNotFound(chat_configuration_arn.to_string())
88            })?;
89        config.slack_channel_id = slack_channel_id.to_string();
90        if let Some(name) = slack_channel_name {
91            config.slack_channel_name = Some(name);
92        }
93        if let Some(iam) = iam_role_arn {
94            config.iam_role_arn = iam;
95        }
96        if let Some(arns) = sns_topic_arns {
97            config.sns_topic_arns = arns;
98        }
99        if let Some(level) = logging_level {
100            config.logging_level = Some(level);
101        }
102        if let Some(arns) = guardrail_policy_arns {
103            config.guardrail_policy_arns = arns;
104        }
105        if let Some(auth) = user_authorization_required {
106            config.user_authorization_required = Some(auth);
107        }
108        Ok(config.clone())
109    }
110
111    pub fn delete_slack_channel_configuration(
112        &mut self,
113        chat_configuration_arn: &str,
114    ) -> Result<(), ChatbotError> {
115        if self.slack_configs.remove(chat_configuration_arn).is_none() {
116            return Err(ChatbotError::ConfigurationNotFound(
117                chat_configuration_arn.to_string(),
118            ));
119        }
120        Ok(())
121    }
122
123    #[allow(clippy::too_many_arguments)]
124    pub fn create_chime_webhook_configuration(
125        &mut self,
126        account_id: &str,
127        region: &str,
128        configuration_name: &str,
129        webhook_url: &str,
130        webhook_description: &str,
131        iam_role_arn: &str,
132        sns_topic_arns: Vec<String>,
133        logging_level: Option<String>,
134        tags: HashMap<String, String>,
135    ) -> Result<ChimeConfig, ChatbotError> {
136        let id = Uuid::new_v4().to_string();
137        let arn = format!(
138            "arn:aws:chatbot:{}:{}:chat-configuration/chime-webhook/{}",
139            region, account_id, id
140        );
141        let config = ChimeConfig {
142            arn: arn.clone(),
143            configuration_name: configuration_name.to_string(),
144            webhook_url: webhook_url.to_string(),
145            webhook_description: webhook_description.to_string(),
146            iam_role_arn: iam_role_arn.to_string(),
147            sns_topic_arns,
148            logging_level,
149            tags,
150        };
151        self.chime_configs.insert(arn, config.clone());
152        Ok(config)
153    }
154
155    pub fn describe_chime_webhook_configurations(
156        &self,
157        filter_arn: Option<&str>,
158    ) -> Vec<&ChimeConfig> {
159        if let Some(arn) = filter_arn {
160            self.chime_configs.get(arn).into_iter().collect()
161        } else {
162            self.chime_configs.values().collect()
163        }
164    }
165
166    pub fn delete_chime_webhook_configuration(
167        &mut self,
168        chat_configuration_arn: &str,
169    ) -> Result<(), ChatbotError> {
170        if self.chime_configs.remove(chat_configuration_arn).is_none() {
171            return Err(ChatbotError::ConfigurationNotFound(
172                chat_configuration_arn.to_string(),
173            ));
174        }
175        Ok(())
176    }
177
178    #[allow(clippy::too_many_arguments)]
179    pub fn create_microsoft_teams_channel_configuration(
180        &mut self,
181        account_id: &str,
182        region: &str,
183        configuration_name: &str,
184        team_id: &str,
185        team_name: Option<String>,
186        tenant_id: &str,
187        channel_id: &str,
188        channel_name: Option<String>,
189        iam_role_arn: &str,
190        sns_topic_arns: Vec<String>,
191        logging_level: Option<String>,
192        guardrail_policy_arns: Vec<String>,
193        user_authorization_required: Option<bool>,
194        tags: HashMap<String, String>,
195    ) -> Result<TeamsConfig, ChatbotError> {
196        let arn = format!(
197            "arn:aws:chatbot:{}:{}:chat-configuration/microsoft-teams-channel/{}",
198            region, account_id, configuration_name
199        );
200        let config = TeamsConfig {
201            arn: arn.clone(),
202            configuration_name: configuration_name.to_string(),
203            team_id: team_id.to_string(),
204            team_name,
205            tenant_id: tenant_id.to_string(),
206            channel_id: channel_id.to_string(),
207            channel_name,
208            iam_role_arn: iam_role_arn.to_string(),
209            sns_topic_arns,
210            logging_level,
211            guardrail_policy_arns,
212            user_authorization_required,
213            tags,
214        };
215        self.teams_configs.insert(arn, config.clone());
216        Ok(config)
217    }
218
219    pub fn list_microsoft_teams_channel_configurations(
220        &self,
221        filter_arn: Option<&str>,
222    ) -> Vec<&TeamsConfig> {
223        if let Some(arn) = filter_arn {
224            self.teams_configs.get(arn).into_iter().collect()
225        } else {
226            self.teams_configs.values().collect()
227        }
228    }
229
230    pub fn delete_microsoft_teams_channel_configuration(
231        &mut self,
232        chat_configuration_arn: &str,
233    ) -> Result<(), ChatbotError> {
234        if self.teams_configs.remove(chat_configuration_arn).is_none() {
235            return Err(ChatbotError::ConfigurationNotFound(
236                chat_configuration_arn.to_string(),
237            ));
238        }
239        Ok(())
240    }
241
242    // FIX(terraform-e2e): GetMicrosoftTeamsChannelConfiguration needed by terraform
243    // provider to read back Teams channel config after create.
244    pub fn get_microsoft_teams_channel_configuration(
245        &self,
246        chat_configuration_arn: &str,
247    ) -> Result<&TeamsConfig, ChatbotError> {
248        self.teams_configs
249            .get(chat_configuration_arn)
250            .ok_or_else(|| ChatbotError::ConfigurationNotFound(chat_configuration_arn.to_string()))
251    }
252
253    // FIX(terraform-e2e): UpdateMicrosoftTeamsChannelConfiguration needed by terraform
254    // provider to update Teams channel config in-place.
255    #[allow(clippy::too_many_arguments)]
256    pub fn update_microsoft_teams_channel_configuration(
257        &mut self,
258        chat_configuration_arn: &str,
259        channel_id: &str,
260        channel_name: Option<String>,
261        iam_role_arn: Option<String>,
262        sns_topic_arns: Option<Vec<String>>,
263        logging_level: Option<String>,
264        guardrail_policy_arns: Option<Vec<String>>,
265        user_authorization_required: Option<bool>,
266    ) -> Result<TeamsConfig, ChatbotError> {
267        let config = self
268            .teams_configs
269            .get_mut(chat_configuration_arn)
270            .ok_or_else(|| {
271                ChatbotError::ConfigurationNotFound(chat_configuration_arn.to_string())
272            })?;
273        config.channel_id = channel_id.to_string();
274        if let Some(name) = channel_name {
275            config.channel_name = Some(name);
276        }
277        if let Some(iam) = iam_role_arn {
278            config.iam_role_arn = iam;
279        }
280        if let Some(arns) = sns_topic_arns {
281            config.sns_topic_arns = arns;
282        }
283        if let Some(level) = logging_level {
284            config.logging_level = Some(level);
285        }
286        if let Some(arns) = guardrail_policy_arns {
287            config.guardrail_policy_arns = arns;
288        }
289        if let Some(auth) = user_authorization_required {
290            config.user_authorization_required = Some(auth);
291        }
292        Ok(config.clone())
293    }
294
295    // FIX(terraform-e2e): ListTagsForResource needed by terraform provider when tags
296    // are specified on chatbot resources.
297    pub fn list_tags_for_resource(
298        &self,
299        resource_arn: &str,
300    ) -> Result<HashMap<String, String>, ChatbotError> {
301        if let Some(config) = self.slack_configs.get(resource_arn) {
302            return Ok(config.tags.clone());
303        }
304        if let Some(config) = self.chime_configs.get(resource_arn) {
305            return Ok(config.tags.clone());
306        }
307        if let Some(config) = self.teams_configs.get(resource_arn) {
308            return Ok(config.tags.clone());
309        }
310        Err(ChatbotError::ResourceNotFound(resource_arn.to_string()))
311    }
312
313    // FIX(terraform-e2e): TagResource needed by terraform provider for tag management.
314    pub fn tag_resource(
315        &mut self,
316        resource_arn: &str,
317        tags: HashMap<String, String>,
318    ) -> Result<(), ChatbotError> {
319        if let Some(config) = self.slack_configs.get_mut(resource_arn) {
320            config.tags.extend(tags);
321            return Ok(());
322        }
323        if let Some(config) = self.chime_configs.get_mut(resource_arn) {
324            config.tags.extend(tags);
325            return Ok(());
326        }
327        if let Some(config) = self.teams_configs.get_mut(resource_arn) {
328            config.tags.extend(tags);
329            return Ok(());
330        }
331        Err(ChatbotError::ResourceNotFound(resource_arn.to_string()))
332    }
333
334    // FIX(terraform-e2e): UntagResource needed by terraform provider for tag management.
335    pub fn untag_resource(
336        &mut self,
337        resource_arn: &str,
338        tag_keys: &[String],
339    ) -> Result<(), ChatbotError> {
340        if let Some(config) = self.slack_configs.get_mut(resource_arn) {
341            for key in tag_keys {
342                config.tags.remove(key);
343            }
344            return Ok(());
345        }
346        if let Some(config) = self.chime_configs.get_mut(resource_arn) {
347            for key in tag_keys {
348                config.tags.remove(key);
349            }
350            return Ok(());
351        }
352        if let Some(config) = self.teams_configs.get_mut(resource_arn) {
353            for key in tag_keys {
354                config.tags.remove(key);
355            }
356            return Ok(());
357        }
358        Err(ChatbotError::ResourceNotFound(resource_arn.to_string()))
359    }
360}