Public/Tasks.ps1

<#
.SYNOPSIS
Adds a new Task.
 
.DESCRIPTION
Adds a new Task, which can be asynchronously or synchronously invoked.
 
.PARAMETER Name
The Name of the Task.
 
.PARAMETER ScriptBlock
The script for the Task.
 
.PARAMETER FilePath
A literal, or relative, path to a file containing a ScriptBlock for the Task's logic.
 
.PARAMETER ArgumentList
A hashtable of arguments to supply to the Task's ScriptBlock.
 
.EXAMPLE
Add-PodeTask -Name 'Example1' -ScriptBlock { Invoke-SomeLogic }
 
.EXAMPLE
Add-PodeTask -Name 'Example1' -ScriptBlock { return Get-SomeObject }
#>

function Add-PodeTask
{
    [CmdletBinding(DefaultParameterSetName='Script')]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name,

        [Parameter(Mandatory=$true, ParameterSetName='Script')]
        [scriptblock]
        $ScriptBlock,

        [Parameter(Mandatory=$true, ParameterSetName='File')]
        [string]
        $FilePath,

        [Parameter()]
        [hashtable]
        $ArgumentList
    )
    # ensure the task doesn't already exist
    if ($PodeContext.Tasks.Items.ContainsKey($Name)) {
        throw "[Task] $($Name): Task already defined"
    }

    # if we have a file path supplied, load that path as a scriptblock
    if ($PSCmdlet.ParameterSetName -ieq 'file') {
        $ScriptBlock = Convert-PodeFileToScriptBlock -FilePath $FilePath
    }

    # check if the scriptblock has any using vars
    $ScriptBlock, $usingVars = Invoke-PodeUsingScriptConversion -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState

    # check for state/session vars
    $ScriptBlock = Invoke-PodeStateScriptConversion -ScriptBlock $ScriptBlock
    $ScriptBlock = Invoke-PodeSessionScriptConversion -ScriptBlock $ScriptBlock

    # add the task
    $PodeContext.Tasks.Enabled = $true
    $PodeContext.Tasks.Items[$Name] = @{
        Name = $Name
        Script = $ScriptBlock
        UsingVariables = $usingVars
        Arguments = (Protect-PodeValue -Value $ArgumentList -Default @{})
    }
}

<#
.SYNOPSIS
Set the maximum number of concurrent Tasks.
 
.DESCRIPTION
Set the maximum number of concurrent Tasks.
 
.PARAMETER Maximum
The Maximum number of Tasks to run.
 
.EXAMPLE
Set-PodeTaskConcurrency -Maximum 10
#>

function Set-PodeTaskConcurrency
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [int]
        $Maximum
    )

    # error if <=0
    if ($Maximum -le 0) {
        throw "Maximum concurrent tasks must be >=1 but got: $($Maximum)"
    }

    # ensure max > min
    $_min = 1
    if ($null -ne $PodeContext.RunspacePools.Tasks) {
        $_min = $PodeContext.RunspacePools.Tasks.Pool.GetMinRunspaces()
    }

    if ($_min -gt $Maximum) {
        throw "Maximum concurrent tasks cannot be less than the minimum of $($_min) but got: $($Maximum)"
    }

    # set the max tasks
    $PodeContext.Threads.Tasks = $Maximum
    if ($null -ne $PodeContext.RunspacePools.Tasks) {
        $PodeContext.RunspacePools.Tasks.Pool.SetMaxRunspaces($Maximum)
    }
}

<#
.SYNOPSIS
Invoke a Task.
 
.DESCRIPTION
Invoke a Task either asynchronously or synchronously, with support for returning values.
 
.PARAMETER Name
The Name of the Task.
 
.PARAMETER ArgumentList
A hashtable of arguments to supply to the Task's ScriptBlock.
 
.PARAMETER Timeout
A Timeout, in seconds, to abort running the task. (Default: -1 [never timeout])
 
.PARAMETER Wait
If supplied, Pode will wait until the Task has finished executing, and then return any values.
 
.EXAMPLE
Invoke-PodeTask -Name 'Example1' -Wait -Timeout 5
 
.EXAMPLE
$task = Invoke-PodeTask -Name 'Example1'
 
.EXAMPLE
Invoke-PodeTask -Name 'Example1' | Wait-PodeTask -Timeout 3
#>

function Invoke-PodeTask
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]
        $Name,

        [Parameter()]
        [hashtable]
        $ArgumentList = $null,

        [Parameter()]
        [int]
        $Timeout = -1,

        [switch]
        $Wait
    )

    # ensure the task exists
    if (!$PodeContext.Tasks.Items.ContainsKey($Name)) {
        throw "Task '$($Name)' does not exist"
    }

    # run task logic
    $task = Invoke-PodeInternalTask -Task $PodeContext.Tasks.Items[$Name] -ArgumentList $ArgumentList -Timeout $Timeout

    # wait, and return result?
    if ($Wait) {
        return (Wait-PodeTask -Task $task -Timeout $Timeout)
    }

    # return task
    return $task
}

<#
.SYNOPSIS
Removes a specific Task.
 
.DESCRIPTION
Removes a specific Task.
 
.PARAMETER Name
The Name of Task to be removed.
 
.EXAMPLE
Remove-PodeTask -Name 'Example1'
#>

