cleanup code, update deps, remove keycloak lib

This commit is contained in:
Denys Konovalov 2021-11-21 16:58:21 +01:00
parent 0f87ca8b13
commit c7dc7ea6c1
4 changed files with 91 additions and 416 deletions

@ -11,12 +11,11 @@ authors = ["Denys Konovalov <denys.konovalov@protonmail.com>"]
rocket = { version = "0.5.0-rc.1", features = ["json"] } rocket = { version = "0.5.0-rc.1", features = ["json"] }
serde = "1.0" serde = "1.0"
diesel = { version = "1.4", features = ["postgres", "serde_json"] } diesel = { version = "1.4", features = ["postgres", "serde_json"] }
reqwest = "0.11" reqwest = { version="0.11", features = ["json"] }
quickxml_to_serde = "0.4" quickxml_to_serde = "0.4"
serde_json = "1.0" serde_json = "1.0"
jsonwebtoken = "7.2" jsonwebtoken = "7.2"
time = "0.2" time = "0.2"
keycloak = "14"
chrono = "0.4" chrono = "0.4"

@ -1,7 +1,6 @@
use crate::config; use crate::config;
use crate::schema::timetable; use crate::schema::timetable;
use crate::DbConn; use crate::DbConn;
use chrono::{Duration, Local};
use diesel::{Insertable, Queryable}; use diesel::{Insertable, Queryable};
use quickxml_to_serde::{xml_string_to_json, Config}; use quickxml_to_serde::{xml_string_to_json, Config};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -30,14 +29,11 @@ pub struct Lesson {
pub id: i64, pub id: i64,
} }
async fn get_today_timetable_xml() -> serde_json::value::Value { async fn get_timetable_xml(url: &str) -> serde_json::value::Value {
dbg!(format!("{}/{}", config::TIMETABLE_URL, url));
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let resp = client let resp = client
.get(format!( .get(format!("{}/{}", config::TIMETABLE_URL, url))
"{}/PlanKl{}.xml",
config::TIMETABLE_URL,
Local::today().format("%Y%m%d")
))
.basic_auth(config::TIMETABLE_USER, config::TIMETABLE_PASSWORD) .basic_auth(config::TIMETABLE_USER, config::TIMETABLE_PASSWORD)
.send() .send()
.await .await
@ -45,43 +41,12 @@ async fn get_today_timetable_xml() -> serde_json::value::Value {
.text() .text()
.await .await
.unwrap(); .unwrap();
dbg!(&resp);
xml_string_to_json(resp, &Config::new_with_defaults()).unwrap() xml_string_to_json(resp, &Config::new_with_defaults()).unwrap()
} }
async fn get_tomorrow_timetable_xml() -> serde_json::value::Value { async fn get_timetable_xml_data(url: &str) -> Vec<serde_json::value::Value> {
let client = reqwest::Client::new(); let xml = get_timetable_xml(url).await;
let resp = client
.get(format!(
"{}/PlanKl{}.xml",
config::TIMETABLE_URL,
(Local::today() + Duration::days(1)).format("%Y%m%d")
))
.basic_auth(config::TIMETABLE_USER, config::TIMETABLE_PASSWORD)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
xml_string_to_json(resp, &Config::new_with_defaults()).unwrap()
}
async fn get_timetable_xml() -> serde_json::value::Value {
let client = reqwest::Client::new();
let resp = client
.get(format!("{}/Klassen.xml", config::TIMETABLE_URL))
.basic_auth(config::TIMETABLE_USER, config::TIMETABLE_PASSWORD)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
xml_string_to_json(resp, &Config::new_with_defaults()).unwrap()
}
async fn get_today_timetable_xml_data() -> Vec<serde_json::value::Value> {
let xml = get_today_timetable_xml().await;
let classes = xml let classes = xml
.as_object() .as_object()
.unwrap() .unwrap()
@ -96,41 +61,9 @@ async fn get_today_timetable_xml_data() -> Vec<serde_json::value::Value> {
classes.to_owned() classes.to_owned()
} }
async fn get_tomorrow_timetable_xml_data() -> Vec<serde_json::value::Value> { pub async fn get_timetable(_conn: DbConn, url: String) -> Vec<Timetable> {
let xml = get_tomorrow_timetable_xml().await; let xml = get_timetable_xml(&url).await;
let classes = xml let classes = get_timetable_xml_data(&url).await;
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Klassen")
.unwrap()
.get("Kl")
.unwrap()
.as_array()
.unwrap();
classes.to_owned()
}
async fn get_timetable_xml_data() -> Vec<serde_json::value::Value> {
let xml = get_timetable_xml().await;
let classes = xml
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Klassen")
.unwrap()
.get("Kl")
.unwrap()
.as_array()
.unwrap();
classes.to_owned()
}
pub async fn get_today_timetable(_conn: DbConn) -> Vec<Timetable> {
let xml = get_today_timetable_xml().await;
let classes = get_today_timetable_xml_data().await;
let mut timetable: Vec<Timetable> = Vec::new(); let mut timetable: Vec<Timetable> = Vec::new();
for i in classes.iter() { for i in classes.iter() {
let mut courses: Vec<rocket::serde::json::Value> = Vec::new(); let mut courses: Vec<rocket::serde::json::Value> = Vec::new();
@ -221,331 +154,9 @@ pub async fn get_today_timetable(_conn: DbConn) -> Vec<Timetable> {
timetable timetable
} }
pub async fn get_tomorrow_timetable(_conn: DbConn) -> Vec<Timetable> { pub async fn get_class_timetable(_conn: DbConn, class: String, url: String) -> TimetableData {
let xml = get_tomorrow_timetable_xml().await; let xml = get_timetable_xml(&url).await;
let classes = get_tomorrow_timetable_xml_data().await; let classes = get_timetable_xml_data(&url).await;
let mut timetable: Vec<Timetable> = Vec::new();
for i in classes.iter() {
let mut courses: Vec<rocket::serde::json::Value> = Vec::new();
let nothing = json!([""]);
let std = i
.as_object()
.unwrap()
.get("Pl")
.unwrap()
.as_object()
.unwrap()
.get("Std")
.unwrap_or(&nothing);
let mut plan = vec![];
if std.is_array() {
plan.extend(std.as_array().unwrap().iter().cloned())
} else if std.is_object() {
plan.push(std.clone())
}
for i in &plan {
if i.as_object() != None {
courses.push(i.to_owned());
} else {
dbg!("Failed: {:?}", &i);
}
}
let empty_list = serde_json::Value::Array(vec![]);
let empty_obj = serde_json::Value::Object(Map::new());
let empty_vec = Vec::new();
let info_list = xml
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("ZusatzInfo")
.unwrap_or(&empty_obj)
.get("ZiZeile")
.unwrap_or(&empty_list)
.as_array()
.unwrap_or(&empty_vec);
let mut info = String::new();
for item in info_list {
info.push_str(item.as_str().unwrap_or("\r\n"));
info.push_str("\r\n");
}
let response = TimetableData {
count: plan.len(),
courses,
info,
};
let timetable_element = Timetable {
date: String::from(
xml.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Kopf")
.unwrap()
.get("DatumPlan")
.unwrap()
.as_str()
.unwrap(),
),
updated: String::from(
xml.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Kopf")
.unwrap()
.get("zeitstempel")
.unwrap()
.as_str()
.unwrap(),
),
class: String::from(
i.as_object()
.unwrap()
.get("Kurz")
.unwrap()
.as_str()
.unwrap(),
),
timetable_data: serde_json::from_str(&json!(response).to_string()).unwrap(),
};
timetable.push(timetable_element)
}
timetable
}
pub async fn get_timetable(_conn: DbConn) -> Vec<Timetable> {
let xml = get_timetable_xml().await;
let classes = get_timetable_xml_data().await;
let mut timetable: Vec<Timetable> = Vec::new();
for i in classes.iter() {
let mut courses: Vec<rocket::serde::json::Value> = Vec::new();
let nothing = json!([""]);
let std = i
.as_object()
.unwrap()
.get("Pl")
.unwrap()
.as_object()
.unwrap()
.get("Std")
.unwrap_or(&nothing);
let mut plan = vec![];
if std.is_array() {
plan.extend(std.as_array().unwrap().iter().cloned())
} else if std.is_object() {
plan.push(std.clone())
}
for i in &plan {
if i.as_object() != None {
courses.push(i.to_owned());
} else {
dbg!("Failed: {:?}", &i);
}
}
let empty_list = serde_json::Value::Array(vec![]);
let empty_obj = serde_json::Value::Object(Map::new());
let empty_vec = Vec::new();
let info_list = xml
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("ZusatzInfo")
.unwrap_or(&empty_obj)
.get("ZiZeile")
.unwrap_or(&empty_list)
.as_array()
.unwrap_or(&empty_vec);
let mut info = String::new();
for item in info_list {
info.push_str(item.as_str().unwrap_or("\r\n"));
info.push_str("\r\n");
}
let response = TimetableData {
count: plan.len(),
courses,
info,
};
let timetable_element = Timetable {
date: String::from(
xml.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Kopf")
.unwrap()
.get("DatumPlan")
.unwrap()
.as_str()
.unwrap(),
),
updated: String::from(
xml.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("Kopf")
.unwrap()
.get("zeitstempel")
.unwrap()
.as_str()
.unwrap(),
),
class: String::from(
i.as_object()
.unwrap()
.get("Kurz")
.unwrap()
.as_str()
.unwrap(),
),
timetable_data: serde_json::from_str(&json!(response).to_string()).unwrap(),
};
timetable.push(timetable_element)
}
timetable
}
pub async fn get_today_class_timetable(_conn: DbConn, class: String) -> TimetableData {
let xml = get_today_timetable_xml().await;
let classes = get_today_timetable_xml_data().await;
let courses: Vec<rocket::serde::json::Value> = Vec::new();
let empty_list = serde_json::Value::Array(vec![]);
let empty_obj = serde_json::Value::Object(Map::new());
let empty_vec = Vec::new();
let info_list = xml
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("ZusatzInfo")
.unwrap_or(&empty_obj)
.get("ZiZeile")
.unwrap_or(&empty_list)
.as_array()
.unwrap_or(&empty_vec);
let mut info = String::new();
for item in info_list {
info.push_str(item.as_str().unwrap_or("\r\n"));
info.push_str("\r\n");
}
let mut response = TimetableData {
count: 0,
courses,
info,
};
for i in classes.iter() {
if i.as_object()
.unwrap()
.get("Kurz")
.unwrap()
.as_str()
.unwrap()
.replace("/", "_")
== class
{
let nothing = json!([""]);
let std = i
.as_object()
.unwrap()
.get("Pl")
.unwrap()
.as_object()
.unwrap()
.get("Std")
.unwrap_or(&nothing);
let mut plan = vec![];
if std.is_array() {
plan.extend(std.as_array().unwrap().iter().cloned())
} else if std.is_object() {
plan.push(std.clone())
}
response.count = plan.len();
for i in plan {
if i.as_object() != None {
response.courses.push(i.to_owned());
} else {
dbg!("Failed: {:?}", &i);
}
}
break;
}
}
response
}
pub async fn get_tomorrow_class_timetable(_conn: DbConn, class: String) -> TimetableData {
let xml = get_tomorrow_timetable_xml().await;
let classes = get_tomorrow_timetable_xml_data().await;
let courses: Vec<rocket::serde::json::Value> = Vec::new();
let empty_list = serde_json::Value::Array(vec![]);
let empty_obj = serde_json::Value::Object(Map::new());
let empty_vec = Vec::new();
let info_list = xml
.as_object()
.unwrap()
.get("VpMobil")
.unwrap()
.get("ZusatzInfo")
.unwrap_or(&empty_obj)
.get("ZiZeile")
.unwrap_or(&empty_list)
.as_array()
.unwrap_or(&empty_vec);
let mut info = String::new();
for item in info_list {
info.push_str(item.as_str().unwrap_or("\r\n"));
info.push_str("\r\n");
}
let mut response = TimetableData {
count: 0,
courses,
info,
};
for i in classes.iter() {
if i.as_object()
.unwrap()
.get("Kurz")
.unwrap()
.as_str()
.unwrap()
.replace("/", "_")
== class
{
let nothing = json!([""]);
let std = i
.as_object()
.unwrap()
.get("Pl")
.unwrap()
.as_object()
.unwrap()
.get("Std")
.unwrap_or(&nothing);
let mut plan = vec![];
if std.is_array() {
plan.extend(std.as_array().unwrap().iter().cloned())
} else if std.is_object() {
plan.push(std.clone())
}
response.count = plan.len();
for i in plan {
if i.as_object() != None {
response.courses.push(i.to_owned());
} else {
dbg!("Failed: {:?}", &i);
}
}
break;
}
}
response
}
pub async fn get_class_timetable(_conn: DbConn, class: String) -> TimetableData {
let xml = get_timetable_xml().await;
let classes = get_timetable_xml_data().await;
let courses: Vec<rocket::serde::json::Value> = Vec::new(); let courses: Vec<rocket::serde::json::Value> = Vec::new();
let empty_list = serde_json::Value::Array(vec![]); let empty_list = serde_json::Value::Array(vec![]);
let empty_obj = serde_json::Value::Object(Map::new()); let empty_obj = serde_json::Value::Object(Map::new());
@ -612,7 +223,7 @@ pub async fn get_class_timetable(_conn: DbConn, class: String) -> TimetableData
} }
pub async fn get_classes() -> Vec<String> { pub async fn get_classes() -> Vec<String> {
let classes = get_timetable_xml_data().await; let classes = get_timetable_xml_data(&String::from("Klassen.xml")).await;
let mut class_list: Vec<String> = Vec::new(); let mut class_list: Vec<String> = Vec::new();
for i in classes.iter() { for i in classes.iter() {
class_list.push(String::from( class_list.push(String::from(
@ -628,7 +239,7 @@ pub async fn get_classes() -> Vec<String> {
} }
pub async fn get_class_lessons(class: String) -> Vec<Lesson> { pub async fn get_class_lessons(class: String) -> Vec<Lesson> {
let classes = get_timetable_xml_data().await; let classes = get_timetable_xml_data(&String::from("Klassen.xml")).await;
let mut lesson_list: Vec<Lesson> = Vec::new(); let mut lesson_list: Vec<Lesson> = Vec::new();
for i in classes.iter() { for i in classes.iter() {
let empty_list = serde_json::Value::Array(Vec::new()); let empty_list = serde_json::Value::Array(Vec::new());

@ -4,11 +4,11 @@ use crate::config;
use crate::{Claims, Credentials, Roles, Token, TokenStatus}; use crate::{Claims, Credentials, Roles, Token, TokenStatus};
use jsonwebtoken::{encode, EncodingKey, Header}; use jsonwebtoken::{encode, EncodingKey, Header};
use keycloak::KeycloakError;
use rocket::{response::status, serde::json::Json}; use rocket::{response::status, serde::json::Json};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
use std::error::Error; use std::error::Error;
use std::fmt::Display;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use time::OffsetDateTime; use time::OffsetDateTime;
@ -40,6 +40,45 @@ pub struct KeycloakUser {
email: String, email: String,
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct KeycloakHttpError {
pub error: Option<String>,
#[serde(rename = "errorMessage")]
pub error_message: Option<String>,
}
#[derive(Debug)]
pub enum KeycloakError {
ReqwestFailure(reqwest::Error),
HttpFailure {
status: u16,
body: Option<KeycloakHttpError>,
text: String,
},
}
impl From<reqwest::Error> for KeycloakError {
fn from(value: reqwest::Error) -> Self {
KeycloakError::ReqwestFailure(value)
}
}
impl Error for KeycloakError {
fn description(&self) -> &str {
"keycloak error"
}
fn cause(&self) -> Option<&dyn Error> {
None
}
}
impl Display for KeycloakError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "keycloak error")
}
}
async fn error_check(response: reqwest::Response) -> Result<reqwest::Response, KeycloakError> { async fn error_check(response: reqwest::Response) -> Result<reqwest::Response, KeycloakError> {
if !response.status().is_success() { if !response.status().is_success() {
let status = response.status().into(); let status = response.status().into();

@ -13,6 +13,7 @@ extern crate reqwest;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
use chrono::{Duration, Local};
use indiware_connector as timetable_connector; use indiware_connector as timetable_connector;
use jsonwebtoken::{decode, DecodingKey, Validation}; use jsonwebtoken::{decode, DecodingKey, Validation};
use keycloak_connector::KeycloakUser; use keycloak_connector::KeycloakUser;
@ -163,11 +164,11 @@ async fn login(
} }
#[get("/")] #[get("/")]
async fn get_timetable( async fn get_latest_timetable(
conn: DbConn, conn: DbConn,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<Vec<timetable_connector::Timetable>> { ) -> Json<Vec<timetable_connector::Timetable>> {
let timetable = timetable_connector::get_timetable(conn).await; let timetable = timetable_connector::get_timetable(conn, String::from("Klassen.xml")).await;
Json::from(timetable) Json::from(timetable)
} }
@ -176,7 +177,11 @@ async fn get_today_timetable(
conn: DbConn, conn: DbConn,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<Vec<timetable_connector::Timetable>> { ) -> Json<Vec<timetable_connector::Timetable>> {
let timetable = timetable_connector::get_today_timetable(conn).await; let timetable = timetable_connector::get_timetable(
conn,
format!("PlanKl{}.xml", Local::today().format("%Y%m%d")),
)
.await;
Json::from(timetable) Json::from(timetable)
} }
@ -185,17 +190,25 @@ async fn get_tomorrow_timetable(
conn: DbConn, conn: DbConn,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<Vec<timetable_connector::Timetable>> { ) -> Json<Vec<timetable_connector::Timetable>> {
let timetable = timetable_connector::get_tomorrow_timetable(conn).await; let timetable = timetable_connector::get_timetable(
conn,
format!(
"PlanKl{}.xml",
(Local::today() + Duration::days(1)).format("%Y%m%d")
),
)
.await;
Json::from(timetable) Json::from(timetable)
} }
#[get("/<class>")] #[get("/<class>")]
async fn get_class_timetable( async fn get_latest_class_timetable(
conn: DbConn, conn: DbConn,
class: String, class: String,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<timetable_connector::TimetableData> { ) -> Json<timetable_connector::TimetableData> {
let timetable = timetable_connector::get_class_timetable(conn, class).await; let timetable =
timetable_connector::get_class_timetable(conn, class, String::from("Klassen.xml")).await;
Json::from(timetable) Json::from(timetable)
} }
@ -205,7 +218,12 @@ async fn get_today_class_timetable(
class: String, class: String,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<timetable_connector::TimetableData> { ) -> Json<timetable_connector::TimetableData> {
let timetable = timetable_connector::get_today_class_timetable(conn, class).await; let timetable = timetable_connector::get_class_timetable(
conn,
class,
format!("PlanKl{}.xml", Local::today().format("%Y%m%d")),
)
.await;
Json::from(timetable) Json::from(timetable)
} }
@ -215,7 +233,15 @@ async fn get_tomorrow_class_timetable(
class: String, class: String,
_key: ApiKey<'_>, _key: ApiKey<'_>,
) -> Json<timetable_connector::TimetableData> { ) -> Json<timetable_connector::TimetableData> {
let timetable = timetable_connector::get_tomorrow_class_timetable(conn, class).await; let timetable = timetable_connector::get_class_timetable(
conn,
class,
format!(
"PlanKl{}.xml",
(Local::today() + Duration::days(1)).format("%Y%m%d")
),
)
.await;
Json::from(timetable) Json::from(timetable)
} }
@ -243,8 +269,8 @@ fn rocket() -> _ {
.mount( .mount(
"/api/timetable", "/api/timetable",
routes![ routes![
get_timetable, get_latest_timetable,
get_class_timetable, get_latest_class_timetable,
get_today_timetable, get_today_timetable,
get_today_class_timetable, get_today_class_timetable,
get_tomorrow_timetable, get_tomorrow_timetable,