1
1
use chrono:: { DateTime , Utc } ;
2
2
use serde:: { Deserialize , Serialize } ;
3
- use serde_json:: Value ;
3
+ use serde_json:: value :: { RawValue , Value } ;
4
4
5
- use crate :: event:: { EventCandidate , trace_info:: TraceInfo } ;
5
+ use crate :: {
6
+ error:: EventError ,
7
+ event:: { EventCandidate , trace_info:: TraceInfo } ,
8
+ } ;
6
9
#[ cfg( feature = "cloudevents" ) ]
7
10
use cloudevents:: EventBuilder ;
11
+ use sha2:: { Digest , Sha256 } ;
12
+
13
+ #[ derive( Debug , Clone ) ]
14
+ pub struct CustomValue {
15
+ parsed : Value ,
16
+ raw : Box < RawValue > ,
17
+ }
18
+
19
+ impl PartialEq for CustomValue {
20
+ fn eq ( & self , other : & Self ) -> bool {
21
+ self . parsed == other. parsed
22
+ }
23
+ }
24
+
25
+ impl Eq for CustomValue { }
26
+
27
+ impl < ' de > Deserialize < ' de > for CustomValue {
28
+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
29
+ where
30
+ D : serde:: Deserializer < ' de > ,
31
+ {
32
+ let raw = Box :: < RawValue > :: deserialize ( deserializer) ?;
33
+ let parsed: Value = serde_json:: from_str ( raw. get ( ) ) . map_err ( serde:: de:: Error :: custom) ?;
34
+ Ok ( Self { parsed, raw } )
35
+ }
36
+ }
37
+
38
+ impl Serialize for CustomValue {
39
+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
40
+ where
41
+ S : serde:: Serializer ,
42
+ {
43
+ self . raw . serialize ( serializer)
44
+ }
45
+ }
8
46
9
47
/// Represents an event that has been received from the DB.
10
48
#[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
11
49
pub struct Event {
12
- data : Value ,
50
+ data : CustomValue ,
13
51
datacontenttype : String ,
14
52
hash : String ,
15
53
id : String ,
@@ -28,7 +66,7 @@ impl Event {
28
66
/// Get the data of an event.
29
67
#[ must_use]
30
68
pub fn data ( & self ) -> & Value {
31
- & self . data
69
+ & self . data . parsed
32
70
}
33
71
/// Get the data content type of an event.
34
72
#[ must_use]
@@ -92,12 +130,74 @@ impl Event {
92
130
pub fn ty ( & self ) -> & str {
93
131
& self . ty
94
132
}
133
+
134
+ /// Verify the hash of an event.
135
+ ///
136
+ /// ```
137
+ /// use eventsourcingdb::event::EventCandidate;
138
+ /// # use serde_json::json;
139
+ /// # tokio_test::block_on(async {
140
+ /// # let container = eventsourcingdb::container::Container::start_preview().await.unwrap();
141
+ /// let db_url = "http://localhost:3000/";
142
+ /// let api_token = "secrettoken";
143
+ /// # let db_url = container.get_base_url().await.unwrap();
144
+ /// # let api_token = container.get_api_token();
145
+ /// let client = eventsourcingdb::client::Client::new(db_url, api_token);
146
+ /// let candidates = vec![
147
+ /// EventCandidate::builder()
148
+ /// .source("https://www.eventsourcingdb.io".to_string())
149
+ /// .data(json!({"value": 1}))
150
+ /// .subject("/test".to_string())
151
+ /// .ty("io.eventsourcingdb.test".to_string())
152
+ /// .build()
153
+ /// ];
154
+ /// let written_events = client.write_events(candidates, vec![]).await.expect("Failed to write events");
155
+ /// let event = &written_events[0];
156
+ /// event.verify_hash().expect("Hash verification failed");
157
+ /// # })
158
+ /// ```
159
+ ///
160
+ /// # Errors
161
+ /// Returns an error if the hash verification fails.
162
+ pub fn verify_hash ( & self ) -> Result < ( ) , EventError > {
163
+ let metadata = format ! (
164
+ "{}|{}|{}|{}|{}|{}|{}|{}" ,
165
+ self . specversion,
166
+ self . id,
167
+ self . predecessorhash,
168
+ self . time
169
+ . to_rfc3339_opts( chrono:: SecondsFormat :: Nanos , true ) ,
170
+ self . source,
171
+ self . subject,
172
+ self . ty,
173
+ self . datacontenttype,
174
+ ) ;
175
+
176
+ let metadata_hash = Sha256 :: digest ( metadata. as_bytes ( ) ) ;
177
+ let metadata_hash_hex = hex:: encode ( metadata_hash) ;
178
+
179
+ let data_hash = Sha256 :: digest ( self . data . raw . get ( ) ) ;
180
+ let data_hash_hex = hex:: encode ( data_hash) ;
181
+
182
+ let final_hash_input = format ! ( "{metadata_hash_hex}{data_hash_hex}" ) ;
183
+ let final_hash = Sha256 :: digest ( final_hash_input. as_bytes ( ) ) ;
184
+ let final_hash_hex = hex:: encode ( final_hash) ;
185
+
186
+ if final_hash_hex == self . hash {
187
+ Ok ( ( ) )
188
+ } else {
189
+ Err ( EventError :: HashVerificationFailed {
190
+ expected : self . hash . clone ( ) ,
191
+ actual : final_hash_hex,
192
+ } )
193
+ }
194
+ }
95
195
}
96
196
97
197
impl From < Event > for EventCandidate {
98
198
fn from ( event : Event ) -> Self {
99
199
Self {
100
- data : event. data ,
200
+ data : event. data . parsed ,
101
201
source : event. source ,
102
202
subject : event. subject ,
103
203
ty : event. ty ,
0 commit comments