domo 0.3.2

The Domo Rust SDK wrapping our APIs. Also includes a CLI application.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use std::{error::Error, path::Path};

/// The DataSet object allows you to create, import, export and manage DataSets and manage data permissions for DataSets within Domo.
///
/// The DataSet API should be used to create and update small DataSets that occasionally need their data updated. For creating and updating massive, constantly changing, or rapidly growing DataSets, the Stream API is recommended.
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default, rename_all = "camelCase")]
pub struct DataSet {
    /// ID of the DataSet
    pub id: Option<String>,

    /// Name of the DataSet
    pub name: Option<String>,

    /// Description of DataSet
    pub description: Option<String>,

    /// Dataset owner
    pub owner: Option<Owner>,

    /// An ISO-8601 representation of the creation date of the DataSet
    pub created_at: Option<DateTime<Utc>>,

    /// AN ISO-8601 representation of the time the DataSet was last updated
    pub updated_at: Option<DateTime<Utc>>,

    /// An ISO-8601 representation of the time the DataSet was current
    pub data_current_at: Option<DateTime<Utc>>,

    /// The current schema associated with this DataSet
    pub schema: Option<Schema>,

    /// Indicates if PDP [Personalized Data Permission] policy filtering on data is active on this DataSet
    pub pdp_enabled: Option<bool>,

    /// List of policies attached to DataSet
    pub policies: Option<Vec<Policy>>,

    /// The number of rows currently in the DataSet
    pub rows: Option<u64>,

    /// The number of columns currently in the DataSet
    pub columns: Option<u32>,
}

impl DataSet {
    pub fn new() -> Self {
        DataSet {
            id: None,
            name: None,
            description: None,
            owner: None,
            created_at: None,
            updated_at: None,
            data_current_at: None,
            schema: None,
            pdp_enabled: None,
            policies: None,
            rows: None,
            columns: None,
        }
    }
    pub fn template() -> Self {
        DataSet {
            id: Some(String::from("UUID")),
            name: Some(String::from("DataSet Name")),
            description: Some(String::from("DataSet Description")),
            owner: Some(Owner {
                id: 1234,
                name: Some(String::from("DataSet Owner's Name")),
            }),
            created_at: Some(Utc::now()),
            updated_at: Some(Utc::now()),
            data_current_at: Some(Utc::now()),
            schema: Some(Schema {
                columns: Some(vec![Column {
                    name: Some(String::from("Column Name")),
                    column_type: Some(String::from(
                        "STRING | DECIMAL | LONG | DOUBLE | DATE | DATETIME",
                    )),
                }]),
            }),
            pdp_enabled: Some(false),
            policies: Some(vec![Policy::template()]),
            rows: Some(0),
            columns: Some(0),
        }
    }
}

/// The dataset owner
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Owner {
    /// ID of the owner
    pub id: u32,
    /// Name of the owner
    pub name: Option<String>,
}

/// Defines a schema of a dataset
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Schema {
    /// Array of columns in the DataSet
    pub columns: Option<Vec<Column>>,
}

/// A single column schema definition for a dataset
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Column {
    /// Column name in the DataSet schema
    pub name: Option<String>,

    /// Column type in the DataSet schema. Valid types are STRING, DECIMAL, LONG, DOUBLE, DATE, DATETIME.
    #[serde(rename = "type")]
    pub column_type: Option<String>,
}

/// A PDP Policy definition for a dataset
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Policy {
    /// ID of the Policy
    pub id: Option<u32>,

    /// Name of the Policy
    pub name: Option<String>,

    /// Type of policy (user or system)
    #[serde(rename = "type")]
    pub policy_type: Option<String>,

    /// List of filters
    pub filters: Option<Vec<Filter>>,

    /// List of user IDs the policy applies to
    pub users: Option<Vec<u64>>,

    /// List of virtual Ids the policy applies to
    pub virtual_users: Option<Vec<String>>,

    /// List of group IDs the policy applies to
    pub groups: Option<Vec<String>>,
}

impl Policy {
    pub fn new() -> Self {
        Self {
            id: None,
            name: None,
            policy_type: None,
            filters: None,
            users: None,
            virtual_users: None,
            groups: None,
        }
    }

