Pode.Web icon indicating copy to clipboard operation
Pode.Web copied to clipboard

"Resulting JSON is truncated" error with Pode and Pode.Web for API and WEB UI

Open ChrisLynchHPE opened this issue 1 year ago • 5 comments

Describe the Bug

Similar to https://github.com/Badgerati/Pode.Web/issues/605. When combining Pode.Web with Pode to provide API and Web Front-End, I get:

WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 10.

Then the state.json is empty.

If I remove all Add-PodeWebPage calls, I do not get this error.

Steps To Reproduce

Import-Module Pode
Import-Module Pode.Web

Start-PodeServer {

    Class AuthHeaders {

        [String] static $token = 'TOKEN'

        static [Hashtable] GetAuthHeaders() {

            return [Hashtable] @{
                "Authorization" = ("Bearer {0}" -f [AuthHeaders]::token)
                "Content-type"  = "application/json"

            }

        }

    }

    Class AppVeyorJob {

        [ValidateSet("Group1", "Group2", 'Group3', 'Group4', 'TotGroup', 'LTSGroup')] [String] $Name
        [String[]] $Appliances = [String[]]::new(4)
        [String] $JobID
        [String] $JobUrl
        [ValidateSet("Stopped", "Running", 'Completed', 'Maintenance')] [String] $State = 'Stopped'
        [DateTime] $Start
        [DateTime] $End

        AppVeyorJob ($Name) {

            $this.Name = $Name

        }

        AppVeyorJob ([String]$Name, [String]$JobID, [Uri]$JobUrl, [String]$State) {

            # If the evaluation is false, throw error
            if (-not $JobUrl -eq 'INTERNAL' -and -not [String]::IsNullOrEmpty($JobUrl.AbsolutePath) -and -not $JobUrl.AbsolutePath.StartsWith("/api/projects/org/")) {

                Throw "JobURI is not a valid URI.  Must start with /api/projects/org/."

            }

            # $this.Appliances = $state:CurrentConfig.Groups.$Name
            $this.Name = $Name
            $this.JobID = $JobID
            $this.JobUrl = $JobUrl
            $this.State = $State

        }

        [AppVeyorJob] SetAppliances ([String[]]$Appliances) {

            For ($i = 0; $i -lt $Appliances.Count; $i++) {

                $this.Appliances[$i] = $Appliances[$i]

            }

            return $this

        }

        [Boolean] IsRunning() {

            if ([String]::IsNullOrEmpty($this.JobURL)) {

                Throw "JobURL is null.  Cannot check if job is still running."

            }

            elseif ($this.JobUrl -eq 'INTERNAL') {

                $this.State = "Running"

                return $true

            }

            else {

                try {

                    $_currentState = Invoke-RestMethod -Uri $this.JobUrl -Method Get -Headers ([AuthHeaders]::GetAuthHeaders())

                }

                catch {

                    Write-Host "Error: $($_.Exception.Message)"
                    return $false

                }

                if ($_currentState.build.status -eq 'Running') {

                    $this.State = "Running"
                    $this.Start = $_currentState.build.started

                    return $true

                }

                else {

                    $this.State = "Stopped"
                    $this.Start = $_currentState.build.started
                    $this.End = $_currentState.build.updated

                    return $false

                }

            }

        }

        static [AppVeyorJob[]] Init() {

            $GroupNames = "Group1", "Group2", 'Group3', 'Group4', 'TotGroup', 'LTSGroup'

            $Collection = @()

            ForEach ($_group in $GroupNames) {

                $Collection += [AppVeyorJob]::new($_group)

            }

            return $Collection

        }

    }

    # Create static class to return network interface(s) index to bind Pode service to based on connected state and those with an IPv4 address
    Class NetInterface {

        static [Int[]] GetAvailableAdapterIndex() {

            $FoundAdapterIndexes = Get-NetAdapter | Where-Object {
                ($_.MediaConnectionState -eq 'Connected' -or $_.ifOperStatus -eq 'Connected') -and
                $_.ifDesc -notmatch 'Loopback' -and
                $_.ifDesc -notmatch 'VPN' -and
                $_.ifDesc -notmatch 'TAP'
            } | Select-Object ifIndex -Unique -ExpandProperty ifIndex

            if ($FoundAdapterIndexes.Count -eq 0) {

                Throw "No network interfaces found with a connected state."

            }

            else {

                return $FoundAdapterIndexes

            }

        }

    }


    # request logging
    # New-PodeLoggingMethod -Terminal -Batch 10 -BatchTimeout 10 | Enable-PodeRequestLogging
    New-PodeLoggingMethod -Terminal | Enable-PodeErrorLogging -Levels Error, Warning, Informational, Verbose
    Set-PodeSecurityAccessControl -Methods 'GET', 'POST', 'PATCH' -Origin '*' -Headers 'Content-Type'

    $PodeBasePath = Join-Path 'C:' 'Temp' 'pode'
    $BaseUploadPath = Join-Path 'C:' 'Temp' 'pode' 'uploads'

    "Setting PodeStateConfigFileLocation" | Out-Default
    # Location where the Pode server state variables are stored
    $PodeStateConfigFileLocation = (Join-Path $PodeBasePath 'state.json')
    $cache:PodeStateConfig = $PodeStateConfigFileLocation

    "Setting UploadFileDirectoryLocation" | Out-Default
    # File upload location, which is for uploading pester unit test result documents
    $cache:UploadFileDirectory = $BaseUploadPath

    Get-PodeConfig

    "Attempting to restore pode state" | Out-Default
    # attempt to re-initialise the state (will do nothing if the file doesn't exist)
    Restore-PodeState -Path $cache:PodeStateConfig

    # create the shared variable
    if ($null -eq $state:CurrentJobs) {

        # $Appliances = Set-PodeState -name 'Appliances' -value @{} -Scope Scope0
        Set-PodeState -Name 'CurrentJobs' -Value ([AppVeyorJob]::Init())

        $null = Save-PodeState -Path $cache:PodeStateConfig

    }

    # Need to loop through each group and check if the job is still running
    else {

        # Need to re-create the class objects after restoring the state from the JSON file
        For ($j = 0; $j -lt $state:CurrentJobs.Count; $j++) {

            $state:CurrentJobs[$j] = [AppVeyorJob]::new($state:CurrentJobs[$j].Name, $state:CurrentJobs[$j].JobID, $state:CurrentJobs[$j].JobUrl, $state:CurrentJobs[$j].State)

            if ($state:CurrentJobs[$j].State -eq "Running" -and -not [String]::IsNullOrEmpty($state:CurrentJobs[$j].JobUrl)) {

                # check if the job is still running
                $Result = $state:CurrentJobs[$j] -ne "Maintenance" ? $state:CurrentJobs[$j].IsRunning() : @{ status = 'Maintenance' }

                "Job still running: {0}" -f $Result | Out-Host

                if ($Result.status -eq "running") {

                    "Job {0} is still running." -f $state:CurrentJobs[$j].JobID | Out-Host

                    $state:CurrentJobs[$j].State = "Running"

                }

                elseif ($Result.status -ne 'Maintenance') {

                    "Job {0} is no longer running.  Clearing from tracking." -f $state:CurrentJobs[$j].JobID | Out-Host

                    $state:CurrentJobs[$j].State = "Stopped"
                    $state:CurrentJobs[$j].Appliances.Clear()
                    $state:CurrentJobs[$j].JobID = $null
                    $state:CurrentJobs[$j].JobUrl = $null

                }

            }

            elseif ($state:CurrentJobs[$j].State -eq "Running" -and [String]::IsNullOrEmpty($state:CurrentJobs[$j].JobUrl)) {

                "Job {0} is no longer running as JobURL is null.  Clearing from tracking." -f $state:CurrentJobs[$j].JobId | Out-Host

                $state:CurrentJobs[$j].State = "Stopped"
                $state:CurrentJobs[$j].JobID = $null

            }

        }

        $null = Save-PodeState -Path $cache:PodeStateConfig

    }

    # Check to make sure the cert has not expired
    $Cert = Get-ChildItem Cert:\LocalMachine\My | ? { $_.Subject -match $env:COMPUTERNAME -and $_.Issuer -match "CA" -and -not $_.Archived }

    if ($null -eq $Cert) {

        Thorw "No available web certificates found."

    }

    # Attempt to renew the cert if expired
    if ([datetime]::Now -ge $Cert.NotAfter) {

        certreq -enroll -machine -q -PolicyServer * -cert $Cert.Thumbprint renew reusekeys

    }

    ForEach ($ip in ([Netinterface]::GetAvailableAdapterIndex() | % { Get-NetIPAddress -InterfaceIndex $_ }).IPAddress) {

        "Binding to $ip" | Out-Default

        Add-PodeEndpoint -Address $ip -Port 8443 -Protocol Https -CertificateStoreLocation LocalMachine -CertificateStoreName My -CertificateThumbprint $Cert.Thumbprint

    }

    # Return the appliance configuration JSON object
    Add-PodeRoute -Method Get -Path '/api/currentConfig' -ScriptBlock {
        
        Lock-PodeObject -ScriptBlock {
        
            Write-PodeJsonResponse -StatusCode 200 -Value $state:CurrentConfig
        
        }
    
    }

    # API to check out an available group and assign a job to it.  If JobURL is "INTERNAL", the job is not checked with Appveyor API
    # POST /api/jobs/checkOut
    # Request body:
    #     {
    #         "JobID": "1.10.10",
    #         "JobURL": "https://ci.appveyor.com/api/projects/org/proj/build/1.10.10"
    #     }
    #
    # Response body:
    #     {
    #         "Name": "Group1",
    #         "Appliances": [
    #             "Appliance1",
    #             "Appliance2",
    #             "Appliance3",
    #             "Appliance4"
    #         ],
    #         "JobID": "1.10.10",
    #         "JobURL": "https://ci.appveyor.com/api/projects/org/proj/build/1.10.10"
    #         "State": "Running"
    #     }
    #
    # Error response body with HTTP400:
    #     {
    #         "ErrorID": "Invalid_Request_Body",
    #         "ErrorDescription": "The missing JobID and JobURL in request body."
    #     }
    #
    # Error response body with HTTP400:
    #     {
    #         "ErrorID": "Invalid_Request_Body",
    #         "ErrorDescription": "The missing JobID in request body."
    #     }
    #
    # Error response body with HTTP400:
    #     {
    #         "ErrorID": "Invalid_Request_Body",
    #         "ErrorDescription": "The missing JobURL in request body."
    #     }
    #
    #
    # Error response body with HTTP406:
    #     {
    #         "ErrorID": "No_Available_Slots",
    #         "ErrorDescription": "No available slots."
    #     }
    #
    Add-PodeRoute -Method Post -Path '/api/jobs/checkOut' -ScriptBlock {

        $WebEvent.Data | Out-PodeHost

        $RequestBody = $WebEvent.Data

        if ($RequestBody.Count -eq 0 -or $null -eq $RequestBody) {

            Write-PodeJsonResponse -StatusCode 400 -Value @{ ErrorID = 'Invalid_Request_Body'; ErrorDescription = "The missing JobID and JobURL in request body." }

        }

        elseif (-not $RequestBody['JobID']) {

            Write-PodeJsonResponse -StatusCode 400 -Value @{ ErrorID = 'Invalid_Request_Body'; ErrorDescription = "The missing JobID in request body." }

        }

        elseif (-not $RequestBody['JobURL']) {

            Write-PodeJsonResponse -StatusCode 400 -Value @{ ErrorID = 'Invalid_Request_Body'; ErrorDescription = "The missing JobURL in request body." }

        }

        else {

            $JobIdRegex = [regex]::new("^\d{1,3}\.\d+(\.\d+){0,2}$")

            "JobID: {0}" -f $RequestBody['JobID'] | Out-Host

            # Will also need to have a validation on the JobID format
            if (-not $JobIdRegex.Match($RequestBody['JobID']).Success) {

                Write-PodeJsonResponse -StatusCode 400 -Value @{ ErrorID = 'Invalid_JobID_Format'; ErrorDescription = "JobID format is invalid.  Expected format: 1.10, 1.10.100, 1.10.1234.5678." }

            }

            else {

                $FoundDuplicate = $state:CurrentJobs.GetEnumerator() | ? { $_['JobID'] -eq $RequestBody['JobID'] }

                if ($FoundDuplicate) {

                    Write-PodeJsonResponse -StatusCode 409 -Value @{ ErrorID = 'Duplicate_JobID'; ErrorDescription = "JobID already exists." }

                }

                else {

                    "No dupe found." | Out-Host

                    # again, ensure we're thread safe
                    Lock-PodeObject -ScriptBlock {

                        "Locked." | Out-Host

                        # $state:CurrentJobs.GetEnumerator() | ? { $_.State -eq 'Stopped' } | Select -First 1 | Out-Host
                        [Version]$JobVersion = $RequestBody['JobID']
                        [Version]$LtsVersion = $state:CurrentConfig.WhatIsLts
                        [Version]$TotVersion = $state:CurrentConfig.WhatIsTot

                        # Need to enhance this to handle LTS and TOT groups based on JobID (which should be a version string)
                        if ($JobVersion.Major -eq $LtsVersion.Major -and $JobVersion.Minor -eq $LtsVersion.Minor) {

                            $Result = $state:CurrentJobs.GetEnumerator() | ? { $_.Name -eq 'LTSGroup' -and $_.State -eq 'Stopped' }

                        }

                        elseif ($JobVersion.Major -eq $TotVersion.Major -and $JobVersion.Minor -eq $TotVersion.Minor) {

                            $Result = $state:CurrentJobs.GetEnumerator() | ? { $_.Name -eq 'TotGroup' -and $_.State -eq 'Stopped' }

                        }

                        else {

                            $Result = $state:CurrentJobs.GetEnumerator() | ? { $_.State -eq 'Stopped' } | Select -First 1

                        }

                        if ($null -eq $Result -or $WebEvent.Query['TestError'] -eq "True") {

                            Write-PodeJsonResponse -StatusCode 406 -Value @{ ErrorID = 'No_Available_Slots'; ErrorDescription = "No available slots." }

                        }

                        else {

                            "Setting job to Group '{0}'." -f $Result.Name | Out-Host

                            $Result.JobID = $RequestBody['JobID']
                            $Result.JobUrl = $RequestBody['JobURL']
                            $Result.State = "Running"

                            $GroupID = $Result.Name

                            "Setting appliances within tracking object" | Out-Host

                            $Result.SetAppliances($state:CurrentConfig.Groups.${GroupID})

                            # Do not monitor an INTERNAL job
                            if ($Result.JobUrl -ne 'INTERNAL') {

                                "Job not 'INTERNAL', getting AppVeyor job state." | Out-Host

                                # Need to connect back to AppVeyors API to get the job start time
                                $JobStatus = Invoke-RestMethod -Uri $Result.JobUrl -Method Get -Headers ([AuthHeaders]::GetAuthHeaders())
                                $Result.Start = $JobStatus.build.started

                            }

                            else {

                                "Job 'INTERNAL'." | Out-Host

                                $Result.Start = [datetime]::now

                            }

                            "Return result." | Out-Host

                            # Will need to add logic here to validate the job is still running by using Invoke-RestMethod to $Result.JobUrl
                            Write-PodeJsonResponse -Value $Result

                            # Need to have Pode to continuously check the job status as a scheduled task
                            # Invoke-PodeTask -Name 'MonitorJob' -ArgumentList @{ GroupID = $Result.Name }

                            "Saving StateConfig." | Out-Host

                            $null = Save-PodeState -Path $cache:PodeStateConfig

                        }

                    }

                    "Done." | Out-Host

                }

            }

        }

    }


    Add-PodeRoute -Method Get -Path '/api/ping' -ScriptBlock {
        Write-PodeJsonResponse -Value @{ 'value' = 'pong' }
    }

    # WEB UI FRONT-END

    Use-PodeWebTemplates -Title 'Example' -Theme Dark
    # # Use-PodeWebTemplates -Title 'Example' -Theme Dark -EndpointName Admin

    $navAppVeyor = New-PodeWebNavLink -Name 'Appveyor' -Icon 'factory' -Url 'https://ci.appveyor.com' -NewTab
    $navDiv = New-PodeWebNavDivider

    Set-PodeWebNavDefault -Items $navAppVeyor, $navDiv

    Set-PodeWebHomePage -Layouts @(
        New-PodeWebHero -Title 'Welcome!' -Message 'This is the home page' -Content @(
            New-PodeWebText -Value 'Here is some text!' -InParagraph -Alignment Center
        )
    )

    Add-PodeWebPage -Name Services -Icon Settings -ScriptBlock {
        $value = $WebEvent.Query['value']
    
        # table of services
        if ([string]::IsNullOrWhiteSpace($value)) {
            New-PodeWebCard -Content @(
                New-PodeWebTable -Name 'Services' -DataColumn Name -Click -ScriptBlock {
                    foreach ($svc in (Get-Service)) {
                        [ordered]@{
                            Name = $svc.Name
                            Status = "$($svc.Status)"
                        }
                    }
                }
            )
        }
    
        # code-block with service info
        else {
            $svc = Get-Service -Name $value | Out-String
    
            New-PodeWebCard -Name "$($value) Details" -Content @(
                New-PodeWebCodeBlock -Value $svc -NoHighlight
            )
        }
    }

}

