Skip to content

Commit

Permalink
feat(room-api): Add GET /rooms-v2 endpoint returning rooms in geojson…
Browse files Browse the repository at this point in the history
… format

Adds a new rooms-v2 endpoint for returning changing rooms in geojson format.
Should make it more intuitive to map the changing rooms using Streamlit/Pydeck.

Kind of related to #14
  • Loading branch information
christianfosli committed Dec 30, 2024
1 parent 1d2c097 commit 4e2c8fc
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 7 deletions.
74 changes: 72 additions & 2 deletions room-api/src/get_rooms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ use std::sync::LazyLock;

use axum::{
extract::{Path, State},
http::StatusCode,
http::{header, StatusCode},
response::IntoResponse,
Json,
};
use futures::TryStreamExt;
use geojson::{feature::Id, Feature, FeatureCollection, JsonObject};
use mongodb::{
bson::{doc, Uuid},
Database,
};
use serde_json::Value;

use crate::models::ChangingRoom;

Expand Down Expand Up @@ -42,6 +45,73 @@ pub async fn get_all_rooms(
Ok(Json(rooms))
}

pub async fn get_all_rooms_v2(
State(db): State<Database>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let collection = db.collection::<ChangingRoom>("rooms");

let rooms: Vec<ChangingRoom> = collection
.find(doc! {})
.await
.map_err(|e| {
tracing::error!(err = e.to_string(), "Unable to get cursor for all rooms");
GENERIC_DB_ERROR.clone()
})?
.try_collect()
.await
.map_err(|e| {
tracing::error!(err = e.to_string(), "Unable to collect rooms into Vec");
GENERIC_DB_ERROR.clone()
})?;

let rooms_geo = rooms
.into_iter()
.filter_map(|r| {
// ^ Replace with ord map when data migr complete

// Properties
let mut props = JsonObject::new();
props.insert(String::from("name"), Value::String(r.name));
props.insert(
String::from("ratings"),
serde_json::to_value(r.ratings)
.inspect_err(|e| {
tracing::error!(
err = e.to_string(),
room_id = r.id.to_string(),
"Unable to serialize rating"
);
})
.unwrap(),
);
if let Some(ext_id) = r.external_id {
props.insert(String::from("externalId"), Value::String(ext_id));
} else {
props.insert(String::from("externalId"), Value::Null);
}

// Geo Feature
r.location_geo.map(|geo| Feature {
bbox: None,
geometry: Some(geo),
id: Some(Id::String(r.id.to_string())),
properties: Some(props),
foreign_members: None,
})
})
.collect::<Vec<_>>();

Ok((
StatusCode::OK,
[(header::CONTENT_TYPE, "application/geo+json")],
Json(FeatureCollection {
bbox: None,
features: rooms_geo,
foreign_members: None,
}),
))
}

pub async fn get_room_by_id(
Path(id): Path<String>,
State(db): State<Database>,
Expand All @@ -64,7 +134,7 @@ pub async fn get_room_by_id(
Some(room) => Ok(Json(room)),
None => Err((
StatusCode::NOT_FOUND,
format!("No room found with id {:?}", id),
format!("No room found with id {id:?}"),
)),
}
}
3 changes: 2 additions & 1 deletion room-api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tower_http::cors::CorsLayer;

use crate::create_room::create_room;
use crate::delete_room::delete_room;
use crate::get_rooms::{get_all_rooms, get_room_by_id};
use crate::get_rooms::{get_all_rooms, get_all_rooms_v2, get_room_by_id};
use crate::healthcheck::{live, ready};
use crate::update_room::update_room;

Expand All @@ -32,6 +32,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.route("/livez", routing::get(live))
.route("/rooms", routing::post(create_room))
.route("/rooms", routing::get(get_all_rooms))
.route("/rooms-v2", routing::get(get_all_rooms_v2))
.route("/rooms/:id", routing::get(get_room_by_id))
.route("/rooms/:id", routing::put(update_room))
.route("/rooms/:id", routing::delete(delete_room))
Expand Down
8 changes: 4 additions & 4 deletions room-api/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ pub struct Location {

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Ratings {
availability: StarRating,
safety: StarRating,
cleanliness: StarRating,
pub availability: StarRating,
pub safety: StarRating,
pub cleanliness: StarRating,
}

type StarRating = BoundedU8<1, 5>;
pub type StarRating = BoundedU8<1, 5>;

0 comments on commit 4e2c8fc

Please sign in to comment.