ironflow_ops_gitlab/operation.rs
1//! [`GitLabOp`] -- wraps any [`Endpoint`] as a tracked [`Operation`].
2
3use async_trait::async_trait;
4use gitlab::AsyncGitlab;
5use gitlab::api::{AsyncQuery, Endpoint};
6use ironflow_core::error::OperationError;
7use ironflow_core::operation::{Operation, OperationContext};
8use serde_json::Value;
9
10/// A GitLab endpoint wrapped as an Ironflow [`Operation`].
11///
12/// Created via [`GitLab::op`](crate::GitLab::op). Implements `Operation` so it
13/// can be passed to `WorkflowContext::operation()` for step lifecycle tracking
14/// (step record, status transitions, duration, output persistence).
15///
16/// The endpoint is executed against the cloned [`AsyncGitlab`] client and the
17/// response is deserialized as a JSON [`Value`].
18///
19/// # Examples
20///
21/// ```no_run
22/// use ironflow_ops_gitlab::GitLab;
23/// use ironflow_core::operation::{Operation, OperationContext, NoopSecretResolver};
24/// use gitlab::api::projects;
25/// use std::sync::Arc;
26///
27/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
28/// let ctx = OperationContext::new(Arc::new(NoopSecretResolver));
29/// let gitlab = GitLab::from_context(&ctx).await?;
30///
31/// let endpoint = projects::Project::builder().project(42).build()?;
32/// let op = gitlab.op(endpoint);
33///
34/// assert_eq!(op.kind(), "gitlab");
35/// # Ok(())
36/// # }
37/// ```
38pub struct GitLabOp<E> {
39 client: AsyncGitlab,
40 endpoint: E,
41}
42
43impl<E> GitLabOp<E> {
44 pub(crate) fn new(client: AsyncGitlab, endpoint: E) -> Self {
45 Self { client, endpoint }
46 }
47}
48
49#[async_trait]
50impl<E> Operation for GitLabOp<E>
51where
52 E: Endpoint + Sync + Send,
53{
54 fn kind(&self) -> &str {
55 "gitlab"
56 }
57
58 async fn execute(&self, _ctx: &OperationContext) -> Result<Value, OperationError> {
59 let result: Value =
60 self.endpoint
61 .query_async(&self.client)
62 .await
63 .map_err(|e| OperationError::Http {
64 status: None,
65 message: e.to_string(),
66 })?;
67 Ok(result)
68 }
69
70 fn input(&self) -> Option<Value> {
71 Some(Value::Object(serde_json::Map::from_iter([(
72 "endpoint".to_string(),
73 Value::String(self.endpoint.endpoint().into_owned()),
74 )])))
75 }
76}