function Remove-PodeTask
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]
        $Name
    )

    $null = $PodeContext.Tasks.Items.Remove($Name)
}

<#
.SYNOPSIS
Removes all Tasks.
 
.DESCRIPTION
Removes all Tasks.
 
.EXAMPLE
Clear-PodeTasks
#>

function Clear-PodeTasks
{
    [CmdletBinding()]
    param()

    $PodeContext.Tasks.Items.Clear()
}

<#
.SYNOPSIS
Edits an existing Task.
 
.DESCRIPTION
Edits an existing Task's properties, such as scriptblock.
 
.PARAMETER Name
The Name of the Task.
 
.PARAMETER ScriptBlock
The new ScriptBlock for the Task.
 
.PARAMETER ArgumentList
Any new Arguments for the Task.
 
.EXAMPLE
Edit-PodeTask -Name 'Example1' -ScriptBlock { Invoke-SomeNewLogic }
#>

function Edit-PodeTask
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string]
        $Name,

        [Parameter()]
        [scriptblock]
        $ScriptBlock,

        [Parameter()]
        [hashtable]
        $ArgumentList
    )

    # ensure the task exists
    if (!$PodeContext.Tasks.Items.ContainsKey($Name)) {
        throw "Task '$($Name)' does not exist"
    }

    $_task = $PodeContext.Tasks.Items[$Name]

    # edit scriptblock if supplied
    if (!(Test-PodeIsEmpty $ScriptBlock)) {
        $ScriptBlock, $usingVars = Invoke-PodeUsingScriptConversion -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState
        $ScriptBlock = Invoke-PodeStateScriptConversion -ScriptBlock $ScriptBlock
        $ScriptBlock = Invoke-PodeSessionScriptConversion -ScriptBlock $ScriptBlock
        $_task.Script = $ScriptBlock
        $_task.UsingVariables = $usingVars
    }

    # edit arguments if supplied
    if (!(Test-PodeIsEmpty $ArgumentList)) {
        $_task.Arguments = $ArgumentList
    }
}

<#
.SYNOPSIS
Returns any defined Tasks.
 
.DESCRIPTION
Returns any defined Tasks, with support for filtering.
 
.PARAMETER Name
Any Task Names to filter the Tasks.
 
.EXAMPLE
Get-PodeTask
 
.EXAMPLE
Get-PodeTask -Name Example1, Example2
#>

function Get-PodeTask
{
    [CmdletBinding()]
    param(
        [Parameter()]
        [string[]]
        $Name
    )

    $tasks = $PodeContext.Tasks.Items.Values

    # further filter by task names
    if (($null -ne $Name) -and ($Name.Length -gt 0)) {
        $tasks = @(foreach ($_name in $Name) {
            foreach ($task in $tasks) {
                if ($task.Name -ine $_name) {
                    continue
                }

                $task
            }
        })
    }

    # return
    return $tasks
}

<#
.SYNOPSIS
Automatically loads task ps1 files
 
.DESCRIPTION
Automatically loads task ps1 files from either a /tasks folder, or a custom folder. Saves space dot-sourcing them all one-by-one.
 
.PARAMETER Path
Optional Path to a folder containing ps1 files, can be relative or literal.
 
.EXAMPLE
Use-PodeTasks
 
.EXAMPLE
Use-PodeTasks -Path './my-tasks'
#>

function Use-PodeTasks
{
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]
        $Path
    )

    Use-PodeFolder -Path $Path -DefaultPath 'tasks'
}

<#
.SYNOPSIS
Close and dispose of a Task.
 
.DESCRIPTION
Close and dispose of a Task, even if still running.
 
.PARAMETER Task
The Task to be closed.
 
.EXAMPLE
Invoke-PodeTask -Name 'Example1' | Close-PodeTask
#>

function Close-PodeTask
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [hashtable]
        $Task
    )

    Close-PodeTaskInternal -Result $Task
}

<#
.SYNOPSIS
Test if a running Task has completed.
 
.DESCRIPTION
Test if a running Task has completed.
 
.PARAMETER Task
The Task to be check.
 
.EXAMPLE
Invoke-PodeTask -Name 'Example1' | Test-PodeTaskCompleted
#>

function Test-PodeTaskCompleted
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [hashtable]
        $Task
    )

    return [bool]$Task.Runspace.Handler.IsCompleted
}

<#
.SYNOPSIS
Waits for a task to finish, and returns a result if there is one.
 
.DESCRIPTION
Waits for a task to finish, and returns a result if there is one.
 
.PARAMETER Task
The task to wait on.
 
.PARAMETER Timeout
An optional Timeout in milliseconds.
 
.EXAMPLE
$context = Wait-PodeTask -Task $listener.GetContextAsync()
 
.EXAMPLE
$result = Invoke-PodeTask -Name 'Example1' | Wait-PodeTask
#>

function Wait-PodeTask
{
    [CmdletBinding()]
    [OutputType([object])]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        $Task,

        [Parameter()]
        [int]
        $Timeout = -1
    )

    if ($Task -is [System.Threading.Tasks.Task]) {
        return (Wait-PodeNetTaskInternal -Task $Task -Timeout $Timeout)
    }

    if ($Task -is [hashtable]) {
        return (Wait-PodeTaskInternal -Task $Task -Timeout $Timeout)
    }

    throw "Task type is invalid, expected either [System.Threading.Tasks.Task] or [hashtable]"
}