···156156 oauth_client
157157}
158158159159+/// Add oauthClients field to NetworkSlicesSlice type
160160+pub fn add_oauth_clients_field_to_slice(
161161+ object: Object,
162162+ auth_base_url: String,
163163+) -> Object {
164164+ use crate::graphql::schema_builder::RecordContainer;
165165+166166+ let base_url_for_oauth = auth_base_url.clone();
167167+168168+ object.field(
169169+ Field::new(
170170+ "oauthClients",
171171+ TypeRef::named_nn_list_nn("OAuthClient"),
172172+ move |ctx| {
173173+ let base_url = base_url_for_oauth.clone();
174174+175175+ FieldFuture::new(async move {
176176+ let container = ctx.parent_value.try_downcast_ref::<RecordContainer>()?;
177177+ let slice_uri = &container.record.uri;
178178+179179+ // Get pool from context and create database instance
180180+ let pool = ctx.data::<sqlx::PgPool>()
181181+ .map_err(|_| Error::new("Database pool not found in context"))?;
182182+ let database = crate::database::Database::new(pool.clone());
183183+184184+ // Fetch OAuth clients from database
185185+ let clients = database
186186+ .get_oauth_clients_for_slice(slice_uri)
187187+ .await
188188+ .map_err(|e| Error::new(format!("Failed to fetch OAuth clients: {}", e)))?;
189189+190190+ if clients.is_empty() {
191191+ return Ok(Some(FieldValue::list(Vec::<FieldValue<'_>>::new())));
192192+ }
193193+194194+ // Fetch details from AIP server
195195+ let http_client = Client::new();
196196+ let mut client_data_list = Vec::new();
197197+198198+ for oauth_client in clients {
199199+ let aip_url = format!("{}/oauth/clients/{}", base_url, oauth_client.client_id);
200200+ let mut request_builder = http_client.get(&aip_url);
201201+202202+ if let Some(token) = &oauth_client.registration_access_token {
203203+ request_builder = request_builder.bearer_auth(token);
204204+ }
205205+206206+ match request_builder.send().await {
207207+ Ok(response) if response.status().is_success() => {
208208+ if let Ok(response_text) = response.text().await {
209209+ if let Ok(aip_client) = serde_json::from_str::<AipClientResponse>(&response_text) {
210210+ client_data_list.push(OAuthClientData {
211211+ client_id: aip_client.client_id,
212212+ client_secret: aip_client.client_secret,
213213+ client_name: aip_client.client_name,
214214+ redirect_uris: aip_client.redirect_uris,
215215+ grant_types: aip_client.grant_types,
216216+ response_types: aip_client.response_types,
217217+ scope: aip_client.scope,
218218+ client_uri: aip_client.client_uri,
219219+ logo_uri: aip_client.logo_uri,
220220+ tos_uri: aip_client.tos_uri,
221221+ policy_uri: aip_client.policy_uri,
222222+ created_at: oauth_client.created_at,
223223+ created_by_did: oauth_client.created_by_did,
224224+ });
225225+ }
226226+ }
227227+ }
228228+ _ => {
229229+ // Fallback for clients we can't fetch details for
230230+ client_data_list.push(OAuthClientData {
231231+ client_id: oauth_client.client_id,
232232+ client_secret: None,
233233+ client_name: "Unknown".to_string(),
234234+ redirect_uris: vec![],
235235+ grant_types: vec!["authorization_code".to_string()],
236236+ response_types: vec!["code".to_string()],
237237+ scope: None,
238238+ client_uri: None,
239239+ logo_uri: None,
240240+ tos_uri: None,
241241+ policy_uri: None,
242242+ created_at: oauth_client.created_at,
243243+ created_by_did: oauth_client.created_by_did,
244244+ });
245245+ }
246246+ }
247247+ }
248248+249249+ // Convert to GraphQL values
250250+ let field_values: Vec<FieldValue<'_>> = client_data_list
251251+ .into_iter()
252252+ .map(|client_data| {
253253+ let container = OAuthClientContainer { client: client_data };
254254+ FieldValue::owned_any(container)
255255+ })
256256+ .collect();
257257+258258+ Ok(Some(FieldValue::list(field_values)))
259259+ })
260260+ },
261261+ )
262262+ .description("Get all OAuth clients for this slice")
263263+ )
264264+}
265265+159266/// Add oauthClients query to the Query type
160267pub fn add_oauth_clients_query(query: Object, slice_uri: String, auth_base_url: String) -> Object {
161268 query.field(
+3
frontend-v2/schema.graphql
···14951495 Get statistics for this slice including collection counts, record counts, and actor counts
14961496 """
14971497 stats: SliceStats!
14981498+14991499+ """Get all OAuth clients for this slice"""
15001500+ oauthClients: [OAuthClient!]!
14981501}
1499150215001503type NetworkSlicesSliceAggregated {