diff --git a/crates/pile-client/src/lib.rs b/crates/pile-client/src/lib.rs index 110a58a..bd7b5f1 100644 --- a/crates/pile-client/src/lib.rs +++ b/crates/pile-client/src/lib.rs @@ -10,7 +10,9 @@ use std::pin::Pin; use thiserror::Error; use tracing::{trace, warn}; -pub use pile_dataset::serve::{ItemsResponse, LookupRequest, LookupResponse}; +pub use pile_dataset::serve::{ + FieldSpec, FieldsResponse, ItemsResponse, LookupRequest, LookupResponse, +}; #[derive(Debug, Error)] pub enum ClientError { @@ -197,6 +199,14 @@ impl DatasetClient { Ok(FieldResponse { content_type, data }) } + /// `GET /schema` — retrieve this dataset's schema. + pub async fn schema(&self) -> Result { + let url = format!("{}/schema", self.base_url); + trace!(url, "GET /schema"); + let resp = self.client.get(url).send().await?; + check_status(resp).await?.json().await.map_err(Into::into) + } + /// `GET /items` — paginate over all items in this dataset, ordered by (source, key). pub async fn list_items( &self, diff --git a/crates/pile-config/src/lib.rs b/crates/pile-config/src/lib.rs index d25936b..a3f329e 100644 --- a/crates/pile-config/src/lib.rs +++ b/crates/pile-config/src/lib.rs @@ -1,4 +1,4 @@ -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::{collections::HashMap, fmt::Debug, path::PathBuf}; use crate::{objectpath::ObjectPath, pattern::GroupPattern}; @@ -61,7 +61,7 @@ pub enum Source { // MARK: schema // -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct FieldSpec { /// How to find this field in a data entry pub path: Vec, diff --git a/crates/pile-dataset/src/serve/mod.rs b/crates/pile-dataset/src/serve/mod.rs index f1392b2..66a64d1 100644 --- a/crates/pile-dataset/src/serve/mod.rs +++ b/crates/pile-dataset/src/serve/mod.rs @@ -23,10 +23,13 @@ pub use field::*; mod items; pub use items::*; +mod schema; +pub use schema::*; + #[derive(OpenApi)] #[openapi( tags(), - paths(lookup, item_get, get_extract, items_list, get_field), + paths(lookup, item_get, get_extract, items_list, get_field, get_schema), components(schemas( LookupRequest, LookupResponse, @@ -55,6 +58,7 @@ impl Datasets { .route("/extract", get(get_extract)) .route("/field", get(get_field)) .route("/items", get(items_list)) + .route("/schema", get(get_schema)) .with_state(self.clone()); if let Some(prefix) = prefix { diff --git a/crates/pile-dataset/src/serve/schema.rs b/crates/pile-dataset/src/serve/schema.rs new file mode 100644 index 0000000..037c80a --- /dev/null +++ b/crates/pile-dataset/src/serve/schema.rs @@ -0,0 +1,31 @@ +use axum::{ + Json, + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use std::{collections::HashMap, sync::Arc}; + +pub use pile_config::FieldSpec; + +use crate::Datasets; + +pub type FieldsResponse = HashMap; + +/// Retrieve this dataset's schema. +#[utoipa::path( + get, + path = "/schema", + responses( + (status = 200, description = "This dataset's schema"), + ) +)] +pub async fn get_schema(State(state): State>) -> Response { + let fields: FieldsResponse = state + .config + .schema + .iter() + .map(|(k, v)| (k.as_str().to_owned(), v.clone())) + .collect(); + (StatusCode::OK, Json(fields)).into_response() +}