Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add event list to new frontend #500

Merged
merged 2 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ofdb-app-clearance/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions ofdb-boundary/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ rust-version.workspace = true

[dependencies]
serde = { version = "1.0.188", features = ["derive"] }
time = { version = "0.3.28", features = ["serde"] }

[dependencies.time]
version = "0.3.28"
default-features = false
features = ["serde"]

[dependencies.ofdb-entities]
version = "0.12.0"
version = "0.12.6"
path = "../ofdb-entities"
optional = true

Expand All @@ -24,10 +28,10 @@ version = "1.0.48"
optional = true

[features]
default = ["entity-conversions", "thiserror"]
extra-derive = ["thiserror"]
entity-conversions = ["ofdb-entities"]
wasm-bindgen = ["ofdb-entities/wasm-bindgen"]
default = ["entity-conversions", "dep:thiserror"]
extra-derive = ["dep:thiserror"]
entity-conversions = ["dep:ofdb-entities"]
wasm-bindgen = ["ofdb-entities/wasm-bindgen", "time/wasm-bindgen"]

[badges]
maintenance = { status = "actively-developed" }
13 changes: 13 additions & 0 deletions ofdb-boundary/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ pub struct UnixTimeMillis(i64);
#[cfg_attr(feature = "extra-derive", derive(Debug, Clone, Copy, Eq, PartialEq))]
pub struct UnixTimeSeconds(i64);

impl From<time::OffsetDateTime> for UnixTimeSeconds {
fn from(from: time::OffsetDateTime) -> Self {
Self(from.unix_timestamp())
}
}

impl TryFrom<UnixTimeSeconds> for time::OffsetDateTime {
type Error = time::error::ComponentRange;
fn try_from(from: UnixTimeSeconds) -> Result<Self, Self::Error> {
Self::from_unix_timestamp(from.0)
}
}

#[rustfmt::skip]
#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "extra-derive", derive(Debug, Clone, PartialEq))]
Expand Down
1 change: 1 addition & 0 deletions ofdb-frontend-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ rust-version.workspace = true
[dependencies]
percent-encoding = "2.3.0"
serde = "1.0.188"
serde_json = "1.0.107"
thiserror = "1.0.48"

[dependencies.gloo-net]
Expand Down
14 changes: 13 additions & 1 deletion ofdb-frontend-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@ impl From<gloo_net::Error> for Error {
}
}

impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::Fetch(format!("{err}"))
}
}

