Skip to main content

zammad_api/api/
organizations.rs

1use async_stream::try_stream;
2use futures_core::stream::BoxStream;
3use reqwest::Method;
4use serde::Serialize;
5use serde_json::{Map, Value};
6
7use crate::client::Client;
8use crate::error::{Error, Result};
9use crate::models::{NewOrganization, Organization};
10
11const PATH: &str = "api/v1/organizations";
12
13/// Organization endpoints.
14#[derive(Debug)]
15pub struct OrganizationsApi<'a> {
16    client: &'a Client,
17}
18
19impl<'a> OrganizationsApi<'a> {
20    pub(crate) fn new(client: &'a Client) -> Self {
21        Self { client }
22    }
23
24    /// `GET /api/v1/organizations/:id`.
25    pub async fn get(&self, id: u64) -> Result<Organization> {
26        let req = self.client.request(Method::GET, &format!("{PATH}/{id}"));
27        self.client.send_json(req).await
28    }
29
30    /// `GET /api/v1/organizations` - paginated list.
31    pub fn list(&self) -> ListOrganizations<'a> {
32        ListOrganizations {
33            client: self.client,
34            per_page: 100,
35        }
36    }
37
38    /// `GET /api/v1/organizations/search?query=…`.
39    pub fn search(&self, query: impl Into<String>) -> SearchOrganizations<'a> {
40        SearchOrganizations {
41            client: self.client,
42            query: query.into(),
43            per_page: 100,
44        }
45    }
46
47    /// `POST /api/v1/organizations` - create from a [`NewOrganization`].
48    pub async fn create(&self, org: &NewOrganization) -> Result<Organization> {
49        let req = self.client.request(Method::POST, PATH).json(org);
50        self.client.send_json(req).await
51    }
52
53    /// `PUT /api/v1/organizations/:id` - accepts any [`Serialize`] patch.
54    pub async fn update<P: Serialize + ?Sized>(&self, id: u64, patch: &P) -> Result<Organization> {
55        let req = self
56            .client
57            .request(Method::PUT, &format!("{PATH}/{id}"))
58            .json(patch);
59        self.client.send_json(req).await
60    }
61
62    /// Set a single field and verify it was persisted server-side.
63    /// See [`crate::api::TicketsApi::set_field`] for the rationale.
64    pub async fn set_field(
65        &self,
66        id: u64,
67        field: &str,
68        value: impl Into<Value>,
69    ) -> Result<Organization> {
70        let value: Value = value.into();
71        let mut body = Map::new();
72        body.insert(field.to_string(), value.clone());
73        let updated = self.update(id, &Value::Object(body)).await?;
74        let actual = updated.extra.get(field).cloned();
75        if actual.as_ref() != Some(&value) {
76            return Err(Error::WriteVerificationFailed {
77                field: field.into(),
78                actual,
79            });
80        }
81        Ok(updated)
82    }
83
84    /// `DELETE /api/v1/organizations/:id`.
85    pub async fn delete(&self, id: u64) -> Result<()> {
86        let req = self.client.request(Method::DELETE, &format!("{PATH}/{id}"));
87        self.client.send_unit(req).await
88    }
89}
90
91/// Builder + iterator for `GET /api/v1/organizations`.
92#[derive(Debug)]
93pub struct ListOrganizations<'a> {
94    client: &'a Client,
95    per_page: u32,
96}
97
98impl<'a> ListOrganizations<'a> {
99    /// Page size (Zammad caps at 200).
100    pub fn per_page(mut self, n: u32) -> Self {
101        self.per_page = n;
102        self
103    }
104
105    /// Fetch a single page.
106    pub async fn page(&self, page: u32) -> Result<Vec<Organization>> {
107        let req = self.client.request(Method::GET, PATH).query(&[
108            ("page", page.to_string()),
109            ("per_page", self.per_page.to_string()),
110        ]);
111        self.client.send_json(req).await
112    }
113
114    /// Yield every organization, fetching pages lazily.
115    pub fn stream(self) -> BoxStream<'a, Result<Organization>> {
116        let client = self.client;
117        let per_page = self.per_page;
118        Box::pin(try_stream! {
119            let mut page = 1u32;
120            loop {
121                let req = client.request(Method::GET, PATH).query(&[
122                    ("page", page.to_string()),
123                    ("per_page", per_page.to_string()),
124                ]);
125                let chunk: Vec<Organization> = client.send_json(req).await?;
126                let len = chunk.len() as u32;
127                for o in chunk { yield o; }
128                if len < per_page { break; }
129                page += 1;
130            }
131        })
132    }
133}
134
135/// Builder + iterator for `GET /api/v1/organizations/search`.
136#[derive(Debug)]
137pub struct SearchOrganizations<'a> {
138    client: &'a Client,
139    query: String,
140    per_page: u32,
141}
142
143impl<'a> SearchOrganizations<'a> {
144    /// Page size (Zammad caps at 200).
145    pub fn per_page(mut self, n: u32) -> Self {
146        self.per_page = n;
147        self
148    }
149
150    /// Fetch a single page of search results.
151    pub async fn page(&self, page: u32) -> Result<Vec<Organization>> {
152        let req = self
153            .client
154            .request(Method::GET, &format!("{PATH}/search"))
155            .query(&[
156                ("query", self.query.clone()),
157                ("page", page.to_string()),
158                ("per_page", self.per_page.to_string()),
159            ]);
160        self.client.send_json(req).await
161    }
162
163    /// Yield every search hit, fetching pages lazily.
164    pub fn stream(self) -> BoxStream<'a, Result<Organization>> {
165        let client = self.client;
166        let per_page = self.per_page;
167        let query = self.query;
168        Box::pin(try_stream! {
169            let mut page = 1u32;
170            loop {
171                let req = client
172                    .request(Method::GET, &format!("{PATH}/search"))
173                    .query(&[
174                        ("query", query.clone()),
175                        ("page", page.to_string()),
176                        ("per_page", per_page.to_string()),
177                    ]);
178                let chunk: Vec<Organization> = client.send_json(req).await?;
179                let len = chunk.len() as u32;
180                for o in chunk { yield o; }
181                if len < per_page { break; }
182                page += 1;
183            }
184        })
185    }
186}