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#[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#[derive(Serialize, Deserialize, Debug, Clone)]
44#[non_exhaustive]
45pub struct ListScimGroupsResponse {
46 pub data: Vec<ScimGroup>,
47 pub total_count: u64,
48}
49
50#[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#[derive(Serialize, Deserialize, Debug, Clone)]
65#[non_exhaustive]
66pub struct ListScimGroupMappingsResponse {
67 pub data: Vec<ScimGroupMapping>,
68 pub total_count: u64,
69}
70
71#[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#[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
105pub 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
143pub 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
181pub 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
216pub 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
252pub 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
291pub 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}