changeset 59:373b6ad155ba

Add accuracy field and properly treat notes
author Lewin Bormann <lbo@spheniscida.de>
date Fri, 04 Dec 2020 11:02:22 +0100
parents f64f759b5b28
children 5074024fc84d
files pgsql_schema.sql src/db.rs src/http.rs src/main.rs src/types.rs
diffstat 5 files changed, 43 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/pgsql_schema.sql	Fri Dec 04 10:02:38 2020 +0100
+++ b/pgsql_schema.sql	Fri Dec 04 11:02:22 2020 +0100
@@ -32,6 +32,7 @@
     lat double precision,
     long double precision,
     spd double precision,
+    accuracy double precision,
     t timestamp with time zone not null,
     ele double precision
     secret bytea,
--- a/src/db.rs	Fri Dec 04 10:02:38 2020 +0100
+++ b/src/db.rs	Fri Dec 04 11:02:22 2020 +0100
@@ -21,14 +21,14 @@
     ) -> Result<types::GeoJSON, postgres::Error> {
         let mut returnable = types::GeoJSON::new();
         let stmt = self.0.prepare_cached(
-            r"SELECT id, t, lat, long, spd, ele, note FROM geohub.geodata
+            r"SELECT id, t, lat, long, spd, ele, note, accuracy FROM geohub.geodata
         WHERE (client = $1) and (t between $2 and $3) AND (secret = public.digest($4, 'sha256') or secret is null) AND (id > $5)
         ORDER BY t ASC
         LIMIT $6").unwrap(); // Must succeed.
         let rows = stmt.query(&[&name, &from_ts, &to_ts, &secret, &last.unwrap_or(0), &limit])?;
         returnable.reserve_features(rows.len());
         for row in rows.iter() {
-            let (id, ts, lat, long, spd, ele, note) = (
+            let (id, ts, lat, long, spd, ele, note, acc) = (
                 row.get(0),
                 row.get(1),
                 row.get(2),
@@ -36,12 +36,14 @@
                 row.get(4),
                 row.get(5),
                 row.get(6),
+                row.get(7),
             );
             let point = types::GeoPoint {
                 lat: lat,
                 long: long,
                 spd: spd,
                 ele: ele,
+                accuracy: acc,
                 note: note,
                 time: ts,
             };
@@ -56,7 +58,9 @@
         secret: &Option<String>,
         point: &types::GeoPoint,
     ) -> Result<(), postgres::Error> {
-        let stmt = self.0.prepare_cached("INSERT INTO geohub.geodata (client, lat, long, spd, t, ele, secret, note) VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'), $8)").unwrap();
+        let stmt = self.0.prepare_cached(
+            r"INSERT INTO geohub.geodata (client, lat, long, spd, t, ele, secret, note, accuracy)
+            VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'), $8, $9)").unwrap();
         let channel = format!(
             "NOTIFY {}, '{}'",
             ids::channel_name(name, secret.as_ref().unwrap_or(&"".into()).as_str()),
@@ -72,6 +76,7 @@
             &point.ele,
             &secret,
             &point.note,
+            &point.accuracy,
         ])
         .unwrap();
         notify.execute(&[]).unwrap();
@@ -88,7 +93,7 @@
     ) -> Option<(types::GeoJSON, i32)> {
         let mut returnable = types::GeoJSON::new();
         let check_for_new = self.0.prepare_cached(
-            r"SELECT id, t, lat, long, spd, ele, note FROM geohub.geodata
+            r"SELECT id, t, lat, long, spd, ele, note, accuracy FROM geohub.geodata
             WHERE (client = $1) and (id > $2) AND (secret = public.digest($3, 'sha256') or secret is null)
             ORDER BY id DESC
             LIMIT $4").unwrap(); // Must succeed.
@@ -104,7 +109,7 @@
                 let mut last = 0;
 
                 for row in rows.iter() {
-                    let (id, ts, lat, long, spd, ele, note) = (
+                    let (id, ts, lat, long, spd, ele, note, acc) = (
                         row.get(0),
                         row.get(1),
                         row.get(2),
@@ -112,6 +117,7 @@
                         row.get(4),
                         row.get(5),
                         row.get(6),
+                        row.get(7),
                     );
                     let point = types::GeoPoint {
                         time: ts,
@@ -120,6 +126,7 @@
                         spd: spd,
                         ele: ele,
                         note: note,
+                        accuracy: acc,
                     };
                     returnable.push_feature(types::geofeature_from_point(Some(id), point));
                     if id > last {
--- a/src/http.rs	Fri Dec 04 10:02:38 2020 +0100
+++ b/src/http.rs	Fri Dec 04 11:02:22 2020 +0100
@@ -1,5 +1,7 @@
 use rocket::response::Responder;
 
+use std::io::Read;
+
 #[derive(Responder)]
 pub enum GeoHubResponse {
     #[response(status = 200, content_type = "json")]
@@ -26,3 +28,13 @@
 pub fn server_error(msg: String) -> GeoHubResponse {
     GeoHubResponse::ServerError(msg)
 }
+
+pub fn read_data(d: rocket::Data, limit: u64) -> Result<String, GeoHubResponse> {
+    let mut ds = d.open().take(limit);
+    let mut dest = Vec::with_capacity(limit as usize);
+    if let Err(e) = std::io::copy(&mut ds, &mut dest) {
+        return Err(GeoHubResponse::BadRequest(format!("Error reading request: {}", e)));
+    }
+
+    String::from_utf8(dest).map_err(|e| GeoHubResponse::BadRequest(format!("Decoding error: {}", e)))
+}
--- a/src/main.rs	Fri Dec 04 10:02:38 2020 +0100
+++ b/src/main.rs	Fri Dec 04 11:02:22 2020 +0100
@@ -122,7 +122,7 @@
 /// time is like 2020-11-30T20:12:36.444Z (ISO 8601). By default, server time is set.
 /// secret can be used to protect points.
 #[rocket::post(
-    "/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>",
+    "/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>&<accuracy>",
     data = "<note>"
 )]
 fn log(
@@ -134,7 +134,8 @@
     time: Option<String>,
     s: Option<f64>,
     ele: Option<f64>,
-    note: Option<String>,
+    accuracy: Option<f64>,
+    note: rocket::data::Data,
 ) -> http::GeoHubResponse {
     let db = db::DBQuery(&db.0);
     // Check that secret and client name are legal.
@@ -144,28 +145,6 @@
                 .into(),
         );
     }
-    // Length-limit notes.
-    if let Some(note) = note.as_ref() {
-        if note.len() > 4096 {
-            return http::bad_request("A note attached to a point may not be longer than 4 KiB.".into());
-        }
-    }
-
-    let mut ts = chrono::Utc::now();
-    if let Some(time) = time {
-        ts = util::flexible_timestamp_parse(time).unwrap_or(ts);
-    }
-
-    // Only store a note if one was attached.
-    let note = if let Some(note) = note {
-        if note.is_empty() {
-            None
-        } else {
-            Some(note)
-        }
-    } else {
-        note
-    };
     let secret = if let Some(secret) = secret {
         if secret.is_empty() {
             None
@@ -176,12 +155,24 @@
         secret
     };
 
+    let mut ts = chrono::Utc::now();
+    if let Some(time) = time {
+        ts = util::flexible_timestamp_parse(time).unwrap_or(ts);
+    }
+
+    // Length-limit notes.
+    let note = match http::read_data(note, 4096) {
+        Ok(n) => { if n.is_empty() { None } else { Some(n) } },
+        Err(e) => return e,
+    };
+
     let point = types::GeoPoint {
         lat: lat,
         long: longitude,
         time: ts,
         spd: s,
         ele: ele,
+        accuracy: accuracy,
         note: note,
     };
     if let Err(e) = db.log_geopoint(name.as_str(), &secret, &point) {
--- a/src/types.rs	Fri Dec 04 10:02:38 2020 +0100
+++ b/src/types.rs	Fri Dec 04 11:02:22 2020 +0100
@@ -5,6 +5,7 @@
     pub long: f64,
     pub spd: Option<f64>,
     pub ele: Option<f64>,
+    pub accuracy: Option<f64>,
     pub time: chrono::DateTime<chrono::Utc>,
     pub note: Option<String>,
 }
@@ -36,6 +37,7 @@
     time: chrono::DateTime<chrono::Utc>,
     altitude: Option<f64>,
     speed: Option<f64>,
+    accuracy: Option<f64>,
     /// The unique ID of the point.
     id: Option<i32>,
     /// An arbitrary note attached by the logging client.
@@ -88,6 +90,7 @@
             altitude: point.ele,
             speed: point.spd,
             note: point.note,
+            accuracy: point.accuracy,
         },
         geometry: GeoGeometry {
             typ: "Point".into(),