1
1
use std:: { collections:: HashMap , error:: Error , fmt:: Display } ;
2
2
3
+ use chrono:: NaiveDateTime ;
3
4
use semver:: Version ;
4
5
use sqlite:: { Connection , Row , Value } ;
5
6
@@ -36,22 +37,31 @@ impl Display for ApplicationNodeType {
36
37
}
37
38
38
39
/// Entity related to the `app_version` database table.
39
- #[ derive( Debug , PartialEq , Eq ) ]
40
+ #[ derive( Debug , PartialEq , Eq , Clone ) ]
40
41
pub struct ApplicationVersion {
41
42
/// Semver of the database structure.
42
- pub database_version : Version ,
43
+ pub semver : Version ,
43
44
44
45
/// Name of the application.
45
46
pub application_type : ApplicationNodeType ,
47
+
48
+ /// Date of the last version upgrade, Sqlite does not store timezone
49
+ /// information hence we have to use a `Chrono::NaiveDateTime` here.
50
+ pub updated_at : NaiveDateTime ,
46
51
}
47
52
48
53
impl SqLiteEntity for ApplicationVersion {
49
54
fn hydrate ( row : Row ) -> Result < Self , HydrationError > {
50
55
Ok ( Self {
51
- database_version : Version :: parse ( & row. get :: < String , _ > ( 0 ) )
56
+ semver : Version :: parse ( & row. get :: < String , _ > ( 0 ) )
52
57
. map_err ( |e| HydrationError :: InvalidData ( format ! ( "{}" , e) ) ) ?,
53
58
application_type : ApplicationNodeType :: new ( & row. get :: < String , _ > ( 1 ) )
54
59
. map_err ( |e| HydrationError :: InvalidData ( format ! ( "{}" , e) ) ) ?,
60
+ updated_at : NaiveDateTime :: parse_from_str (
61
+ & row. get :: < String , _ > ( 2 ) ,
62
+ "%Y-%m-%d %H:%M:%S" ,
63
+ )
64
+ . map_err ( |e| HydrationError :: InvalidData ( format ! ( "{}" , e) ) ) ?,
55
65
} )
56
66
}
57
67
}
@@ -73,12 +83,13 @@ impl Projection for ApplicationVersionProjection {
73
83
impl ApplicationVersionProjection {
74
84
pub fn new ( ) -> Self {
75
85
let mut projection = Self { fields : Vec :: new ( ) } ;
76
- projection. add_field ( "version " , "{:app_version:}.version " , "text" ) ;
86
+ projection. add_field ( "semver " , "{:app_version:}.semver " , "text" ) ;
77
87
projection. add_field (
78
88
"application_type" ,
79
89
"{:app_version:}.application_type" ,
80
90
"text" ,
81
91
) ;
92
+ projection. add_field ( "updated_at" , "{:app_version:}.updated_at" , "timestamp" ) ;
82
93
83
94
projection
84
95
}
@@ -115,16 +126,16 @@ impl<'conn> VersionProvider<'conn> {
115
126
116
127
if !table_exists {
117
128
let sql = r#"
118
- create table app_version (application_type text not null primary key, version text not null)
129
+ create table app_version (application_type text not null primary key, semver text not null, updated_at timestamp not null default CURRENT_TIMESTAMP )
119
130
"# ;
120
131
connection. execute ( sql) ?;
121
132
}
122
133
123
134
Ok ( ( ) )
124
135
}
125
136
126
- /// Read the database version from the database.
127
- pub fn get_database_version (
137
+ /// Read the application version from the database.
138
+ pub fn get_application_version (
128
139
& self ,
129
140
application_type : & ApplicationNodeType ,
130
141
) -> Result < Option < ApplicationVersion > , Box < dyn Error > > {
@@ -183,7 +194,7 @@ impl<'conn> VersionUpdaterProvider<'conn> {
183
194
pub fn save ( & self , version : ApplicationVersion ) -> Result < ApplicationVersion , Box < dyn Error > > {
184
195
let params = [
185
196
Value :: String ( format ! ( "{}" , version. application_type) ) ,
186
- Value :: String ( version. database_version . to_string ( ) ) ,
197
+ Value :: String ( version. semver . to_string ( ) ) ,
187
198
] ;
188
199
let entity = self
189
200
. find ( None , & params) ?
@@ -213,8 +224,8 @@ impl<'conn> Provider<'conn> for VersionUpdaterProvider<'conn> {
213
224
214
225
format ! (
215
226
r#"
216
- insert into app_version (application_type, version ) values (?, ?)
217
- on conflict (application_type) do update set version = excluded.version
227
+ insert into app_version (application_type, semver ) values (?, ?)
228
+ on conflict (application_type) do update set semver = excluded.semver, updated_at = CURRENT_TIMESTAMP
218
229
returning {projection}
219
230
"#
220
231
)
@@ -232,7 +243,7 @@ mod tests {
232
243
let _ = aliases. insert ( "{:app_version:}" . to_string ( ) , "whatever" . to_string ( ) ) ;
233
244
234
245
assert_eq ! (
235
- "whatever.version as version , whatever.application_type as application_type"
246
+ "whatever.semver as semver , whatever.application_type as application_type, whatever.updated_at as updated_at "
236
247
. to_string( ) ,
237
248
projection. expand( aliases)
238
249
) ;
@@ -245,7 +256,7 @@ mod tests {
245
256
246
257
assert_eq ! (
247
258
r#"
248
- select app_version.version as version , app_version.application_type as application_type
259
+ select app_version.semver as semver , app_version.application_type as application_type, app_version.updated_at as updated_at
249
260
from app_version
250
261
where true
251
262
"# ,
@@ -260,9 +271,9 @@ where true
260
271
261
272
assert_eq ! (
262
273
r#"
263
- insert into app_version (application_type, version ) values (?, ?)
264
- on conflict (application_type) do update set version = excluded.version
265
- returning app_version.version as version , app_version.application_type as application_type
274
+ insert into app_version (application_type, semver ) values (?, ?)
275
+ on conflict (application_type) do update set semver = excluded.semver, updated_at = CURRENT_TIMESTAMP
276
+ returning app_version.semver as semver , app_version.application_type as application_type, app_version.updated_at as updated_at
266
277
"# ,
267
278
provider. get_definition( None )
268
279
)
0 commit comments