Last active
May 27, 2026 17:00
-
-
Save eeeschwartz/565198bedebc3303b0e2fe20ac8d3f58 to your computer and use it in GitHub Desktop.
Reproduce stale CE API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| Reproduce Henderson's stale ComputerEase /catalog/job-totals response. | |
| Usage: | |
| .\reproduce-job-totals-stale.ps1 ` | |
| -BaseUrl "https://YOUR-CE-HOST/v1" ` | |
| -ApiKey "YOUR_API_KEY" ` | |
| -ApiKeyId "YOUR_API_KEY_ID" ` | |
| -RefreshToken "YOUR_REFRESH_TOKEN" | |
| Notes: | |
| - This script intentionally contains no API key or refresh token. | |
| - Job checks only send GET requests. | |
| - If the first GET returns 401, the script makes the one sanctioned | |
| rotate-key PUT, saves the returned credentials to JSON, and retries. | |
| - It prints the response timestamp and a few totals for each sample job. | |
| #> | |
| param( | |
| [Parameter(Mandatory = $true)] | |
| [string]$BaseUrl, | |
| [Parameter(Mandatory = $true)] | |
| [string]$ApiKey, | |
| [Parameter(Mandatory = $true)] | |
| [string]$ApiKeyId, | |
| [Parameter(Mandatory = $true)] | |
| [string]$RefreshToken, | |
| [string]$RotatedCredentialPath = ".\rotated-ce-credentials.json", | |
| [string[]]$Jobs = @( | |
| "JS9005", | |
| "JS9000", | |
| "BE1184", | |
| "BE1134", | |
| "BE1244", | |
| "DA1761", | |
| "DA1803", | |
| "DA1858", | |
| "DA1859", | |
| "BE1159", | |
| "RR1430", | |
| "BA2292" | |
| ) | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| $script:ApiKey = $ApiKey | |
| $script:RefreshToken = $RefreshToken | |
| $script:headers = @{ | |
| "Authorization" = $ApiKey | |
| "Accept" = "application/json" | |
| } | |
| $base = $BaseUrl.TrimEnd("/") | |
| $baseUri = [System.Uri]$base | |
| if (-not $baseUri.Host.ToLowerInvariant().EndsWith("computerease.com")) { | |
| throw "Refusing to send CE credentials to non-ComputerEase host: $($baseUri.Host)" | |
| } | |
| function Get-UtcStamp { | |
| return (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") | |
| } | |
| function Invoke-JobTotalsGet { | |
| param([Parameter(Mandatory = $true)][string]$Job) | |
| $encodedJob = [System.Uri]::EscapeDataString($Job) | |
| $uri = "$base/catalog/job-totals?job_code=$encodedJob" | |
| try { | |
| $response = Invoke-RestMethod -Method Get -Uri $uri -Headers $script:headers | |
| return @{ | |
| Ok = $true | |
| StatusCode = 200 | |
| Response = $response | |
| ErrorMessage = $null | |
| } | |
| } | |
| catch { | |
| $statusCode = $null | |
| if ($_.Exception.Response -and $_.Exception.Response.StatusCode) { | |
| $statusCode = [int]$_.Exception.Response.StatusCode | |
| } | |
| return @{ | |
| Ok = $false | |
| StatusCode = $statusCode | |
| Response = $null | |
| ErrorMessage = $_.Exception.Message | |
| } | |
| } | |
| } | |
| function Rotate-ComputerEaseApiKey { | |
| $encodedKeyId = [System.Uri]::EscapeDataString($ApiKeyId) | |
| $rotateUri = "$base/company-key/$encodedKeyId/rotate-key" | |
| $rotateHeaders = @{ | |
| "Authorization" = $script:RefreshToken | |
| "Accept" = "application/json" | |
| } | |
| Write-Host "[ce-api] GET returned 401; rotating API key once..." | |
| $rotateResponse = Invoke-RestMethod -Method Put -Uri $rotateUri -Headers $rotateHeaders | |
| if (-not $rotateResponse.api_key -or -not $rotateResponse.new_key) { | |
| throw "rotate-key response did not include api_key and new_key." | |
| } | |
| $script:ApiKey = [string]$rotateResponse.api_key | |
| $script:RefreshToken = [string]$rotateResponse.new_key | |
| $script:headers["Authorization"] = $script:ApiKey | |
| $credentialOut = [ordered]@{ | |
| api_key = $script:ApiKey | |
| refresh_token = $script:RefreshToken | |
| api_key_id = $ApiKeyId | |
| rotated_at_utc = Get-UtcStamp | |
| } | |
| $credentialDir = Split-Path -Parent $RotatedCredentialPath | |
| if ($credentialDir) { | |
| New-Item -ItemType Directory -Force -Path $credentialDir | Out-Null | |
| } | |
| $credentialOut | ConvertTo-Json | Set-Content -Path $RotatedCredentialPath -Encoding UTF8 | |
| Write-Host "[ce-api] Rotated credentials saved to $RotatedCredentialPath" | |
| } | |
| Write-Host "Now (UTC): $(Get-UtcStamp)" | |
| Write-Host "Endpoint: $base/catalog/job-totals?job_code=<JOB_CODE>" | |
| Write-Host "" | |
| $rotated = $false | |
| foreach ($job in $Jobs) { | |
| $result = Invoke-JobTotalsGet -Job $job | |
| if (-not $result.Ok -and $result.StatusCode -eq 401 -and -not $rotated) { | |
| Rotate-ComputerEaseApiKey | |
| $rotated = $true | |
| $result = Invoke-JobTotalsGet -Job $job | |
| } | |
| if (-not $result.Ok) { | |
| Write-Host "$job`tERROR status=$($result.StatusCode) $($result.ErrorMessage)" | |
| continue | |
| } | |
| $row = $result.Response.data | Select-Object -First 1 | |
| if ($null -eq $row) { | |
| Write-Host "$job`t(no job-totals rows)" | |
| continue | |
| } | |
| Write-Host "$job`tupdated_at=$($row.updated_at)`tcontract=$($row.contract_amount)`tbilled=$($row.billed)" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment