Private/Provider/Invoke-ProviderSearch.ps1
|
function Invoke-ProviderSearch { param( [string]$Query, [int]$MaxResults = 99, [int]$Skip = 0, [bool]$ExactSearch = $false, [string]$SortBy = 'Relevance', [string]$Repository = 'PSGallery', [bool]$AuthorSearch = $false, [bool]$TagSearch = $false ) $provider = Get-ModuleProvider $maxRetries = 2 $serverErrorPattern = '5\d{2}|Internal Server Error|Service Unavailable|Gateway Timeout|operation has timed out' for ($attempt = 1; $attempt -le ($maxRetries + 1); $attempt++) { try { Import-ActiveProvider -Provider $provider Write-PSMBLog -Message "Invoke-ProviderSearch: provider=$provider query='$Query' skip=$Skip max=$MaxResults exact=$ExactSearch author=$AuthorSearch tag=$TagSearch sort=$SortBy attempt=$attempt" -Level 'DEBUG' $totalNeeded = $Skip + $MaxResults if ($TagSearch) { if ($provider -eq 'PSResourceGet') { if ($Repository -eq 'PSGallery') { $raw = Invoke-PSGalleryApiSearch -SearchTerm $Query -TotalNeeded ($totalNeeded * 5) $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PSResourceGet' | Where-Object { $_.Tags -contains $Query } } else { $raw = Find-PSResource -Tag $Query -Repository $Repository -ErrorAction Stop | Where-Object { $_.Type -ne 'Script' } | Select-Object -First $totalNeeded $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PSResourceGet' } } else { $raw = Find-Module -Tag $Query -Repository $Repository -ErrorAction Stop | Select-Object -First $totalNeeded $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PowerShellGet' } } elseif ($AuthorSearch) { if ($provider -eq 'PSResourceGet') { if ($Repository -eq 'PSGallery') { $raw = Invoke-PSGalleryApiSearch -SearchTerm $Query -TotalNeeded ($totalNeeded * 5) $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PSResourceGet' | Where-Object { $_.Author -like "*$Query*" } } else { try { $raw = Find-PSResource -Tag $Query -Repository $Repository -ErrorAction Stop | Where-Object { $_.Type -ne 'Script' -and $_.Author -like "*$Query*" } | Select-Object -First $totalNeeded } catch { if ($_.Exception.Message -match $serverErrorPattern) { throw } $raw = @() } if ($raw.Count -eq 0) { try { $raw = Find-PSResource -Name "${Query}*" -Repository $Repository -ErrorAction Stop | Where-Object { $_.Type -ne 'Script' -and $_.Author -like "*$Query*" } | Select-Object -First $totalNeeded } catch { if ($_.Exception.Message -match $serverErrorPattern) { throw } $raw = @() } } } $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PSResourceGet' } else { $raw = Find-Module -Filter $Query -Repository $Repository -ErrorAction Stop | Where-Object { $_.Author -like "*$Query*" } | Select-Object -First $totalNeeded $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PowerShellGet' } } else { $resolvedQuery = if ($ExactSearch) { $Query } else { "*$Query*" } if ($provider -eq 'PSResourceGet') { $fetchCount = if ($SortBy -eq 'Downloads') { $totalNeeded * 2 } else { $totalNeeded } if (-not $ExactSearch -and $Repository -eq 'PSGallery') { # Use direct PSGallery Search API (searchTerm) — fast full-text index # instead of Find-PSResource substringof() OData filter which causes 500s $raw = Invoke-PSGalleryApiSearch -SearchTerm $Query -TotalNeeded $fetchCount } else { $raw = Find-PSResource -Name $resolvedQuery -Repository $Repository -ErrorAction Stop | Where-Object { $_.Type -ne 'Script' } | Select-Object -First $fetchCount } $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PSResourceGet' } else { $raw = Find-Module -Name $resolvedQuery -Repository $Repository -ErrorAction Stop | Select-Object -First $totalNeeded $results = $raw | ConvertTo-ModuleInfo -ProviderType 'PowerShellGet' } } switch ($SortBy) { 'Downloads' { $results = $results | Sort-Object -Property DownloadCount -Descending } 'LastUpdated' { $results = $results | Sort-Object -Property PublishedDate -Descending } } # Deduplicate by module name (API may return multiple entries per module) $seen = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $results = @($results | Where-Object { $seen.Add($_.Name) }) if ($Skip -gt 0) { return $results | Select-Object -Skip $Skip -First $MaxResults } return $results | Select-Object -First $MaxResults } catch { if ($attempt -le $maxRetries -and $_.Exception.Message -match $serverErrorPattern) { $delaySec = 2 * $attempt Write-PSMBLog -Message "Retry $attempt/$maxRetries in ${delaySec}s after server error: $_" -Level 'WARN' Start-Sleep -Seconds $delaySec continue } Write-PSMBLog -Message "Invoke-ProviderSearch error: $_" -Level 'ERROR' Write-Error "Search failed: $_" return @() } } } |