From d8116feb887407f7acca67283216fb690d0a17d9 Mon Sep 17 00:00:00 2001 From: Tomas Tulka Date: Thu, 12 Dec 2024 16:22:18 +0100 Subject: [PATCH] init gw list ordering --- api/proto/api/gateway.proto | 3 + api/rust/proto/chirpstack/api/gateway.proto | 3 + chirpstack/src/api/gateway.rs | 8 +- chirpstack/src/storage/device.rs | 6 +- chirpstack/src/storage/gateway.rs | 95 ++++++++++++++++++++- ui/src/views/gateways/ListGateways.tsx | 6 +- 6 files changed, 113 insertions(+), 8 deletions(-) diff --git a/api/proto/api/gateway.proto b/api/proto/api/gateway.proto index 480391cc..dfcee17c 100644 --- a/api/proto/api/gateway.proto +++ b/api/proto/api/gateway.proto @@ -229,6 +229,9 @@ message ListGatewaysRequest { // Multicast-group ID (UUID) to filter gateways on. string multicast_group_id = 5; + + // If set, the given string will be used to sort by (optional). + string order_by = 6; } message ListGatewaysResponse { diff --git a/api/rust/proto/chirpstack/api/gateway.proto b/api/rust/proto/chirpstack/api/gateway.proto index 480391cc..dfcee17c 100644 --- a/api/rust/proto/chirpstack/api/gateway.proto +++ b/api/rust/proto/chirpstack/api/gateway.proto @@ -229,6 +229,9 @@ message ListGatewaysRequest { // Multicast-group ID (UUID) to filter gateways on. string multicast_group_id = 5; + + // If set, the given string will be used to sort by (optional). + string order_by = 6; } message ListGatewaysResponse { diff --git a/chirpstack/src/api/gateway.rs b/chirpstack/src/api/gateway.rs index a1b3b389..10427492 100644 --- a/chirpstack/src/api/gateway.rs +++ b/chirpstack/src/api/gateway.rs @@ -237,8 +237,14 @@ impl GatewayService for Gateway { }, }; + let order_by = if req.order_by.is_empty() { + None + } else { + Some(device::OrderBy::new(&req.order_by)) + }; + let count = gateway::get_count(&filters).await.map_err(|e| e.status())?; - let result = gateway::list(req.limit as i64, req.offset as i64, &filters) + let result = gateway::list(req.limit as i64, req.offset as i64, &filters, order_by) .await .map_err(|e| e.status())?; diff --git a/chirpstack/src/storage/device.rs b/chirpstack/src/storage/device.rs index eaa35e68..299500ba 100644 --- a/chirpstack/src/storage/device.rs +++ b/chirpstack/src/storage/device.rs @@ -235,7 +235,7 @@ impl OrderBy { } else { Self { column: value.trim().into(), - modifier: String::from("asc"), + modifier: String::from("asc"), } } } @@ -650,7 +650,7 @@ pub async fn list( device::battery_level, )) .distinct() - .into_boxed(); + .into_boxed(); if let Some(application_id) = &filters.application_id { q = q.filter(device::dsl::application_id.eq(fields::Uuid::from(application_id))); @@ -676,7 +676,7 @@ pub async fn list( if let Some(order) = order_by { match order.column.as_str() { - "name" if "" == order.modifier => + "name" if "asc" == order.modifier => q = q.order_by(device::dsl::name), "name" if "desc" == order.modifier => q = q.order_by(device::dsl::name.desc()), diff --git a/chirpstack/src/storage/gateway.rs b/chirpstack/src/storage/gateway.rs index b725d2cd..03ebd8f1 100644 --- a/chirpstack/src/storage/gateway.rs +++ b/chirpstack/src/storage/gateway.rs @@ -110,6 +110,31 @@ pub struct Filters { pub search: Option, } +#[derive(Default, Clone, Debug)] +pub struct OrderBy { + column: String, + modifier: String, +} + +impl OrderBy { + pub fn new(value: &str) -> Self { + if value.contains(',') { + let i = value.find(',').expect(""); + let column = value[0..i].trim().into(); + let modifier = value[(i+1)..].trim().into(); + Self { + column, + modifier, + } + } else { + Self { + column: value.trim().into(), + modifier: String::from("asc"), + } + } + } +} + #[derive(QueryableByName, PartialEq, Eq, Debug)] pub struct GatewayCountsByState { #[diesel(sql_type = diesel::sql_types::BigInt)] @@ -309,6 +334,7 @@ pub async fn list( limit: i64, offset: i64, filters: &Filters, + order_by: Option, ) -> Result, Error> { let mut q = gateway::dsl::gateway .left_join(multicast_group_gateway::table) @@ -351,8 +377,29 @@ pub async fn list( ); } + if let Some(order) = order_by { + match order.column.as_str() { + "name" if "asc" == order.modifier => + q = q.order_by(gateway::dsl::name), + "name" if "desc" == order.modifier => + q = q.order_by(gateway::dsl::name.desc()), + "gatewayId" if "asc" == order.modifier => + q = q.order_by(gateway::dsl::gateway_id), + "gatewayId" if "desc" == order.modifier => + q = q.order_by(gateway::dsl::gateway_id.desc()), + "lastSeenAt" if "asc" == order.modifier => + q = q.order_by(gateway::dsl::last_seen_at) + .then_order_by(gateway::dsl::name), + "lastSeenAt" if "desc" == order.modifier => + q = q.order_by(gateway::dsl::last_seen_at.desc()) + .then_order_by(gateway::dsl::name), + _ => q = q.order_by(gateway::dsl::name) + }; + } else { + q = q.order_by(gateway::dsl::name) + }; + let items = q - .order_by(gateway::dsl::name) .limit(limit) .offset(offset) .load(&mut get_async_db_conn().await?) @@ -522,6 +569,7 @@ pub mod test { count: usize, limit: i64, offset: i64, + order: Option, } struct RelayGatewayFilterTest<'a> { @@ -603,6 +651,7 @@ pub mod test { count: 1, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -614,6 +663,7 @@ pub mod test { count: 0, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -625,6 +675,7 @@ pub mod test { count: 1, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -636,6 +687,7 @@ pub mod test { count: 1, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -647,6 +699,7 @@ pub mod test { count: 0, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -658,6 +711,7 @@ pub mod test { count: 1, limit: 10, offset: 0, + order: None, }, FilterTest { filters: Filters { @@ -669,6 +723,43 @@ pub mod test { count: 0, limit: 10, offset: 0, + order: None, + }, + FilterTest { + filters: Filters { + tenant_id: None, + multicast_group_id: None, + search: None, + }, + gws: vec![&gw], + count: 1, + limit: 10, + offset: 0, + order: Some(OrderBy::new("name")), + }, + FilterTest { + filters: Filters { + tenant_id: None, + multicast_group_id: None, + search: None, + }, + gws: vec![&gw], + count: 1, + limit: 10, + offset: 0, + order: Some(OrderBy::new("name,asc")), + }, + FilterTest { + filters: Filters { + tenant_id: None, + multicast_group_id: None, + search: None, + }, + gws: vec![&gw], + count: 1, + limit: 10, + offset: 0, + order: Some(OrderBy::new("name,desc")), }, ]; @@ -676,7 +767,7 @@ pub mod test { let count = get_count(&tst.filters).await.unwrap() as usize; assert_eq!(tst.count, count); - let items = list(tst.limit, tst.offset, &tst.filters).await.unwrap(); + let items = list(tst.limit, tst.offset, &tst.filters, tst.order).await.unwrap(); assert_eq!( tst.gws .iter() diff --git a/ui/src/views/gateways/ListGateways.tsx b/ui/src/views/gateways/ListGateways.tsx index 75b352eb..107ee999 100644 --- a/ui/src/views/gateways/ListGateways.tsx +++ b/ui/src/views/gateways/ListGateways.tsx @@ -71,6 +71,7 @@ function ListGateways(props: IProps) { return format(ts, "yyyy-MM-dd HH:mm:ss"); } }, + sorter: true, }, { title: "Gateway ID", @@ -80,11 +81,13 @@ function ListGateways(props: IProps) { render: (text, record) => ( {text} ), + sorter: true, }, { title: "Name", dataIndex: "name", key: "name", + sorter: true, }, { title: "Region ID", @@ -97,7 +100,6 @@ function ListGateways(props: IProps) { return {v}; } } - return ""; }, }, @@ -112,7 +114,6 @@ function ListGateways(props: IProps) { return v; } } - return ""; }, }, @@ -152,6 +153,7 @@ function ListGateways(props: IProps) { req.setTenantId(props.tenant.getId()); req.setLimit(limit); req.setOffset(offset); + req.setOrderBy(orderBy || ""); GatewayStore.list(req, (resp: ListGatewaysResponse) => { const obj = resp.toObject();