From d6f24a6cb541a848c4927cabedfeabd64066d1af Mon Sep 17 00:00:00 2001 From: Callum Forrester Date: Thu, 5 Dec 2024 14:57:14 +0000 Subject: [PATCH 1/3] Expand configuration query Expand configuration query to be able to return multiple configurations. Change name to configurations. Make beamline parameter optional (and rename to beamlineFilter). When a filter is provided, only details for that beamline are returned, otherwise all beamline configurations are returned. Also add a test for the multi-configuration DB query and update the example query in the README. --- README.md | 20 +++++++++++--------- src/db_service.rs | 36 ++++++++++++++++++++++++++++++++++++ src/graphql.rs | 19 ++++++++++++++----- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d19a148..ff28e29 100644 --- a/README.md +++ b/README.md @@ -109,13 +109,13 @@ Get the visit directory for a beamline and visit } ``` -#### configuration -Get the current configuration values for the given beamline +#### configurations +Get the current configuration values, optionally filtering for a specific beamline ##### Query ```graphql { - configuration(beamline: "i22") { + configurations(beamlineFilter: "i22") { visitTemplate scanTemplate detectorTemplate @@ -127,12 +127,14 @@ Get the current configuration values for the given beamline ##### Response ```json { - "configuration": { - "visitTemplate": "/data/{instrument}/data/{year}/{visit}", - "scanTemplate": "{subdirectory}/{instrument}-{scan_number}", - "detectorTemplate": "{subdirectory}/{instrument}-{scan_number}-{detector}", - "latestScanNumber": 20839 - } + "configurations": [ + { + "visitTemplate": "/tmp/{instrument}/data/{year}/{visit}", + "scanTemplate": "{subdirectory}/{instrument}-{scan_number}", + "detectorTemplate": "{subdirectory}/{instrument}-{scan_number}-{detector}", + "latestScanNumber": 12345 + } + ] } ``` diff --git a/src/db_service.rs b/src/db_service.rs index 6e77590..294c861 100644 --- a/src/db_service.rs +++ b/src/db_service.rs @@ -303,6 +303,15 @@ impl SqliteScanPathService { .ok_or(ConfigurationError::MissingBeamline(beamline.into())) } + pub async fn configurations(&self) -> Result, ConfigurationError> { + Ok(query_as!(DbBeamlineConfig, "SELECT * FROM beamline") + .fetch_all(&self.pool) + .await? + .into_iter() + .map(BeamlineConfiguration::from) + .collect()) + } + pub async fn next_scan_configuration( &self, beamline: &str, @@ -604,6 +613,33 @@ mod db_tests { assert_eq!(fb.extension, "ext"); } + #[rstest] + #[test] + async fn configurations(#[future(awt)] db: SqliteScanPathService) { + let confs = ok!(db.configurations()); + assert_eq!(confs.len(), 1); + let conf = confs.first().unwrap(); + assert_eq!(conf.name(), "i22"); + assert_eq!(conf.scan_number(), 122); + assert_eq!( + conf.visit().unwrap().to_string(), + "/tmp/{instrument}/data/{year}/{visit}" + ); + assert_eq!( + conf.scan().unwrap().to_string(), + "{subdirectory}/{instrument}-{scan_number}" + ); + assert_eq!( + conf.detector().unwrap().to_string(), + "{subdirectory}/{instrument}-{scan_number}-{detector}" + ); + let Some(fb) = conf.fallback() else { + panic!("Missing fallback configuration"); + }; + assert_eq!(fb.directory, "/tmp/trackers"); + assert_eq!(fb.extension, "ext"); + } + type Update = BeamlineConfigurationUpdate; #[rstest] diff --git a/src/graphql.rs b/src/graphql.rs index 0f858bc..077fcdd 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -255,14 +255,23 @@ impl Query { } #[instrument(skip(self, ctx))] - async fn configuration( + async fn configurations( &self, ctx: &Context<'_>, - beamline: String, - ) -> async_graphql::Result { + beamline_filter: Option, + ) -> async_graphql::Result> { let db = ctx.data::()?; - trace!("Getting config for {beamline:?}"); - Ok(db.current_configuration(&beamline).await?) + match beamline_filter { + Some(filter) => { + trace!("Getting configs matching {filter:?}"); + let singleton = db.current_configuration(&filter).await?; + Ok(vec![singleton]) + } + None => { + trace!("Getting all configs"); + Ok(db.configurations().await?) + } + } } } From 015d27b9a10a193e12a0561ec36550132fbcba24 Mon Sep 17 00:00:00 2001 From: Callum Forrester Date: Thu, 5 Dec 2024 15:23:50 +0000 Subject: [PATCH 2/3] Add instrument to configuration query response Expose the name of a configuration (which matches the name of the beamline) in the GraphQL API. Now that we have a query that returns a list of configurations it is useful to have a field that can differentiate between them. --- src/graphql.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/graphql.rs b/src/graphql.rs index 077fcdd..c9906be 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -207,6 +207,10 @@ impl ScanPaths { #[Object] impl BeamlineConfiguration { + pub async fn instrument(&self) -> async_graphql::Result { + Ok(self.name().to_owned()) + } + pub async fn visit_template(&self) -> async_graphql::Result { Ok(self.visit()?.to_string()) } From cf8eddafa57044fbad4016809c13453415a43d4e Mon Sep 17 00:00:00 2001 From: Callum Forrester Date: Tue, 10 Dec 2024 13:10:55 +0000 Subject: [PATCH 3/3] Return empty vector if filter not found Return an empty vector from the configurations query if a beamlineFilter is provided but does not match a beamline, previously the API returned an error in this case. --- src/db_service.rs | 2 +- src/graphql.rs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/db_service.rs b/src/db_service.rs index 294c861..8c7c410 100644 --- a/src/db_service.rs +++ b/src/db_service.rs @@ -356,7 +356,7 @@ impl fmt::Debug for SqliteScanPathService { } } -mod error { +pub(crate) mod error { use std::error::Error; use std::fmt::{self, Display}; diff --git a/src/graphql.rs b/src/graphql.rs index c9906be..26a9c5c 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -38,6 +38,7 @@ use crate::cli::ServeOptions; use crate::db_service::{ BeamlineConfiguration, BeamlineConfigurationUpdate, SqliteScanPathService, }; +use crate::db_service::error::ConfigurationError; use crate::numtracker::GdaNumTracker; use crate::paths::{ BeamlineField, DetectorField, DetectorTemplate, PathSpec, ScanField, ScanTemplate, @@ -265,17 +266,21 @@ impl Query { beamline_filter: Option, ) -> async_graphql::Result> { let db = ctx.data::()?; - match beamline_filter { + let matching = match beamline_filter { Some(filter) => { trace!("Getting configs matching {filter:?}"); - let singleton = db.current_configuration(&filter).await?; - Ok(vec![singleton]) + match db.current_configuration(&filter).await { + Ok(configuration) => vec![configuration], + Err(ConfigurationError::MissingBeamline(_)) => vec![], + Err(other) => Err(other)? + } } None => { trace!("Getting all configs"); - Ok(db.configurations().await?) + db.configurations().await? } - } + }; + Ok(matching) } }