functions/Add-JobSchedulerEvent.ps1

function Add-JobSchedulerEvent
{
<#
.SYNOPSIS
Adds an event to a JobScheduler instance.
 
.DESCRIPTION
Events can be added to a JobScheduler Master or Supervisor instance that implements event handling.
 
Should the JobScheduler Master or Supervisor not be accessible then events are stored in
the local file %TEMP%\jobscheduler_events.xml for later dequeueing. Subsequent calls to this
cmdlet will dequeue any previously stored events.
 
.PARAMETER EventClass
Specifies a common name for a set of events that enable event handlers to process multiple events of the
same class. For example, "daily_closing" could be an event class for jobs that should start once day-time
business processes have drawn to a close.
 
Specifies a unique identifier when used together with the -EventId parameter.
 
.PARAMETER EventId
An identifier for an event. Allows event handlers to react to events having a particular ID.
 
Specifies a unique identifier when used together with the -EventClass parameter. An event id is required to be unique
for the same event class.
 
.PARAMETER ExitCode
Specifies the exit code that is added to the event. Usually this signals the execution status of a job,
however, you can assign a specific numeric exit code.
 
Without this parameter being used the $LastExitCode is implicitely assumed, i.e. the last exit code
that was provided from a command or program.
 
Consider that exit codes other than 0 signal failed execution. You can specify allowed exit codes
with the -AllowedExitCodes parameter.
 
Default: last script exit code or failed execution of previous command.
 
.PARAMETER AllowedExitCodes
Specifies a list of exit codes that signal that a job is considered as having run successfully.
This is useful if job scripts provide return values in the form of exit codes and these codes
should not be considered as errors. When adding an event then any exit codes that match one of the
allowed exit codes are set to 0.
 
A range of allowed exit codes is specified by e.g -AllowedExitCodes 1..4 or -AllowedExitCodes 1,2,3,4.
 
.PARAMETER Job
Specifies the name of the job that is assigned to the event.
 
Default: the current job name if the cmdlet is used by a job.
 
.PARAMETER JobChain
Specifies the name of the job chain that is assigned to the event.
 
Default: the current job chain name if the cmdlet is used by a job chain.
 
.PARAMETER Order
Specifies the identifier of the order assigned to the event.
 
Default: the current order identification (order id) if the cmdlet is used by a job chain.
 
.PARAMETER MasterUrl
Specifies the Job Scheduler Master instance URL.
 
The URL consists of the protocol, host name and port, e.g. http://localhost:4454.
 
Default: If used with a job then the CLI will assign by default the JobScheduler Master that the job is running for
otherwise the JobScheduler Master as specified with the Use-JobSchedulerMaster cmdlet will be used.
 
.PARAMETER SupervisorUrl
Specifies a Job Scheduler Supervisor instance URL.
 
Job Scheduler Master instances register with a JobScheduler Supervisor (if configured to do so)
in order to synchronize job configurations. The Supervisor instance receives events, executes the
event handler and starts jobs and job chains for registered JobScheduler Master instances.
 
The URL consists of the protocol, host name and port, e.g. http://localhost:4454.
 
Default: If used with a job then the CLI will assign by default the JobScheduler Supervisor that the
current JobScheduler Master is registered for and otherwise assign the JobScheduler Master.
 
.PARAMETER SupervisorJobChain
Specifies the path of the job chain in the JobScheduler Master or Supervisor instance that implements the event
processor.
 
Default: /sos/events/scheduler_event_service
 
.PARAMETER ExpirationDate
Specifies the point in time for which an event automatically expires: this parameter is considered by the
Event Processor. Should no expiration be considered then use of the -NoExpiration parameter is recommended.
 
The parameters -ExpirationDate, -ExpirationCycle, -ExpirationPeriod, -NoExpiration may not be used at the same time.
 
Default: midnight on the following day in the UTC time zone.
 
.PARAMETER ExpirationCycle
Specifies the time for which the event will expire in the current period.
Periods start at 00:00 and end at 24:00. An expiration cycle of 21:00
specifies 9pm of the current cycle for event expiration.
 
The time zone specified with the -Timezone parameter is applied to the value
of the expiration cycle.
 
Values are specified by use of the format HH:mm:ss.
 
The parameters -ExpirationDate, -ExpirationCycle, -ExpirationPeriod, -NoExpiration may not be used at the same time.
 
.PARAMETER ExpirationPeriod
Specifies the duration after which the event will expire in the current period.
Periods start at 00:00 and end at 24:00. An expiration period of 04:00
specifies that the event will expire 4 hours starting from the current point in time.
 
Values are specified by use of the format HH:mm:ss.
 
The parameters -ExpirationDate, -ExpirationCycle, -ExpirationPeriod, -NoExpiration may not be used at the same time.
 
.PARAMETER Timezone
Specifies the time zone that is applied to the -ExpirationCycle parameter.
This parameter is not used for the -ExpirationDate parameter that implicitly specifies its time zone.
 
A time zone can e.g. be specified like this:
 
  Add-JobSchedulerEvent -ExpirationCycle 04:00 -Timezone (Get-Timezone -Id 'GMT Standard Time') ...
 
Default: The value of the -ExpirationCycle parameters is considered for the UTC time zone.
 
.PARAMETER NoExpiration
Specifies that an event will not expire. Such events have to be removed by event action scripts.
 
.PARAMETER Parameters
Allows additional parameters for event handlers to be specified. Parameters are created using name-value
pairs from a hashmap as e.g. @{'name1'='value1'; 'name2'='value2'}.
 
Parameter names can be freely chosen and event handlers configured to
take account of values handed over. Any number of parameters can be added.
 
.OUTPUTS
This cmdlet returns the XML object of the JobScheduler response.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678
 
Creates an event with the specified event class and event id.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678 -AllowedExitCodes @(1..4)
 
Creates an event with the specified event class and event id. The exit code is implicitely added
from the global $LastExitCode value. Should the exit code be contained in the list of
allowed exit codes then its value is set to 0.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678 -ExpirationPeriod 04:00
 
Creates an event that will expire 4 hrs. from now.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678 -ExpirationCycle 21:00
 
Creates an event that will expire at 9pm of the current day.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678 -ExpirationDate (Get-Date).AddDays(2)
 
Creates an event that will expire two days later.
 
.EXAMPLE
Add-JobSchedulerEvent -EventClass daily_closing -EventId 12345678 -Parameters @{'name1'='value1'; 'name2'='value2'}
 
Creates an event with two additional parameters from a hashtable.
 
.LINK
about_jobscheduler
 
#>

[cmdletbinding()]
param
(
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $EventClass,
    [Parameter(Mandatory=$True,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $EventId,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [int] $ExitCode,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [int[]] $AllowedExitCodes,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $Job,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $JobChain,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $Order,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [Uri] $MasterUrl,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $SupervisorJobChain = '/sos/events/scheduler_event_service',
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [DateTime] $ExpirationDate,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $ExpirationCycle,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [string] $ExpirationPeriod,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [TimeZoneInfo] $Timezone = (Get-Timezone -Id 'UTC'),
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [switch] $NoExpiration,
    [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)]
    [hashtable] $Parameters
)

    Begin
    {
        Approve-JobSchedulerCommand $MyInvocation.MyCommand
        $stopWatch = Start-JobSchedulerStopWatch

        $tmpEventsLocation = "$env:TEMP\jobscheduler.events.log"

        if ( Test-Path $tmpEventsLocation -PathType Leaf )
        {
            [xml] $xmlDoc = '<commands>' + ( Get-Content $tmpEventsLocation ) + '</commands>'
            $commandsNode = $xmlDoc.commands
            $eventCount = $xmlDoc.commands.SelectNodes( 'add_order' ).count
            Write-Warning ".. $($MyInvocation.MyCommand.Name): found $($eventCount) enqueued events, events are processed for dequeueing"
        } else {
            [xml] $xmlDoc  = "<commands/>"
            $commandsNode = $xmlDoc.CreateElement( 'commands' )
            $eventCount = 0
        }

        $countArgs = 0

        if ( $ExpirationDate )
        {
            $countArgs++
        }

        if ( $ExpirationCycle )
        {
            $countArgs++
        }

        if ( $ExpirationPeriod )
        {
            $countArgs++
        }

        if ( $NoExpiration )
        {
            $countArgs++
        }

        if ( $countArgs -gt 1 )
        {
            throw "$($MyInvocation.MyCommand.Name): only one of the parameters -ExpirationDate, -ExpirationCycle, -ExpirationPeriod, -NoExpiration can be specified"
        }
    }

    Process
    {
        if ( !$MasterUrl )
        {
            $MasterUrl = $script:js.Url
        }

        # exit code was not assigned
        if ( !$ExitCode -and $ExitCode -ne 0 )
        {
            # last exit code provided from a command or program
            $ExitCode = $LastExitCode

            # last failed execution
            if ( !$ExitCode -and !$? )
            {
                $ExitCode = 1
            }
        }

        if ( !$EventId )
        {
            $EventId = $ExitCode
        }

        if ( $ExitCode )
        {
            if ( $AllowedExitCodes -contains $ExitCode )
            {
               $ExitCode = 0
            }
        }

        if ( $script:jsOperations )
        {
            if ( !$Job )
            {
                $Job = $spooler_job.name()
            }

            if ( $spooler_task.order() )
            {
                if ( !$JobChain )
                {
                    $JobChain = $spooler_task.order().job_chain().name()
                }

                if ( !$Order )
                {
                    $Order = $spooler_task.order().id()
                }
            }
<#
            if ( !$SupervisorUrl )
            {
                if ( $spooler.supervisor_client() )
                {
                    $SupervisorUrl = "http://$($spooler.supervisor_client().hostname()):$($spooler.supervisor_client.tcp_port())"
                } else {
                    $SupervisorUrl = $MasterUrl
                }
            }
#>

        } else {
            if ( !$Job )
            {
                $Job = $env:SCHEDULER_JOB_NAME
            }

            if ( $env:SCHEDULER_ORDER_ID )
            {
                if ( !$JobChain )
                {
                    $JobChain = $env:SCHEDULER_JOB_CHAIN
                }

                if ( !$Order )
                {
                    $Order = $env:SCHEDULER_ORDER_ID
                }
            }
<#
            if ( !$SupervisorUrl )
            {
                $SupervisorUrl = $MasterUrl
            }
#>

        }

        $currentDate = Get-Date

        $orderNode = $xmlDoc.CreateElement( 'add_order' )
        $orderNode.SetAttribute( 'job_chain', $SupervisorJobChain )

        $paramsNode = $xmlDoc.CreateElement( 'params' )

        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'action' -Value 'add' ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'remote_scheduler_host' -Value $MasterUrl.Host ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'remote_scheduler_port' -Value $MasterUrl.Port ) ) | Out-Null

        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'job_chain' -Value $JobChain ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'order_id' -Value $Order ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'job_name' -Value $Job ) ) | Out-Null

        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'event_class' -Value $EventClass ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'event_id' -Value $EventId ) ) | Out-Null
        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'exit_code' -Value $ExitCode ) ) | Out-Null

        $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'created' -Value ( Get-Date $currentDate -Format 'yyyy-MM-dd HH:mm:ss' ) ) ) | Out-Null

        if ( $ExpirationDate )
        {
           $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'expires' -Value ( Get-Date (Get-Date $ExpirationDate).ToUniversalTime() -Format 'yyyy-MM-dd HH:mm:ss' ) ) ) | Out-Null
        } elseif ( $NoExpiration ) {
           $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'expires' -Value 'never' ) ) | Out-Null
        } elseif ( $ExpirationCycle ) {
            if ( $ExpirationCycle.split(':').count -eq 2 )
            {
                $ExpirationCycle = "$($ExpirationCycle):00"
            }

            $tmpCycle = Get-Date (Get-Date ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]::Now, $Timezone.id)) -Hour $ExpirationCycle.split(':')[0] -Minute $ExpirationCycle.split(':')[1] -Second $ExpirationCycle.split(':')[2]).ToUniversalTime() -Format 'HH:mm:ss'
            $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'expiration_cycle' -Value $tmpCycle ) ) | Out-Null
        } elseif ( $ExpirationPeriod ) {
            $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name 'expiration_period' -Value $ExpirationPeriod ) ) | Out-Null
        }

        if ( $Parameters )
        {
            $Parameters.Keys | ForEach-Object {
                $paramsNode.AppendChild( ( New-JobSchedulerParamNode -XmlDoc $xmlDoc -Name $_ -Value $Parameters.Item($_) ) ) | Out-Null
            }
        }

        $orderNode.AppendChild( $paramsNode ) | Out-Null
        $commandsNode.AppendChild( $orderNode ) | Out-Null

        $e = New-JobSchedulerEventObject
        $e.EventClass = $EventClass
        $e.EventId = $EventId
        $e.ExitCode = $ExitCode
        $e.Job = $Job
        $e.Order = $Order
        $e.JobChain = $JobChain
        $e.ExpirationDate = $ExpirationDate
        $e.ExpirationCycle = $ExpirationCycle
        $e.ExpirationPeriod = $ExpirationPeriod
        $e.Created = Get-Date $currentDate -Format 'yyyy-MM-dd HH:mm:ss'
        # $e.MasterUrl = $MasterUrl

        $e
        $eventCount++
    }

    End
    {
        $xmlDoc.RemoveAll()
        $xmlDecl = $xmlDoc.CreateXmlDeclaration( '1.0', 'ISO-8859-1', $null )
        $xmlDoc.InsertBefore( $xmlDecl, $xmlDoc.DocumentElement ) | Out-Null
        $xmlDoc.AppendChild( $commandsNode ) | Out-Null

        if ( $eventCount )
        {
<#
            if ( $SupervisorUrl )
            {
                Write-Debug ".. $($MyInvocation.MyCommand.Name): sending command to JobScheduler $($SupervisorUrl)"
            } else {
                Write-Debug ".. $($MyInvocation.MyCommand.Name): sending command to JOC Cockpit Web Service"
            }
            Write-Debug ".. $($MyInvocation.MyCommand.Name): sending command: $($commandsNode.outerXml)"
#>

            try
            {
                $response = Invoke-JobSchedulerWebRequestXmlCommand -Command $commandsNode.outerXml
                $response

                if ( Test-Path $tmpEventsLocation -PathType Leaf )
                {
                    Remove-Item $tmpEventsLocation -Force
                }

                Write-Verbose ".. $($MyInvocation.MyCommand.Name): $($eventCount) events added"
            } catch {
                if ( Test-Path $tmpEventsLocation -PathType Leaf )
                {
                    Remove-Item $tmpEventsLocation -Force
                }

                $nodes = Select-Xml -Xml $xmlDoc.commands -XPath '//add_order'
                foreach( $item in $nodes )
                {
                    $item.node.outerXml | Out-File $tmpEventsLocation -Encoding UTF8 -Append
                }

                Write-Warning ".. $($MyInvocation.MyCommand.Name): could not forward $($eventCount) events to JobScheduler, events are stored for later dequeueing in $($tmpEventsLocation): $($_.Exception.Message)"
                Write-Verbose ".. $($MyInvocation.MyCommand.Name): response: $($response)"
            }
        } else {
            Write-Warning "$($MyInvocation.MyCommand.Name): no events found to add"
        }

        Trace-JobSchedulerStopWatch $MyInvocation.MyCommand.Name $stopWatch
    }
}