66namespace Microsoft . Azure . Functions . PowerShellWorker . DependencyManagement
77{
88 using System ;
9- using System . IO ;
109 using System . Management . Automation ;
11- using System . Net . Http ;
1210 using System . Xml ;
1311
1412 using Microsoft . Azure . Functions . PowerShellWorker . PowerShell ;
1513 using Microsoft . Azure . Functions . PowerShellWorker . Utility ;
1614
1715 internal class PowerShellGalleryModuleProvider : IModuleProvider
1816 {
17+ private readonly IPowerShellGallerySearchInvoker _searchInvoker ;
18+
19+ public PowerShellGalleryModuleProvider ( IPowerShellGallerySearchInvoker searchInvoker = null )
20+ {
21+ _searchInvoker = searchInvoker ?? new PowerShellGallerySearchInvoker ( ) ;
22+ }
23+
1924 /// <summary>
2025 /// Returns the latest module version from the PSGallery for the given module name and major version.
2126 /// </summary>
@@ -27,38 +32,18 @@ public string GetLatestPublishedModuleVersion(string moduleName, string majorVer
2732
2833 Uri address = new Uri ( $ "{ PowerShellGalleryFindPackagesByIdUri } '{ moduleName } '") ;
2934
30- string latestMajorVersion = null ;
31- Stream stream = null ;
35+ var expectedVersionStart = majorVersion + "." ;
36+
37+ Version latestVersion = null ;
3238
33- var retryCount = 3 ;
34- while ( true )
39+ do
3540 {
36- using ( var client = new HttpClient ( ) )
41+ var stream = _searchInvoker . Invoke ( address ) ;
42+ if ( stream == null )
3743 {
38- try
39- {
40- var response = client . GetAsync ( address ) . Result ;
41-
42- // Throw is not a successful request
43- response . EnsureSuccessStatusCode ( ) ;
44-
45- stream = response . Content . ReadAsStreamAsync ( ) . Result ;
46- break ;
47- }
48- catch ( Exception )
49- {
50- if ( retryCount <= 0 )
51- {
52- throw ;
53- }
54-
55- retryCount -- ;
56- }
44+ break ;
5745 }
58- }
5946
60- if ( stream != null )
61- {
6247 // Load up the XML response
6348 XmlDocument doc = new XmlDocument ( ) ;
6449 using ( XmlReader reader = XmlReader . Create ( stream ) )
@@ -68,27 +53,20 @@ public string GetLatestPublishedModuleVersion(string moduleName, string majorVer
6853
6954 // Add the namespaces for the gallery xml content
7055 XmlNamespaceManager nsmgr = new XmlNamespaceManager ( doc . NameTable ) ;
71- nsmgr . AddNamespace ( "ps " , "http://www.w3.org/2005/Atom" ) ;
56+ nsmgr . AddNamespace ( "a " , "http://www.w3.org/2005/Atom" ) ;
7257 nsmgr . AddNamespace ( "d" , "http://schemas.microsoft.com/ado/2007/08/dataservices" ) ;
7358 nsmgr . AddNamespace ( "m" , "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" ) ;
7459
75- // Find the version information
7660 XmlNode root = doc . DocumentElement ;
77- var props = root . SelectNodes ( "//m:properties[d:IsPrerelease = \" false \" ]/d:Version" , nsmgr ) ;
61+ latestVersion = GetLatestVersion ( root , nsmgr , expectedVersionStart , latestVersion ) ;
7862
79- if ( props != null && props . Count > 0 )
80- {
81- foreach ( XmlNode prop in props )
82- {
83- if ( prop . FirstChild . Value . StartsWith ( majorVersion ) )
84- {
85- latestMajorVersion = prop . FirstChild . Value ;
86- }
87- }
88- }
63+ // The response may be paginated. In this case, the current page
64+ // contains a link to the next page.
65+ address = GetNextLink ( root , nsmgr ) ;
8966 }
67+ while ( address != null ) ;
9068
91- return latestMajorVersion ;
69+ return latestVersion ? . ToString ( ) ;
9270 }
9371
9472 /// <summary>
@@ -125,5 +103,33 @@ public void Cleanup(PowerShell pwsh)
125103 . AddParameter ( "ErrorAction" , "SilentlyContinue" )
126104 . InvokeAndClearCommands ( ) ;
127105 }
106+
107+ private static Version GetLatestVersion (
108+ XmlNode root , XmlNamespaceManager namespaceManager , string expectedVersionStart , Version latestVersion )
109+ {
110+ var versions = root . SelectNodes ( "/a:feed/a:entry/m:properties[d:IsPrerelease = \" false\" ]/d:Version" , namespaceManager ) ;
111+ if ( versions != null )
112+ {
113+ foreach ( XmlNode prop in versions )
114+ {
115+ if ( prop . FirstChild . Value . StartsWith ( expectedVersionStart )
116+ && Version . TryParse ( prop . FirstChild . Value , out var thisVersion ) )
117+ {
118+ if ( latestVersion == null || thisVersion > latestVersion )
119+ {
120+ latestVersion = thisVersion ;
121+ }
122+ }
123+ }
124+ }
125+
126+ return latestVersion ;
127+ }
128+
129+ private static Uri GetNextLink ( XmlNode root , XmlNamespaceManager namespaceManager )
130+ {
131+ var nextLink = root . SelectNodes ( "/a:feed/a:link[@rel=\" next\" ]/@href" , namespaceManager ) ;
132+ return nextLink . Count == 1 ? new Uri ( nextLink [ 0 ] . Value ) : null ;
133+ }
128134 }
129135}
0 commit comments