Skip to content

Commit 21a835b

Browse files
committed
Move continuation token to PagerOptions
Resolves Azure#3363
1 parent 1df4e35 commit 21a835b

File tree

15 files changed

+136
-110
lines changed

15 files changed

+136
-110
lines changed

sdk/core/azure_core/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
### Features Added
66

7+
- Added `continuation_token` to `PagerOptions`.
8+
79
### Breaking Changes
810

9-
The callback for `Pager::with_callback` has a `PagerOptions` second parameter rather than a `Context` parameter.
11+
- Changed `Pager::with_callback` to take a `PagerOptions` as the second parameter rather than a `Context` parameter.
12+
- Removed `ItemIterator::with_continuation_token()`. Pass a continuation token to `PagerOptions::continuation_token` instead.
13+
- Removed `PageIterator::with_continuation_token()`. Pass a continuation token to `PagerOptions::continuation_token` instead.
1014

1115
### Bugs Fixed
1216

sdk/core/azure_core/src/http/pager.rs

Lines changed: 79 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,44 @@ pub type Pager<P, F = JsonFormat> = ItemIterator<Response<P, F>>;
211211
pub struct PagerOptions<'a> {
212212
/// Context for HTTP requests made by the [`Pager`].
213213
pub context: Context<'a>,
214+
215+
/// Optional continuation token or next link to resume paging.
216+
///
217+
/// # Examples
218+
///
219+
/// ``` no_run
220+
/// use azure_core::http::pager::PagerOptions;
221+
/// use azure_identity::DeveloperToolsCredential;
222+
/// use azure_security_keyvault_secrets::{
223+
/// models::SecretClientListSecretPropertiesOptions,
224+
/// SecretClient,
225+
/// };
226+
/// use futures::stream::TryStreamExt as _;
227+
///
228+
/// # #[tokio::main]
229+
/// # async fn main() -> azure_core::Result<()> {
230+
/// let client = SecretClient::new("https://my-vault.vault.azure.net", DeveloperToolsCredential::new(None)?, None)?;
231+
///
232+
/// // Start the first pager at the first page.
233+
/// let mut pager = client.list_secret_properties(None)?;
234+
///
235+
/// // Continue the second pager from where the first pager left off,
236+
/// // which is the first page in this example.
237+
/// let options = SecretClientListSecretPropertiesOptions {
238+
/// method_options: PagerOptions {
239+
/// continuation_token: pager.continuation_token(),
240+
/// ..Default::default()
241+
/// },
242+
/// ..Default::default()
243+
/// };
244+
/// let mut pager = client.list_secret_properties(Some(options))?;
245+
/// while let Some(secret) = pager.try_next().await? {
246+
/// println!("{:?}", secret.id);
247+
/// }
248+
/// # Ok(())
249+
/// # }
250+
/// ```
251+
pub continuation_token: Option<String>,
214252
}
215253

