Skip to content

Commit 9769158

Browse files
Adds partial document updates in data_cosmos (#1454)
1 parent 05fe095 commit 9769158

File tree

3 files changed

+212
-0
lines changed

3 files changed

+212
-0
lines changed

sdk/data_cosmos/src/clients/document.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,30 @@ impl DocumentClient {
4242
ReplaceDocumentBuilder::new(self.clone(), document)
4343
}
4444

45+
/// Patch the document.
46+
/// The Patch Document operation does path-level updates to specific files/properties in a single document.
47+
///
48+
/// # Example
49+
/// ```no_run
50+
/// # use azure_data_cosmos::prelude::*;
51+
/// # async fn func(document_client: &DocumentClient) -> azure_core::Result<()> {
52+
/// let operations = vec![
53+
/// Operation::add("/color", "silver")?,
54+
/// Operation::remove("/used"),
55+
/// Operation::set("/price", 355.45)?,
56+
/// Operation::incr("/inventory/quantity", 10)?,
57+
/// Operation::add("/tags/-", "featured-bikes")?,
58+
/// Operation::r#move("/inventory/color", "/color"),
59+
/// ];
60+
///
61+
/// document_client.patch_document(operations).await?;
62+
/// # Ok(())
63+
/// # }
64+
/// ```
65+
pub fn patch_document(&self, operations: Vec<Operation>) -> PatchDocumentBuilder {
66+
PatchDocumentBuilder::new(self.clone(), operations)
67+
}
68+
4569
/// Delete the document.
4670
pub fn delete_document(&self) -> DeleteDocumentBuilder {
4771
DeleteDocumentBuilder::new(self.clone())

sdk/data_cosmos/src/operations/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mod list_stored_procedures;
3838
mod list_triggers;
3939
mod list_user_defined_functions;
4040
mod list_users;
41+
mod patch_document;
4142
mod query_documents;
4243
mod replace_collection;
4344
mod replace_document;
@@ -81,6 +82,7 @@ pub use list_stored_procedures::*;
8182
pub use list_triggers::*;
8283
pub use list_user_defined_functions::*;
8384
pub use list_users::*;
85+
pub use patch_document::*;
8486
pub use query_documents::*;
8587
pub use replace_collection::*;
8688
pub use replace_document::*;
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
use crate::headers::from_headers::*;
2+
use crate::prelude::*;
3+
use crate::resources::document::DocumentAttributes;
4+
use crate::ResourceQuota;
5+
6+
use azure_core::headers::session_token_from_headers;
7+
use azure_core::Response as HttpResponse;
8+
use azure_core::SessionToken;
9+
use serde::Serialize;
10+
use serde_json::Value;
11+
use time::OffsetDateTime;
12+
13+
operation! {
14+
PatchDocument,
15+
client: DocumentClient,
16+
operations: Vec<Operation>,
17+
?condition: String
18+
}
19+
20+
impl PatchDocumentBuilder {
21+
pub fn into_future(self) -> PatchDocument {
22+
Box::pin(async move {
23+
let mut request = self.client.document_request(azure_core::Method::Patch);
24+
25+
crate::cosmos_entity::add_as_partition_key_header_serialized(
26+
self.client.partition_key_serialized(),
27+
&mut request,
28+
);
29+
30+
let patch_request = PatchDocumentRequest {
31+
condition: self.condition,
32+
operations: self.operations,
33+
};
34+
35+
let serialized = azure_core::to_json(&patch_request)?;
36+
request.set_body(serialized);
37+
38+
let response = self
39+
.client
40+
.cosmos_client()
41+
.pipeline()
42+
.send(
43+
self.context.clone().insert(ResourceType::Documents),
44+
&mut request,
45+
)
46+
.await?;
47+
48+
PatchDocumentResponse::try_from(response).await
49+
})
50+
}
51+
}
52+
53+
#[derive(Serialize, Debug)]
54+
struct PatchDocumentRequest {
55+
#[serde(skip_serializing_if = "Option::is_none")]
56+
condition: Option<String>,
57+
operations: Vec<Operation>,
58+
}
59+
60+
#[derive(Serialize, Debug, Clone)]
61+
#[serde(rename_all = "lowercase")]
62+
#[serde(tag = "op")]
63+
pub enum Operation {
64+
Add { path: String, value: Value },
65+
Remove { path: String },
66+
Set { path: String, value: Value },
67+
Incr { path: String, value: Value },
68+
Replace { path: String, value: Value },
69+
Move { path: String, from: String },
70+
}
71+
72+
impl Operation {
73+
pub fn add<P: Into<String>, V: Serialize>(
74+
path: P,
75+
value: V,
76+
) -> Result<Operation, serde_json::Error> {
77+
Ok(Operation::Add {
78+
path: path.into(),
79+
value: serde_json::to_value(value)?,
80+
})
81+
}
82+
83+
pub fn remove<P: Into<String>>(path: P) -> Operation {
84+
Operation::Remove { path: path.into() }
85+
}
86+
87+
pub fn set<P: Into<String>, V: Serialize>(
88+
path: P,
89+
value: V,
90+
) -> Result<Operation, serde_json::Error> {
91+
Ok(Operation::Set {
92+
path: path.into(),
93+
value: serde_json::to_value(value)?,
94+
})
95+
}
96+
97+
pub fn incr<P: Into<String>, V: Serialize>(
98+
path: P,
99+
value: V,
100+
) -> Result<Operation, serde_json::Error> {
101+
Ok(Operation::Incr {
102+
path: path.into(),
103+
value: serde_json::to_value(value)?,
104+
})
105+
}
106+
107+
pub fn replace<P: Into<String>, V: Serialize>(
108+
path: P,
109+
value: V,
110+
) -> Result<Operation, serde_json::Error> {
111+
Ok(Operation::Replace {
112+
path: path.into(),
113+
value: serde_json::to_value(value)?,
114+
})
115+
}
116+
117+
pub fn r#move<P: Into<String>, F: Into<String>>(path: P, from: F) -> Operation {
118+
Operation::Move {
119+
path: path.into(),
120+
from: from.into(),
121+
}
122+
}
123+
}
124+
125+
#[derive(Debug, Clone)]
126+
pub struct PatchDocumentResponse {
127+
pub document_attributes: DocumentAttributes,
128+
pub content_location: Option<String>,
129+
pub last_state_change: OffsetDateTime,
130+
pub resource_quota: Vec<ResourceQuota>,
131+
pub resource_usage: Vec<ResourceQuota>,
132+
pub lsn: u64,
133+
pub schema_version: String,
134+
pub alt_content_path: String,
135+
pub content_path: String,
136+
pub quorum_acked_lsn: Option<u64>,
137+
pub current_write_quorum: Option<u64>,
138+
pub current_replica_set_size: Option<u64>,
139+
pub role: u32,
140+
pub global_committed_lsn: u64,
141+
pub number_of_read_regions: u32,
142+
pub transport_request_id: u64,
143+
pub cosmos_llsn: u64,
144+
pub cosmos_quorum_acked_llsn: Option<u64>,
145+
pub session_token: SessionToken,
146+
pub charge: f64,
147+
pub service_version: String,
148+
pub activity_id: uuid::Uuid,
149+
pub gateway_version: String,
150+
pub date: OffsetDateTime,
151+
}
152+
153+
impl PatchDocumentResponse {
154+
pub async fn try_from(response: HttpResponse) -> azure_core::Result<Self> {
155+
let (_status_code, headers, body) = response.deconstruct();
156+
let body = body.collect().await?;
157+
let document_attributes = serde_json::from_slice(&body)?;
158+
159+
Ok(Self {
160+
content_location: content_location_from_headers(&headers)?,
161+
last_state_change: last_state_change_from_headers(&headers)?,
162+
resource_quota: resource_quota_from_headers(&headers)?,
163+
resource_usage: resource_usage_from_headers(&headers)?,
164+
lsn: lsn_from_headers(&headers)?,
165+
schema_version: schema_version_from_headers(&headers)?,
166+
alt_content_path: alt_content_path_from_headers(&headers)?,
167+
content_path: content_path_from_headers(&headers)?,
168+
quorum_acked_lsn: quorum_acked_lsn_from_headers_optional(&headers)?,
169+
current_write_quorum: current_write_quorum_from_headers_optional(&headers)?,
170+
current_replica_set_size: current_replica_set_size_from_headers_optional(&headers)?,
171+
role: role_from_headers(&headers)?,
172+
global_committed_lsn: global_committed_lsn_from_headers(&headers)?,
173+
number_of_read_regions: number_of_read_regions_from_headers(&headers)?,
174+
transport_request_id: transport_request_id_from_headers(&headers)?,
175+
cosmos_llsn: cosmos_llsn_from_headers(&headers)?,
176+
cosmos_quorum_acked_llsn: cosmos_quorum_acked_llsn_from_headers_optional(&headers)?,
177+
session_token: session_token_from_headers(&headers)?,
178+
charge: request_charge_from_headers(&headers)?,
179+
service_version: service_version_from_headers(&headers)?,
180+
activity_id: activity_id_from_headers(&headers)?,
181+
gateway_version: gateway_version_from_headers(&headers)?,
182+
date: date_from_headers(&headers)?,
183+
document_attributes,
184+
})
185+
}
186+
}

0 commit comments

Comments
 (0)