Skip to main content

uptrakit_openapi_client/
services.rs

1use crate::Result;
2use crate::UptrakitClient;
3use crate::types_impl::agents::MessageResponse;
4use crate::types_impl::batch_actions::{BatchActionRequest, BatchActionResponse};
5use crate::types_impl::pagination::PaginatedResponse;
6use crate::types_impl::services::{
7    ListServicesQuery, MergeAgentRequest, ServiceResponse, SetUpdateFreezeRequest,
8    UpdateServiceRequest,
9};
10use uuid::Uuid;
11
12impl UptrakitClient {
13    /// List services with optional filters and pagination.
14    pub async fn list_services(
15        &self,
16        query: &ListServicesQuery,
17    ) -> Result<PaginatedResponse<ServiceResponse>> {
18        self.get_with_query(crate::paths::services::BASE, query)
19            .await
20    }
21
22    /// Fetch all services matching the given filters across all pages.
23    ///
24    /// Automatically iterates through every page at [`MAX_PER_PAGE`] items per
25    /// request. The `page` and `per_page` fields of `query` are ignored; use
26    /// [`list_services`] for manual pagination control.
27    ///
28    /// [`MAX_PER_PAGE`]: uptrakit_web_api_types::pagination::MAX_PER_PAGE
29    /// [`list_services`]: Self::list_services
30    pub async fn list_all_services(
31        &self,
32        query: &ListServicesQuery,
33    ) -> Result<Vec<ServiceResponse>> {
34        self.fetch_all_pages(crate::paths::services::BASE, query)
35            .await
36    }
37
38    /// Get a single service by ID.
39    pub async fn get_service(&self, id: &Uuid) -> Result<ServiceResponse> {
40        self.get(&crate::paths::services::by_id(id)).await
41    }
42
43    /// Approve a pending service.
44    pub async fn approve_service(&self, id: &Uuid) -> Result<ServiceResponse> {
45        self.post_empty(&crate::paths::services::approve(id)).await
46    }
47
48    /// Reject a pending service.
49    pub async fn reject_service(&self, id: &Uuid) -> Result<ServiceResponse> {
50        self.post_empty(&crate::paths::services::reject(id)).await
51    }
52
53    /// Update a service's configurable settings (e.g. ping interval).
54    pub async fn update_service(
55        &self,
56        id: &Uuid,
57        req: &UpdateServiceRequest,
58    ) -> Result<ServiceResponse> {
59        self.put_json(&crate::paths::services::by_id(id), req).await
60    }
61
62    /// Deactivate (remove) a service.
63    pub async fn remove_service(&self, id: &Uuid) -> Result<()> {
64        self.delete(&crate::paths::services::by_id(id)).await
65    }
66
67    /// Enable or disable the update freeze on a connected service.
68    pub async fn set_update_freeze(
69        &self,
70        id: &Uuid,
71        req: &SetUpdateFreezeRequest,
72    ) -> Result<MessageResponse> {
73        self.post_json(&crate::paths::services::update_freeze(id), req)
74            .await
75    }
76
77    /// Perform a batch action on multiple services.
78    ///
79    /// Supported actions: `approve`, `reject`, `deactivate`.
80    pub async fn batch_services(&self, req: &BatchActionRequest) -> Result<BatchActionResponse> {
81        self.post_json(crate::paths::services::BATCH, req).await
82    }
83
84    /// Merge a pending source service into an approved target service.
85    pub async fn merge_service(
86        &self,
87        target_id: &Uuid,
88        req: &MergeAgentRequest,
89    ) -> Result<ServiceResponse> {
90        self.post_json(&crate::paths::services::merge(target_id), req)
91            .await
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use crate::shared_types_impl::ServiceStatus;
98    use crate::types_impl::services::{ListServicesQuery, MergeAgentRequest};
99    use uuid::Uuid;
100
101    #[test]
102    fn list_services_query_serialization_with_all_fields() {
103        let query = ListServicesQuery {
104            capability: Some("software_discovery".to_string()),
105            status: Some(ServiceStatus::Approved),
106            page: Some(2),
107            per_page: Some(50),
108        };
109        let qs = serde_urlencoded::to_string(&query).expect("serialize");
110        assert!(qs.contains("capability=software_discovery"));
111        assert!(qs.contains("status=approved"));
112        assert!(qs.contains("page=2"));
113        assert!(qs.contains("per_page=50"));
114    }
115
116    #[test]
117    fn list_services_query_serialization_skips_none() {
118        let query = ListServicesQuery {
119            capability: None,
120            status: None,
121            page: None,
122            per_page: None,
123        };
124        let qs = serde_urlencoded::to_string(&query).expect("serialize");
125        assert!(qs.is_empty());
126    }
127
128    #[test]
129    fn merge_agent_request_serialization() {
130        let source_uuid =
131            Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("valid uuid");
132        let req = MergeAgentRequest {
133            source_id: source_uuid,
134        };
135        let json = serde_json::to_string(&req).expect("serialize");
136        assert!(json.contains("550e8400-e29b-41d4-a716-446655440000"));
137        let parsed: MergeAgentRequest = serde_json::from_str(&json).expect("deserialize");
138        assert_eq!(parsed.source_id, req.source_id);
139    }
140
141    #[test]
142    fn update_service_request_cert_lifetime_hours_round_trip() {
143        use crate::types_impl::services::UpdateServiceRequest;
144
145        let req = UpdateServiceRequest {
146            ping_interval_seconds: None,
147            cert_lifetime_hours: Some(48),
148        };
149        let json = serde_json::to_string(&req).expect("serialize");
150        assert!(json.contains(r#""cert_lifetime_hours":48"#));
151        let parsed: UpdateServiceRequest = serde_json::from_str(&json).expect("deserialize");
152        assert_eq!(parsed.cert_lifetime_hours, Some(48));
153    }
154}