Classes/Composites/OAIComplianceRequestClient.ps1
class OAIComplianceRequestClient { [string]$WorkspaceId [string]$BaseUri [system.collections.generic.list[pscustomobject]]$Results hidden [string]$APIKey hidden [hashtable]$Headers hidden [hashtable]$RequestDetails OAIComplianceRequestClient([string]$workspaceId, [string]$apiKey) { $this.WorkspaceId = $workspaceId $this.BaseUri = "https://api.chatgpt.com/v1/compliance/workspaces/$($this.WorkspaceId)" $this.APIKey = $apiKey $this.Headers = @{} $this.Headers["Authorization"] = "Bearer $($this.APIKey)" $this.Headers["Content-Type"] = "application/json" } #region Request Methods # Invoke a request to the OpenAI Compliance API hidden [object]InvokeRequest([string]$method, [hashtable]$body, [string[]]$segments, [hashtable]$queryParams) { $max_retries = 3 For ($attempt = 1; $attempt -le $max_retries; $attempt++) { # Invoke-RestMethod parameters $invoke_rest_params = @{} $invoke_rest_params["Method"] = $method $invoke_rest_params["Uri"] = $this.BuildComplianceUri($segments, $queryParams) $invoke_rest_params["Headers"] = $this.Headers If ($body) { $invoke_rest_params["Body"] = $body } # Invoke-RestMethod Try { $this.RequestDetails = $invoke_rest_params $response = Invoke-RestMethod @invoke_rest_params return $response } Catch { # Check if we should retry due to rate limiting If ($this.HandleRateLimit()) { continue } # Log the error for non-retryable errors or final attempt Write-Error "Failed to invoke request: $($_.Exception.Message)" If ($attempt -eq $max_retries) { return $null } } } return $null } # Invoke a GET request to the OpenAI Compliance API [object]InvokeGetRequest([string[]]$segments, [hashtable]$queryParams) { return $this.InvokeRequest("GET", $null, $segments, $queryParams) } # Invoke a DELETE request to the OpenAI Compliance API hidden [object]InvokeDeleteRequest([string[]]$segments, [hashtable]$queryParams) { return $this.InvokeRequest("DELETE", $null, $segments, $queryParams) } # Paginate through all results for a GET request [object]Paginate([string[]]$segments, [hashtable]$queryParams, [int]$top = 0) { $this.Results = [system.collections.generic.list[pscustomobject]]::new() $params = $queryParams.Clone() $total_retrieved = 0 Do { $response = $this.InvokeGetRequest($segments, $params) If (!$response) { Write-Error "Failed to retrieve data during pagination" break } ElseIf ($response.data) { $items_to_add = $this.GetItemsToAdd($response.data, $top, $total_retrieved) Foreach ($item in $items_to_add) { $this.Results.Add($item) } $total_retrieved += $items_to_add.Count # Check if we've reached the top limit If ($top -gt 0 -and $total_retrieved -ge $top) { break } } # Setup next page if more data exists If ($response.has_more -and $response.last_id) { $this.SetupNextPage($params, $response.last_id) } } While ($response.has_more -and $response.last_id) return $this.Results } # Get the items to add based on top limit hidden [object[]]GetItemsToAdd([object[]]$data, [int]$top, [int]$total_retrieved) { If ($top -gt 0) { $remaining_needed = $top - $total_retrieved If ($remaining_needed -le 0) { return @() } ElseIf ($data.Count -gt $remaining_needed) { return $data[0..($remaining_needed - 1)] } } return $data } # Setup parameters for next page hidden [void]SetupNextPage([hashtable]$params, [string]$lastId) { # Remove since_timestamp to avoid parameter conflict If ($params.ContainsKey("since_timestamp")) { $params.Remove("since_timestamp") } $params["after"] = $lastId } # Rate limiting method - reactive handling hidden [bool]HandleRateLimit() { $last_error = $Error[0] If ($this.IsRateLimitError($last_error)) { $retry_after = $this.GetRetryAfterValue($last_error) $sleep_time = If ($retry_after) { [int]$retry_after } Else { 60 } Write-Warning "Rate limit hit (429). Sleeping for $sleep_time seconds" Start-Sleep -Seconds $sleep_time return $true } return $false } # Check if error is a rate limit error hidden [bool]IsRateLimitError([object]$errorRecord) { $exception_type = $errorRecord.Exception.GetType().FullName # PowerShell Core 6+ uses HttpResponseException If ($exception_type -eq "Microsoft.PowerShell.Commands.HttpResponseException") { $status_code = $errorRecord.Exception.Response.StatusCode return ($status_code -eq 429) # Windows PowerShell 5.1 uses WebException } ElseIf ($errorRecord.Exception -is [System.Net.WebException]) { $response = $errorRecord.Exception.Response If ($response -and $response.StatusCode) { return ($response.StatusCode -eq 429) } } return $false } # Get retry after value from error response hidden [object]GetRetryAfterValue([object]$errorRecord) { $exception_type = $errorRecord.Exception.GetType().FullName # PowerShell Core 6+ uses HttpResponseException If ($exception_type -eq "Microsoft.PowerShell.Commands.HttpResponseException") { return $errorRecord.Exception.Response.Headers["Retry-After"] # Windows PowerShell 5.1 uses WebException } ElseIf ($errorRecord.Exception -is [System.Net.WebException]) { $response = $errorRecord.Exception.Response If ($response -and $response.Headers) { return $response.Headers["Retry-After"] } } return $null } #endregion #region URI Building # Build the URI for the OpenAI Compliance API hidden [string]BuildComplianceUri([string[]]$segments, [hashtable]$queryParams) { $endpoint = "$($this.BaseUri)/$(($segments -replace '(^/+|/+$)', '') -join "/")" If ($queryParams) { $endpoint = "$($endpoint)?$(($queryParams.GetEnumerator() | ForEach-Object { "$($_.Key)=$([System.Web.HttpUtility]::UrlEncode($_.Value.ToString()))" }) -join "&")" } return $endpoint } #endregion #region Last Request # Get the last request [string]DebugRequest() { $masked_headers = $this.RequestDetails.Headers.Clone() $masked_headers.Authorization = $this.MaskApiKey($masked_headers.Authorization) $this.RequestDetails.Headers = $masked_headers return $this.RequestDetails | ConvertTo-Json -Depth 10 } # Get the last results [string]DebugHeaders() { $masked_headers = $this.Headers.Clone() $masked_headers.Authorization = $this.MaskApiKey($masked_headers.Authorization) return $masked_headers | ConvertTo-Json -Depth 10 } # Mask the API key [string]MaskApiKey([string]$apiKey) { return $apiKey.Substring(0, 7) + "******" + $apiKey.Substring($apiKey.Length - 4) } #endregion #region Static utilities # Convert datetime or int to unix timestamp static [int]ConvertToUnixTimestamp($sinceTimestamp) { If ($sinceTimestamp -is [datetime]) { return [int][double]::Parse((Get-Date $sinceTimestamp -UFormat %s)) } Else { return $sinceTimestamp } } #endregion } |