216254
#[cfg(not(target_arch = "wasm32"))]
@@ -405,12 +443,15 @@ impl<P: Page> ItemIterator<P> {
405443
<C as FromStr>::Err: std::error::Error,
406444
{
407445
let options = options.unwrap_or_default();
408-
let next_token = Arc::new(Mutex::new(None::<String>));
446+
447+
// Start from the optional `PagerOptions::continuation_token`.
448+
let continuation_token = options.continuation_token.clone();
449+
let next_token = Arc::new(Mutex::new(continuation_token.clone()));
409450
let stream = iter_from_callback(make_request, options, next_token.clone());
410451

411452
Self {
412453
stream: Box::pin(stream),
413-
continuation_token: None,
454+
continuation_token,
414455
next_token,
415456
current: None,
416457
}
@@ -456,52 +497,9 @@ impl<P: Page> ItemIterator<P> {
456497
}
457498
}
458499

459-
/// Start the `ItemIterator` at the page referenced by `continuation_token`.
460-
///
461-
/// You should call this before iterating the [`Stream`] or results may be unpredictable.
462-
/// Iteration of items will start from the beginning on the current page until _after_ all items are iterated.
463-
/// It does not continue on the next page until you call `next()` after the last item in the current page
464-
/// because of how iterators are implemented. This may yield duplicates but will reduce the likelihood of skipping items instead.
465-
///
466-
/// # Examples
467-
///
468-
/// Using a result of a call to [`ItemIterator::continuation_token`] in another process, you can create a new `ItemIterator`
469-
/// that, when first iterated, will get the next page of results.
470-
///
471-
/// ``` no_run
472-
/// use azure_identity::DeveloperToolsCredential;
473-
/// use azure_security_keyvault_secrets::SecretClient;
474-
/// use futures::stream::TryStreamExt as _;
475-
///
476-
/// # #[tokio::main]
477-
/// # async fn main() -> azure_core::Result<()> {
478-
/// let client = SecretClient::new("https://my-vault.vault.azure.net", DeveloperToolsCredential::new(None)?, None)?;
479-
///
480-
/// // Start the first pager at the first page.
481-
/// let mut pager = client.list_secret_properties(None)?;
482-
///
483-
/// // Continue the second pager from where the first pager left off,
484-
/// // which is the first page in this example.
485-
/// let mut pager = client.list_secret_properties(None)?
486-
/// .with_continuation_token(Some("continuation_token_from_another_pager".into()));
487-
///
488-
/// while let Some(secret) = pager.try_next().await? {
489-
/// println!("{:?}", secret.id);
490-
/// }
491-
/// # Ok(())
492-
/// # }
493-
/// ```
494-
pub fn with_continuation_token(self, continuation_token: Option<String>) -> Self {
495-
// Set the next_token because that's what is passed to iter_from_callback to get the initial page.
496-
if let Ok(mut token) = self.next_token.lock() {
497-
*token = continuation_token;
498-
}
499-
self
500-
}
501-
502500
/// Gets the continuation token for the current page.
503501
///
504-
/// Pass this to [`ItemIterator::with_continuation_token`] to create a `ItemIterator` that, when first iterated,
502+
/// Pass this in [`PagerOptions::continuation_token`] to create a `ItemIterator` that, when first iterated,
505503
/// will return the current page until _after_ all items are iterated.
506504
/// It does not continue on the next page until you call `next()` after the last item in the current page
507505
/// because of how iterators are implemented. This may yield duplicates but will reduce the likelihood of skipping items instead.
@@ -560,7 +558,9 @@ impl<P: Page> futures::Stream for ItemIterator<P> {
560558

561559
impl<P: Page> fmt::Debug for ItemIterator<P> {
562560
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563-
f.debug_struct("ItemIterator").finish_non_exhaustive()
561+
f.debug_struct("ItemIterator")
562+
.field("continuation_token", &self.continuation_token)
563+
.finish_non_exhaustive()
564564
}
565565
}
566566

@@ -707,7 +707,9 @@ impl<P> PageIterator<P> {
707707
<C as FromStr>::Err: std::error::Error,
708708
{
709709
let options = options.unwrap_or_default();
710-
let continuation_token = Arc::new(Mutex::new(None::<String>));
710+
711+
// Start from the optional `PagerOptions::continuation_token`.
712+
let continuation_token = Arc::new(Mutex::new(options.continuation_token.clone()));
711713
let stream = iter_from_callback(make_request, options, continuation_token.clone());
712714

713715
Self {
@@ -732,54 +734,9 @@ impl<P> PageIterator<P> {
732734
}
733735
}
734736

735-
/// Start the `PageIterator` at the page referenced by `continuation_token`.
736-
///
737-
/// You should call this before iterating the [`Stream`] or results may be unpredictable.
738-
///
739-
/// # Examples
740-
///
741-
/// Using a result of a call to [`PageIterator::continuation_token`] in another process, you can create a new `PageIterator`
742-
/// that, when first iterated, will get the next page of results.
743-
///
744-
/// ``` no_run
745-
/// use azure_identity::DeveloperToolsCredential;
746-
/// use azure_security_keyvault_secrets::SecretClient;
747-
/// use futures::stream::TryStreamExt as _;
748-
///
749-
/// # #[tokio::main]
750-
/// # async fn main() -> azure_core::Result<()> {
751-
/// let client = SecretClient::new("https://my-vault.vault.azure.net", DeveloperToolsCredential::new(None)?, None)?;
752-
///
753-
/// // Start the first pager at the first page.
754-
/// let mut pager = client.list_secret_properties(None)?
755-
/// .into_pages();
756-
///
757-
/// // Continue the second pager from where the first pager left off,
758-
/// // which is the first page in this example.
759-
/// let mut pager = client.list_secret_properties(None)?
760-
/// .into_pages()
761-
/// .with_continuation_token("continuation_token_from_another_pager".to_string());
762-
///
763-
/// while let Some(secrets) = pager.try_next().await? {
764-
/// let secrets = secrets.into_model()?;
765-
/// for secret in secrets.value {
766-
/// println!("{:?}", secret.id);
767-
/// }
768-
/// }
769-
/// # Ok(())
770-
/// # }
771-
/// ```
772-
pub fn with_continuation_token(self, continuation_token: String) -> Self {
773-
if let Ok(mut token_guard) = self.continuation_token.lock() {
774-
*token_guard = Some(continuation_token);
775-
}
776-
777-
self
778-
}
779-
780737
/// Gets the continuation token for the current page.
781738
///
782-
/// Pass this to [`PageIterator::with_continuation_token`] to create a `PageIterator` that, when first iterated,
739+
/// Pass this to [`PagerOptions::continuation_token`] to create a `PageIterator` that, when first iterated,
783740
/// will return the next page. You can use this to page results across separate processes.
784741
pub fn continuation_token(&self) -> Option<String> {
785742
if let Ok(token) = self.continuation_token.lock() {
@@ -1165,9 +1122,13 @@ mod tests {
11651122
assert_eq!(continuation_token, "next-token-1");
11661123

11671124
// Create the second PageIterator.
1168-
let mut second_pager: PageIterator<Response<Page>> =
1169-
PageIterator::from_callback(make_three_page_callback(), None)
1170-
.with_continuation_token(continuation_token);
1125+
let mut second_pager: PageIterator<Response<Page>> = PageIterator::from_callback(
1126+
make_three_page_callback(),
1127+
Some(PagerOptions {
1128+
continuation_token: Some(continuation_token),
1129+
..Default::default()
1130+
}),
1131+
);
11711132

11721133
// Should start with link to second page.
11731134
assert_eq!(
@@ -1348,8 +1309,13 @@ mod tests {
13481309
assert_eq!(continuation_token, None);
13491310

13501311
// Create the second ItemIterator with continuation token.
1351-
let mut second_pager: Pager<Page> = Pager::from_callback(make_three_page_callback(), None)
1352-
.with_continuation_token(continuation_token);
1312+
let mut second_pager: Pager<Page> = Pager::from_callback(
1313+
make_three_page_callback(),
1314+
Some(PagerOptions {
1315+
continuation_token,
1316+
..Default::default()
1317+
}),
1318+
);
13531319

13541320
// Should start with link to first page.
13551321
assert_eq!(second_pager.continuation_token(), None);
@@ -1422,8 +1388,13 @@ mod tests {
14221388
assert_eq!(continuation_token.as_deref(), Some("next-token-1"));
14231389

14241390
// Create the second ItemIterator with continuation token.
1425-
let mut second_pager: Pager<Page> = Pager::from_callback(make_three_page_callback(), None)
1426-
.with_continuation_token(continuation_token);
1391+
let mut second_pager: Pager<Page> = Pager::from_callback(
1392+
make_three_page_callback(),
1393+
Some(PagerOptions {
1394+
continuation_token,
1395+
..Default::default()
1396+
}),
1397+
);
14271398

14281399
// When continuing with a continuation token, we should start over from the
14291400
// beginning of the current page (second page), not where we left off.
@@ -1475,8 +1446,13 @@ mod tests {
14751446
assert_eq!(continuation_token, None);
14761447

14771448
// Create the second ItemIterator with continuation token.
1478-
let mut second_pager: Pager<Page> = Pager::from_callback(make_three_page_callback(), None)
1479-
.with_continuation_token(continuation_token);
1449+
let mut second_pager: Pager<Page> = Pager::from_callback(
1450+
make_three_page_callback(),
1451+
Some(PagerOptions {
1452+
continuation_token,
1453+
..Default::default()
1454+
}),
1455+
);
14801456

14811457
// When continuing with a continuation token after finishing a page, we should
14821458
// start from the beginning of the current page.

sdk/cosmos/azure_data_cosmos/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
### Features Added
66

7+
- Added `continuation_token` to `PagerOptions` for methods that return a `Pager`.
8+
79
### Breaking Changes
810

11+
- Removed `Pager::with_continuation_token()` for methods that return a `Pager`.
12+
913
### Bugs Fixed
1014

1115
### Other Changes

sdk/cosmos/azure_data_cosmos/src/pipeline/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ impl CosmosPipeline {
117117
let pipeline = self.pipeline.clone();
118118
let options = PagerOptions {
119119
context: ctx.with_value(resource_link).into_owned(),
120+
..Default::default()
120121
};
121122
Ok(FeedPager::from_callback(
122123
move |continuation, pager_options| {

sdk/keyvault/azure_security_keyvault_certificates/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
### Features Added
66

7+
- Added `continuation_token` to `PagerOptions` for methods that return a `Pager`.
8+
79
### Breaking Changes
810

11+
- Removed `Pager::with_continuation_token()` for methods that return a `Pager`.
12+
913
### Bugs Fixed
1014

1115
### Other Changes

sdk/keyvault/azure_security_keyvault_certificates/src/generated/models/method_options.rs

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/keyvault/azure_security_keyvault_keys/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
### Features Added
66

7+
- Added `continuation_token` to `PagerOptions` for methods that return a `Pager`.
8+
79
### Breaking Changes
810

11+
- Removed `Pager::with_continuation_token()` for methods that return a `Pager`.
12+
913
### Bugs Fixed
1014

1115
### Other Changes

sdk/keyvault/azure_security_keyvault_keys/src/generated/models/method_options.rs

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/keyvault/azure_security_keyvault_secrets/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
### Features Added
66

7+
- Added `continuation_token` to `PagerOptions` for methods that return a `Pager`.
8+
79
### Breaking Changes
810

11+
- Removed `Pager::with_continuation_token()` for methods that return a `Pager`.
12+
913
### Bugs Fixed
1014

1115
### Other Changes

0 commit comments

Comments
 (0)