Public/Timers.ps1
<#
.SYNOPSIS Adds a new Timer with logic to periodically invoke. .DESCRIPTION Adds a new Timer with logic to periodically invoke, with options to only run a specific number of times. .PARAMETER Name The Name of the Timer. .PARAMETER Interval The number of seconds to periodically invoke the Timer's ScriptBlock. .PARAMETER ScriptBlock The script for the Timer. .PARAMETER Limit The number of times the Timer should be invoked before being removed. (If 0, it will run indefinitely) .PARAMETER Skip The number of "invokes" to skip before the Timer actually runs. .PARAMETER ArgumentList An array of arguments to supply to the Timer's ScriptBlock. .PARAMETER FilePath A literal, or relative, path to a file containing a ScriptBlock for the Timer's logic. .PARAMETER OnStart If supplied, the timer will trigger when the server starts. .EXAMPLE Add-PodeTimer -Name 'Hello' -Interval 10 -ScriptBlock { 'Hello, world!' | Out-Default } .EXAMPLE Add-PodeTimer -Name 'RunOnce' -Interval 1 -Limit 1 -ScriptBlock { /* logic */ } .EXAMPLE Add-PodeTimer -Name 'RunAfter60secs' -Interval 10 -Skip 6 -ScriptBlock { /* logic */ } .EXAMPLE Add-PodeTimer -Name 'Args' -Interval 2 -ScriptBlock { /* logic */ } -ArgumentList 'arg1', 'arg2' #> function Add-PodeTimer { [CmdletBinding(DefaultParameterSetName = 'Script')] param( [Parameter(Mandatory = $true)] [string] $Name, [Parameter(Mandatory = $true)] [int] $Interval, [Parameter(Mandatory = $true, ParameterSetName = 'Script')] [scriptblock] $ScriptBlock, [Parameter()] [int] $Limit = 0, [Parameter()] [int] $Skip = 0, [Parameter(Mandatory = $true, ParameterSetName = 'File')] [string] $FilePath, [Parameter()] [object[]] $ArgumentList, [switch] $OnStart ) # error if serverless Test-PodeIsServerless -FunctionName 'Add-PodeTimer' -ThrowError # ensure the timer doesn't already exist if ($PodeContext.Timers.Items.ContainsKey($Name)) { # [Timer] Name: Timer already defined throw ($PodeLocale.timerAlreadyDefinedExceptionMessage -f $Name) } # is the interval valid? if ($Interval -le 0) { # [Timer] Name: parameter must be greater than 0 throw ($PodeLocale.timerParameterMustBeGreaterThanZeroExceptionMessage -f $Name, 'Interval') } # is the limit valid? if ($Limit -lt 0) { # [Timer] Name: parameter must be greater than 0 throw ($PodeLocale.timerParameterMustBeGreaterThanZeroExceptionMessage -f $Name, 'Limit') } # is the skip valid? if ($Skip -lt 0) { # [Timer] Name: parameter must be greater than 0 throw ($PodeLocale.timerParameterMustBeGreaterThanZeroExceptionMessage -f $Name, 'Skip') } # if we have a file path supplied, load that path as a scriptblock if ($PSCmdlet.ParameterSetName -ieq 'file') { $ScriptBlock = Convert-PodeFileToScriptBlock -FilePath $FilePath } # check for scoped vars $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState # calculate the next tick time (based on Skip) $NextTriggerTime = [DateTime]::Now.AddSeconds($Interval) if ($Skip -gt 1) { $NextTriggerTime = $NextTriggerTime.AddSeconds($Interval * $Skip) } # add the timer $PodeContext.Timers.Enabled = $true $PodeContext.Timers.Items[$Name] = @{ Name = $Name Interval = $Interval Limit = $Limit Count = 0 Skip = $Skip NextTriggerTime = $NextTriggerTime LastTriggerTime = $null Script = $ScriptBlock UsingVariables = $usingVars Arguments = $ArgumentList OnStart = $OnStart Completed = $false } } <# .SYNOPSIS Adhoc invoke a Timer's logic. .DESCRIPTION Adhoc invoke a Timer's logic outside of its defined interval. This invocation doesn't count towards the Timer's limit. .PARAMETER Name The Name of the Timer. .PARAMETER ArgumentList An array of arguments to supply to the Timer's ScriptBlock. .EXAMPLE Invoke-PodeTimer -Name 'timer-name' #> function Invoke-PodeTimer { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string] $Name, [Parameter()] [object[]] $ArgumentList = $null ) process { # ensure the timer exists if (!$PodeContext.Timers.Items.ContainsKey($Name)) { # Timer 'Name' does not exist throw ($PodeLocale.timerDoesNotExistExceptionMessage -f $Name) } # run timer logic Invoke-PodeInternalTimer -Timer $PodeContext.Timers.Items[$Name] -ArgumentList $ArgumentList } } <# .SYNOPSIS Removes a specific Timer. .DESCRIPTION Removes a specific Timer. .PARAMETER Name The Name of Timer to be removed. .EXAMPLE Remove-PodeTimer -Name 'SaveState' #> function Remove-PodeTimer { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string] $Name ) process { $null = $PodeContext.Timers.Items.Remove($Name) } } <# .SYNOPSIS Removes all Timers. .DESCRIPTION Removes all Timers. .EXAMPLE Clear-PodeTimers #> function Clear-PodeTimers { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param() $PodeContext.Timers.Items.Clear() } <# .SYNOPSIS Edits an existing Timer. .DESCRIPTION Edits an existing Timer's properties, such as interval or scriptblock. .PARAMETER Name The Name of the Timer. .PARAMETER Interval The new Interval for the Timer in seconds. .PARAMETER ScriptBlock The new ScriptBlock for the Timer. .PARAMETER ArgumentList Any new Arguments for the Timer. .EXAMPLE Edit-PodeTimer -Name 'Hello' -Interval 10 #> function Edit-PodeTimer { [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [string] $Name, [Parameter()] [int] $Interval = 0, [Parameter()] [scriptblock] $ScriptBlock, [Parameter()] [object[]] $ArgumentList ) process { # ensure the timer exists if (!$PodeContext.Timers.Items.ContainsKey($Name)) { # Timer 'Name' does not exist throw ($PodeLocale.timerDoesNotExistExceptionMessage -f $Name) } $_timer = $PodeContext.Timers.Items[$Name] # edit interval if supplied if ($Interval -gt 0) { $_timer.Interval = $Interval } # edit scriptblock if supplied if (!(Test-PodeIsEmpty $ScriptBlock)) { $ScriptBlock, $usingVars = Convert-PodeScopedVariables -ScriptBlock $ScriptBlock -PSSession $PSCmdlet.SessionState $_timer.Script = $ScriptBlock $_timer.UsingVariables = $usingVars } # edit arguments if supplied if (!(Test-PodeIsEmpty $ArgumentList)) { $_timer.Arguments = $ArgumentList } } } <# .SYNOPSIS Returns any defined timers. .DESCRIPTION Returns any defined timers, with support for filtering. .PARAMETER Name Any timer Names to filter the timers. .EXAMPLE Get-PodeTimer .EXAMPLE Get-PodeTimer -Name Name1, Name2 #> function Get-PodeTimer { [CmdletBinding()] param( [Parameter()] [string[]] $Name ) $timers = $PodeContext.Timers.Items.Values # further filter by timer names if (($null -ne $Name) -and ($Name.Length -gt 0)) { $timers = @(foreach ($_name in $Name) { foreach ($timer in $timers) { if ($timer.Name -ine $_name) { continue } $timer } }) } # return return $timers } <# .SYNOPSIS Tests whether the passed Timer exists. .DESCRIPTION Tests whether the passed Timer exists by its name. .PARAMETER Name The Name of the Timer. .EXAMPLE if (Test-PodeTimer -Name TimerName) { } #> function Test-PodeTimer { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Name ) return (($null -ne $PodeContext.Timers.Items) -and $PodeContext.Timers.Items.ContainsKey($Name)) } <# .SYNOPSIS Automatically loads timer ps1 files .DESCRIPTION Automatically loads timer ps1 files from either a /timers 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-PodeTimers .EXAMPLE Use-PodeTimers -Path './my-timers' #> function Use-PodeTimers { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param( [Parameter()] [string] $Path ) Use-PodeFolder -Path $Path -DefaultPath 'timers' } |