Skip to content

Commit 1e525ae

Browse files
authored
Document operations (#476)
* Move replace_document to pipeline * Move delete document operation to pipeline * Address feedback
1 parent 1e5bc12 commit 1e525ae

19 files changed

+314
-373
lines changed

sdk/cosmos/examples/document_entries_00.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,13 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
147147
let replace_document_response = client
148148
.clone()
149149
.into_document_client(id.clone(), &id)?
150-
.replace_document()
151-
.consistency_level(ConsistencyLevel::from(&response))
152-
.if_match_condition(IfMatchCondition::Match(&doc.etag)) // use optimistic concurrency check
153-
.execute(&doc.document)
150+
.replace_document(
151+
Context::new(),
152+
&doc.document,
153+
ReplaceDocumentOptions::new()
154+
.consistency_level(ConsistencyLevel::from(&response))
155+
.if_match_condition(IfMatchCondition::Match(&doc.etag)), // use optimistic concurrency check
156+
)
154157
.await?;
155158

156159
println!(
@@ -180,9 +183,10 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
180183
client
181184
.clone()
182185
.into_document_client(id.clone(), &id)?
183-
.delete_document()
184-
.consistency_level(&response)
185-
.execute()
186+
.delete_document(
187+
Context::new(),
188+
DeleteDocumentOptions::new().consistency_level(&response),
189+
)
186190
.await?;
187191
}
188192
println!("Cleaned up");

sdk/cosmos/examples/document_entries_01.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,11 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
107107

108108
let replace_document_response = client
109109
.into_document_client(doc.id.clone(), &doc.id)?
110-
.replace_document()
111-
.consistency_level(&query_documents_response)
112-
.execute(&doc)
110+
.replace_document(
111+
Context::new(),
112+
&doc,
113+
ReplaceDocumentOptions::new().consistency_level(&query_documents_response),
114+
)
113115
.await?;
114116
println!(
115117
"replace_document_response == {:#?}",

sdk/cosmos/examples/readme.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,12 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
150150
collection_client
151151
.clone()
152152
.into_document_client(document.result.id.clone(), &document.result.a_number)?
153-
.delete_document()
154-
.consistency_level(session_token.clone())
155-
.if_match_condition(&document.document_attributes)
156-
.execute()
153+
.delete_document(
154+
Context::new(),
155+
DeleteDocumentOptions::new()
156+
.consistency_level(session_token.clone())
157+
.if_match_condition(&document.document_attributes),
158+
)
157159
.await?;
158160
}
159161

sdk/cosmos/examples/remove_all_documents.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use azure_core::prelude::*;
12
use azure_cosmos::prelude::*;
23
use futures::stream::StreamExt;
34
use serde_json::Value;
@@ -73,8 +74,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
7374
client
7475
.clone()
7576
.into_document_client(id.clone(), &partition_key)?
76-
.delete_document()
77-
.execute()
77+
.delete_document(Context::new(), DeleteDocumentOptions::new())
7878
.await?;
7979
}
8080

