metabase_api_rs/core/models/
dashboard.rs1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use super::common::{DashboardId, UserId};
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct Dashboard {
14 pub id: Option<DashboardId>,
16
17 pub name: String,
19
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub description: Option<String>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub collection_id: Option<i32>,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub creator_id: Option<UserId>,
31
32 #[serde(default, skip_serializing_if = "Vec::is_empty")]
34 pub parameters: Vec<DashboardParameter>,
35
36 #[serde(default, skip_serializing_if = "Vec::is_empty")]
38 pub cards: Vec<DashboardCard>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub created_at: Option<DateTime<Utc>>,
43
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub updated_at: Option<DateTime<Utc>>,
47
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub archived: Option<bool>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
55 pub cache_ttl: Option<i32>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub collection_position: Option<i32>,
60
61 #[serde(default)]
63 pub enable_embedding: bool,
64
65 #[serde(default)]
67 pub embedding_params: serde_json::Value,
68}
69
70#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub struct DashboardParameter {
73 pub id: String,
75
76 pub name: String,
78
79 pub slug: String,
81
82 #[serde(rename = "type")]
84 pub parameter_type: String,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub default: Option<serde_json::Value>,
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct DashboardCard {
94 pub id: i32,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub card_id: Option<i32>,
100
101 pub row: i32,
103 pub col: i32,
104 pub size_x: i32,
105 pub size_y: i32,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub visualization_settings: Option<serde_json::Value>,
110
111 #[serde(default, skip_serializing_if = "Vec::is_empty")]
113 pub parameter_mappings: Vec<ParameterMapping>,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub struct ParameterMapping {
119 pub parameter_id: String,
121
122 pub card_id: i32,
124
125 pub target: serde_json::Value,
127}
128
129#[derive(Debug, Clone, Serialize)]
131pub struct CreateDashboardRequest {
132 pub name: String,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub description: Option<String>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub collection_id: Option<i32>,
142
143 #[serde(default, skip_serializing_if = "Vec::is_empty")]
145 pub parameters: Vec<DashboardParameter>,
146}
147
148#[derive(Debug, Clone, Default, Serialize)]
150pub struct UpdateDashboardRequest {
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub name: Option<String>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub description: Option<String>,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub collection_id: Option<i32>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub archived: Option<bool>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub parameters: Option<Vec<DashboardParameter>>,
170}
171
172impl Dashboard {
173 pub fn builder(name: impl Into<String>) -> DashboardBuilder {
175 DashboardBuilder::new(name)
176 }
177}
178
179pub struct DashboardBuilder {
181 name: String,
182 description: Option<String>,
183 collection_id: Option<i32>,
184 parameters: Vec<DashboardParameter>,
185 cards: Vec<DashboardCard>,
186 cache_ttl: Option<i32>,
187 collection_position: Option<i32>,
188 enable_embedding: bool,
189 embedding_params: serde_json::Value,
190}
191
192impl DashboardBuilder {
193 pub fn new(name: impl Into<String>) -> Self {
195 Self {
196 name: name.into(),
197 description: None,
198 collection_id: None,
199 parameters: Vec::new(),
200 cards: Vec::new(),
201 cache_ttl: None,
202 collection_position: None,
203 enable_embedding: false,
204 embedding_params: serde_json::Value::Object(serde_json::Map::new()),
205 }
206 }
207
208 pub fn description(mut self, desc: impl Into<String>) -> Self {
210 self.description = Some(desc.into());
211 self
212 }
213
214 pub fn collection_id(mut self, id: i32) -> Self {
216 self.collection_id = Some(id);
217 self
218 }
219
220 pub fn add_parameter(mut self, param: DashboardParameter) -> Self {
222 self.parameters.push(param);
223 self
224 }
225
226 pub fn add_card(mut self, card: DashboardCard) -> Self {
228 self.cards.push(card);
229 self
230 }
231
232 pub fn cache_ttl(mut self, ttl: i32) -> Self {
234 self.cache_ttl = Some(ttl);
235 self
236 }
237
238 pub fn collection_position(mut self, position: i32) -> Self {
240 self.collection_position = Some(position);
241 self
242 }
243
244 pub fn enable_embedding(mut self, enabled: bool) -> Self {
246 self.enable_embedding = enabled;
247 self
248 }
249
250 pub fn embedding_params(mut self, params: serde_json::Value) -> Self {
252 self.embedding_params = params;
253 self
254 }
255
256 pub fn build(self) -> Dashboard {
258 Dashboard {
259 id: None, name: self.name,
261 description: self.description,
262 collection_id: self.collection_id,
263 creator_id: None,
264 parameters: self.parameters,
265 cards: self.cards,
266 created_at: None,
267 updated_at: None,
268 archived: Some(false),
269 cache_ttl: self.cache_ttl,
270 collection_position: self.collection_position,
271 enable_embedding: self.enable_embedding,
272 embedding_params: self.embedding_params,
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_dashboard_creation() {
283 let dashboard = Dashboard::builder("Sales Dashboard")
284 .description("Monthly sales metrics")
285 .collection_id(10)
286 .build();
287
288 assert_eq!(dashboard.name, "Sales Dashboard");
289 assert_eq!(
290 dashboard.description,
291 Some("Monthly sales metrics".to_string())
292 );
293 assert_eq!(dashboard.collection_id, Some(10));
294 assert_eq!(dashboard.archived, Some(false));
295 assert!(dashboard.parameters.is_empty());
296 assert!(dashboard.cards.is_empty());
297 }
298
299 #[test]
300 fn test_dashboard_with_parameters() {
301 let param = DashboardParameter {
302 id: "date_range".to_string(),
303 name: "Date Range".to_string(),
304 slug: "date_range".to_string(),
305 parameter_type: "date/range".to_string(),
306 default: None,
307 };
308
309 let dashboard = Dashboard::builder("Analytics Dashboard")
310 .add_parameter(param.clone())
311 .build();
312
313 assert_eq!(dashboard.parameters.len(), 1);
314 assert_eq!(dashboard.parameters[0].id, "date_range");
315 }
316
317 #[test]
318 fn test_dashboard_card() {
319 let card = DashboardCard {
320 id: 1,
321 card_id: Some(100),
322 row: 0,
323 col: 0,
324 size_x: 4,
325 size_y: 3,
326 visualization_settings: None,
327 parameter_mappings: Vec::new(),
328 };
329
330 assert_eq!(card.card_id, Some(100));
331 assert_eq!(card.size_x, 4);
332 assert_eq!(card.size_y, 3);
333 }
334
335 #[test]
336 fn test_create_dashboard_request() {
337 let request = CreateDashboardRequest {
338 name: "New Dashboard".to_string(),
339 description: Some("Test dashboard".to_string()),
340 collection_id: Some(5),
341 parameters: vec![],
342 };
343
344 assert_eq!(request.name, "New Dashboard");
345 assert_eq!(request.description, Some("Test dashboard".to_string()));
346 assert_eq!(request.collection_id, Some(5));
347 }
348
349 #[test]
350 fn test_update_dashboard_request() {
351 let request = UpdateDashboardRequest {
352 name: Some("Updated Name".to_string()),
353 archived: Some(true),
354 ..Default::default()
355 };
356
357 assert_eq!(request.name, Some("Updated Name".to_string()));
358 assert_eq!(request.archived, Some(true));
359 assert!(request.description.is_none());
360 assert!(request.collection_id.is_none());
361 }
362
363 #[test]
364 fn test_dashboard_with_new_fields() {
365 let dashboard = Dashboard::builder("Enhanced Dashboard")
366 .cache_ttl(600)
367 .collection_position(1)
368 .enable_embedding(true)
369 .embedding_params(serde_json::json!({"key": "value"}))
370 .build();
371
372 assert_eq!(dashboard.name, "Enhanced Dashboard");
373 assert_eq!(dashboard.cache_ttl, Some(600));
374 assert_eq!(dashboard.collection_position, Some(1));
375 assert!(dashboard.enable_embedding);
376 assert_eq!(dashboard.embedding_params["key"], "value");
377 }
378}