Functions/Invoke-GitHubApiRoute.ps1
<#
.SYNOPSIS Invokes a GitHub API route with support for authentication, retries, and custom headers. .DESCRIPTION The Invoke-GitHubApiRoute function sends HTTP requests to the GitHub API, handling authentication, retries on certain HTTP errors, and custom headers. It supports GET, POST, and other HTTP methods, and can return raw responses or save output to a file. The function also manages GitHub authentication tokens and can update them if authentication errors occur. .PARAMETER Path The API route path (relative to https://api.github.com/) to invoke. .PARAMETER Query A hashtable of query parameters to append to the request URI. .PARAMETER Method The HTTP method to use for the request (default is "GET"). .PARAMETER Body The request body to send (for POST, PUT, PATCH, etc.). .PARAMETER Token The GitHub authentication token to use. Defaults to the output of 'gh auth token'. .PARAMETER ResponseHeaders A reference variable to receive the response headers. .PARAMETER StatusCode A reference variable to receive the HTTP status code. .PARAMETER Raw If specified, returns the raw response instead of parsing as JSON. .PARAMETER SkipHttpErrorCheck If specified, skips HTTP error checking. .PARAMETER SearchText Optional text to search for in the response. .PARAMETER OutFile If specified, saves the response content to the given file path. .PARAMETER ExtraHeaders A hashtable of additional headers to include in the request. .EXAMPLE Invoke-GitHubApiRoute -Path "repos/owner/repo/issues" -Method "GET" .EXAMPLE Invoke-GitHubApiRoute -Path "user/repos" -Method "POST" -Body $jsonBody -Token $myToken .NOTES - Handles 401 and 403 errors by attempting to refresh the authentication token and retrying. - Retries up to 5 times on 500 and 502 errors, waiting 60 seconds between attempts. - Rate limiting is handled by the Invoke-RateLimitedEndpoint function. #> function Invoke-GitHubApiRoute { [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Path, [hashtable] $Query = @{}, [string] $Method = "GET", [string] $Body, [string] $Token = "$(gh auth token)", [ref] $ResponseHeaders, [ref] $StatusCode, [switch] $Raw, [switch] $SkipHttpErrorCheck, [string] $SearchText, [string] $OutFile, [hashtable] $ExtraHeaders = @{} ) # Setup Headers $Headers = @{ Authorization = "Bearer $Token" "X-GitHub-Api-Version" = "2022-11-28" } if ($ExtraHeaders) { foreach ($key in $ExtraHeaders.Keys) { $Headers[$key] = $ExtraHeaders[$key] } } $RetryCount = 0 $AuthError = $false do { try { # Build URI $uri = "https://api.github.com/$Path" if ($Query.Count -gt 0) { $queryString = ($Query.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "&" $uri += "?$queryString" } Write-Host "$Method $uri" if (-not $ResponseHeaders) { $ResponseHeaders = ([ref]([hashtable]::new())) } if (-not $StatusCode) { $StatusCode = ([ref]([int]::new())) } # Invoke the API $data = Invoke-RateLimitedEndpoint ` -Method $Method ` -Uri $uri ` -Headers $Headers ` -Body $Body ` -ResponseHeaders $ResponseHeaders ` -StatusCode $StatusCode ` -SkipHttpErrorCheck:$SkipHttpErrorCheck ` -Raw:$Raw ` -SearchText $SearchText ` -OutFile $OutFile return $data } catch { Write-Host "Error: $($_.Exception.Message)" if ($_.Exception.Message -match "401|403") { # Clear Cached Etags and Last-Modified headers $Global:CachedEtags = @{} $Global:CachedLastModified = @{} if (-not $AuthError) { $AuthError = $true Update-GitHubToken -Organization $env:GH_AUTH_ORG -Repository $env:GH_AUTH_REPO Write-Host "Retrying after 5 seconds..." Start-Sleep -Seconds 5 $Headers["Authorization"] = "Bearer $(gh auth token)" continue } throw } if ($_.Exception.Message -match "500|502") { $RetryCount++ if ($RetryCount -gt 5) { throw } Write-Host "Retrying after 60 seconds..." Start-Sleep -Seconds 60 continue } throw } } while ($true) } |