Add support for emitting Prefer header
This commit is contained in:
@@ -10,7 +10,7 @@ pub enum ContainerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ContainerType {
|
impl ContainerType {
|
||||||
pub fn as_named_node(&self) -> NamedNodeRef<'_> {
|
pub fn as_named_node_ref(&self) -> NamedNodeRef<'_> {
|
||||||
match self {
|
match self {
|
||||||
ContainerType::Basic => vocab::ldp::BASIC_CONTAINER,
|
ContainerType::Basic => vocab::ldp::BASIC_CONTAINER,
|
||||||
ContainerType::Direct => vocab::ldp::DIRECT_CONTAINER,
|
ContainerType::Direct => vocab::ldp::DIRECT_CONTAINER,
|
||||||
@@ -18,7 +18,7 @@ impl ContainerType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_named_node(node: NamedNodeRef<'_>) -> Option<Self> {
|
pub fn from_named_node_ref(node: NamedNodeRef<'_>) -> Option<Self> {
|
||||||
match node {
|
match node {
|
||||||
vocab::ldp::BASIC_CONTAINER => Some(ContainerType::Basic),
|
vocab::ldp::BASIC_CONTAINER => Some(ContainerType::Basic),
|
||||||
vocab::ldp::DIRECT_CONTAINER => Some(ContainerType::Direct),
|
vocab::ldp::DIRECT_CONTAINER => Some(ContainerType::Direct),
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ use reqwest_middleware::reqwest::header::HeaderName;
|
|||||||
pub const X_STATE_TOKEN: HeaderName = HeaderName::from_static("x-state-token");
|
pub const X_STATE_TOKEN: HeaderName = HeaderName::from_static("x-state-token");
|
||||||
pub const X_IF_STATE_TOKEN: HeaderName = HeaderName::from_static("x-if-state-token");
|
pub const X_IF_STATE_TOKEN: HeaderName = HeaderName::from_static("x-if-state-token");
|
||||||
pub const PREFER: HeaderName = HeaderName::from_static("prefer");
|
pub const PREFER: HeaderName = HeaderName::from_static("prefer");
|
||||||
pub const PREFERENCE_APPLIED: HeaderName = HeaderName::from_static("preference-applied");
|
|
||||||
|
|||||||
+5
-3
@@ -2,24 +2,26 @@
|
|||||||
|
|
||||||
mod container;
|
mod container;
|
||||||
mod error;
|
mod error;
|
||||||
pub mod header;
|
mod header;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
pub mod model;
|
pub mod model;
|
||||||
|
mod prefer;
|
||||||
mod rdf_source;
|
mod rdf_source;
|
||||||
mod resource;
|
mod resource;
|
||||||
pub mod traverse;
|
mod traverse;
|
||||||
pub mod vocab;
|
pub mod vocab;
|
||||||
|
|
||||||
pub use http;
|
|
||||||
pub use oxigraph;
|
pub use oxigraph;
|
||||||
pub use reqwest_middleware;
|
pub use reqwest_middleware;
|
||||||
pub use reqwest_middleware::reqwest;
|
pub use reqwest_middleware::reqwest;
|
||||||
|
|
||||||
pub use container::ContainerType;
|
pub use container::ContainerType;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
pub use prefer::{Preference, header_for_preferences};
|
||||||
pub use rdf_source::{
|
pub use rdf_source::{
|
||||||
RdfSource, RdfSourceUpdateRequest, RdfSourceUpdateResponse, SerializationOptions,
|
RdfSource, RdfSourceUpdateRequest, RdfSourceUpdateResponse, SerializationOptions,
|
||||||
};
|
};
|
||||||
pub use resource::{
|
pub use resource::{
|
||||||
Resource, ResourceRequest, ResourceRequestBuilder, ResourceType, ResponseFormat,
|
Resource, ResourceRequest, ResourceRequestBuilder, ResourceType, ResponseFormat,
|
||||||
};
|
};
|
||||||
|
pub use traverse::Traverse;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! This module contains functionality related to the content of RDF documents.
|
||||||
|
|
||||||
mod dataset;
|
mod dataset;
|
||||||
#[cfg(feature = "keyed")]
|
#[cfg(feature = "keyed")]
|
||||||
mod keyed_dataset;
|
mod keyed_dataset;
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use crate::vocab;
|
||||||
|
use http::{HeaderMap, HeaderValue};
|
||||||
|
use oxigraph::model::{NamedNode, NamedNodeRef};
|
||||||
|
|
||||||
|
/// [Request preferences](https://www.w3.org/TR/ldp/#prefer-parameters)
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Preference {
|
||||||
|
Containment,
|
||||||
|
Membership,
|
||||||
|
MinimalContainer,
|
||||||
|
Other(NamedNode),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Preference {
|
||||||
|
pub fn as_named_node_ref(&self) -> NamedNodeRef<'_> {
|
||||||
|
match self {
|
||||||
|
Preference::Containment => vocab::ldp::PREFER_CONTAINMENT,
|
||||||
|
Preference::Membership => vocab::ldp::PREFER_MEMBERSHIP,
|
||||||
|
Preference::MinimalContainer => vocab::ldp::PREFER_MINIMAL_CONTAINER,
|
||||||
|
Preference::Other(preference) => preference.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_for_preferences<'a, I, J>(include: I, omit: J) -> crate::Result<HeaderMap>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a Preference>,
|
||||||
|
J: IntoIterator<Item = &'a Preference>,
|
||||||
|
{
|
||||||
|
let include_str = include
|
||||||
|
.into_iter()
|
||||||
|
.map(|preference| preference.as_named_node_ref().as_str().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
let omit_str = omit
|
||||||
|
.into_iter()
|
||||||
|
.map(|preference| preference.as_named_node_ref().as_str().to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
let mut value = String::from("return=representation");
|
||||||
|
|
||||||
|
if !include_str.is_empty() {
|
||||||
|
value = format!("{value}; include=\"{include_str}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !omit_str.is_empty() {
|
||||||
|
value = format!("{value}; omit=\"{omit_str}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
let header_value = HeaderValue::from_str(&value)
|
||||||
|
.expect("Failed to produce Prefer header value");
|
||||||
|
|
||||||
|
Ok(HeaderMap::from_iter([(
|
||||||
|
crate::header::PREFER,
|
||||||
|
header_value,
|
||||||
|
)]))
|
||||||
|
}
|
||||||
+30
-5
@@ -1,6 +1,6 @@
|
|||||||
use crate::container::ContainerType;
|
use crate::container::ContainerType;
|
||||||
use crate::rdf_source::RdfSource;
|
use crate::rdf_source::RdfSource;
|
||||||
use crate::vocab;
|
use crate::{Preference, prefer, vocab};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
@@ -41,6 +41,8 @@ pub struct ResourceRequestBuilder {
|
|||||||
follow_described_by: bool,
|
follow_described_by: bool,
|
||||||
validate_support: bool,
|
validate_support: bool,
|
||||||
formats: Vec<RdfFormat>,
|
formats: Vec<RdfFormat>,
|
||||||
|
include_preferences: Vec<Preference>,
|
||||||
|
omit_preferences: Vec<Preference>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceRequestBuilder {
|
impl ResourceRequestBuilder {
|
||||||
@@ -60,6 +62,8 @@ impl ResourceRequestBuilder {
|
|||||||
follow_described_by: true,
|
follow_described_by: true,
|
||||||
validate_support: true,
|
validate_support: true,
|
||||||
formats: Vec::new(),
|
formats: Vec::new(),
|
||||||
|
include_preferences: Vec::new(),
|
||||||
|
omit_preferences: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +115,18 @@ impl ResourceRequestBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Include a representation preference according to [section 7.2](https://www.w3.org/TR/ldp/#prefer-parameters).
|
||||||
|
pub fn include_preference(mut self, preference: Preference) -> Self {
|
||||||
|
self.include_preferences.push(preference);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Omit a representation preference according to [section 7.2](https://www.w3.org/TR/ldp/#prefer-parameters).
|
||||||
|
pub fn omit_preference(mut self, preference: Preference) -> Self {
|
||||||
|
self.omit_preferences.push(preference);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the request.
|
/// Build the request.
|
||||||
pub fn build(self) -> ResourceRequest {
|
pub fn build(self) -> ResourceRequest {
|
||||||
ResourceRequest { builder: self }
|
ResourceRequest { builder: self }
|
||||||
@@ -290,6 +306,15 @@ impl ResourceRequest {
|
|||||||
let mut request_builder = self.builder.client.get(url_to_get);
|
let mut request_builder = self.builder.client.get(url_to_get);
|
||||||
request_builder = self.add_media_types(request_builder);
|
request_builder = self.add_media_types(request_builder);
|
||||||
|
|
||||||
|
if !self.builder.include_preferences.is_empty() || !self.builder.omit_preferences.is_empty()
|
||||||
|
{
|
||||||
|
let header = prefer::header_for_preferences(
|
||||||
|
&self.builder.include_preferences,
|
||||||
|
&self.builder.omit_preferences,
|
||||||
|
)?;
|
||||||
|
request_builder = request_builder.headers(header);
|
||||||
|
}
|
||||||
|
|
||||||
response = request_builder.send().await?.error_for_status()?;
|
response = request_builder.send().await?.error_for_status()?;
|
||||||
|
|
||||||
if self.builder.validate_support {
|
if self.builder.validate_support {
|
||||||
@@ -335,20 +360,20 @@ pub enum ResourceType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceType {
|
impl ResourceType {
|
||||||
pub fn as_named_node(&self) -> NamedNodeRef<'_> {
|
pub fn as_named_node_ref(&self) -> NamedNodeRef<'_> {
|
||||||
match self {
|
match self {
|
||||||
ResourceType::NonRdfSource => vocab::ldp::NON_RDF_SOURCE,
|
ResourceType::NonRdfSource => vocab::ldp::NON_RDF_SOURCE,
|
||||||
ResourceType::RdfSource(None) => vocab::ldp::RDF_SOURCE,
|
ResourceType::RdfSource(None) => vocab::ldp::RDF_SOURCE,
|
||||||
ResourceType::RdfSource(Some(container_type)) => container_type.as_named_node(),
|
ResourceType::RdfSource(Some(container_type)) => container_type.as_named_node_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_named_node(node: NamedNodeRef<'_>) -> Option<Self> {
|
pub fn from_named_node_ref(node: NamedNodeRef<'_>) -> Option<Self> {
|
||||||
match node {
|
match node {
|
||||||
vocab::ldp::NON_RDF_SOURCE => Some(ResourceType::NonRdfSource),
|
vocab::ldp::NON_RDF_SOURCE => Some(ResourceType::NonRdfSource),
|
||||||
vocab::ldp::RDF_SOURCE => Some(ResourceType::RdfSource(None)),
|
vocab::ldp::RDF_SOURCE => Some(ResourceType::RdfSource(None)),
|
||||||
_ => {
|
_ => {
|
||||||
let container_type = ContainerType::from_named_node(node);
|
let container_type = ContainerType::from_named_node_ref(node);
|
||||||
if container_type.is_some() {
|
if container_type.is_some() {
|
||||||
Some(ResourceType::RdfSource(container_type))
|
Some(ResourceType::RdfSource(container_type))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,4 +17,11 @@ pub mod ldp {
|
|||||||
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#DirectContainer");
|
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#DirectContainer");
|
||||||
pub const INDIRECT_CONTAINER: NamedNodeRef<'_> =
|
pub const INDIRECT_CONTAINER: NamedNodeRef<'_> =
|
||||||
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#IndirectContainer");
|
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#IndirectContainer");
|
||||||
|
|
||||||
|
pub const PREFER_CONTAINMENT: NamedNodeRef<'_> =
|
||||||
|
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#PreferContainment");
|
||||||
|
pub const PREFER_MEMBERSHIP: NamedNodeRef<'_> =
|
||||||
|
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#PreferMembership");
|
||||||
|
pub const PREFER_MINIMAL_CONTAINER: NamedNodeRef<'_> =
|
||||||
|
NamedNodeRef::new_unchecked("http://www.w3.org/ns/ldp#PreferMinimalContainer");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user