blob: 8fee01c950e045e1d079b3c46d2f4d8eaefe7049 [file] [log] [blame]
// 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(())
}
}