Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): test for read_with_override_content_composition #2067

Merged
merged 1 commit into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions core/src/services/azblob/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,16 +452,20 @@ impl Accessor for AzblobBackend {
.set_root(&self.core.root)
.set_name(&self.core.container)
.set_capability(Capability {
stat: true,
stat_with_if_match: true,
stat_with_if_none_match: true,

read: true,
read_can_next: true,
read_with_if_match: true,
read_with_if_none_match: true,
stat: true,
stat_with_if_match: true,
stat_with_if_none_match: true,
read_with_override_content_disposition: true,

write: true,
write_with_content_type: true,
write_with_cache_control: true,

delete: true,
create_dir: true,
list: true,
Expand Down
16 changes: 15 additions & 1 deletion core/src/services/s3/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,8 +912,16 @@ impl Accessor for S3Backend {
.set_root(&self.core.root)
.set_name(&self.core.bucket)
.set_capability(Capability {
stat: true,
stat_with_if_match: true,
stat_with_if_none_match: true,

read: true,
read_can_next: true,
read_with_if_match: true,
read_with_if_none_match: true,
read_with_override_content_disposition: true,

write: true,
list: true,
scan: true,
Expand Down Expand Up @@ -951,7 +959,13 @@ impl Accessor for S3Backend {
async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
let resp = self
.core
.s3_get_object(path, args.range(), args.if_none_match(), args.if_match())
.s3_get_object(
path,
args.range(),
args.if_none_match(),
args.if_match(),
args.override_content_disposition(),
)
.await?;

let status = resp.status();
Expand Down
11 changes: 9 additions & 2 deletions core/src/services/s3/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,16 @@ impl S3Core {
range: BytesRange,
if_none_match: Option<&str>,
if_match: Option<&str>,
override_content_disposition: Option<&str>,
) -> Result<Response<IncomingAsyncBody>> {
let mut req =
self.s3_get_object_request(path, range, None, None, if_none_match, if_match)?;
let mut req = self.s3_get_object_request(
path,
range,
override_content_disposition,
None,
if_none_match,
if_match,
)?;

self.sign(&mut req).await?;

Expand Down
14 changes: 7 additions & 7 deletions core/src/types/capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
/// - Operation with limtations should be named like `batch_max_operations`.
#[derive(Copy, Clone, Debug, Default)]
pub struct Capability {
/// If operator supports stat natively, it will be true.
pub stat: bool,
/// If operator supports stat with if match natively, it will be true.
pub stat_with_if_match: bool,
/// If operator supports stat with if none match natively, it will be true.
pub stat_with_if_none_match: bool,

/// If operator supports read natively, it will be true.
pub read: bool,
/// If operator supports seek on returning reader natively, it will
Expand All @@ -63,13 +70,6 @@ pub struct Capability {
/// if operator supports read with override content disposition natively, it will be true.
pub read_with_override_content_disposition: bool,

/// If operator supports stat natively, it will be true.
pub stat: bool,
/// If operator supports stat with if match natively, it will be true.
pub stat_with_if_match: bool,
/// If operator supports stat with if none match natively, it will be true.
pub stat_with_if_none_match: bool,

/// If operator supports write natively, it will be true.
pub write: bool,
/// If operator supports write with without content length, it will
Expand Down
5 changes: 5 additions & 0 deletions core/src/types/operator/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ impl OperatorInfo {
self.0.name()
}

/// Get [`Capability`] of operator.
pub fn capability(&self) -> Capability {
self.0.capability()
}

/// Check if current backend supports [`Accessor::read`] or not.
pub fn can_read(&self) -> bool {
self.0.capability().read
Expand Down
58 changes: 58 additions & 0 deletions core/tests/behavior/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ use futures::AsyncSeekExt;
use futures::StreamExt;
use log::debug;
use log::warn;
use opendal::ops::OpRead;
use opendal::EntryMode;
use opendal::ErrorKind;
use opendal::Operator;
use reqwest::Url;
use sha2::Digest;
use sha2::Sha256;
use std::str::FromStr;
use std::time::Duration;

use super::utils::*;

Expand Down Expand Up @@ -92,6 +96,7 @@ macro_rules! behavior_write_tests {
test_fuzz_part_reader,
test_read_with_dir_path,
test_read_with_special_chars,
test_read_with_override_content_disposition,
test_delete,
test_delete_empty_dir,
test_delete_with_special_chars,
Expand Down Expand Up @@ -580,6 +585,59 @@ pub async fn test_read_with_special_chars(op: Operator) -> Result<()> {
Ok(())
}

// Read file with override_content_disposition should succeed.
pub async fn test_read_with_override_content_disposition(op: Operator) -> Result<()> {
if !(op
.info()
.capability()
.read_with_override_content_disposition
&& op.info().can_presign())
{
return Ok(());
}

let path = uuid::Uuid::new_v4().to_string();
let (content, _) = gen_bytes();

op.write(&path, content.clone())
.await
.expect("write must succeed");

let target_content_disposition = "attachment; filename=foo.txt";

let mut op_read = OpRead::default();
op_read = op_read.with_override_content_disposition(target_content_disposition);

let signed_req = op
.presign_read_with(&path, op_read, Duration::from_secs(60))
.await
.expect("presign must succeed");

let client = reqwest::Client::new();
let mut req = client.request(
signed_req.method().clone(),
Url::from_str(&signed_req.uri().to_string()).expect("must be valid url"),
);
for (k, v) in signed_req.header() {
req = req.header(k, v);
}

let resp = req.send().await.expect("send must succeed");

assert_eq!(resp.status(), http::StatusCode::OK);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
target_content_disposition
);
assert_eq!(resp.bytes().await?, content);

op.delete(&path).await.expect("delete must succeed");

Ok(())
}

// Delete existing file should succeed.
pub async fn test_writer_abort(op: Operator) -> Result<()> {
let path = uuid::Uuid::new_v4().to_string();
Expand Down