Error handling

This commit is contained in:
2023-09-04 20:15:05 -04:00
parent 9db32e4d5a
commit cf3ee4feef
10 changed files with 182 additions and 105 deletions

16
backend/Cargo.lock generated
View File

@@ -19,6 +19,21 @@ dependencies = [
"tracing", "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]] [[package]]
name = "actix-http" name = "actix-http"
version = "3.4.0" version = "3.4.0"
@@ -258,6 +273,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
name = "aviation-weather-backend" name = "aviation-weather-backend"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"actix-cors",
"actix-rt", "actix-rt",
"actix-web", "actix-web",
"chrono", "chrono",

View File

@@ -23,3 +23,4 @@ serde_json = "1.0.105"
tokio = { version = "1.32.0", features = ["macros", "rt"] } tokio = { version = "1.32.0", features = ["macros", "rt"] }
uuid = { version = "1.4.1", features = ["serde", "v4"] } uuid = { version = "1.4.1", features = ["serde", "v4"] }
log = "0.4.20" log = "0.4.20"
actix-cors = "0.6.4"

View File

@@ -1,7 +1,7 @@
CREATE TABLE "airports" ( CREATE TABLE "airports" (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
full_name VARCHAR NOT NULL, full_name TEXT NOT NULL,
icao VARCHAR NOT NULL, icao TEXT NOT NULL,
latitude INT NOT NULL, latitude DOUBLE PRECISION NOT NULL,
longitude INT NOT NULL longitude DOUBLE PRECISION NOT NULL
) )

View File

@@ -1,38 +1,66 @@
use crate::airports::{Airport, Airports}; use crate::airports::{Airport, Airports};
use crate::error_handler::CustomError; use crate::error_handler::CustomError;
use actix_web::{delete, get, post, put, web, HttpResponse}; use actix_web::{delete, get, post, put, web, HttpResponse};
use log::error;
use serde_json::json; use serde_json::json;
#[get("/airports")] #[get("/airports")]
async fn find_all() -> Result<HttpResponse, CustomError> { async fn find_all() -> Result<HttpResponse, CustomError> {
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)) Ok(HttpResponse::Ok().json(airports))
} }
#[get("/airports/{id}")] #[get("/airports/{id}")]
async fn find(id: web::Path<i32>) -> Result<HttpResponse, CustomError> { async fn find(id: web::Path<i32>) -> Result<HttpResponse, CustomError> {
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)) Ok(HttpResponse::Ok().json(airport))
} }
#[post("/airports")] #[post("/airports")]
async fn create(airport: web::Json<Airport>) -> Result<HttpResponse, CustomError> { async fn create(airport: web::Json<Airport>) -> Result<HttpResponse, CustomError> {
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)) Ok(HttpResponse::Ok().json(airport))
} }
#[put("/airports/{id}")] #[put("/airports/{id}")]
async fn update( async fn update(id: web::Path<i32>, airport: web::Json<Airport>) -> Result<HttpResponse, CustomError> {
id: web::Path<i32>, let airport = match Airports::update(id.into_inner(), airport.into_inner()) {
airport: web::Json<Airport>, Ok(a) => a,
) -> Result<HttpResponse, CustomError> { Err(err) => {
let airport = Airports::update(id.into_inner(), airport.into_inner())?; error!("{}", err);
return Err(err);
}
};
Ok(HttpResponse::Ok().json(airport)) Ok(HttpResponse::Ok().json(airport))
} }
#[delete("/airports/{id}")] #[delete("/airports/{id}")]
async fn delete(id: web::Path<i32>) -> Result<HttpResponse, CustomError> { async fn delete(id: web::Path<i32>) -> Result<HttpResponse, CustomError> {
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 }))) Ok(HttpResponse::Ok().json(json!({ "deleted": deleted_airport })))
} }

View File

@@ -3,9 +3,11 @@ extern crate diesel;
#[macro_use] #[macro_use]
extern crate diesel_migrations; extern crate diesel_migrations;
use actix_web::{App, HttpServer}; use actix_cors::Cors;
use actix_web::{App, HttpServer, middleware::Logger};
use dotenv::dotenv; use dotenv::dotenv;
use listenfd::ListenFd; use listenfd::ListenFd;
use log::debug;
use std::env; use std::env;
mod airports; mod airports;
@@ -17,20 +19,31 @@ mod schema;
#[actix_rt::main] #[actix_rt::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
dotenv().ok(); 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(); env_logger::init();
db::init(); db::init();
let mut listenfd = ListenFd::from_env(); 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(airports::init_routes)
.configure(metars::init_routes) .configure(metars::init_routes)
); .wrap(cors)
.wrap(Logger::default())
});
server = match listenfd.take_tcp_listener(0)? { server = match listenfd.take_tcp_listener(0)? {
Some(listener) => server.listen(listener)?, Some(listener) => server.listen(listener)?,
None => { None => {
let host = env::var("HOST").expect("Please set host in .env"); let host = env::var("HOST").expect("Please set host in .env");
let port = env::var("PORT").expect("Please set port in .env"); let port = env::var("PORT").expect("Please set port in .env");
debug!("Binding server to {}:{}", host, port);
server.bind(format!("{}:{}", host, port))? server.bind(format!("{}:{}", host, port))?
} }
}; };

View File

