Skip to content

Commit e6b21a0

Browse files
authored
[Cosmos] Control-Plane Over Data-Plane APIs (Database/Container CRUD) (Azure#1853)
1 parent 1899267 commit e6b21a0

22 files changed

+1187
-427
lines changed

sdk/cosmos/azure_data_cosmos/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ documentation = "https://docs.rs/azure_data_cosmos"
1313
keywords = ["sdk", "azure", "rest", "cloud", "cosmos", "database"]
1414
categories = ["api-bindings"]
1515

16-
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
17-
1816
[dependencies]
1917
async-trait.workspace = true
2018
azure_core.workspace = true
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="warning">
2+
3+
This is a control-plane API and requires that you authenticate using a key. To use Entra ID to perform this operation, you must use the [Azure Resource Manager APIs](https://learn.microsoft.com/en-us/azure/templates/microsoft.documentdb/databaseaccounts).
4+
5+
</div>

sdk/cosmos/azure_data_cosmos/examples/cosmos/create.rs

Lines changed: 108 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,125 @@ use std::error::Error;
22

33
use azure_data_cosmos::{
44
clients::{ContainerClientMethods, DatabaseClientMethods},
5+
models::{ContainerProperties, PartitionKeyDefinition},
56
CosmosClient, CosmosClientMethods, PartitionKey,
67
};
7-
use clap::Args;
8+
use clap::{Args, Subcommand};
89

9-
/// Creates a new item.
10+
/// Creates a new item, database, or container.
1011
#[derive(Clone, Args)]
1112
pub struct CreateCommand {
12-
/// The database in which to create the item.
13-
database: String,
13+
#[command(subcommand)]
14+
subcommand: Subcommands,
15+
}
16+
17+
#[derive(Clone, Subcommand)]
18+
pub enum Subcommands {
19+
/// Create an item in a container.
20+
Item {
21+
/// The database in which to create the item.
22+
database: String,
23+
24+
/// The container in which to create the item.
25+
container: String,
26+
27+
/// The partition key of the new item.
28+
#[clap(long, short)]
29+
partition_key: String,
30+
31+
/// The JSON of the new item.
32+
#[clap(long, short)]
33+
json: String,
34+
},
35+
36+
/// Create a database (does not support Entra ID).
37+
Database {
38+
/// The ID of the new database to create.
39+
id: String,
40+
},
1441

15-
/// The container in which to create the item.
16-
container: String,
42+
/// Create a container (does not support Entra ID).
43+
Container {
44+
/// The ID of the database to create the container in.
45+
database: String,
1746

18-
/// The partition key of the new item.
19-
#[clap(long, short)]
20-
partition_key: String,
47+
/// The ID of the new container to create.
48+
#[clap(long, short)]
49+
id: Option<String>,
2150

22-
/// The JSON of the new item.
23-
#[clap(long, short)]
24-
json: String,
51+
/// The path to the partition key properties (supports up to 3).
52+
#[clap(long, short)]
53+
partition_key: Vec<String>,
54+
55+
/// The JSON for a ContainerProperties value. The 'id' and 'partition key' options are ignored if this is set.
56+
#[clap(long)]
57+
json: Option<String>,
58+
},
2559
}
2660

2761
impl CreateCommand {
2862
pub async fn run(self, client: CosmosClient) -> Result<(), Box<dyn Error>> {
29-
let db_client = client.database_client(&self.database);
30-
let container_client = db_client.container_client(&self.container);
31-
32-
let pk = PartitionKey::from(&self.partition_key);
33-
let item: serde_json::Value = serde_json::from_str(&self.json)?;
34-
35-
let created = container_client
36-
.create_item(pk, item, None)
37-
.await?
38-
.deserialize_body()
39-
.await?
40-
.unwrap();
41-
println!("Created item:");
42-
println!("{:#?}", created);
43-
Ok(())
63+
match self.subcommand {
64+
Subcommands::Item {
65+
database,
66+
container,
67+
partition_key,
68+
json,
69+
} => {
70+
let db_client = client.database_client(database);
71+
let container_client = db_client.container_client(container);
72+
73+
let pk = PartitionKey::from(&partition_key);
74+
let item: serde_json::Value = serde_json::from_str(&json)?;
75+
76+
let created = container_client
77+
.create_item(pk, item, None)
78+
.await?
79+
.deserialize_body()
80+
.await?
81+
.unwrap();
82+
println!("Created item:");
83+
println!("{:#?}", created);
84+
Ok(())
85+
}
86+
87+
Subcommands::Database { id } => {
88+
let db = client
89+
.create_database(id, None)
90+
.await?
91+
.deserialize_body()
92+
.await?
93+
.unwrap();
94+
println!("Created database:");
95+
println!("{:#?}", db);
96+
Ok(())
97+
}
98+
99+
Subcommands::Container {
100+
database,
101+
id,
102+
partition_key,
103+
json,
104+
} => {
105+
let properties = match json {
106+
Some(j) => serde_json::from_str(&j).unwrap(),
107+
None => ContainerProperties {
108+
id: id.expect("the ID is required when not using '--json'"),
109+
partition_key: PartitionKeyDefinition::new(partition_key),
110+
..Default::default()
111+
},
112+
};
113+
let container = client
114+
.database_client(database)
115+
.create_container(properties, None)
116+
.await?
117+
.deserialize_body()
118+
.await?
119+
.unwrap();
120+
println!("Created container:");
121+
println!("{:#?}", container);
122+
Ok(())
123+
}
124+
}
44125
}
45126
}

sdk/cosmos/azure_data_cosmos/examples/cosmos/delete.rs

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,87 @@ use azure_data_cosmos::{
55
clients::{ContainerClientMethods, DatabaseClientMethods},
66
CosmosClient, CosmosClientMethods,
77
};
8-
use clap::Args;
8+
use clap::{Args, Subcommand};
99

10-
/// Deletes an item.
10+
/// Deletes an item, database, or container.
1111
#[derive(Clone, Args)]
1212
pub struct DeleteCommand {
13-
/// The database containing the item.
14-
database: String,
13+
#[command(subcommand)]
14+
subcommand: Subcommands,
15+
}
16+
17+
#[derive(Clone, Subcommand)]
18+
pub enum Subcommands {
19+
/// Delete an item in a container.
20+
Item {
21+
/// The database containing the item.
22+
database: String,
23+
24+
/// The container containing the item.
25+
container: String,
26+
27+
/// The ID of the item.
28+
#[clap(long, short)]
29+
item_id: String,
1530

16-
/// The container containing the item.
17-
container: String,
31+
/// The partition key of the item.
32+
#[clap(long, short)]
33+
partition_key: String,
34+
},
1835

19-
/// The ID of the item.
20-
#[clap(long, short)]
21-
item_id: String,
36+
/// Create a database (does not support Entra ID).
37+
Database {
38+
/// The ID of the database to delete.
39+
id: String,
40+
},
2241

23-
/// The partition key of the item.
24-
#[clap(long, short)]
25-
partition_key: String,
42+
/// Create a container (does not support Entra ID).
43+
Container {
44+
/// The ID of the database the container is in.
45+
database: String,
46+
47+
/// The ID of the container to delete
48+
id: String,
49+
},
2650
}
2751

2852
impl DeleteCommand {
2953
pub async fn run(self, client: CosmosClient) -> Result<(), Box<dyn Error>> {
30-
let db_client = client.database_client(&self.database);
31-
let container_client = db_client.container_client(&self.container);
32-
33-
let response = container_client
34-
.delete_item(&self.partition_key, &self.item_id, None)
35-
.await;
36-
match response {
37-
Err(e) if e.http_status() == Some(StatusCode::NotFound) => println!("Item not found!"),
38-
Ok(_) => println!("Item deleted"),
39-
Err(e) => return Err(e.into()),
40-
};
41-
Ok(())
54+
match self.subcommand {
55+
Subcommands::Item {
56+
database,
57+
container,
58+
item_id,
59+
partition_key,
60+
} => {
61+
let db_client = client.database_client(database);
62+
let container_client = db_client.container_client(container);
63+
64+
let response = container_client
65+
.delete_item(partition_key, item_id, None)
66+
.await;
67+
match response {
68+
Err(e) if e.http_status() == Some(StatusCode::NotFound) => {
69+
println!("Item not found!")
70+
}
71+
Ok(_) => println!("Item deleted"),
72+
Err(e) => return Err(e.into()),
73+
};
74+
Ok(())
75+
}
76+
77+
Subcommands::Database { id } => {
78+
let db_client = client.database_client(id);
79+
db_client.delete(None).await?;
80+
Ok(())
81+
}
82+
83+
Subcommands::Container { database, id } => {
84+
let db_client = client.database_client(database);
85+
let container_client = db_client.container_client(id);
86+
container_client.delete(None).await?;
87+
Ok(())
88+
}
89+
}
4290
}
4391
}

sdk/cosmos/azure_data_cosmos/examples/cosmos/main.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ struct SharedArgs {
3434

3535
/// An authentication key to use when connecting to the Cosmos DB account. If omitted, the connection will use Entra ID.
3636
#[clap(long)]
37-
#[cfg(feature = "key_auth")]
3837
key: Option<String>,
3938
}
4039

@@ -62,7 +61,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
6261
return Ok(());
6362
};
6463

65-
let client = create_client(&args.shared_args);
64+
let client = create_client(&args.shared_args)?;
6665

6766
match cmd {
6867
Subcommands::Create(cmd) => cmd.run(client).await,
@@ -75,18 +74,19 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7574
}
7675
}
7776

