Skip to content
Open
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
19 changes: 16 additions & 3 deletions src/aws/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ use crate::multipart::{MultipartStore, PartId};
use crate::signer::Signer;
use crate::util::STRICT_ENCODE_SET;
use crate::{
CopyMode, CopyOptions, Error, GetOptions, GetResult, ListResult, MultipartId, MultipartUpload,
ObjectMeta, ObjectStore, Path, PutMode, PutMultipartOptions, PutOptions, PutPayload, PutResult,
Result, UploadPart,
CopyMode, CopyOptions, DeleteOptions, Error, GetOptions, GetResult, ListResult, MultipartId,
MultipartUpload, ObjectMeta, ObjectStore, Path, PutMode, PutMultipartOptions, PutOptions,
PutPayload, PutResult, Result, UploadPart,
};

static TAGS_HEADER: HeaderName = HeaderName::from_static("x-amz-tagging");
Expand Down Expand Up @@ -259,6 +259,17 @@ impl ObjectStore for AmazonS3 {
self.client.get_opts(location, options).await
}

async fn delete_opts(&self, location: &Path, opts: DeleteOptions) -> Result<()> {
let mut request = self.client.request(Method::DELETE, location);

if let Some(if_match) = &opts.if_match {
request = request.header(&IF_MATCH, if_match);
}

request.with_extensions(opts.extensions).send().await?;
Ok(())
}

