Skip to main content

winterbaume_chatbot/
views.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
5
6use crate::handlers::ChatbotService;
7use crate::state::ChatbotState;
8use crate::types::{ChimeConfig, SlackConfig, TeamsConfig};
9
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct ChatbotStateView {
12    #[serde(default)]
13    pub slack_configs: HashMap<String, SlackConfigView>,
14    #[serde(default)]
15    pub chime_configs: HashMap<String, ChimeConfigView>,
16    #[serde(default)]
17    pub teams_configs: HashMap<String, TeamsConfigView>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SlackConfigView {
22    pub arn: String,
23    pub configuration_name: String,
24    pub slack_team_id: String,
25    pub slack_channel_id: String,
26    pub slack_channel_name: Option<String>,
27    pub iam_role_arn: String,
28    pub sns_topic_arns: Vec<String>,
29    pub logging_level: Option<String>,
30    pub guardrail_policy_arns: Vec<String>,
31    pub user_authorization_required: Option<bool>,
32    pub tags: HashMap<String, String>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ChimeConfigView {
37    pub arn: String,
38    pub configuration_name: String,
39    pub webhook_url: String,
40    pub webhook_description: String,
41    pub iam_role_arn: String,
42    pub sns_topic_arns: Vec<String>,
43    pub logging_level: Option<String>,
44    pub tags: HashMap<String, String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct TeamsConfigView {
49    pub arn: String,
50    pub configuration_name: String,
51    pub team_id: String,
52    pub team_name: Option<String>,
53    pub tenant_id: String,
54    pub channel_id: String,
55    pub channel_name: Option<String>,
56    pub iam_role_arn: String,
57    pub sns_topic_arns: Vec<String>,
58    pub logging_level: Option<String>,
59    pub guardrail_policy_arns: Vec<String>,
60    pub user_authorization_required: Option<bool>,
61    pub tags: HashMap<String, String>,
62}
63
64impl From<&SlackConfig> for SlackConfigView {
65    fn from(c: &SlackConfig) -> Self {
66        Self {
67            arn: c.arn.clone(),
68            configuration_name: c.configuration_name.clone(),
69            slack_team_id: c.slack_team_id.clone(),
70            slack_channel_id: c.slack_channel_id.clone(),
71            slack_channel_name: c.slack_channel_name.clone(),
72            iam_role_arn: c.iam_role_arn.clone(),
73            sns_topic_arns: c.sns_topic_arns.clone(),
74            logging_level: c.logging_level.clone(),
75            guardrail_policy_arns: c.guardrail_policy_arns.clone(),
76            user_authorization_required: c.user_authorization_required,
77            tags: c.tags.clone(),
78        }
79    }
80}
81
82impl From<SlackConfigView> for SlackConfig {
83    fn from(v: SlackConfigView) -> Self {
84        Self {
85            arn: v.arn,
86            configuration_name: v.configuration_name,
87            slack_team_id: v.slack_team_id,
88            slack_channel_id: v.slack_channel_id,
89            slack_channel_name: v.slack_channel_name,
90            iam_role_arn: v.iam_role_arn,
91            sns_topic_arns: v.sns_topic_arns,
92            logging_level: v.logging_level,
93            guardrail_policy_arns: v.guardrail_policy_arns,
94            user_authorization_required: v.user_authorization_required,
95            tags: v.tags,
96        }
97    }
98}
99
100impl From<&ChimeConfig> for ChimeConfigView {
101    fn from(c: &ChimeConfig) -> Self {
102        Self {
103            arn: c.arn.clone(),
104            configuration_name: c.configuration_name.clone(),
105            webhook_url: c.webhook_url.clone(),
106            webhook_description: c.webhook_description.clone(),
107            iam_role_arn: c.iam_role_arn.clone(),
108            sns_topic_arns: c.sns_topic_arns.clone(),
109            logging_level: c.logging_level.clone(),
110            tags: c.tags.clone(),
111        }
112    }
113}
114
115impl From<ChimeConfigView> for ChimeConfig {
116    fn from(v: ChimeConfigView) -> Self {
117        Self {
118            arn: v.arn,
119            configuration_name: v.configuration_name,
120            webhook_url: v.webhook_url,
121            webhook_description: v.webhook_description,
122            iam_role_arn: v.iam_role_arn,
123            sns_topic_arns: v.sns_topic_arns,
124            logging_level: v.logging_level,
125            tags: v.tags,
126        }
127    }
128}
129
130impl From<&TeamsConfig> for TeamsConfigView {
131    fn from(c: &TeamsConfig) -> Self {
132        Self {
133            arn: c.arn.clone(),
134            configuration_name: c.configuration_name.clone(),
135            team_id: c.team_id.clone(),
136            team_name: c.team_name.clone(),
137            tenant_id: c.tenant_id.clone(),
138            channel_id: c.channel_id.clone(),
139            channel_name: c.channel_name.clone(),
140            iam_role_arn: c.iam_role_arn.clone(),
141            sns_topic_arns: c.sns_topic_arns.clone(),
142            logging_level: c.logging_level.clone(),
143            guardrail_policy_arns: c.guardrail_policy_arns.clone(),
144            user_authorization_required: c.user_authorization_required,
145            tags: c.tags.clone(),
146        }
147    }
148}
149
150impl From<TeamsConfigView> for TeamsConfig {
151    fn from(v: TeamsConfigView) -> Self {
152        Self {
153            arn: v.arn,
154            configuration_name: v.configuration_name,
155            team_id: v.team_id,
156            team_name: v.team_name,
157            tenant_id: v.tenant_id,
158            channel_id: v.channel_id,
159            channel_name: v.channel_name,
160            iam_role_arn: v.iam_role_arn,
161            sns_topic_arns: v.sns_topic_arns,
162            logging_level: v.logging_level,
163            guardrail_policy_arns: v.guardrail_policy_arns,
164            user_authorization_required: v.user_authorization_required,
165            tags: v.tags,
166        }
167    }
168}
169
170impl From<&ChatbotState> for ChatbotStateView {
171    fn from(state: &ChatbotState) -> Self {
172        Self {
173            slack_configs: state
174                .slack_configs
175                .iter()
176                .map(|(k, v)| (k.clone(), SlackConfigView::from(v)))
177                .collect(),
178            chime_configs: state
179                .chime_configs
180                .iter()
181                .map(|(k, v)| (k.clone(), ChimeConfigView::from(v)))
182                .collect(),
183            teams_configs: state
184                .teams_configs
185                .iter()
186                .map(|(k, v)| (k.clone(), TeamsConfigView::from(v)))
187                .collect(),
188        }
189    }
190}
191
192impl From<ChatbotStateView> for ChatbotState {
193    fn from(view: ChatbotStateView) -> Self {
194        Self {
195            slack_configs: view
196                .slack_configs
197                .into_iter()
198                .map(|(k, v)| (k, SlackConfig::from(v)))
199                .collect(),
200            chime_configs: view
201                .chime_configs
202                .into_iter()
203                .map(|(k, v)| (k, ChimeConfig::from(v)))
204                .collect(),
205            teams_configs: view
206                .teams_configs
207                .into_iter()
208                .map(|(k, v)| (k, TeamsConfig::from(v)))
209                .collect(),
210        }
211    }
212}
213
214impl StatefulService for ChatbotService {
215    type StateView = ChatbotStateView;
216
217    async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
218        let state = self.state.get(account_id, region);
219        let guard = state.read().await;
220        ChatbotStateView::from(&*guard)
221    }
222
223    async fn restore(
224        &self,
225        account_id: &str,
226        region: &str,
227        view: Self::StateView,
228    ) -> Result<(), StateViewError> {
229        let state = self.state.get(account_id, region);
230        {
231            let mut guard = state.write().await;
232            *guard = ChatbotState::from(view);
233        }
234        self.notify_state_changed(account_id, region).await;
235        Ok(())
236    }
237
238    async fn merge(
239        &self,
240        account_id: &str,
241        region: &str,
242        view: Self::StateView,
243    ) -> Result<(), StateViewError> {
244        let state = self.state.get(account_id, region);
245        {
246            let mut guard = state.write().await;
247            for (k, v) in view.slack_configs {
248                guard.slack_configs.insert(k, SlackConfig::from(v));
249            }
250            for (k, v) in view.chime_configs {
251                guard.chime_configs.insert(k, ChimeConfig::from(v));
252            }
253            for (k, v) in view.teams_configs {
254                guard.teams_configs.insert(k, TeamsConfig::from(v));
255            }
256        }
257        self.notify_state_changed(account_id, region).await;
258        Ok(())
259    }
260
261    fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
262        &self.notifier
263    }
264}