66using System . Net . Http . Headers ;
77using System . Text . RegularExpressions ;
88using System . Threading . Tasks ;
9+ using ARMExplorer . Model ;
910using Newtonsoft . Json ;
1011using Newtonsoft . Json . Linq ;
1112
@@ -14,6 +15,7 @@ namespace ARMExplorer.Controllers
1415 public class ArmRepository : IArmRepository
1516 {
1617 private readonly IHttpClientWrapper _clientWrapper ;
18+ private readonly int _maxNextLinkDepth = 5 ;
1719
1820 public ArmRepository ( IHttpClientWrapper clientWrapper )
1921 {
@@ -40,17 +42,63 @@ public async Task<IList<string>> GetSubscriptionIdsAsync(HttpRequestMessage requ
4042 return subscriptionIds ;
4143 }
4244
45+ private static bool AddResourceToList ( IEnumerable < ArmResource > resources , ISet < ArmResource > allResources )
46+ {
47+ var initalCount = allResources . Count ;
48+
49+ foreach ( var resource in resources )
50+ {
51+ allResources . Add ( resource ) ;
52+ }
53+
54+ var updatedCount = allResources . Count ;
55+
56+ return updatedCount > initalCount ;
57+ }
58+
59+ private async Task < HashSet < ArmResource > > GetResources ( HttpRequestMessage requestMessage , string getResourcesUrl )
60+ {
61+ var allResources = new HashSet < ArmResource > ( ) ;
62+ var currentNextLinkDepth = 0 ;
63+
64+ while ( ! string . IsNullOrEmpty ( getResourcesUrl ) )
65+ {
66+ var response = await GetAsync ( requestMessage , getResourcesUrl ) ;
67+ response . EnsureSuccessStatusCode ( ) ;
68+ var armResourceListResult = await response . Content . ReadAsAsync < ArmResourceListResult > ( ) ;
69+
70+ var newResourceFound = AddResourceToList ( armResourceListResult . Value , allResources ) ;
71+
72+ // ARM API returns the same skiptoken and resources repeatedly when there are no more resources. To avoid infinite cycle break when
73+ // 1. No new resource was found in the current response or
74+ // 2. Limit the max number of links to follow to _maxNextLinkDepth or
75+ // 3. When nextLink is empty
76+
77+ if ( ! newResourceFound )
78+ {
79+ break ;
80+ }
81+
82+ if ( currentNextLinkDepth ++ > _maxNextLinkDepth )
83+ {
84+ break ;
85+ }
86+
87+ getResourcesUrl = armResourceListResult . NextLink ;
88+ }
89+
90+ return allResources ;
91+ }
92+
4393 public async Task < HashSet < string > > GetProviderNamesFor ( HttpRequestMessage requestMessage , string subscriptionId )
4494 {
45- var response = await GetResourcesForAsync ( requestMessage , subscriptionId ) ;
46- response . EnsureSuccessStatusCode ( ) ;
47- dynamic resources = await response . Content . ReadAsAsync < JObject > ( ) ;
48- JArray values = resources . value ;
95+ var initialGetResourcesUrl = string . Format ( Utils . ResourcesTemplate , HyakUtils . CSMUrl , subscriptionId , Utils . CSMApiVersion ) ;
96+ var resources = await GetResources ( requestMessage , initialGetResourcesUrl ) ;
4997 var uniqueProviders = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
50- foreach ( dynamic value in values )
98+
99+ foreach ( var resource in resources )
51100 {
52- string id = value . id ;
53- var match = Regex . Match ( id , "/subscriptions/.*?/resourceGroups/(.*?)/providers/(.*?)/(.*?)/" ) ;
101+ var match = Regex . Match ( resource . Id , "/subscriptions/.*?/resourceGroups/(.*?)/providers/(.*?)/(.*?)/" ) ;
54102 if ( match . Success )
55103 {
56104 var provider = match . Groups [ 2 ] . Value . ToUpperInvariant ( ) ;
@@ -63,15 +111,13 @@ public async Task<HashSet<string>> GetProviderNamesFor(HttpRequestMessage reques
63111
64112 public async Task < Dictionary < string , Dictionary < string , HashSet < string > > > > GetProvidersFor ( HttpRequestMessage requestMessage , string subscriptionId )
65113 {
66- var response = await GetResourcesForAsync ( requestMessage , subscriptionId ) ;
67- response . EnsureSuccessStatusCode ( ) ;
68-
69- dynamic resources = await response . Content . ReadAsAsync < JObject > ( ) ;
70- JArray values = resources . value ;
114+ var initialGetResourcesUrl = string . Format ( Utils . ResourcesTemplate , HyakUtils . CSMUrl , subscriptionId , Utils . CSMApiVersion ) ;
115+ var resources = await GetResources ( requestMessage , initialGetResourcesUrl ) ;
71116 var result = new Dictionary < string , Dictionary < string , HashSet < string > > > ( ) ;
72- foreach ( dynamic value in values )
117+
118+ foreach ( var resource in resources )
73119 {
74- string id = value . id ;
120+ string id = resource . Id ;
75121 var match = Regex . Match ( id , "/subscriptions/.*?/resourceGroups/(.*?)/providers/(.*?)/(.*?)/" ) ;
76122 if ( match . Success )
77123 {
@@ -116,10 +162,10 @@ private async Task<HttpResponseMessage> GetSubscriptionsAsync(HttpRequestMessage
116162 return await _clientWrapper . SendAsync ( requestMessage , sendRequest ) ;
117163 }
118164
119- private async Task < HttpResponseMessage > GetResourcesForAsync ( HttpRequestMessage requestMessage , string subscriptionId )
165+ private async Task < HttpResponseMessage > GetAsync ( HttpRequestMessage requestMessage , string url )
120166 {
121- var sendRequest = new HttpRequestMessage ( HttpMethod . Get , string . Format ( Utils . ResourcesTemplate , HyakUtils . CSMUrl , subscriptionId , Utils . CSMApiVersion ) ) ;
122- return await _clientWrapper . SendAsync ( requestMessage , sendRequest ) ;
167+ var sendRequest = new HttpRequestMessage ( HttpMethod . Get , url ) ;
168+ return await _clientWrapper . ExecuteAsync ( requestMessage , sendRequest ) ;
123169 }
124170 }
125171}
0 commit comments