Skip to content

Commit

Permalink
feat: Add if-match & if-none-match support for reader
Browse files Browse the repository at this point in the history
  • Loading branch information
XmchxUp committed Jan 1, 2025
1 parent b6fdd28 commit 25723d3
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 1 deletion.
10 changes: 10 additions & 0 deletions core/src/types/operator/operator_futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ impl<F: Future<Output = Result<Buffer>>> FutureRead<F> {
pub type FutureReader<F> = OperatorFuture<(OpRead, OpReader), Reader, F>;

impl<F: Future<Output = Result<Reader>>> FutureReader<F> {
/// Set the If-Match for this operation.
pub fn if_match(self, etag: &str) -> Self {
self.map(|(op_read, op_reader)| (op_read.with_if_match(etag), op_reader))
}

/// Set the If-None-Match for this operation.
pub fn if_none_match(self, etag: &str) -> Self {
self.map(|(op_read, op_reader)| (op_read.with_if_none_match(etag), op_reader))
}

/// Set the version for this operation.
pub fn version(self, v: &str) -> Self {
self.map(|(op_read, op_reader)| (op_read.with_version(v), op_reader))
Expand Down
133 changes: 132 additions & 1 deletion core/tests/behavior/async_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
test_read_full,
test_read_range,
test_reader,
test_reader_with_if_match,
test_reader_with_if_none_match,
test_read_not_exist,
test_read_with_if_match,
test_read_with_if_none_match,
Expand All @@ -59,7 +61,9 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
test_read_only_read_not_exist,
test_read_only_read_with_dir_path,
test_read_only_read_with_if_match,
test_read_only_read_with_if_none_match
test_read_only_read_with_if_none_match,
test_reader_only_read_with_if_match,
test_reader_only_read_with_if_none_match
))
}
}
Expand Down Expand Up @@ -174,6 +178,36 @@ pub async fn test_read_not_exist(op: Operator) -> anyhow::Result<()> {
Ok(())
}

/// Reader with if_match should match, else get a ConditionNotMatch error.
pub async fn test_reader_with_if_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_match {
return Ok(());
}

let (path, content, _) = TEST_FIXTURE.new_file(op.clone());

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

let meta = op.stat(&path).await?;

let reader = op.reader_with(&path).if_match("\"invalid_etag\"").await?;
let res = reader.read(..).await;
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::ConditionNotMatch);

let reader = op
.reader_with(&path)
.if_match(meta.etag().expect("etag must exist"))
.await?;

let bs = reader.read(..).await.expect("read must succeed").to_bytes();
assert_eq!(bs, content);

Ok(())
}

/// Read with if_match should match, else get a ConditionNotMatch error.
pub async fn test_read_with_if_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_match {
Expand Down Expand Up @@ -203,6 +237,39 @@ pub async fn test_read_with_if_match(op: Operator) -> anyhow::Result<()> {
Ok(())
}

/// Reader with if_none_match should match, else get a ConditionNotMatch error.
pub async fn test_reader_with_if_none_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_none_match {
return Ok(());
}

let (path, content, _) = TEST_FIXTURE.new_file(op.clone());

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

let meta = op.stat(&path).await?;

let reader = op
.reader_with(&path)
.if_none_match(meta.etag().expect("etag must exist"))
.await?;
let res = reader.read(..).await;

assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::ConditionNotMatch);

let reader = op
.reader_with(&path)
.if_none_match("\"invalid_etag\"")
.await?;
let bs = reader.read(..).await.expect("read must succeed").to_bytes();
assert_eq!(bs, content);

Ok(())
}

/// Read with if_none_match should match, else get a ConditionNotMatch error.
pub async fn test_read_with_if_none_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_none_match {
Expand Down Expand Up @@ -493,6 +560,38 @@ pub async fn test_read_only_read_with_dir_path(op: Operator) -> anyhow::Result<(
Ok(())
}

/// Reader with if_match should match, else get a ConditionNotMatch error.
pub async fn test_reader_only_read_with_if_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_match {
return Ok(());
}

let path = "normal_file.txt";

let meta = op.stat(path).await?;

let reader = op.reader_with(path).if_match("invalid_etag").await?;
let res = reader.read(..).await;
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::ConditionNotMatch);

let reader = op
.reader_with(path)
.if_match(meta.etag().expect("etag must exist"))
.await?;

let bs = reader.read(..).await.expect("read must succeed").to_bytes();

assert_eq!(bs.len(), 30482, "read size");
assert_eq!(
format!("{:x}", Sha256::digest(&bs)),
"943048ba817cdcd786db07d1f42d5500da7d10541c2f9353352cd2d3f66617e5",
"read content"
);

Ok(())
}

/// Read with if_match should match, else get a ConditionNotMatch error.
pub async fn test_read_only_read_with_if_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_match {
Expand Down Expand Up @@ -523,6 +622,38 @@ pub async fn test_read_only_read_with_if_match(op: Operator) -> anyhow::Result<(
Ok(())
}

/// Reader with if_none_match should match, else get a ConditionNotMatch error.
pub async fn test_reader_only_read_with_if_none_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_none_match {
return Ok(());
}

let path = "normal_file.txt";

let meta = op.stat(path).await?;

let reader = op
.reader_with(path)
.if_none_match(meta.etag().expect("etag must exist"))
.await?;

let res = reader.read(..).await;
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::ConditionNotMatch);

let reader = op.reader_with(path).if_none_match("invalid_etag").await?;
let bs = reader.read(..).await.expect("read must succeed").to_bytes();

assert_eq!(bs.len(), 30482, "read size");
assert_eq!(
format!("{:x}", Sha256::digest(&bs)),
"943048ba817cdcd786db07d1f42d5500da7d10541c2f9353352cd2d3f66617e5",
"read content"
);

Ok(())
}

/// Read with if_none_match should match, else get a ConditionNotMatch error.
pub async fn test_read_only_read_with_if_none_match(op: Operator) -> anyhow::Result<()> {
if !op.info().full_capability().read_with_if_none_match {
Expand Down

0 comments on commit 25723d3

Please sign in to comment.