Skip to content

Commit

Permalink
Support moving an object (#284)
Browse files Browse the repository at this point in the history
* support  operation on object

* rust fmt

* add comments

* increment version

* fix typo

* optimize code and return early

---------

Co-authored-by: Pratim Chaudhuri <pratim.sc@yantra.cloud>
  • Loading branch information
pratimsc and Pratim Chaudhuri authored Jul 22, 2024
1 parent 6d65b6e commit 39a0832
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ Cargo.lock
# Added by cargo

/target

# IDE
.idea
2 changes: 1 addition & 1 deletion storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "MIT"
name = "google-cloud-storage"
readme = "README.md"
repository = "https://github.com/yoshidan/google-cloud-rust/tree/main/storage"
version = "0.20.0"
version = "0.21.0"

[dependencies]
anyhow = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions storage/src/http/objects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub mod delete;
pub mod download;
pub mod get;
pub mod list;

pub mod r#move;
pub mod patch;
pub mod rewrite;
pub mod upload;
Expand Down
89 changes: 89 additions & 0 deletions storage/src/http/objects/move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use reqwest_middleware::{ClientWithMiddleware as Client, RequestBuilder};

use crate::http::objects::copy::CopyObjectRequest;
use crate::http::objects::delete::DeleteObjectRequest;
use crate::http::objects::{Encryption, Object};
use crate::http::{object_access_controls::Projection, Escape};

/// Request message for moving an object.
#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct MoveObjectRequest {
/// Name of the new object. Required when the object metadata is not otherwise provided. Overrides the object metadata's name value, if any.
pub destination_bucket: String,
/// Name of the new object. Required when the object metadata is not otherwise provided. Overrides the object metadata's name value, if any.
pub destination_object: String,
// Name of the source object. For information about how to URL encode object names to be path safe, see Encoding URI path parts.
pub source_object: String,
/// Name of the bucket in which to find the source object.
#[serde(skip_serializing)]
pub source_bucket: String,

/// Makes the operation conditional on there being a live destination object with a generation number that matches the given value. Setting ifGenerationMatch to 0 makes the operation succeed only if there is no live destination object.
pub if_generation_match: Option<i64>,
/// Makes the operation conditional on there being a live destination object with a generation number that does not match the given value. If no live destination object exists, the precondition fails. Setting ifGenerationNotMatch to 0 makes the operation succeed if there is a live version of the object.
pub if_generation_not_match: Option<i64>,
/// Makes the operation conditional on there being a live destination object with a metageneration number that matches the given value.
pub if_metageneration_match: Option<i64>,
/// Makes the operation conditional on there being a live destination object with a metageneration number that does not match the given value.
pub if_metageneration_not_match: Option<i64>,
/// Makes the operation conditional on whether the source object's generation matches the given value.
pub if_source_generation_match: Option<i64>,
/// Makes the operation conditional on whether the source object's generation does not match the given value.
pub if_source_generation_not_match: Option<i64>,
/// Makes the operation conditional on whether the source object's current metageneration matches the given value.
pub if_source_metageneration_match: Option<i64>,
/// Makes the operation conditional on whether the source object's current metageneration does not match the given value.
pub if_source_metageneration_not_match: Option<i64>,
/// Set of properties to return. Defaults to noAcl, unless the object resource specifies the acl property, when it defaults to full.
///
/// Acceptable values are:
/// full: Include all properties.
/// noAcl: Omit the owner, acl property.
pub projection: Option<Projection>,
/// If present, selects a specific revision of the source object (as opposed to the latest version, the default)
pub source_generation: Option<i64>,
/// The Object metadata for updating.
#[serde(skip_serializing)]
pub metadata: Option<Object>,

#[serde(skip_serializing)]
pub encryption: Option<Encryption>,
}