sdk/cosmos/src/clients/document_client.rs

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{AttachmentClient, CollectionClient, CosmosClient, DatabaseClient};
2-
use crate::prelude::{GetDocumentOptions, GetDocumentResponse};
2+
use crate::operations::*;
33
use crate::resources::ResourceType;
44
use crate::{requests, ReadonlyString};
55
use azure_core::{Context, HttpClient, PipelineContext, Request};
@@ -16,10 +16,11 @@ pub struct DocumentClient {
1616

1717
impl DocumentClient {
1818
/// This function creates a new instance of a DocumentClient. A document is identified by its
19-
/// primary key and its partition key. Partition key is eagerly evaluated: the json
20-
/// representation is generated as soon as you call the `new` function. This avoids doing the
21-
/// serialization over and over, saving time. It also releases the borrow since the serialized
22-
/// string is owned by the DocumentClient.
19+
/// primary key and its partition key.
20+
///
21+
/// Partition key is eagerly evaluated: the json representation is generated as soon as you
22+
/// call the `new` function. This avoids doing the serialization over and over, saving time.
23+
/// It also releases the borrow since the serialized string is owned by the `DocumentClient`.
2324
pub(crate) fn new<S: Into<String>, PK: Serialize>(
2425
collection_client: CollectionClient,
2526
document_name: S,
@@ -57,11 +58,6 @@ impl DocumentClient {
5758
&self.partition_key_serialized
5859
}
5960

60-
/// replace a document in a collection
61-
pub fn replace_document<'a>(&'a self) -> requests::ReplaceDocumentBuilder<'a, '_> {
62-
requests::ReplaceDocumentBuilder::new(self)
63-
}
64-
6561
/// Get a document
6662
pub async fn get_document<T>(
6763
&self,
@@ -72,7 +68,7 @@ impl DocumentClient {
7268
T: DeserializeOwned,
7369
{
7470
let mut request = self.prepare_request_pipeline_with_document_name(http::Method::GET);
75-
let mut pipeline_context = PipelineContext::new(ctx, ResourceType::Databases.into());
71+
let mut pipeline_context = PipelineContext::new(ctx, ResourceType::Documents.into());
7672

7773
options.decorate_request(&mut request)?;
7874

@@ -85,9 +81,45 @@ impl DocumentClient {
8581
GetDocumentResponse::try_from(response).await
8682
}
8783

84+
/// replace a document in a collection
85+
pub async fn replace_document<T: Serialize>(
86+
&self,
87+
ctx: Context,
88+
document: &T,
89+
options: ReplaceDocumentOptions<'_>,
90+
) -> crate::Result<ReplaceDocumentResponse> {
91+
let mut request = self.prepare_request_pipeline_with_document_name(http::Method::PUT);
92+
let mut pipeline_context = PipelineContext::new(ctx, ResourceType::Documents.into());
93+
94+
options.decorate_request(&mut request, document, self.partition_key_serialized())?;
95+
96+
let response = self
97+
.cosmos_client()
98+
.pipeline()
99+
.send(&mut pipeline_context, &mut request)
100+
.await?;
101+
102+
ReplaceDocumentResponse::try_from(response).await
103+
}
104+
88105
/// Delete a document
89-
pub fn delete_document(&self) -> requests::DeleteDocumentBuilder<'_> {
90-
requests::DeleteDocumentBuilder::new(self)
106+
pub async fn delete_document(
107+
&self,
108+
ctx: Context,
109+
options: DeleteDocumentOptions<'_>,
110+
) -> crate::Result<DeleteDocumentResponse> {
111+
let mut request = self.prepare_request_pipeline_with_document_name(http::Method::DELETE);
112+
let mut pipeline_context = PipelineContext::new(ctx, ResourceType::Documents.into());
113+
114+
options.decorate_request(&mut request, self.partition_key_serialized())?;
115+
116+
let response = self
117+
.cosmos_client()
118+
.pipeline()
119+
.send(&mut pipeline_context, &mut request)
120+
.await?;
121+
122+
DeleteDocumentResponse::try_from(response).await
91123
}
92124

93125
/// List all attachments for a document
@@ -103,22 +135,6 @@ impl DocumentClient {
103135
AttachmentClient::new(self, attachment_name)
104136
}
105137

106-
pub(crate) fn prepare_request_with_document_name(
107-
&self,
108-
method: http::Method,
109-
) -> http::request::Builder {
110-
self.cosmos_client().prepare_request(
111-
&format!(
112-
"dbs/{}/colls/{}/docs/{}",
113-
self.database_client().database_name(),
114-
self.collection_client().collection_name(),
115-
self.document_name()
116-
),
117-
method,
118-
ResourceType::Documents,
119-
)
120-
}
121-
122138
fn prepare_request_pipeline_with_document_name(&self, method: http::Method) -> Request {
123139
self.cosmos_client().prepare_request_pipeline(
124140
&format!(

sdk/cosmos/src/operations/create_document.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub struct CreateDocumentOptions<'a> {
1919
if_match_condition: Option<IfMatchCondition<'a>>,
2020
if_modified_since: Option<IfModifiedSince<'a>>,
2121
consistency_level: Option<ConsistencyLevel>,
22-
allow_tentative_writes: TenativeWritesAllowance,
22+
allow_tentative_writes: TentativeWritesAllowance,
2323
partition_key: Option<String>,
2424
}
2525

@@ -31,7 +31,7 @@ impl<'a> CreateDocumentOptions<'a> {
3131
if_match_condition: None,
3232
if_modified_since: None,
3333
consistency_level: None,
34-
allow_tentative_writes: TenativeWritesAllowance::Deny,
34+
allow_tentative_writes: TentativeWritesAllowance::Deny,
3535
partition_key: None,
3636
}
3737
}
@@ -40,7 +40,7 @@ impl<'a> CreateDocumentOptions<'a> {
4040
consistency_level: ConsistencyLevel => Some(consistency_level),
4141
if_match_condition: IfMatchCondition<'a> => Some(if_match_condition),
4242
if_modified_since: &'a DateTime<Utc> => Some(IfModifiedSince::new(if_modified_since)),
43-
allow_tentative_writes: TenativeWritesAllowance,
43+
allow_tentative_writes: TentativeWritesAllowance,
4444
is_upsert: bool => if is_upsert { IsUpsert::Yes } else { IsUpsert::No },
4545
indexing_directive: IndexingDirective,
4646
}
@@ -62,10 +62,10 @@ impl<'a> CreateDocumentOptions<'a> {
6262
DOC: Serialize + CosmosEntity<'b>,
6363
{
6464
let serialized = serde_json::to_string(document)?;
65-
let partition_key = self
66-
.partition_key
67-
.clone()
68-
.unwrap_or_else(|| serialize_partition_key(&document.partition_key()).unwrap());
65+
let partition_key = match &self.partition_key {
66+
Some(s) => s.clone(),
67+
None => serialize_partition_key(&document.partition_key())?,
68+
};
6969

7070
add_as_partition_key_header_serialized2(&partition_key, req);
7171
azure_core::headers::add_optional_header2(&self.if_match_condition, req)?;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use crate::prelude::*;
2+
3+
use azure_core::prelude::*;
4+
use azure_core::{Request as HttpRequest, Response as HttpResponse};
5+
use chrono::{DateTime, Utc};
6+
7+
#[derive(Debug, Clone)]
8+
pub struct DeleteDocumentOptions<'a> {
9+
if_match_condition: Option<IfMatchCondition<'a>>,
10+
if_modified_since: Option<IfModifiedSince<'a>>,
11+
consistency_level: Option<ConsistencyLevel>,
12+
allow_tentative_writes: TentativeWritesAllowance,
13+
}
14+
15+
impl<'a> DeleteDocumentOptions<'a> {
16+
pub fn new() -> DeleteDocumentOptions<'a> {
17+
Self {
18+
if_match_condition: None,
19+
if_modified_since: None,
20+
consistency_level: None,
21+
allow_tentative_writes: TentativeWritesAllowance::Deny,
22+
}
23+
}
24+
25+
setters! {
26+
consistency_level: ConsistencyLevel => Some(consistency_level),
27+
if_match_condition: IfMatchCondition<'a> => Some(if_match_condition),
28+
allow_tentative_writes: TentativeWritesAllowance,
29+
if_modified_since: &'a DateTime<Utc> => Some(IfModifiedSince::new(if_modified_since)),
30+
}
31+
32+
pub fn decorate_request(
33+
&self,
34+
request: &mut HttpRequest,
35+
serialized_partition_key: &str,
36+
) -> crate::Result<()> {
37+
azure_core::headers::add_optional_header2(&self.if_match_condition, request)?;
38+
azure_core::headers::add_optional_header2(&self.if_modified_since, request)?;
39+
azure_core::headers::add_optional_header2(&self.consistency_level, request)?;
40+
azure_core::headers::add_mandatory_header2(&self.allow_tentative_writes, request)?;
41+
42+
crate::cosmos_entity::add_as_partition_key_header_serialized2(
43+
serialized_partition_key,
44+
request,
45+
);
46+
47+
Ok(())
48+
}
49+
}
50+
51+
use crate::headers::from_headers::*;
52+
use azure_core::headers::session_token_from_headers;
53+
54+
#[derive(Debug, Clone)]
55+
pub struct DeleteDocumentResponse {
56+
pub charge: f64,
57+
pub activity_id: uuid::Uuid,
58+
pub session_token: String,
59+
}
60+
61+
impl DeleteDocumentResponse {
62+
pub async fn try_from(response: HttpResponse) -> crate::Result<Self> {
63+
let (_status_code, headers, _pinned_stream) = response.deconstruct();
64+
65+
let charge = request_charge_from_headers(&headers)?;
66+
let activity_id = activity_id_from_headers(&headers)?;
67+
let session_token = session_token_from_headers(&headers)?;
68+
69+
Ok(Self {
70+
charge,
71+
activity_id,
72+
session_token,
73+
})
74+
}
75+
}

sdk/cosmos/src/operations/get_document.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ impl<'a> GetDocumentOptions<'a> {
3333
}
3434

3535
pub(crate) fn decorate_request(&self, request: &mut HttpRequest) -> crate::Result<()> {
36-
// add trait headers
3736
azure_core::headers::add_optional_header2(&self.if_match_condition, request)?;
3837
azure_core::headers::add_optional_header2(&self.if_modified_since, request)?;
3938
azure_core::headers::add_optional_header2(&self.consistency_level, request)?;

sdk/cosmos/src/operations/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod create_permission;
99
mod create_user;
1010
mod delete_collection;
1111
mod delete_database;
12+
mod delete_document;
1213
mod delete_permission;
1314
mod delete_user;
1415
mod get_collection;
@@ -20,6 +21,7 @@ mod list_collections;
2021
mod list_databases;
2122
mod list_users;
2223
mod replace_collection;
24+
mod replace_document;
2325
mod replace_permission;
2426
mod replace_user;
2527

@@ -30,6 +32,7 @@ pub use create_permission::*;
3032
pub use create_user::*;
3133
pub use delete_collection::*;
3234
pub use delete_database::*;
35+
pub use delete_document::*;
3336
pub use delete_permission::*;
3437
pub use delete_user::*;
3538
pub use get_collection::*;
@@ -41,5 +44,6 @@ pub use list_collections::*;
4144
pub use list_databases::*;
4245
pub use list_users::*;
4346
pub use replace_collection::*;
47+
pub use replace_document::*;
4448
pub use replace_permission::*;
4549
pub use replace_user::*;

0 commit comments

Comments
 (0)