diff --git a/src/element/model.rs b/src/element/model.rs index f3520f8..18e71b8 100644 --- a/src/element/model.rs +++ b/src/element/model.rs @@ -3,8 +3,9 @@ use crate::{osm::overpass::OverpassElement, Error}; use deadpool_sqlite::Pool; use rusqlite::{named_params, Connection, OptionalExtension, Row}; use serde_json::{Map, Value}; +use std::collections::HashMap; +use std::sync::Arc; use std::time::{Duration, Instant}; -use std::{collections::HashMap, sync::Arc}; use time::{format_description::well_known::Rfc3339, OffsetDateTime}; use tokio::time::sleep; use tracing::{debug, info}; @@ -211,6 +212,18 @@ impl Element { .collect::, _>>()?) } + pub fn select_by_id_or_osm_id(id: &str, conn: &Connection) -> Result> { + match id.parse::() { + Ok(id) => Element::select_by_id(id, conn), + Err(_) => { + let parts: Vec<_> = id.split(':').collect(); + let osm_type = parts[0]; + let osm_id = parts[1].parse::().unwrap(); + Element::select_by_osm_type_and_id(osm_type, osm_id, conn) + } + } + } + pub fn select_by_id(id: i64, conn: &Connection) -> Result> { let query = format!( r#" @@ -451,15 +464,12 @@ const fn mapper() -> fn(&Row) -> rusqlite::Result { #[cfg(test)] mod test { - use std::collections::HashMap; - + use super::Element; + use crate::{osm::overpass::OverpassElement, test::mock_conn, Result}; use serde_json::json; + use std::collections::HashMap; use time::{macros::datetime, OffsetDateTime}; - use crate::{osm::overpass::OverpassElement, test::mock_conn, Result}; - - use super::Element; - #[test] fn insert() -> Result<()> { let conn = mock_conn(); diff --git a/src/element/v3.rs b/src/element/v3.rs index f581783..ddcac07 100644 --- a/src/element/v3.rs +++ b/src/element/v3.rs @@ -5,11 +5,14 @@ use crate::Error; use actix_web::get; use actix_web::web::Data; use actix_web::web::Json; +use actix_web::web::Path; use actix_web::web::Query; +use deadpool_sqlite::Pool; use serde::Deserialize; use serde::Serialize; use serde_json::Value; use std::collections::HashMap; +use std::sync::Arc; use time::OffsetDateTime; #[derive(Deserialize)] @@ -76,6 +79,19 @@ pub async fn get( )) } +#[get("{id}")] +pub async fn get_by_id(id: Path, pool: Data>) -> Result, Error> { + let id_clone = id.clone(); + pool.get() + .await? + .interact(move |conn| Element::select_by_id_or_osm_id(&id_clone, conn)) + .await?? + .ok_or(Error::HttpNotFound(format!( + "Element with id {id} doesn't exist" + ))) + .map(|it| it.into()) +} + #[cfg(test)] mod test { use crate::element::ElementRepo; diff --git a/src/osm/overpass.rs b/src/osm/overpass.rs index 35e18c5..8d5869a 100644 --- a/src/osm/overpass.rs +++ b/src/osm/overpass.rs @@ -33,7 +33,9 @@ struct Osm3s { pub struct OverpassElement { pub r#type: String, pub id: i64, + #[serde(skip_serializing_if = "Option::is_none")] pub lat: Option, // for nodes only + #[serde(skip_serializing_if = "Option::is_none")] pub lon: Option, // for nodes only pub timestamp: Option, pub version: Option, @@ -41,9 +43,13 @@ pub struct OverpassElement { pub user: Option, pub uid: Option, pub tags: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub bounds: Option, // for ways and relations only + #[serde(skip_serializing_if = "Option::is_none")] pub nodes: Option, // for ways only + #[serde(skip_serializing_if = "Option::is_none")] pub geometry: Option, // for ways only + #[serde(skip_serializing_if = "Option::is_none")] pub members: Option, // for relations only } diff --git a/src/server/mod.rs b/src/server/mod.rs index ec08c34..0b0737b 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -141,7 +141,11 @@ pub async fn run() -> Result<()> { .service( scope("v3") .wrap(Governor::new(&rate_limit_conf)) - .service(scope("elements").service(element::v3::get)) + .service( + scope("elements") + .service(element::v3::get) + .service(element::v3::get_by_id), + ) .service( scope("events") .service(event::v3::get)