//! Admin authority-record management. Reads require `ViewInternal`; writes `EditCatalogue`. use auth::{Authorized, EditCatalogue, ViewInternal}; use axum::{ Json, Router, extract::{Query, State}, http::StatusCode, routing::get, }; use domain::{AuthorityKind, LocalizedLabel, NewAuthority}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use crate::{ AppState, admin_objects::LabelView, admin_vocab::{CreatedId, LabelInput}, }; #[derive(Serialize, ToSchema)] pub(crate) struct AuthorityView { pub id: String, pub kind: String, pub external_uri: Option, pub labels: Vec, } #[derive(Deserialize, ToSchema)] pub(crate) struct NewAuthorityRequest { /// "person" | "organisation" | "place". pub kind: String, pub external_uri: Option, pub labels: Vec, } #[derive(Deserialize)] pub(crate) struct KindQuery { kind: String, } #[utoipa::path( get, path = "/api/admin/authorities", params(("kind" = String, Query, description = "person | organisation | place")), responses( (status = 200, body = [AuthorityView]), (status = 401), (status = 403), (status = 422) ) )] pub(crate) async fn list_authorities( _auth: Authorized, State(state): State, Query(q): Query, ) -> Result>, StatusCode> { let kind = AuthorityKind::from_db(&q.kind).ok_or(StatusCode::UNPROCESSABLE_ENTITY)?; let authorities = db::authority::list_by_kind(state.db.pool(), kind) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json( authorities .into_iter() .map(|authority| AuthorityView { id: authority.id.to_string(), kind: authority.kind.as_str().to_owned(), external_uri: authority.external_uri, labels: authority .labels .into_iter() .map(|label| LabelView { lang: label.lang, label: label.label, }) .collect(), }) .collect(), )) } #[utoipa::path( post, path = "/api/admin/authorities", request_body = NewAuthorityRequest, responses( (status = 201, body = CreatedId), (status = 401), (status = 403), (status = 422) ) )] pub(crate) async fn create_authority( _auth: Authorized, State(state): State, Json(req): Json, ) -> Result<(StatusCode, Json), StatusCode> { let kind = AuthorityKind::from_db(&req.kind).ok_or(StatusCode::UNPROCESSABLE_ENTITY)?; let new = NewAuthority { kind, external_uri: req.external_uri, labels: req .labels .into_iter() .map(|label| LocalizedLabel { lang: label.lang, label: label.label, }) .collect(), }; let mut tx = state .db .pool() .begin() .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let id = db::authority::create_authority(&mut tx, &new) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; tx.commit() .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok((StatusCode::CREATED, Json(CreatedId { id: id.to_string() }))) } pub(crate) fn routes() -> Router { Router::new().route( "/api/admin/authorities", get(list_authorities).post(create_authority), ) }