Skip to content

Commit b1ec1ea

Browse files
Implement pagination for receiving MCR catalog items (#1870)
1 parent 73f3118 commit b1ec1ea

File tree

2 files changed

+83
-14
lines changed

2 files changed

+83
-14
lines changed

src/code/ContainerRegistryServerAPICalls.cs

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
using Microsoft.PowerShell.PSResourceGet.UtilClasses;
54
using System;
65
using System.Collections;
76
using System.Collections.Generic;
7+
using System.Collections.ObjectModel;
88
using System.IO;
9-
using System.Net.Http;
10-
using NuGet.Versioning;
11-
using System.Threading.Tasks;
12-
using System.Net;
9+
using System.Linq;
1310
using System.Management.Automation;
14-
using Newtonsoft.Json.Linq;
15-
using Newtonsoft.Json;
16-
using System.Collections.ObjectModel;
11+
using System.Net;
12+
using System.Net.Http;
1713
using System.Net.Http.Headers;
18-
using System.Linq;
19-
using Microsoft.PowerShell.PSResourceGet.Cmdlets;
20-
using System.Text;
2114
using System.Security.Cryptography;
15+
using System.Text;
2216
using System.Text.Json;
17+
using System.Text.RegularExpressions;
18+
using System.Threading.Tasks;
19+
using Microsoft.PowerShell.PSResourceGet.Cmdlets;
20+
using Microsoft.PowerShell.PSResourceGet.UtilClasses;
21+
using Newtonsoft.Json;
22+
using Newtonsoft.Json.Linq;
23+
using NuGet.Versioning;
2324

2425
namespace Microsoft.PowerShell.PSResourceGet
2526
{
@@ -688,7 +689,7 @@ internal JObject FindAllRepositories(string containerRegistryAccessToken, out Er
688689
_cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::FindAllRepositories()");
689690
string repositoryListUrl = string.Format(containerRegistryRepositoryListTemplate, Registry);
690691
var defaultHeaders = GetDefaultHeaders(containerRegistryAccessToken);
691-
return GetHttpResponseJObjectUsingDefaultHeaders(repositoryListUrl, HttpMethod.Get, defaultHeaders, out errRecord);
692+
return GetHttpResponseJObjectUsingDefaultHeaders(repositoryListUrl, HttpMethod.Get, defaultHeaders, out errRecord, usePagination: true);
692693
}
693694

694695
/// <summary>
@@ -937,7 +938,7 @@ internal async Task<HttpContent> GetHttpContentResponseJObject(string url, Colle
937938
/// <summary>
938939
/// Get response object when using default headers in the request.
939940
/// </summary>
940-
internal JObject GetHttpResponseJObjectUsingDefaultHeaders(string url, HttpMethod method, Collection<KeyValuePair<string, string>> defaultHeaders, out ErrorRecord errRecord)
941+
internal JObject GetHttpResponseJObjectUsingDefaultHeaders(string url, HttpMethod method, Collection<KeyValuePair<string, string>> defaultHeaders, out ErrorRecord errRecord, bool usePagination = false)
941942
{
942943
_cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::GetHttpResponseJObjectUsingDefaultHeaders()");
943944
try
@@ -946,7 +947,8 @@ internal JObject GetHttpResponseJObjectUsingDefaultHeaders(string url, HttpMetho
946947
HttpRequestMessage request = new HttpRequestMessage(method, url);
947948
SetDefaultHeaders(defaultHeaders);
948949

949-
return SendRequestAsync(request).GetAwaiter().GetResult();
950+
var response = usePagination ? SendRequestAsyncWithPagination(request) : SendRequestAsync(request);
951+
return response.GetAwaiter().GetResult();
950952
}
951953
catch (ResourceNotFoundException e)
952954
{
@@ -1152,6 +1154,72 @@ private async Task<JObject> SendRequestAsync(HttpRequestMessage message)
11521154
return JsonConvert.DeserializeObject<JObject>(await response.Content.ReadAsStringAsync());
11531155
}
11541156

1157+
private async Task<JObject> SendRequestAsyncWithPagination(HttpRequestMessage initialMessage)
1158+
{
1159+
HttpResponseMessage response;
1160+
string nextUrl = initialMessage.RequestUri.ToString();
1161+
string urlBase = initialMessage.RequestUri.Scheme + "://" + initialMessage.RequestUri.Host;
1162+
JObject finalResult = new JObject();
1163+
JArray allRepositories = new JArray();
1164+
1165+
do
1166+
{
1167+
var message = new HttpRequestMessage(HttpMethod.Get, nextUrl);
1168+
try
1169+
{
1170+
response = await _sessionClient.SendAsync(message);
1171+
}
1172+
catch (Exception e)
1173+
{
1174+
throw new SendRequestException($"Error occurred while sending request to Container Registry server with: {e.GetType()} '{e.Message}'", e);
1175+
}
1176+
1177+
switch (response.StatusCode)
1178+
{
1179+
case HttpStatusCode.OK:
1180+
break;
1181+
case HttpStatusCode.Unauthorized:
1182+
throw new UnauthorizedException($"Response returned status code: {response.ReasonPhrase}.");
1183+
case HttpStatusCode.NotFound:
1184+
throw new ResourceNotFoundException($"Response returned status code package: {response.ReasonPhrase}.");
1185+
default:
1186+
throw new Exception($"Response returned error with status code {response.StatusCode}: {response.ReasonPhrase}.");
1187+
}
1188+
1189+
var content = await response.Content.ReadAsStringAsync();
1190+
var json = JObject.Parse(content);
1191+
var repositories = json["repositories"] as JArray;
1192+
if (repositories != null)
1193+
{
1194+
allRepositories.Merge(repositories);
1195+
}
1196+
1197+
// Check for Link header to continue pagination
1198+
if (response.Headers.TryGetValues("Link", out var linkHeaders))
1199+
{
1200+
var linkHeader = string.Join(",", linkHeaders);
1201+
var match = Regex.Match(linkHeader, @"<([^>]+)>;\s*rel=""next""");
1202+
var nextUrlPart = match.Success ? match.Groups[1].Value : null;
1203+
if (!string.IsNullOrEmpty(nextUrlPart))
1204+
{
1205+
nextUrl = urlBase + nextUrlPart;
1206+
}
1207+
else
1208+
{
1209+
nextUrl = null;
1210+
}
1211+
}
1212+
else
1213+
{
1214+
nextUrl = null;
1215+
}
1216+
1217+
} while (!string.IsNullOrEmpty(nextUrl));
1218+
1219+
finalResult["repositories"] = allRepositories;
1220+
return finalResult;
1221+
}
1222+
11551223
/// <summary>
11561224
/// Send request to get response headers.
11571225
/// </summary>

test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
175175
It "Should find all resources given Name '*'" {
176176
# FindAll()
177177
$res = Find-PSResource -Name "*" -Repository $ACRRepoName -ErrorVariable err -ErrorAction SilentlyContinue
178+
$err | Should -BeNullOrEmpty
178179
$res | Should -Not -BeNullOrEmpty
179180
$res.Count | Should -BeGreaterThan 0
180181
}

0 commit comments

Comments
 (0)