From cf3ee4feef3411668580424f60b4169a3c0b3b3b Mon Sep 17 00:00:00 2001 From: Ben Sherriff Date: Mon, 4 Sep 2023 20:15:05 -0400 Subject: [PATCH] Error handling --- backend/Cargo.lock | 16 +++ backend/Cargo.toml | 1 + backend/migrations/create_airports/up.sql | 8 +- backend/src/airports/routes.rs | 46 +++++-- backend/src/main.rs | 19 ++- backend/src/metars/routes.rs | 17 ++- src/app/page.tsx | 32 +++-- src/components/Metar.tsx | 2 +- src/js/airport.ts | 6 +- src/js/weather.ts | 140 ++++++++++++---------- 10 files changed, 182 insertions(+), 105 deletions(-) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 0ff41ec..3ca63ae 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -19,6 +19,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "actix-cors" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + [[package]] name = "actix-http" version = "3.4.0" @@ -258,6 +273,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" name = "aviation-weather-backend" version = "0.1.0" dependencies = [ + "actix-cors", "actix-rt", "actix-web", "chrono", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index b50ad77..bbd0fe5 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -23,3 +23,4 @@ serde_json = "1.0.105" tokio = { version = "1.32.0", features = ["macros", "rt"] } uuid = { version = "1.4.1", features = ["serde", "v4"] } log = "0.4.20" +actix-cors = "0.6.4" diff --git a/backend/migrations/create_airports/up.sql b/backend/migrations/create_airports/up.sql index 5dbf63f..0de128f 100644 --- a/backend/migrations/create_airports/up.sql +++ b/backend/migrations/create_airports/up.sql @@ -1,7 +1,7 @@ CREATE TABLE "airports" ( id SERIAL PRIMARY KEY, - full_name VARCHAR NOT NULL, - icao VARCHAR NOT NULL, - latitude INT NOT NULL, - longitude INT NOT NULL + full_name TEXT NOT NULL, + icao TEXT NOT NULL, + latitude DOUBLE PRECISION NOT NULL, + longitude DOUBLE PRECISION NOT NULL ) \ No newline at end of file diff --git a/backend/src/airports/routes.rs b/backend/src/airports/routes.rs index 4a372a9..20a2f3e 100644 --- a/backend/src/airports/routes.rs +++ b/backend/src/airports/routes.rs @@ -1,38 +1,66 @@ use crate::airports::{Airport, Airports}; use crate::error_handler::CustomError; use actix_web::{delete, get, post, put, web, HttpResponse}; +use log::error; use serde_json::json; #[get("/airports")] async fn find_all() -> Result { - let airports = web::block(|| Airports::find_all()).await.unwrap(); + let airports = match web::block(|| Airports::find_all()).await.unwrap() { + Ok(a) => a, + Err(err) => { + error!("{}", err); + return Err(err); + } + }; Ok(HttpResponse::Ok().json(airports)) } #[get("/airports/{id}")] async fn find(id: web::Path) -> Result { - let airport = Airports::find(id.into_inner())?; + let airport = match Airports::find(id.into_inner()) { + Ok(a) => a, + Err(err) => { + error!("{}", err); + return Err(err); + } + }; Ok(HttpResponse::Ok().json(airport)) } #[post("/airports")] async fn create(airport: web::Json) -> Result { - let airport = Airports::create(airport.into_inner())?; + let airport = match Airports::create(airport.into_inner()) { + Ok(a) => a, + Err(err) => { + error!("{}", err); + return Err(err); + } + }; Ok(HttpResponse::Ok().json(airport)) } #[put("/airports/{id}")] -async fn update( - id: web::Path, - airport: web::Json, -) -> Result { - let airport = Airports::update(id.into_inner(), airport.into_inner())?; +async fn update(id: web::Path, airport: web::Json) -> Result { + let airport = match Airports::update(id.into_inner(), airport.into_inner()) { + Ok(a) => a, + Err(err) => { + error!("{}", err); + return Err(err); + } + }; Ok(HttpResponse::Ok().json(airport)) } #[delete("/airports/{id}")] async fn delete(id: web::Path) -> Result { - let deleted_airport = Airports::delete(id.into_inner())?; + let deleted_airport = match Airports::delete(id.into_inner()) { + Ok(a) => a, + Err(err) => { + error!("{}", err); + return Err(err); + } + }; Ok(HttpResponse::Ok().json(json!({ "deleted": deleted_airport }))) } diff --git a/backend/src/main.rs b/backend/src/main.rs index be3486c..f0b4b39 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -3,9 +3,11 @@ extern crate diesel; #[macro_use] extern crate diesel_migrations; -use actix_web::{App, HttpServer}; +use actix_cors::Cors; +use actix_web::{App, HttpServer, middleware::Logger}; use dotenv::dotenv; use listenfd::ListenFd; +use log::debug; use std::env; mod airports; @@ -17,20 +19,31 @@ mod schema; #[actix_rt::main] async fn main() -> std::io::Result<()> { dotenv().ok(); + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "info,actix=info,diesel_migrations=warn,reqwest=warn,hyper=warn"); + } env_logger::init(); db::init(); let mut listenfd = ListenFd::from_env(); - let mut server = HttpServer::new(|| App::new() + let mut server = HttpServer::new(|| { + let cors = Cors::default() + .allow_any_origin() + .allow_any_method() + .allow_any_header(); + App::new() .configure(airports::init_routes) .configure(metars::init_routes) - ); + .wrap(cors) + .wrap(Logger::default()) + }); server = match listenfd.take_tcp_listener(0)? { Some(listener) => server.listen(listener)?, None => { let host = env::var("HOST").expect("Please set host in .env"); let port = env::var("PORT").expect("Please set port in .env"); + debug!("Binding server to {}:{}", host, port); server.bind(format!("{}:{}", host, port))? } }; diff --git a/backend/src/metars/routes.rs b/backend/src/metars/routes.rs index 22cc253..2368ea2 100644 --- a/backend/src/metars/routes.rs +++ b/backend/src/metars/routes.rs @@ -1,16 +1,27 @@ use crate::error_handler::CustomError; use crate::metars::Metars; -use actix_web::{get, web, HttpResponse}; +use actix_web::{get, web, HttpResponse, Responder}; + +// #[get("metars/{ids}")] +// async fn get_all(ids: web::Path) -> Result { +// let airports = web::block(|| Ok::<_, CustomError>(async {Metars::get_all(ids.into_inner()).await})) +// .await +// .unwrap() +// .unwrap() +// .await +// .unwrap(); +// Ok(HttpResponse::Ok().json(airports)) +// } #[get("metars/{ids}")] -async fn get_all(ids: web::Path) -> Result { +async fn get_all(ids: web::Path) -> impl Responder { let airports = web::block(|| Ok::<_, CustomError>(async {Metars::get_all(ids.into_inner()).await})) .await .unwrap() .unwrap() .await .unwrap(); - Ok(HttpResponse::Ok().json(airports)) + HttpResponse::Ok().json(airports) } pub fn init_routes(config: &mut web::ServiceConfig) { diff --git a/src/app/page.tsx b/src/app/page.tsx index 807685d..acda07d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,25 +3,23 @@ import { setAirport } from "@/js/state"; import { Airport } from "@/js/airport"; import Metar from '@/components/Metar'; -setAirport('KJYO', new Airport('Leesburg Executive Airport', 'KJYO')) -setAirport('KHEF', new Airport('Manassas Regional Airpoirt', 'KHEF')) -setAirport('KIAD', new Airport('Dulles International Airport', 'KIAD')) -setAirport('KFDK', new Airport('Frederick Municipal Airport', 'KFDK')) -setAirport('KMRB', new Airport('Eastern West Virginia Regional Airport', 'KMRB')) -setAirport('KOKV', new Airport('Winchester Regional Airport', 'KOKV')) -setAirport('KFRR', new Airport('Front Royal-Warren County Airport', 'KFRR')) -setAirport('KLUA', new Airport('Luray Caverns Airport', 'KLUA')) -setAirport('KSHD', new Airport('Shenandoah Valley Airport', 'KSHD')) -setAirport('KCHO', new Airport('Charlottesville-Albemarle Airport', 'KCHO')) -setAirport('KCJR', new Airport('Culpeper Regional Airport', 'KCJR')) -setAirport('KHWY', new Airport('Warrenton-Fauquier Airport', 'KHWY')) -setAirport('KRMN', new Airport('Stafford Regional Airport', 'KRMN')) -setAirport('KEZF', new Airport('Shannon Airport', 'KEZF')) -setAirport('KDCA', new Airport('Ronald Reagan Washington National Airport', 'KDCA')) +// setAirport('KJYO', new Airport('Leesburg Executive Airport', 'KJYO')) +setAirport('KHEF', new Airport('Manassas Regional Airpoirt', 'KHEF', 38.724, -77517)) +// setAirport('KIAD', new Airport('Dulles International Airport', 'KIAD')) +// setAirport('KFDK', new Airport('Frederick Municipal Airport', 'KFDK')) +// setAirport('KMRB', new Airport('Eastern West Virginia Regional Airport', 'KMRB')) +// setAirport('KOKV', new Airport('Winchester Regional Airport', 'KOKV')) +// setAirport('KFRR', new Airport('Front Royal-Warren County Airport', 'KFRR')) +// setAirport('KLUA', new Airport('Luray Caverns Airport', 'KLUA')) +// setAirport('KSHD', new Airport('Shenandoah Valley Airport', 'KSHD')) +// setAirport('KCHO', new Airport('Charlottesville-Albemarle Airport', 'KCHO')) +// setAirport('KCJR', new Airport('Culpeper Regional Airport', 'KCJR')) +// setAirport('KHWY', new Airport('Warrenton-Fauquier Airport', 'KHWY')) +// setAirport('KRMN', new Airport('Stafford Regional Airport', 'KRMN')) +// setAirport('KEZF', new Airport('Shannon Airport', 'KEZF')) +// setAirport('KDCA', new Airport('Ronald Reagan Washington National Airport', 'KDCA')) export default function Page() { - - return <>
diff --git a/src/components/Metar.tsx b/src/components/Metar.tsx index 81cd2e7..a89e83f 100644 --- a/src/components/Metar.tsx +++ b/src/components/Metar.tsx @@ -17,7 +17,7 @@ export default async function Metar() { async function update() { const airports: Airport[] = getAirports(); const metars = await getMetars(airports); - for (let i = 0; i < airports.length; i++) { + for (let i = 0; i < metars.length; i++) { airports[i].metar = metars[i]; airports[i].latitude = metars[i].latitude; airports[i].longitude = metars[i].longitude; diff --git a/src/js/airport.ts b/src/js/airport.ts index 6e2e548..6860b2c 100644 --- a/src/js/airport.ts +++ b/src/js/airport.ts @@ -7,11 +7,11 @@ export class Airport { longitude: number; metar: Metar | undefined; - constructor(name: string, icao: string) { + constructor(name: string, icao: string, latitude: number, longitude: number) { this.name = name; this.icao = icao; - this.latitude = 0; - this.longitude = 0; + this.latitude = latitude; + this.longitude = longitude; this.metar = undefined; } } diff --git a/src/js/weather.ts b/src/js/weather.ts index e8cd335..954eafd 100644 --- a/src/js/weather.ts +++ b/src/js/weather.ts @@ -2,78 +2,88 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import axios from 'axios'; -import { xml2json } from 'xml-js'; +// import { xml2json } from 'xml-js'; import { Airport } from './airport'; -const base_url = 'https://beta.aviationweather.gov/cgi-bin/data'; +// const base_url = 'https://beta.aviationweather.gov/cgi-bin/data'; export async function getMetars(airports: Airport[]): Promise { const stationICAOs: string = airports - .map((airport) => airport.icao) - .join(','); - const url = `${base_url}/metar.php?ids=${stationICAOs}&format=xml`; - const response = await axios - .get(`${url}`) - .catch((error) => console.error(`${error}`)); - const metars: Metar[] = []; - if (response != null && response != undefined) { - const json = xml2json(response.data, { compact: true }); - const jsonObject = JSON.parse(json); - let metarData = jsonObject?.response?.data?.METAR; - if (!Array.isArray(metarData)) { - metarData = [metarData]; - } - for (const data of metarData) { - const sky_condition: { - sky_cover: string; - cloud_base_ft_agl: number; - }[] = []; - if (Array.isArray(data.sky_condition)) { - for (const sc of data.sky_condition) { - sky_condition.push({ - sky_cover: sc.sky_cover, - cloud_base_ft_agl: Number(sc.cloud_base_ft_agl) - }) - } - } else { - sky_condition.push({ - sky_cover: data.sky_condition?.sky_cover, - cloud_base_ft_agl: Number(data.sky_condition?.cloud_base_ft_agl) - }) - } - const metar: Metar = { - raw_text: data.raw_text._text, - station_id: data.station_id._text, - observation_time: data.observation_time._text, - latitude: Number(data.latitude._text), - longitude: Number(data.longitude._text), - temp_c: Number(data.temp_c._text), - dewpoint_c: Number(data.dewpoint_c._text), - wind_dir_degrees: data.wind_dir_degrees._text, - wind_speed_kt: Number(data.wind_speed_kt._text), - visibility_statute_mi: data.visibility_statute_mi?._text, - altim_in_hg: Number(data.altim_in_hg?._text), - sea_level_pressure_mb: data.sea_level_pressure_mb?._text, - quality_control_flags: { - auto: data.quality_control_flags?.auto?._text == 'TRUE', - auto_station: data.quality_control_flags?.auto_station?._text == 'TRUE', - }, - wx_string: data.wx_string?._text, - sky_condition: sky_condition, - flight_category: data.flight_category._text, - three_hr_pressure_tendency_mb: data.three_hr_pressure_tendency_mb?._text, - metar_type: data.metar_type._text, - maxT_c: Number(data.maxT_c?._text), - minT_c: Number(data.minT_c?._text), - precip_in: Number(data.precip_in?._text), - elevation_m: Number(data.elevation_m._text), - }; - metars.push(metar); - } - } - return metars; + .map((airport) => airport.icao) + .join(','); + const url = `http://localhost:5000/metars/KHEF`; + const response = await axios.get(url).catch((error) => console.error(error)); + console.log(response); + return []; } +// export async function getMetars(airports: Airport[]): Promise { +// const stationICAOs: string = airports +// .map((airport) => airport.icao) +// .join(','); +// const url = `${base_url}/metar.php?ids=${stationICAOs}&format=xml`; +// const response = await axios +// .get(`${url}`) +// .catch((error) => console.error(`${error}`)); +// const metars: Metar[] = []; +// if (response != null && response != undefined) { +// const json = xml2json(response.data, { compact: true }); +// const jsonObject = JSON.parse(json); +// let metarData = jsonObject?.response?.data?.METAR; +// if (!Array.isArray(metarData)) { +// metarData = [metarData]; +// } +// for (const data of metarData) { +// const sky_condition: { +// sky_cover: string; +// cloud_base_ft_agl: number; +// }[] = []; +// if (Array.isArray(data.sky_condition)) { +// for (const sc of data.sky_condition) { +// sky_condition.push({ +// sky_cover: sc.sky_cover, +// cloud_base_ft_agl: Number(sc.cloud_base_ft_agl) +// }) +// } +// } else { +// sky_condition.push({ +// sky_cover: data.sky_condition?.sky_cover, +// cloud_base_ft_agl: Number(data.sky_condition?.cloud_base_ft_agl) +// }) +// } +// const metar: Metar = { +// raw_text: data.raw_text._text, +// station_id: data.station_id._text, +// observation_time: data.observation_time._text, +// latitude: Number(data.latitude._text), +// longitude: Number(data.longitude._text), +// temp_c: Number(data.temp_c._text), +// dewpoint_c: Number(data.dewpoint_c._text), +// wind_dir_degrees: data.wind_dir_degrees._text, +// wind_speed_kt: Number(data.wind_speed_kt._text), +// visibility_statute_mi: data.visibility_statute_mi?._text, +// altim_in_hg: Number(data.altim_in_hg?._text), +// sea_level_pressure_mb: data.sea_level_pressure_mb?._text, +// quality_control_flags: { +// auto: data.quality_control_flags?.auto?._text == 'TRUE', +// auto_station: data.quality_control_flags?.auto_station?._text == 'TRUE', +// }, +// wx_string: data.wx_string?._text, +// sky_condition: sky_condition, +// flight_category: data.flight_category._text, +// three_hr_pressure_tendency_mb: data.three_hr_pressure_tendency_mb?._text, +// metar_type: data.metar_type._text, +// maxT_c: Number(data.maxT_c?._text), +// minT_c: Number(data.minT_c?._text), +// precip_in: Number(data.precip_in?._text), +// elevation_m: Number(data.elevation_m._text), +// }; +// metars.push(metar); +// } +// } +// return metars; +// } + export interface Metar { raw_text: string; station_id: string;