Private/EAI/Resolve-EAIDebugInfo.ps1

<#
.SYNOPSIS
Writes debug information for Edwin API requests.

.DESCRIPTION
The Resolve-EAIDebugInfo function provides debug information for Edwin API requests including
command context, formatted parameters, URL details, secure header display, and formatted payload data.

.PARAMETER Url
The URL that was invoked for the API request.

.PARAMETER Headers
The headers hashtable used in the request.

.PARAMETER Command
The PowerShell command invocation context (typically $MyInvocation).

.PARAMETER Payload
The request payload (typically JSON string).

.EXAMPLE
Resolve-EAIDebugInfo -Url "https://myorg.dexda.ai/integration/event/v1" -Headers $Headers -Command $MyInvocation -Payload $JsonData
#>

function Resolve-EAIDebugInfo {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [String]$Url,

        [Parameter(Mandatory)]
        [Hashtable]$Headers,

        [Parameter(Mandatory)]
        [System.Management.Automation.InvocationInfo]$Command,

        [String]$Payload
    )

    $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'
    $httpMethod = $null

    if ($Headers.ContainsKey('__EAIMethod')) {
        $httpMethod = $Headers['__EAIMethod']
        $Headers.Remove('__EAIMethod') | Out-Null
    }

    if (-not $httpMethod) {
        $commandName = $Command.MyCommand.Name
        switch -Regex ($commandName) {
            '^(Get|Find|Search|Test|Resolve|Format|Measure|Show)-' { $httpMethod = 'GET'; break }
            '^(Remove|Uninstall|Disconnect|Stop|Clear|Delete)-' { $httpMethod = 'DELETE'; break }
            '^(Set|Update|Enable|Disable|Rename|Move|Merge|Patch|Edit)-' { $httpMethod = 'PATCH'; break }
            '^(New|Add|Copy|Send|Import|Invoke|Start|Publish|Submit|Approve|Convert)-' { $httpMethod = 'POST'; break }
        }
    }

    if (-not $httpMethod) {
        if ($Headers.ContainsKey('Content-Type') -and $Payload) {
            $httpMethod = 'POST'
        }
        elseif ($Payload) {
            $httpMethod = 'POST'
        }
        else {
            $httpMethod = 'GET'
        }
    }

    Write-Debug '============ Edwin API Debug Info =============='
    Write-Debug "Command: $($Command.MyCommand) | Method: $httpMethod | Timestamp: $timestamp"

    if ($Command.BoundParameters.Count -gt 0) {
        $paramList = $Command.BoundParameters.GetEnumerator() | ForEach-Object {
            $value = if ($_.Value -is [Array]) { "[$($_.Value -join ', ')]" }
            elseif ($_.Value -is [Hashtable]) { "{$($_.Value.Keys -join ', ')}" }
            else { $_.Value }
            "$($_.Key): $value"
        }
        Write-Debug "Parameters: $($paramList -join ' | ')"
    }

    $uriObj = [System.Uri]$Url
    Write-Debug "Endpoint: $($uriObj.PathAndQuery)"
    Write-Debug "Portal: $($uriObj.Host)"

    $sensitiveHeaders = @('Authorization', 'accessKey', 'bearerToken', 'cookie', 'X-CSRF-Token', 'client_secret', 'client_id')
    $headerInfo = $Headers.GetEnumerator() | ForEach-Object {
        $value = if ($sensitiveHeaders -contains $_.Key) {
            if ($_.Value.Length -gt 25) {
                $_.Value.Substring(0, 25) + '...'
            }
            else {
                $_.Value
            }
        }
        else {
            $_.Value
        }
        "$($_.Key): $value"
    }
    Write-Debug "Headers: $($headerInfo -join ' | ')"

    if ($Payload) {
        try {
            $jsonObj = $Payload | ConvertFrom-Json
            $sensitiveFields = @('password', 'accessKey', 'token', 'secret', 'apiKey', 'bearerToken', 'client_secret', 'client_id', 'clientSecret', 'clientId')

            function Redact-EAISensitiveData {
                param($Object)

                if ($Object -is [PSCustomObject]) {
                    $Object.PSObject.Properties | ForEach-Object {
                        if ($sensitiveFields -contains $_.Name) {
                            $_.Value = '[REDACTED]'
                        }
                        elseif ($_.Value -is [PSCustomObject] -or $_.Value -is [Array]) {
                            Redact-EAISensitiveData $_.Value
                        }
                    }
                }
                elseif ($Object -is [Array]) {
                    for ($i = 0; $i -lt $Object.Count; $i++) {
                        if ($Object[$i] -is [PSCustomObject] -or $Object[$i] -is [Array]) {
                            Redact-EAISensitiveData $Object[$i]
                        }
                    }
                }
            }

            Redact-EAISensitiveData $jsonObj

            $jsonString = ConvertTo-Json -InputObject $jsonObj -Depth 10
            $formattedPayload = $jsonString -split "`r?`n" | ForEach-Object { " $_" }

            Write-Debug 'Request Payload:'
            $formattedPayload | ForEach-Object { Write-Debug $_ }
        }
        catch {
            Write-Debug "Request Payload (Raw): $Payload"
        }
    }
    else {
        Write-Debug "Request Payload: None ($httpMethod request)"
    }

    Write-Debug '========================================================'
}