@@ -3,40 +3,50 @@ use std::collections::HashMap;
3
3
use chrono:: NaiveDateTime ;
4
4
use semver:: Version ;
5
5
use serde:: Serialize ;
6
+ use uuid:: Uuid ;
6
7
7
8
use crate :: Error ;
8
9
9
10
#[ derive( Serialize , Debug , PartialEq , Eq ) ]
10
11
pub struct Event {
11
12
event : String ,
12
- properties : Properties ,
13
- timestamp : Option < NaiveDateTime > ,
14
- }
15
-
16
- #[ derive( Serialize , Debug , PartialEq , Eq ) ]
17
- pub struct Properties {
13
+ #[ serde( rename = "$distinct_id" ) ]
18
14
distinct_id : String ,
19
- props : HashMap < String , serde_json:: Value > ,
15
+ properties : HashMap < String , serde_json:: Value > ,
16
+ groups : HashMap < String , String > ,
17
+ timestamp : Option < NaiveDateTime > ,
20
18
}
21
19
22
- impl Properties {
23
- fn new < S : Into < String > > ( distinct_id : S ) -> Self {
20
+ impl Event {
21
+ /// Capture a new identified event. Unless you have a distinct ID you can
22
+ /// associate with a user, you probably want to use `new_anon` instead.
23
+ pub fn new < S : Into < String > > ( event : S , distinct_id : S ) -> Self {
24
24
Self {
25
+ event : event. into ( ) ,
25
26
distinct_id : distinct_id. into ( ) ,
26
- props : Default :: default ( ) ,
27
+ properties : HashMap :: new ( ) ,
28
+ groups : HashMap :: new ( ) ,
29
+ timestamp : None ,
27
30
}
28
31
}
29
- }
30
32
31
- impl Event {
32
- pub fn new < S : Into < String > > ( event : S , distinct_id : S ) -> Self {
33
- Self {
33
+ /// Capture a new anonymous event.
34
+ /// See https://posthog.com/docs/data/anonymous-vs-identified-events#how-to-capture-anonymous-events
35
+ pub fn new_anon < S : Into < String > > ( event : S ) -> Self {
36
+ let mut res = Self {
34
37
event : event. into ( ) ,
35
- properties : Properties :: new ( distinct_id) ,
38
+ distinct_id : Uuid :: now_v7 ( ) . to_string ( ) ,
39
+ properties : HashMap :: new ( ) ,
40
+ groups : HashMap :: new ( ) ,
36
41
timestamp : None ,
37
- }
42
+ } ;
43
+ res. insert_prop ( "$process_person_profile" , false )
44
+ . expect ( "bools are safe for serde" ) ;
45
+ res
38
46
}
39
47
48
+ /// Add a property to the event
49
+ ///
40
50
/// Errors if `prop` fails to serialize
41
51
pub fn insert_prop < K : Into < String > , P : Serialize > (
42
52
& mut self ,
@@ -45,54 +55,87 @@ impl Event {
45
55
) -> Result < ( ) , Error > {
46
56
let as_json =
47
57
serde_json:: to_value ( prop) . map_err ( |e| Error :: Serialization ( e. to_string ( ) ) ) ?;
48
- let _ = self . properties . props . insert ( key. into ( ) , as_json) ;
58
+ let _ = self . properties . insert ( key. into ( ) , as_json) ;
49
59
Ok ( ( ) )
50
60
}
61
+
62
+ /// Capture this as a group event. See https://posthog.com/docs/product-analytics/group-analytics#how-to-capture-group-events
63
+ /// Note that group events cannot be personless, and will be automatically upgraded to include person profile processing if
64
+ /// they were anonymous. This might lead to "empty" person profiles being created.
65
+ pub fn add_group ( & mut self , group_name : & str , group_id : & str ) {
66
+ // You cannot disable person profile processing for groups
67
+ self . insert_prop ( "$process_person_profile" , true )
68
+ . expect ( "bools are safe for serde" ) ;
69
+ self . groups . insert ( group_name. into ( ) , group_id. into ( ) ) ;
70
+ }
51
71
}
52
72
53
73
// This exists so that the client doesn't have to specify the API key over and over
54
74
#[ derive( Serialize ) ]
55
75
pub struct InnerEvent {
56
76
api_key : String ,
77
+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
78
+ uuid : Option < Uuid > ,
57
79
event : String ,
58
- properties : Properties ,
80
+ #[ serde( rename = "$distinct_id" ) ]
81
+ distinct_id : String ,
82
+ properties : HashMap < String , serde_json:: Value > ,
59
83
timestamp : Option < NaiveDateTime > ,
60
84
}
61
85
62
86
impl InnerEvent {
63
87
pub fn new ( event : Event , api_key : String ) -> Self {
88
+ Self :: new_with_uuid ( event, api_key, None )
89
+ }
90
+
91
+ pub fn new_with_uuid ( event : Event , api_key : String , uuid : Option < Uuid > ) -> Self {
64
92
let mut properties = event. properties ;
65
93
66
94
// Add $lib_name and $lib_version to the properties
67
- properties. props . insert (
95
+ properties. insert (
68
96
"$lib_name" . into ( ) ,
69
97
serde_json:: Value :: String ( "posthog-rs" . into ( ) ) ,
70
98
) ;
71
99
72
100
let version_str = env ! ( "CARGO_PKG_VERSION" ) ;
73
- properties. props . insert (
101
+ properties. insert (
74
102
"$lib_version" . into ( ) ,
75
103
serde_json:: Value :: String ( version_str. into ( ) ) ,
76
104
) ;
77
105
78
106
if let Ok ( version) = version_str. parse :: < Version > ( ) {
79
- properties. props . insert (
107
+ properties. insert (
80
108
"$lib_version__major" . into ( ) ,
81
109
serde_json:: Value :: Number ( version. major . into ( ) ) ,
82
110
) ;
83
- properties. props . insert (
111
+ properties. insert (
84
112
"$lib_version__minor" . into ( ) ,
85
113
serde_json:: Value :: Number ( version. minor . into ( ) ) ,
86
114
) ;
87
- properties. props . insert (
115
+ properties. insert (
88
116
"$lib_version__patch" . into ( ) ,
89
117
serde_json:: Value :: Number ( version. patch . into ( ) ) ,
90
118
) ;
91
119
}
92
120
121
+ if !event. groups . is_empty ( ) {
122
+ properties. insert (
123
+ "$groups" . into ( ) ,
124
+ serde_json:: Value :: Object (
125
+ event
126
+ . groups
127
+ . into_iter ( )
128
+ . map ( |( k, v) | ( k, serde_json:: Value :: String ( v) ) )
129
+ . collect ( ) ,
130
+ ) ,
131
+ ) ;
132
+ }
133
+
93
134
Self {
94
135
api_key,
136
+ uuid,
95
137
event : event. event ,
138
+ distinct_id : event. distinct_id ,
96
139
properties,
97
140
timestamp : event. timestamp ,
98
141
}
@@ -114,10 +157,21 @@ pub mod tests {
114
157
let inner_event = InnerEvent :: new ( event, api_key) ;
115
158
116
159
// Assert
117
- let props = & inner_event. properties . props ;
160
+ let props = & inner_event. properties ;
118
161
assert_eq ! (
119
162
props. get( "$lib_name" ) ,
120
163
Some ( & serde_json:: Value :: String ( "posthog-rs" . to_string( ) ) )
121
164
) ;
122
165
}
166
+
167
+ #[ test]
168
+ fn event_structure_is_correct ( ) {
169
+ let mut event = Event :: new ( "unit test event" , "1234" ) ;
170
+ event. insert_prop ( "key1" , "value1" ) . unwrap ( ) ;
171
+ let api_key = "test_api_key" . to_string ( ) ;
172
+ let inner_event = InnerEvent :: new ( event, api_key) ;
173
+ let payload = serde_json:: to_string ( & inner_event) . unwrap ( ) ;
174
+
175
+ println ! ( "{}" , payload) ;
176
+ }
123
177
}
0 commit comments