Skip to content

Commit

Permalink
Added Sse documentation
Browse files Browse the repository at this point in the history
Move not essential property from route
  • Loading branch information
mdaneri committed Sep 25, 2024
1 parent 4c8f460 commit 67e75fb
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 90 deletions.
32 changes: 27 additions & 5 deletions docs/Tutorials/Routes/Async/Utilities/Progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,35 @@ The `Set-PodeAsyncRouteProgress` function manages the progress of an asynchronou
- **Start and End Progress with Ticks**: Define a starting and ending progress value, with optional steps to increment progress. Use ticks to advance the progress in this scenario.
- **Time-based Progress**: Automatically increment progress over a specified duration with interval-based ticks.
- **Set Specific Progress Value**: Directly set the progress to a specific value.
- **SSE Integration**: If `Server-Sent Events (Sse)` is configured for the route, a 'pode.progress' event is automatically sent to the web client, providing real-time updates on the task's progress.


#### Example Usage



##### Start and End Progress with Ticks

```powershell
Add-PodeRoute -PassThru -Method Post -Path '/process-data' -ScriptBlock {
$data = Get-PodeBody
Set-PodeAsyncRouteProgress -Start 0 -End 100 -Steps 5
# Simulate long-running task
for ($i = 0; $i -le 100; $i += 5) {
Set-PodeAsyncRouteProgress -Tick
Start-Sleep 1
}
return @{ message = "Processing completed" }
} | Set-PodeAsyncRoute
```

In this example:
- An async route processes data and sends progress updates using `Set-PodeAsyncRouteProgress`.

##### Multiple Start and End Progress with Ticks

```powershell
Add-PodeRoute -PassThru -Method Get -Path '/SumOfSquareRoot' -ScriptBlock {
$start = [int](Get-PodeHeader -Name 'Start')
Expand Down Expand Up @@ -46,6 +70,8 @@ In this example:
- The first progress runs from 0 to 80 with a default step of 1, representing progress as a decimal number.
- The second progress runs from 80 to 100 with a step of 4, also representing progress as a decimal number.



##### Time-based Progress

```powershell
Expand Down Expand Up @@ -96,8 +122,4 @@ Add-PodeRoute -PassThru -Method Get '/process' {
$progress = Get-PodeAsyncRouteProgress
Write-PodeHost "Current Progress: $progress"
} | Set-PodeAsyncRoute -ResponseContentType 'application/json'
```

#### Parameters

This function is intended to be used inside an asynchronous route scriptblock to get the current progress of the task.
```
91 changes: 91 additions & 0 deletions docs/Tutorials/Routes/Async/Utilities/SSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## Server-Sent Events (SSE)

Server-Sent Events (SSE) can be integrated with asynchronous routes to enable real-time updates to the client. This feature is especially useful for long-running tasks, where clients can receive continuous feedback without needing to poll the server.

### Key Features

- **Real-time Server Push**: Sends updates from the server to the client in real-time, keeping the client informed about the progress or completion of asynchronous tasks.
- **Progress and Task Completion Events**: Automatically trigger events like `pode.taskCompleted` to notify clients when a task finishes, with the option to include result data or error details.
- **Customizable SSE Groups**: SSE connections can be grouped, either by the route’s path or by a custom group name, for better organization and management.
- **Send Task Results**: Optionally include the result of the asynchronous task in the SSE event when the task completes.
- **Seamless Async Route Integration**: SSE routes work alongside async routes, enabling instant feedback without additional complexity.

### Example Use Cases

#### Real-time Progress Updates

When processing long-running tasks such as file uploads, data processing, or background calculations, SSE provides a way to continuously send updates to the client. As the task progresses, events are pushed to inform the client of the current state, eliminating the need for polling.

##### Example: Adding SSE to a Progressing Task

```powershell
Add-PodeRoute -PassThru -Method Post -Path '/process-data' -ScriptBlock {
$data = Get-PodeBody
Set-PodeAsyncRouteProgress -Start 0 -End 100 -Steps 5
# Simulate long-running task
for ($i = 0; $i -le 100; $i += 5) {
Set-PodeAsyncRouteProgress -Tick
Start-Sleep 1
}
return @{ message = "Processing completed" }
} | Set-PodeAsyncRoute -MaxRunspaces 2 -PassThru |
Add-PodeAsyncRouteSse -SseGroup 'DataProcess'
```

In this example:
- An async route processes data and sends progress updates using `Set-PodeAsyncRouteProgress`.
- The route is enabled for SSE, pushing real-time progress events to clients grouped under 'DataProcess'.

#### Task Completion and Result Delivery

Once an async task completes, an SSE event can automatically be sent to the client with the task's result or an error if it failed. This ensures that clients are immediately informed when a task has finished, without requiring them to repeatedly check the task’s status.

##### Example: Task Completion with Result Transmission

```powershell
Add-PodeRoute -PassThru -Method Get -Path '/generate-report' -ScriptBlock {
# Simulate long-running report generation
Start-Sleep 10
return @{ report = "Report generation completed" }
} | Set-PodeAsyncRoute -MaxRunspaces 3 -PassThru |
Add-PodeAsyncRouteSse -SendResult
```

In this example:
- A GET route generates a report asynchronously.
- The SSE sends the final report result to the client upon task completion.

