use crate::conductor::api::error::ConductorApiError;
use crate::conductor::api::error::ConductorApiResult;
use crate::conductor::api::error::SerializationError;
use crate::conductor::interface::error::InterfaceError;
use crate::conductor::interface::error::InterfaceResult;
use crate::conductor::ConductorHandle;
pub use holochain_conductor_api::*;
use holochain_serialized_bytes::prelude::*;
use holochain_types::prelude::*;
#[derive(Clone)]
pub struct AppInterfaceApi {
conductor_handle: ConductorHandle,
}
impl AppInterfaceApi {
pub fn new(conductor_handle: ConductorHandle) -> Self {
Self { conductor_handle }
}
pub async fn auth(&self, auth: AppAuthentication) -> InterfaceResult<InstalledAppId> {
self.conductor_handle
.authenticate_app_token(auth.token, auth.installed_app_id)
.map_err(Box::new)
.map_err(InterfaceError::RequestHandler)
}
pub async fn handle_request(
&self,
installed_app_id: InstalledAppId,
request: Result<AppRequest, SerializedBytesError>,
) -> InterfaceResult<AppResponse> {
{
self.conductor_handle
.check_running()
.map_err(Box::new)
.map_err(InterfaceError::RequestHandler)?;
}
match request {
Ok(request) => Ok(self.handle_app_request(installed_app_id, request).await),
Err(e) => Ok(AppResponse::Error(SerializationError::from(e).into())),
}
}
async fn handle_app_request(
&self,
installed_app_id: InstalledAppId,
request: AppRequest,
) -> AppResponse {
tracing::debug!("app request: {:?}", request);
let res = self
.handle_app_request_inner(installed_app_id, request)
.await
.unwrap_or_else(|e| AppResponse::Error(e.into()));
tracing::debug!("app response: {:?}", res);
res
}
async fn handle_app_request_inner(
&self,
installed_app_id: InstalledAppId,
request: AppRequest,
) -> ConductorApiResult<AppResponse> {
match request {
AppRequest::AppInfo => Ok(AppResponse::AppInfo(
self.conductor_handle
.get_app_info(&installed_app_id)
.await?,
)),
AppRequest::AgentInfo { dna_hashes } => {
let agent_infos = self
.conductor_handle
.get_app_agent_infos(&installed_app_id, dna_hashes)
.await?;
let items: Result<Vec<_>, _> =
agent_infos.into_iter().map(|info| info.encode()).collect();
Ok(AppResponse::AgentInfo(items?))
}
AppRequest::PeerMetaInfo { url, dna_hashes } => {
let r = self
.conductor_handle
.app_peer_meta_info(&installed_app_id, url, dna_hashes)
.await?;
Ok(AppResponse::PeerMetaInfo(r))
}
AppRequest::CallZome(zome_call_params_signed) => {
match self.conductor_handle.handle_external_zome_call(*zome_call_params_signed).await? {
Ok(ZomeCallResponse::Ok(output)) => Ok(AppResponse::ZomeCalled(Box::new(output))),
Ok(ZomeCallResponse::AuthenticationFailed(signature, provenance)) => Ok(AppResponse::Error(
ExternalApiWireError::ZomeCallAuthenticationFailed(format!(
"Authentication failure. Bad signature {signature:?} by provenance {provenance:?}.",
)),
)),
Ok(ZomeCallResponse::Unauthorized(zome_call_authorization, cap_secret, zome_name, fn_name)) => Ok(AppResponse::Error(
ExternalApiWireError::ZomeCallUnauthorized(format!(
"Call was not authorized with reason {zome_call_authorization:?}, cap secret {cap_secret:?} to call the function {fn_name} in zome {zome_name}"
)),
)),
Ok(ZomeCallResponse::NetworkError(e)) => unreachable!(
"Interface zome calls should never be routed to the network. This is a bug. Got {}",
e
),
Ok(ZomeCallResponse::CountersigningSession(e)) => Ok(AppResponse::Error(
ExternalApiWireError::CountersigningSessionError(format!(
"A countersigning session has failed to start on this zome call because: {e}"
)),
)),
Err(e) => Ok(AppResponse::Error(e.into())),
}
}
#[cfg(feature = "unstable-countersigning")]
AppRequest::GetCountersigningSessionState(payload) => {
let countersigning_session_state = self
.conductor_handle
.clone()
.get_countersigning_session_state(&payload)
.await?;
Ok(AppResponse::CountersigningSessionState(Box::new(
countersigning_session_state,
)))
}
#[cfg(feature = "unstable-countersigning")]
AppRequest::AbandonCountersigningSession(payload) => {
self.conductor_handle
.clone()
.abandon_countersigning_session(&payload)
.await?;
Ok(AppResponse::CountersigningSessionAbandoned)
}
#[cfg(feature = "unstable-countersigning")]
AppRequest::PublishCountersigningSession(payload) => {
self.conductor_handle
.clone()
.publish_countersigning_session(&payload)
.await?;
Ok(AppResponse::PublishCountersigningSessionTriggered)
}
AppRequest::CreateCloneCell(payload) => {
let clone_cell = self
.conductor_handle
.clone()
.create_clone_cell(&installed_app_id, *payload)
.await?;
Ok(AppResponse::CloneCellCreated(clone_cell))
}
AppRequest::DisableCloneCell(payload) => {
self.conductor_handle
.clone()
.disable_clone_cell(&installed_app_id, &payload)
.await?;
Ok(AppResponse::CloneCellDisabled)
}
AppRequest::EnableCloneCell(payload) => {
let enabled_cell = self
.conductor_handle
.clone()
.enable_clone_cell(&installed_app_id, &payload)
.await?;
Ok(AppResponse::CloneCellEnabled(enabled_cell))
}
AppRequest::DumpNetworkMetrics {
dna_hash,
include_dht_summary,
} => {
let info = self
.conductor_handle
.dump_network_metrics_for_app(
&installed_app_id,
Kitsune2NetworkMetricsRequest {
dna_hash,
include_dht_summary,
},
)
.await?;
Ok(AppResponse::NetworkMetricsDumped(info))
}
AppRequest::DumpNetworkStats => {
let stats = self
.conductor_handle
.dump_network_stats_for_app(&installed_app_id)
.await?;
Ok(AppResponse::NetworkStatsDumped(stats))
}
AppRequest::ListWasmHostFunctions => Ok(AppResponse::ListWasmHostFunctions(
self.conductor_handle.list_wasm_host_functions().await?,
)),
AppRequest::ProvideMemproofs(memproofs) => {
self.conductor_handle
.clone()
.provide_memproofs(&installed_app_id, memproofs)
.await?;
Ok(AppResponse::Ok)
}
AppRequest::EnableApp => {
let status = self
.conductor_handle
.get_app_info(&installed_app_id)
.await?
.ok_or(ConductorApiError::other("app not found".to_string()))?
.status;
match status {
AppStatus::Enabled
| AppStatus::Disabled(DisabledAppReason::NotStartedAfterProvidingMemproofs) => {
self.conductor_handle
.clone()
.enable_app(installed_app_id.clone())
.await?;
Ok(AppResponse::Ok)
}
_ => Err(ConductorApiError::other(
"app not in correct state to enable".to_string(),
)),
}
}
}
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize, SerializedBytes)]
pub struct AppAuthentication {
pub token: Vec<u8>,
pub installed_app_id: Option<InstalledAppId>,
}