impl From<MoveObjectRequest> for CopyObjectRequest {
fn from(value: MoveObjectRequest) -> Self {
CopyObjectRequest {
destination_bucket: value.destination_bucket,
destination_object: value.destination_object,
source_object: value.source_object,
source_bucket: value.source_bucket,
if_generation_match: value.if_generation_match,
if_generation_not_match: value.if_generation_not_match,
if_metageneration_match: value.if_metageneration_match,
if_metageneration_not_match: value.if_metageneration_not_match,
if_source_generation_match: value.if_source_generation_match,
if_source_generation_not_match: value.if_source_generation_not_match,
if_source_metageneration_match: value.if_source_metageneration_match,
if_source_metageneration_not_match: value.if_source_metageneration_not_match,
projection: value.projection,
source_generation: value.source_generation,
metadata: value.metadata,
encryption: value.encryption,
}
}
}

impl From<MoveObjectRequest> for DeleteObjectRequest {
fn from(value: MoveObjectRequest) -> Self {
DeleteObjectRequest {
bucket: value.source_bucket,
object: value.source_object,
generation: value.source_generation,
if_generation_match: value.if_source_generation_match,
if_generation_not_match: value.if_source_generation_not_match,
if_metageneration_match: value.if_metageneration_match,
if_metageneration_not_match: value.if_metageneration_not_match,
}
}
}
38 changes: 34 additions & 4 deletions storage/src/http/storage_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use crate::http::objects::download::Range;
use crate::http::objects::get::GetObjectRequest;
use crate::http::objects::list::{ListObjectsRequest, ListObjectsResponse};
use crate::http::objects::patch::PatchObjectRequest;
use crate::http::objects::r#move::MoveObjectRequest;
use crate::http::objects::rewrite::{RewriteObjectRequest, RewriteObjectResponse};
use crate::http::objects::upload::{UploadObjectRequest, UploadType};
use crate::http::objects::Object;
Expand Down Expand Up @@ -930,6 +931,35 @@ impl StorageClient {
self.send(builder).await
}

/// Move the object.
/// https://cloud.google.com/storage/docs/copying-renaming-moving-objects#rest-move-object
/// This function will first make a copy of the object, and then delete the original object.
///
/// ```
/// use google_cloud_storage::client::Client;
/// use google_cloud_storage::http::objects::r#move::MoveObjectRequest;
///
/// async fn run(client:Client) {
/// let result = client.move_object(&MoveObjectRequest{
/// source_bucket: "source_bucket".to_string(),
/// source_object: "source_object".to_string(),
/// destination_bucket: "destination_source".to_string(),
/// destination_object: "destination_object".to_string(),
/// ..Default::default()
/// }).await;
/// }
/// ```
///
#[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
pub async fn move_object(&self, req: &MoveObjectRequest) -> Result<Object, Error> {
let copy_req: CopyObjectRequest = req.clone().into();
let delete_req: DeleteObjectRequest = req.clone().into();
// Only result of the copy operations is of interest, as it contains details of destination.
let copy_result = self.copy_object(&copy_req).await?;
let _ = self.delete_object(&delete_req).await?;
Ok(copy_result)
}

/// Download the object.
/// https://cloud.google.com/storage/docs/json_api/v1/objects/get
/// alt is always media
Expand Down Expand Up @@ -2195,7 +2225,7 @@ pub(crate) mod test {
status1,
UploadStatus::ResumeIncomplete(UploadedRange {
first_byte: 0,
last_byte: chunk1_data.len() as u64 - 1
last_byte: chunk1_data.len() as u64 - 1,
})
);

Expand All @@ -2205,7 +2235,7 @@ pub(crate) mod test {
status_check,
UploadStatus::ResumeIncomplete(UploadedRange {
first_byte: 0,
last_byte: chunk1_data.len() as u64 - 1
last_byte: chunk1_data.len() as u64 - 1,
})
);

Expand Down Expand Up @@ -2311,7 +2341,7 @@ pub(crate) mod test {
status1,
UploadStatus::ResumeIncomplete(UploadedRange {
first_byte: 0,
last_byte: chunk1_data.len() as u64 - 1
last_byte: chunk1_data.len() as u64 - 1,
})
);

Expand All @@ -2325,7 +2355,7 @@ pub(crate) mod test {
status1,
UploadStatus::ResumeIncomplete(UploadedRange {
first_byte: 0,
last_byte: chunk1_data.len() as u64 - 1
last_byte: chunk1_data.len() as u64 - 1,
})
);

Expand Down

0 comments on commit 39a0832

Please sign in to comment.