fn delete_stream(
&self,
locations: BoxStream<'static, Result<Path>>,
Expand Down Expand Up @@ -658,6 +669,8 @@ mod tests {
}
if test_conditional_put {
put_opts(&integration, true).await;
delete_opts(&integration, true).await;
delete_opts_race_condition(&integration, true).await;
}

// run integration test with unsigned payload enabled
Expand Down
50 changes: 45 additions & 5 deletions src/azure/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use crate::list::{PaginatedListOptions, PaginatedListResult};
use crate::multipart::PartId;
use crate::util::{GetRange, deserialize_rfc1123};
use crate::{
Attribute, Attributes, ClientOptions, GetOptions, ListResult, ObjectMeta, Path, PutMode,
PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, RetryConfig, TagSet,
Attribute, Attributes, ClientOptions, DeleteOptions, GetOptions, ListResult, ObjectMeta, Path,
PutMode, PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, RetryConfig, TagSet,
};
use async_trait::async_trait;
use base64::Engine;
Expand Down Expand Up @@ -74,6 +74,12 @@ pub(crate) enum Error {
path: String,
},

#[error("Error performing delete request {}: {}", path, source)]
DeleteRequest {
source: crate::client::retry::RetryError,
path: String,
},

#[error("Error performing bulk delete request: {}", source)]
BulkDeleteRequest {
source: crate::client::retry::RetryError,
Expand Down Expand Up @@ -144,9 +150,9 @@ pub(crate) enum Error {
impl From<Error> for crate::Error {
fn from(err: Error) -> Self {
match err {
Error::GetRequest { source, path } | Error::PutRequest { source, path } => {
source.error(STORE, path)
}
Error::GetRequest { source, path }
| Error::PutRequest { source, path }
| Error::DeleteRequest { source, path } => source.error(STORE, path),
_ => Self::Generic {
store: STORE,
source: Box::new(err),
Expand Down Expand Up @@ -568,6 +574,40 @@ impl AzureClient {
.map_err(|source| Error::Metadata { source })?)
}

/// Make an Azure DELETE request <https://learn.microsoft.com/en-us/rest/api/storageservices/delete-blob>
pub(crate) async fn delete_request(&self, path: &Path, opts: DeleteOptions) -> Result<()> {
let credential = self.get_credential().await?;
let url = self.config.path_url(path);
let sensitive = credential
.as_deref()
.map(|c| c.sensitive_request())
.unwrap_or_default();

let mut builder = self
.client
.delete(url.as_str())
.header(CONTENT_LENGTH, HeaderValue::from_static("0"))
.extensions(opts.extensions);

if let Some(etag) = &opts.if_match {
builder = builder.header(&IF_MATCH, etag);
}

builder
.with_azure_authorization(&credential, &self.config.account)
.retryable(&self.config.retry_config)
.sensitive(sensitive)
.idempotent(true)
.send()
.await
.map_err(|source| Error::DeleteRequest {
source,
path: path.to_string(),
})?;

Ok(())
}

/// PUT a block <https://learn.microsoft.com/en-us/rest/api/storageservices/put-block>
pub(crate) async fn put_block(
&self,
Expand Down
12 changes: 9 additions & 3 deletions src/azure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
//!
//! Unused blocks will automatically be dropped after 7 days.
use crate::{
CopyMode, CopyOptions, GetOptions, GetResult, ListResult, MultipartId, MultipartUpload,
ObjectMeta, ObjectStore, PutMultipartOptions, PutOptions, PutPayload, PutResult, Result,
UploadPart,
CopyMode, CopyOptions, DeleteOptions, GetOptions, GetResult, ListResult, MultipartId,
MultipartUpload, ObjectMeta, ObjectStore, PutMultipartOptions, PutOptions, PutPayload,
PutResult, Result, UploadPart,
multipart::{MultipartStore, PartId},
path::Path,
signer::Signer,
Expand Down Expand Up @@ -117,6 +117,10 @@ impl ObjectStore for MicrosoftAzure {
self.client.get_opts(location, options).await
}

async fn delete_opts(&self, location: &Path, opts: DeleteOptions) -> Result<()> {
self.client.delete_request(location, opts).await
}

fn list(&self, prefix: Option<&Path>) -> BoxStream<'static, Result<ObjectMeta>> {
self.client.list(prefix)
}
Expand Down Expand Up @@ -331,6 +335,8 @@ mod tests {
copy_if_not_exists(&integration).await;
stream_get(&integration).await;
put_opts(&integration, true).await;
delete_opts(&integration, true).await;
delete_opts_race_condition(&integration, true).await;
multipart(&integration, &integration).await;
multipart_race_condition(&integration, false).await;
multipart_out_of_order(&integration).await;
Expand Down
8 changes: 6 additions & 2 deletions src/chunked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use futures::stream::BoxStream;

use crate::path::Path;
use crate::{
CopyOptions, GetOptions, GetResult, GetResultPayload, ListResult, MultipartUpload, ObjectMeta,
ObjectStore, PutMultipartOptions, PutOptions, PutResult, RenameOptions,
CopyOptions, DeleteOptions, GetOptions, GetResult, GetResultPayload, ListResult, MultipartUpload,
ObjectMeta, ObjectStore, PutMultipartOptions, PutOptions, PutResult, RenameOptions,
};
use crate::{PutPayload, Result};

Expand Down Expand Up @@ -139,6 +139,10 @@ impl ObjectStore for ChunkedStore {
self.inner.get_ranges(location, ranges).await
}

async fn delete_opts(&self, location: &Path, opts: DeleteOptions) -> Result<()> {
self.inner.delete_opts(location, opts).await
}

fn delete_stream(
&self,
locations: BoxStream<'static, Result<Path>>,
Expand Down
17 changes: 13 additions & 4 deletions src/gcp/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use crate::multipart::PartId;
use crate::path::Path;
use crate::util::hex_encode;
use crate::{
Attribute, Attributes, ClientOptions, GetOptions, MultipartId, PutMode, PutMultipartOptions,
PutOptions, PutPayload, PutResult, Result, RetryConfig,
Attribute, Attributes, ClientOptions, DeleteOptions, GetOptions, MultipartId, PutMode,
PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, RetryConfig,
};
use async_trait::async_trait;
use base64::Engine;
Expand Down Expand Up @@ -560,8 +560,17 @@ impl GoogleCloudStorageClient {
}

/// Perform a delete request <https://cloud.google.com/storage/docs/xml-api/delete-object>
pub(crate) async fn delete_request(&self, path: &Path) -> Result<()> {
self.request(Method::DELETE, path).send().await?;
pub(crate) async fn delete_request(&self, path: &Path, opts: DeleteOptions) -> Result<()> {
let mut builder = self
.request(Method::DELETE, path)
.with_extensions(opts.extensions)
.idempotent(true);

if let Some(if_match) = &opts.if_match {
builder = builder.header(&HeaderName::from_static("if-match"), if_match);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this?

The docs would suggest If-Match is only supported on GET and HEAD

https://docs.cloud.google.com/storage/docs/xml-api/reference-headers#ifmatch

This is why PutMode has UpdateVersion

}

builder.send().await?;
Ok(())
}

Expand Down
16 changes: 12 additions & 4 deletions src/gcp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ use crate::gcp::credential::GCSAuthorizer;
use crate::signer::Signer;
use crate::{CopyMode, CopyOptions};
use crate::{
GetOptions, GetResult, ListResult, MultipartId, MultipartUpload, ObjectMeta, ObjectStore,
PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, UploadPart, multipart::PartId,
path::Path,
DeleteOptions, GetOptions, GetResult, ListResult, MultipartId, MultipartUpload, ObjectMeta,
ObjectStore, PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, UploadPart,
multipart::PartId, path::Path,
};
use async_trait::async_trait;
use client::GoogleCloudStorageClient;
Expand Down Expand Up @@ -181,6 +181,10 @@ impl ObjectStore for GoogleCloudStorage {
self.client.get_opts(location, options).await
}

async fn delete_opts(&self, location: &Path, opts: DeleteOptions) -> Result<()> {
self.client.delete_request(location, opts).await
}

fn delete_stream(
&self,
locations: BoxStream<'static, Result<Path>>,
Expand All @@ -191,7 +195,9 @@ impl ObjectStore for GoogleCloudStorage {
let client = Arc::clone(&client);
async move {
let location = location?;
client.delete_request(&location).await?;
client
.delete_request(&location, DeleteOptions::default())
.await?;
Ok(location)
}
})
Expand Down Expand Up @@ -334,6 +340,8 @@ mod test {
// Fake GCS server doesn't currently honor preconditions
get_opts(&integration).await;
put_opts(&integration, true).await;
delete_opts(&integration, true).await;
delete_opts_race_condition(&integration, true).await;
// Fake GCS server doesn't currently support attributes
put_get_attributes(&integration).await;
}
Expand Down
14 changes: 11 additions & 3 deletions src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ use crate::client::{HttpConnector, http_connector};
use crate::http::client::Client;
use crate::path::Path;
use crate::{
ClientConfigKey, ClientOptions, CopyMode, CopyOptions, GetOptions, GetResult, ListResult,
MultipartUpload, ObjectMeta, ObjectStore, PutMode, PutMultipartOptions, PutOptions, PutPayload,
PutResult, Result, RetryConfig,
ClientConfigKey, ClientOptions, CopyMode, CopyOptions, DeleteOptions, GetOptions, GetResult,
ListResult, MultipartUpload, ObjectMeta, ObjectStore, PutMode, PutMultipartOptions, PutOptions,
PutPayload, PutResult, Result, RetryConfig,
};

mod client;
Expand Down Expand Up @@ -138,6 +138,14 @@ impl ObjectStore for HttpStore {
self.client.get_opts(location, options).await
}

async fn delete_opts(&self, location: &Path, opts: DeleteOptions) -> Result<()> {
let _ = (location, opts);
Err(crate::Error::NotImplemented {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be strictly worse than the default impl?

operation: "`delete_opts`".into(),
implementer: self.to_string(),
})
}

fn delete_stream(
&self,
locations: BoxStream<'static, Result<Path>>,
Expand Down
Loading