pub async fn into_json<T>(response: Response) -> Result<T>
where
T: DeserializeOwned,
{
// ensure we've got 2xx status
if response.ok() {
Ok(response.json().await?)
let data = if response.status() == 204 {
// No content
serde_json::from_value(serde_json::Value::Null)?
} else {
response.json().await?
};
Ok(data)
} else {
Err(response.json::<ofdb_boundary::Error>().await?.into())
}
Expand Down
113 changes: 109 additions & 4 deletions ofdb-frontend-api/src/public.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use gloo_net::http::Request;
use ofdb_boundary::*;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use web_sys::RequestCredentials;

use ofdb_boundary::*;

use crate::{into_json, Result, UserApi};

/// Public OpenFairDB API
Expand All @@ -11,15 +12,52 @@ pub struct PublicApi {
url: &'static str,
}

#[derive(Debug, Clone, Default)]
pub struct EventQuery {
pub bbox: Option<MapBbox>,
pub limit: Option<u64>,
pub tags: Vec<String>,
pub start_min: Option<i64>,
pub start_max: Option<i64>,
pub end_min: Option<i64>,
pub end_max: Option<i64>,
pub text: Option<String>,
pub created_by: Option<String>,
}

impl EventQuery {
pub fn is_empty(&self) -> bool {
let Self {
bbox,
limit,
tags,
start_min,
start_max,
end_min,
end_max,
text,
created_by,
} = self;
bbox.is_none()
&& limit.is_none()
&& tags.is_empty()
&& start_min.is_none()
&& start_max.is_none()
&& end_min.is_none()
&& end_max.is_none()
&& text.is_none()
&& created_by.is_none()
}
}

impl PublicApi {
pub const fn new(url: &'static str) -> Self {
Self { url }
}
pub async fn search(&self, text: &str, bbox: &MapBbox) -> Result<SearchResponse> {
let encoded_txt = utf8_percent_encode(text, NON_ALPHANUMERIC);
let MapBbox { sw, ne } = bbox;
let bbox_str = format!("{},{},{},{}", sw.lat, sw.lng, ne.lat, ne.lng);
let url = format!("{}/search?text={encoded_txt}&bbox={bbox_str}", self.url);
let bbox_string = bbox_string(bbox);
let url = format!("{}/search?text={encoded_txt}&bbox={bbox_string}", self.url);
let response = Request::get(&url).send().await?;
into_json(response).await
}
Expand Down Expand Up @@ -51,6 +89,68 @@ impl PublicApi {
let response = Request::get(&url).send().await?;
into_json(response).await
}
pub async fn events(&self, query: &EventQuery) -> Result<Vec<Event>> {
let mut url = format!("{}/events", self.url);
if !query.is_empty() {
let EventQuery {
bbox,
limit,
tags,
start_min,
start_max,
end_min,
end_max,
text,
created_by,
} = query;
let mut params = vec![];

if let Some(bbox) = bbox {
let bbox_string = bbox_string(bbox);
params.push(("bbox", bbox_string));
}
if let Some(limit) = limit {
params.push(("limit", limit.to_string()));
}
if !tags.is_empty() {
params.push(("tag", tags.join(",")));
}
if let Some(start_min) = start_min {
params.push(("start_min", start_min.to_string()));
}
if let Some(start_max) = start_max {
params.push(("start_max", start_max.to_string()));
}
if let Some(end_min) = end_min {
params.push(("end_min", end_min.to_string()));
}
if let Some(end_max) = end_max {
params.push(("end_max", end_max.to_string()));
}
if let Some(text) = text {
let encoded_text = utf8_percent_encode(text, NON_ALPHANUMERIC);
params.push(("text", encoded_text.to_string()));
}
if let Some(email) = created_by {
let encoded_email = utf8_percent_encode(email.as_str(), NON_ALPHANUMERIC);
params.push(("created_by", encoded_email.to_string()));
}
let params = params
.into_iter()
.map(|(key, value)| [key, &value].join("="))
.collect::<Vec<_>>()
.join("&");
url = format!("{url}?{params}");
}
let response = Request::get(&url).send().await?;
into_json(response).await
}
pub async fn event(&self, id: &str) -> Result<Event> {
let url = format!("{}/events/{id}", self.url);
let request = Request::get(&url);
let response = request.send().await?;
into_json(response).await
}
pub async fn create_place(&self, place: &NewPlace) -> Result<()> {
let url = format!("{}/entries", self.url);
let request = Request::post(&url).json(place)?;
Expand Down Expand Up @@ -107,3 +207,8 @@ impl PublicApi {
into_json(response).await
}
}

fn bbox_string(bbox: &MapBbox) -> String {
let MapBbox { sw, ne } = bbox;
format!("{},{},{},{}", sw.lat, sw.lng, ne.lat, ne.lng)
}
16 changes: 11 additions & 5 deletions ofdb-frontend-api/src/user.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use gloo_net::http::{Request, RequestBuilder};
use ofdb_boundary::*;
use serde::de::DeserializeOwned;
use web_sys::RequestCredentials;

use ofdb_boundary::*;

use crate::{into_json, Result};

/// Authorized OpenFairDB API
Expand Down Expand Up @@ -32,8 +33,8 @@ impl UserApi {
}
pub async fn user_info(&self) -> Result<User> {
let url = format!("{}/users/current", self.url);
self.send(Request::get(&url).credentials(RequestCredentials::Include))
.await
let request = Request::get(&url).credentials(RequestCredentials::Include);
self.send(request).await
}
pub async fn bbox_subscriptions(&self) -> Result<Vec<BboxSubscription>> {
let url = format!("{}/bbox-subscriptions", self.url);
Expand All @@ -45,10 +46,15 @@ impl UserApi {
}
pub async fn logout(&self) -> Result<()> {
let url = format!("{}/logout", self.url);
self.send(Request::post(&url).credentials(RequestCredentials::Include))
.await
let request = Request::post(&url).credentials(RequestCredentials::Include);
self.send(request).await
}
pub fn token(&self) -> &JwtToken {
&self.token
}
pub async fn archive_events(&self, ids: &[&str]) -> Result<()> {
let url = format!("{}/events/{}/archive", self.url, ids.join(","));
let request = Request::post(&url).credentials(RequestCredentials::Include);
self.send(request).await
}
}
Loading
Loading