The Services web page is just for testing purposes and was copied from the Pode.Web documentation, here.

I then issue the following POST to the /api/jobs/checkOut:

$Body = @{
     JobID = "1.10.5555.8900";
     JobURL = "INTERNAL"
 } | ConvertTo-Json

Invoke-RestMethod -Uri https://172.30.231.75:8443/api/jobs/checkOut -Method Post -Body $Body -SkipCertificateCheck -Headers @{"Content-Type" = "application/json"}

This is then displayed on the Pode server console:

No dupe found.
Locked.
Setting job to Group 'Group1'.
Setting appliances within tracking object
Job 'INTERNAL'.
Return result.
Saving StateConfig.
WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 10.
[Warning]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Request timeout reached: 30 seconds
[Warning]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Request timeout reached: 30 seconds
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Disposing Context
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Sending response timed-out
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Response timed-out sent
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Request disposed
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Response disposed
[Verbose] OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Disposing Context
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Sending response timed-out
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Response timed-out sent
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Request disposed
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Response disposed
[Verbose] OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: 3cf913ee-b41d-f21a-c4bd-08907aad20ba] Received request
[Warning]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Request timeout reached: 30 seconds
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Disposing Context
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Sending response timed-out
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Response timed-out sent
[Verbose]: [ContextId: b6942c87-8438-6bed-05af-7d36932aa8ae] Disposing Context
[Verbose] OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Request disposed
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Response disposed
[Verbose]: [ContextId: fd0eb77f-3d0b-019a-cf45-63d9e03d5611] Received request
[Warning]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Request timeout reached: 30 seconds
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Disposing Context
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Sending response timed-out
[Verbose] OperationCanceledException: The operation was canceled.
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Response timed-out sent
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Request disposed
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Response disposed
[Verbose]: [ContextId: 77d9402e-20a6-6faa-d5c2-bb1834da37ec] Received request
[Warning]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Request timeout reached: 30 seconds
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Disposing Context
[Verbose] OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Sending response timed-out
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Response timed-out sent
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Request disposed
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Response disposed
[Verbose]: [ContextId: b926254a-baa6-aa28-b9e6-036f86c84fff] Disposing Context
[Warning]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Request timeout reached: 30 seconds
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Disposing Context
[Verbose] OperationCanceledException: The operation was canceled.
   at System.Threading.CancellationToken.ThrowOperationCanceledException()
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Pode.PodeRequest.Receive(CancellationToken cancellationToken) in C:\Projects\pode-builds\2.11.1\src\Listener\PodeRequest.cs:line 243
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Sending response timed-out
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Response timed-out sent
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Request disposed
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Response disposed
[Verbose]: [ContextId: 03dde4ef-87e0-46ff-af6a-cb4e94f23a11] Disposing Context
[Warning]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Request timeout reached: 30 seconds
[Verbose]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Disposing Context
[Verbose]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Sending response timed-out
[Verbose]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Response timed-out sent
[Verbose]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Request disposed
[Verbose]: [ContextId: 8d79a583-6779-108e-09b1-634d34370609] Response disposed

