Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samples/cosmos_read_item_native_tls/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;

let db_client = client.database_client(&args.database);
let container_client = db_client.container_client(&args.container).await?;
let container_client = db_client.container_client(&args.container, None).await?;

let response = container_client
.read_item(&args.partition_key, &args.item_id, None)
Expand Down
1 change: 1 addition & 0 deletions sdk/cosmos/azure_data_cosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

### Breaking Changes

- `DatabaseClient::container_client` now requires a second argument of type `Option<ContainerClientOptions>`; pass `None` to retain the previous behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could you link the pr

- `CosmosClient::database_client` and `DatabaseClient::container_client` now take `impl Into<ResourceIdentity>` instead of `&str`; call sites passing a deref-able string (for example a `Cow<str>` field) need `&*value` or `.as_ref()`. ([#4687](https://github.com/Azure/azure-sdk-for-rust/pull/4687))
- `DatabaseClient::id()` now returns `&ResourceIdentity` instead of `&str`. ([#4687](https://github.com/Azure/azure-sdk-for-rust/pull/4687))
- Control-plane APIs are now gated behind the new `control_plane` feature, which is **not** enabled by default. Code using database or container management (`CosmosClient::create_database`/`query_databases`, `DatabaseClient::read`/`create_container`/`query_containers`/`delete`, `ContainerClient::replace`/`delete`), throughput management (`read_throughput`/`begin_replace_throughput`, `ThroughputPoller`), or the associated model and options types (`DatabaseProperties`, `ThroughputProperties`, and the container create/replace/delete/query, database, and throughput option types) must now enable the `control_plane` feature. Reading container properties via `ContainerClient::read()` — along with `ContainerProperties`, `IndexingPolicy`, `ResourceResponse`, and `ReadContainerOptions` — remains available without the feature, since it works with Entra ID authentication and mirrors the metadata read the SDK already performs internally. ([#4854](https://github.com/Azure/azure-sdk-for-rust/pull/4854))
Expand Down
5 changes: 4 additions & 1 deletion sdk/cosmos/azure_data_cosmos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ async fn example(cosmos_client: CosmosClient) -> Result<(), Box<dyn std::error::
value: "2".into(),
};

let container = cosmos_client.database_client("myDatabase").container_client("myContainer").await?;
let container = cosmos_client
.database_client("myDatabase")
.container_client("myContainer", None)
.await?;

// Create an item
container.create_item("partition1", "1", item, None).await?;
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/examples/cosmos/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub struct BatchCommand {
impl BatchCommand {
pub async fn run(&self, client: &CosmosClient) -> Result<(), Box<dyn Error>> {
let db_client = client.database_client(&self.database);
let container_client = db_client.container_client(&self.container).await?;
let container_client = db_client.container_client(&self.container, None).await?;

// Parse the operations JSON
let operations: Vec<Value> = serde_json::from_str(&self.operations)?;
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/examples/cosmos/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl CreateCommand {
show_updated,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;

let pk = PartitionKey::from(&partition_key);
let item: serde_json::Value = serde_json::from_str(&json)?;
Expand Down
4 changes: 2 additions & 2 deletions sdk/cosmos/azure_data_cosmos/examples/cosmos/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl DeleteCommand {
partition_key,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;

let response = container_client
.delete_item(partition_key, &item_id, None)
Expand All @@ -81,7 +81,7 @@ impl DeleteCommand {

Subcommands::Container { database, id } => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&id).await?;
let container_client = db_client.container_client(&id, None).await?;
container_client.delete(None).await?;
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/examples/cosmos/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl MetadataCommand {
pub async fn run(self, client: CosmosClient) -> Result<(), Box<dyn Error>> {
let db_client = client.database_client(&self.database);
if let Some(container_name) = &self.container {
let container_client = db_client.container_client(container_name).await?;
let container_client = db_client.container_client(container_name, None).await?;
let response = container_client.read(None).await?.into_model()?;
println!("{:#?}", response);
return Ok(());
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/examples/cosmos/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl QueryCommand {
partition_key,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;

let scope = match partition_key {
Some(pk) => FeedScope::partition(pk),
Expand Down
4 changes: 2 additions & 2 deletions sdk/cosmos/azure_data_cosmos/examples/cosmos/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl ReadCommand {
partition_key,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;

let response = container_client
.read_item(&partition_key, &item_id, None)
Expand Down Expand Up @@ -93,7 +93,7 @@ impl ReadCommand {
container,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;
let response = container_client.read(None).await?.into_model()?;
println!("Container:");
println!(" {:#?}", response);
Expand Down
4 changes: 2 additions & 2 deletions sdk/cosmos/azure_data_cosmos/examples/cosmos/replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl ReplaceCommand {
show_updated,
} => {
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;

let pk = PartitionKey::from(&partition_key);
let item: serde_json::Value = serde_json::from_str(&json)?;
Expand Down Expand Up @@ -127,7 +127,7 @@ impl ReplaceCommand {
} => {
let throughput_properties = throughput_options.try_into()?;
let db_client = client.database_client(&database);
let container_client = db_client.container_client(&container).await?;
let container_client = db_client.container_client(&container, None).await?;
let new_throughput = container_client
.begin_replace_throughput(throughput_properties, None)
.await?
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/examples/cosmos/upsert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub struct UpsertCommand {
impl UpsertCommand {
pub async fn run(self, client: CosmosClient) -> Result<(), Box<dyn Error>> {
let db_client = client.database_client(&self.database);
let container_client = db_client.container_client(&self.container).await?;
let container_client = db_client.container_client(&self.container, None).await?;

let pk = PartitionKey::from(&self.partition_key);
let item: serde_json::Value = serde_json::from_str(&self.json)?;
Expand Down
30 changes: 6 additions & 24 deletions sdk/cosmos/azure_data_cosmos/src/clients/container_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ impl ContainerClient {
context: ClientContext,
database: &ResourceIdentity,
container: ResourceIdentity,
options: crate::options::ContainerClientOptions,
) -> crate::Result<Self> {
// The container's addressing mode must match the database's: name-with-name
// or RID-with-RID. Mixing the two is not supported by the service routing.
let container_ref = match (database, &container) {
(ResourceIdentity::Name(db_name), ResourceIdentity::Name(container_name)) => context
.driver
.resolve_container(db_name, container_name)
.resolve_container(db_name, container_name, options.operation)
.await
.map_err(|e| {
azure_data_cosmos_driver::error::CosmosErrorBuilder::from_error(e)
Expand All @@ -77,38 +78,20 @@ impl ContainerClient {
))
.build()
})?;

// The parent database RID is derived from the container RID, not
// taken from this `DatabaseClient`. Reject a container whose parent
// database does not match the addressed database so callers can't
// accidentally reach into a different database.
if resolved.database_rid() != db_rid.as_str() {
return Err(azure_data_cosmos_driver::error::CosmosError::builder()
.with_status(
azure_data_cosmos_driver::error::CosmosStatus::CLIENT_INVALID_RESOURCE_ID,
)
.with_message(format!(
"container RID '{}' belongs to database '{}', not the addressed database '{}'",
container_rid.as_str(),
resolved.database_rid(),
db_rid.as_str()
))
.with_status(azure_data_cosmos_driver::error::CosmosStatus::CLIENT_INVALID_RESOURCE_ID)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I seem to see a bunch of reformatting from one line to multiple and back, is there something we can do to reduce noise here (like configuring a longer max line length) or something?

.with_message(format!("container RID '{}' belongs to database '{}', not the addressed database '{}'", container_rid.as_str(), resolved.database_rid(), db_rid.as_str()))
.build()
.into());
}

resolved
}
(ResourceIdentity::Name(_), ResourceIdentity::Rid(_))
| (ResourceIdentity::Rid(_), ResourceIdentity::Name(_)) => {
return Err(azure_data_cosmos_driver::error::CosmosError::builder()
.with_status(
azure_data_cosmos_driver::error::CosmosStatus::CLIENT_MIXED_NAME_RID_ADDRESSING,
)
.with_message(
"database and container must use the same addressing mode: \
address both by name or both by RID",
)
.with_status(azure_data_cosmos_driver::error::CosmosStatus::CLIENT_MIXED_NAME_RID_ADDRESSING)
.with_message("database and container must use the same addressing mode: address both by name or both by RID")
.build()
.into());
}
Expand All @@ -119,7 +102,6 @@ impl ContainerClient {
context,
})
}

/// Builds the SDK-side [`CosmosOperationContext`] for this container's
/// operations, carrying the operation name plus the database and container
/// identity the driver context does not know.
Expand Down
26 changes: 12 additions & 14 deletions sdk/cosmos/azure_data_cosmos/src/clients/database_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

use crate::clients::{ClientContext, ContainerClient};
use crate::options::ContainerClientOptions;
use crate::{ResourceId, ResourceIdentity};
#[cfg(feature = "control_plane")]
use azure_data_cosmos_driver::models::DatabaseReference;
Expand Down Expand Up @@ -71,28 +72,24 @@ impl DatabaseClient {
/// Gets a [`ContainerClient`] that can be used to access the container with the
/// specified identity.
///
/// This method eagerly resolves immutable container metadata (resource ID and partition key
/// definition) from the service, so the returned client is ready for immediate use without
/// per-operation cache lookups.
///
/// The container's addressing mode must match this database's: a name-addressed
/// database accepts only name-addressed containers, and a RID-addressed database
/// accepts only [`ResourceId`](crate::ResourceId)-addressed containers.
/// This method eagerly resolves immutable container metadata before returning the client.
///
/// # Arguments
/// * `container` - The name or RID of the container.
///
/// # Errors
///
/// Returns an error if the container does not exist, the metadata cannot be
/// resolved, or the addressing mode does not match this database's.
/// * `options` - Optional parameters for creating the client.
Comment on lines -74 to +79

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a mis-merge, or did you remove the other content on purpose?

pub async fn container_client(
&self,
container: impl Into<ResourceIdentity>,
options: Option<ContainerClientOptions>,
) -> crate::Result<ContainerClient> {
Comment thread
analogrelay marked this conversation as resolved.
ContainerClient::new(self.context.clone(), &self.identity, container.into()).await
ContainerClient::new(
self.context.clone(),
&self.identity,
container.into(),
options.unwrap_or_default(),
)
.await
}

/// Returns the identity (name or RID) used to construct this client.
pub fn id(&self) -> &ResourceIdentity {
&self.identity
Expand Down Expand Up @@ -404,6 +401,7 @@ mod tests {
fn _assert_futures_are_send() {
fn assert_send<T: Send>(_: T) {}
let client: &DatabaseClient = todo!();
assert_send(client.container_client(todo!(), None));
let container_identity: ResourceIdentity = todo!();
assert_send(client.container_client(container_identity));
assert_send(client.read(todo!()));
Expand Down
8 changes: 8 additions & 0 deletions sdk/cosmos/azure_data_cosmos/src/options/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ use azure_data_cosmos_driver::options::OperationOptions;
#[cfg(feature = "control_plane")]
use crate::models::ThroughputProperties;

/// Options to be passed to [`DatabaseClient::container_client()`](crate::clients::DatabaseClient::container_client()).
#[derive(Clone, Default)]
#[non_exhaustive]
pub struct ContainerClientOptions {
/// General-purpose options used when resolving the container metadata.
pub operation: OperationOptions,
}

/// Options to be passed to [`DatabaseClient::create_container()`](crate::clients::DatabaseClient::create_container()).
#[cfg(feature = "control_plane")]
#[derive(Clone, Default)]
Expand Down
2 changes: 1 addition & 1 deletion sdk/cosmos/azure_data_cosmos/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use batch::{
pub use change_feed::{ChangeFeedMode, ChangeFeedOptions, ChangeFeedStartFrom};
pub use client::CosmosClientOptions;
pub use consistency::ConsistencyLevel;
pub use container::ReadContainerOptions;
pub use container::{ContainerClientOptions, ReadContainerOptions};
#[cfg(feature = "control_plane")]
pub use container::{
CreateContainerOptions, DeleteContainerOptions, QueryContainersOptions, ReplaceContainerOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub async fn aad_item_crud_roundtrip() -> Result<(), Box<dyn Error>> {
let (aad_client, recorder) = run_context.aad_client().await?;
let aad_container = aad_client
.database_client(db_client.id())
.container_client(&container_id)
.container_client(&container_id, None)
.await?;

let unique = Uuid::new_v4().to_string();
Expand Down Expand Up @@ -183,7 +183,7 @@ pub async fn aad_read_container_metadata() -> Result<(), Box<dyn Error>> {
let (aad_client, recorder) = run_context.aad_client().await?;
let aad_container = aad_client
.database_client(db_client.id())
.container_client(&container_id)
.container_client(&container_id, None)
.await?;

let properties = aad_container.read(None).await?.into_model()?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async fn create_container(
None,
)
.await?;
let container_client = db_client.container_client(&container_id).await?;
let container_client = db_client.container_client(&container_id, None).await?;

Ok(container_client)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub async fn container_crud_simple() -> Result<(), Box<dyn Error>> {
}
assert_eq!(vec![properties.id.clone()], ids);

let container_client = db_client.container_client(&properties.id, None).await?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this just a duplicate line from the one below?

let container_client = db_client.container_client(properties.id.as_ref()).await?;
let mut updated_indexing_policy = IndexingPolicy::default();
updated_indexing_policy.automatic = false;
Expand Down
Loading
Loading