Skip to content

[Feature] Add support for setting blob properties #869

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

Merged
merged 8 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
51 changes: 51 additions & 0 deletions sdk/storage_blobs/examples/set_blob_properties_00.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#[macro_use]
extern crate log;
use azure_storage::core::prelude::*;
use azure_storage_blobs::prelude::*;

#[tokio::main]
async fn main() -> azure_core::Result<()> {
// First we retrieve the account name and master key from environment variables.
let account =
std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!");
let master_key =
std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!");

let container = std::env::args()
.nth(1)
.expect("please specify container name as command line parameter");
let blob = std::env::args()
.nth(2)
.expect("please specify blob name as command line parameter");

let http_client = azure_core::new_http_client();
let storage_account_client =
StorageAccountClient::new_access_key(http_client.clone(), &account, &master_key);

// this is how you would use the SAS token:
// let storage_account_client = StorageAccountClient::new_sas_token(http_client.clone(), &account,
// "sv=2018-11-09&ss=b&srt=o&se=2021-01-15T12%3A09%3A01Z&sp=r&st=2021-01-15T11%3A09%3A01Z&spr=http,https&sig=some_signature")?;

let storage_client = storage_account_client.storage_client();
let blob_client = storage_client
.container_client(&container)
.blob_client(&blob);

trace!("Requesting blob properties");

let properties = blob_client
.get_properties()
.into_future()
.await?
.blob
.properties;

blob_client
.set_properties()
.set_from_blob_properties(properties)
.content_md5(md5::compute("howdy"))
.into_future()
.await?;

Ok(())
}
2 changes: 2 additions & 0 deletions sdk/storage_blobs/src/blob/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod release_lease;
mod renew_lease;
mod set_blob_tier;
mod set_metadata;
mod set_properties;
mod update_page;
pub use acquire_lease::*;
pub use append_block::*;
Expand All @@ -47,4 +48,5 @@ pub use release_lease::*;
pub use renew_lease::*;
pub use set_blob_tier::*;
pub use set_metadata::*;
pub use set_properties::*;
pub use update_page::*;
132 changes: 132 additions & 0 deletions sdk/storage_blobs/src/blob/operations/set_properties.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use crate::{blob::BlobProperties, prelude::*};
use azure_core::prelude::*;
use azure_core::{
headers::{
date_from_headers, etag_from_headers, request_id_from_headers, server_from_headers, Headers,
},
Method, RequestId,
};
use chrono::{DateTime, Utc};
use std::convert::{TryFrom, TryInto};

#[derive(Debug, Clone)]
pub struct SetPropertiesBuilder {
blob_client: BlobClient,
lease_id: Option<LeaseId>,
timeout: Option<Timeout>,
cache_control: Option<BlobCacheControl>,
content_type: Option<BlobContentType>,
content_encoding: Option<BlobContentEncoding>,
content_language: Option<BlobContentLanguage>,
content_disposition: Option<BlobContentDisposition>,
content_md5: Option<BlobContentMD5>,
context: Context,
}

impl SetPropertiesBuilder {
pub(crate) fn new(blob_client: BlobClient) -> Self {
Self {
blob_client,
lease_id: None,
timeout: None,
cache_control: None,
content_type: None,
content_encoding: None,
content_language: None,
content_disposition: None,
content_md5: None,
context: Context::new(),
}
}

pub fn set_from_blob_properties(self, blob_properties: BlobProperties) -> Self {
let mut s = self;

if let Some(cc) = blob_properties.cache_control {
s = s.cache_control(cc);
}
if !blob_properties.content_type.is_empty() {
s = s.content_type(blob_properties.content_type);
}
if let Some(ce) = blob_properties.content_encoding {
s = s.content_encoding(ce);
}
if let Some(cl) = blob_properties.content_language {
s = s.content_language(cl);
}
if let Some(cd) = blob_properties.content_disposition {
s = s.content_disposition(cd);
}
if let Some(cmd5) = blob_properties.content_md5 {
s = s.content_md5(cmd5);
}
s
}

setters! {
lease_id: LeaseId => Some(lease_id),
timeout: Timeout => Some(timeout),
cache_control: BlobCacheControl => Some(cache_control),
content_type: BlobContentType => Some(content_type),
content_encoding: BlobContentEncoding => Some(content_encoding),
content_language: BlobContentLanguage => Some(content_language),
content_disposition: BlobContentDisposition => Some(content_disposition),
content_md5: BlobContentMD5 => Some(content_md5),
context: Context => context,
}

pub fn into_future(mut self) -> Response {
Box::pin(async move {
let mut url = self.blob_client.url_with_segments(None)?;

url.query_pairs_mut().append_pair("comp", "properties");
self.timeout.append_to_url_query(&mut url);

let mut request = self.blob_client.prepare_request(url, Method::PUT, None)?;
request.add_optional_header(&self.lease_id);
request.add_optional_header(&self.cache_control);
request.add_optional_header(&self.content_type);
request.add_optional_header(&self.content_encoding);
request.add_optional_header(&self.content_language);
request.add_optional_header(&self.content_disposition);
request.add_optional_header(&self.content_md5);

let response = self
.blob_client
.send(&mut self.context, &mut request)
.await?;
response.headers().try_into()
})
}
}

#[derive(Debug, Clone)]
pub struct SetPropertiesResponse {
pub request_id: RequestId,
pub etag: String,
pub server: String,
pub date: DateTime<Utc>,
}

