Server: store POST: avoids arbitrary choice if the mime type matches both a graph and dataset format

pull/300/head
Tpt 2 years ago committed by Thomas Tanon
parent 38fdffc147
commit 112631a0d7
  1. 119
      server/src/main.rs

@ -1,4 +1,4 @@
use anyhow::bail; use anyhow::{anyhow, bail};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use flate2::read::MultiGzDecoder; use flate2::read::MultiGzDecoder;
use oxhttp::model::{Body, HeaderName, HeaderValue, Request, Response, Status}; use oxhttp::model::{Body, HeaderName, HeaderValue, Request, Response, Status};
@ -177,7 +177,7 @@ enum GraphOrDatasetFormat {
impl GraphOrDatasetFormat { impl GraphOrDatasetFormat {
fn from_path(path: &Path) -> anyhow::Result<Self> { fn from_path(path: &Path) -> anyhow::Result<Self> {
if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) {
Self::from_name(ext).map_err(|e| { Self::from_extension(ext).map_err(|e| {
e.context(format!( e.context(format!(
"Not able to guess the file format from file name extension '{}'", "Not able to guess the file format from file name extension '{}'",
ext ext
@ -191,31 +191,30 @@ impl GraphOrDatasetFormat {
} }
} }
fn from_name(name: &str) -> anyhow::Result<Self> { fn from_extension(name: &str) -> anyhow::Result<Self> {
let mut candidates = Vec::with_capacity(4); match (GraphFormat::from_extension(name), DatasetFormat::from_extension(name)) {
if let Some(f) = GraphFormat::from_extension(name) { (Some(g), Some(d)) => Err(anyhow!("The file extension '{}' can be resolved to both '{}' and '{}', not sure what to pick", name, g.file_extension(), d.file_extension())),
candidates.push(GraphOrDatasetFormat::Graph(f)); (Some(g), None) => Ok(GraphOrDatasetFormat::Graph(g)),
(None, Some(d)) => Ok(GraphOrDatasetFormat::Dataset(d)),
(None, None) =>
Err(anyhow!("The file extension '{}' is unknown", name))
} }
if let Some(f) = DatasetFormat::from_extension(name) { }
candidates.push(GraphOrDatasetFormat::Dataset(f));
} fn from_media_type(name: &str) -> anyhow::Result<Self> {
if let Some(f) = GraphFormat::from_media_type(name) { match (
candidates.push(GraphOrDatasetFormat::Graph(f)); GraphFormat::from_media_type(name),
} DatasetFormat::from_media_type(name),
if let Some(f) = DatasetFormat::from_media_type(name) { ) {
candidates.push(GraphOrDatasetFormat::Dataset(f)); (Some(g), Some(d)) => Err(anyhow!(
} "The media type '{}' can be resolved to both '{}' and '{}', not sure what to pick",
if candidates.is_empty() { name,
bail!("The format '{}' is unknown", name) g.file_extension(),
} else if candidates.len() == 1 { d.file_extension()
Ok(candidates[0]) )),
} else { (Some(g), None) => Ok(GraphOrDatasetFormat::Graph(g)),
bail!("The format '{}' can be resolved to multiple known formats, not sure what to pick ({})", name, candidates.iter().fold(String::new(), |a, f| { (None, Some(d)) => Ok(GraphOrDatasetFormat::Dataset(d)),
a + " " + match f { (None, None) => Err(anyhow!("The media type '{}' is unknown", name)),
GraphOrDatasetFormat::Graph(f) => f.file_extension(),
GraphOrDatasetFormat::Dataset(f) => f.file_extension(),
}
}).trim())
} }
} }
} }
@ -442,41 +441,45 @@ fn handle_request(request: &mut Request, store: Store) -> Result<Response, HttpE
let content_type = let content_type =
content_type(request).ok_or_else(|| bad_request("No Content-Type given"))?; content_type(request).ok_or_else(|| bad_request("No Content-Type given"))?;
if let Some(target) = store_target(request)? { if let Some(target) = store_target(request)? {
if let Some(format) = GraphFormat::from_media_type(&content_type) { let format = GraphFormat::from_media_type(&content_type)
let new = assert_that_graph_exists(&store, &target).is_ok(); .ok_or_else(|| unsupported_media_type(&content_type))?;
store let new = assert_that_graph_exists(&store, &target).is_ok();
.load_graph(
BufReader::new(request.body_mut()),
format,
GraphName::from(target).as_ref(),
None,
)
.map_err(bad_request)?;
Ok(Response::builder(if new {
Status::CREATED
} else {
Status::NO_CONTENT
})
.build())
} else {
Err(unsupported_media_type(&content_type))
}
} else if let Some(format) = DatasetFormat::from_media_type(&content_type) {
store
.load_dataset(BufReader::new(request.body_mut()), format, None)
.map_err(bad_request)?;
Ok(Response::builder(Status::NO_CONTENT).build())
} else if let Some(format) = GraphFormat::from_media_type(&content_type) {
let graph = resolve_with_base(request, &format!("/store/{:x}", random::<u128>()))?;
store store
.load_graph(BufReader::new(request.body_mut()), format, &graph, None) .load_graph(
BufReader::new(request.body_mut()),
format,
GraphName::from(target).as_ref(),
None,
)
.map_err(bad_request)?; .map_err(bad_request)?;
Ok(Response::builder(Status::CREATED) Ok(Response::builder(if new {
.with_header(HeaderName::LOCATION, graph.into_string()) Status::CREATED
.unwrap() } else {
.build()) Status::NO_CONTENT
})
.build())
} else { } else {
Err(unsupported_media_type(&content_type)) match GraphOrDatasetFormat::from_media_type(&content_type)
.map_err(|_| unsupported_media_type(&content_type))?
{
GraphOrDatasetFormat::Graph(format) => {
let graph =
resolve_with_base(request, &format!("/store/{:x}", random::<u128>()))?;
store
.load_graph(BufReader::new(request.body_mut()), format, &graph, None)
.map_err(bad_request)?;
Ok(Response::builder(Status::CREATED)
.with_header(HeaderName::LOCATION, graph.into_string())
.unwrap()
.build())
}
GraphOrDatasetFormat::Dataset(format) => {
store
.load_dataset(BufReader::new(request.body_mut()), format, None)
.map_err(bad_request)?;
Ok(Response::builder(Status::NO_CONTENT).build())
}
}
} }
} }
(path, "HEAD") if path.starts_with("/store") => { (path, "HEAD") if path.starts_with("/store") => {

Loading…
Cancel
Save