diff --git a/api/Cargo.lock b/api/Cargo.lock index 5eceef0..d8a78b9 100644 --- a/api/Cargo.lock +++ b/api/Cargo.lock @@ -381,6 +381,7 @@ dependencies = [ "futures-util", "geo-types", "log", + "moka", "rand 0.9.0", "rand_chacha 0.9.0", "redis", @@ -421,6 +422,17 @@ dependencies = [ "password-hash", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" version = "0.1.87" @@ -757,6 +769,24 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -1001,6 +1031,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1164,6 +1204,19 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +dependencies = [ + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1504,7 +1557,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1820,6 +1873,28 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "maybe-async" version = "0.2.10" @@ -1889,6 +1964,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "moka" +version = "0.12.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +dependencies = [ + "async-lock", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "event-listener", + "futures-util", + "loom", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -1906,6 +2003,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2038,6 +2145,12 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking" version = "2.2.1" @@ -2317,8 +2430,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2329,7 +2451,7 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] [[package]] @@ -2338,6 +2460,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -2584,6 +2712,12 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -2700,6 +2834,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3065,6 +3208,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tempfile" version = "3.18.0" @@ -3119,6 +3268,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.39" @@ -3314,6 +3473,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -3412,6 +3601,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3545,6 +3740,38 @@ dependencies = [ "wasite", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -3554,6 +3781,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.1.0" @@ -3566,11 +3828,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result", - "windows-strings", + "windows-result 0.3.1", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.3.1" @@ -3580,6 +3851,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.3.1" diff --git a/api/Cargo.toml b/api/Cargo.toml index 06b142c..d998357 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -34,3 +34,4 @@ rand_chacha = "0.9.0" geo-types = "0.7.15" byteorder = "1.5.0" futures = "0.3.31" +moka = { version = "0.12.10", features = ["future"] } diff --git a/api/src/airports/model/airport.rs b/api/src/airports/model/airport.rs index 98ac600..f7c3dc2 100644 --- a/api/src/airports/model/airport.rs +++ b/api/src/airports/model/airport.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::str::FromStr; use actix_web::web::Json; +use futures_util::try_join; +use moka::future::Cache; use serde::{Deserialize, Serialize}; use sqlx::{Execute, Postgres, QueryBuilder}; use crate::airports::model::airport_category::AirportCategory; @@ -172,7 +174,7 @@ impl Airport { let metar_fut = async { if metar { - match Metar::find_all(&[icao]).await { + match Metar::find_all(&vec![icao.to_string()]).await { Ok(m) => Some(m.into_iter().nth(0)), Err(err) => { log::error!("{}", err); @@ -223,7 +225,7 @@ impl Airport { Some(m) => Some(m), None => None, }, - None => None + None => None, }; airport_row.map(|row| { @@ -281,22 +283,49 @@ impl Airport { let airport_rows: Vec = airport_query.fetch_all(pool).await?; let mut airports: Vec = airport_rows.into_iter().map(From::from).collect(); - // Bulk update airports with runways and frequencies - if !airports.is_empty() { - let icaos: Vec = airports.iter().map(|a| a.icao.clone()).collect(); - let mut runway_map = Runway::select_all_map(icaos.clone()).await?; - let mut frequency_map = Frequency::select_all_map(icaos.clone()).await?; - let mut metar_map: HashMap = HashMap::new(); - if query.metars.unwrap_or_else(|| false) { - let icaos_list: Vec<&str> = icaos.iter().map(|x| &**x).collect(); - let metars = Metar::find_all(&icaos_list).await?; - metar_map = metars.into_iter() - .map(|metar| (metar.station_id.clone(), metar)) - .collect(); + if airports.is_empty() { + return Ok(airports); + } + + // Bulk update airport sub-fields + let icaos: Vec = airports.iter().map(|a| a.icao.clone()).collect(); + + let runway_future = Runway::select_all_map(icaos.clone()); + let frequency_future = Frequency::select_all_map(icaos.clone()); + let metar_future = if query.metars.unwrap_or(false) { + Some(Metar::find_all(&icaos)) + } else { + None + }; + + let (runway_map, frequency_map, mut metars_opt) = match metar_future { + Some(future_metars) => { + let (runway_map, frequency_map, metars) = + try_join!(runway_future, frequency_future, future_metars)?; + ( + runway_map, + frequency_map, + Some( + metars + .into_iter() + .map(|m| (m.station_id.clone(), m)) + .collect::>(), + ), + ) } - for airport in airports.iter_mut() { - airport.runways = runway_map.remove(&airport.icao).unwrap_or_default(); - airport.frequencies = frequency_map.remove(&airport.icao).unwrap_or_default(); + None => { + let (runway_map, frequency_map) = try_join!(runway_future, frequency_future)?; + (runway_map, frequency_map, None) + } + }; + + for airport in airports.iter_mut() { + airport.runways = runway_map.get(&airport.icao).cloned().unwrap_or_default(); + airport.frequencies = frequency_map + .get(&airport.icao) + .cloned() + .unwrap_or_default(); + if let Some(ref mut metar_map) = metars_opt { airport.latest_metar = metar_map.remove(&airport.icao); } } diff --git a/api/src/airports/model/frequency.rs b/api/src/airports/model/frequency.rs index 7f92d1a..ad75b90 100644 --- a/api/src/airports/model/frequency.rs +++ b/api/src/airports/model/frequency.rs @@ -7,7 +7,7 @@ use crate::error::ApiResult; const TABLE_NAME: &str = "frequencies"; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Frequency { #[serde(rename = "id")] pub frequency_id: String, diff --git a/api/src/airports/model/runway.rs b/api/src/airports/model/runway.rs index 5a5e3ef..5981218 100644 --- a/api/src/airports/model/runway.rs +++ b/api/src/airports/model/runway.rs @@ -7,7 +7,7 @@ use crate::error::ApiResult; const TABLE_NAME: &str = "runways"; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Runway { #[serde(rename = "id")] pub runway_id: String, diff --git a/api/src/main.rs b/api/src/main.rs index 8f1aea6..31caf3d 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -1,8 +1,9 @@ use std::env; use actix_cors::Cors; -use actix_web::{App, HttpServer, middleware::Logger}; +use actix_web::{App, HttpServer, middleware::Logger, web}; use dotenv::from_filename; +use moka::future::Cache; use crate::auth::hash; use crate::users::{User, ADMIN_ROLE}; diff --git a/api/src/metars/model.rs b/api/src/metars/model.rs index 74dbe3c..f0ea6fb 100644 --- a/api/src/metars/model.rs +++ b/api/src/metars/model.rs @@ -2,7 +2,10 @@ use crate::error::Error; use crate::{error::ApiResult, db}; use chrono::{DateTime, Datelike, Utc}; use std::collections::HashSet; +use moka::future::Cache; +use redis::{AsyncCommands, RedisResult}; use serde::{Deserialize, Serialize}; +use crate::db::redis_async_connection; const TABLE_NAME: &str = "metars"; @@ -195,13 +198,39 @@ impl Default for Metar { } #[derive(Serialize, Deserialize, sqlx::FromRow, Debug)] -struct MetarDb { +struct MetarRow { icao: String, observation_time: DateTime, raw_text: String, data: serde_json::Value, } +impl MetarRow { + async fn insert(&self) -> ApiResult<()> { + let pool = db::pool(); + sqlx::query(&format!( + r#" + INSERT INTO {} ( + icao, + observation_time, + raw_text, + data + ) + VALUES ($1, $2, $3, $4, $5) + "#, + TABLE_NAME, + )) + .bind(self.icao.clone()) + .bind(self.observation_time.clone()) + .bind(self.raw_text.clone()) + .bind(self.data.clone()) + .execute(pool) + .await?; + + Ok(()) + } +} + impl Metar { fn parse_multiple(metar_strings: &Vec<&str>) -> ApiResult> { let mut metars: Vec = vec![]; @@ -794,14 +823,17 @@ impl Metar { Ok(metar) } - fn get_missing_metar_icaos(db_metars: &Vec, station_icaos: &[&str]) -> Vec { + async fn get_missing_metar_icaos( + db_metars: &Vec, + station_icaos: &Vec, + ) -> Vec { let mut missing_metar_icaos: Vec = vec![]; let current_time = chrono::Local::now().naive_local().and_utc().timestamp(); let db_metars_set: HashSet<&str> = db_metars .iter() .map(|icao| icao.station_id.as_str()) .collect(); - let station_icaos_set: HashSet<&str> = station_icaos.to_owned().into_iter().collect(); + let station_icaos_set: HashSet<&str> = station_icaos.iter().map(|s| s.as_str()).collect(); for difference in db_metars_set.symmetric_difference(&station_icaos_set) { missing_metar_icaos.push(difference.to_string()); } @@ -865,14 +897,14 @@ impl Metar { Ok(metars) } - fn from_db(metar_db: MetarDb) -> ApiResult { + fn from_db(metar_db: MetarRow) -> ApiResult { let metar: Metar = serde_json::from_value(metar_db.data)?; Ok(metar) } - fn to_db(&self) -> ApiResult { + fn to_db(&self) -> ApiResult { let data = serde_json::to_value(self)?; - Ok(MetarDb { + Ok(MetarRow { icao: self.station_id.clone(), observation_time: self.observation_time, raw_text: self.raw_text.clone(), @@ -880,13 +912,13 @@ impl Metar { }) } - pub async fn find_all(icao_list: &[&str]) -> ApiResult> { + pub async fn find_all(icao_list: &Vec) -> ApiResult> { if icao_list.is_empty() { return Ok(Vec::new()); } let pool = db::pool(); - let metar_dbs: Vec = match sqlx::query_as::<_, MetarDb>(&format!( + let metar_rows: Vec = sqlx::query_as::<_, MetarRow>(&format!( r#" SELECT DISTINCT ON (icao) * FROM {} WHERE icao = ANY($1) ORDER BY icao, observation_time DESC "#, @@ -894,28 +926,34 @@ impl Metar { )) .bind(icao_list) .fetch_all(pool) - .await - { - Ok(m) => m, - Err(err) => { - return Err(Error::new( - 500, - format!("Unable to find METARs with input {:?}: {}", icao_list, err), - )); - } - }; - let mut metars: Vec = metar_dbs + .await?; + let mut metars: Vec = metar_rows .into_iter() .filter_map(|metar_db| Metar::from_db(metar_db).ok()) .collect(); + let mut conn = redis_async_connection().await?; // Check for missing metars - let missing_icao_list = Self::get_missing_metar_icaos(&metars, icao_list); + let missing_icao_list = Self::get_missing_metar_icaos(&metars, icao_list).await; if !missing_icao_list.is_empty() { log::trace!("Retrieving missing METAR data for {:?}", missing_icao_list); - let missing_icao_list: Vec<&str> = missing_icao_list.iter().map(|s| s.as_str()).collect(); - let mut missing_icao_list = Self::get_remote_metars(&missing_icao_list) + let mut updated_missing_icao_list: Vec<&str> = Vec::new(); + for icao in &missing_icao_list { + let result: RedisResult> = conn.get(icao).await; + match result { + Ok(Some(value)) => { + if value { + updated_missing_icao_list.push(icao); + } + } + Ok(None) => { + updated_missing_icao_list.push(icao); + } + Err(err) => return Err(err.into()), + } + } + let mut missing_icao_list = Self::get_remote_metars(&updated_missing_icao_list) .await .unwrap_or_else(|err| { log::warn!("Unable to get remote METAR data; {}", err); @@ -925,37 +963,28 @@ impl Metar { if missing_icao_list.len() > 0 { // Insert missing METARs for missing_metar in &missing_icao_list { + let _: RedisResult<()> = conn.set(&missing_metar.station_id, true).await; missing_metar.insert().await?; } metars.append(&mut missing_icao_list) } + + // Invalidate the still missing icaos + let still_missing_icao_list = + Self::get_missing_metar_icaos(&missing_icao_list, icao_list).await; + if !still_missing_icao_list.is_empty() { + for icao in still_missing_icao_list { + let _: RedisResult<()> = conn.set_ex(&icao, false, 3600).await; + } + } } Ok(metars) } pub async fn insert(&self) -> ApiResult<()> { - let pool = db::pool(); - let metar: MetarDb = self.to_db()?; - sqlx::query(&format!( - r#" - INSERT INTO {} ( - icao, - observation_time, - raw_text, - data - ) - VALUES ($1, $2, $3, $4) - "#, - TABLE_NAME, - )) - .bind(metar.icao) - .bind(metar.observation_time) - .bind(metar.raw_text) - .bind(metar.data) - .execute(pool) - .await?; - + let metar: MetarRow = self.to_db()?; + metar.insert().await?; Ok(()) } } diff --git a/api/src/metars/routes.rs b/api/src/metars/routes.rs index bbfb335..fbe07ab 100644 --- a/api/src/metars/routes.rs +++ b/api/src/metars/routes.rs @@ -17,7 +17,7 @@ async fn find_all(req: HttpRequest) -> HttpResponse { Some(i) => i, None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"), }; - let icaos: Vec<&str> = icao_string.split(',').collect(); + let icaos: Vec = icao_string.split(',').map(|s| s.to_string()).collect(); let metars = match Metar::find_all(&icaos).await { Ok(a) => a, diff --git a/bruno/Airports/Get All Airports.bru b/bruno/Airports/Get All Airports.bru index b5890bc..84b428c 100644 --- a/bruno/Airports/Get All Airports.bru +++ b/bruno/Airports/Get All Airports.bru @@ -5,7 +5,7 @@ meta { } get { - url: {{BASE_URL}}/airports?page=1&limit=1000&icaos=KHEF,KJYO,KMRB,KOKV&metars=true + url: {{BASE_URL}}/airports?page=1&limit=1000&metars=true body: none auth: none } @@ -13,6 +13,7 @@ get { params:query { page: 1 limit: 1000 - icaos: KHEF,KJYO,KMRB,KOKV metars: true + ~icaos: 00AA + ~icaos: KHEF,KJYO,KMRB,KOKV }