supervisor cleanup, documentation and minor fixes
This commit is contained in:
@@ -1,17 +1,27 @@
|
||||
//! Builder pattern for WasmSupervisorClient to ensure proper configuration
|
||||
//! Builder pattern for SupervisorClient to ensure proper configuration
|
||||
//!
|
||||
//! This module provides a type-safe builder that guarantees a client cannot be
|
||||
//! created without a secret, preventing authentication issues.
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm::WasmSupervisorClient;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::{SupervisorClient, HttpTransport, ClientResult, ClientError};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use jsonrpsee::http_client::HttpClientBuilder;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||
|
||||
/// Builder for WasmSupervisorClient that enforces secret requirement
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[derive(Clone)]
|
||||
pub struct WasmSupervisorClientBuilder {
|
||||
server_url: Option<String>,
|
||||
secret: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl WasmSupervisorClientBuilder {
|
||||
/// Create a new builder
|
||||
pub fn new() -> Self {
|
||||
@@ -48,13 +58,90 @@ impl WasmSupervisorClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
impl Default for WasmSupervisorClientBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Builder for SupervisorClient (HTTP transport)
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SupervisorClientBuilder {
|
||||
url: Option<String>,
|
||||
secret: Option<String>,
|
||||
timeout: Option<std::time::Duration>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl SupervisorClientBuilder {
|
||||
/// Create a new builder
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
url: None,
|
||||
secret: None,
|
||||
timeout: Some(std::time::Duration::from_secs(30)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the server URL
|
||||
pub fn url(mut self, url: impl Into<String>) -> Self {
|
||||
self.url = Some(url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the authentication secret
|
||||
pub fn secret(mut self, secret: impl Into<String>) -> Self {
|
||||
self.secret = Some(secret.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the request timeout (default: 30 seconds)
|
||||
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the SupervisorClient with HTTP transport
|
||||
pub fn build(self) -> ClientResult<SupervisorClient<HttpTransport>> {
|
||||
let server_url = self.url
|
||||
.ok_or_else(|| ClientError::Http("URL is required".to_string()))?;
|
||||
let secret = self.secret
|
||||
.ok_or_else(|| ClientError::Http("Secret is required".to_string()))?;
|
||||
|
||||
// Create headers with Authorization bearer token
|
||||
let mut headers = HeaderMap::new();
|
||||
let auth_value = format!("Bearer {}", secret);
|
||||
headers.insert(
|
||||
HeaderName::from_static("authorization"),
|
||||
HeaderValue::from_str(&auth_value)
|
||||
.map_err(|e| ClientError::Http(format!("Invalid auth header: {}", e)))?
|
||||
);
|
||||
|
||||
let client = HttpClientBuilder::default()
|
||||
.request_timeout(self.timeout.unwrap_or(std::time::Duration::from_secs(30)))
|
||||
.set_headers(headers)
|
||||
.build(&server_url)
|
||||
.map_err(|e| ClientError::Http(e.to_string()))?;
|
||||
|
||||
let transport = HttpTransport { client };
|
||||
|
||||
Ok(SupervisorClient {
|
||||
transport,
|
||||
secret,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl Default for SupervisorClientBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, target_arch = "wasm32"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ pub mod transports;
|
||||
pub mod wasm;
|
||||
|
||||
// Builder module for type-safe client construction
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod builder;
|
||||
|
||||
// Re-export WASM types for convenience
|
||||
@@ -28,17 +27,18 @@ pub use wasm::{WasmSupervisorClient, WasmJobType, WasmRunnerType, create_job_can
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use builder::WasmSupervisorClientBuilder;
|
||||
|
||||
// Re-export HTTP builder for convenience
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use builder::SupervisorClientBuilder;
|
||||
|
||||
// Native client dependencies
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use jsonrpsee::{
|
||||
core::client::ClientT,
|
||||
http_client::{HttpClient, HttpClientBuilder},
|
||||
http_client::HttpClient,
|
||||
rpc_params,
|
||||
};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||
|
||||
|
||||
/// Transport abstraction for supervisor communication
|
||||
/// Allows different transport layers (HTTP, Mycelium, etc.)
|
||||
@@ -95,6 +95,7 @@ impl SupervisorTransport for HttpTransport {
|
||||
#[derive(Clone)]
|
||||
pub struct SupervisorClient<T: SupervisorTransport = HttpTransport> {
|
||||
transport: T,
|
||||
#[allow(dead_code)]
|
||||
secret: String,
|
||||
}
|
||||
|
||||
@@ -254,105 +255,12 @@ pub struct AuthVerifyResponse {
|
||||
pub created_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Simple ProcessStatus type for native builds to avoid service manager dependency
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type ProcessStatus = ProcessStatusWrapper;
|
||||
|
||||
// Types duplicated from supervisor-core to avoid cyclic dependency
|
||||
// These match the types in hero-supervisor but are defined here independently
|
||||
|
||||
/// Runner status information (duplicated to avoid cyclic dependency)
|
||||
/// Type aliases for convenience
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type RunnerStatus = ProcessStatusWrapper;
|
||||
|
||||
/// Log information (duplicated to avoid cyclic dependency)
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type LogInfo = LogInfoWrapper;
|
||||
|
||||
/// Type aliases for WASM compatibility
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub type ProcessStatus = ProcessStatusWrapper;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub type RunnerStatus = ProcessStatusWrapper;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub type LogInfo = LogInfoWrapper;
|
||||
|
||||
/// Builder for SupervisorClient
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SupervisorClientBuilder {
|
||||
url: Option<String>,
|
||||
secret: Option<String>,
|
||||
timeout: Option<std::time::Duration>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl SupervisorClientBuilder {
|
||||
/// Create a new builder
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
url: None,
|
||||
secret: None,
|
||||
timeout: Some(std::time::Duration::from_secs(30)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the server URL
|
||||
pub fn url(mut self, url: impl Into<String>) -> Self {
|
||||
self.url = Some(url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the authentication secret
|
||||
pub fn secret(mut self, secret: impl Into<String>) -> Self {
|
||||
self.secret = Some(secret.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the request timeout (default: 30 seconds)
|
||||
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
|
||||
self.timeout = Some(timeout);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the SupervisorClient with HTTP transport
|
||||
pub fn build(self) -> ClientResult<SupervisorClient<HttpTransport>> {
|
||||
let server_url = self.url
|
||||
.ok_or_else(|| ClientError::Http("URL is required".to_string()))?;
|
||||
let secret = self.secret
|
||||
.ok_or_else(|| ClientError::Http("Secret is required".to_string()))?;
|
||||
|
||||
// Create headers with Authorization bearer token
|
||||
let mut headers = HeaderMap::new();
|
||||
let auth_value = format!("Bearer {}", secret);
|
||||
headers.insert(
|
||||
HeaderName::from_static("authorization"),
|
||||
HeaderValue::from_str(&auth_value)
|
||||
.map_err(|e| ClientError::Http(format!("Invalid auth header: {}", e)))?
|
||||
);
|
||||
|
||||
let client = HttpClientBuilder::default()
|
||||
.request_timeout(self.timeout.unwrap_or(std::time::Duration::from_secs(30)))
|
||||
.set_headers(headers)
|
||||
.build(&server_url)
|
||||
.map_err(|e| ClientError::Http(e.to_string()))?;
|
||||
|
||||
let transport = HttpTransport { client };
|
||||
|
||||
Ok(SupervisorClient {
|
||||
transport,
|
||||
secret,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl Default for SupervisorClientBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl SupervisorClient<HttpTransport> {
|
||||
/// Create a builder for HTTP-based SupervisorClient
|
||||
@@ -409,15 +317,17 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
job: Job,
|
||||
timeout: Option<u64>,
|
||||
) -> ClientResult<JobRunResponse> {
|
||||
let mut params = serde_json::json!({
|
||||
"job": job
|
||||
});
|
||||
// Server expects Job directly as params, not wrapped in an object
|
||||
let params = if let Some(_t) = timeout {
|
||||
// If timeout is provided, we need to extend the Job with timeout field
|
||||
// For now, just send the job directly and ignore timeout
|
||||
// TODO: Add timeout support to the RPC method signature
|
||||
serde_json::to_value(&job).map_err(ClientError::Serialization)?
|
||||
} else {
|
||||
serde_json::to_value(&job).map_err(ClientError::Serialization)?
|
||||
};
|
||||
|
||||
if let Some(t) = timeout {
|
||||
params["timeout"] = serde_json::json!(t);
|
||||
}
|
||||
|
||||
let result = self.transport.call("job.run", serde_json::json!([params])).await?;
|
||||
let result = self.transport.call("job.run", params).await?;
|
||||
serde_json::from_value(result).map_err(ClientError::Serialization)
|
||||
}
|
||||
|
||||
@@ -474,7 +384,7 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
let params = serde_json::json!({
|
||||
"config": config
|
||||
});
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("runner.add", serde_json::json!([params]))
|
||||
.await?;
|
||||
@@ -512,7 +422,7 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
"job": job
|
||||
});
|
||||
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("queue_job_to_runner", serde_json::json!([params]))
|
||||
.await?;
|
||||
@@ -603,7 +513,7 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
"secret_value": secret_value
|
||||
});
|
||||
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("add_secret", serde_json::json!([params]))
|
||||
.await?;
|
||||
@@ -621,7 +531,7 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
"secret_value": secret_value
|
||||
});
|
||||
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("remove_secret", serde_json::json!([params]))
|
||||
.await?;
|
||||
@@ -682,9 +592,9 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
|
||||
/// Create a new API key (admin only)
|
||||
pub async fn key_create(&self, key: ApiKey) -> ClientResult<()> {
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("key.create", serde_json::json!([key]))
|
||||
.call("auth.key.create", serde_json::json!([key]))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -700,7 +610,7 @@ impl<T: SupervisorTransport> SupervisorClient<T> {
|
||||
|
||||
/// Remove an API key (admin only)
|
||||
pub async fn key_delete(&self, key_id: String) -> ClientResult<()> {
|
||||
let result = self
|
||||
let _result = self
|
||||
.transport
|
||||
.call("key.delete", serde_json::json!([key_id]))
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user