lib/Classes/Public/TMBroker.ps1
class TMBroker { #region Non-Static Properties [TMBrokerSetting]$Settings [TMTask]$Task [TMBrokerSubject]$Init [TMSession]$TMSession [TMBrokerEventData]$EventData [TMBrokerStatus]$Status [Object]$Cache [System.Collections.Generic.List[System.Object]]$Subjects #endregion Non-Static Properties #region Constructors # Initializes an empty TMBroker object with default settings TMBroker() { $this.Settings = [TMBrokerSetting]::new() $this.Status = [TMBrokerStatus]::new() $this.EventData = [TMBrokerEventData]::new() } TMBroker([String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskProperty, $_matchingCriteria) $this.Status = [TMBrokerStatus]::new() $this.EventData = [TMBrokerEventData]::new() } TMBroker([String]$_type, [ScriptBlock]$_matchExpression) { $this.Settings = [TMBrokerSetting]::new($_type, $_matchExpression) $this.Status = [TMBrokerStatus]::new() $this.EventData = [TMBrokerEventData]::new() } TMBroker([String]$_type, [TMBrokerTaskFilter]$_taskFilter) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskFilter) $this.Status = [TMBrokerStatus]::new() $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskProperty, $_matchingCriteria, $_timeout, $_pauseSeconds) $this.Status = [TMBrokerStatus]::new($_timeout) $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [ScriptBlock]$_matchExpression, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.Settings = [TMBrokerSetting]::new($_type, $_matchExpression, $_timeout, $_pauseSeconds) $this.Status = [TMBrokerStatus]::new($_timeout) $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [TMBrokerTaskFilter]$_taskFilter, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskFilter, $_timeout, $_pauseSeconds) $this.Status = [TMBrokerStatus]::new($_timeout) $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskProperty, $_matchingCriteria, $_timeout, $_pauseSeconds, $_parallel, $_throttle) $this.Status = [TMBrokerStatus]::new($_timeout, $_throttle) $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [ScriptBlock]$_matchExpression, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.Settings = [TMBrokerSetting]::new($_type, $_matchExpression, $_timeout, $_pauseSeconds, $_parallel, $_throttle) $this.Status = [TMBrokerStatus]::new($_timeout, $_throttle) $this.EventData = [TMBrokerEventData]::new() } TMBroker( [String]$_type, [TMBrokerTaskFilter]$_taskFilter, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.Settings = [TMBrokerSetting]::new($_type, $_taskFilter, $_timeout, $_pauseSeconds, $_parallel, $_throttle) $this.Status = [TMBrokerStatus]::new($_timeout, $_throttle) $this.EventData = [TMBrokerEventData]::new() } #endregion Constructors #region Non-Static Methods <# Method: GetEventData Description: Loads the EventData property using this object's TMSession Parameters: None #> [void]GetEventData() { if (-not $this.TMSession) { throw 'A TM Session is required to invoke this method' } if ($this.Settings.SubjectScope.FilterType -eq 'TaskFilter') { $this.EventData.GetEventData( $this.TMSession.UserContext.project.id, $this.TMSession.UserContext.event.name, $this.TMSession.Name, $this.Settings.SubjectScope.TaskFilter ) } else { $this.EventData.GetEventData( $this.TMSession.UserContext.project.id, $this.TMSession.UserContext.event.name, $this.TMSession.Name ) } } <# Method: GetTaskData Description: Loads all of the broker-related tasks Parameters: TaskId - The Id of broker task #> [void]GetTaskData($TaskId) { if (-not $this.EventData) { throw 'Event data must be loaded before invoking this method' } # Store this Broker Task's data $this.Task = ($this.EventData.Tasks | Where-Object { $_.Id -eq $TaskId }) if (-not $this.Task) { $this.Task = Get-TMTask -Id $TaskId -TMSession $this.TMSession.Name } # Determine if there is an init cache Task $this.GetInitTask() # Get all of the subject tasks $this.GetSubjectTasks() } <# Method: GetInitTask Description: Gets the init task, if present, from the Event's task data Parameters: None #> [void]GetInitTask() { if ($this.Settings.SubjectScope.Type -eq 'Inline') { if (-not $this.Task) { throw 'Broker Task data must be loaded before invoking this method' } $InitTask = ( $this.EventData.Tasks | Where-Object { $_.Id -in $this.Task.Successors.TaskId } | Where-Object -FilterScript $this.Settings.SubjectScope.MatchExpression ) if ($InitTask) { $this.Init = [TMBrokerSubject]::new($InitTask) } } } <# Method: GetSubjectTasks Description: Gets all of the subject tasks that will be managed by the broker from the Event's task data Parameters: None #> [void]GetSubjectTasks() { switch ($this.Settings.SubjectScope.Type) { 'Inline' { if (-not $this.Task -and -not $this.Init) { throw 'Broker Task or Init Task data must be loaded before invoking this method' } # Initialize the subjects list $this.Subjects = [System.Collections.Generic.List[System.Collections.Generic.List[TMBrokerSubject]]]::new() foreach ($TaskId in ($this.Init.Task.Successors.TaskId ?? $this.Task.Successors.TaskId)) { # Initialize a list to hold all of the subject tasks for a specific asset $Workflow = [System.Collections.Generic.List[TMBrokerSubject]]::new() # Find the first/direct successor subject task $SubjectTask = $this.EventData.Tasks | Where-Object { $_.Id -eq $TaskId } $i = 0 while ($SubjectTask) { $i++ # Add the subject task data to the workflow $Workflow.Add([TMBrokerSubject]::new($SubjectTask, $i)) # Look for the next subject task in the workflow $SubjectTask = $this.EventData.Tasks | Where-Object { $_.Id -eq $SubjectTask.Successors.TaskId } } # Record how many tasks are in each asset's workflow $this.Status.WorkflowTaskCount = $i # Add this workflow to the list of subjects $this.Subjects.Add($Workflow) } } 'Service' { # Initialize the subjects list $this.Subjects = [System.Collections.Generic.List[TMBrokerSubject]]::new() # Filter all Tasks down to the specified scope $ServiceSubjectTasks = $this.EventData.Tasks | Where-Object { ($_.id -ne $Broker.task.id ) -and ($_.Action.Id -ne 0) -and ($_.Action.name -notlike '*broker*') -and -not ($_.Action.MethodParams | Where-Object { $_.ParamName -match 'get_' }) } # Apply the match expression to filter tasks further if ($this.Settings.SubjectScope.FilterType -eq 'MatchExpression') { $ServiceSubjectTasks = $ServiceSubjectTasks | Where-Object -FilterScript $this.Settings.SubjectScope.MatchExpression } foreach ($Task in $ServiceSubjectTasks) { $this.Subjects.Add([TMBrokerSubject]::new($Task)) } } default { } } } <# Method: PopulateCache Description: Invokes the Init Task's Action to fill the cache Parameters: None #> [void]PopulateCache() { if (-not $this.Init) { throw 'Init Task data must be loaded before invoking this method' } $this.Init.Invoke($this.TMSession.Name) } <# Method: RefreshTaskStatuses Description: Updates each Task's status using fresh data from TM Parameters: None #> [void]RefreshTaskStatuses() { $TaskIds = [Array]@( $this.Subjects.Task.Id $this.Task.Id $this.Init.Task.Id ) | Where-Object { $_ -gt 0 } $Statement = "find Task by 'id' inList([$($TaskIds -join ', ')]) fetch 'id', 'status', 'lastUpdated'" $TaskStatuses = Invoke-TMQLStatement -TMSession $this.TMSession.Name -Statement $Statement $this.Task.Status = ($TaskStatuses | Where-Object Id -eq $this.Task.Id).Status if ($this.Init) { $this.Init.Task.Status = ($TaskStatuses | Where-Object Id -eq $this.Init.Id).Status } switch ($this.Settings.SubjectScope.Type) { 'Inline' { foreach ($Workflow in $this.Subjects) { foreach ($Subject in $Workflow) { $Subject.Task.Status = ($TaskStatuses | Where-Object Id -eq $Subject.Task.Id).Status } } } 'Service' { foreach ($Subject in $this.Subjects) { $TaskFromTM = $TaskStatuses | Where-Object Id -eq $Subject.Task.Id $Subject.Task.Status = $TaskFromTM.Status $Subject.Task.LastUpdated = $TaskFromTM.LastUpdated ## Review Task states to update throttling settings if ($this.Settings.Parallel) { ## Handle updating Subject Task data based on the status of the task switch ($Subject.Task.Status) { 'Started' { ## Mark the Action as Started so the Broker ignores it for next time $Subject.Action.ExecutionStatus = 'Started' } 'Completed' { ## Check to ensure the broker does not believe it's running completed Tasks if ($this.Status.ActiveSubjects -contains $Subject.Task.Id) { $this.Status.ActiveSubjects.Remove($Subject.Task.Id) } $Subject.Action.ExecutionStatus = 'Successful' } 'Hold' { ## If the Task's status was changed either manually or due to failure, ## reset the execution status so that it can be re-run if ($this.Status.ActiveSubjects -contains $Subject.Task.Id) { $this.Status.ActiveSubjects.Remove($Subject.Task.Id) } $Subject.Action.ExecutionStatus = 'Pending' # Check if a retry was defined in the Action params if ($Subject.Action.Settings.RetryCount -gt 0) { if ($Subject.Task.LastUpdated) { # Check if enough time has elapsed $ElapsedTime = New-TimeSpan -Start $Subject.Task.LastUpdated -End (Get-Date) if ($ElapsedTime.TotalSeconds -ge $Subject.Action.Settings.WaitSeconds) { # Attempt to reset the Task Action and Reset the Task to Ready try { Write-Verbose "Retrying Task #: $($Subject.Task.Number). Retries left: $($Subject.Action.Settings.RetryCount)" Reset-TMTaskAction -TMSession $this.TMSession.Name -TaskId $Subject.Task.Id Set-TMTaskStatus -TMSession $this.TMSession.Name -TaskId $Subject.Task.Id -Status 'Ready' $Subject.Action.Settings.RetryCount-- } catch { Write-Host "Could not retry Task # $($Subject.Task.Number): $($_.Exception.Message)" -ForegroundColor Magenta } } } } } { $_ -in 'Pending', 'Ready' } { ## If the Task's status was changed either manually or due to failure, ## reset the execution status so that it can be re-run if ($this.Status.ActiveSubjects -contains $Subject.Task.Id) { $this.Status.ActiveSubjects.Remove($Subject.Task.Id) } $Subject.Action.ExecutionStatus = 'Pending' } } } } } } } <# Method: RefreshBrokerProgress Description: Updates the TMBrokerProgress properties on this Status object to be used for tracking and progress bars Parameters: None #> [void]RefreshBrokerProgress() { $this.Status.RemainingTasks.Value = ($this.Subjects | Where-Object { $_.Task.Status -ne 'Completed' -and $_.Action.ExecutionStatus -eq 'Pending' }).Count $this.Status.RemainingMinutes.Value = [Math]::Ceiling($this.Settings.Timing.TimeoutMinutes - $this.Settings.Timing.Timer.Elapsed.TotalMinutes) if ($this.Settings.Parallel) { $this.Status.Throttle.Value = $this.Status.ActiveSubjects.Count } } <# Method: Run Description: Executes the scoped subject Tasks Parameters: None #> [void]Run() { $this.Status.RemainingTasks.MaxValue = $this.Subjects.Count Write-Progress -Id 1 -ParentId 0 -Activity 'Subject Tasks' -Status "$($this.Subjects.Count) remaining tasks" -PercentComplete 0 Write-Progress -Id 2 -ParentId 0 -Activity 'Timeout' -Status "$($this.Settings.Timing.TimeoutMinutes) minutes left" -PercentComplete 0 if ($this.Settings.Parallel) { Write-Progress -Id 3 -ParentId 0 -Activity 'Throttle' -Status "$($this.Status.ActiveSubjects.Count) of $($this.Settings.Throttle)" -PercentComplete 0 } $this.Settings.Timing.Timer.Start() $this.RefreshTaskStatuses() $this.RefreshBrokerProgress() while ( ($this.Settings.Timing.Timer.Elapsed.TotalMinutes -lt $this.Settings.Timing.TimeoutMinutes) -and ($this.Subjects | Where-Object { $_.Action.ExecutionStatus -eq 'Pending' }) ) { ## Saftey Check the broker task status in TM, exit if the task status is not Started if ($this.Task.Status -ne 'Started') { throw 'The status of the Broker Task has changed outside of TMConsole' } ## Force a refresh after a few tasks being executed if ($this.Status.TasksExecutedSinceRefresh -ge 3) { $this.RefreshTaskStatuses() $this.Status.TasksExecutedSinceRefresh = 0 } switch ($this.Settings.SubjectScope.Type) { ## Inline Brokers run a workflow step worth of tasks at once 'Inline' { foreach ($Workflow in $this.Subjects) { $Subject = $Workflow | Where-Object { $_.Task.Status -ne 'Completed' -and $_.Action.ExecutionStatus -eq 'Pending' } | Sort-Object Order | Select-Object -First 1 if ($Subject) { $Subject.Invoke($this.TMSession.Name, $this.Cache) $this.Status.TasksExecutedSinceRefresh++ } } } ## Service Brokers run one task at a time, when they become ready 'Service' { ## Get the most preferred actionable subject $PreferredActionableSubject = $this.Subjects | Where-Object { $_.Task.Status -eq 'Ready' -and $_.Action.ExecutionStatus -eq 'Pending' } | Sort-Object { $_.Task."$($this.Settings.ExecutionOrder)" } | Select-Object -First 1 ## Invoke the Most Preferred, Actionable Subject if ($PreferredActionableSubject) { ## Update the local cache so this task won't run again until another refresh from TM $PreferredActionableSubject.Task.Status = 'Started' ## Run a Subject in a normal invocation runspace, but track that task so it 'consumes' one runspace if ($this.Settings.Parallel) { ## Honor Throttling settings if ($this.Status.ActiveSubjects.Count -lt $this.Settings.Throttle) { ## Record the Task ID as belonging to this broker for Throttling $this.Status.ActiveSubjects.Add($PreferredActionableSubject.Task.Id) Write-Verbose "Invoking Task $($PreferredActionableSubject.Task.Id)" $PreferredActionableSubject.InvokeParallel($this.TMSession.Name, $this.Cache) $this.Status.TasksExecutedSinceRefresh++ } } else { ## Invoke this ActionRequest directly, in this runspace $PreferredActionableSubject.Invoke($this.TMSession.Name, $this.Cache) $this.Status.TasksExecutedSinceRefresh++ } } } } # Refresh the progress properties to be output to the TMC UI $this.RefreshBrokerProgress() $ProgressSplat = @{ Id = 1 ParentId = 0 Activity = 'Subject Tasks' Status = "$($this.Status.RemainingTasks.Value) remaining tasks" PercentComplete = $this.Status.RemainingTasks.PercentComplete } Write-Progress @ProgressSplat $ProgressSplat = @{ Id = 2 ParentId = 0 Activity = 'Timeout' Status = "$($this.Status.RemainingMinutes.Value) minutes left" PercentComplete = $this.Status.RemainingMinutes.PercentComplete } Write-Progress @ProgressSplat if ($this.Settings.Parallel) { $ProgressSplat = @{ Id = 3 ParentId = 0 Activity = 'Throttle' Status = "$($this.Status.ActiveSubjects.Count) of $($this.Settings.Throttle)" PercentComplete = $this.Status.Throttle.PercentComplete } Write-Progress @ProgressSplat } ## Sleep, unless there are more tasks ready if ($this.Subjects.Task.Status -notcontains 'Ready') { Start-Sleep -Seconds $this.Settings.Timing.PauseSeconds ## Refresh the Task statuses $this.RefreshTaskStatuses() $this.RefreshBrokerProgress() $this.Status.TasksExecutedSinceRefresh = 0 } } } #endregion Non-Static Methods } class TMBrokerEventData { [TMEvent]$Event [TMTask[]]$Tasks TMBrokerEventData() {} TMBrokerEventData([Int]$ProjectId, [String]$EventName, [String]$TMSession) { $this.GetEventData($ProjectId, $EventName, $TMSession) } [void]GetEventData([Int]$ProjectId, [String]$EventName, [String]$TMSession) { # Get the Event object $this.Event = Get-TMEvent -TMSession $TMSession -ProjectId $ProjectId -Name $EventName # Get all of the broker-related Tasks in the Event $this.Tasks = Get-TMTask -TMSession $TMSession -ProjectId $ProjectId -EventName $EventName } [void]GetEventData([Int]$ProjectId, [String]$EventName, [String]$TMSession, [TMBrokerTaskFilter]$Filter) { # Get the Event object $this.Event = Get-TMEvent -TMSession $TMSession -ProjectId $ProjectId -Name $EventName # Get all of the broker-related Tasks in the Event $TaskSplat = $Filter.ToHashTable() $this.Tasks = Get-TMTask -TMSession $TMSession -ProjectId $ProjectId -EventName $EventName @TaskSplat } } class TMBrokerSubjectScope { [ValidateSet('Service', 'Inline')] [String]$Type [ValidateSet('ActionName', 'AssetClass', 'AssetName', 'AssetType', 'Category', 'Status', 'TaskNumber', 'TaskSpecId', 'Team', 'Title')] hidden [String]$TaskProperty hidden [String[]]$MatchingCriteria [ScriptBlock]$MatchExpression [ValidateSet('TaskFilter', 'MatchExpression')] [String]$FilterType [TMBrokerTaskFilter]$TaskFilter hidden [String]$MatchRegexString static $ValidTypes = @('Service', 'Inline') static $ValidTaskProperties = @('ActionName', 'AssetClass', 'AssetName', 'AssetType', 'Category', 'Status', 'TaskNumber', 'TaskSpecId', 'Team', 'Title') TMBrokerSubjectScope() { $this.Type = 'Inline' $this.TaskFilter = [TMBrokerTaskFilter]::new() $this.TaskFilter.Title.Add('\[Subject\]') $this.FilterType = 'TaskFilter' } TMBrokerSubjectScope([String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria) { $this.Type = $_type $this.TaskProperty = $_taskProperty $this.MatchingCriteria = $_matchingCriteria $this.TaskFilter = [TMBrokerTaskFilter]::new() $_matchingCriteria | ForEach-Object { $this.TaskFilter."$_taskProperty".Add($_) } $this.FilterType = 'TaskFilter' } TMBrokerSubjectScope([String]$_type, [ScriptBlock]$_matchExpression) { $this.Type = $_type $this.MatchExpression = $_matchExpression $this.FilterType = 'MatchExpression' } TMBrokerSubjectScope([String]$_type, [TMBrokerTaskFilter]$_taskFilter) { $this.Type = $_type $this.TaskFilter = $_taskFilter $this.FilterType = 'TaskFilter' } hidden [void]GetMatchExpression() { $this.MatchExpression = [ScriptBlock]::Create("`$_.$($this.TaskProperty) -match '$([TMBrokerSubjectScope]::GetMatchString($this.MatchingCriteria))'") } static [String]GetMatchString([String[]]$_criteria) { return ('(' + ($_criteria -join ')|(') + ')') } } class TMBrokerSetting { [TMBrokerSubjectScope]$SubjectScope [TMBrokerTiming]$Timing [ValidateSet('TaskNumber', 'Score')] [String]$ExecutionOrder [bool]$Parallel = $false [Int]$Throttle = 8 TMBrokerSetting() { $this.Timing = [TMBrokerTiming]::new() $this.SubjectScope = [TMBrokerSubjectScope]::new() $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskProperty, $_matchingCriteria) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [ScriptBlock]$_matchExpression, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_matchExpression) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [TMBrokerTaskFilter]$_taskFilter, [Int]$_timeout, [Int]$_pauseSeconds ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskFilter) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskProperty, $_matchingCriteria) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' $this.Parallel = $_parallel $this.Throttle = $_throttle } TMBrokerSetting( [String]$_type, [ScriptBlock]$_matchExpression, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_matchExpression) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' $this.Parallel = $_parallel $this.Throttle = $_throttle } TMBrokerSetting( [String]$_type, [TMBrokerTaskFilter]$_taskFilter, [Int]$_timeout, [Int]$_pauseSeconds, [bool]$_parallel, [int]$_throttle ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskFilter) $this.Timing = [TMBrokerTiming]::new($_timeout, $_pauseSeconds) $this.ExecutionOrder = 'TaskNumber' $this.Parallel = $_parallel $this.Throttle = $_throttle } TMBrokerSetting( [String]$_type, [String]$_taskProperty, [String[]]$_matchingCriteria ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskProperty, $_matchingCriteria) $this.Timing = [TMBrokerTiming]::new() $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [ScriptBlock]$_matchExpression ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_matchExpression) $this.Timing = [TMBrokerTiming]::new() $this.ExecutionOrder = 'TaskNumber' } TMBrokerSetting( [String]$_type, [TMBrokerTaskFilter]$_taskFilter ) { $this.SubjectScope = [TMBrokerSubjectScope]::new($_type, $_taskFilter) $this.Timing = [TMBrokerTiming]::new() $this.ExecutionOrder = 'TaskNumber' } } class TMBrokerTiming { [System.Int64]$TimeoutMinutes [System.Int64]$PauseSeconds [System.Diagnostics.Stopwatch]$Timer TMBrokerTiming () { $this.TimeoutMinutes = 120 $this.PauseSeconds = 15 $this.Timer = [System.Diagnostics.Stopwatch]::new() } TMBrokerTiming ([System.Int64]$_timeoutMinutes, [System.Int64]$_pauseSeconds) { $this.TimeoutMinutes = $_timeoutMinutes $this.PauseSeconds = $_pauseSeconds $this.Timer = [System.Diagnostics.Stopwatch]::new() } } class TMBrokerStatus { [System.Collections.Generic.List[Int64]]$ActiveSubjects [System.Int64]$WorkflowTaskCount [System.Int64]$TasksExecutedSinceRefresh [TMBrokerProgress]$RemainingTasks [TMBrokerProgress]$RemainingMinutes [TMBrokerProgress]$Throttle TMBrokerStatus () { $this.ActiveSubjects = [System.Collections.Generic.List[Int64]]::new() $this.TasksExecutedSinceRefresh = 0 $this.RemainingTasks = [TMBrokerProgress]::new() $this.RemainingMinutes = [TMBrokerProgress]::new() $this.Throttle = [TMBrokerProgress]::new() } TMBrokerStatus ([System.Int32]$_timeoutMinutes) { $this.ActiveSubjects = [System.Collections.Generic.List[Int64]]::new() $this.TasksExecutedSinceRefresh = 0 $this.RemainingTasks = [TMBrokerProgress]::new() $this.RemainingMinutes = [TMBrokerProgress]::new($_timeoutMinutes) $this.Throttle = [TMBrokerProgress]::new() } TMBrokerStatus ([System.Int32]$_timeoutMinutes, [System.Int32]$_throttle) { $this.ActiveSubjects = [System.Collections.Generic.List[Int64]]::new() $this.TasksExecutedSinceRefresh = 0 $this.RemainingTasks = [TMBrokerProgress]::new() $this.RemainingMinutes = [TMBrokerProgress]::new($_timeoutMinutes) $this.Throttle = [TMBrokerProgress]::new($_throttle) } } class TMBrokerProgress { hidden [System.Int32]$_currentValue = 0 hidden [System.Int32]$_maxValue = 1 [System.Int32]$PercentComplete TMBrokerProgress() { $MemberSplat = @{ Name = 'MaxValue' MemberType = 'ScriptProperty' Value = { return $this._maxValue } SecondValue = { param($value) $this._maxValue = $value $this.CalculatePercentComplete() } } $this | Add-Member @MemberSplat $MemberSplat = @{ Name = 'Value' MemberType = 'ScriptProperty' Value = { return $this._currentValue } SecondValue = { param($value) $this._currentValue = $value $this.CalculatePercentComplete() } } $this | Add-Member @MemberSplat $this.CalculatePercentComplete() } TMBrokerProgress([System.Int32]$maxValue) { $MemberSplat = @{ Name = 'MaxValue' MemberType = 'ScriptProperty' Value = { return $this._maxValue } SecondValue = { param($value) $this._maxValue = $value $this.CalculatePercentComplete() } } $this | Add-Member @MemberSplat $MemberSplat = @{ Name = 'Value' MemberType = 'ScriptProperty' Value = { return $this._currentValue } SecondValue = { param($value) $this._currentValue = $value $this.PercentComplete = [System.Int32][Math]::Ceiling(($value / $this.MaxValue) * 100) } } $this | Add-Member @MemberSplat $this.MaxValue = $maxValue } TMBrokerProgress([System.Int32]$currentValue, [System.Int32]$maxValue) { $MemberSplat = @{ Name = 'MaxValue' MemberType = 'ScriptProperty' Value = { return $this._maxValue } SecondValue = { param($value) $this._maxValue = $value $this.CalculatePercentComplete() } } $this | Add-Member @MemberSplat $MemberSplat = @{ Name = 'Value' MemberType = 'ScriptProperty' Value = { return $this._currentValue } SecondValue = { param($value) $this._currentValue = $value $this.PercentComplete = [System.Int32][Math]::Ceiling(($value / $this.MaxValue) * 100) } } $this | Add-Member @MemberSplat $this.Value = $currentValue $this.MaxValue = $maxValue } [void]CalculatePercentComplete() { $this.PercentComplete = [System.Int32][Math]::Ceiling(($this._currentValue / $this._maxValue) * 100) } } class TMBrokerTaskFilter { [Collections.Generic.List[Int32]]$TaskNumber [Collections.Generic.List[Int32]]$TaskSpecId [Collections.Generic.List[String]]$Status [Collections.Generic.List[String]]$AssetName [Collections.Generic.List[String]]$AssetType [Collections.Generic.List[String]]$AssetClass [Collections.Generic.List[String]]$ActionName [Collections.Generic.List[String]]$Category [Collections.Generic.List[String]]$Title [Collections.Generic.List[String]]$Team TMBrokerTaskFilter() { $this.TaskNumber = [Collections.Generic.List[Int32]]::new() $this.TaskSpecId = [Collections.Generic.List[Int32]]::new() $this.Status = [Collections.Generic.List[String]]::new() $this.AssetName = [Collections.Generic.List[String]]::new() $this.AssetType = [Collections.Generic.List[String]]::new() $this.AssetClass = [Collections.Generic.List[String]]::new() $this.ActionName = [Collections.Generic.List[String]]::new() $this.Category = [Collections.Generic.List[String]]::new() $this.Title = [Collections.Generic.List[String]]::new() $this.Team = [Collections.Generic.List[String]]::new() } [Hashtable]ToHashTable() { $returnHashtable = @{} if ($this.TaskNumber) {$returnHashtable.TaskNumber = $this.TaskNumber} if ($this.TaskSpecId) {$returnHashtable.TaskSpecId = $this.TaskSpecId} if ($this.Status) {$returnHashtable.Status = $this.Status} if ($this.AssetName) {$returnHashtable.AssetName = $this.AssetName} if ($this.AssetType) {$returnHashtable.AssetType = $this.AssetType} if ($this.AssetClass) {$returnHashtable.AssetClass = $this.AssetClass} if ($this.ActionName) {$returnHashtable.ActionName = $this.ActionName} if ($this.Category) {$returnHashtable.Category = $this.Category} if ($this.Title) {$returnHashtable.Title = $this.Title} if ($this.Team) {$returnHashtable.Team = $this.Team} return $returnHashtable } [String]ToString() { return $this.ToHashTable() | ConvertTo-Json } } |