Skip to main content

treedx/
client.rs

1use std::sync::Arc;
2
3use serde_json::Value;
4
5use crate::adapters::{
6    AdminAdapter, ArtifactsAdapter, AuditAdapter, BlobsAdapter, ContextAdapter, ExecAdapter,
7    FederationAdapter, FederationInternalAdapter, FilesAdapter, GraphAdapter, MigrationsAdapter,
8    MirrorsAdapter, ObservabilityAdapter, PolicyAdapter, QueryAdapter, RegistryAdapter,
9    RepositoriesAdapter, SearchIndexAdapter, SnapshotsAdapter, WorkspacesAdapter,
10};
11use crate::config::TreeDxConfig;
12use crate::error::TreeDxResult;
13use crate::generated::openapi_types::TREEDX_OPENAPI_OPERATIONS;
14use crate::transport::{ReqwestTransport, Transport, TreeDxHttpMethod};
15
16#[derive(Clone)]
17pub struct TreeDxClient {
18    config: TreeDxConfig,
19    transport: Arc<dyn Transport>,
20    repositories: RepositoriesAdapter,
21    workspaces: WorkspacesAdapter,
22    files: FilesAdapter,
23    blobs: BlobsAdapter,
24    query: QueryAdapter,
25    graph: GraphAdapter,
26    context: ContextAdapter,
27    federation: FederationAdapter,
28    registry: RegistryAdapter,
29    snapshots: SnapshotsAdapter,
30    artifacts: ArtifactsAdapter,
31    mirrors: MirrorsAdapter,
32    migrations: MigrationsAdapter,
33    exec: ExecAdapter,
34    observability: ObservabilityAdapter,
35    admin: AdminAdapter,
36    audit: AuditAdapter,
37    policy: PolicyAdapter,
38    search_index: SearchIndexAdapter,
39    federation_internal: FederationInternalAdapter,
40}
41
42impl TreeDxClient {
43    pub fn new(config: TreeDxConfig) -> Self {
44        let transport = Arc::new(ReqwestTransport::new(config.clone()));
45        Self::with_transport(config, transport)
46    }
47
48    pub fn with_transport(config: TreeDxConfig, transport: Arc<dyn Transport>) -> Self {
49        Self {
50            config,
51            repositories: RepositoriesAdapter::new(transport.clone()),
52            workspaces: WorkspacesAdapter::new(transport.clone()),
53            files: FilesAdapter::new(transport.clone()),
54            blobs: BlobsAdapter::new(transport.clone()),
55            query: QueryAdapter::new(transport.clone()),
56            graph: GraphAdapter::new(transport.clone()),
57            context: ContextAdapter::new(transport.clone()),
58            federation: FederationAdapter::new(transport.clone()),
59            registry: RegistryAdapter::new(transport.clone()),
60            snapshots: SnapshotsAdapter::new(transport.clone()),
61            artifacts: ArtifactsAdapter::new(transport.clone()),
62            mirrors: MirrorsAdapter::new(transport.clone()),
63            migrations: MigrationsAdapter::new(transport.clone()),
64            exec: ExecAdapter::new(transport.clone()),
65            observability: ObservabilityAdapter::new(transport.clone()),
66            admin: AdminAdapter::new(transport.clone()),
67            audit: AuditAdapter::new(transport.clone()),
68            policy: PolicyAdapter::new(transport.clone()),
69            search_index: SearchIndexAdapter::new(transport.clone()),
70            federation_internal: FederationInternalAdapter::new(transport.clone()),
71            transport,
72        }
73    }
74
75    pub fn config(&self) -> &TreeDxConfig {
76        &self.config
77    }
78    pub fn transport(&self) -> &Arc<dyn Transport> {
79        &self.transport
80    }
81    pub fn repositories(&self) -> &RepositoriesAdapter {
82        &self.repositories
83    }
84    pub fn workspaces(&self) -> &WorkspacesAdapter {
85        &self.workspaces
86    }
87    pub fn files(&self) -> &FilesAdapter {
88        &self.files
89    }
90    pub fn blobs(&self) -> &BlobsAdapter {
91        &self.blobs
92    }
93    pub fn query(&self) -> &QueryAdapter {
94        &self.query
95    }
96    pub fn graph(&self) -> &GraphAdapter {
97        &self.graph
98    }
99    pub fn context(&self) -> &ContextAdapter {
100        &self.context
101    }
102    pub fn federation(&self) -> &FederationAdapter {
103        &self.federation
104    }
105    pub fn registry(&self) -> &RegistryAdapter {
106        &self.registry
107    }
108    pub fn snapshots(&self) -> &SnapshotsAdapter {
109        &self.snapshots
110    }
111    pub fn artifacts(&self) -> &ArtifactsAdapter {
112        &self.artifacts
113    }
114    pub fn mirrors(&self) -> &MirrorsAdapter {
115        &self.mirrors
116    }
117    pub fn migrations(&self) -> &MigrationsAdapter {
118        &self.migrations
119    }
120    pub fn exec(&self) -> &ExecAdapter {
121        &self.exec
122    }
123    pub fn observability(&self) -> &ObservabilityAdapter {
124        &self.observability
125    }
126    pub fn admin(&self) -> &AdminAdapter {
127        &self.admin
128    }
129    pub fn audit(&self) -> &AuditAdapter {
130        &self.audit
131    }
132    pub fn policy(&self) -> &PolicyAdapter {
133        &self.policy
134    }
135    pub fn search_index(&self) -> &SearchIndexAdapter {
136        &self.search_index
137    }
138    pub fn federation_internal(&self) -> &FederationInternalAdapter {
139        &self.federation_internal
140    }
141
142    pub async fn health(&self) -> TreeDxResult<Value> {
143        self.observability.health().await
144    }
145    pub async fn version(&self) -> TreeDxResult<Value> {
146        crate::adapters::common::json_request(
147            &self.transport,
148            crate::transport::TreeDxHttpMethod::Get,
149            "/api/v1/version",
150            None,
151            None,
152        )
153        .await
154    }
155    pub async fn whoami(&self) -> TreeDxResult<Value> {
156        crate::adapters::common::json_request(
157            &self.transport,
158            crate::transport::TreeDxHttpMethod::Get,
159            "/api/v1/auth/whoami",
160            None,
161            None,
162        )
163        .await
164    }
165    pub async fn effective_scope(&self) -> TreeDxResult<Value> {
166        crate::adapters::common::json_request(
167            &self.transport,
168            crate::transport::TreeDxHttpMethod::Get,
169            "/api/v1/policy/effective-scope",
170            None,
171            None,
172        )
173        .await
174    }
175
176    pub async fn auth_mode(&self) -> TreeDxResult<Value> {
177        crate::adapters::common::json_request(
178            &self.transport,
179            TreeDxHttpMethod::Get,
180            "/api/v1/auth/mode",
181            None,
182            None,
183        )
184        .await
185    }
186
187    pub async fn create_dev_token(&self, body: Option<Value>) -> TreeDxResult<Value> {
188        crate::adapters::common::json_request(
189            &self.transport,
190            TreeDxHttpMethod::Post,
191            "/api/v1/auth/dev-token",
192            body,
193            None,
194        )
195        .await
196    }
197
198    pub async fn operation(
199        &self,
200        method: TreeDxHttpMethod,
201        path: &str,
202        options: OperationOptions,
203    ) -> TreeDxResult<Value> {
204        let method_name = method.as_str();
205        if !TREEDX_OPENAPI_OPERATIONS
206            .iter()
207            .any(|operation| operation.method == method_name && operation.path == path)
208        {
209            return Err(crate::error::TreeDxApiError::network(format!(
210                "unknown TreeDX OpenAPI operation: {method_name} {path}"
211            )));
212        }
213        let mut resolved = path.to_string();
214        for name in path.match_indices('{').filter_map(|(start, _)| {
215            let rest = &path[start + 1..];
216            rest.find('}').map(|end| &rest[..end])
217        }) {
218            let value = options.path_params.get(name).ok_or_else(|| {
219                crate::error::TreeDxApiError::network(format!(
220                    "missing path parameter {name} for {method_name} {path}"
221                ))
222            })?;
223            resolved = resolved.replace(
224                &format!("{{{name}}}"),
225                &crate::adapters::common::segment(value),
226            );
227        }
228        crate::adapters::common::json_request(
229            &self.transport,
230            method,
231            resolved,
232            options.body,
233            Some(options.query),
234        )
235        .await
236    }
237}
238
239#[derive(Clone, Debug, Default)]
240pub struct OperationOptions {
241    pub path_params: std::collections::BTreeMap<String, String>,
242    pub query: std::collections::BTreeMap<String, String>,
243    pub body: Option<Value>,
244}
245
246#[derive(Clone)]
247pub struct TreeDxRegistryClient {
248    registry: RegistryAdapter,
249}
250
251impl TreeDxRegistryClient {
252    pub fn new(client: &TreeDxClient) -> Self {
253        Self {
254            registry: client.registry().clone(),
255        }
256    }
257
258    pub fn registry(&self) -> &RegistryAdapter {
259        &self.registry
260    }
261}
262
263#[derive(Clone)]
264pub struct TreeDxFederatedClient {
265    federation: FederationAdapter,
266}
267
268impl TreeDxFederatedClient {
269    pub fn new(client: &TreeDxClient) -> Self {
270        Self {
271            federation: client.federation().clone(),
272        }
273    }
274
275    pub fn federation(&self) -> &FederationAdapter {
276        &self.federation
277    }
278}