use super::ObjectTransferAccess;
use crate::{AbsolutePath, ContentRef, NamespaceId, RevisionNo};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(deny_unknown_fields)]
pub struct BeginDownloadRequest {
pub path: AbsolutePath,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub revision_no: Option<RevisionNo>,
}
impl BeginDownloadRequest {
pub fn for_path(path: AbsolutePath) -> Self {
Self {
path,
revision_no: None,
}
}
pub fn for_revision(path: AbsolutePath, revision_no: RevisionNo) -> Self {
Self {
path,
revision_no: Some(revision_no),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct BeginDownloadResponse {
pub namespace_id: NamespaceId,
pub absolute_path: AbsolutePath,
pub revision_no: RevisionNo,
pub content_ref: ContentRef,
pub access: ObjectTransferAccess,
}
#[cfg(test)]
mod tests {
use super::{BeginDownloadRequest, BeginDownloadResponse};
use crate::v0::ObjectTransferAccess;
use crate::{AbsolutePath, ContentId, ContentRef, NamespaceId, RevisionNo};
use std::collections::BTreeMap;
fn absolute_path() -> AbsolutePath {
AbsolutePath::parse("/docs/report.txt").expect("absolute path")
}
#[test]
fn a_download_request_names_only_a_path_and_a_revision() {
let request: BeginDownloadRequest =
serde_json::from_str(r#"{"path":"/docs/report.txt"}"#).expect("decode request");
assert_eq!(request.path, absolute_path());
assert_eq!(request.revision_no, None);
let pinned: BeginDownloadRequest =
serde_json::from_str(r#"{"path":"/docs/report.txt","revision_no":3}"#)
.expect("decode pinned request");
assert_eq!(pinned.revision_no, Some(RevisionNo(3)));
assert!(
serde_json::from_str::<BeginDownloadRequest>(
r#"{"path":"/docs/report.txt","content_id":"con_0123456789abcdef0123456789abcdef"}"#
)
.is_err(),
"a client must not be able to name the content object"
);
}
#[test]
fn a_download_grant_exposes_only_presigned_access() {
let response = BeginDownloadResponse {
namespace_id: NamespaceId::parse("demo").expect("namespace id"),
absolute_path: absolute_path(),
revision_no: RevisionNo(7),
content_ref: ContentRef::blob_v1(ContentId::generate(), b"hello"),
access: ObjectTransferAccess::PresignedUrl {
method: "GET".to_owned(),
url: "https://bucket.example/object?X-Amz-Signature=abc".to_owned(),
headers: BTreeMap::new(),
expires_at_ms: 1,
},
};
let json = serde_json::to_string(&response).expect("serialize response");
assert!(json.contains(r#""kind":"presigned_url""#));
assert!(!json.contains("object_key"));
}
}