Class: Tasker::WorkflowStepTransition

Inherits:
ApplicationRecord show all
Defined in:
app/models/tasker/workflow_step_transition.rb

Overview

WorkflowStepTransition represents state transitions for WorkflowStep entities using Statesman

This model stores the audit trail of all workflow step state changes, providing a complete history of step lifecycle events with metadata and timestamps.

Defined Under Namespace

Classes: TransitionDescriptionFormatter

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

configure_database_connections, database_configuration_exists?

Class Method Details

.for_attempt(attempt) ⇒ ActiveRecord::Relation

Find transitions by attempt number



93
94
95
# File 'app/models/tasker/workflow_step_transition.rb', line 93

def for_attempt(attempt)
  ('attempt_number', attempt)
end

.in_time_range(start_time, end_time) ⇒ ActiveRecord::Relation

Find all transitions that occurred within a time range



67
68
69
# File 'app/models/tasker/workflow_step_transition.rb', line 67

def in_time_range(start_time, end_time)
  where(created_at: start_time..end_time)
end

.most_recent_to_state(state) ⇒ WorkflowStepTransition?

Find the most recent transition to a specific state



58
59
60
# File 'app/models/tasker/workflow_step_transition.rb', line 58

def most_recent_to_state(state)
  to_state(state.to_s).order(sort_key: :desc).first
end

.retry_transitionsActiveRecord::Relation

Find retry transitions



83
84
85
86
87
# File 'app/models/tasker/workflow_step_transition.rb', line 83

def retry_transitions
  joins("INNER JOIN #{table_name} prev ON prev.workflow_step_id = #{table_name}.workflow_step_id")
    .where(to_state: 'pending')
    .where('prev.to_state = ? AND prev.sort_key < ?', 'error', arel_table[:sort_key])
end

.statisticsHash

Get transition statistics for analytics



100
101
102
103
104
105
106
107
108
109
# File 'app/models/tasker/workflow_step_transition.rb', line 100

def statistics
  {
    total_transitions: count,
    states: group(:to_state).count,
    recent_activity: where(created_at: 24.hours.ago..Time.current).count,
    retry_attempts: retry_transitions.count,
    average_execution_time: average_execution_time,
    average_time_between_transitions: average_time_between_transitions
  }
end

.with_metadata_value(key, value) ⇒ ActiveRecord::Relation

Find transitions with specific metadata values



76
77
78
# File 'app/models/tasker/workflow_step_transition.rb', line 76

def (key, value)
  where('metadata->:key = :value', key: key.to_s, value: value.to_json.delete('"'))
end

Instance Method Details

#attempt_numberInteger

Get the attempt number for this transition



199
200
201
# File 'app/models/tasker/workflow_step_transition.rb', line 199

def attempt_number
  ('attempt_number', 1)
end

#backoff_infoHash?

Get backoff information if this is an error transition



263
264
265
266
267
268
269
270
271
# File 'app/models/tasker/workflow_step_transition.rb', line 263

def backoff_info
  return nil unless error_transition? && has_metadata?('backoff_until')

  {
    backoff_until: Time.zone.parse(('backoff_until')),
    backoff_seconds: ('backoff_seconds'),
    retry_available: ('retry_available', false)
  }
end

#cancellation_transition?Boolean

Check if this transition represents cancellation



185
186
187
# File 'app/models/tasker/workflow_step_transition.rb', line 185

def cancellation_transition?
  to_state == 'cancelled'
end

#completion_transition?Boolean

Check if this transition represents completion



178
179
180
# File 'app/models/tasker/workflow_step_transition.rb', line 178

def completion_transition?
  %w[complete resolved_manually].include?(to_state)
end

#descriptionString

Get human-readable description of the transition



213
214
215
# File 'app/models/tasker/workflow_step_transition.rb', line 213

def description
  TransitionDescriptionFormatter.format(self)
end

#duration_since_previousFloat?

Get the duration since the previous transition



157
158
159
160
161
162
163
164
165
166
# File 'app/models/tasker/workflow_step_transition.rb', line 157

def duration_since_previous
  previous_transition = self.class.where(workflow_step_id: workflow_step_id)
                            .where(sort_key: ...sort_key)
                            .order(sort_key: :desc)
                            .first

  return nil unless previous_transition

  created_at - previous_transition.created_at
end

#error_transition?Boolean

Check if this transition represents an error state



171
172
173
# File 'app/models/tasker/workflow_step_transition.rb', line 171

def error_transition?
  to_state == 'error'
end

#execution_durationFloat?

Get the execution duration if available



206
207
208
# File 'app/models/tasker/workflow_step_transition.rb', line 206

def execution_duration
  ('execution_duration')
end

#formatted_metadataHash

Get formatted metadata for display



220
221
222
223
224
225
226
227
228
229
230
231
# File 'app/models/tasker/workflow_step_transition.rb', line 220

def 
   = .dup

  # Add computed fields
  ['duration_since_previous'] = duration_since_previous
  ['transition_description'] = description
  ['transition_timestamp'] = created_at.iso8601
  ['step_name'] = step_name
  ['task_id'] = workflow_step.task_id

  
end

#get_metadata(key, default = nil) ⇒ Object

Get metadata value with default



246
247
248
# File 'app/models/tasker/workflow_step_transition.rb', line 246

def (key, default = nil)
  .fetch(key.to_s, default)
end

#has_metadata?(key) ⇒ Boolean

Check if transition has specific metadata



237
238
239
# File 'app/models/tasker/workflow_step_transition.rb', line 237

def has_metadata?(key)
  .key?(key.to_s)
end

#retry_transition?Boolean

Check if this transition represents a retry attempt



192
193
194
# File 'app/models/tasker/workflow_step_transition.rb', line 192

def retry_transition?
  to_state == 'pending' && has_metadata?('retry_attempt')
end

#set_metadata(key, value) ⇒ Object

Set metadata value



255
256
257
258
# File 'app/models/tasker/workflow_step_transition.rb', line 255

def (key, value)
  self. = .merge(key.to_s => value)
  value
end

#step_nameString

Get the step name through the workflow step



150
151
152
# File 'app/models/tasker/workflow_step_transition.rb', line 150

def step_name
  workflow_step.name
end

#taskTask

Get the associated task through the workflow step



145
# File 'app/models/tasker/workflow_step_transition.rb', line 145

delegate :task, to: :workflow_step