#### Example: Sending Custom SSE Events with Delays

```powershell
Add-PodeRoute -PassThru -Method Get -Path '/sse' -ScriptBlock {
# First SSE event with a message about the current time
$msg = "Start - Hello there! The datetime is: $([datetime]::Now.TimeOfDay)"
Send-PodeSseEvent -Data $msg -FromEvent
# Simulate a delay between messages (10 seconds)
Start-Sleep -Seconds 10
# Final SSE event after all operations are done
$msg = "End - Hello there! The datetime is: $([datetime]::Now.TimeOfDay)"
Send-PodeSseEvent -Data $msg -FromEvent
# Return a JSON response to the client indicating the operation is complete
return @{ message = 'Done' }
} | Set-PodeAsyncRoute -ResponseContentType 'application/json' -MaxRunspaces 3 -PassThru |
Add-PodeAsyncRouteSse -SseGroup 'Test events' -SendResult
```

In this example:
- Custom SSE events are sent to the client at specific intervals with messages that include the current time.
- After a delay, a final SSE event is sent, and the client receives a JSON response once the task is complete.

### Parameters

- **Route**: A hashtable array representing the async route(s) to which SSE functionality will be added. This parameter is mandatory and accepts pipeline input.
- **PassThru**: If specified, the modified route object is returned after adding the SSE route.
- **SseGroup**: The group name for the SSE connection. If not provided, the route’s path will be used as the group name.
- **SendResult**: If specified, the result of the async operation will be sent to the client when the task completes.
2 changes: 2 additions & 0 deletions docs/Tutorials/Routes/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ The following is the structure of the Route object internally, as well as the ob
| OpenApi | hashtable[] | The OpenAPI definition/settings for the route |
| Path | string | The path of the route - this path will have regex in place of route parameters |
| TransferEncoding | string | The transfer encoding to use when parsing the payload in the request |
| IsAsync | bool | The route is Async |


Static routes have a slightly different format:

Expand Down
17 changes: 16 additions & 1 deletion examples/Web-AsyncRouteSse.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,47 @@ catch { throw }
# or just:
# Import-Module Pode


# Start the Pode server with 6 threads
Start-PodeServer -Threads 6 {

# Add an HTTP endpoint on localhost:8081
Add-PodeEndpoint -Address localhost -Port 8081 -Protocol Http -DualMode
# Enable logging for asynchronous SSE events, logs stored in the script's path under /logs
New-PodeLoggingMethod -name 'asyncSse' -File -Path "$ScriptPath/logs" | Enable-PodeErrorLogging

# Define an asynchronous SSE route at the '/sse' path
Add-PodeRoute -PassThru -Method Get -Path '/sse' -ScriptBlock {
# Set progress for the asynchronous route, simulating progress updates over 23 seconds
Set-PodeAsyncRouteProgress -IntervalSeconds 2 -DurationSeconds 23 -MaxProgress 100

# First SSE event with a message about the current time
$msg = "Start - Hello there! The datetime is: $([datetime]::Now.TimeOfDay)"
Send-PodeSseEvent -Data $msg -FromEvent

# Simulate a delay between messages (10 seconds)
for ($i = 0; $i -lt 10; $i++) {
Start-Sleep -Seconds 1
}

# Second SSE event with a new message after the delay
$msg = "InTheMiddle - Hello there! The datetime is: $([datetime]::Now.TimeOfDay)"
Send-PodeSseEvent -Data $msg -FromEvent

# Another delay between the second and final messages
for ($i = 0; $i -lt 10; $i++) {
Start-Sleep -Seconds 1
}

# Final SSE event after all operations are done
$msg = "End - Hello there! The datetime is: $([datetime]::Now.TimeOfDay)"
Send-PodeSseEvent -Data $msg -FromEvent

# Return a JSON response to the client indicating the operation is complete
return @{'message' = 'Done' }
} | Set-PodeAsyncRoute -ResponseContentType 'application/json' -MaxRunspaces 4 -PassThru |
Add-PodeAsyncRouteSse -SseGroup 'Test events' -SendResult

# Add a static route to serve files located in the /AsyncRoute directory
Add-PodeStaticRoute -Path '/' -File "$ScriptPath/AsyncRoute"

}
4 changes: 1 addition & 3 deletions src/Private/AsyncRoute.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -733,10 +733,8 @@ function Get-PodeAsyncRouteSetScriptBlock {
# Retrieve the task to be executed asynchronously
$asyncRouteTask = $PodeContext.AsyncRoutes.Items[$WebEvent.Route.AsyncRouteId]

# Invoke the internal async route task
# $asyncOperation = Invoke-PodeAsyncRoute
# Generate an Id for the async route task, using the provided IdGenerator or a new GUID
$id = Invoke-PodeScriptBlock -ScriptBlock $WebEvent.Route.AsyncRouteTaskIdGenerator -Return
$id = Invoke-PodeScriptBlock -ScriptBlock $asyncRouteTask.AsyncRouteTaskIdGenerator -Return

# Make a deepcopy of webEvent
$webEventClone = [System.Collections.Concurrent.ConcurrentDictionary[string, PSObject]]::new()
Expand Down
Loading

0 comments on commit 67e75fb

Please sign in to comment.