| // 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. |
| |
| use { |
| crate::{story_manager::StoryManager, utils}, |
| failure::{Error, ResultExt}, |
| fidl_fuchsia_app_discover::{ |
| SessionDiscoverContextRequest, SessionDiscoverContextRequestStream, |
| StoryDiscoverContextRequest, StoryDiscoverContextRequestStream, SurfaceData, |
| }, |
| fuchsia_async as fasync, |
| fuchsia_syslog::macros::*, |
| futures::prelude::*, |
| parking_lot::Mutex, |
| std::sync::Arc, |
| }; |
| |
| pub async fn run_server( |
| mut stream: SessionDiscoverContextRequestStream, |
| story_manager: Arc<Mutex<StoryManager>>, |
| ) -> Result<(), Error> { |
| while let Some(request) = stream.try_next().await.context("Error running session context")? { |
| match request { |
| SessionDiscoverContextRequest::GetStoryContext { story_id, request, .. } => { |
| let story_context_stream = request.into_stream()?; |
| StoryContextService::new(story_id, story_manager.clone()) |
| .spawn(story_context_stream); |
| } |
| } |
| } |
| Ok(()) |
| } |
| |
| /// The StoryDiscoverContext protocol implementation. |
| pub struct StoryContextService { |
| /// The story id to which the module belongs. |
| story_id: String, |
| story_manager: Arc<Mutex<StoryManager>>, |
| } |
| |
| impl StoryContextService { |
| pub fn new(story_id: impl Into<String>, story_manager: Arc<Mutex<StoryManager>>) -> Self { |
| StoryContextService { story_id: story_id.into(), story_manager } |
| } |
| |
| pub fn spawn(self, mut stream: StoryDiscoverContextRequestStream) { |
| fasync::spawn_local( |
| async move { |
| while let Some(request) = stream |
| .try_next() |
| .await |
| .context(format!("Error running story context for {:?}", self.story_id))? |
| { |
| match request { |
| StoryDiscoverContextRequest::GetSurfaceData { surface_id, responder } => { |
| // TODO: actually return the proper data. |
| let manager_lock = self.story_manager.lock(); |
| let graph_result = manager_lock.get_story_graph(&self.story_id).await; |
| let result = graph_result |
| .as_ref() |
| .and_then(|result| result.get_module_data(&surface_id)); |
| match result { |
| None => { |
| responder.send(SurfaceData { |
| action: None, |
| parameter_types: None, |
| })?; |
| } |
| Some(ref module_data) => { |
| responder.send(SurfaceData { |
| action: module_data.last_intent.action.clone(), |
| // TODO: story_manager still doesn't contain the outputs |
| parameter_types: Some(vec![]), |
| })?; |
| } |
| } |
| } |
| StoryDiscoverContextRequest::SetProperty { key, value, responder } => { |
| let mut story_manager = self.story_manager.lock(); |
| story_manager |
| .set_property( |
| &self.story_id, |
| &key, |
| utils::vmo_buffer_to_string(Box::new(value))?, |
| ) |
| .await?; |
| // TODO: handle the errors properly in a followup CL. |
| responder.send(&mut Ok(()))?; |
| } |
| |
| StoryDiscoverContextRequest::GetProperty { key, responder } => { |
| let story_manager = self.story_manager.lock(); |
| let property = story_manager.get_property(&self.story_id, &key).await?; |
| // TODO: handle the errors properly in a followup CL. |
| responder.send(&mut Ok(utils::string_to_vmo_buffer(property)?))?; |
| } |
| } |
| } |
| Ok(()) |
| } |
| .unwrap_or_else(|e: Error| fx_log_err!("error serving story context {}", e)), |
| ) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| crate::{ |
| constants::TITLE_KEY, models::AddModInfo, story_manager::StoryManager, |
| story_storage::MemoryStorage, utils, |
| }, |
| fidl_fuchsia_app_discover::{SessionDiscoverContextMarker, StoryDiscoverContextMarker}, |
| fuchsia_async as fasync, |
| }; |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn story_context_get_surface_data() -> Result<(), Error> { |
| // Initialize some fake state. |
| let story_id = "my-story".to_string(); |
| let mod_name = "my-mod".to_string(); |
| let action_name = "my-action".to_string(); |
| let story_manager = Arc::new(Mutex::new(StoryManager::new(Box::new(MemoryStorage::new())))); |
| let mut action = AddModInfo::new_raw( |
| "some-component-url", |
| Some(story_id.clone()), |
| Some(mod_name.clone()), |
| ); |
| action.intent.action = Some(action_name.clone()); |
| { |
| let mut manager_lock = story_manager.lock(); |
| manager_lock.add_to_story_graph(&action, vec![]).await?; |
| } |
| |
| // Initialize service client and server. |
| let (client, request_stream) = |
| fidl::endpoints::create_proxy_and_stream::<SessionDiscoverContextMarker>().unwrap(); |
| fasync::spawn_local( |
| async move { run_server(request_stream, story_manager).await } |
| .unwrap_or_else(|e: Error| eprintln!("error running server {}", e)), |
| ); |
| |
| // Get the story context |
| let (story_context_proxy, server_end) = |
| fidl::endpoints::create_proxy::<StoryDiscoverContextMarker>()?; |
| assert!(client.get_story_context(&story_id, server_end).is_ok()); |
| |
| let surface_data = story_context_proxy.get_surface_data(&mod_name).await?; |
| assert_eq!(surface_data.action, Some(action_name)); |
| assert_eq!(surface_data.parameter_types, Some(vec![])); |
| Ok(()) |
| } |
| |
| #[fasync::run_until_stalled(test)] |
| async fn test_get_set_property() -> Result<(), Error> { |
| let (client, request_stream) = |
| fidl::endpoints::create_proxy_and_stream::<SessionDiscoverContextMarker>().unwrap(); |
| let story_manager_arc = |
| Arc::new(Mutex::new(StoryManager::new(Box::new(MemoryStorage::new())))); |
| |
| let cloned_story_manager_arc = story_manager_arc.clone(); |
| fasync::spawn_local( |
| async move { run_server(request_stream, cloned_story_manager_arc).await } |
| .unwrap_or_else(|e: Error| eprintln!("error running server {}", e)), |
| ); |
| |
| // Get the StoryDiscoverContext connection. |
| let (story_discover_context_proxy, server_end) = |
| fidl::endpoints::create_proxy::<StoryDiscoverContextMarker>()?; |
| assert!(client.get_story_context("story_name", server_end).is_ok()); |
| |
| // Set the title of the story via SetProperty service |
| assert!(story_discover_context_proxy |
| .set_property(TITLE_KEY, &mut utils::string_to_vmo_buffer("new_title")?) |
| .await |
| .is_ok()); |
| |
| // Get the title of the story via GetProperty service |
| let returned_title = utils::vmo_buffer_to_string(Box::new( |
| story_discover_context_proxy.get_property(TITLE_KEY).await?.unwrap(), |
| ))?; |
| |
| // Ensure that set & get all succeed |
| assert_eq!(returned_title, "new_title".to_string()); |
| Ok(()) |
| } |
| } |