    pub fn template() -> Self {
        Self {
            id: Some(0),
            name: Some(String::from("Policy Name")),
            policy_type: Some(String::from("user | system")),
            filters: Some(vec![Filter {
                column: Some(String::from("Column to filter on")),
                not: Some(false),
                operator: Some(String::from("EQUALS")),
                values: vec![String::from("values in this column that match will apply")],
            }]),
            users: Some(vec![27]),
            virtual_users: Some(vec![String::from("vu:324ds")]),
            groups: Some(vec![String::from("15")]),
        }
    }
}

/// Policy filter for a dataset
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct Filter {
    /// Name of the column to filter on
    pub column: Option<String>,

    /// Determines if NOT is applied to the filter operation
    pub not: Option<bool>,

    /// Matching operator (EQUALS)
    pub operator: Option<String>,

    /// Values to filter on
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub values: Vec<String>,
}

/// Contains the results from a dataset query
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default, rename_all = "camelCase")]
pub struct QueryResult {
    /// The associated dataset
    /// TODO This should be dataset (consistency)
    pub datasource: Option<String>,

    /// The result set column names
    pub columns: Option<Vec<String>>,

    /// Metadata about the resultset rows and columns
    pub metadata: Option<Vec<QueryMetadata>>,

    /// The raw row data from the result set
    pub rows: Option<Vec<Vec<Value>>>,

    /// The number of rows in the result set
    pub num_rows: Option<u64>,

    /// The number of columns in the result set
    pub num_columns: Option<u32>,

    /// Whether this result set was served from cache
    pub from_cache: Option<bool>,
}

#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default, rename_all = "camelCase")]
pub struct QueryMetadata {
    /// The column type of the result set, if the column has this defined
    #[serde(rename = "type")]
    pub column_type: Option<String>,

    /// The dataset id
    /// TODO This should be dataset (consistency)
    pub datasource_id: Option<String>,

    /// max length
    pub max_length: Option<i32>,

    /// min length
    pub min_length: Option<i32>,

    /// period index
    pub period_index: Option<i32>,

    // aggregated
    pub aggregated: Option<bool>,
}