Prefacing the Save-PodeState call in the Add-PodeRoute with $null = or even piping to Out-Null, the error continues. Removing the Add-PodeWebPage code I don't get the error at all.

Here is state.json:

{
  "CurrentJobs": {
    "Scope": [],
    "Value": [
      {
        "Name": "Group1",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      },
      {
        "Name": "Group2",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      },
      {
        "Name": "Group3",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      },
      {
        "Name": "Group4",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      },
      {
        "Name": "TotGroup",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      },
      {
        "Name": "LTSGroup",
        "Appliances": [
          null,
          null,
          null,
          null
        ],
        "JobID": "",
        "JobUrl": "",
        "State": "Stopped",
        "Start": "0001-01-01T00:00:00",
        "End": "0001-01-01T00:00:00"
      }
    ]
  },
  "CurrentConfig": {
    "Value": {
      "Groups": {
        "Group1": [
          "name1",
          "name2",
          "name3",
          "namefex"
        ],
        "Group2": [
          "name5",
          "name6",
          "name7",
          "namefex2"
        ],
        "Group3": [
          "name9",
          "name10",
          "name11",
          "namefex3"
        ],
        "Group4": [
          "name13",
          "name14",
          "name15",
          "namefex4"
        ],
        "LTS": [
          "namelts1",
          "namelts2",
          "namelts3",
          "nameltsfex"
        ],
        "TOT": [
          "name-tot1",
          "name-tot2",
          "name-tot3",
          "totfex"
        ]
      },
      "version": {
        "ExpectedAPIVersion": 1234,
        "Appliance1": "name1",
        "Appliance2": "name2",
        "Appliance3": "name3",
        "Appliance4": "name4",
        "key_1": "[REDACTED]",
        "key_2": "[REDACTED]",
        "key_3": "[REDACTED]"
      },
      "Config": {
        "RemoteBackupPublicKey": "ssh-rsa [REDACTED]",
        "Key1": "[REDACTED]",
        "Key2": "[REDACTED]",
        "Key3": "[REDACTED]",
        "Key4": "[REDACTED]"
      }
    },
    "Scope": []
  },
  "pode.web.app-path": {
    "Value": "",
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.title": {
    "Value": "Example",
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.logo": {
    "Value": "",
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.favicon": {
    "Value": "/pode.web/images/favicon.ico",
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.no-page-filter": {
    "Value": false,
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.hide-sidebar": {
    "Value": false,
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.social": {
    "Value": {},
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.pages": {
    "Value": {
      "/": {
        "Path": "/",
        "Title": "Home",
        "Layouts": [
          {
            "Title": "Welcome!",
            "CssClasses": "",
            "CssStyles": "",
            "ID": "hero_tgehx",
            "ComponentType": "Layout",
            "Message": "This is the home page",
            "Content": [
              {
                "ID": "txt_fmrxl",
                "Parent": null,
                "NoEvents": true,
                "InParagraph": true,
                "Value": "Here is some text!",
                "CssClasses": "",
                "CssStyles": "",
                "Style": "Normal",
                "ObjectType": "Text",
                "Pronunciation": "",
                "ComponentType": "Element",
                "Alignment": "center"
              }
            ],
            "ObjectType": "Hero"
          }
        ],
        "IsSystem": true,
        "Navigation": null,
        "ObjectType": "Page",
        "DisplayName": "Home",
        "ComponentType": "Page",
        "NoTitle": false,
        "Name": "Home"
      }
    },
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.default-nav": {
    "Value": [
      {
        "ID": "navlink_appveyor",
        "Disabled": false,
        "InDropdown": false,
        "IsDynamic": false,
        "NavType": "Link",
        "Icon": "factory",
        "DisplayName": "Appveyor",
        "ComponentType": "Navigation",
        "Url": "https://ci.appveyor.com",
        "NewTab": true,
        "Name": "Appveyor"
      },
      {
        "InDropdown": false,
        "ComponentType": "Navigation",
        "NavType": "Divider"
      }
    ],
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.endpoint-name": {
    "Value": null,
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.custom-css": {
    "Value": [],
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.custom-js": {
    "Value": [],
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.theme": {
    "Value": "dark",
    "Scope": [
      "pode.web"
    ]
  },
  "pode.web.custom-themes": {
    "Value": {
      "Themes": {},
      "Default": null
    },
    "Scope": [
      "pode.web"
    ]
  }
}

Expected Behavior

I'm trying to implement an API service, and a web UI front end to show the data from the $state:CurrentJobs variable, and need to save its state across restarts.

Platform

  • OS: Windows Server 2016 (10.0.14393.7428)
  • Versions:
    • Pode: 2.11.1
    • Pode.Web: 0.8.3
    • PowerShell: 7.4.5

ChrisLynchHPE avatar Nov 12 '24 20:11 ChrisLynchHPE