docs(azure_core): add migration guide#4239
docs(azure_core): add migration guide#4239ronniegeraghty wants to merge 2 commits intoAzure:mainfrom
Conversation
Closes Azure#4230 Refs Azure#4228 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Fixed an authoring error in the Cargo.toml |
There was a problem hiding this comment.
Pull request overview
Adds a new MIGRATION.md at the azure_core crate root to document how to migrate from azure_core ≤0.21 to the current (v0.22+) API surface, including dependency/feature changes and updated patterns for credentials, pipelines, paging, polling, and error handling.
Changes:
- Introduces a structured migration guide with side-by-side “before/after” guidance for key APIs.
- Documents new module layout and configuration patterns (
azure_core::http,ClientOptions, etc.). - Adds a feature-flag table and links to further resources.
| #[async_trait::async_trait] | ||
| pub trait TokenCredential: Send + Sync + std::fmt::Debug { | ||
| async fn get_token( | ||
| &self, | ||
| scopes: &[&str], |
There was a problem hiding this comment.
This code block defines a new pub trait TokenCredential, which will conflict with the existing azure_core::credentials::TokenCredential if a reader copy/pastes it into their project (and therefore won’t compile). Consider presenting this as a signature excerpt (or marking the block rust,ignore/text) to make it clear it’s reference-only and to better align with the “compilable code samples” goal.
|
|
||
| // Service clients return a Pager. Iterate pages: | ||
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | ||
| let mut pages = pager.into_pages(); | ||
| while let Some(page) = pages.next().await { | ||
| let page = page?; | ||
| // Process each page | ||
| } | ||
|
|
||
| // Or iterate items directly: | ||
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | ||
| let mut items = pager.into_items(); | ||
| while let Some(item) = items.next().await { | ||
| let item = item?; |
There was a problem hiding this comment.
The Pager example uses pages.next().await/items.next().await, but azure_core::http::pager iterators are TryStreams and the crate’s own docs use TryStreamExt::try_next() (not next). Updating the example to the supported iteration pattern will prevent broken migration guidance.
| // Service clients return a Pager. Iterate pages: | |
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | |
| let mut pages = pager.into_pages(); | |
| while let Some(page) = pages.next().await { | |
| let page = page?; | |
| // Process each page | |
| } | |
| // Or iterate items directly: | |
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | |
| let mut items = pager.into_items(); | |
| while let Some(item) = items.next().await { | |
| let item = item?; | |
| use futures::TryStreamExt; | |
| // Service clients return a Pager. Iterate pages: | |
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | |
| let mut pages = pager.into_pages(); | |
| while let Some(page) = pages.try_next().await? { | |
| // Process each page | |
| } | |
| // Or iterate items directly: | |
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | |
| let mut items = pager.into_items(); | |
| while let Some(item) = items.try_next().await? { |
| // Service clients return a Pager. Iterate pages: | ||
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | ||
| let mut pages = pager.into_pages(); |
There was a problem hiding this comment.
These Pager/Poller snippets use placeholders like MyPageResponse/client/params, so they won’t compile as-is. Since the PR description emphasizes “compilable code samples”, consider either making the examples self-contained (real client type + minimal models) or marking them rust,ignore/rust,no_run so readers aren’t led to expect copy/paste compilation.
|
|
||
| // Service clients return a Poller for LROs: | ||
| let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?; | ||
| let result = poller.wait().await?; |
There was a problem hiding this comment.
Poller doesn’t have a wait() method in azure_core::http::poller (it’s used via stream iteration and/or by awaiting the poller directly). As written, this example won’t compile and may mislead users; please update it to the supported Poller usage pattern.
| let result = poller.wait().await?; | |
| let result = poller.await?; |
There was a problem hiding this comment.
Right. This is old code. Don't just use the LLM trained old data. Use my heaths/akv-cli-rs repo for best practices.
| | `reqwest_rustls` | ✅ | Use [rustls](https://crates.io/crates/rustls) TLS backend (instead of OpenSSL) | | ||
| | `tokio` | ✅ | Enable [tokio](https://tokio.rs) async runtime support | |
There was a problem hiding this comment.
The feature table describes reqwest_rustls as “instead of OpenSSL”, but in this repo reqwest is built with default-features = false and TLS is only enabled via the reqwest_rustls feature. Without reqwest_rustls, consumers typically lose HTTPS/TLS support rather than automatically switching to an OpenSSL/native-tls backend.
| To switch from the default `rustls` TLS backend to OpenSSL: | ||
|
|
||
| ```toml | ||
| azure_core = { version = "0.35", default-features = false, features = [ | ||
| "reqwest", |
There was a problem hiding this comment.
This section says you can switch from rustls to OpenSSL by disabling default features and omitting reqwest_rustls, but azure_core currently doesn’t expose a feature that enables reqwest’s native-tls/OpenSSL backend. As a result, the shown feature set will generally compile without TLS and break HTTPS scenarios. Please adjust this guidance to reflect what’s actually supported (or introduce a dedicated feature if OpenSSL/native-tls is intended).
|
Note: after |
heaths
left a comment
There was a problem hiding this comment.
Lots of changes needed. Overall topics covered are fine, but 1) this needs to be compiled as we do README.md and some of the code is outdated. Use my akv repo for context and best practices to better emit code if you're using an LLM, or at least see it for examples (I linked to specific ones).
| @@ -0,0 +1,408 @@ | |||
| # Migrating to azure_core v0.22+ from azure_core ≤0.21 | |||
There was a problem hiding this comment.
Can we just focus on GA? We're about to, and we don't support people on the prerelease so why even mention it?
Besides, we're well beyond 0.22 and a lot has changed. What we have in 0.34.0 is basically what we plan to ship with.
|
|
||
| ## Why Migrate | ||
|
|
||
| - **Stable API design**: The ≤0.21 versions were experimental and never reached a stable API. The v0.22+ rewrite follows the [Azure SDK Design Guidelines for Rust](https://azure.github.io/azure-sdk/rust_introduction.html) and targets GA stability. |
There was a problem hiding this comment.
I won't repeat this again, but just to drive the point home: 0.22 doesn't follow the guidelines. I updated the guidelines as we made new decisions. Hence, let's just focus on 1.0.0.
| ```diff | ||
| [dependencies] | ||
| - azure_core = "0.21" | ||
| + azure_core = "0.35" |
There was a problem hiding this comment.
Not released, nor will it probably be. We shipped 0.34 and will next ship 1.0.
|
|
||
| Authentication in `azure_core` is defined through the `TokenCredential` trait. The trait signature changed: | ||
|
|
||
| ```rust |
There was a problem hiding this comment.
You need to add no_run to these and include them in a tests/migration.rs as documented: https://azure.github.io/azure-sdk/rust_implementation.html#rust-client-tests-doc-examples. You don't necessarily have to use the include_markdown!() macro if you don't need it i.e., the code is self-contained. This makes sure it actually compiles. So the block would be:
```rust no_run
// ...
```| use azure_core::http::pager::{Pager, PageIterator, ItemIterator}; | ||
|
|
||
| // Service clients return a Pager. Iterate pages: | ||
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; |
There was a problem hiding this comment.
They shouldn't have to declare the type. That's just unnecessary and complicates the code they should write. See https://github.com/heaths/akv-cli-rs/blob/60e2bb1353728b67bbd7be81f7e6fe3ba818f805/examples/blobs.rs#L30-L36. Most of the time, the type is inferred.
| // Service clients return a Pager. Iterate pages: | ||
| let pager: Pager<MyPageResponse> = client.list_items(None).await?; | ||
| let mut pages = pager.into_pages(); | ||
| while let Some(page) = pages.next().await { |
There was a problem hiding this comment.
Better that they use try_next().await? and get the actual type. Less boiler plate and better DX. See my sample link above. If you're using an LLM to write this, I'd point it at that repo for sample context.
|
|
||
| ### Long-Running Operations with Poller | ||
|
|
||
| Long-running operations now use the `Poller` type instead of ad-hoc polling patterns. |
There was a problem hiding this comment.
Use common terms for SEO after defining them.
| Long-running operations now use the `Poller` type instead of ad-hoc polling patterns. | |
| Long-running operations (LRO) now use the `Poller` type instead of ad-hoc polling patterns. |
| use azure_core::http::Poller; | ||
|
|
||
| // Service clients return a Poller for LROs: | ||
| let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?; |
There was a problem hiding this comment.
Again, don't need the type: https://github.com/heaths/akv-cli-rs/blob/60e2bb1353728b67bbd7be81f7e6fe3ba818f805/src/bin/akv/commands/certificate.rs#L335-L338. We should also be highlighting getting the final model. That's what the vast majority of people are interested in. Manual polling isn't something most people would have to or want to do. It's not really useful in most applications, especially since we don't know an end time i.e., can't report accurate progress.
|
|
||
| // Service clients return a Poller for LROs: | ||
| let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?; | ||
| let result = poller.wait().await?; |
There was a problem hiding this comment.
Right. This is old code. Don't just use the LLM trained old data. Use my heaths/akv-cli-rs repo for best practices.
Migration guide: azure_core v0.22+ from ≤0.21
Closes #4230
Refs #4228
Placement decision
The migration guide is placed at
sdk/core/azure_core/MIGRATION.md— the crate root, alongside the existingREADME.mdandCHANGELOG.md. This follows the pattern used across Tier 1 Azure SDKs:sdk/{service}/{Package}/MigrationGuide.mdsdk/{service}/{artifact}/migration_guide.mdsdk/{service}/{package}/MigrationGuide.mdsdk/{service}/{package}/migration_guide.mdThe Rust repo uses
UPPER_CASE.mdnaming (matchingREADME.md,CHANGELOG.md,TROUBLESHOOTING.md), soMIGRATION.mdis the natural fit.What this covers
azure_core::http, re-exports fromtypespec_client_core)TokenCredentialtrait signature change (scopes, options,AccessTokenreturn type)ClientOptionsunified configuration patternPager(replacingPageable) andPollernew APIsErrorKindmatchingtokio) requirementReviewer checklist (Quality Bar)
rustcode block compiles against the current crate versionTokenCredentialtrait changesErrorKindmatching patterns#[tokio::main]example