diff --git a/shared/comm-lib/src/blob/client.rs b/shared/comm-lib/src/blob/client.rs --- a/shared/comm-lib/src/blob/client.rs +++ b/shared/comm-lib/src/blob/client.rs @@ -17,9 +17,12 @@ auth::{AuthorizationCredential, UserIdentity}, blob::types::http::{ AssignHolderRequest, AssignHolderResponse, RemoveHolderRequest, + RemoveHoldersRequest, }, }; +use super::types::{http::RemoveHoldersResponse, BlobInfo}; + #[derive(From, Error, Debug, Display)] pub enum BlobServiceError { /// HTTP Client errors, this includes: @@ -247,6 +250,52 @@ Err(error) } + /// Removes multiple holders. + /// - Holders don't have to own the same blob item. For each item + /// a (blob hash; holder) pair is specified. + /// - Operation is idempotent. Not existing holders are treated as + /// successfully removed. + /// - If one or more removal failed server-side, these will be returned + /// in the `failed_requests` response field. It has the same format + /// as this function input and can be directly used to retry removal. + /// + /// For single holder removal, see [`BlobServiceClient::revoke_holder`]. + pub async fn remove_multiple_holders( + &self, + blob_infos: Vec, + ) -> BlobResult { + let num_holders = blob_infos.len(); + debug!(num_holders, "Revoke multiple holders request."); + + let url = self.get_holders_url()?; + let payload = RemoveHoldersRequest { + requests: blob_infos, + instant_delete: false, + }; + trace!("Request payload: {:?}", payload); + let response = self + .request(Method::DELETE, url)? + .json(&payload) + .send() + .await?; + debug!("Response status: {}", response.status()); + + if response.status().is_success() { + let result: RemoveHoldersResponse = response.json().await?; + debug!( + "Request successful. {} holders failed to be removed.", + result.failed_requests.len() + ); + return Ok(result); + } + + let error = handle_http_error(response.status()); + if let Ok(message) = response.text().await { + debug!("Error response message: {}", message); + } + Err(error) + } + /// Uploads a blob. Returns `BlobServiceError::AlreadyExists` if blob with given hash /// already exists. /// @@ -370,6 +419,15 @@ Ok(url) } + fn get_holders_url(&self) -> Result { + let url = self + .blob_service_url + .join("/holders") + .map_err(|err| BlobServiceError::URLError(err.to_string()))?; + trace!("Constructed request URL: {}", url); + Ok(url) + } + fn request( &self, http_method: Method, diff --git a/shared/comm-lib/src/blob/types.rs b/shared/comm-lib/src/blob/types.rs --- a/shared/comm-lib/src/blob/types.rs +++ b/shared/comm-lib/src/blob/types.rs @@ -30,14 +30,14 @@ } // Remove multiple holders - #[derive(Deserialize, Debug)] + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct RemoveHoldersRequest { pub requests: Vec, #[serde(default)] pub instant_delete: bool, } - #[derive(Serialize, Debug)] + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct RemoveHoldersResponse { pub failed_requests: Vec,