Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zome_client for client_holon_service #215

Open
wants to merge 5 commits into
base: 188-align-holons-zome-codebase-structure-to-architecture
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
926 changes: 159 additions & 767 deletions Cargo.lock

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion crates/dances_core/src/dance_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use crate::session_state::SessionState;
use hdk::prelude::*;

use holons::reference_layer::{HolonReference, StagedReference};
use holons::core_shared_objects::{CommitResponse, CommitRequestStatus};
use holons_core::core_shared_objects::{summarize_holons, Holon, HolonError};
use holons_guest::query_layer::NodeCollection;
use shared_types_holon::MapString;
use shared_types_holon::{MapInteger, MapString};

#[hdk_entry_helper]
#[derive(Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -139,3 +140,25 @@ impl DanceResponse {
)
}
}


/// get a CommitResponse from a DanceResponse
/// note: this is a short-term solution as information about the CommitResponse
/// is lost and ideally should be part of the DanceResponse
impl From<DanceResponse> for CommitResponse {
fn from(res: DanceResponse) -> Self {
CommitResponse {
status: match res.status_code {
ResponseStatusCode::OK => CommitRequestStatus::Complete,
ResponseStatusCode::Accepted => CommitRequestStatus::Complete,
_ => CommitRequestStatus::Incomplete,
},
commits_attempted: MapInteger(1),
saved_holons: match res.body {
ResponseBody::Holons(holons) => holons,
_ => vec![],
},
abandoned_holons:vec![]
}
}
}
9 changes: 9 additions & 0 deletions crates/holons_client/Cargo.toml
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holons_client must not depend on holons_guest. What's driving this dependency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed a few types:

use holons_guest::query_layer::{Node, NodeCollection, QueryExpression};

Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,23 @@ name = "holons_client"
derive-new = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
anyhow = "1.0"

# Holochain Dependencies
hdi = { workspace = true }
hdk = { workspace = true }

# dependencies for the holochain client library (currently SweetConductor
holochain = { version = "0.3", default-features = false, features = ["test_utils", "sqlite-encrypted"] }
tokio = { version = "1.35.1", features = ["full"] }


# MAP Dependencies
shared_types_holon = { workspace = true }
holons_core = { workspace = true }
holon_dance_builders = { workspace = true}
dances_core = { workspace = true }
holons_guest = { workspace = true }



Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,102 @@
use dances_core::dance_response::{DanceResponse, ResponseBody};
use dances_core::session_state::SessionState;
use holon_dance_builders::commit_dance::*;
use hdk::prelude::fake_action_hash;
use holochain::conductor::api::error::ConductorApiError;
use holon_dance_builders::commit_dance::build_commit_dance_request;
use holon_dance_builders::delete_holon_dance::build_delete_holon_dance_request;
use holon_dance_builders::get_holon_by_id_dance::build_get_holon_by_id_dance_request;
use holon_dance_builders::query_relationships_dance::build_query_relationships_dance_request;
use holons_core::core_shared_objects::{
CommitResponse, Holon, HolonCollection, HolonError, RelationshipMap, RelationshipName,
CollectionState, CommitResponse, Holon, HolonCollection, HolonError, RelationshipMap, RelationshipName
};
use holons_core::reference_layer::{HolonServiceApi, HolonsContextBehavior};
use holons_core::{HolonReference, SmartReference};
use holons_guest::query_layer::{Node, NodeCollection, QueryExpression};
use shared_types_holon::{HolonId, LocalId};
use std::rc::Rc;
use tokio::runtime::Runtime;


use crate::zome_client::{self, ZomeClient};
use crate::AppInstallation;


/// A concrete implementation of the `HolonResolver` trait for resolving local Holons.
#[derive(Debug,Clone)]
pub struct ClientHolonService;// {
//app_installation: AppInstallation
//}
pub struct ClientHolonService;

