Skip to content

Commit 6a7faef

Browse files
authored
[Cosmos] Refactoring how the resource type and link are passed down to the auth policy (Azure#1861)
1 parent 3fa79e3 commit 6a7faef

File tree

9 files changed

+521
-385
lines changed

9 files changed

+521
-385
lines changed

sdk/cosmos/azure_data_cosmos/src/clients/container_client.rs

Lines changed: 39 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,37 @@ use crate::{
55
constants,
66
models::{ContainerProperties, Item, QueryResults},
77
options::{QueryOptions, ReadContainerOptions},
8-
pipeline::{CosmosPipeline, ResourceType},
9-
utils::AppendPathSegments,
8+
pipeline::CosmosPipeline,
9+
resource_context::{ResourceLink, ResourceType},
1010
DeleteContainerOptions, ItemOptions, PartitionKey, Query, QueryPartitionStrategy,
1111
};
1212

1313
use azure_core::{Context, Method, Pager, Request, Response};
1414
use serde::{de::DeserializeOwned, Serialize};
15-
use typespec_client_core::http::PagerResult;
16-
use url::Url;
1715

1816
/// A client for working with a specific container in a Cosmos DB account.
1917
///
2018
/// You can get a `Container` by calling [`DatabaseClient::container_client()`](crate::clients::DatabaseClient::container_client()).
2119
pub struct ContainerClient {
22-
container_url: Url,
20+
link: ResourceLink,
21+
items_link: ResourceLink,
2322
pipeline: CosmosPipeline,
2423
}
2524

2625
impl ContainerClient {
27-
pub(crate) fn new(pipeline: CosmosPipeline, database_url: &Url, container_name: &str) -> Self {
28-
let container_url = database_url.with_path_segments(["colls", container_name]);
26+
pub(crate) fn new(
27+
pipeline: CosmosPipeline,
28+
database_link: &ResourceLink,
29+
container_id: &str,
30+
) -> Self {
31+
let link = database_link
32+
.feed(ResourceType::Containers)
33+
.item(container_id);
34+
let items_link = link.feed(ResourceType::Items);
2935

3036
Self {
31-
container_url,
37+
link,
38+
items_link,
3239
pipeline,
3340
}
3441
}
@@ -58,9 +65,10 @@ impl ContainerClient {
5865
// REASON: This is a documented public API so prefixing with '_' is undesirable.
5966
options: Option<ReadContainerOptions>,
6067
) -> azure_core::Result<Response<ContainerProperties>> {
61-
let mut req = Request::new(self.container_url.clone(), Method::Get);
68+
let url = self.pipeline.url(&self.link);
69+
let mut req = Request::new(url, Method::Get);
6270
self.pipeline
63-
.send(Context::new(), &mut req, ResourceType::Containers)
71+
.send(Context::new(), &mut req, self.link.clone())
6472
.await
6573
}
6674

@@ -76,9 +84,10 @@ impl ContainerClient {
7684
// REASON: This is a documented public API so prefixing with '_' is undesirable.
7785
options: Option<DeleteContainerOptions>,
7886
) -> azure_core::Result<Response> {
79-
let mut req = Request::new(self.container_url.clone(), Method::Delete);
87+
let url = self.pipeline.url(&self.link);
88+
let mut req = Request::new(url, Method::Delete);
8089
self.pipeline
81-
.send(Context::new(), &mut req, ResourceType::Containers)
90+
.send(Context::new(), &mut req, self.link.clone())
8291
.await
8392
}
8493

@@ -126,12 +135,12 @@ impl ContainerClient {
126135
// REASON: This is a documented public API so prefixing with '_' is undesirable.
127136
options: Option<ItemOptions>,
128137
) -> azure_core::Result<Response<Item<T>>> {
129-
let url = self.container_url.with_path_segments(["docs"]);
138+
let url = self.pipeline.url(&self.items_link);
130139
let mut req = Request::new(url, Method::Post);
131140
req.insert_headers(&partition_key.into())?;
132141
req.set_json(&item)?;
133142
self.pipeline
134-
.send(Context::new(), &mut req, ResourceType::Items)
143+
.send(Context::new(), &mut req, self.items_link.clone())
135144
.await
136145
}
137146

@@ -181,15 +190,12 @@ impl ContainerClient {
181190
// REASON: This is a documented public API so prefixing with '_' is undesirable.
182191
options: Option<ItemOptions>,
183192
) -> azure_core::Result<Response<Item<T>>> {
184-
let url = self
185-
.container_url
186-
.with_path_segments(["docs", item_id.as_ref()]);
193+
let link = self.items_link.item(item_id);
194+
let url = self.pipeline.url(&link);
187195
let mut req = Request::new(url, Method::Put);
188196
req.insert_headers(&partition_key.into())?;
189197
req.set_json(&item)?;
190-
self.pipeline
191-
.send(Context::new(), &mut req, ResourceType::Items)
192-
.await
198+
self.pipeline.send(Context::new(), &mut req, link).await
193199
}
194200

195201
/// Creates or replaces an item in the container.
@@ -239,13 +245,13 @@ impl ContainerClient {
239245
// REASON: This is a documented public API so prefixing with '_' is undesirable.
240246
options: Option<ItemOptions>,
241247
) -> azure_core::Result<Response<Item<T>>> {
242-
let url = self.container_url.with_path_segments(["docs"]);
248+
let url = self.pipeline.url(&self.items_link);
243249
let mut req = Request::new(url, Method::Post);
244250
req.insert_header(constants::IS_UPSERT, "true");
245251
req.insert_headers(&partition_key.into())?;
246252
req.set_json(&item)?;
247253
self.pipeline
248-
.send(Context::new(), &mut req, ResourceType::Items)
254+
.send(Context::new(), &mut req, self.items_link.clone())
249255
.await
250256
}
251257

@@ -288,14 +294,11 @@ impl ContainerClient {
288294
// REASON: This is a documented public API so prefixing with '_' is undesirable.
289295
options: Option<ItemOptions>,
290296
) -> azure_core::Result<Response<Item<T>>> {
291-
let url = self
292-
.container_url
293-
.with_path_segments(["docs", item_id.as_ref()]);
297+
let link = self.items_link.item(item_id);
298+
let url = self.pipeline.url(&link);
294299
let mut req = Request::new(url, Method::Get);
295300
req.insert_headers(&partition_key.into())?;
296-
self.pipeline
297-
.send(Context::new(), &mut req, ResourceType::Items)
298-
.await
301+
self.pipeline.send(Context::new(), &mut req, link).await
299302
}
300303

301304
/// Deletes an item from the container.
@@ -326,14 +329,11 @@ impl ContainerClient {
326329
// REASON: This is a documented public API so prefixing with '_' is undesirable.
327330
options: Option<ItemOptions>,
328331
) -> azure_core::Result<Response> {
329-
let url = self
330-
.container_url
331-
.with_path_segments(["docs", item_id.as_ref()]);
332+
let link = self.items_link.item(item_id);
333+
let url = self.pipeline.url(&link);
332334
let mut req = Request::new(url, Method::Delete);
333335
req.insert_headers(&partition_key.into())?;
334-
self.pipeline
335-
.send(Context::new(), &mut req, ResourceType::Items)
336-
.await
336+
self.pipeline.send(Context::new(), &mut req, link).await
337337
}
338338

339339
/// Executes a single-partition query against items in the container.
@@ -399,40 +399,12 @@ impl ContainerClient {
399399
// REASON: This is a documented public API so prefixing with '_' is undesirable.
400400
options: Option<QueryOptions>,
401401
) -> azure_core::Result<Pager<QueryResults<T>>> {
402-
let mut url = self.container_url.clone();
403-
url.append_path_segments(["docs"]);
404-
let mut base_req = Request::new(url, Method::Post);
405-
406-
base_req.insert_header(constants::QUERY, "True");
407-
base_req.add_mandatory_header(&constants::QUERY_CONTENT_TYPE);
408-
402+
let url = self.pipeline.url(&self.items_link);
403+
let mut base_request = Request::new(url, Method::Post);
409404
let QueryPartitionStrategy::SinglePartition(partition_key) = partition_key.into();
410-
base_req.insert_headers(&partition_key)?;
405+
base_request.insert_headers(&partition_key)?;
411406

412-
base_req.set_json(&query.into())?;
413-
414-
// We have to double-clone here.
415-
// First we clone the pipeline to pass it in to the closure
416-
let pipeline = self.pipeline.clone();
417-
Ok(Pager::from_callback(move |continuation| {
418-
// Then we have to clone it again to pass it in to the async block.
419-
// This is because Pageable can't borrow any data, it has to own it all.
420-
// That's probably good, because it means a Pageable can outlive the client that produced it, but it requires some extra cloning.
421-
let pipeline = pipeline.clone();
422-
let mut req = base_req.clone();
423-
async move {
424-
if let Some(continuation) = continuation {
425-
req.insert_header(constants::CONTINUATION, continuation);
426-
}
427-
428-
let response = pipeline
429-
.send(Context::new(), &mut req, ResourceType::Items)
430-
.await?;
431-
Ok(PagerResult::from_response_header(
432-
response,
433-
&constants::CONTINUATION,
434-
))
435-
}
436-
}))
407+
self.pipeline
408+
.send_query_request(query.into(), base_request, self.items_link.clone())
437409
}
438410
}

sdk/cosmos/azure_data_cosmos/src/clients/cosmos_client.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
use crate::{
55
clients::DatabaseClient,
66
models::{DatabaseProperties, DatabaseQueryResults, Item},
7-
pipeline::{AuthorizationPolicy, CosmosPipeline, ResourceType},
8-
utils::AppendPathSegments,
7+
pipeline::{AuthorizationPolicy, CosmosPipeline},
8+
resource_context::{ResourceLink, ResourceType},
99
CosmosClientOptions, CreateDatabaseOptions, Query, QueryDatabasesOptions,
1010
};
1111
use azure_core::{credentials::TokenCredential, Context, Method, Request, Response, Url};
12-
1312
use serde::Serialize;
1413
use std::sync::Arc;
1514

@@ -19,8 +18,8 @@ use azure_core::credentials::Secret;
1918
/// Client for Azure Cosmos DB.
2019
#[derive(Debug, Clone)]
2120
pub struct CosmosClient {
22-
endpoint: Url,
23-
pub(crate) pipeline: CosmosPipeline,
21+
databases_link: ResourceLink,
22+
pipeline: CosmosPipeline,
2423

2524
#[allow(dead_code)]
2625
options: CosmosClientOptions,
@@ -51,8 +50,9 @@ impl CosmosClient {
5150
) -> azure_core::Result<Self> {
5251
let options = options.unwrap_or_default();
5352
Ok(Self {
54-
endpoint: endpoint.as_ref().parse()?,
53+
databases_link: ResourceLink::root(ResourceType::Databases),
5554
pipeline: CosmosPipeline::new(
55+
endpoint.as_ref().parse()?,
5656
AuthorizationPolicy::from_token_credential(credential),
5757
options.client_options.clone(),
5858
),
@@ -83,8 +83,9 @@ impl CosmosClient {
8383
) -> azure_core::Result<Self> {
8484
let options = options.unwrap_or_default();
8585
Ok(Self {
86-
endpoint: endpoint.as_ref().parse()?,
86+
databases_link: ResourceLink::root(ResourceType::Databases),
8787
pipeline: CosmosPipeline::new(
88+
endpoint.as_ref().parse()?,
8889
AuthorizationPolicy::from_shared_key(key.into()),
8990
options.client_options.clone(),
9091
),
@@ -97,12 +98,12 @@ impl CosmosClient {
9798
/// # Arguments
9899
/// * `id` - The ID of the database.
99100
pub fn database_client(&self, id: impl AsRef<str>) -> DatabaseClient {
100-
DatabaseClient::new(self.pipeline.clone(), &self.endpoint, id.as_ref())
101+
DatabaseClient::new(self.pipeline.clone(), id.as_ref())
101102
}
102103

103104
/// Gets the endpoint of the database account this client is connected to.
104105
pub fn endpoint(&self) -> &Url {
105-
&self.endpoint
106+
&self.pipeline.endpoint
106107
}
107108

108109
/// Executes a query against databases in the account.
@@ -136,11 +137,11 @@ impl CosmosClient {
136137
// REASON: This is a documented public API so prefixing with '_' is undesirable.
137138
options: Option<QueryDatabasesOptions>,
138139
) -> azure_core::Result<azure_core::Pager<DatabaseQueryResults>> {
139-
let url = self.endpoint.with_path_segments(["dbs"]);
140+
let url = self.pipeline.url(&self.databases_link);
140141
let base_request = Request::new(url, azure_core::Method::Post);
141142

142143
self.pipeline
143-
.send_query_request(query.into(), base_request, ResourceType::Databases)
144+
.send_query_request(query.into(), base_request, self.databases_link.clone())
144145
}
145146

146147
/// Creates a new database.
@@ -163,12 +164,12 @@ impl CosmosClient {
163164
id: String,
164165
}
165166

166-
let url = self.endpoint.with_path_segments(["dbs"]);
167+
let url = self.pipeline.url(&self.databases_link);
167168
let mut req = Request::new(url, Method::Post);
168169
req.set_json(&RequestBody { id })?;
169170

170171
self.pipeline
171-
.send(Context::new(), &mut req, ResourceType::Databases)
172+
.send(Context::new(), &mut req, self.databases_link.clone())
172173
.await
173174
}
174175
}

0 commit comments

Comments
 (0)