@@ -1,16 +1,27 @@
use crate::error_handler::CustomError; use crate::error_handler::CustomError;
use crate::metars::Metars; 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<String>) -> Result<HttpResponse, CustomError> {
// 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}")] #[get("metars/{ids}")]
async fn get_all(ids: web::Path<String>) -> Result<HttpResponse, CustomError> { async fn get_all(ids: web::Path<String>) -> impl Responder {
let airports = web::block(|| Ok::<_, CustomError>(async {Metars::get_all(ids.into_inner()).await})) let airports = web::block(|| Ok::<_, CustomError>(async {Metars::get_all(ids.into_inner()).await}))
.await .await
.unwrap() .unwrap()
.unwrap() .unwrap()
.await .await
.unwrap(); .unwrap();
Ok(HttpResponse::Ok().json(airports)) HttpResponse::Ok().json(airports)
} }
pub fn init_routes(config: &mut web::ServiceConfig) { pub fn init_routes(config: &mut web::ServiceConfig) {

View File

@@ -3,25 +3,23 @@ import { setAirport } from "@/js/state";
import { Airport } from "@/js/airport"; import { Airport } from "@/js/airport";
import Metar from '@/components/Metar'; import Metar from '@/components/Metar';
setAirport('KJYO', new Airport('Leesburg Executive Airport', 'KJYO')) // setAirport('KJYO', new Airport('Leesburg Executive Airport', 'KJYO'))
setAirport('KHEF', new Airport('Manassas Regional Airpoirt', 'KHEF')) setAirport('KHEF', new Airport('Manassas Regional Airpoirt', 'KHEF', 38.724, -77517))
setAirport('KIAD', new Airport('Dulles International Airport', 'KIAD')) // setAirport('KIAD', new Airport('Dulles International Airport', 'KIAD'))
setAirport('KFDK', new Airport('Frederick Municipal Airport', 'KFDK')) // setAirport('KFDK', new Airport('Frederick Municipal Airport', 'KFDK'))
setAirport('KMRB', new Airport('Eastern West Virginia Regional Airport', 'KMRB')) // setAirport('KMRB', new Airport('Eastern West Virginia Regional Airport', 'KMRB'))
setAirport('KOKV', new Airport('Winchester Regional Airport', 'KOKV')) // setAirport('KOKV', new Airport('Winchester Regional Airport', 'KOKV'))
setAirport('KFRR', new Airport('Front Royal-Warren County Airport', 'KFRR')) // setAirport('KFRR', new Airport('Front Royal-Warren County Airport', 'KFRR'))
setAirport('KLUA', new Airport('Luray Caverns Airport', 'KLUA')) // setAirport('KLUA', new Airport('Luray Caverns Airport', 'KLUA'))
setAirport('KSHD', new Airport('Shenandoah Valley Airport', 'KSHD')) // setAirport('KSHD', new Airport('Shenandoah Valley Airport', 'KSHD'))
setAirport('KCHO', new Airport('Charlottesville-Albemarle Airport', 'KCHO')) // setAirport('KCHO', new Airport('Charlottesville-Albemarle Airport', 'KCHO'))
setAirport('KCJR', new Airport('Culpeper Regional Airport', 'KCJR')) // setAirport('KCJR', new Airport('Culpeper Regional Airport', 'KCJR'))
setAirport('KHWY', new Airport('Warrenton-Fauquier Airport', 'KHWY')) // setAirport('KHWY', new Airport('Warrenton-Fauquier Airport', 'KHWY'))
setAirport('KRMN', new Airport('Stafford Regional Airport', 'KRMN')) // setAirport('KRMN', new Airport('Stafford Regional Airport', 'KRMN'))
setAirport('KEZF', new Airport('Shannon Airport', 'KEZF')) // setAirport('KEZF', new Airport('Shannon Airport', 'KEZF'))
setAirport('KDCA', new Airport('Ronald Reagan Washington National Airport', 'KDCA')) // setAirport('KDCA', new Airport('Ronald Reagan Washington National Airport', 'KDCA'))
export default function Page() { export default function Page() {
return <> return <>
<div> <div>
<Metar/> <Metar/>

View File

@@ -17,7 +17,7 @@ export default async function Metar() {
async function update() { async function update() {
const airports: Airport[] = getAirports(); const airports: Airport[] = getAirports();
const metars = await getMetars(airports); 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].metar = metars[i];
airports[i].latitude = metars[i].latitude; airports[i].latitude = metars[i].latitude;
airports[i].longitude = metars[i].longitude; airports[i].longitude = metars[i].longitude;

View File

@@ -7,11 +7,11 @@ export class Airport {
longitude: number; longitude: number;
metar: Metar | undefined; metar: Metar | undefined;
constructor(name: string, icao: string) { constructor(name: string, icao: string, latitude: number, longitude: number) {
this.name = name; this.name = name;
this.icao = icao; this.icao = icao;
this.latitude = 0; this.latitude = latitude;
this.longitude = 0; this.longitude = longitude;
this.metar = undefined; this.metar = undefined;
} }
} }

View File

@@ -2,78 +2,88 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import axios from 'axios'; import axios from 'axios';
import { xml2json } from 'xml-js'; // import { xml2json } from 'xml-js';
import { Airport } from './airport'; 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<Metar[]> { export async function getMetars(airports: Airport[]): Promise<Metar[]> {
const stationICAOs: string = airports const stationICAOs: string = airports
.map((airport) => airport.icao) .map((airport) => airport.icao)
.join(','); .join(',');
const url = `${base_url}/metar.php?ids=${stationICAOs}&format=xml`; const url = `http://localhost:5000/metars/KHEF`;
const response = await axios const response = await axios.get(url).catch((error) => console.error(error));
.get(`${url}`) console.log(response);
.catch((error) => console.error(`${error}`)); return [];
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 async function getMetars(airports: Airport[]): Promise<Metar[]> {
// 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 { export interface Metar {
raw_text: string; raw_text: string;
station_id: string; station_id: string;