diff options
| author | Carpenter, Adam (CORP) <adam.carpenter@adp.com> | 2021-11-30 15:22:37 -0500 | 
|---|---|---|
| committer | Carpenter, Adam (CORP) <adam.carpenter@adp.com> | 2021-11-30 15:22:37 -0500 | 
| commit | cd9f750990166dfa9b9fc0276749be307e26d008 (patch) | |
| tree | 963bae4a76c06d6f89636eccb633dc06bc206620 /angelsharkd/src | |
| parent | 1fab7c878d55a307e5ef2078801be231b13ebab8 (diff) | |
| download | altruistic-angelshark-cd9f750990166dfa9b9fc0276749be307e26d008.tar.xz altruistic-angelshark-cd9f750990166dfa9b9fc0276749be307e26d008.zip  | |
refactor: module structure and comments
Diffstat (limited to 'angelsharkd/src')
| -rw-r--r-- | angelsharkd/src/routes/extensions/mod.rs | 9 | ||||
| -rw-r--r-- | angelsharkd/src/routes/extensions/simple_search/README.md | 3 | ||||
| -rw-r--r-- | angelsharkd/src/routes/extensions/simple_search/mod.rs | 66 | ||||
| -rw-r--r-- | angelsharkd/src/routes/extensions/simple_search/types.rs (renamed from angelsharkd/src/routes/extensions/simple_search.rs) | 97 | 
4 files changed, 109 insertions, 66 deletions
diff --git a/angelsharkd/src/routes/extensions/mod.rs b/angelsharkd/src/routes/extensions/mod.rs index adcea3f..7f217d6 100644 --- a/angelsharkd/src/routes/extensions/mod.rs +++ b/angelsharkd/src/routes/extensions/mod.rs @@ -6,18 +6,22 @@ mod simple_deprov;  #[cfg(feature = "simple_search")]  mod simple_search; +/// The extension filter; consists of all compiled optional Angelshark extension +/// filters combined under `/extensions`.  pub fn filter(config: &Config) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {      // Note: this next line deals with the common edge case of having no      // extensions loaded with feature flags. It ensures that the the type      // checking is right when the return `.and()` is called below.      let filters = default().or(default()); +    // Block to enable simple_search extension feature. Instantiates a +    // searchable haystack and configures filters to handle search requests.      #[cfg(feature = "simple_search")]      let haystack = simple_search::Haystack::new(config.runner.clone());      #[cfg(feature = "simple_search")]      let filters = filters -        .or(simple_search::search(haystack.clone())) -        .or(simple_search::refresh(haystack)); +        .or(simple_search::search_filter(haystack.clone())) +        .or(simple_search::refresh_filter(haystack));      #[cfg(feature = "simple_deprov")]      let filters = filters.or(simple_deprov::filter()); @@ -25,6 +29,7 @@ pub fn filter(config: &Config) -> impl Filter<Extract = impl Reply, Error = Reje      path("extensions").and(filters)  } +/// The default, informational extension route.  fn default() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {      warp::path::end().map(|| "Angelshark extension route index. Enable extensions with feature switches and access them at `/extensions/<feature>`.")  } diff --git a/angelsharkd/src/routes/extensions/simple_search/README.md b/angelsharkd/src/routes/extensions/simple_search/README.md new file mode 100644 index 0000000..527f686 --- /dev/null +++ b/angelsharkd/src/routes/extensions/simple_search/README.md @@ -0,0 +1,3 @@ +# Angelshark Simple Extension Search + +This extension implements very simple extension searching for end users. It allows for multi-keyword searches on a pre-downloaded collection of CM extension-type and station data. This data can be refreshed on-demand or periodically. Searches always return immediately, even when no data has been downloaded yet. diff --git a/angelsharkd/src/routes/extensions/simple_search/mod.rs b/angelsharkd/src/routes/extensions/simple_search/mod.rs new file mode 100644 index 0000000..5b5dc8e --- /dev/null +++ b/angelsharkd/src/routes/extensions/simple_search/mod.rs @@ -0,0 +1,66 @@ +use log::{error, info}; +use std::convert::Infallible; +pub use types::Haystack; +use types::*; +use warp::{ +    body::{content_length_limit, json}, +    get, +    hyper::{header, StatusCode}, +    path, post, +    reply::{self, with}, +    Filter, Rejection, Reply, +}; + +mod types; + +/// Returns a warp filter to handle HTTP POSTs for searching the haystack. +pub fn search_filter( +    haystack: Haystack, +) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { +    path("search") +        .and(post()) +        .and(content_length_limit(1024 * 16)) +        .and(json()) +        .and_then(move |terms: Needles| search(haystack.to_owned(), terms)) +        .with(with::header(header::PRAGMA, "no-cache")) +        .with(with::header(header::CACHE_CONTROL, "no-store, max-age=0")) +        .with(with::header(header::X_FRAME_OPTIONS, "DENY")) +} + +/// Returns a warp filter to handle HTTP GETs for refreshing the haystack. +pub fn refresh_filter( +    haystack: Haystack, +) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { +    path!("search" / "refresh") +        .and(get()) +        .and_then(move || refresh(haystack.to_owned())) +} + +/// Runs the search request to find all needles in the haystack and converts the +/// results into a reply. +async fn search(haystack: Haystack, needles: Needles) -> Result<impl Reply, Infallible> { +    // Ok(haystack.search(Vec::new())?) +    // if let Ok(matches = haystack.search(needle); +    match haystack.search(needles) { +        Ok(matches) => Ok(reply::with_status(reply::json(&matches), StatusCode::OK)), +        Err(e) => Ok(reply::with_status( +            reply::json(&e.to_string()), +            StatusCode::INTERNAL_SERVER_ERROR, +        )), +    } +} + +/// Immediately returns. Spawns an asynchronous task to complete the haystack +/// refresh in the background. +async fn refresh(haystack: Haystack) -> Result<impl Reply, Infallible> { +    // Run refresh as a background task and immediately return. +    tokio::spawn(async move { +        if let Err(e) = haystack.refresh() { +            error!("{}", e.to_string()); // TODO: use logger +        } else { +            info!("Search haystack refreshed."); +        } +    }); + +    Ok("Refresh scheduled") +} diff --git a/angelsharkd/src/routes/extensions/simple_search.rs b/angelsharkd/src/routes/extensions/simple_search/types.rs index b728c1e..184b1a6 100644 --- a/angelsharkd/src/routes/extensions/simple_search.rs +++ b/angelsharkd/src/routes/extensions/simple_search/types.rs @@ -1,69 +1,25 @@  use anyhow::{anyhow, Context, Error};  use libangelshark::{AcmRunner, Message, ParallelIterator}; -use log::{error, info}; +use log::error;  use std::{      collections::HashMap, -    convert::Infallible, +    env,      sync::{Arc, Mutex},  }; -use warp::{ -    body::{content_length_limit, json}, -    get, -    hyper::{header, StatusCode}, -    path, post, -    reply::{self, with}, -    Filter, Rejection, Reply, -}; + +const ANGELSHARKD_EXT_SEARCH_ACMS: &str = "ANGELSHARKD_EXT_SEARCH_ACMS"; +const OSSI_STAT_NUMBER_FIELD: &str = "8005ff00"; +const OSSI_STAT_ROOM_FIELD: &str = "0031ff00"; +const OSSI_LIST_STAT_CMD: &str = "list station"; +const OSSI_LIST_EXT_CMD: &str = "list extension-type";  /// Collection of search terms -type Needles = Vec<String>; +pub type Needles = Vec<String>;  /// Collection of ACM extension types with ROOMs (if applicable) and ACM names  type HaystackEntries = Vec<Vec<String>>; -pub fn search(haystack: Haystack) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { -    path("search") -        .and(post()) -        .and(content_length_limit(1024 * 16)) -        .and(json()) -        .and_then(move |terms: Needles| handle_search(haystack.to_owned(), terms)) -        .with(with::header(header::PRAGMA, "no-cache")) -        .with(with::header(header::CACHE_CONTROL, "no-store, max-age=0")) -        .with(with::header(header::X_FRAME_OPTIONS, "DENY")) -} - -pub fn refresh(haystack: Haystack) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone { -    path!("search" / "refresh") -        .and(get()) -        .and_then(move || handle_refresh(haystack.to_owned())) -} - -async fn handle_search(haystack: Haystack, needle: Needles) -> Result<impl Reply, Infallible> { -    // Ok(haystack.search(Vec::new())?) -    // if let Ok(matches = haystack.search(needle); -    match haystack.search(needle) { -        Ok(matches) => Ok(reply::with_status(reply::json(&matches), StatusCode::OK)), -        Err(e) => Ok(reply::with_status( -            reply::json(&e.to_string()), -            StatusCode::INTERNAL_SERVER_ERROR, -        )), -    } -} - -async fn handle_refresh(haystack: Haystack) -> Result<impl Reply, Infallible> { -    // Run refresh as a background task and immediately return. -    tokio::spawn(async move { -        if let Err(e) = haystack.refresh() { -            error!("{}", e.to_string()); // TODO: use logger -        } else { -            info!("Search haystack refreshed."); -        } -    }); - -    Ok("Refresh scheduled") -} - -/// A lazy-loaded, asynchronously-refreshed exension-type haystack cache. +/// Represents a searchable, refreshable collection of ACM extension data.  #[derive(Clone)]  pub struct Haystack {      entries: Arc<Mutex<HaystackEntries>>, @@ -78,6 +34,7 @@ impl Haystack {          }      } +    /// Searches for haystack entries that contain all given needles and returns them.      pub fn search(&self, needles: Needles) -> Result<HaystackEntries, Error> {          let needles: Vec<_> = needles.iter().map(|n| n.to_lowercase()).collect(); @@ -100,20 +57,32 @@ impl Haystack {          Ok(matches)      } +    /// Refreshes the haystack data by running relevant commands on a runner, +    /// parsing the results, and updating the entries field with the fresh data. +    /// TODO: Do we want simultaneous refreshes to be possible?      pub fn refresh(&self) -> Result<(), Error> { -        let mut runner = self.runner.to_owned(); // TODO: This alone allows multiple refreshers to be run at once. Do we want that? +        let mut runner = self.runner.to_owned();          // Queue jobs in ACM runner -        for acm in &[ -            "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", -        ] { +        let configured_acms = env::var(ANGELSHARKD_EXT_SEARCH_ACMS).with_context(|| { +            format!( +                "{} var missing. Cannot refresh haystack.", +                ANGELSHARKD_EXT_SEARCH_ACMS +            ) +        })?; + +        // Generate jobs and queue on runner. +        for acm in configured_acms.split_whitespace() {              runner -                .queue_input(acm, &Message::new("list extension-type")) +                .queue_input(acm, &Message::new(OSSI_LIST_EXT_CMD))                  .queue_input(                      acm,                      &Message { -                        command: String::from("list station"), -                        fields: Some(vec![String::from("8005ff00"), String::from("0031ff00")]), +                        command: String::from(OSSI_LIST_STAT_CMD), +                        fields: Some(vec![ +                            String::from(OSSI_STAT_NUMBER_FIELD), +                            String::from(OSSI_STAT_ROOM_FIELD), +                        ]),                          datas: None,                          error: None,                      }, @@ -149,7 +118,7 @@ impl Haystack {              .map(|(_, messages)| {                  messages                      .iter() -                    .filter(|message| message.command == "list station") +                    .filter(|message| message.command == OSSI_LIST_STAT_CMD)                      .filter_map(|message| message.datas.to_owned())                      .flatten()                      .filter_map(|stat| Some((stat.get(0)?.to_owned(), stat.get(1)?.to_owned()))) @@ -166,7 +135,7 @@ impl Haystack {                  messages                      .into_iter() -                    .filter(|message| message.command == "list extension-type") +                    .filter(|message| message.command == OSSI_LIST_EXT_CMD)                      .filter_map(|message| message.datas)                      .flatten()                      .map(move |mut extension| { @@ -185,7 +154,7 @@ impl Haystack {              .flatten()              .collect(); -        // Propogate the new data to the shared haystack entries +        // Overwrite shared haystack entries with new data.          let mut lock = self              .entries              .lock()  |