cat_gateway/db/event/signed_docs/
full_signed_doc.rs

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
//! `FullSignedDoc` struct implementation.

use super::SignedDocBody;
use crate::{
    db::event::{EventDB, NotFoundError},
    jinja::{get_template, JinjaTemplateSource},
};

/// Insert sql query
const INSERT_SIGNED_DOCS: &str = include_str!("./sql/insert_signed_documents.sql");

/// Select sql query jinja template
pub(crate) const SELECT_SIGNED_DOCS_TEMPLATE: JinjaTemplateSource = JinjaTemplateSource {
    name: "select_signed_documents.jinja.template",
    source: include_str!("./sql/select_signed_documents.sql.jinja"),
};

/// Full signed doc event db struct
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct FullSignedDoc {
    /// Signed doc body
    body: SignedDocBody,
    /// `signed_doc` table `payload` field
    payload: Option<serde_json::Value>,
    /// `signed_doc` table `raw` field
    raw: Vec<u8>,
}

impl FullSignedDoc {
    /// Creates a  `FullSignedDoc` instance.
    #[allow(dead_code)]
    pub(crate) fn new(
        body: SignedDocBody, payload: Option<serde_json::Value>, raw: Vec<u8>,
    ) -> Self {
        Self { body, payload, raw }
    }

    /// Returns the document id.
    pub(crate) fn id(&self) -> &uuid::Uuid {
        self.body.id()
    }

    /// Returns the document version.
    pub(crate) fn ver(&self) -> &uuid::Uuid {
        self.body.ver()
    }

    /// Returns the document author.
    #[allow(dead_code)]
    pub(crate) fn authors(&self) -> &Vec<String> {
        self.body.authors()
    }

    /// Returns the document metadata.
    #[allow(dead_code)]
    pub(crate) fn metadata(&self) -> Option<&serde_json::Value> {
        self.body.metadata()
    }

    /// Returns the `SignedDocBody`.
    #[allow(dead_code)]
    pub(crate) fn body(&self) -> &SignedDocBody {
        &self.body
    }

    /// Returns the document raw bytes.
    #[allow(dead_code)]
    pub(crate) fn raw(&self) -> &Vec<u8> {
        &self.raw
    }

    /// Uploads a `FullSignedDoc` to the event db.
    /// Returns `true` if document was added into the db, `false` if it was already added
    /// previously.
    ///
    /// Make an insert query into the `event-db` by adding data into the `signed_docs`
    /// table.
    ///
    /// * IF the record primary key (id,ver) does not exist, then add the new record.
    ///   Return success.
    /// * IF the record does exist, but all values are the same as stored, return Success.
    /// * Otherwise return an error. (Can not over-write an existing record with new
    ///   data).
    ///
    /// # Arguments:
    ///  - `id` is a UUID v7
    ///  - `ver` is a UUID v7
    ///  - `doc_type` is a UUID v4
    #[allow(dead_code)]
    pub(crate) async fn store(&self) -> anyhow::Result<bool> {
        match Self::retrieve(self.id(), Some(self.ver())).await {
            Ok(res_doc) => {
                anyhow::ensure!(
                    &res_doc == self,
                    "Document with the same `id` and `ver` already exists"
                );
                Ok(false)
            },
            Err(err) if err.is::<NotFoundError>() => {
                EventDB::modify(INSERT_SIGNED_DOCS, &self.postgres_db_fields()).await?;
                Ok(true)
            },
            Err(err) => Err(err),
        }
    }

    /// Loads a `FullSignedDoc` from the event db.
    ///
    /// Make a select query into the `event-db` by getting data from the `signed_docs`
    /// table.
    ///
    /// * This returns a single document. All data from the document is returned,
    ///   including the `payload` and `raw` fields.
    /// * `ver` should be able to be optional, in which case get the latest ver of the
    ///   given `id`.
    ///
    /// # Arguments:
    ///  - `id` is a UUID v7
    ///  - `ver` is a UUID v7
    #[allow(dead_code)]
    pub(crate) async fn retrieve(
        id: &uuid::Uuid, ver: Option<&uuid::Uuid>,
    ) -> anyhow::Result<Self> {
        let query_template = get_template(&SELECT_SIGNED_DOCS_TEMPLATE)?;
        let query = query_template.render(serde_json::json!({
            "id": id,
            "ver": ver,
        }))?;
        let row = EventDB::query_one(&query, &[]).await?;

        Self::from_row(id, ver, &row)
    }

    /// Returns all signed document fields for the event db queries
    fn postgres_db_fields(&self) -> [&(dyn tokio_postgres::types::ToSql + Sync); 7] {
        let body_fields = self.body.postgres_db_fields();
        [
            body_fields[0],
            body_fields[1],
            body_fields[2],
            body_fields[3],
            body_fields[4],
            &self.payload,
            &self.raw,
        ]
    }

    /// Creates a  `FullSignedDoc` from postgresql row object.
    fn from_row(
        id: &uuid::Uuid, ver: Option<&uuid::Uuid>, row: &tokio_postgres::Row,
    ) -> anyhow::Result<Self> {
        let ver = if let Some(ver) = ver {
            *ver
        } else {
            row.try_get("ver")?
        };

        Ok(FullSignedDoc {
            body: SignedDocBody::new(
                *id,
                ver,
                row.try_get("type")?,
                row.try_get("authors")?,
                row.try_get("metadata")?,
            ),
            payload: row.try_get("payload")?,
            raw: row.try_get("raw")?,
        })
    }
}