-
Notifications
You must be signed in to change notification settings - Fork 75
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
Paging support: byPage not exposing settings parameter #1199
Comments
This looks like a valid bug. We need to pass the settings in the generated byPage method. |
I had a teams meeting with @deyaaeldeen. Here is the gist of the conversation:
public list(options?: VirtualMachineImageTemplatesListOptionalParams): PagedAsyncIterableIterator<ImageTemplate> {
const iter = this.listPagingAll(options);
return {
next() {
return iter.next();
},
[Symbol.asyncIterator]() {
return this;
},
byPage: () => {
return this.listPagingPage(options);
}
};
}
........
byPage: (settings?: PageSettings) => {
......
/**
* An interface that tracks the settings for paged iteration
*/
export interface PageSettings {
/**
* The token that keeps track of where to continue the iterator
*/
continuationToken?: string;
/**
* The size of the page during paged iteration
*/
maxPageSize?: number;
}
During the discussion, Deyaa Pointed that there is an abstraction created in the @deyaaeldeen Am I clear in formulating our discussion? Is there anything I missed? |
Yes! thanks for summarizing! one nit:
for getting the next page, typically named |
@danielgerlag @deyaaeldeen any updates on this? |
@sarangan12, I don't have an update for the customers on this issue, could you please provide one? |
Exposing Unfortunately, since byPage() already returns export interface PagedResult<TPage> {
page: TPage;
continuationToken?: string;
}
export interface PagedAsyncIterableIterator<TElement, TPage = TElement[], TPageSettings = PageSettings> {
[Symbol.asyncIterator](): PagedAsyncIterableIterator<TElement, TPage, TPageSettings>;
byPage: (settings?: TPageSettings) => AsyncIterableIterator<TPage>;
byPagedResult: (settings?: TPageSettings) => AsyncIterableIterator<PagedResult<TPage>>;
next(): Promise<IteratorResult<TElement>>;
} The only alternative I can think of is maybe having an abstraction where the generated client annotates this information in a WeakMap and the consumer uses a helper method to check if the result page has an associated continuationToken or not. @deyaaeldeen @MRayermannMSFT any thoughts on how you'd like this to be implemented? |
I think what I'm asking is how we'd solve #1326 while addressing this item |
@xirzec I agree, it is not an ideal situation. Extending the @MRayermannMSFT I am curious to hear your feedback on my proposed alternative above and whether is it sufficient for your use case. |
Could a class be created that takes |
Ya this could work. Maybe named
I don't not-like this either. |
@bwateratmsft Unless the construct that took the iterator had some secret means of accessing associated page data, I'm not sure if I understand how that would work. I'm leaning back towards somehow tying this information into the lifetime of How would we feel about something like this: import { FooClient, getContinuationToken } from "@azure/foo";
const client = new FooClient();
const iterator = client.listFoo().byPage();
const firstPage = await iterator.next();
const continuationToken = getContinuationToken(firstPage);
const laterIterator = client.listFoo().byPage({continuationToken});
// laterIterator starts where iterator left off |
Not saying it's necessarily a good idea, but TypeScript / JavaScript lets you do anything. Such a class could peek under the hood to get what it needed. |
I'm curious as to what |
@MRayermannMSFT I have an initial draft implementation here: https://github.com/Azure/autorest.typescript/blob/e45434438d16edac5c63a7ddd9a4322e9a25e025/packages/autorest.typescript/src/pagingHelper.ts It's the same function for all pages, so the input parameter is not typed to any one particular page shape. |
@MRayermannMSFT just merged this, let me know if you have any feedback or issues with using it. |
@xirzec what's the best way for me to get my hands on the changes so I can provide you feedback? I'm not seeing any differences with the packages I'm using, so I'm guessing they probably all need to be regenerated? |
@MRayermannMSFT yeah, we should be able to regenerate with the latest |
Hey @xirzec , for the getting of initial feedback, regenerating |
@qiaozha - could you help with regenerating this package? |
Hi @MRayermannMSFT , We would like you help us verify whether the package works as your expected before release, you can also learn more by this pr: Azure/azure-sdk-for-js#23688 Thanks. |
First, let me show you the code I ended up with for context: And here's my feedback:
|
Hey @MRayermannMSFT thanks for trying this out! I hear you on the second point of feedback and agreed that we could at the very least improve the comment since it's not really feasible to scope this down to a type given you could have many different pageables in an SDK each with different page types. I always struggle with how to speak to iterators since I feel like most JS devs don't really understand or think about the iteration protocol and simply Would calling it I rewrote your above example slightly in a way that I think shows what is going on a bit better. One of our struggles with pagination is that it's not obvious that the list operation doesn't return a promise, but rather an object that implements the async iterator protocol: const iterator = client.resourceGroups.list(options).byPage({continuationToken: options?.continuationToken);
const iteratorResult = await iterator.next();
if (!iteratorResult.done) {
const nextPage = iteratorResult.value;
return { resourceGroups: nextPage, continuationToken: getContinuationToken(nextPage) };
} else {
return { resourceGroups: [] };
} To your other point (about |
Haha, I think when we migrated from the super old SDKs we just assumed methods named like
Yes I think that would be clearer. Even more clearer would be if the comment put "value" and "byPage" in back-tics (`), and maybe even with a leading dot on "value". So like: /**
* ...the last `.value` produced by the `byPage` iterator...
*/
function getContinuationToken() {} |
For the API I am using....yes I think that sounds accurate. For all Azure APIs, no idea! 😅 Maybe another way to look at it is to not duplicate query params? |
The problem is we don't know anything about
And at runtime while it may sound very sane to not duplicate, we do have APIs that allow for specifying the same query parameter multiple times (perhaps treating them as an OR condition) with different services wanting these values either concatenated (with comma, semicolon, pipes, or something custom!) or literally repeated e.g. (foo=1&foo=2) -- so the poor logic in ServiceClient really can't ascertain what was intended just from looking at the operation spec and operation args. |
Sure, if the "continuation token" is a next link, then I think saying "we will use that link exactly and not add anything to it" is reasonable. I was merely thinking about APIs where the "continuation token" is not a link. Which in that case it hopefully isn't called |
Ah, yeah the pagination contract tries to abstract this better, but since x-ms-pageable I believe only works today with next links, we can probably use that as the hint to avoid query params. |
When generating an operation with paging, it mostly works well, except the
byPage
method that is generated does not pass the settings parameter down as defined on thePagedAsyncIterableIterator
interface.Generate code:
The text was updated successfully, but these errors were encountered: