Skip to content

Commit 9167ab5

Browse files
authored
[Feature] Add support for setting blob properties (#869)
1 parent 42f8996 commit 9167ab5

File tree

12 files changed

+423
-2
lines changed

12 files changed

+423
-2
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#[macro_use]
2+
extern crate log;
3+
use azure_storage::core::prelude::*;
4+
use azure_storage_blobs::prelude::*;
5+
6+
#[tokio::main]
7+
async fn main() -> azure_core::Result<()> {
8+
// First we retrieve the account name and master key from environment variables.
9+
let account =
10+
std::env::var("STORAGE_ACCOUNT").expect("Set env variable STORAGE_ACCOUNT first!");
11+
let master_key =
12+
std::env::var("STORAGE_MASTER_KEY").expect("Set env variable STORAGE_MASTER_KEY first!");
13+
14+
let container = std::env::args()
15+
.nth(1)
16+
.expect("please specify container name as command line parameter");
17+
let blob = std::env::args()
18+
.nth(2)
19+
.expect("please specify blob name as command line parameter");
20+
21+
let http_client = azure_core::new_http_client();
22+
let storage_account_client =
23+
StorageAccountClient::new_access_key(http_client.clone(), &account, &master_key);
24+
25+
// this is how you would use the SAS token:
26+
// let storage_account_client = StorageAccountClient::new_sas_token(http_client.clone(), &account,
27+
// "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")?;
28+
29+
let storage_client = storage_account_client.storage_client();
30+
let blob_client = storage_client
31+
.container_client(&container)
32+
.blob_client(&blob);
33+
34+
trace!("Requesting blob properties");
35+
36+
let properties = blob_client
37+
.get_properties()
38+
.into_future()
39+
.await?
40+
.blob
41+
.properties;
42+
43+
blob_client
44+
.set_properties()
45+
.set_from_blob_properties(properties)
46+
.content_md5(md5::compute("howdy"))
47+
.into_future()
48+
.await?;
49+
50+
Ok(())
51+
}

sdk/storage_blobs/src/blob/operations/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod release_lease;
2222
mod renew_lease;
2323
mod set_blob_tier;
2424
mod set_metadata;
25+
mod set_properties;
2526
mod update_page;
2627
pub use acquire_lease::*;
2728
pub use append_block::*;
@@ -47,4 +48,5 @@ pub use release_lease::*;
4748
pub use renew_lease::*;
4849
pub use set_blob_tier::*;
4950
pub use set_metadata::*;
51+
pub use set_properties::*;
5052
pub use update_page::*;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use crate::{blob::BlobProperties, prelude::*};
2+
use azure_core::prelude::*;
3+
use azure_core::{
4+
headers::{
5+
date_from_headers, etag_from_headers, request_id_from_headers, server_from_headers, Headers,
6+
},
7+
Method, RequestId,
8+
};
9+
use chrono::{DateTime, Utc};
10+
use std::convert::{TryFrom, TryInto};
11+
12+
#[derive(Debug, Clone)]
13+
pub struct SetPropertiesBuilder {
14+
blob_client: BlobClient,
15+
lease_id: Option<LeaseId>,
16+
timeout: Option<Timeout>,
17+
cache_control: Option<BlobCacheControl>,
18+
content_type: Option<BlobContentType>,
19+
content_encoding: Option<BlobContentEncoding>,
20+
content_language: Option<BlobContentLanguage>,
21+
content_disposition: Option<BlobContentDisposition>,
22+
content_md5: Option<BlobContentMD5>,
23+
context: Context,
24+
}
25+
26+
impl SetPropertiesBuilder {
27+
pub(crate) fn new(blob_client: BlobClient) -> Self {
28+
Self {
29+
blob_client,
30+
lease_id: None,
31+
timeout: None,
32+
cache_control: None,
33+
content_type: None,
34+
content_encoding: None,
35+
content_language: None,
36+
content_disposition: None,
37+
content_md5: None,
38+
context: Context::new(),
39+
}
40+
}
41+
42+
pub fn set_from_blob_properties(self, blob_properties: BlobProperties) -> Self {
43+
let mut s = self;
44+
45+
if let Some(cc) = blob_properties.cache_control {
46+
s = s.cache_control(cc);
47+
}
48+
if !blob_properties.content_type.is_empty() {
49+
s = s.content_type(blob_properties.content_type);
50+
}
51+
if let Some(ce) = blob_properties.content_encoding {
52+
s = s.content_encoding(ce);
53+
}
54+
if let Some(cl) = blob_properties.content_language {
55+
s = s.content_language(cl);
56+
}
57+
if let Some(cd) = blob_properties.content_disposition {
58+
s = s.content_disposition(cd);
59+
}
60+
if let Some(cmd5) = blob_properties.content_md5 {
61+
s = s.content_md5(cmd5);
62+
}
63+
s
64+
}
65+
66+
setters! {
67+
lease_id: LeaseId => Some(lease_id),
68+
timeout: Timeout => Some(timeout),
69+
cache_control: BlobCacheControl => Some(cache_control),
70+
content_type: BlobContentType => Some(content_type),
71+
content_encoding: BlobContentEncoding => Some(content_encoding),
72+
content_language: BlobContentLanguage => Some(content_language),
73+
content_disposition: BlobContentDisposition => Some(content_disposition),
74+
content_md5: BlobContentMD5 => Some(content_md5),
75+
context: Context => context,
76+
}
77+
78+
pub fn into_future(mut self) -> Response {
79+
Box::pin(async move {
80+
let mut url = self.blob_client.url_with_segments(None)?;
81+
82+
url.query_pairs_mut().append_pair("comp", "properties");
83+
self.timeout.append_to_url_query(&mut url);
84+
85+
let mut request = self.blob_client.prepare_request(url, Method::Put, None)?;
86+
request.add_optional_header(&self.lease_id);
87+
request.add_optional_header(&self.cache_control);
88+
request.add_optional_header(&self.content_type);
89+
request.add_optional_header(&self.content_encoding);
90+
request.add_optional_header(&self.content_language);
91+
request.add_optional_header(&self.content_disposition);
92+
request.add_optional_header(&self.content_md5);
93+
94+
let response = self
95+
.blob_client
96+
.send(&mut self.context, &mut request)
97+
.await?;
98+
response.headers().try_into()
99+
})
100+
}
101+
}
102+
103+
#[derive(Debug, Clone)]
104+
pub struct SetPropertiesResponse {
105+
pub request_id: RequestId,
106+
pub etag: String,
107+
pub server: String,
108+
pub date: DateTime<Utc>,
109+
}
110+
111+
impl TryFrom<&Headers> for SetPropertiesResponse {
112+
type Error = crate::Error;
113+
114+
fn try_from(headers: &Headers) -> Result<Self, Self::Error> {
115+
Ok(SetPropertiesResponse {
116+
request_id: request_id_from_headers(headers)?,
117+
etag: etag_from_headers(headers)?,
118+
server: server_from_headers(headers)?.to_owned(),
119+
date: date_from_headers(headers)?,
120+
})
121+
}
122+
}
123+
pub type Response = futures::future::BoxFuture<'static, azure_core::Result<SetPropertiesResponse>>;
124+
125+
#[cfg(feature = "into_future")]
126+
impl std::future::IntoFuture for SetPropertiesBuilder {
127+
type IntoFuture = Response;
128+
type Output = <Response as std::future::Future>::Output;
129+
fn into_future(self) -> Self::IntoFuture {
130+
Self::into_future(self)
131+
}
132+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use azure_core::headers::{self, Header};
2+
3+
#[derive(Clone, Debug, PartialEq, Eq)]
4+
pub struct BlobCacheControl(std::borrow::Cow<'static, str>);
5+
6+
impl BlobCacheControl {
7+
pub const fn from_static(s: &'static str) -> Self {
8+
Self(std::borrow::Cow::Borrowed(s))
9+
}
10+
11+
pub fn as_str(&self) -> &str {
12+
self.0.as_ref()
13+
}
14+
}
15+
16+
impl From<&'static str> for BlobCacheControl {
17+
fn from(s: &'static str) -> Self {
18+
Self::from_static(s)
19+
}
20+
}
21+
22+
impl From<String> for BlobCacheControl {
23+
fn from(s: String) -> Self {
24+
Self(std::borrow::Cow::Owned(s))
25+
}
26+
}
27+
28+
impl From<&String> for BlobCacheControl {
29+
fn from(s: &String) -> Self {
30+
Self(std::borrow::Cow::Owned(s.clone()))
31+
}
32+
}
33+
34+
impl Header for BlobCacheControl {
35+
fn name(&self) -> headers::HeaderName {
36+
azure_core::headers::BLOB_CACHE_CONTROL
37+
}
38+
39+
fn value(&self) -> headers::HeaderValue {
40+
self.0.to_string().into()
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use azure_core::headers::{self, Header};
2+
3+
#[derive(Clone, Debug, PartialEq, Eq)]
4+
pub struct BlobContentDisposition(std::borrow::Cow<'static, str>);
5+
6+
impl BlobContentDisposition {
7+
pub const fn from_static(s: &'static str) -> Self {
8+
Self(std::borrow::Cow::Borrowed(s))
9+
}
10+
11+
pub fn as_str(&self) -> &str {
12+
self.0.as_ref()
13+
}
14+
}
15+
16+
impl From<&'static str> for BlobContentDisposition {
17+
fn from(s: &'static str) -> Self {
18+
Self::from_static(s)
19+
}
20+
}
21+
22+
impl From<String> for BlobContentDisposition {
23+
fn from(s: String) -> Self {
24+
Self(std::borrow::Cow::Owned(s))
25+
}
26+
}
27+
28+
impl From<&String> for BlobContentDisposition {
29+
fn from(s: &String) -> Self {
30+
Self(std::borrow::Cow::Owned(s.clone()))
31+
}
32+
}
33+
34+
impl Header for BlobContentDisposition {
35+
fn name(&self) -> headers::HeaderName {
36+
"x-ms-blob-content-disposition".into()
37+
}
38+
39+
fn value(&self) -> headers::HeaderValue {
40+
self.0.to_string().into()
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use azure_core::headers::{self, Header};
2+
3+
#[derive(Clone, Debug, PartialEq, Eq)]
4+
pub struct BlobContentEncoding(std::borrow::Cow<'static, str>);
5+
6+
impl BlobContentEncoding {
7+
pub const fn from_static(s: &'static str) -> Self {
8+
Self(std::borrow::Cow::Borrowed(s))
9+
}
10+
11+
pub fn as_str(&self) -> &str {
12+
self.0.as_ref()
13+
}
14+
}
15+
16+
impl From<&'static str> for BlobContentEncoding {
17+
fn from(s: &'static str) -> Self {
18+
Self::from_static(s)
19+
}
20+
}
21+
22+
impl From<String> for BlobContentEncoding {
23+
fn from(s: String) -> Self {
24+
Self(std::borrow::Cow::Owned(s))
25+
}
26+
}
27+
28+
impl From<&String> for BlobContentEncoding {
29+
fn from(s: &String) -> Self {
30+
Self(std::borrow::Cow::Owned(s.clone()))
31+
}
32+
}
33+
34+
impl Header for BlobContentEncoding {
35+
fn name(&self) -> headers::HeaderName {
36+
"x-ms-blob-content-encoding".into()
37+
}
38+
39+
fn value(&self) -> headers::HeaderValue {
40+
self.0.to_string().into()
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use azure_core::headers::{self, Header};
2+
3+
#[derive(Clone, Debug, PartialEq, Eq)]
4+
pub struct BlobContentLanguage(std::borrow::Cow<'static, str>);
5+
6+
impl BlobContentLanguage {
7+
pub const fn from_static(s: &'static str) -> Self {
8+
Self(std::borrow::Cow::Borrowed(s))
9+
}
10+
11+
pub fn as_str(&self) -> &str {
12+
self.0.as_ref()
13+
}
14+
}
15+
16+
impl From<&'static str> for BlobContentLanguage {
17+
fn from(s: &'static str) -> Self {
18+
Self::from_static(s)
19+
}
20+
}
21+
22+
impl From<String> for BlobContentLanguage {
23+
fn from(s: String) -> Self {
24+
Self(std::borrow::Cow::Owned(s))
25+
}
26+
}
27+
28+
impl From<&String> for BlobContentLanguage {
29+
fn from(s: &String) -> Self {
30+
Self(std::borrow::Cow::Owned(s.clone()))
31+
}
32+
}
33+
34+
impl Header for BlobContentLanguage {
35+
fn name(&self) -> headers::HeaderName {
36+
"x-ms-blob-content-language".into()
37+
}
38+
39+
fn value(&self) -> headers::HeaderValue {
40+
self.0.to_string().into()
41+
}
42+
}

sdk/storage_blobs/src/blob_content_md5.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use azure_core::headers::{self, Header};
2+
use azure_storage::ConsistencyMD5;
23

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

13+
impl From<ConsistencyMD5> for BlobContentMD5 {
14+
fn from(md5: ConsistencyMD5) -> Self {
15+
BlobContentMD5(*md5.as_slice())
16+
}
17+
}
18+
1219
impl Header for BlobContentMD5 {
1320
fn name(&self) -> headers::HeaderName {
1421
"x-ms-blob-content-md5".into()

0 commit comments

Comments
 (0)