use std::sync::Arc;
use serde_json::Value;
use crate::adapters::{
AdminAdapter, ArtifactsAdapter, AuditAdapter, BlobsAdapter, ContextAdapter, ExecAdapter,
FederationAdapter, FederationInternalAdapter, FilesAdapter, GraphAdapter, MigrationsAdapter,
MirrorsAdapter, ObservabilityAdapter, PolicyAdapter, QueryAdapter, RegistryAdapter,
RepositoriesAdapter, SearchIndexAdapter, SnapshotsAdapter, WorkspacesAdapter,
};
use crate::config::TreeDxConfig;
use crate::error::TreeDxResult;
use crate::generated::openapi_types::TREEDX_OPENAPI_OPERATIONS;
use crate::transport::{ReqwestTransport, Transport, TreeDxHttpMethod};
#[derive(Clone)]
pub struct TreeDxClient {
config: TreeDxConfig,
transport: Arc<dyn Transport>,
repositories: RepositoriesAdapter,
workspaces: WorkspacesAdapter,
files: FilesAdapter,
blobs: BlobsAdapter,
query: QueryAdapter,
graph: GraphAdapter,
context: ContextAdapter,
federation: FederationAdapter,
registry: RegistryAdapter,
snapshots: SnapshotsAdapter,
artifacts: ArtifactsAdapter,
mirrors: MirrorsAdapter,
migrations: MigrationsAdapter,
exec: ExecAdapter,
observability: ObservabilityAdapter,
admin: AdminAdapter,
audit: AuditAdapter,
policy: PolicyAdapter,
search_index: SearchIndexAdapter,
federation_internal: FederationInternalAdapter,
}
impl TreeDxClient {
pub fn new(config: TreeDxConfig) -> Self {
let transport = Arc::new(ReqwestTransport::new(config.clone()));
Self::with_transport(config, transport)
}
pub fn with_transport(config: TreeDxConfig, transport: Arc<dyn Transport>) -> Self {
Self {
config,
repositories: RepositoriesAdapter::new(transport.clone()),
workspaces: WorkspacesAdapter::new(transport.clone()),
files: FilesAdapter::new(transport.clone()),
blobs: BlobsAdapter::new(transport.clone()),
query: QueryAdapter::new(transport.clone()),
graph: GraphAdapter::new(transport.clone()),
context: ContextAdapter::new(transport.clone()),
federation: FederationAdapter::new(transport.clone()),
registry: RegistryAdapter::new(transport.clone()),
snapshots: SnapshotsAdapter::new(transport.clone()),
artifacts: ArtifactsAdapter::new(transport.clone()),
mirrors: MirrorsAdapter::new(transport.clone()),
migrations: MigrationsAdapter::new(transport.clone()),
exec: ExecAdapter::new(transport.clone()),
observability: ObservabilityAdapter::new(transport.clone()),
admin: AdminAdapter::new(transport.clone()),
audit: AuditAdapter::new(transport.clone()),
policy: PolicyAdapter::new(transport.clone()),
search_index: SearchIndexAdapter::new(transport.clone()),
federation_internal: FederationInternalAdapter::new(transport.clone()),
transport,
}
}
pub fn config(&self) -> &TreeDxConfig {
&self.config
}
pub fn transport(&self) -> &Arc<dyn Transport> {
&self.transport
}
pub fn repositories(&self) -> &RepositoriesAdapter {
&self.repositories
}
pub fn workspaces(&self) -> &WorkspacesAdapter {
&self.workspaces
}
pub fn files(&self) -> &FilesAdapter {
&self.files
}
pub fn blobs(&self) -> &BlobsAdapter {
&self.blobs
}
pub fn query(&self) -> &QueryAdapter {
&self.query
}
pub fn graph(&self) -> &GraphAdapter {
&self.graph
}
pub fn context(&self) -> &ContextAdapter {
&self.context
}
pub fn federation(&self) -> &FederationAdapter {
&self.federation
}
pub fn registry(&self) -> &RegistryAdapter {
&self.registry
}
pub fn snapshots(&self) -> &SnapshotsAdapter {
&self.snapshots
}
pub fn artifacts(&self) -> &ArtifactsAdapter {
&self.artifacts
}
pub fn mirrors(&self) -> &MirrorsAdapter {
&self.mirrors
}
pub fn migrations(&self) -> &MigrationsAdapter {
&self.migrations
}
pub fn exec(&self) -> &ExecAdapter {
&self.exec
}
pub fn observability(&self) -> &ObservabilityAdapter {
&self.observability
}
pub fn admin(&self) -> &AdminAdapter {
&self.admin
}
pub fn audit(&self) -> &AuditAdapter {
&self.audit
}
pub fn policy(&self) -> &PolicyAdapter {
&self.policy
}
pub fn search_index(&self) -> &SearchIndexAdapter {
&self.search_index
}
pub fn federation_internal(&self) -> &FederationInternalAdapter {
&self.federation_internal
}
pub async fn health(&self) -> TreeDxResult<Value> {
self.observability.health().await
}
pub async fn version(&self) -> TreeDxResult<Value> {
crate::adapters::common::json_request(
&self.transport,
crate::transport::TreeDxHttpMethod::Get,
"/api/v1/version",
None,
None,
)
.await
}
pub async fn whoami(&self) -> TreeDxResult<Value> {
crate::adapters::common::json_request(
&self.transport,
crate::transport::TreeDxHttpMethod::Get,
"/api/v1/auth/whoami",
None,
None,
)
.await
}
pub async fn effective_scope(&self) -> TreeDxResult<Value> {
crate::adapters::common::json_request(
&self.transport,
crate::transport::TreeDxHttpMethod::Get,
"/api/v1/policy/effective-scope",
None,
None,
)
.await
}
pub async fn auth_mode(&self) -> TreeDxResult<Value> {
crate::adapters::common::json_request(
&self.transport,
TreeDxHttpMethod::Get,
"/api/v1/auth/mode",
None,
None,
)
.await
}
pub async fn create_dev_token(&self, body: Option<Value>) -> TreeDxResult<Value> {
crate::adapters::common::json_request(
&self.transport,
TreeDxHttpMethod::Post,
"/api/v1/auth/dev-token",
body,
None,
)
.await
}
pub async fn operation(
&self,
method: TreeDxHttpMethod,
path: &str,
options: OperationOptions,
) -> TreeDxResult<Value> {
let method_name = method.as_str();
if !TREEDX_OPENAPI_OPERATIONS
.iter()
.any(|operation| operation.method == method_name && operation.path == path)
{
return Err(crate::error::TreeDxApiError::network(format!(
"unknown TreeDX OpenAPI operation: {method_name} {path}"
)));
}
let mut resolved = path.to_string();
for name in path.match_indices('{').filter_map(|(start, _)| {
let rest = &path[start + 1..];
rest.find('}').map(|end| &rest[..end])
}) {
let value = options.path_params.get(name).ok_or_else(|| {
crate::error::TreeDxApiError::network(format!(
"missing path parameter {name} for {method_name} {path}"
))
})?;
resolved = resolved.replace(
&format!("{{{name}}}"),
&crate::adapters::common::segment(value),
);
}
crate::adapters::common::json_request(
&self.transport,
method,
resolved,
options.body,
Some(options.query),
)
.await
}
}
#[derive(Clone, Debug, Default)]
pub struct OperationOptions {
pub path_params: std::collections::BTreeMap<String, String>,
pub query: std::collections::BTreeMap<String, String>,
pub body: Option<Value>,
}
#[derive(Clone)]
pub struct TreeDxRegistryClient {
registry: RegistryAdapter,
}
impl TreeDxRegistryClient {
pub fn new(client: &TreeDxClient) -> Self {
Self {
registry: client.registry().clone(),
}
}
pub fn registry(&self) -> &RegistryAdapter {
&self.registry
}
}
#[derive(Clone)]
pub struct TreeDxFederatedClient {
federation: FederationAdapter,
}
impl TreeDxFederatedClient {
pub fn new(client: &TreeDxClient) -> Self {
Self {
federation: client.federation().clone(),
}
}
pub fn federation(&self) -> &FederationAdapter {
&self.federation
}
}