azure_storage_blobs/clients/
blob_service_client.rs

1use crate::{
2    clients::{BlobClient, BlobLeaseClient, ContainerClient, ContainerLeaseClient},
3    service::operations::*,
4};
5use azure_core::{
6    headers::Headers, request_options::LeaseId, Body, ClientOptions, Context, Method, Pipeline,
7    Request, Response, Url,
8};
9use azure_storage::{
10    clients::{new_pipeline_from_options, shared_access_signature, ServiceType},
11    prelude::{AccountSasPermissions, AccountSasResource, AccountSasResourceType},
12    shared_access_signature::account_sas::AccountSharedAccessSignature,
13    CloudLocation, StorageCredentials,
14};
15use time::OffsetDateTime;
16
17/// A builder for the blob service client.
18#[derive(Debug, Clone)]
19pub struct ClientBuilder {
20    cloud_location: CloudLocation,
21    options: ClientOptions,
22    credentials: StorageCredentials,
23}
24
25impl ClientBuilder {
26    /// Create a new instance of `ClientBuilder`.
27    #[must_use]
28    pub fn new<A, C>(account: A, credentials: C) -> Self
29    where
30        A: Into<String>,
31        C: Into<StorageCredentials>,
32    {
33        Self::with_location(
34            CloudLocation::Public {
35                account: account.into(),
36            },
37            credentials,
38        )
39    }
40
41    /// Create a new instance of `ClientBuilder` with a cloud location.
42    #[must_use]
43    pub fn with_location<C>(cloud_location: CloudLocation, credentials: C) -> Self
44    where
45        C: Into<StorageCredentials>,
46    {
47        Self {
48            options: ClientOptions::default(),
49            cloud_location,
50            credentials: credentials.into(),
51        }
52    }
53
54    /// Use the emulator with default settings
55    #[must_use]
56    pub fn emulator() -> Self {
57        Self::with_location(
58            CloudLocation::Emulator {
59                address: "127.0.0.1".to_owned(),
60                port: 10000,
61            },
62            StorageCredentials::emulator(),
63        )
64    }
65
66    /// Convert the builder into a `BlobServiceClient` instance.
67    #[must_use]
68    pub fn blob_service_client(self) -> BlobServiceClient {
69        let Self {
70            cloud_location,
71            options,
72            credentials,
73        } = self;
74
75        BlobServiceClient {
76            pipeline: new_pipeline_from_options(options, credentials.clone()),
77            cloud_location,
78            credentials,
79        }
80    }
81
82    /// Convert the builder into a `ContainerClient` instance.
83    #[must_use]
84    pub fn container_client(self, container_name: impl Into<String>) -> ContainerClient {
85        self.blob_service_client().container_client(container_name)
86    }
87
88    /// Convert the builder into a `BlobClient` instance.
89    #[must_use]
90    pub fn blob_client(
91        self,
92        container_name: impl Into<String>,
93        blob_name: impl Into<String>,
94    ) -> BlobClient {
95        self.blob_service_client()
96            .container_client(container_name)
97            .blob_client(blob_name)
98    }
99
100    /// Convert the builder into a `ContainerLeaseClient` instance.
101    #[must_use]
102    pub fn container_lease_client(
103        self,
104        container_name: impl Into<String>,
105        lease_id: LeaseId,
106    ) -> ContainerLeaseClient {
107        self.blob_service_client()
108            .container_client(container_name)
109            .container_lease_client(lease_id)
110    }
111
112    /// Convert the builder into a `BlobLeaseClient` instance.
113    #[must_use]
114    pub fn blob_lease_client(
115        self,
116        container_name: impl Into<String>,
117        blob_name: impl Into<String>,
118        lease_id: LeaseId,
119    ) -> BlobLeaseClient {
120        self.blob_service_client()
121            .container_client(container_name)
122            .blob_client(blob_name)
123            .blob_lease_client(lease_id)
124    }
125
126    /// Set the cloud location.
127    #[must_use]
128    pub fn cloud_location(mut self, cloud_location: CloudLocation) -> Self {
129        self.cloud_location = cloud_location;
130        self
131    }
132
133    /// Set the retry options.
134    #[must_use]
135    pub fn retry(mut self, retry: impl Into<azure_core::RetryOptions>) -> Self {
136        self.options = self.options.retry(retry);
137        self
138    }
139
140    /// Set the transport options.
141    #[must_use]
142    pub fn transport(mut self, transport: impl Into<azure_core::TransportOptions>) -> Self {
143        self.options = self.options.transport(transport);
144        self
145    }
146
147    /// Override all of the client options.
148    ///
149    /// *Warning!*: This overrides all client options that have been previously set on this builder.
150    #[must_use]
151    pub fn client_options(mut self, options: impl Into<azure_core::ClientOptions>) -> Self {
152        self.options = options.into();
153        self
154    }
155}
156
157/// A client for interacting with the blob storage service.
158#[derive(Debug, Clone)]
159pub struct BlobServiceClient {
160    pipeline: Pipeline,
161    cloud_location: CloudLocation,
162    credentials: StorageCredentials,
163}
164
165impl BlobServiceClient {
166    /// Create a new `BlobServiceClient` which connects to the account's instance in the public Azure cloud.
167    #[must_use]
168    pub fn new(account: impl Into<String>, credentials: impl Into<StorageCredentials>) -> Self {
169        ClientBuilder::new(account, credentials).blob_service_client()
170    }
171
172    /// Create a new `ClientBuilder`.
173    #[must_use]
174    pub fn builder(
175        account: impl Into<String>,
176        credentials: impl Into<StorageCredentials>,
177    ) -> ClientBuilder {
178        ClientBuilder::new(account, credentials)
179    }
180
181    pub fn account(&self) -> &str {
182        self.cloud_location.account()
183    }
184
185    /// Get information about the blob storage account
186    pub fn get_account_information(&self) -> GetAccountInformationBuilder {
187        GetAccountInformationBuilder::new(self.clone())
188    }
189
190    /// Get all the blobs with the given tags in the where expression
191    pub fn find_blobs_by_tags(&self, expression: String) -> FindBlobsByTagsBuilder {
192        FindBlobsByTagsBuilder::new(self.clone(), expression)
193    }
194
195    /// List all the containers in the blob account
196    pub fn list_containers(&self) -> ListContainersBuilder {
197        ListContainersBuilder::new(self.clone())
198    }
199
200    pub fn get_properties(&self) -> GetBlobServicePropertiesBuilder {
201        GetBlobServicePropertiesBuilder::new(self.clone())
202    }
203
204    pub fn url(&self) -> azure_core::Result<Url> {
205        self.cloud_location.url(ServiceType::Blob)
206    }
207
208    pub fn container_client<S: Into<String>>(&self, container_name: S) -> ContainerClient {
209        ContainerClient::new(self.clone(), container_name.into())
210    }
211
212    pub fn get_user_deligation_key(
213        &self,
214        start: OffsetDateTime,
215        expiry: OffsetDateTime,
216    ) -> GetUserDelegationKeyBuilder {
217        GetUserDelegationKeyBuilder::new(self.clone(), start, expiry)
218    }
219
220    pub async fn shared_access_signature(
221        &self,
222        resource_type: AccountSasResourceType,
223        expiry: OffsetDateTime,
224        permissions: AccountSasPermissions,
225    ) -> azure_core::Result<AccountSharedAccessSignature> {
226        shared_access_signature(
227            self.credentials(),
228            AccountSasResource::Blob,
229            resource_type,
230            expiry,
231            permissions,
232        )
233        .await
234    }
235
236    pub async fn update_credentials(
237        &self,
238        new_credentials: StorageCredentials,
239    ) -> azure_core::Result<()> {
240        self.credentials.replace(new_credentials).await
241    }
242
243    pub(crate) fn credentials(&self) -> &StorageCredentials {
244        &self.credentials
245    }
246
247    pub(crate) fn finalize_request(
248        url: Url,
249        method: Method,
250        headers: Headers,
251        request_body: Option<Body>,
252    ) -> azure_core::Result<Request> {
253        azure_storage::clients::finalize_request(url, method, headers, request_body)
254    }
255
256    pub(crate) async fn send(
257        &self,
258        context: &mut Context,
259        request: &mut Request,
260    ) -> azure_core::Result<Response> {
261        self.pipeline
262            .send(context.insert(ServiceType::Blob), request)
263            .await
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use azure_storage::StorageCredentialsInner;
271    use std::ops::Deref;
272
273    #[tokio::test]
274    async fn update_credentials() -> azure_core::Result<()> {
275        let account = "test";
276
277        let keyed = StorageCredentials::access_key(account, "test");
278        let anonymous = StorageCredentials::anonymous();
279
280        let service_client = BlobServiceClient::new(account, keyed);
281        let container_client = service_client.container_client("test");
282        let blob_client = container_client.blob_client("test");
283
284        service_client.update_credentials(anonymous).await?;
285
286        // check that the update occurred
287        let credentials = blob_client.container_client().credentials();
288        let locked = credentials.0.read().await;
289        assert!(matches!(
290            locked.deref(),
291            &StorageCredentialsInner::Anonymous
292        ));
293
294        Ok(())
295    }
296}