impl HolonServiceApi for ClientHolonService {

//fn install_app(&self) -> Result<AppInstallation, HolonError> {
/// ZomeClient::install_app()
//}

fn commit(&self, _context: &dyn HolonsContextBehavior) -> Result<CommitResponse, HolonError> {
//let request = build_commit_dance_request(&SessionState::empty())?;
// let response: DanceResponse = conductor.call(&cell.zome("dances"), "dance", valid_request).await;
// _context.get_space_manager()
todo!()
let request = build_commit_dance_request(&SessionState::empty())?;
//TODO - temporary install - should be in the context
let rt = Runtime::new().unwrap();
let installed_app:AppInstallation = rt.block_on(<zome_client::AppInstallation as ZomeClient>::install())
.map_err(|err: ConductorApiError| HolonError::CommitFailure(err.to_string()))?;

let cell_id = installed_app.cells[0].cell_id().clone();
let response = rt.block_on(installed_app.zomecall(cell_id,"dances","dance", request))?;
let commit_response:CommitResponse = response.into();
Ok(commit_response)
}

fn delete_holon(&self, _local_id: &LocalId) -> Result<(), HolonError> {
todo!()
let request = build_delete_holon_dance_request(&SessionState::empty(), LocalId(fake_action_hash(234)))?;
let rt = Runtime::new().unwrap();
//TODO - temporary install - should be in the context
let installed_app:AppInstallation = rt.block_on(<zome_client::AppInstallation as ZomeClient>::install())
.map_err(|err: ConductorApiError| HolonError::CommitFailure(err.to_string()))?;

let cell_id = installed_app.cells[0].cell_id().clone();
let _response = rt.block_on(installed_app.zomecall(cell_id,"dances","dance", request))?;
Ok(())
}

fn fetch_holon(&self, _id: &HolonId) -> Result<Holon, HolonError> {
todo!()
let request = build_get_holon_by_id_dance_request(&SessionState::empty(), _id.clone())?;
let rt = Runtime::new().unwrap();
//TODO - temporary install - should be in the context
let installed_app:AppInstallation = rt.block_on(<zome_client::AppInstallation as ZomeClient>::install())
.map_err(|err: ConductorApiError| HolonError::CommitFailure(err.to_string()))?;

let cell_id = installed_app.cells[0].cell_id().clone();
let response = rt.block_on(installed_app.zomecall(cell_id,"dances","dance", request))?;
match response.body {
ResponseBody::Holon(holon) => return Ok(holon),
_ => return Err(HolonError::HolonNotFound("Invalid response body".to_string())),
}
}

fn fetch_related_holons(
&self,
_source_id: &HolonId,
_relationship_name: &RelationshipName,
) -> Result<HolonCollection, HolonError> {
todo!()
let holon_reference: HolonReference =
HolonReference::Smart(SmartReference::new(_source_id.to_owned(), None));
let node_collection =
NodeCollection { members: vec![Node::new(holon_reference, None)], query_spec: None };
let request = build_query_relationships_dance_request(
&SessionState::empty(),
node_collection, QueryExpression{ relationship_name:_relationship_name.clone()})?;
let rt = Runtime::new().unwrap();
//TODO - temporary install - should be in the context
let installed_app:AppInstallation = rt.block_on(<zome_client::AppInstallation as ZomeClient>::install())
.map_err(|err: ConductorApiError| HolonError::CommitFailure(err.to_string()))?;

let cell_id = installed_app.cells[0].cell_id().clone();
let response = rt.block_on(installed_app.zomecall(cell_id,"dances","dance", request))?;
let node_collection = match response.body {
ResponseBody::Collection(nodes) => nodes,
_ => return Err(HolonError::HolonNotFound("Invalid response body".to_string())),
};
Ok(HolonCollection::from_parts(CollectionState::Fetched, node_collection.to_holon_references()))
}

fn fetch_all_populated_relationships(
&self,
_source_id: HolonId,
) -> Result<Rc<RelationshipMap>, HolonError> {
todo!()
unimplemented!()
}
}
5 changes: 5 additions & 0 deletions crates/holons_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ pub mod client_context;
pub mod client_shared_objects;

pub use client_context::ClientHolonsContext;

pub mod zome_client;
pub use zome_client::*;

//
// pub use client_shared_objects::*;

146 changes: 146 additions & 0 deletions crates/holons_client/src/zome_client.rs
evomimic marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@

use dances_core::dance_request::DanceRequest;
use dances_core::dance_response::{DanceResponse, ResponseStatusCode};
use hdi::prelude::AgentPubKey;

use hdk::prelude::CellId;
use holochain::conductor::api::error::ConductorApiError;
use holochain::sweettest::{SweetAgents, SweetApp, SweetCell, SweetConductor, SweetDnaFile};
use holons_core::core_shared_objects::HolonError;


pub trait ZomeClient: Sized {
fn install() -> impl std::future::Future<Output = Result<Self, ConductorApiError>> + Send;
fn install_app(app_name:&str, happ_url:Option<&str>) -> impl std::future::Future<Output = Result<Self, ConductorApiError>> + Send;
async fn zomecall(self, cell_id:CellId, zome_name:&str, fn_name:&str, request:DanceRequest) -> Result<DanceResponse, HolonError>;
//async fn wait_on_signal(&self, cell_id:CellId)-> Result<(), ConductorApiError>;
}

#[derive(Debug)]
pub struct AppInstallation {
pub conductor: SweetConductor,
pub app: SweetApp,
pub cells: Vec<SweetCell>,
//pub signer: ClientAgentSigner,
}

