mithril_aggregator/database/query/certificate/
insert_certificate.rs

1use std::iter::repeat_n;
2
3use sqlite::Value;
4
5use mithril_persistence::sqlite::{Query, SourceAlias, SqLiteEntity, WhereCondition};
6
7use crate::database::record::CertificateRecord;
8
9/// Query to insert [CertificateRecord] in the sqlite database
10pub struct InsertCertificateRecordQuery {
11    condition: WhereCondition,
12}
13
14impl InsertCertificateRecordQuery {
15    pub fn one(certificate_record: CertificateRecord) -> Self {
16        Self::many(vec![certificate_record])
17    }
18
19    pub fn many(certificates_records: Vec<CertificateRecord>) -> Self {
20        let columns = "(\
21        certificate_id, \
22        parent_certificate_id, \
23        message, \
24        signature, \
25        aggregate_verification_key, \
26        epoch, \
27        network, \
28        signed_entity_type_id, \
29        signed_entity_beacon, \
30        protocol_version, \
31        protocol_parameters, \
32        protocol_message, \
33        signers, \
34        initiated_at, \
35        sealed_at)";
36        let values_columns: Vec<&str> = repeat_n(
37            "(?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*, ?*)",
38            certificates_records.len(),
39        )
40        .collect();
41
42        let values: Vec<Value> = certificates_records
43            .into_iter()
44            .flat_map(|certificate_record| {
45                vec![
46                    Value::String(certificate_record.certificate_id),
47                    match certificate_record.parent_certificate_id {
48                        Some(parent_certificate_id) => Value::String(parent_certificate_id),
49                        None => Value::Null,
50                    },
51                    Value::String(certificate_record.message),
52                    Value::String(certificate_record.signature),
53                    Value::String(certificate_record.aggregate_verification_key),
54                    Value::Integer(certificate_record.epoch.try_into().unwrap()),
55                    Value::String(certificate_record.network),
56                    Value::Integer(certificate_record.signed_entity_type.index() as i64),
57                    Value::String(
58                        certificate_record
59                            .signed_entity_type
60                            .get_json_beacon()
61                            .unwrap(),
62                    ),
63                    Value::String(certificate_record.protocol_version),
64                    Value::String(
65                        serde_json::to_string(&certificate_record.protocol_parameters).unwrap(),
66                    ),
67                    Value::String(
68                        serde_json::to_string(&certificate_record.protocol_message).unwrap(),
69                    ),
70                    Value::String(serde_json::to_string(&certificate_record.signers).unwrap()),
71                    Value::String(certificate_record.initiated_at.to_rfc3339()),
72                    Value::String(certificate_record.sealed_at.to_rfc3339()),
73                ]
74            })
75            .collect();
76
77        let condition = WhereCondition::new(
78            format!("{columns} values {}", values_columns.join(", ")).as_str(),
79            values,
80        );
81
82        Self { condition }
83    }
84}
85
86impl Query for InsertCertificateRecordQuery {
87    type Entity = CertificateRecord;
88
89    fn filters(&self) -> WhereCondition {
90        self.condition.clone()
91    }
92
93    fn get_definition(&self, condition: &str) -> String {
94        // it is important to alias the fields with the same name as the table
95        // since the table cannot be aliased in a RETURNING statement in SQLite.
96        let projection = Self::Entity::get_projection()
97            .expand(SourceAlias::new(&[("{:certificate:}", "certificate")]));
98
99        format!("insert into certificate {condition} returning {projection}")
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use mithril_common::crypto_helper::tests_setup::setup_certificate_chain;
106    use mithril_persistence::sqlite::ConnectionExtensions;
107
108    use crate::database::test_helper::main_db_connection;
109
110    use super::*;
111
112    #[test]
113    fn test_insert_certificate_record() {
114        let (certificates, _) = setup_certificate_chain(5, 2);
115
116        let connection = main_db_connection().unwrap();
117
118        for certificate in certificates {
119            let certificate_record: CertificateRecord = certificate.into();
120            let certificate_record_saved = connection
121                .fetch_first(InsertCertificateRecordQuery::one(
122                    certificate_record.clone(),
123                ))
124                .unwrap();
125            assert_eq!(Some(certificate_record), certificate_record_saved);
126        }
127    }
128
129    #[test]
130    fn test_insert_many_certificates_records() {
131        let (certificates, _) = setup_certificate_chain(5, 2);
132        let certificates_records: Vec<CertificateRecord> =
133            certificates.into_iter().map(|cert| cert.into()).collect();
134
135        let connection = main_db_connection().unwrap();
136
137        let certificates_records_saved: Vec<CertificateRecord> = connection
138            .fetch_collect(InsertCertificateRecordQuery::many(
139                certificates_records.clone(),
140            ))
141            .expect("saving many records should not fail");
142
143        assert_eq!(certificates_records, certificates_records_saved);
144    }
145}