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",
]
[[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",

View File

@@ -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"

View File

@@ -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
)

View File

@@ -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<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))
}
#[get("/airports/{id}")]
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))
}
#[post("/airports")]
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))
}
#[put("/airports/{id}")]
async fn update(
id: web::Path<i32>,
airport: web::Json<Airport>,
) -> Result<HttpResponse, CustomError> {
let airport = Airports::update(id.into_inner(), airport.into_inner())?;
async fn update(id: web::Path<i32>, airport: web::Json<Airport>) -> Result<HttpResponse, CustomError> {
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<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 })))
}

View File

@@ -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))?
}
};

View File

@@ -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<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}")]
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}))
.await
.unwrap()
.unwrap()
.await
.unwrap();
Ok(HttpResponse::Ok().json(airports))
HttpResponse::Ok().json(airports)
}
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 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 <>
<div>
<Metar/>

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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<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;
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<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 {
raw_text: string;
station_id: string;