datadog_workflow_lib/database/
timeboards.rs

1use crate::database::models::{InsertTimeBoard, TimeBoard};
2use crate::database::DbContext;
3use failure::{format_err, Error};
4use rusqlite::{ToSql, NO_PARAMS};
5
6pub struct Timeboards<'a> {
7    db: &'a mut DbContext,
8}
9
10impl<'a> Timeboards<'a> {
11    #[inline]
12    pub fn new(db: &'a mut DbContext) -> Self {
13        Self { db }
14    }
15
16    #[inline]
17    pub fn run_migrations(&self) -> Result<(), Error> {
18        self.db.conn.execute(
19            "CREATE TABLE IF NOT EXISTS timeboards (
20                id          TEXT     NOT NULL PRIMARY KEY,
21                title       TEXT     NOT NULL,
22                description TEXT     NOT NULL,
23                url         TEXT     NOT NULL,
24                modified    DATETIME NOT NULL
25            );",
26            NO_PARAMS,
27        )?;
28        self.db.conn.execute(
29            "CREATE INDEX IF NOT EXISTS idx_timeboards_title_modified ON timeboards (title, modified);",
30            NO_PARAMS,
31        )?;
32        Ok(())
33    }
34
35    #[inline]
36    pub fn delete_all(&self) -> Result<(), Error> {
37        self.db.conn.execute("DELETE FROM timeboards;", NO_PARAMS)?;
38        Ok(())
39    }
40
41    #[inline]
42    pub fn insert(&mut self, timeboards: &[InsertTimeBoard]) -> Result<(), Error> {
43        let tx = self.db.conn.transaction()?;
44        let mut stmt = tx.prepare("INSERT INTO timeboards (id, title, description, url, modified) VALUES (?1, ?2, ?3, ?4, ?5)")?;
45
46        for board in timeboards {
47            let url = format!(
48                "https://{}.datadoghq.com/dash/{}",
49                self.db.subdomain, board.id
50            );
51            stmt.execute(&[
52                &board.id as &dyn ToSql,
53                &board.title,
54                &board.description.clone().unwrap_or_default(),
55                &url,
56                &board.modified,
57            ])?;
58        }
59
60        stmt.finalize()?;
61        tx.commit()?;
62        Ok(())
63    }
64
65    #[inline]
66    pub fn find(&self, title: &str, limit: i64) -> Result<Vec<TimeBoard>, Error> {
67        // This will allow searching by full name or just the words within the name;
68        // it's not a regex but it's good enough.
69        let query = format!(
70            "%{}%",
71            title
72                .split_terminator(' ')
73                .flat_map(|s| s.split_terminator('_'))
74                .flat_map(|s| s.split_terminator('-'))
75                .collect::<Vec<&str>>()
76                .join("%")
77        );
78
79        let results = self.db.conn.prepare(
80            "SELECT id, title, description, url, modified FROM timeboards WHERE title LIKE ? ORDER BY modified DESC LIMIT ?",
81        )?.query_map(&[&query as &dyn ToSql,&limit], |row| {
82            Ok(TimeBoard {
83                id: row.get(0)?,
84                title:row.get(1)?,
85                description:row.get(2)?,
86                url:row.get(3)?,
87                modified:row.get(4)?,
88            })
89        })?.map(|r|{
90            match r{
91                Ok(v) => Ok(v),
92                Err(e)=> Err(format_err!("Query + Transform into Repository failed: {}",e)),
93            }
94        }).collect::<Result<Vec<_>, _>>();
95        results
96    }
97}