const DNA_FILEPATH: &str = "../../workdir/map_holons.dna";
const APP_ID: &str = "map_holons";
const HAPP_FILEPATH: &str = "../../workdir/map_holons.happ";



impl ZomeClient for AppInstallation {
async fn install() -> Result<AppInstallation, ConductorApiError> {
Self::install_app(APP_ID, Some(HAPP_FILEPATH)).await
}
async fn install_app(app_id:&str, happ_url:Option<&str>) -> Result<AppInstallation, ConductorApiError> {

let dna = SweetDnaFile::from_bundle(std::path::Path::new(&DNA_FILEPATH)).await.unwrap();
let mut conductor = SweetConductor::from_standard_config().await;
let holo_core_agent = SweetAgents::one(conductor.keystore()).await;
let app = conductor
.setup_app_for_agent(app_id, holo_core_agent.clone(), &[dna.clone()])
.await
.unwrap();

let cells = &app.cells().clone();//[0].clone();

//let agent_hash = holo_core_agent.into_inner();
//let agent = AgentPubKey::from_raw_39(agent_hash).unwrap();

Ok(Self{conductor, app, cells:cells.to_vec()})
}

async fn zomecall(self, cell_id:CellId, zome_name:&str, fn_name:&str, request:DanceRequest) -> Result<DanceResponse, HolonError> {

let zome = self.conductor.get_sweet_cell(cell_id).map_err(|err: ConductorApiError|HolonError::WasmError(err.to_string()))?.zome(zome_name);
println!("{:?}", zome);
let response: DanceResponse = self.conductor.call::<DanceRequest,DanceResponse>(&zome, fn_name, request).await;
match response.status_code {
ResponseStatusCode::OK => return Ok(response),
ResponseStatusCode::Accepted => return Ok(response),
_ => return Err(HolonError::WasmError(response.status_code.to_string())),
};
}

/* async fn wait_on_signal(&self, cell_id:CellId) -> Result<(), ConductorApiError> {

// Connect admin client
let admin_port = self.conductor.get_arbitrary_admin_websocket_port().unwrap();
let admin_ws = AdminWebsocket::connect((Ipv4Addr::LOCALHOST, admin_port))
.await
.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::Error::new(ErrorKind::ConnectionRefused, (arg0.to_string()))))?;
//.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::WebsocketError::Other(arg0.to_string())))?;

let credentials = admin_ws
.authorize_signing_credentials(AuthorizeSigningCredentialsPayload {
cell_id: cell_id.clone(),
functions: None,
})
.await
.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::Error::new(ErrorKind::ConnectionRefused, (arg0.to_string()))))?;
//.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::WebsocketError::Other(arg0.to_string())))?;
self.signer.add_credentials(cell_id.clone(), credentials);


let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();
// Connect app agent client
let app_ws_port = admin_ws
.attach_app_interface(0, AllowedOrigins::Any, None)
.await?;

let token_issued = admin_ws
.issue_app_auth_token(self.app_id.clone().into())
.await?;
let app_ws = AppWebsocket::connect(
(Ipv4Addr::LOCALHOST, app_ws_port),
token_issued.token,
self.signer.clone().into(),
)
.await
.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::Error::new(ErrorKind::ConnectionRefused, (arg0.to_string()))))?;
//.map_err(|arg0: anyhow::Error| ConductorApiError::WebsocketError(holochain_websocket::WebsocketError::Other(arg0.to_string())))?;

app_ws
.on_signal(move |signal| match signal {
Signal::App { signal, .. } => {
let ts: TestString = signal.into_inner().decode().unwrap();
assert_eq!(ts.0.as_str(), "i am a signal");
barrier_clone.wait();
}
_ => panic!("Invalid signal"),
})
.await
.unwrap();
barrier.wait();
Ok(())
}*/
}

/// MOCK CONDUCTOR

pub async fn setup_conductor() -> (SweetConductor, AgentPubKey, SweetCell) {
let dna = SweetDnaFile::from_bundle(std::path::Path::new(&DNA_FILEPATH)).await.unwrap();

// let dna_path = std::env::current_dir().unwrap().join(DNA_FILEPATH);
// println!("{}", dna_path.to_string_lossy());
// let dna = SweetDnaFile::from_bundle(&dna_path).await.unwrap();

let mut conductor = SweetConductor::from_standard_config().await;

let holo_core_agent = SweetAgents::one(conductor.keystore()).await;
let app = conductor
.setup_app_for_agent("app", holo_core_agent.clone(), &[dna.clone()])
.await
.unwrap();

let cell = app.into_cells()[0].clone();

let agent_hash = holo_core_agent.into_inner();
let agent = AgentPubKey::from_raw_39(agent_hash).unwrap();

(conductor, agent, cell)
}

Loading