Skip to main content

openrouter_rs/api/
scim.rs

1use derive_builder::Builder;
2use reqwest::Client as HttpClient;
3use serde::{Deserialize, Serialize};
4use urlencoding::encode;
5
6use crate::{
7    error::OpenRouterError,
8    transport::{request as transport_request, response as transport_response},
9    types::{ApiResponse, PaginationOptions},
10};
11
12#[derive(Serialize)]
13struct PaginationQuery {
14    #[serde(skip_serializing_if = "Option::is_none")]
15    offset: Option<u32>,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    limit: Option<u32>,
18}
19
20#[derive(Serialize)]
21struct KeepMembersQuery {
22    keep_members: bool,
23}
24
25#[derive(Deserialize)]
26struct DeleteScimGroupMappingResponse {
27    deleted: bool,
28}
29
30/// One SCIM group synchronized into the organization.
31#[derive(Serialize, Deserialize, Debug, Clone)]
32#[non_exhaustive]
33pub struct ScimGroup {
34    pub id: String,
35    pub organization_id: String,
36    pub external_id: Option<String>,
37    pub display_name: String,
38    pub created_at: String,
39    pub updated_at: String,
40}
41
42/// Paginated SCIM group response.
43#[derive(Serialize, Deserialize, Debug, Clone)]
44#[non_exhaustive]
45pub struct ListScimGroupsResponse {
46    pub data: Vec<ScimGroup>,
47    pub total_count: u64,
48}
49
50/// One SCIM group-to-workspace role mapping.
51#[derive(Serialize, Deserialize, Debug, Clone)]
52#[non_exhaustive]
53pub struct ScimGroupMapping {
54    pub id: String,
55    pub organization_id: String,
56    pub scim_group_id: String,
57    pub workspace_id: String,
58    pub role: String,
59    pub created_at: String,
60    pub updated_at: String,
61}
62
63/// Paginated SCIM group mapping response.
64#[derive(Serialize, Deserialize, Debug, Clone)]
65#[non_exhaustive]
66pub struct ListScimGroupMappingsResponse {
67    pub data: Vec<ScimGroupMapping>,
68    pub total_count: u64,
69}
70
71/// Request to map a SCIM group to one workspace role.
72#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
73#[builder(build_fn(error = "OpenRouterError"))]
74#[non_exhaustive]
75pub struct CreateScimGroupMappingRequest {
76    #[builder(setter(into))]
77    pub scim_group_id: String,
78    #[builder(setter(into))]
79    pub workspace_id: String,
80    #[builder(setter(into))]
81    pub role: String,
82}
83
84impl CreateScimGroupMappingRequest {
85    pub fn builder() -> CreateScimGroupMappingRequestBuilder {
86        CreateScimGroupMappingRequestBuilder::default()
87    }
88}
89
90/// Request to update a SCIM group mapping role.
91#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
92#[builder(build_fn(error = "OpenRouterError"))]
93#[non_exhaustive]
94pub struct UpdateScimGroupMappingRequest {
95    #[builder(setter(into))]
96    pub role: String,
97}
98
99impl UpdateScimGroupMappingRequest {
100    pub fn builder() -> UpdateScimGroupMappingRequestBuilder {
101        UpdateScimGroupMappingRequestBuilder::default()
102    }
103}
104
105/// List SCIM groups (`GET /scim/groups`).
106pub async fn list_scim_groups(
107    base_url: &str,
108    management_key: &str,
109    pagination: Option<PaginationOptions>,
110) -> Result<ListScimGroupsResponse, OpenRouterError> {
111    let http_client = crate::transport::new_client()?;
112    list_scim_groups_with_client(&http_client, base_url, management_key, pagination).await
113}
114
115pub(crate) async fn list_scim_groups_with_client(
116    http_client: &HttpClient,
117    base_url: &str,
118    management_key: &str,
119    pagination: Option<PaginationOptions>,
120) -> Result<ListScimGroupsResponse, OpenRouterError> {
121    let query = PaginationQuery {
122        offset: pagination.and_then(|value| value.offset),
123        limit: pagination.and_then(|value| value.limit),
124    };
125    let request = transport_request::with_bearer_auth(
126        transport_request::get(http_client, &format!("{base_url}/scim/groups")),
127        management_key,
128    );
129    let response = if query.offset.is_none() && query.limit.is_none() {
130        request.send().await?
131    } else {
132        request.query(&query).send().await?
133    };
134
135    if response.status().is_success() {
136        transport_response::parse_json_response(response, "SCIM group list").await
137    } else {
138        transport_response::handle_error(response).await?;
139        unreachable!()
140    }
141}
142
143/// List SCIM group mappings (`GET /scim/group-mappings`).
144pub async fn list_scim_group_mappings(
145    base_url: &str,
146    management_key: &str,
147    pagination: Option<PaginationOptions>,
148) -> Result<ListScimGroupMappingsResponse, OpenRouterError> {
149    let http_client = crate::transport::new_client()?;
150    list_scim_group_mappings_with_client(&http_client, base_url, management_key, pagination).await
151}
152
153pub(crate) async fn list_scim_group_mappings_with_client(
154    http_client: &HttpClient,
155    base_url: &str,
156    management_key: &str,
157    pagination: Option<PaginationOptions>,
158) -> Result<ListScimGroupMappingsResponse, OpenRouterError> {
159    let query = PaginationQuery {
160        offset: pagination.and_then(|value| value.offset),
161        limit: pagination.and_then(|value| value.limit),
162    };
163    let request = transport_request::with_bearer_auth(
164        transport_request::get(http_client, &format!("{base_url}/scim/group-mappings")),
165        management_key,
166    );
167    let response = if query.offset.is_none() && query.limit.is_none() {
168        request.send().await?
169    } else {
170        request.query(&query).send().await?
171    };
172
173    if response.status().is_success() {
174        transport_response::parse_json_response(response, "SCIM group mapping list").await
175    } else {
176        transport_response::handle_error(response).await?;
177        unreachable!()
178    }
179}
180
181/// Create a SCIM group mapping (`POST /scim/group-mappings`).
182pub async fn create_scim_group_mapping(
183    base_url: &str,
184    management_key: &str,
185    request: &CreateScimGroupMappingRequest,
186) -> Result<ScimGroupMapping, OpenRouterError> {
187    let http_client = crate::transport::new_client()?;
188    create_scim_group_mapping_with_client(&http_client, base_url, management_key, request).await
189}
190
191pub(crate) async fn create_scim_group_mapping_with_client(
192    http_client: &HttpClient,
193    base_url: &str,
194    management_key: &str,
195    request: &CreateScimGroupMappingRequest,
196) -> Result<ScimGroupMapping, OpenRouterError> {
197    let response = transport_request::with_bearer_auth(
198        transport_request::post(http_client, &format!("{base_url}/scim/group-mappings")),
199        management_key,
200    )
201    .json(request)
202    .send()
203    .await?;
204
205    if response.status().is_success() {
206        let response: ApiResponse<ScimGroupMapping> =
207            transport_response::parse_json_response(response, "SCIM group mapping creation")
208                .await?;
209        Ok(response.data)
210    } else {
211        transport_response::handle_error(response).await?;
212        unreachable!()
213    }
214}
215
216/// Get one SCIM group mapping (`GET /scim/group-mappings/{id}`).
217pub async fn get_scim_group_mapping(
218    base_url: &str,
219    management_key: &str,
220    id: &str,
221) -> Result<ScimGroupMapping, OpenRouterError> {
222    let http_client = crate::transport::new_client()?;
223    get_scim_group_mapping_with_client(&http_client, base_url, management_key, id).await
224}
225
226pub(crate) async fn get_scim_group_mapping_with_client(
227    http_client: &HttpClient,
228    base_url: &str,
229    management_key: &str,
230    id: &str,
231) -> Result<ScimGroupMapping, OpenRouterError> {
232    let response = transport_request::with_bearer_auth(
233        transport_request::get(
234            http_client,
235            &format!("{base_url}/scim/group-mappings/{}", encode(id)),
236        ),
237        management_key,
238    )
239    .send()
240    .await?;
241
242    if response.status().is_success() {
243        let response: ApiResponse<ScimGroupMapping> =
244            transport_response::parse_json_response(response, "SCIM group mapping").await?;
245        Ok(response.data)
246    } else {
247        transport_response::handle_error(response).await?;
248        unreachable!()
249    }
250}
251
252/// Update one SCIM group mapping (`PATCH /scim/group-mappings/{id}`).
253pub async fn update_scim_group_mapping(
254    base_url: &str,
255    management_key: &str,
256    id: &str,
257    request: &UpdateScimGroupMappingRequest,
258) -> Result<ScimGroupMapping, OpenRouterError> {
259    let http_client = crate::transport::new_client()?;
260    update_scim_group_mapping_with_client(&http_client, base_url, management_key, id, request).await
261}
262
263pub(crate) async fn update_scim_group_mapping_with_client(
264    http_client: &HttpClient,
265    base_url: &str,
266    management_key: &str,
267    id: &str,
268    request: &UpdateScimGroupMappingRequest,
269) -> Result<ScimGroupMapping, OpenRouterError> {
270    let response = transport_request::with_bearer_auth(
271        transport_request::patch(
272            http_client,
273            &format!("{base_url}/scim/group-mappings/{}", encode(id)),
274        ),
275        management_key,
276    )
277    .json(request)
278    .send()
279    .await?;
280
281    if response.status().is_success() {
282        let response: ApiResponse<ScimGroupMapping> =
283            transport_response::parse_json_response(response, "SCIM group mapping update").await?;
284        Ok(response.data)
285    } else {
286        transport_response::handle_error(response).await?;
287        unreachable!()
288    }
289}
290
291/// Delete one SCIM group mapping (`DELETE /scim/group-mappings/{id}`).
292pub async fn delete_scim_group_mapping(
293    base_url: &str,
294    management_key: &str,
295    id: &str,
296    keep_members: bool,
297) -> Result<bool, OpenRouterError> {
298    let http_client = crate::transport::new_client()?;
299    delete_scim_group_mapping_with_client(&http_client, base_url, management_key, id, keep_members)
300        .await
301}
302
303pub(crate) async fn delete_scim_group_mapping_with_client(
304    http_client: &HttpClient,
305    base_url: &str,
306    management_key: &str,
307    id: &str,
308    keep_members: bool,
309) -> Result<bool, OpenRouterError> {
310    let response = transport_request::with_bearer_auth(
311        transport_request::delete(
312            http_client,
313            &format!("{base_url}/scim/group-mappings/{}", encode(id)),
314        ),
315        management_key,
316    )
317    .query(&KeepMembersQuery { keep_members })
318    .send()
319    .await?;
320
321    if response.status().is_success() {
322        let response: DeleteScimGroupMappingResponse =
323            transport_response::parse_json_response(response, "SCIM group mapping deletion")
324                .await?;
325        Ok(response.deleted)
326    } else {
327        transport_response::handle_error(response).await?;
328        unreachable!()
329    }
330}