78-
#[cfg(feature = "key_auth")]
79-
fn create_client(args: &SharedArgs) -> CosmosClient {
77+
fn create_client(args: &SharedArgs) -> Result<CosmosClient, Box<dyn Error>> {
8078
if let Some(key) = args.key.as_ref() {
81-
CosmosClient::with_key(&args.endpoint, key.clone(), None).unwrap()
79+
#[cfg(feature = "key_auth")]
80+
{
81+
Ok(CosmosClient::with_key(&args.endpoint, key.clone(), None)?)
82+
}
83+
#[cfg(not(feature = "key_auth"))]
84+
{
85+
let _ = key; // Mark 'key' as used to make the compiler happy.
86+
Err("cannot authenticate with a key unless the 'key_auth' feature is enabled".into())
87+
}
8288
} else {
8389
let cred = DefaultAzureCredential::new().unwrap();
84-
CosmosClient::new(&args.endpoint, cred, None).unwrap()
90+
Ok(CosmosClient::new(&args.endpoint, cred, None)?)
8591
}
8692
}
87-
88-
#[cfg(not(feature = "key_auth"))]
89-
fn create_client(args: &SharedArgs) -> CosmosClient {
90-
let cred = DefaultAzureCredential::new().unwrap();
91-
CosmosClient::new(&args.endpoint, cred, None).unwrap()
92-
}

0 commit comments

Comments
 (0)