From 7a59d02f8a3ba0200f318a5fbdd1afb924a98b9e Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Tue, 1 Sep 2026 16:41:46 +0200 Subject: [PATCH 01/10] implement paging for listing apps and flights --- MSStore.API/Packaged/Models/PagedResponse.cs | 2 ++ MSStore.API/Packaged/StorePackagedAPI.cs | 28 +++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/MSStore.API/Packaged/Models/PagedResponse.cs b/MSStore.API/Packaged/Models/PagedResponse.cs index 9213ee1..0ab27f0 100644 --- a/MSStore.API/Packaged/Models/PagedResponse.cs +++ b/MSStore.API/Packaged/Models/PagedResponse.cs @@ -2,11 +2,13 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Text.Json.Serialization; namespace MSStore.API.Packaged.Models { public class PagedResponse { + [JsonPropertyName("@nextLink")] public string? NextLink { get; set; } public List? Value { get; set; } public int TotalCount { get; set; } diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 585870c..26e34ac 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -219,8 +219,18 @@ public async Task> GetApplicationsAsync(CancellationT { try { - var devCenterApplicationsResponse = await GetDevCenterApplicationsAsync(0, 100, ct); // TODO: pagination - return devCenterApplicationsResponse.Value ?? []; + PagedResponse? lastDevCenterApplicationsResponse = null; + var result = new List(); + int skip = 0; + const int top = 100; + do + { + lastDevCenterApplicationsResponse = await GetDevCenterApplicationsAsync(skip, top, ct); + skip += top; + result.AddRange(lastDevCenterApplicationsResponse.Value ?? []); + } + while (lastDevCenterApplicationsResponse?.NextLink is not null); + return result; } catch (Exception error) { @@ -384,8 +394,18 @@ public async Task> GetFlightsAsync(string productId, Cance { try { - var devCenterFlightsResponse = await GetFlightsAsync(productId, 0, 100, ct); // TODO: pagination - return devCenterFlightsResponse.Value ?? []; + PagedResponse? lastDevCenterFlightsResponse = null; + var result = new List(); + int skip = 0; + const int top = 100; + do + { + lastDevCenterFlightsResponse = await GetFlightsAsync(productId, skip, top, ct); + skip += top; + result.AddRange(lastDevCenterFlightsResponse.Value ?? []); + } + while (lastDevCenterFlightsResponse?.NextLink is not null); + return result; } catch (Exception error) { From f21f813f69256f8cb0548692c449669abb110836 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Thu, 3 Sep 2026 11:14:25 +0200 Subject: [PATCH 02/10] addresss feedback --- MSStore.API/Packaged/StorePackagedAPI.cs | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 26e34ac..7800ff3 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Net.Http; +using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; @@ -219,18 +221,7 @@ public async Task> GetApplicationsAsync(CancellationT { try { - PagedResponse? lastDevCenterApplicationsResponse = null; - var result = new List(); - int skip = 0; - const int top = 100; - do - { - lastDevCenterApplicationsResponse = await GetDevCenterApplicationsAsync(skip, top, ct); - skip += top; - result.AddRange(lastDevCenterApplicationsResponse.Value ?? []); - } - while (lastDevCenterApplicationsResponse?.NextLink is not null); - return result; + return await GetAllObjectsPagedAsync(pageFunc: GetDevCenterApplicationsAsync, ct).ToListAsync(ct); } catch (Exception error) { @@ -394,18 +385,7 @@ public async Task> GetFlightsAsync(string productId, Cance { try { - PagedResponse? lastDevCenterFlightsResponse = null; - var result = new List(); - int skip = 0; - const int top = 100; - do - { - lastDevCenterFlightsResponse = await GetFlightsAsync(productId, skip, top, ct); - skip += top; - result.AddRange(lastDevCenterFlightsResponse.Value ?? []); - } - while (lastDevCenterFlightsResponse?.NextLink is not null); - return result; + return await GetAllObjectsPagedAsync((skip, top, ct) => GetFlightsAsync(productId, skip, top, ct), ct).ToListAsync(ct); } catch (Exception error) { @@ -683,5 +663,25 @@ public async Task FinalizePackageRolloutAsync(string productId, SourceGenerationContext.GetCustom().PackageRollout, ct); } + + private static async IAsyncEnumerable GetAllObjectsPagedAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) + { + int skip = 0; + int top = 100; + PagedResponse? lastPage; + do + { + ct.ThrowIfCancellationRequested(); + + lastPage = await pageFunc(skip, top, ct); + skip += top; + + foreach (var item in lastPage.Value ?? []) + { + yield return item; + } + } + while (!string.IsNullOrEmpty(lastPage.NextLink) && lastPage.Value?.Count == top); + } } } From df235bde23d43708e0f428d5cf6747f3f9fb3ac0 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Thu, 3 Sep 2026 11:31:03 +0200 Subject: [PATCH 03/10] change top to const --- MSStore.API/Packaged/StorePackagedAPI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 7800ff3..c303c3d 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -667,7 +667,7 @@ public async Task FinalizePackageRolloutAsync(string productId, private static async IAsyncEnumerable GetAllObjectsPagedAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) { int skip = 0; - int top = 100; + const int top = 10; PagedResponse? lastPage; do { @@ -681,7 +681,7 @@ private static async IAsyncEnumerable GetAllObjectsPagedAsync(Func 0, Value.Count: top }); } } } From 9be1ab3a81cc7a62d93cdb6d0c4a73d67b6b2116 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Thu, 3 Sep 2026 11:33:28 +0200 Subject: [PATCH 04/10] change const back to 100 --- MSStore.API/Packaged/StorePackagedAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index c303c3d..81f4978 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -667,7 +667,7 @@ public async Task FinalizePackageRolloutAsync(string productId, private static async IAsyncEnumerable GetAllObjectsPagedAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) { int skip = 0; - const int top = 10; + const int top = 100; PagedResponse? lastPage; do { From cccd4c19b8aa201bdb3fb0b151f3a7e783361d18 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Sun, 6 Sep 2026 12:17:42 +0200 Subject: [PATCH 05/10] fix naming --- MSStore.API/Packaged/StorePackagedAPI.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 81f4978..d1cd2fc 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -221,7 +221,7 @@ public async Task> GetApplicationsAsync(CancellationT { try { - return await GetAllObjectsPagedAsync(pageFunc: GetDevCenterApplicationsAsync, ct).ToListAsync(ct); + return await GetAllPagesAsync(pageFunc: GetDevCenterApplicationsAsync, ct).ToListAsync(ct); } catch (Exception error) { @@ -385,7 +385,7 @@ public async Task> GetFlightsAsync(string productId, Cance { try { - return await GetAllObjectsPagedAsync((skip, top, ct) => GetFlightsAsync(productId, skip, top, ct), ct).ToListAsync(ct); + return await GetAllPagesAsync((skip, top, ct) => GetFlightsAsync(productId, skip, top, ct), ct).ToListAsync(ct); } catch (Exception error) { @@ -664,7 +664,7 @@ public async Task FinalizePackageRolloutAsync(string productId, ct); } - private static async IAsyncEnumerable GetAllObjectsPagedAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) + private static async IAsyncEnumerable GetAllPagesAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) { int skip = 0; const int top = 100; From 5bfd1d4e432eb2b993ad93de91153a8b13f634ea Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Sun, 6 Sep 2026 12:24:55 +0200 Subject: [PATCH 06/10] rename named argument --- MSStore.API/Packaged/StorePackagedAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index d1cd2fc..ed0c5f5 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -221,7 +221,7 @@ public async Task> GetApplicationsAsync(CancellationT { try { - return await GetAllPagesAsync(pageFunc: GetDevCenterApplicationsAsync, ct).ToListAsync(ct); + return await GetAllPagesAsync(GetDevCenterApplicationsAsync, ct).ToListAsync(ct); } catch (Exception error) { From 695155398c9f749ad52bf686e3cfc4898d0884af Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Wed, 9 Sep 2026 10:33:24 +0200 Subject: [PATCH 07/10] fix --- MSStore.API/Packaged/StorePackagedAPI.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index ed0c5f5..7547b23 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -385,7 +385,7 @@ public async Task> GetFlightsAsync(string productId, Cance { try { - return await GetAllPagesAsync((skip, top, ct) => GetFlightsAsync(productId, skip, top, ct), ct).ToListAsync(ct); + return await GetAllPagesAsync((skip, top, token) => GetFlightsAsync(productId, skip, top, token), ct).ToListAsync(ct); } catch (Exception error) { @@ -667,21 +667,23 @@ public async Task FinalizePackageRolloutAsync(string productId, private static async IAsyncEnumerable GetAllPagesAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) { int skip = 0; - const int top = 100; + const int top = 10; PagedResponse? lastPage; do { ct.ThrowIfCancellationRequested(); lastPage = await pageFunc(skip, top, ct); - skip += top; + skip += lastPage.Value?.Count ?? 0; foreach (var item in lastPage.Value ?? []) { + ct.ThrowIfCancellationRequested(); + yield return item; } } - while (lastPage is { NextLink.Length: > 0, Value.Count: top }); + while (!string.IsNullOrEmpty(lastPage.NextLink) && skip < lastPage.TotalCount); } } } From 0f5f4bb4b5114bed34fd81d72a79adf6ac2b7205 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Wed, 9 Sep 2026 10:39:07 +0200 Subject: [PATCH 08/10] back to 100 --- MSStore.API/Packaged/StorePackagedAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 7547b23..b7deecd 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -667,7 +667,7 @@ public async Task FinalizePackageRolloutAsync(string productId, private static async IAsyncEnumerable GetAllPagesAsync(Func>> pageFunc, [EnumeratorCancellation] CancellationToken ct = default) { int skip = 0; - const int top = 10; + const int top = 100; PagedResponse? lastPage; do { From 02aec9066c412edeaa301ad21fe9b091bba842f7 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Wed, 9 Sep 2026 12:42:40 +0200 Subject: [PATCH 09/10] improve --- MSStore.API/Packaged/StorePackagedAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index b7deecd..4119d47 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -683,7 +683,7 @@ private static async IAsyncEnumerable GetAllPagesAsync(Func 0 && skip < lastPage.TotalCount); } } } From 7da0006230f9017dfefd513f7d20a15b452aa030 Mon Sep 17 00:00:00 2001 From: Dave Smits Date: Thu, 10 Sep 2026 09:05:43 +0200 Subject: [PATCH 10/10] change guard --- MSStore.API/Packaged/StorePackagedAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSStore.API/Packaged/StorePackagedAPI.cs b/MSStore.API/Packaged/StorePackagedAPI.cs index 4119d47..67e062e 100644 --- a/MSStore.API/Packaged/StorePackagedAPI.cs +++ b/MSStore.API/Packaged/StorePackagedAPI.cs @@ -683,7 +683,7 @@ private static async IAsyncEnumerable GetAllPagesAsync(Func 0 && skip < lastPage.TotalCount); + while (!string.IsNullOrEmpty(lastPage.NextLink) && lastPage.Value?.Count > 0 && (lastPage.TotalCount <= 0 || skip < lastPage.TotalCount)); } } }