| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![feature(async_await)] |
| |
| use { |
| crate::{ |
| cloud_action_provider::get_cloud_actions, |
| mod_manager::ModManager, |
| story_context_store::StoryContextStore, |
| story_manager::StoryManager, |
| story_storage::{LedgerStorage, MemoryStorage, StoryStorage}, |
| suggestion_providers::{ |
| ActionSuggestionsProvider, ContextualSuggestionsProvider, PackageSuggestionsProvider, |
| StorySuggestionsProvider, |
| }, |
| suggestions_manager::SuggestionsManager, |
| suggestions_service::SuggestionsService, |
| }, |
| failure::{Error, ResultExt}, |
| fidl_fuchsia_app_discover::{ |
| DiscoverRegistryRequestStream, SessionDiscoverContextRequestStream, |
| SuggestionsRequestStream, |
| }, |
| fidl_fuchsia_modular::{ |
| EntityResolverMarker, LifecycleRequest, LifecycleRequestStream, PuppetMasterMarker, |
| }, |
| fuchsia_async as fasync, |
| fuchsia_component::{client::connect_to_service, server::ServiceFs}, |
| fuchsia_syslog::{self as syslog, macros::*}, |
| futures::prelude::*, |
| parking_lot::Mutex, |
| std::sync::Arc, |
| }; |
| |
| #[macro_use] |
| mod testing; |
| mod action_match; |
| mod cloud_action_provider; |
| mod constants; |
| mod discover_registry; |
| mod indexing; |
| mod local_action_provider; |
| mod mod_manager; |
| mod models; |
| mod module_output; |
| mod session_context; |
| mod story_context_store; |
| mod story_graph; |
| mod story_manager; |
| mod story_module; |
| mod story_storage; |
| mod suggestion_providers; |
| mod suggestions_manager; |
| mod suggestions_service; |
| mod utils; |
| |
| // The directory name where the discovermgr FIDL services are exposed. |
| static SERVICE_DIRECTORY: &str = "svc"; |
| |
| enum IncomingServices { |
| DiscoverRegistry(DiscoverRegistryRequestStream), |
| Suggestions(SuggestionsRequestStream), |
| Lifecycle(LifecycleRequestStream), |
| SessionDiscoverContext(SessionDiscoverContextRequestStream), |
| } |
| |
| async fn run_lifecycle_server(mut stream: LifecycleRequestStream) -> Result<(), Error> { |
| while let Some(request) = stream.try_next().await.context("Error running lifecycle")? { |
| match request { |
| LifecycleRequest::Terminate { .. } => { |
| std::process::exit(0); |
| } |
| } |
| } |
| Ok(()) |
| } |
| |
| /// Handle incoming service requests |
| async fn run_fidl_service( |
| story_context_store: Arc<Mutex<StoryContextStore>>, |
| story_manager: Arc<Mutex<StoryManager>>, |
| suggestions_manager: Arc<Mutex<SuggestionsManager>>, |
| mod_manager: Arc<Mutex<ModManager<StoryContextStore>>>, |
| incoming_service_stream: IncomingServices, |
| ) -> Result<(), Error> { |
| match incoming_service_stream { |
| IncomingServices::DiscoverRegistry(stream) => { |
| discover_registry::run_server(story_context_store, mod_manager, stream).await |
| } |
| IncomingServices::Suggestions(stream) => { |
| let mut service = SuggestionsService::new(story_context_store, suggestions_manager); |
| service.handle_client(stream).await |
| } |
| IncomingServices::Lifecycle(stream) => run_lifecycle_server(stream).await, |
| IncomingServices::SessionDiscoverContext(stream) => { |
| session_context::run_server(stream, story_manager).await |
| } |
| } |
| } |
| |
| #[fasync::run_singlethreaded] |
| async fn main() -> Result<(), Error> { |
| syslog::init_with_tags(&["discovermgr"])?; |
| |
| let entity_resolver = connect_to_service::<EntityResolverMarker>() |
| .context("failed to connect to entity resolver")?; |
| let story_context_store = Arc::new(Mutex::new(StoryContextStore::new(entity_resolver))); |
| |
| let puppet_master = |
| connect_to_service::<PuppetMasterMarker>().context("failed to connect to puppet master")?; |
| |
| let cloud_actions = get_cloud_actions().await.unwrap_or_else(|e| { |
| fx_log_err!("Error fetching cloud actions index: {}", e); |
| vec![] |
| }); |
| let actions_arc = Arc::new(cloud_actions); |
| |
| let storage = |
| LedgerStorage::new().map(|s| Box::new(s) as Box<dyn StoryStorage>).unwrap_or_else(|_| { |
| fx_log_err!("Error in creating LedgerStorage, Use MemoryStorage instead"); |
| Box::new(MemoryStorage::new()) as Box<dyn StoryStorage> |
| }); |
| let story_manager = Arc::new(Mutex::new(StoryManager::new(storage))); |
| let mod_manager = Arc::new(Mutex::new(ModManager::new( |
| story_context_store.clone(), |
| puppet_master, |
| story_manager.clone(), |
| actions_arc.clone(), |
| ))); |
| |
| let mut suggestions_manager = SuggestionsManager::new(mod_manager.clone()); |
| suggestions_manager.register_suggestions_provider(Box::new(StorySuggestionsProvider::new( |
| story_manager.clone(), |
| ))); |
| suggestions_manager.register_suggestions_provider(Box::new(PackageSuggestionsProvider::new())); |
| |
| suggestions_manager.register_suggestions_provider(Box::new( |
| ContextualSuggestionsProvider::new(actions_arc.clone()), |
| )); |
| |
| suggestions_manager |
| .register_suggestions_provider(Box::new(ActionSuggestionsProvider::new(actions_arc))); |
| |
| let suggestions_manager_ref = Arc::new(Mutex::new(suggestions_manager)); |
| |
| let mut fs = ServiceFs::new_local(); |
| fs.dir(SERVICE_DIRECTORY) |
| .add_fidl_service(IncomingServices::DiscoverRegistry) |
| .add_fidl_service(IncomingServices::Suggestions) |
| .add_fidl_service(IncomingServices::Lifecycle) |
| .add_fidl_service(IncomingServices::SessionDiscoverContext); |
| |
| fs.take_and_serve_directory_handle()?; |
| |
| const MAX_CONCURRENT: usize = 10_000; |
| let fut = fs.for_each_concurrent(MAX_CONCURRENT, |incoming_service_stream| { |
| run_fidl_service( |
| story_context_store.clone(), |
| story_manager.clone(), |
| suggestions_manager_ref.clone(), |
| mod_manager.clone(), |
| incoming_service_stream, |
| ) |
| .unwrap_or_else(|e| fx_log_err!("{:?}", e)) |
| }); |
| |
| fut.await; |
| Ok(()) |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use { |
| super::*, |
| crate::models::Action, |
| fuchsia_async as fasync, |
| std::{collections::HashSet, iter::FromIterator}, |
| }; |
| |
| // Verify the logic for removing duplicates |
| #[fasync::run_singlethreaded(test)] |
| async fn test_duplicates() -> Result<(), Error> { |
| let cloud_actions: Vec<Action> = |
| serde_json::from_str(include_str!("../test_data/test_actions.json")).unwrap(); |
| // test_actions_dupes contains 1 duplicate and 1 new |
| let local_actions: Vec<Action> = |
| serde_json::from_str(include_str!("../test_data/test_actions_dupes.json")).unwrap(); |
| |
| let cloud_actions_len = cloud_actions.len(); |
| // This is the logic used above |
| let actions: Vec<Action> = HashSet::<Action>::from_iter( |
| cloud_actions.into_iter().chain(local_actions.into_iter()), |
| ) |
| .into_iter() |
| .collect::<Vec<Action>>(); |
| |
| // check if the new and duplicated are added/filtered |
| assert_eq!(cloud_actions_len + 1, actions.len()); |
| |
| Ok(()) |
| } |
| } |