impl TryFrom<&Headers> for SetPropertiesResponse {
type Error = crate::Error;

fn try_from(headers: &Headers) -> Result<Self, Self::Error> {
Ok(SetPropertiesResponse {
request_id: request_id_from_headers(headers)?,
etag: etag_from_headers(headers)?,
server: server_from_headers(headers)?.to_owned(),
date: date_from_headers(headers)?,
})
}
}
pub type Response = futures::future::BoxFuture<'static, azure_core::Result<SetPropertiesResponse>>;

#[cfg(feature = "into_future")]
impl std::future::IntoFuture for SetPropertiesBuilder {
type IntoFuture = Response;
type Output = <Response as std::future::Future>::Output;
fn into_future(self) -> Self::IntoFuture {
Self::into_future(self)
}
}
42 changes: 42 additions & 0 deletions sdk/storage_blobs/src/blob_cache_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use azure_core::headers::{self, Header};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobCacheControl(std::borrow::Cow<'static, str>);

impl BlobCacheControl {
pub const fn from_static(s: &'static str) -> Self {
Self(std::borrow::Cow::Borrowed(s))
}

pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}

impl From<&'static str> for BlobCacheControl {
fn from(s: &'static str) -> Self {
Self::from_static(s)
}
}

impl From<String> for BlobCacheControl {
fn from(s: String) -> Self {
Self(std::borrow::Cow::Owned(s))
}
}

impl From<&String> for BlobCacheControl {
fn from(s: &String) -> Self {
Self(std::borrow::Cow::Owned(s.clone()))
}
}

impl Header for BlobCacheControl {
fn name(&self) -> headers::HeaderName {
azure_core::headers::BLOB_CACHE_CONTROL
}

fn value(&self) -> headers::HeaderValue {
self.0.to_string().into()
}
}
42 changes: 42 additions & 0 deletions sdk/storage_blobs/src/blob_content_disposition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use azure_core::headers::{self, Header};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobContentDisposition(std::borrow::Cow<'static, str>);

impl BlobContentDisposition {
pub const fn from_static(s: &'static str) -> Self {
Self(std::borrow::Cow::Borrowed(s))
}

pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}

impl From<&'static str> for BlobContentDisposition {
fn from(s: &'static str) -> Self {
Self::from_static(s)
}
}

impl From<String> for BlobContentDisposition {
fn from(s: String) -> Self {
Self(std::borrow::Cow::Owned(s))
}
}

impl From<&String> for BlobContentDisposition {
fn from(s: &String) -> Self {
Self(std::borrow::Cow::Owned(s.clone()))
}
}

impl Header for BlobContentDisposition {
fn name(&self) -> headers::HeaderName {
"x-ms-blob-content-disposition".into()
}

fn value(&self) -> headers::HeaderValue {
self.0.to_string().into()
}
}
42 changes: 42 additions & 0 deletions sdk/storage_blobs/src/blob_content_encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use azure_core::headers::{self, Header};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobContentEncoding(std::borrow::Cow<'static, str>);

impl BlobContentEncoding {
pub const fn from_static(s: &'static str) -> Self {
Self(std::borrow::Cow::Borrowed(s))
}

pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}

impl From<&'static str> for BlobContentEncoding {
fn from(s: &'static str) -> Self {
Self::from_static(s)
}
}

impl From<String> for BlobContentEncoding {
fn from(s: String) -> Self {
Self(std::borrow::Cow::Owned(s))
}
}

impl From<&String> for BlobContentEncoding {
fn from(s: &String) -> Self {
Self(std::borrow::Cow::Owned(s.clone()))
}
}

impl Header for BlobContentEncoding {
fn name(&self) -> headers::HeaderName {
"x-ms-blob-content-encoding".into()
}

fn value(&self) -> headers::HeaderValue {
self.0.to_string().into()
}
}
42 changes: 42 additions & 0 deletions sdk/storage_blobs/src/blob_content_language.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use azure_core::headers::{self, Header};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlobContentLanguage(std::borrow::Cow<'static, str>);

impl BlobContentLanguage {
pub const fn from_static(s: &'static str) -> Self {
Self(std::borrow::Cow::Borrowed(s))
}

pub fn as_str(&self) -> &str {
self.0.as_ref()
}
}

impl From<&'static str> for BlobContentLanguage {
fn from(s: &'static str) -> Self {
Self::from_static(s)
}
}

impl From<String> for BlobContentLanguage {
fn from(s: String) -> Self {
Self(std::borrow::Cow::Owned(s))
}
}

impl From<&String> for BlobContentLanguage {
fn from(s: &String) -> Self {
Self(std::borrow::Cow::Owned(s.clone()))
}
}

impl Header for BlobContentLanguage {
fn name(&self) -> headers::HeaderName {
"x-ms-blob-content-language".into()
}

fn value(&self) -> headers::HeaderValue {
self.0.to_string().into()
}
}
7 changes: 7 additions & 0 deletions sdk/storage_blobs/src/blob_content_md5.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use azure_core::headers::{self, Header};
use azure_storage::ConsistencyMD5;

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct BlobContentMD5([u8; 16]);
Expand All @@ -9,6 +10,12 @@ impl From<md5::Digest> for BlobContentMD5 {
}
}

impl From<ConsistencyMD5> for BlobContentMD5 {
fn from(md5: ConsistencyMD5) -> Self {
BlobContentMD5(*md5.as_slice())
}
}

impl Header for BlobContentMD5 {
fn name(&self) -> headers::HeaderName {
"x-ms-blob-content-md5".into()
Expand Down
Loading