use crate::CommonOpts;
use anyhow::{Context, Result};
use holochain_client::{
AdminWebsocket, AppWebsocket, AuthorizeSigningCredentialsPayload, CellInfo, ClientAgentSigner,
ExternIO, ZomeCallTarget,
};
use serde::de::DeserializeOwned;
pub struct Ham {
app_connection: AppWebsocket,
_signer: ClientAgentSigner,
}
impl Ham {
pub async fn connect(value: CommonOpts) -> Result<Self> {
let admin_port = value.admin_port;
let app_id = value.app_id;
use std::net::Ipv4Addr;
let admin = AdminWebsocket::connect((Ipv4Addr::LOCALHOST, admin_port))
.await
.context("Failed to connect to admin interface")?;
let app_interfaces = admin
.list_app_interfaces()
.await
.context("Failed to list app interfaces")?;
let app_interface = app_interfaces
.iter()
.find(|app_interface| app_interface.installed_app_id == None);
let port = if let Some(app_interface) = app_interface {
println!("Using existing app interface: {:?}", app_interface.port);
app_interface.port
} else {
let port = admin
.attach_app_interface(value.app_port, holochain_client::AllowedOrigins::Any, None)
.await
.context("Failed to attach app interface")?;
port
};
let issued_token: holochain_client::AppAuthenticationTokenIssued = admin
.issue_app_auth_token(app_id.to_string().into())
.await
.context("Failed to issue app auth token")?;
let signer = ClientAgentSigner::default();
let app_connection = AppWebsocket::connect(
(Ipv4Addr::LOCALHOST, port),
issued_token.token,
signer.clone().into(),
)
.await
.context("Failed to connect to app interface")?;
let installed_app = app_connection.app_info().await.unwrap().unwrap();
let cells = installed_app.cell_info.into_values().next().unwrap();
let cell_id = match cells[0].clone() {
CellInfo::Provisioned(c) => c.cell_id,
_ => panic!("Invalid cell type"),
};
let credentials = admin
.authorize_signing_credentials(AuthorizeSigningCredentialsPayload {
cell_id: cell_id.clone(),
functions: None,
})
.await
.unwrap();
signer.add_credentials(cell_id.clone(), credentials);
Ok(Self {
app_connection,
_signer: signer,
})
}
pub async fn zome_call<I, R>(
&self,
role_name: &str,
zome_name: &str,
fn_name: &str,
payload: I,
) -> Result<R>
where
I: serde::Serialize + std::fmt::Debug,
R: DeserializeOwned,
{
let response = self
.app_connection
.call_zome(
ZomeCallTarget::RoleName(role_name.to_string()),
zome_name.into(),
fn_name.into(),
ExternIO::encode(payload)?,
)
.await
.map_err(|e| anyhow::anyhow!("Failed to call zome: {}", e))?;
rmp_serde::from_slice(&response.0).context("Failed to deserialize response")
}
}