/// DataSet API methods
/// Uses the form method_object
impl super::Client {
    /// Get a list of all DataSets in your Domo instance.
    pub async fn get_datasets(
        &self,
        limit: Option<u32>,
        offset: Option<u32>,
    ) -> Result<Vec<DataSet>, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        #[derive(Serialize)]
        struct ListParams {
            pub limit: Option<u32>,
            pub offset: Option<u32>,
            pub sort: String,
        }
        let q = ListParams {
            limit,
            offset,
            sort: "name".to_string(),
        };
        let mut response = surf::get(&format!("{}{}", self.host, "/v1/datasets"))
            .query(&q)?
            .header("Authorization", at)
            .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Creates a new DataSet in your Domo instance. Once the DataSet has been created, data can then be imported into the DataSet.
    pub async fn post_dataset(
        &self,
        ds: DataSet,
    ) -> Result<DataSet, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::post(&format!("{}{}", self.host, "/v1/datasets"))
            .header("Authorization", at)
            .body(surf::Body::from_json(&ds)?)
            .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Retrieves the details of an existing DataSet.
    pub async fn get_dataset(
        &self,
        id: &str,
    ) -> Result<DataSet, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::get(&format!("{}{}{}", self.host, "/v1/datasets/", id))
            .header("Authorization", at)
            .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Updates the specified DataSet’s metadata by providing values to parameters passed.
    pub async fn put_dataset(
        &self,
        id: &str,
        ds: DataSet,
    ) -> Result<DataSet, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::put(&format!("{}{}{}", self.host, "/v1/datasets/", id))
            .header("Authorization", at)
            .body(surf::Body::from_json(&ds)?)
            .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Permanently deletes a DataSet from your Domo instance. This can be done for all DataSets, not just those created through the API.
    ///
    /// This is destructive and cannot be reversed.
    pub async fn delete_dataset(
        &self,
        id: &str,
    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::delete(&format!("{}{}{}", self.host, "/v1/datasets/", id))
            .header("Authorization", at)
            .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Export data from a DataSet in your Domo instance.
    ///
    /// Data types will be exported as they are currently stored in the dataset. In addition, the only supported export type is CSV.
    ///
    /// TODO Parameters includeHeader and fileName
    pub async fn get_dataset_data(
        &self,
        id: &str,
    ) -> Result<String, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        #[derive(Serialize)]
        struct QueryParams {
            #[serde(rename = "includeHeader")]
            pub include_header: bool,
        }
        let q = QueryParams {
            include_header: true,
        };
        let mut response = surf::get(&format!(
            "{}{}{}{}",
            self.host, "/v1/datasets/", id, "/data"
        ))
        .query(&q)?
        .header("Authorization", at)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_string().await?)
    }

    /// Import data into a DataSet in your Domo instance. This request will replace the data currently in the DataSet.
    ///
    /// The only supported content type is currently CSV format.
    ///
    /// To upload data in CSV format, the Domo specification used for representing data grids in CSV format closely follows the RFC standard for CSV (RFC-4180).
    pub async fn put_dataset_data(
        &self,
        id: &str,
        csv: impl AsRef<Path>,
    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::put(&format!(
            "{}{}{}{}",
            self.host, "/v1/datasets/", id, "/data"
        ))
        .header("Authorization", at)
        //TODO Have the csv data passed in as an async_std::io::Read. <- Should just need to change the below to Body::from_reader
        .body(surf::Body::from_file(csv).await?)
        .header("Content-Type", "text/csv")
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Returns data from the DataSet based on your SQL query.
    pub async fn post_dataset_query(
        &self,
        id: &str,
        query: &str,
    ) -> Result<QueryResult, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::post(&format!(
            "{}{}{}",
            self.host, "/v1/datasets/query/execute/", id
        ))
        .header("Authorization", at)
        .body(surf::Body::from_json(&json!({ "sql": query }))?)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// List the Personalized Data Permission (PDP) policies for a specified DataSet.
    pub async fn get_dataset_policies(
        &self,
        id: &str,
    ) -> Result<Vec<Policy>, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::get(&format!(
            "{}{}{}{}",
            self.host, "/v1/datasets/", id, "/policies"
        ))
        .header("Authorization", at)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Create a PDP policy for user and or group access to data within a DataSet.
    /// Users and groups must exist before creating PDP policy.
    pub async fn post_dataset_policy(
        &self,
        id: &str,
        policy: Policy,
    ) -> Result<Policy, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::post(&format!(
            "{}{}{}{}",
            self.host, "/v1/datasets/", id, "/policies"
        ))
        .header("Authorization", at)
        .body(surf::Body::from_json(&policy)?)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Retrieve a policy from a DataSet within Domo.
    /// A DataSet is required for a PDP policy to exist.
    pub async fn get_dataset_policy(
        &self,
        id: &str,
        policy_id: u32,
    ) -> Result<Policy, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::get(&format!(
            "{}{}{}{}{}",
            self.host, "/v1/datasets/", id, "/policies/", policy_id
        ))
        .header("Authorization", at)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Update the specific PDP policy for a DataSet by providing values to parameters passed.
    pub async fn put_dataset_policy(
        &self,
        id: &str,
        policy_id: u32,
        policy: Policy,
    ) -> Result<Policy, Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::put(&format!(
            "{}{}{}{}{}",
            self.host, "/v1/datasets/", id, "/policies/", policy_id
        ))
        .header("Authorization", at)
        .body(surf::Body::from_json(&policy)?)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }

    /// Permanently deletes a PDP policy on a DataSet in your Domo instance.
    ///
    /// This is destructive and cannot be reversed.
    pub async fn delete_dataset_policy(
        &self,
        id: &str,
        policy_id: u32,
    ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        let at = self.get_access_token("data").await?;
        let mut response = surf::delete(&format!(
            "{}{}{}{}{}",
            self.host, "/v1/datasets/", id, "/policies/", policy_id
        ))
        .header("Authorization", at)
        .await?;
        if !response.status().is_success() {
            let e: Box<super::PubAPIError> = response.body_json().await?;
            return Err(e);
        }
        Ok(response.body_json().await?)
    }
}