Skip to content

Convert get database operation to pipeline architecture #286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2021

Conversation

rylev
Copy link
Contributor

@rylev rylev commented Jun 10, 2021

No description provided.

@rylev rylev requested a review from MindFlavor June 10, 2021 15:51
Copy link
Member

@heaths heaths left a comment

Choose a reason for hiding this comment

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

Nothing to hold up the PR - the code changes look good - but I do see a number of design changes we should make before moving more to the new pipeline. For example, the pipeline should define common methods for sending requests and handling responses, including extensible status code validation, request and response sanitization (later - at least for tests), etc. We consolidate all that in other SDKs for ease, consistency, and productivity.

@@ -20,7 +21,9 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {

let database_client = client.into_database_client(database_name.clone());

let response = database_client.get_database().execute().await?;
let response = database_client
.get_database(Context::new(), GetDatabaseOptions::new())
Copy link
Member

Choose a reason for hiding this comment

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

For client and method options, do we want to standardize on new or default? See my comment above for context. In other SDKs, this argument is typically optional e.g. default parameter value in .NET, overload in Java, kwargs in Python, and options bag in JS, hence I was thinking default.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some options won't be able to have default implementations because they have required arguments. If we opt for Default, we won't be able to do that for all option types.

Unfortunately since Rust doesn't have default params, this is fairly verbose and Default impls don't help necessarily. We could have one method get_database that only takes a context and default the options, and a get_database_with_options which takes an options param.

Copy link
Member

Choose a reason for hiding this comment

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

Generally, if a parameter is required (hence making it required to create options) it's its own parameter. Eventually we'll want to be consistent with the other SDKs when not idiomatic e.g. which parameters are parameters and which are in some sort of options bag.

@@ -196,6 +183,10 @@ impl CosmosClient {
DatabaseClient::new(self, database_name)
}

/// Prepares an `http::RequestBuilder`.
///
/// Note: This is used by legacy operations that have not moved to the use of the
Copy link
Member

Choose a reason for hiding this comment

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

Nit: replace "Note:" with "TODO:" so we have a consistent approach to finding them all. We should do so by GA, so having GA'd docs with "TODO" shouldn't be likely.

Comment on lines +43 to +55
let mut request = self
.prepare_request_with_database_name(http::Method::GET)
.body(bytes::Bytes::new())
.unwrap()
.into();
options.decorate_request(&mut request)?;
let response = self
.pipeline()
.send(&mut ctx, &mut request)
.await
.map_err(crate::Error::PolicyError)?
.validate(http::StatusCode::OK)
.await?;
Copy link
Member

Choose a reason for hiding this comment

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

This is something the pipeline should actually do, and the types of errors, which can be retried, etc. is typically handled by the pipeline. Again, we can do this in a separate PR, but I bring it up as a design point. Having every single method repeat this is burdensome and exactly what the pipeline was meant to handle. For an example reference, see .NETs: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/src/Pipeline/HttpPipeline.cs. Our other language SDKs use something similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you talking about the validation? This something that depends on the specific operation being performed, so I don't think we can move this into the pipeline. I agree it's currently fairly verbose, but I'm not sure the right way to solve that is moving it into pipelines rather than moving some of this into a function somewhere.

Copy link
Member

Choose a reason for hiding this comment

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

Some of our SDKs use a response classifier they can pass in when building the pipeline. We don't necessarily have to be the same, but - even if validate is kept separate here - serializing and sending the request, and deserializing and handling the response should be done in the pipeline as much as possible. We do have some clients (Key Vault does it in a couple places) that will occasionally "override" that behavior, but it's pretty rare we need to.

@rylev rylev merged commit 58238de into Azure:master Jun 11, 2021
@rylev rylev deleted the get-database branch June 11, 2021 08:47
@cataggar cataggar mentioned this pull request Jun 14, 2022
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants