[#26] Moved all routing to server binary
parent
bffbb61124
commit
d19fe5c42e
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# This hook lints the code, and if we're on develop or master, also forces the tests to pass.
|
# This hook lints the code, and if we're on develop or master, also forces the tests to pass.
|
||||||
./fejctl lint &> /dev/null 2>&1 || {
|
./fejctl lint &> /dev/null 2>&1 || {
|
||||||
>&2 echo "Format check failed, use 'make lint' for more information.";
|
>&2 echo "Format check failed, use './fejctl lint' for more information.";
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
mod catchers;
|
||||||
use fej::{catchers, ivago};
|
mod routes;
|
||||||
|
|
||||||
// Very temporary solution for CORS
|
// Very temporary solution for CORS
|
||||||
// https://stackoverflow.com/questions/62412361/how-to-set-up-cors-or-options-for-rocket-rs
|
// https://stackoverflow.com/questions/62412361/how-to-set-up-cors-or-options-for-rocket-rs
|
||||||
|
@ -33,7 +35,7 @@ impl Fairing for CORS {
|
||||||
fn rocket() -> rocket::Rocket {
|
fn rocket() -> rocket::Rocket {
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.attach(CORS)
|
.attach(CORS)
|
||||||
.mount("/ivago", ivago::routes())
|
.mount("/ivago", routes::ivago())
|
||||||
.register(catchers![catchers::not_found])
|
.register(catchers![catchers::not_found])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
use fej::ivago::{get_pickup_times, search_streets, BasicDate, PickupTime, Street};
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
|
/// This route handles the Ivago search endpoint. It returns a list of streets,
|
||||||
|
/// consisting of a street name & a city.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `search_term` - Search term to use to look for streets
|
||||||
|
#[get("/search?<q>")]
|
||||||
|
pub fn route_search_streets(q: String) -> Result<Json<Vec<Street>>, Status> {
|
||||||
|
Ok(Json(search_streets(q.as_str())?))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles returning of pickup times for a specific address. It returns a list
|
||||||
|
/// of pickup times, containing a date and a description of the trash type.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `street` - Street to look up
|
||||||
|
/// * `number` - House number in the given street
|
||||||
|
/// * `start_date` - Earliest date that can be returned
|
||||||
|
/// * `end_date` - Latest date that can be returned
|
||||||
|
#[get("/?<street>&<number>&<start_date>&<end_date>")]
|
||||||
|
pub fn route_get_pickup_times(
|
||||||
|
street: Street,
|
||||||
|
number: u32,
|
||||||
|
start_date: BasicDate,
|
||||||
|
end_date: BasicDate,
|
||||||
|
) -> Result<Json<Vec<PickupTime>>, Status> {
|
||||||
|
Ok(Json(get_pickup_times(
|
||||||
|
&street,
|
||||||
|
&number,
|
||||||
|
&start_date.0,
|
||||||
|
&end_date.0,
|
||||||
|
)?))
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod ivago;
|
||||||
|
|
||||||
|
pub fn ivago() -> Vec<rocket::Route> {
|
||||||
|
routes![ivago::route_search_streets, ivago::route_get_pickup_times]
|
||||||
|
}
|
|
@ -1,95 +0,0 @@
|
||||||
use crate::errors::FejError;
|
|
||||||
use chrono::DateTime;
|
|
||||||
use chrono_tz::Tz;
|
|
||||||
use reqwest::blocking as reqwest;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::convert::{From, TryFrom};
|
|
||||||
|
|
||||||
mod basic_date;
|
|
||||||
mod pickup_time;
|
|
||||||
mod street;
|
|
||||||
|
|
||||||
pub use basic_date::BasicDate;
|
|
||||||
pub use pickup_time::PickupTime;
|
|
||||||
pub use street::Street;
|
|
||||||
|
|
||||||
/// Endpoint for the search feature
|
|
||||||
const SEARCH_URL: &str = "https://www.ivago.be/nl/particulier/autocomplete/garbage/streets";
|
|
||||||
/// Endpoint for populating the initial cookies
|
|
||||||
const BASE_URL: &str = "https://www.ivago.be/nl/particulier/afval/ophaling";
|
|
||||||
/// Endpoint for the actual calendar output
|
|
||||||
const CAL_URL: &str = "https://www.ivago.be/nl/particulier/garbage/pick-up/pickups";
|
|
||||||
|
|
||||||
/// Searches the Ivago API for streets in the given city.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `search_term` - Search term to use to look for streets
|
|
||||||
pub fn search_streets(search_term: &str) -> Result<Vec<Street>, FejError> {
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let response = client.get(SEARCH_URL).query(&[("q", search_term)]).send()?;
|
|
||||||
let data: Vec<HashMap<String, String>> = response.json()?;
|
|
||||||
|
|
||||||
// This is pretty cool, filter_map first does get() on all the maps, and
|
|
||||||
// then filters out any None values
|
|
||||||
// Then, we do the same thing for streets
|
|
||||||
Ok(data
|
|
||||||
.iter()
|
|
||||||
.filter_map(|m| m.get("value"))
|
|
||||||
.filter_map(|v| Street::try_from(v.as_str()).ok())
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the pickup times for the various trash types.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `street` - Street to look up
|
|
||||||
/// * `number` - House number in given street
|
|
||||||
/// * `start_date` - Earliest date for the results
|
|
||||||
/// * `end_date` - Latest date for the results
|
|
||||||
pub fn get_pickup_times(
|
|
||||||
street: &Street,
|
|
||||||
number: &u32,
|
|
||||||
start_date: &DateTime<Tz>,
|
|
||||||
end_date: &DateTime<Tz>,
|
|
||||||
) -> Result<Vec<PickupTime>, FejError> {
|
|
||||||
let client = reqwest::Client::builder().cookie_store(true).build()?;
|
|
||||||
|
|
||||||
// This populates the cookies with the necessary values
|
|
||||||
client
|
|
||||||
.post(BASE_URL)
|
|
||||||
.form(&[
|
|
||||||
("garbage_type", ""),
|
|
||||||
("ivago_street", &String::from(street)),
|
|
||||||
("number", &number.to_string()),
|
|
||||||
("form_id", "garbage_address_form"),
|
|
||||||
])
|
|
||||||
.send()?;
|
|
||||||
|
|
||||||
let response = client
|
|
||||||
.get(CAL_URL)
|
|
||||||
.query(&[
|
|
||||||
("_format", "json"),
|
|
||||||
("type", ""),
|
|
||||||
("start", &start_date.timestamp().to_string()),
|
|
||||||
("end", &end_date.timestamp().to_string()),
|
|
||||||
])
|
|
||||||
.send()?;
|
|
||||||
let data: Vec<HashMap<String, String>> = response.json()?;
|
|
||||||
|
|
||||||
let mut output: Vec<PickupTime> = Vec::new();
|
|
||||||
|
|
||||||
for map in data
|
|
||||||
.iter()
|
|
||||||
.filter(|m| m.contains_key("date") && m.contains_key("label"))
|
|
||||||
{
|
|
||||||
// Because we filtered the maps in the loop, we can safely use unwrap
|
|
||||||
// here
|
|
||||||
if let Ok(date) = BasicDate::try_from(map.get("date").unwrap().as_str()) {
|
|
||||||
output.push(PickupTime::new(date, map.get("label").unwrap().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
112
src/ivago/mod.rs
112
src/ivago/mod.rs
|
@ -1,45 +1,95 @@
|
||||||
mod controller;
|
use crate::errors::FejError;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
use reqwest::blocking as reqwest;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::{From, TryFrom};
|
||||||
|
|
||||||
use controller::{get_pickup_times, search_streets};
|
mod basic_date;
|
||||||
use controller::{BasicDate, PickupTime, Street};
|
mod pickup_time;
|
||||||
use rocket::http::Status;
|
mod street;
|
||||||
use rocket_contrib::json::Json;
|
|
||||||
|
|
||||||
pub fn routes() -> Vec<rocket::Route> {
|
pub use basic_date::BasicDate;
|
||||||
routes![route_search_streets, route_get_pickup_times]
|
pub use pickup_time::PickupTime;
|
||||||
}
|
pub use street::Street;
|
||||||
|
|
||||||
/// This route handles the Ivago search endpoint. It returns a list of streets,
|
/// Endpoint for the search feature
|
||||||
/// consisting of a street name & a city.
|
const SEARCH_URL: &str = "https://www.ivago.be/nl/particulier/autocomplete/garbage/streets";
|
||||||
|
/// Endpoint for populating the initial cookies
|
||||||
|
const BASE_URL: &str = "https://www.ivago.be/nl/particulier/afval/ophaling";
|
||||||
|
/// Endpoint for the actual calendar output
|
||||||
|
const CAL_URL: &str = "https://www.ivago.be/nl/particulier/garbage/pick-up/pickups";
|
||||||
|
|
||||||
|
/// Searches the Ivago API for streets in the given city.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `search_term` - Search term to use to look for streets
|
/// * `search_term` - Search term to use to look for streets
|
||||||
#[get("/search?<q>")]
|
pub fn search_streets(search_term: &str) -> Result<Vec<Street>, FejError> {
|
||||||
pub fn route_search_streets(q: String) -> Result<Json<Vec<Street>>, Status> {
|
let client = reqwest::Client::new();
|
||||||
Ok(Json(search_streets(q.as_str())?))
|
let response = client.get(SEARCH_URL).query(&[("q", search_term)]).send()?;
|
||||||
|
let data: Vec<HashMap<String, String>> = response.json()?;
|
||||||
|
|
||||||
|
// This is pretty cool, filter_map first does get() on all the maps, and
|
||||||
|
// then filters out any None values
|
||||||
|
// Then, we do the same thing for streets
|
||||||
|
Ok(data
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| m.get("value"))
|
||||||
|
.filter_map(|v| Street::try_from(v.as_str()).ok())
|
||||||
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles returning of pickup times for a specific address. It returns a list
|
/// Returns the pickup times for the various trash types.
|
||||||
/// of pickup times, containing a date and a description of the trash type.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `street` - Street to look up
|
/// * `street` - Street to look up
|
||||||
/// * `number` - House number in the given street
|
/// * `number` - House number in given street
|
||||||
/// * `start_date` - Earliest date that can be returned
|
/// * `start_date` - Earliest date for the results
|
||||||
/// * `end_date` - Latest date that can be returned
|
/// * `end_date` - Latest date for the results
|
||||||
#[get("/?<street>&<number>&<start_date>&<end_date>")]
|
pub fn get_pickup_times(
|
||||||
pub fn route_get_pickup_times(
|
street: &Street,
|
||||||
street: Street,
|
number: &u32,
|
||||||
number: u32,
|
start_date: &DateTime<Tz>,
|
||||||
start_date: BasicDate,
|
end_date: &DateTime<Tz>,
|
||||||
end_date: BasicDate,
|
) -> Result<Vec<PickupTime>, FejError> {
|
||||||
) -> Result<Json<Vec<PickupTime>>, Status> {
|
let client = reqwest::Client::builder().cookie_store(true).build()?;
|
||||||
Ok(Json(get_pickup_times(
|
|
||||||
&street,
|
// This populates the cookies with the necessary values
|
||||||
&number,
|
client
|
||||||
&start_date.0,
|
.post(BASE_URL)
|
||||||
&end_date.0,
|
.form(&[
|
||||||
)?))
|
("garbage_type", ""),
|
||||||
|
("ivago_street", &String::from(street)),
|
||||||
|
("number", &number.to_string()),
|
||||||
|
("form_id", "garbage_address_form"),
|
||||||
|
])
|
||||||
|
.send()?;
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get(CAL_URL)
|
||||||
|
.query(&[
|
||||||
|
("_format", "json"),
|
||||||
|
("type", ""),
|
||||||
|
("start", &start_date.timestamp().to_string()),
|
||||||
|
("end", &end_date.timestamp().to_string()),
|
||||||
|
])
|
||||||
|
.send()?;
|
||||||
|
let data: Vec<HashMap<String, String>> = response.json()?;
|
||||||
|
|
||||||
|
let mut output: Vec<PickupTime> = Vec::new();
|
||||||
|
|
||||||
|
for map in data
|
||||||
|
.iter()
|
||||||
|
.filter(|m| m.contains_key("date") && m.contains_key("label"))
|
||||||
|
{
|
||||||
|
// Because we filtered the maps in the loop, we can safely use unwrap
|
||||||
|
// here
|
||||||
|
if let Ok(date) = BasicDate::try_from(map.get("date").unwrap().as_str()) {
|
||||||
|
output.push(PickupTime::new(date, map.get("label").unwrap().to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate rocket;
|
|
||||||
|
|
||||||
// Route modules
|
// Route modules
|
||||||
pub mod ivago;
|
pub mod ivago;
|
||||||
|
|
||||||
// Helper modules
|
// Helper modules
|
||||||
pub mod catchers;
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|
Loading…
Reference in New Issue