Working through airport query params
This commit is contained in:
@@ -1,233 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::db;
|
|
||||||
use log::error;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use sqlx::postgres::types::PgPoint;
|
|
||||||
use crate::error::ApiResult;
|
|
||||||
|
|
||||||
const TABLE_NAME: &str = "airports";
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
|
|
||||||
struct RunwayDb {
|
|
||||||
pub icao: String,
|
|
||||||
pub id: String,
|
|
||||||
pub length_ft: f32,
|
|
||||||
pub width_ft: f32,
|
|
||||||
pub surface: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AirportFilter {
|
|
||||||
pub icaos: Option<Vec<String>>,
|
|
||||||
pub name: Option<String>,
|
|
||||||
// pub bounds: Option<Polygon<Point>>,
|
|
||||||
pub categories: Option<Vec<AirportCategory>>,
|
|
||||||
pub has_metar: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AirportFilter {
|
|
||||||
fn default() -> Self {
|
|
||||||
AirportFilter {
|
|
||||||
icaos: None,
|
|
||||||
name: None,
|
|
||||||
// bounds: None,
|
|
||||||
categories: None,
|
|
||||||
has_metar: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AirportDb {
|
|
||||||
pub async fn find_all(_filter: &AirportFilter, _limit: i32, _page: i32) -> ApiResult<Vec<Self>> {
|
|
||||||
let pool = db::pool();
|
|
||||||
let airports: Vec<Self> = sqlx::query_as::<_, Self>(&format!(
|
|
||||||
"SELECT * FROM {}",
|
|
||||||
TABLE_NAME
|
|
||||||
))
|
|
||||||
.fetch_all(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(airports)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn count(_filter: &AirportFilter) -> ApiResult<i64> {
|
|
||||||
let pool = db::pool();
|
|
||||||
let count: i64 = sqlx::query_scalar::<_, i64>(&format!(
|
|
||||||
"SELECT COUNT(*) FROM {}",
|
|
||||||
TABLE_NAME
|
|
||||||
))
|
|
||||||
.fetch_one(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn build_query<'a>(
|
|
||||||
// mut query: QueryBuilder<'a, Postgres>,
|
|
||||||
// filter: &'a AirportFilter,
|
|
||||||
// ) -> QueryBuilder<'a, Postgres> {
|
|
||||||
// if let Some(bounds) = &filter.bounds {
|
|
||||||
// // convert bounds to a WKT polygon
|
|
||||||
// if bounds.rings.len() > 1 {
|
|
||||||
// return Err(ApiError {
|
|
||||||
// status: 400,
|
|
||||||
// message: "Only one polygon is allowed".to_string(),
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// let mut points: Vec<String> = vec![];
|
|
||||||
// bounds.rings.iter().for_each(|ring| {
|
|
||||||
// ring.iter().for_each(|point| {
|
|
||||||
// points.push(format!("{} {}", point.get_x(), point.get_y()));
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// let bounds = format!("POLYGON(({}))", points.join(","));
|
|
||||||
// query.push(format!(
|
|
||||||
// "ST_Contains(ST_GeomFromText('{}', 4326), point)",
|
|
||||||
// bounds
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if let Some(categories) = &filter.categories {
|
|
||||||
// query.push(format!(
|
|
||||||
// "({})",
|
|
||||||
// categories
|
|
||||||
// .iter()
|
|
||||||
// .map(|category| format!("category = '{}'", category.to_string()))
|
|
||||||
// .collect::<Vec<String>>()
|
|
||||||
// .join(" OR ")
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn sanitize_icao(icao: &str) -> String {
|
|
||||||
// // Sanitize search to only allow [a-zA-Z0-9-\\s]
|
|
||||||
// icao
|
|
||||||
// .chars()
|
|
||||||
// .filter(|c| c.is_alphanumeric() || *c == '-' || *c == ' ')
|
|
||||||
// .collect::<String>()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if &filter.icaos.is_some() == &true && &filter.name.is_some() == &true {
|
|
||||||
// let icaos = filter.icaos.as_ref().unwrap();
|
|
||||||
// let name = sanitize_icao(filter.name.as_ref().unwrap());
|
|
||||||
// let icao_part = format!(
|
|
||||||
// "({})",
|
|
||||||
// icaos
|
|
||||||
// .iter()
|
|
||||||
// .map(|icao| format!("icao ILIKE '{}'", sanitize_icao(icao)))
|
|
||||||
// .collect::<Vec<String>>()
|
|
||||||
// .join(" OR ")
|
|
||||||
// );
|
|
||||||
// let name_part = format!("name ILIKE '%{}%'", name);
|
|
||||||
// parts.push(format!("({} OR {})", icao_part, name_part));
|
|
||||||
// } else if let Some(icaos) = &filter.icaos {
|
|
||||||
// parts.push(format!(
|
|
||||||
// "({})",
|
|
||||||
// icaos
|
|
||||||
// .iter()
|
|
||||||
// .map(|icao| format!("icao ILIKE '{}'", sanitize_icao(icao)))
|
|
||||||
// .collect::<Vec<String>>()
|
|
||||||
// .join(" OR ")
|
|
||||||
// ));
|
|
||||||
// } else if let Some(name) = &filter.name {
|
|
||||||
// let search = sanitize_icao(name);
|
|
||||||
// parts.push(format!("name ILIKE '%{}%'", search));
|
|
||||||
// }
|
|
||||||
// if let Some(has_metar) = &filter.has_metar {
|
|
||||||
// parts.push(format!("has_metar = {}", has_metar));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if parts.len() > 0 {
|
|
||||||
// query = format!("{} WHERE {}", query, parts.join(" AND "));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Ok(query);
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub async fn find_by_icao(icao: &str) -> ApiResult<Self> {
|
|
||||||
let pool = db::pool();
|
|
||||||
let airport =
|
|
||||||
sqlx::query_as::<_, Self>(&format!("SELECT * FROM {} WHERE icao = $1", TABLE_NAME))
|
|
||||||
.bind(icao)
|
|
||||||
.fetch_one(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(airport)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn insert(&self) -> ApiResult<()> {
|
|
||||||
let pool = db::pool();
|
|
||||||
sqlx::query(&format!(
|
|
||||||
"INSERT INTO {} (
|
|
||||||
icao,
|
|
||||||
category,
|
|
||||||
name,
|
|
||||||
elevation_ft,
|
|
||||||
iso_country,
|
|
||||||
iso_region,
|
|
||||||
municipality,
|
|
||||||
has_metar,
|
|
||||||
point,
|
|
||||||
data
|
|
||||||
) VALUES (
|
|
||||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
|
||||||
)",
|
|
||||||
TABLE_NAME
|
|
||||||
))
|
|
||||||
.bind(self.icao.clone())
|
|
||||||
.bind(self.category.clone())
|
|
||||||
.bind(&self.name)
|
|
||||||
.bind(self.elevation_ft)
|
|
||||||
.bind(self.iso_country.clone())
|
|
||||||
.bind(self.iso_region.clone())
|
|
||||||
.bind(self.municipality.clone())
|
|
||||||
.bind(self.has_metar.clone())
|
|
||||||
// .bind(self.point.clone())
|
|
||||||
.bind(self.data.clone())
|
|
||||||
.execute(pool)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn insert_vec(airports: Vec<Self>) -> ApiResult<Vec<Self>> {
|
|
||||||
// let mut conn: r2d2::PooledConnection<diesel::r2d2::ConnectionManager<PgConnection>> =
|
|
||||||
// db::connection()?;
|
|
||||||
// let mut inserted_airports: Vec<Self> = vec![];
|
|
||||||
// for airport in airports {
|
|
||||||
// let airport = Self::from(airport);
|
|
||||||
// let airport = diesel::insert_into(airports::table)
|
|
||||||
// .values(airport)
|
|
||||||
// .on_conflict_do_nothing()
|
|
||||||
// .get_result(&mut conn)?;
|
|
||||||
// inserted_airports.push(airport);
|
|
||||||
// }
|
|
||||||
// Ok(inserted_airports)
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub async fn update(&self) -> ApiResult<()> {
|
|
||||||
// let mut conn = db::pool()?;
|
|
||||||
// let airport = diesel::update(airports::table)
|
|
||||||
// .filter(airports::icao.eq(airport.icao.clone()))
|
|
||||||
// .set(airport)
|
|
||||||
// .get_result(&mut conn)?;
|
|
||||||
// Ok(airport)
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_all() -> ApiResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_by_icao(_icao: &str) -> ApiResult<()> {
|
|
||||||
// let mut conn = db::pool()?;
|
|
||||||
// let res = match icao {
|
|
||||||
// Some(icao) => {
|
|
||||||
// diesel::delete(airports::table.filter(airports::icao.eq(icao))).execute(&mut conn)?
|
|
||||||
// }
|
|
||||||
// None => diesel::delete(airports::table).execute(&mut conn)?,
|
|
||||||
// };
|
|
||||||
// Ok(res)
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use actix_web::web::Json;
|
use actix_web::web::Json;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::{Postgres, QueryBuilder};
|
use sqlx::{Execute, Postgres, QueryBuilder};
|
||||||
use crate::airports::model::airport_category::AirportCategory;
|
use crate::airports::model::airport_category::AirportCategory;
|
||||||
use crate::airports::{Frequency, Runway, UpdateFrequency, UpdateRunway};
|
use crate::airports::{Frequency, Runway, UpdateFrequency, UpdateRunway};
|
||||||
use crate::db;
|
use crate::db;
|
||||||
@@ -33,7 +33,38 @@ pub struct Airport {
|
|||||||
pub public: bool,
|
pub public: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct AirportQuery {
|
||||||
|
pub page: Option<u32>,
|
||||||
|
pub limit: Option<u32>,
|
||||||
|
pub icaos: Option<String>,
|
||||||
|
pub iatas: Option<String>,
|
||||||
|
pub locals: Option<String>,
|
||||||
|
pub names: Option<String>,
|
||||||
|
pub categories: Option<String>,
|
||||||
|
pub iso_countries: Option<String>,
|
||||||
|
pub iso_regions: Option<String>,
|
||||||
|
pub municipalities: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AirportQuery {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
page: Some(1),
|
||||||
|
limit: Some(1000),
|
||||||
|
icaos: None,
|
||||||
|
iatas: None,
|
||||||
|
locals: None,
|
||||||
|
names: None,
|
||||||
|
categories: None,
|
||||||
|
iso_countries: None,
|
||||||
|
iso_regions: None,
|
||||||
|
municipalities: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, sqlx::FromRow)]
|
||||||
struct AirportRow {
|
struct AirportRow {
|
||||||
pub icao: String,
|
pub icao: String,
|
||||||
pub iata: Option<String>,
|
pub iata: Option<String>,
|
||||||
@@ -51,39 +82,23 @@ struct AirportRow {
|
|||||||
pub public: bool,
|
pub public: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct UpdateAirport {
|
pub struct UpdateAirport {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub icao: Option<String>,
|
pub icao: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub iata: Option<String>,
|
pub iata: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub local: Option<String>,
|
pub local: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub category: Option<AirportCategory>,
|
pub category: Option<AirportCategory>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub iso_country: Option<String>,
|
pub iso_country: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub iso_region: Option<String>,
|
pub iso_region: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub municipality: Option<String>,
|
pub municipality: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub elevation_ft: Option<f32>,
|
pub elevation_ft: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub longitude: Option<f32>,
|
pub longitude: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub latitude: Option<f32>,
|
pub latitude: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub has_tower: Option<bool>,
|
pub has_tower: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub has_beacon: Option<bool>,
|
pub has_beacon: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub runways: Option<Vec<UpdateRunway>>,
|
pub runways: Option<Vec<UpdateRunway>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub frequencies: Option<Vec<UpdateFrequency>>,
|
pub frequencies: Option<Vec<UpdateFrequency>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub public: Option<bool>,
|
pub public: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,19 +176,95 @@ impl Airport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn select_all() -> ApiResult<Vec<Self>> {
|
pub async fn select_all(query: &AirportQuery) -> ApiResult<Vec<Self>> {
|
||||||
let pool = db::pool();
|
let pool = db::pool();
|
||||||
|
|
||||||
let airports: Vec<AirportRow> = sqlx::query_as(&format!(
|
let mut builder = QueryBuilder::<Postgres>::new("SELECT * FROM ");
|
||||||
r#"
|
builder.push(TABLE_NAME);
|
||||||
SELECT * FROM {}
|
|
||||||
"#,
|
|
||||||
TABLE_NAME
|
|
||||||
))
|
|
||||||
.fetch_all(pool)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(airports.into_iter().map(From::from).collect())
|
let mut has_where = false;
|
||||||
|
macro_rules! push_condition {
|
||||||
|
($field:expr, $value:expr) => {
|
||||||
|
if let Some(ref val) = $value {
|
||||||
|
if !has_where {
|
||||||
|
builder.push(" WHERE ");
|
||||||
|
has_where = true;
|
||||||
|
} else {
|
||||||
|
builder.push(" AND ");
|
||||||
|
}
|
||||||
|
builder.push($field).push(" = ").push_bind(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// push_condition!("icao", query.icaos);
|
||||||
|
// push_condition!("iata", query.iata);
|
||||||
|
// push_condition!("iso_country", query.iso_country);
|
||||||
|
// push_condition!("iso_region", query.iso_region);
|
||||||
|
// push_condition!("municipality", query.municipality);
|
||||||
|
|
||||||
|
// Apply pagination.
|
||||||
|
if let Some(limit) = query.limit {
|
||||||
|
builder.push(" LIMIT ").push_bind(limit as i64);
|
||||||
|
let offset = if let Some(page) = query.page {
|
||||||
|
// Calculate offset (page is 1-based).
|
||||||
|
(page.saturating_sub(1) * limit) as i64
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
builder.push(" OFFSET ").push_bind(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = builder.build_query_as();
|
||||||
|
let airport_rows: Vec<AirportRow> = query.fetch_all(pool).await?;
|
||||||
|
Ok(airport_rows.into_iter().map(From::from).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn count(query: &AirportQuery) -> i64 {
|
||||||
|
let pool = db::pool();
|
||||||
|
|
||||||
|
let mut builder = QueryBuilder::<Postgres>::new("SELECT COUNT(*) FROM ");
|
||||||
|
builder.push(TABLE_NAME);
|
||||||
|
|
||||||
|
let mut has_where = false;
|
||||||
|
macro_rules! push_condition_array {
|
||||||
|
($column:expr, $field:expr) => {
|
||||||
|
if let Some(ref value_str) = $field {
|
||||||
|
// split on commas, trim whitespace, and drop empties
|
||||||
|
let values: Vec<&str> = value_str
|
||||||
|
.split(',')
|
||||||
|
.map(|s| s.trim())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect();
|
||||||
|
if !values.is_empty() {
|
||||||
|
if !has_where {
|
||||||
|
builder.push(" WHERE ");
|
||||||
|
has_where = true;
|
||||||
|
} else {
|
||||||
|
builder.push(" AND ");
|
||||||
|
}
|
||||||
|
dbg!(&values);
|
||||||
|
builder.push($column);
|
||||||
|
builder.push(" = ANY(");
|
||||||
|
builder.push_bind(values);
|
||||||
|
builder.push(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
push_condition_array!("icao", query.icaos);
|
||||||
|
push_condition_array!("iata", query.iatas);
|
||||||
|
push_condition_array!("iso_country", query.iso_countries);
|
||||||
|
push_condition_array!("iso_region", query.iso_regions);
|
||||||
|
push_condition_array!("municipality", query.municipalities);
|
||||||
|
push_condition_array!("local", query.locals);
|
||||||
|
push_condition_array!("name", query.names);
|
||||||
|
push_condition_array!("category", query.categories);
|
||||||
|
|
||||||
|
let sql_query = builder.build_query_scalar();
|
||||||
|
dbg!(&sql_query.sql());
|
||||||
|
sql_query.fetch_one(pool).await.unwrap_or_else(|_| 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn insert(&self) -> ApiResult<Self> {
|
pub async fn insert(&self) -> ApiResult<Self> {
|
||||||
|
|||||||
@@ -9,22 +9,9 @@ use crate::{
|
|||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest, ResponseError};
|
use actix_web::{delete, get, post, put, web, HttpResponse, HttpRequest, ResponseError};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::airports::UpdateAirport;
|
use crate::airports::{AirportQuery, UpdateAirport};
|
||||||
use crate::users::ADMIN_ROLE;
|
use crate::users::ADMIN_ROLE;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct AirportsQuery {
|
|
||||||
icaos: Option<String>,
|
|
||||||
name: Option<String>,
|
|
||||||
bounds: Option<String>,
|
|
||||||
categories: Option<String>,
|
|
||||||
order_field: Option<String>,
|
|
||||||
order_by: Option<String>,
|
|
||||||
has_metar: Option<String>,
|
|
||||||
limit: Option<i32>,
|
|
||||||
page: Option<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/import")]
|
#[post("/import")]
|
||||||
async fn import_airports(mut payload: Multipart, auth: Auth) -> HttpResponse {
|
async fn import_airports(mut payload: Multipart, auth: Auth) -> HttpResponse {
|
||||||
if let Err(err) = verify_role(&auth, ADMIN_ROLE) {
|
if let Err(err) = verify_role(&auth, ADMIN_ROLE) {
|
||||||
@@ -69,8 +56,27 @@ async fn import_airports(mut payload: Multipart, auth: Auth) -> HttpResponse {
|
|||||||
|
|
||||||
#[get("")]
|
#[get("")]
|
||||||
async fn get_airports(req: HttpRequest) -> HttpResponse {
|
async fn get_airports(req: HttpRequest) -> HttpResponse {
|
||||||
match Airport::select_all().await {
|
let mut query = match web::Query::<AirportQuery>::from_query(req.query_string()) {
|
||||||
Ok(airports) => HttpResponse::Ok().json(airports),
|
Ok(q) => q.into_inner(),
|
||||||
|
Err(_) => AirportQuery::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let total = Airport::count(&query).await;
|
||||||
|
let page = query.page.unwrap_or(1);
|
||||||
|
let mut limit = query.limit.unwrap_or(total as u32);
|
||||||
|
if limit > 1000 {
|
||||||
|
limit = 1000
|
||||||
|
}
|
||||||
|
query.limit = Some(limit);
|
||||||
|
query.page = Some(page);
|
||||||
|
|
||||||
|
match Airport::select_all(&query).await {
|
||||||
|
Ok(airports) => HttpResponse::Ok().json(Paged {
|
||||||
|
data: airports,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
total,
|
||||||
|
}),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("{}", err);
|
log::error!("{}", err);
|
||||||
ResponseError::error_response(&err)
|
ResponseError::error_response(&err)
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ pub async fn delete_file(path: &str) -> ApiResult<ResponseData> {
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Paged<T> {
|
pub struct Paged<T> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
pub page: i32,
|
pub page: u32,
|
||||||
pub limit: i32,
|
pub limit: u32,
|
||||||
pub total: i64,
|
pub total: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Delete Airport
|
name: Delete Airport
|
||||||
type: http
|
type: http
|
||||||
seq: 4
|
seq: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
delete {
|
delete {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Delete All Airports
|
name: Delete All Airports
|
||||||
type: http
|
type: http
|
||||||
seq: 5
|
seq: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
delete {
|
delete {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Get Airport
|
name: Get Airport
|
||||||
type: http
|
type: http
|
||||||
seq: 2
|
seq: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Get All Airports
|
name: Get All Airports
|
||||||
type: http
|
type: http
|
||||||
seq: 3
|
seq: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{BASE_URL}}/airports
|
url: {{BASE_URL}}/airports?icaos=00AA&page=1&limit=1000
|
||||||
body: none
|
body: none
|
||||||
auth: none
|
auth: none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
icaos: 00AA
|
||||||
|
page: 1
|
||||||
|
limit: 1000
|
||||||
|
}
|
||||||
|
|||||||
15
bruno/Airports/Import Airports.bru
Normal file
15
bruno/Airports/Import Airports.bru
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Import Airports
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{BASE_URL}}/airports/import
|
||||||
|
body: multipartForm
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
body:multipart-form {
|
||||||
|
: @file(/Users/bsherriff/git/private/aviation-weather/data/airports_2023-12-21.json)
|
||||||
|
}
|
||||||
@@ -144368,7 +144368,7 @@
|
|||||||
"iata": "",
|
"iata": "",
|
||||||
"local": "64CL",
|
"local": "64CL",
|
||||||
"name": "Goodyear Blimp Base Airport",
|
"name": "Goodyear Blimp Base Airport",
|
||||||
"category": "balloonport",
|
"category": "balloon_port",
|
||||||
"iso_country": "US",
|
"iso_country": "US",
|
||||||
"iso_region": "US-CA",
|
"iso_region": "US-CA",
|
||||||
"municipality": "Gardena",
|
"municipality": "Gardena",
|
||||||
|
|||||||
Reference in New Issue
Block a user