tasker-engine 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (605) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +443 -0
  4. data/Rakefile +10 -0
  5. data/app/controllers/tasker/analytics_controller.rb +179 -0
  6. data/app/controllers/tasker/application_controller.rb +45 -0
  7. data/app/controllers/tasker/graphql_controller.rb +193 -0
  8. data/app/controllers/tasker/handlers_controller.rb +217 -0
  9. data/app/controllers/tasker/health_controller.rb +229 -0
  10. data/app/controllers/tasker/metrics_controller.rb +111 -0
  11. data/app/controllers/tasker/page_sort.rb +97 -0
  12. data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
  13. data/app/controllers/tasker/tasks_controller.rb +123 -0
  14. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  15. data/app/graphql/examples/all_tasks.graphql +22 -0
  16. data/app/graphql/examples/pending_tasks.graphql +23 -0
  17. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  18. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  19. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  20. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  21. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  22. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  24. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  25. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  26. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  27. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  29. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  30. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  32. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  33. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  34. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  35. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  36. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  38. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  39. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  40. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  41. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  42. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  43. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  44. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  45. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  46. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  47. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  48. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  49. data/app/graphql/tasker/queries/base_query.rb +9 -0
  50. data/app/graphql/tasker/queries/helpers.rb +16 -0
  51. data/app/graphql/tasker/queries/one_step.rb +24 -0
  52. data/app/graphql/tasker/queries/one_task.rb +18 -0
  53. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  54. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  55. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  56. data/app/jobs/tasker/application_job.rb +8 -0
  57. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  58. data/app/jobs/tasker/task_runner_job.rb +224 -0
  59. data/app/models/tasker/annotation_type.rb +26 -0
  60. data/app/models/tasker/application_record.rb +70 -0
  61. data/app/models/tasker/dependent_system.rb +26 -0
  62. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  63. data/app/models/tasker/diagram/edge.rb +106 -0
  64. data/app/models/tasker/diagram/flowchart.rb +137 -0
  65. data/app/models/tasker/diagram/node.rb +99 -0
  66. data/app/models/tasker/named_step.rb +41 -0
  67. data/app/models/tasker/named_task.rb +121 -0
  68. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  69. data/app/models/tasker/step_dag_relationship.rb +65 -0
  70. data/app/models/tasker/step_readiness_status.rb +59 -0
  71. data/app/models/tasker/task.rb +424 -0
  72. data/app/models/tasker/task_annotation.rb +36 -0
  73. data/app/models/tasker/task_diagram.rb +332 -0
  74. data/app/models/tasker/task_execution_context.rb +29 -0
  75. data/app/models/tasker/task_namespace.rb +41 -0
  76. data/app/models/tasker/task_transition.rb +235 -0
  77. data/app/models/tasker/workflow_step.rb +461 -0
  78. data/app/models/tasker/workflow_step_edge.rb +94 -0
  79. data/app/models/tasker/workflow_step_transition.rb +434 -0
  80. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  81. data/app/serializers/tasker/handler_serializer.rb +109 -0
  82. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  83. data/app/serializers/tasker/task_serializer.rb +168 -0
  84. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  85. data/app/services/tasker/analytics_service.rb +409 -0
  86. data/app/views/tasker/task/_diagram.html.erb +32 -0
  87. data/config/initializers/dry_struct.rb +11 -0
  88. data/config/initializers/statesman.rb +6 -0
  89. data/config/initializers/tasker_orchestration.rb +17 -0
  90. data/config/initializers/time_formats.rb +4 -0
  91. data/config/routes.rb +34 -0
  92. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  93. data/config/tasker/system_events.yml +305 -0
  94. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  95. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  96. data/db/functions/get_slowest_steps_v01.sql +82 -0
  97. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  98. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  99. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  100. data/db/functions/get_system_health_counts_v01.sql +108 -0
  101. data/db/functions/get_task_execution_context_v01.sql +108 -0
  102. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  103. data/db/init/schema.sql +2277 -0
  104. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  105. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  106. data/docs/APPLICATION_GENERATOR.md +384 -0
  107. data/docs/AUTH.md +1780 -0
  108. data/docs/CIRCUIT_BREAKER.md +224 -0
  109. data/docs/DEVELOPER_GUIDE.md +2665 -0
  110. data/docs/EVENT_SYSTEM.md +637 -0
  111. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  112. data/docs/FLOW_CHART.md +149 -0
  113. data/docs/HEALTH.md +542 -0
  114. data/docs/METRICS.md +731 -0
  115. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  116. data/docs/OVERVIEW.md +552 -0
  117. data/docs/QUICK_START.md +270 -0
  118. data/docs/REGISTRY_SYSTEMS.md +373 -0
  119. data/docs/REST_API.md +632 -0
  120. data/docs/ROADMAP.md +221 -0
  121. data/docs/SQL_FUNCTIONS.md +1408 -0
  122. data/docs/TASK_DIAGRAM.md +252 -0
  123. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  124. data/docs/TELEMETRY.md +795 -0
  125. data/docs/TROUBLESHOOTING.md +756 -0
  126. data/docs/TaskHandlerGenerator.html +255 -0
  127. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  128. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  129. data/docs/Tasker/Analysis.html +117 -0
  130. data/docs/Tasker/AnalyticsController.html +450 -0
  131. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  132. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  133. data/docs/Tasker/AnalyticsService.html +2221 -0
  134. data/docs/Tasker/AnnotationType.html +137 -0
  135. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  136. data/docs/Tasker/ApplicationController.html +147 -0
  137. data/docs/Tasker/ApplicationJob.html +128 -0
  138. data/docs/Tasker/ApplicationRecord.html +378 -0
  139. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  140. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  141. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  142. data/docs/Tasker/Authentication/Interface.html +560 -0
  143. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  144. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  145. data/docs/Tasker/Authentication.html +119 -0
  146. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  147. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  148. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  149. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  150. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
  151. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  152. data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
  153. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  154. data/docs/Tasker/Authorization.html +582 -0
  155. data/docs/Tasker/CacheCapabilities.html +167 -0
  156. data/docs/Tasker/CacheStrategy.html +1297 -0
  157. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  158. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  159. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  160. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  161. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  162. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  163. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  164. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  165. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  166. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  167. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  168. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  169. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  170. data/docs/Tasker/Concerns.html +117 -0
  171. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  172. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  173. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  174. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  175. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  176. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  177. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  178. data/docs/Tasker/Configuration.html +1669 -0
  179. data/docs/Tasker/ConfigurationError.html +143 -0
  180. data/docs/Tasker/ConfiguredTask.html +514 -0
  181. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  182. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  183. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  184. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  185. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  186. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  187. data/docs/Tasker/Constants/StepEvents.html +177 -0
  188. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  189. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  190. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  191. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  192. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  193. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  194. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  195. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  196. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  197. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  198. data/docs/Tasker/Constants/TestEvents.html +163 -0
  199. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  200. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  201. data/docs/Tasker/Constants.html +561 -0
  202. data/docs/Tasker/DependentSystem.html +137 -0
  203. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  204. data/docs/Tasker/DetectorRegistry.html +598 -0
  205. data/docs/Tasker/Diagram/Edge.html +1191 -0
  206. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  207. data/docs/Tasker/Diagram/Node.html +1165 -0
  208. data/docs/Tasker/Diagram.html +117 -0
  209. data/docs/Tasker/Engine.html +215 -0
  210. data/docs/Tasker/Error.html +139 -0
  211. data/docs/Tasker/Events/Bus.html +1226 -0
  212. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  213. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  214. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  215. data/docs/Tasker/Events/Catalog.html +1291 -0
  216. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  217. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  218. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  219. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  220. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  221. data/docs/Tasker/Events/Publisher.html +365 -0
  222. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  223. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  224. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  225. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  226. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  227. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  228. data/docs/Tasker/Events/Subscribers.html +117 -0
  229. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  230. data/docs/Tasker/Events.html +294 -0
  231. data/docs/Tasker/EventsGenerator.html +459 -0
  232. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  233. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  234. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  235. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  236. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  237. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  238. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  239. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  240. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  241. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  242. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  243. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  244. data/docs/Tasker/Functions.html +117 -0
  245. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  246. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  247. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  248. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  249. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  250. data/docs/Tasker/Generators.html +117 -0
  251. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  252. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  255. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  256. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  259. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  260. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  261. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  262. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  265. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  266. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  268. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  269. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  270. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  271. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  272. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  273. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  274. data/docs/Tasker/GraphQLTypes.html +130 -0
  275. data/docs/Tasker/GraphqlController.html +251 -0
  276. data/docs/Tasker/HandlerFactory.html +1518 -0
  277. data/docs/Tasker/HandlerSerializer.html +682 -0
  278. data/docs/Tasker/HandlersController.html +574 -0
  279. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  280. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  281. data/docs/Tasker/Health/StatusChecker.html +653 -0
  282. data/docs/Tasker/Health.html +117 -0
  283. data/docs/Tasker/HealthController.html +523 -0
  284. data/docs/Tasker/IdentityStrategy.html +276 -0
  285. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  286. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  287. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  288. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  289. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  290. data/docs/Tasker/LifecycleEvents.html +799 -0
  291. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  292. data/docs/Tasker/Logging.html +115 -0
  293. data/docs/Tasker/MetricsController.html +293 -0
  294. data/docs/Tasker/MetricsExportJob.html +414 -0
  295. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  296. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  297. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  298. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  299. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  300. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  301. data/docs/Tasker/Mutations.html +117 -0
  302. data/docs/Tasker/NamedStep.html +216 -0
  303. data/docs/Tasker/NamedTask.html +910 -0
  304. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  305. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  306. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  307. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  308. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  309. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  310. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  311. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  312. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  313. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  314. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  315. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  316. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  322. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  323. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  324. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  325. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  326. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  327. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  328. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  329. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  330. data/docs/Tasker/Orchestration.html +130 -0
  331. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  332. data/docs/Tasker/PageSort.html +247 -0
  333. data/docs/Tasker/PermanentError.html +518 -0
  334. data/docs/Tasker/ProceduralError.html +147 -0
  335. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  336. data/docs/Tasker/Queries/AllTasks.html +221 -0
  337. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  338. data/docs/Tasker/Queries/Helpers.html +187 -0
  339. data/docs/Tasker/Queries/OneStep.html +225 -0
  340. data/docs/Tasker/Queries/OneTask.html +217 -0
  341. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  342. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  343. data/docs/Tasker/Queries.html +119 -0
  344. data/docs/Tasker/Railtie.html +124 -0
  345. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  346. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  347. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  348. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  349. data/docs/Tasker/Registry/RegistryError.html +139 -0
  350. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  351. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  352. data/docs/Tasker/Registry/ValidationError.html +132 -0
  353. data/docs/Tasker/Registry.html +119 -0
  354. data/docs/Tasker/RetryableError.html +515 -0
  355. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  356. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  357. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  358. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  359. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  360. data/docs/Tasker/StateMachine.html +602 -0
  361. data/docs/Tasker/StepDagRelationship.html +657 -0
  362. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  363. data/docs/Tasker/StepHandler/Api.html +884 -0
  364. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  365. data/docs/Tasker/StepHandler/Base.html +970 -0
  366. data/docs/Tasker/StepHandler.html +119 -0
  367. data/docs/Tasker/StepReadinessStatus.html +836 -0
  368. data/docs/Tasker/Task.html +2575 -0
  369. data/docs/Tasker/TaskAnnotation.html +137 -0
  370. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  371. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  372. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  373. data/docs/Tasker/TaskBuilder.html +764 -0
  374. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  375. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  376. data/docs/Tasker/TaskDiagram.html +548 -0
  377. data/docs/Tasker/TaskDiagramsController.html +240 -0
  378. data/docs/Tasker/TaskExecutionContext.html +469 -0
  379. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  380. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  381. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  382. data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
  383. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  384. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  385. data/docs/Tasker/TaskHandler.html +271 -0
  386. data/docs/Tasker/TaskNamespace.html +312 -0
  387. data/docs/Tasker/TaskRunnerJob.html +406 -0
  388. data/docs/Tasker/TaskSerializer.html +474 -0
  389. data/docs/Tasker/TaskTransition.html +1517 -0
  390. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  391. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  392. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  393. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  394. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  395. data/docs/Tasker/TasksController.html +595 -0
  396. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  397. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  398. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  399. data/docs/Tasker/Telemetry/Events.html +115 -0
  400. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  401. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  402. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  403. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  404. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  405. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  406. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  407. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  408. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  409. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  410. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  411. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  412. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  413. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  414. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  415. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  416. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  417. data/docs/Tasker/Telemetry.html +130 -0
  418. data/docs/Tasker/Types/AuthConfig.html +886 -0
  419. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  420. data/docs/Tasker/Types/BaseConfig.html +227 -0
  421. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  422. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  423. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  424. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  425. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  426. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  427. data/docs/Tasker/Types/GraphEdge.html +517 -0
  428. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  429. data/docs/Tasker/Types/GraphNode.html +694 -0
  430. data/docs/Tasker/Types/HealthConfig.html +784 -0
  431. data/docs/Tasker/Types/StepSequence.html +353 -0
  432. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  433. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  434. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  435. data/docs/Tasker/Types.html +154 -0
  436. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  437. data/docs/Tasker/WorkflowStep.html +2724 -0
  438. data/docs/Tasker/WorkflowStepEdge.html +304 -0
  439. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  440. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  441. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  442. data/docs/Tasker/WorkflowStepsController.html +462 -0
  443. data/docs/Tasker.html +452 -0
  444. data/docs/VISION.md +584 -0
  445. data/docs/WHY.md +21 -0
  446. data/docs/_index.html +2375 -0
  447. data/docs/class_list.html +54 -0
  448. data/docs/css/common.css +1 -0
  449. data/docs/css/full_list.css +58 -0
  450. data/docs/css/style.css +503 -0
  451. data/docs/events/migration_plan_outcomes.md +80 -0
  452. data/docs/file.README.html +541 -0
  453. data/docs/file_list.html +59 -0
  454. data/docs/frames.html +22 -0
  455. data/docs/index.html +541 -0
  456. data/docs/js/app.js +344 -0
  457. data/docs/js/full_list.js +242 -0
  458. data/docs/js/jquery.js +4 -0
  459. data/docs/method_list.html +9182 -0
  460. data/docs/top-level-namespace.html +110 -0
  461. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  462. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  463. data/lib/generators/tasker/events_generator.rb +91 -0
  464. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  465. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  466. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  467. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  468. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
  469. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
  470. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  471. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  472. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  473. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  474. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  475. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  476. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  477. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  478. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  479. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  480. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  481. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  482. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  483. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  484. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  485. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  486. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  487. data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
  488. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
  489. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  490. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  491. data/lib/tasker/authentication/coordinator.rb +78 -0
  492. data/lib/tasker/authentication/errors.rb +9 -0
  493. data/lib/tasker/authentication/interface.rb +36 -0
  494. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  495. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  496. data/lib/tasker/authorization/errors.rb +26 -0
  497. data/lib/tasker/authorization/resource_constants.rb +74 -0
  498. data/lib/tasker/authorization/resource_registry.rb +143 -0
  499. data/lib/tasker/authorization.rb +75 -0
  500. data/lib/tasker/cache_capabilities.rb +131 -0
  501. data/lib/tasker/cache_strategy.rb +469 -0
  502. data/lib/tasker/concerns/authenticatable.rb +41 -0
  503. data/lib/tasker/concerns/authorizable.rb +204 -0
  504. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  505. data/lib/tasker/concerns/event_publisher.rb +716 -0
  506. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  507. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  508. data/lib/tasker/concerns/structured_logging.rb +387 -0
  509. data/lib/tasker/configuration.rb +325 -0
  510. data/lib/tasker/constants/event_definitions.rb +147 -0
  511. data/lib/tasker/constants/registry_events.rb +54 -0
  512. data/lib/tasker/constants.rb +417 -0
  513. data/lib/tasker/engine.rb +90 -0
  514. data/lib/tasker/errors.rb +90 -0
  515. data/lib/tasker/events/catalog.rb +432 -0
  516. data/lib/tasker/events/custom_registry.rb +175 -0
  517. data/lib/tasker/events/definition_loader.rb +199 -0
  518. data/lib/tasker/events/event_payload_builder.rb +461 -0
  519. data/lib/tasker/events/publisher.rb +149 -0
  520. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  521. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  522. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  523. data/lib/tasker/events/subscription_loader.rb +161 -0
  524. data/lib/tasker/events.rb +37 -0
  525. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  526. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  527. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  528. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  529. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  530. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  531. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  532. data/lib/tasker/functions/function_wrapper.rb +42 -0
  533. data/lib/tasker/functions.rb +12 -0
  534. data/lib/tasker/handler_factory.rb +322 -0
  535. data/lib/tasker/health/readiness_checker.rb +186 -0
  536. data/lib/tasker/health/status_checker.rb +203 -0
  537. data/lib/tasker/identity_strategy.rb +38 -0
  538. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  539. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  540. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  541. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  542. data/lib/tasker/orchestration/coordinator.rb +119 -0
  543. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  544. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  545. data/lib/tasker/orchestration/response_processor.rb +168 -0
  546. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  547. data/lib/tasker/orchestration/step_executor.rb +941 -0
  548. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  549. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  550. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  551. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  552. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  553. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  554. data/lib/tasker/orchestration.rb +45 -0
  555. data/lib/tasker/railtie.rb +9 -0
  556. data/lib/tasker/registry/base_registry.rb +177 -0
  557. data/lib/tasker/registry/event_publisher.rb +91 -0
  558. data/lib/tasker/registry/interface_validator.rb +140 -0
  559. data/lib/tasker/registry/statistics_collector.rb +381 -0
  560. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  561. data/lib/tasker/registry.rb +22 -0
  562. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  563. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  564. data/lib/tasker/state_machine.rb +83 -0
  565. data/lib/tasker/step_handler/api.rb +410 -0
  566. data/lib/tasker/step_handler/base.rb +206 -0
  567. data/lib/tasker/task_builder.rb +432 -0
  568. data/lib/tasker/task_handler/class_methods.rb +324 -0
  569. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  570. data/lib/tasker/task_handler/step_group.rb +182 -0
  571. data/lib/tasker/task_handler.rb +43 -0
  572. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  573. data/lib/tasker/telemetry/event_router.rb +318 -0
  574. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  575. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  576. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  577. data/lib/tasker/telemetry/log_backend.rb +224 -0
  578. data/lib/tasker/telemetry/metric_types.rb +368 -0
  579. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  580. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  581. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  582. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  583. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  584. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  585. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  586. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  587. data/lib/tasker/telemetry.rb +59 -0
  588. data/lib/tasker/types/auth_config.rb +81 -0
  589. data/lib/tasker/types/backoff_config.rb +142 -0
  590. data/lib/tasker/types/cache_config.rb +257 -0
  591. data/lib/tasker/types/database_config.rb +39 -0
  592. data/lib/tasker/types/dependency_graph.rb +225 -0
  593. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  594. data/lib/tasker/types/engine_config.rb +131 -0
  595. data/lib/tasker/types/execution_config.rb +289 -0
  596. data/lib/tasker/types/health_config.rb +84 -0
  597. data/lib/tasker/types/step_sequence.rb +24 -0
  598. data/lib/tasker/types/step_template.rb +63 -0
  599. data/lib/tasker/types/task_request.rb +60 -0
  600. data/lib/tasker/types/telemetry_config.rb +273 -0
  601. data/lib/tasker/types.rb +64 -0
  602. data/lib/tasker/version.rb +7 -0
  603. data/lib/tasker.rb +82 -0
  604. data/lib/tasks/tasker_tasks.rake +302 -0
  605. metadata +958 -0
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'state_machine/task_state_machine'
4
+ require_relative 'state_machine/step_state_machine'
5
+
6
+ module Tasker
7
+ # StateMachine module provides declarative state management for tasks and steps
8
+ #
9
+ # This module integrates Statesman-based state machines with the existing
10
+ # Tasker lifecycle events system to provide a unified, event-driven approach
11
+ # to workflow state management.
12
+ module StateMachine
13
+ # Exception raised when an invalid state transition is attempted
14
+ class InvalidStateTransition < StandardError; end
15
+
16
+ # Compatibility module for legacy state management
17
+ module Compatibility
18
+ # Legacy method for updating status with state machine integration
19
+ #
20
+ # @param entity [Object] The entity (task or step) to update
21
+ # @param new_status [String] The new status to transition to
22
+ # @param metadata [Hash] Optional metadata for the transition
23
+ # @return [Boolean] True if transition succeeded
24
+ def update_status!(entity, new_status, metadata = {})
25
+ return false unless entity.respond_to?(:state_machine)
26
+
27
+ entity.state_machine.transition_to!(new_status, metadata)
28
+ true
29
+ rescue Statesman::GuardFailedError, Statesman::TransitionFailedError => e
30
+ Rails.logger.warn { "State transition failed: #{e.message}" }
31
+ false
32
+ end
33
+ end
34
+
35
+ # Configure state machine behavior
36
+ class << self
37
+ # Configure Statesman for Tasker
38
+ #
39
+ # @return [void]
40
+ def configure_statesman
41
+ # Statesman doesn't require global configuration
42
+ # State machines use ActiveRecord adapters through transition models
43
+ true
44
+ end
45
+
46
+ # Initialize state machines for a task
47
+ #
48
+ # @param task [Task] The task to initialize
49
+ # @return [TaskStateMachine] The initialized state machine
50
+ def initialize_task_state_machine(task)
51
+ TaskStateMachine.new(task)
52
+ end
53
+
54
+ # Initialize state machines for a step
55
+ #
56
+ # @param step [WorkflowStep] The step to initialize
57
+ # @return [StepStateMachine] The initialized state machine
58
+ def initialize_step_state_machine(step)
59
+ StepStateMachine.new(step)
60
+ end
61
+
62
+ # Check if state machines are properly configured
63
+ #
64
+ # @return [Boolean] True if configuration is valid
65
+ def configured?
66
+ !!(defined?(Statesman) &&
67
+ TaskStateMachine.respond_to?(:new) &&
68
+ StepStateMachine.respond_to?(:new))
69
+ end
70
+
71
+ # Get statistics about state machine usage
72
+ #
73
+ # @return [Hash] Statistics hash
74
+ def statistics
75
+ {
76
+ task_states: Constants::VALID_TASK_STATUSES,
77
+ step_states: Constants::VALID_WORKFLOW_STEP_STATUSES,
78
+ configured: configured?
79
+ }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,410 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require_relative '../concerns/event_publisher'
5
+ require_relative '../orchestration/response_processor'
6
+ require_relative '../orchestration/backoff_calculator'
7
+ require_relative '../orchestration/connection_builder'
8
+ require_relative '../errors'
9
+
10
+ module Tasker
11
+ module StepHandler
12
+ # Handles API-based workflow steps by making HTTP requests
13
+ # and processing responses with backoff/retry support
14
+ #
15
+ # @example Creating a custom API step handler
16
+ # class MyApiHandler < Tasker::StepHandler::Api
17
+ # def process(task, sequence, step)
18
+ # connection.post('/endpoint', task.context)
19
+ # end
20
+ # end
21
+ #
22
+ # @example Using error types for better retry control
23
+ # class MyApiHandler < Tasker::StepHandler::Api
24
+ # def process(task, sequence, step)
25
+ # response = connection.post('/endpoint', task.context)
26
+ #
27
+ # case response.status
28
+ # when 400, 422
29
+ # # Client errors - don't retry, these are permanent failures
30
+ # raise Tasker::PermanentError.new(
31
+ # "Invalid request: #{response.body}",
32
+ # error_code: 'VALIDATION_ERROR'
33
+ # )
34
+ # when 429
35
+ # # Rate limited - retry with server-suggested delay
36
+ # retry_after = response.headers['retry-after']&.to_i || 60
37
+ # raise Tasker::RetryableError.new(
38
+ # "Rate limited by API",
39
+ # retry_after: retry_after
40
+ # )
41
+ # when 500..599
42
+ # # Server errors - let Tasker's exponential backoff handle retry timing
43
+ # raise Tasker::RetryableError.new(
44
+ # "Server error: #{response.status}",
45
+ # context: { status: response.status, endpoint: '/endpoint' }
46
+ # )
47
+ # end
48
+ #
49
+ # response
50
+ # end
51
+ # end
52
+ class Api < Base
53
+ include Tasker::Concerns::EventPublisher
54
+
55
+ # @return [Faraday::Connection] The Faraday connection for making HTTP requests
56
+ attr_reader :connection
57
+
58
+ # @return [Config] The configuration for this API handler
59
+ attr_reader :config
60
+
61
+ # Configuration class for API step handlers
62
+ class Config
63
+ # @return [String] The base URL for API requests
64
+ attr_accessor :url
65
+
66
+ # @return [Hash] The default query parameters for requests
67
+ attr_accessor :params
68
+
69
+ # @return [Hash, nil] SSL configuration options
70
+ attr_accessor :ssl
71
+
72
+ # @return [Hash] Request headers
73
+ attr_accessor :headers
74
+
75
+ # @return [Float] Delay in seconds before retrying after failure
76
+ attr_accessor :retry_delay
77
+
78
+ # @return [Boolean] Whether to use exponential backoff for retries
79
+ attr_accessor :enable_exponential_backoff
80
+
81
+ # @return [Float] Random factor for jitter calculation (0.0-1.0)
82
+ attr_accessor :jitter_factor
83
+
84
+ # Creates a new API configuration
85
+ #
86
+ # @param url [String] The base URL for API requests
87
+ # @param params [Hash] Query parameters for requests
88
+ # @param ssl [Hash, nil] SSL configuration options
89
+ # @param headers [Hash] Request headers
90
+ # @param enable_exponential_backoff [Boolean] Whether to use exponential backoff
91
+ # @param retry_delay [Float] Delay in seconds before retrying
92
+ # @param jitter_factor [Float] Random factor for jitter calculation
93
+ # @return [Config] A new configuration instance
94
+ def initialize(
95
+ url:,
96
+ params: {},
97
+ ssl: nil,
98
+ headers: default_headers,
99
+ enable_exponential_backoff: true,
100
+ retry_delay: 1.0,
101
+ jitter_factor: rand
102
+ )
103
+ @url = url
104
+ @params = params
105
+ @ssl = ssl
106
+ @headers = headers
107
+ @enable_exponential_backoff = enable_exponential_backoff
108
+ @retry_delay = retry_delay
109
+ @jitter_factor = jitter_factor
110
+ end
111
+
112
+ # Returns the default headers for API requests
113
+ #
114
+ # @return [Hash] The default headers
115
+ def default_headers
116
+ {
117
+ 'Content-Type' => 'application/json',
118
+ 'Accept' => 'application/json'
119
+ }
120
+ end
121
+ end
122
+
123
+ # Creates a new API step handler
124
+ #
125
+ # @param config [Config] The configuration for this handler
126
+ # @yield [Faraday::Connection] Optional block for configuring the Faraday connection
127
+ # @return [Api] A new API step handler
128
+ def initialize(config: Config.new, &connection_block)
129
+ super(config: config)
130
+
131
+ # Initialize orchestration components
132
+ @response_processor = Tasker::Orchestration::ResponseProcessor.new
133
+ @backoff_calculator = Tasker::Orchestration::BackoffCalculator.new(config: config)
134
+ @connection_builder = Tasker::Orchestration::ConnectionBuilder.new
135
+
136
+ # Build connection using orchestration component
137
+ @connection = @connection_builder.build_connection(config, &connection_block)
138
+ end
139
+
140
+ # Framework coordination method for API step handlers
141
+ #
142
+ # ⚠️ NEVER OVERRIDE THIS METHOD - Framework-only code
143
+ # This method coordinates the API-specific workflow around the developer's process() method
144
+ #
145
+ # @param task [Tasker::Task] The task being executed
146
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
147
+ # @param step [Tasker::WorkflowStep] The current step being handled
148
+ # @return [void]
149
+ def handle(task, sequence, step)
150
+ # Publish step started event
151
+ publish_step_started(step)
152
+
153
+ # Fire the before_handle event for compatibility (matches base class pattern)
154
+ publish_step_before_handle(step)
155
+
156
+ begin
157
+ response = execute_api_workflow(task, sequence, step)
158
+ publish_step_completed(step)
159
+ response
160
+ rescue StandardError => e
161
+ handle_execution_error(step, e)
162
+ raise
163
+ end
164
+ end
165
+
166
+ # Developer extension point for API step handlers
167
+ #
168
+ # ✅ IMPLEMENT THIS METHOD: This is your extension point for API business logic
169
+ #
170
+ # This is where you implement your specific HTTP request logic.
171
+ # Use the provided connection object to make requests and return the response.
172
+ #
173
+ # The framework will automatically:
174
+ # - Publish step_started before calling this method
175
+ # - Handle response processing, error detection, and backoff
176
+ # - Publish step_completed after this method succeeds
177
+ # - Publish step_failed if this method raises an exception
178
+ #
179
+ # Examples:
180
+ # def process(task, sequence, step)
181
+ # user_id = task.context['user_id']
182
+ # connection.get("/users/#{user_id}")
183
+ # end
184
+ #
185
+ # def process(task, sequence, step)
186
+ # connection.post('/orders', task.context)
187
+ # end
188
+ #
189
+ # @param task [Tasker::Task] The task being executed
190
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
191
+ # @param step [Tasker::WorkflowStep] The current step being handled
192
+ # @return [Faraday::Response, Hash] The API response
193
+ def process(task, sequence, step)
194
+ raise NotImplementedError, 'API step handler subclasses must implement the process method to make HTTP requests'
195
+ end
196
+
197
+ private
198
+
199
+ # Execute the main API workflow with orchestration
200
+ #
201
+ # @param task [Tasker::Task] The task being executed
202
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
203
+ # @param step [Tasker::WorkflowStep] The current step being handled
204
+ # @return [Object] The API response
205
+ def execute_api_workflow(task, sequence, step)
206
+ Rails.logger.debug { "StepHandler: Starting execution of step #{step.workflow_step_id} (#{step.name})" }
207
+
208
+ # Store initial results state to detect if developer set them
209
+ initial_results = step.results
210
+
211
+ # Call the developer-implemented process() method to make the HTTP request
212
+ response = process(task, sequence, step)
213
+
214
+ # ResponseProcessor will automatically raise Tasker::RetryableError or Tasker::PermanentError
215
+ # for error responses, so we only process successful responses here
216
+ @response_processor.process_response(step, response)
217
+
218
+ # Set results using overridable method, respecting developer customization
219
+ process_results(step, response, initial_results)
220
+
221
+ Rails.logger.debug { "StepHandler: Completed execution of step #{step.workflow_step_id} (#{step.name})" }
222
+ response
223
+ end
224
+
225
+ # Handle API response processing and error detection
226
+ #
227
+ # @param step [Tasker::WorkflowStep] The current step
228
+ # @param response [Object] The API response to process
229
+ # @raise [Faraday::Error] If response indicates a failure requiring backoff
230
+ def handle_api_response(step, response)
231
+ error_context = @response_processor.process_response(step, response)
232
+
233
+ return unless error_context
234
+
235
+ # Apply backoff for error responses that require it
236
+ @backoff_calculator.calculate_and_apply_backoff(step, error_context)
237
+
238
+ # Raise error with response details for proper error handling
239
+ body = extract_error_body(error_context[:response])
240
+ raise Faraday::Error, "API call failed with status #{error_context[:status]} and body #{body}"
241
+ end
242
+
243
+ # Handle errors that occur during step execution
244
+ #
245
+ # @param step [Tasker::WorkflowStep] The current step
246
+ # @param error [StandardError] The error that occurred
247
+ def handle_execution_error(step, error)
248
+ # Handle Tasker-specific errors appropriately
249
+ case error
250
+ when Tasker::RetryableError
251
+ # Apply backoff for retryable errors, respecting suggested retry_after
252
+ apply_retryable_error_backoff(step, error)
253
+ when Tasker::PermanentError
254
+ # Don't apply backoff for permanent errors - they shouldn't be retried
255
+ Rails.logger.info { "StepHandler: Permanent error encountered - #{error.message}" }
256
+ else
257
+ # Handle legacy Faraday errors and unknown errors
258
+ if error.is_a?(Faraday::Error) && error.response && backoff_error_code?(error.response.status)
259
+ error_context = build_faraday_error_context(step, error.response)
260
+ @backoff_calculator.calculate_and_apply_backoff(step, error_context)
261
+ end
262
+ end
263
+
264
+ # Add error information to results using the same extensible pattern
265
+ add_error_to_results(step, error)
266
+
267
+ # Publish step failed event with error information
268
+ publish_step_failed(step, error: error)
269
+ end
270
+
271
+ # Extract error body from response for error messages
272
+ #
273
+ # @param response [Object] The response object
274
+ # @return [String] The response body
275
+ def extract_error_body(response)
276
+ response.is_a?(Hash) ? response[:body] : response.body
277
+ end
278
+
279
+ # Check if status code requires backoff handling
280
+ #
281
+ # @param status [Integer] HTTP status code
282
+ # @return [Boolean] True if status requires backoff
283
+ def backoff_error_code?(status)
284
+ [429, 503].include?(status)
285
+ end
286
+
287
+ # Build error context for Faraday errors
288
+ #
289
+ # @param step [Tasker::WorkflowStep] The current step
290
+ # @param response [Faraday::Response] The error response
291
+ # @return [Hash] Context for backoff handling
292
+ def build_faraday_error_context(step, response)
293
+ {
294
+ step_id: step.workflow_step_id,
295
+ step_name: step.name,
296
+ status: response.status,
297
+ response: response,
298
+ headers: response.headers
299
+ }
300
+ end
301
+
302
+ # Process the output from process() method and store in step.results
303
+ #
304
+ # ✅ OVERRIDE THIS METHOD: To customize how process() output is stored
305
+ #
306
+ # This method provides a clean extension point for customizing how the response
307
+ # from your process() method gets stored in step.results. The default behavior
308
+ # is to store the raw response, but you can override this to transform the data.
309
+ #
310
+ # @param step [Tasker::WorkflowStep] The current step
311
+ # @param process_output [Object] The return value from process() method
312
+ # @param initial_results [Object] The value of step.results before process() was called
313
+ # @return [void]
314
+ def process_results(step, process_output, initial_results)
315
+ # If developer already set step.results in their process() method, respect it
316
+ if step.results != initial_results
317
+ Rails.logger.debug do
318
+ 'StepHandler: Developer set custom results in process() method - respecting custom results'
319
+ end
320
+ return
321
+ end
322
+
323
+ # Default behavior: store the raw response from process()
324
+ step.results = process_output
325
+ end
326
+
327
+ # Add error information to step results
328
+ #
329
+ # ✅ OVERRIDE THIS METHOD: To customize how error information is stored
330
+ #
331
+ # This method provides a clean extension point for customizing how error
332
+ # information gets added to step.results. The default behavior ensures
333
+ # results is a hash and adds error details.
334
+ #
335
+ # @param step [Tasker::WorkflowStep] The current step
336
+ # @param error [StandardError] The error that occurred
337
+ # @return [void]
338
+ def add_error_to_results(step, error)
339
+ # Ensure results is a hash so we can add error information
340
+ current_results = step.results || {}
341
+
342
+ # If results is not a hash (e.g., Faraday::Response), convert it
343
+ unless current_results.is_a?(Hash)
344
+ current_results = {
345
+ original_response: current_results,
346
+ response_type: current_results.class.name
347
+ }
348
+ end
349
+
350
+ # Add error information
351
+ error_info = {
352
+ error_message: error.message,
353
+ error_class: error.class.name,
354
+ backtrace: error.backtrace&.first(10)
355
+ }
356
+
357
+ # Add response details for Faraday errors
358
+ if error.is_a?(Faraday::Error) && error.response
359
+ error_info[:response_status] = error.response.status
360
+ error_info[:response_body] = error.response.body
361
+ end
362
+
363
+ # Add context for Tasker-specific errors
364
+ error_info[:error_context] = error.context if error.respond_to?(:context)
365
+
366
+ # Add error code for permanent errors
367
+ error_info[:error_code] = error.error_code if error.respond_to?(:error_code)
368
+
369
+ # Add retry information for retryable errors
370
+ error_info[:retry_after] = error.retry_after if error.respond_to?(:retry_after)
371
+
372
+ step.results = current_results.merge(
373
+ error: true,
374
+ error_details: error_info
375
+ )
376
+ end
377
+
378
+ # Apply backoff for RetryableError with custom retry_after
379
+ #
380
+ # @param step [Tasker::WorkflowStep] The current step
381
+ # @param error [Tasker::RetryableError] The retryable error
382
+ def apply_retryable_error_backoff(step, error)
383
+ # Check if this error should skip backoff (e.g., server errors)
384
+ if error.respond_to?(:skip_backoff?) && error.skip_backoff?
385
+ Rails.logger.info { "StepHandler: Skipping backoff for error type that doesn't require it" }
386
+ return
387
+ end
388
+
389
+ error_context = if error.retry_after
390
+ # Use the specific retry delay from the error by providing it as a Retry-After header
391
+ {
392
+ step_id: step.workflow_step_id,
393
+ step_name: step.name,
394
+ headers: { 'Retry-After' => error.retry_after.to_s },
395
+ error_type: 'retryable_error'
396
+ }
397
+ else
398
+ # Use default backoff calculation
399
+ {
400
+ step_id: step.workflow_step_id,
401
+ step_name: step.name,
402
+ status: 503, # Treat as service unavailable
403
+ error_type: 'retryable_error'
404
+ }
405
+ end
406
+ @backoff_calculator.calculate_and_apply_backoff(step, error_context)
407
+ end
408
+ end
409
+ end
410
+ end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../concerns/event_publisher'
4
+ require_relative '../concerns/structured_logging'
5
+
6
+ module Tasker
7
+ module StepHandler
8
+ # Automatic event publishing wrapper for step handlers
9
+ #
10
+ # This module automatically publishes step lifecycle events around
11
+ # the execution of step handler methods, eliminating the need for
12
+ # developers to manually add event publishing code in their handlers.
13
+ #
14
+ # Uses Ruby's prepend mechanism to wrap the handle method transparently.
15
+ module AutomaticEventPublishing
16
+ # Automatically publish events around step handler execution
17
+ #
18
+ # This method wraps the original handle method and ensures that
19
+ # appropriate lifecycle events are published without requiring
20
+ # developers to add event publishing code manually.
21
+ #
22
+ # ⚠️ IMPORTANT: This method should NEVER be overridden by developers.
23
+ # Developers should implement the process() method instead.
24
+ #
25
+ # @param task [Tasker::Task] The task being executed
26
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
27
+ # @param step [Tasker::WorkflowStep] The current step being handled
28
+ # @return [Object] The result of processing the step
29
+ def handle(task, sequence, step)
30
+ # Publish step started event automatically
31
+ publish_step_started(step)
32
+
33
+ # Fire the before_handle event for compatibility with existing code
34
+ publish_step_before_handle(step)
35
+
36
+ begin
37
+ # Call the original handle method implemented by the framework
38
+ result = super
39
+
40
+ # Publish step completed event automatically
41
+ publish_step_completed(step)
42
+
43
+ result
44
+ rescue StandardError => e
45
+ # Publish step failed event automatically with error information
46
+ publish_step_failed(step, error: e)
47
+
48
+ # Re-raise the exception to preserve error handling behavior
49
+ raise
50
+ end
51
+ end
52
+ end
53
+
54
+ # Base class for all step handlers that defines the common interface
55
+ # and provides lifecycle event handling
56
+ #
57
+ # ⚠️ IMPORTANT DEVELOPER GUIDANCE:
58
+ # - NEVER override the handle() method - it's framework-only code
59
+ # - ALWAYS implement the process() method - that's your extension point
60
+ # - The handle() method automatically publishes lifecycle events and calls your process() method
61
+ #
62
+ # 📊 STRUCTURED LOGGING AVAILABLE:
63
+ # All step handlers automatically include structured logging capabilities:
64
+ # - Use log_structured() to emit structured logs with correlation IDs
65
+ # - Correlation ID is automatically extracted from the task for traceability
66
+ # - Example: log_structured(level: :info, message: "Processing order", order_id: order.id)
67
+ class Base
68
+ # Use prepend to automatically wrap handle methods with event publishing
69
+ prepend AutomaticEventPublishing
70
+ include Tasker::Concerns::EventPublisher
71
+ include Tasker::Concerns::StructuredLogging
72
+
73
+ # Creates a new step handler instance
74
+ #
75
+ # @param config [Object, nil] Optional configuration for the handler
76
+ # @return [Base] A new step handler instance
77
+ def initialize(config: nil)
78
+ @config = config
79
+ end
80
+
81
+ # Framework method that coordinates step execution with automatic event publishing
82
+ #
83
+ # ⚠️ NEVER OVERRIDE THIS METHOD IN SUBCLASSES
84
+ # This method is framework-only code that:
85
+ # 1. Is automatically wrapped with event publishing via AutomaticEventPublishing
86
+ # 2. Calls the developer-implemented process() method
87
+ # 3. Handles framework-level concerns like logging and error propagation
88
+ #
89
+ # @param task [Tasker::Task] The task being executed
90
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
91
+ # @param step [Tasker::WorkflowStep] The current step being handled
92
+ # @return [Object] The result of processing the step
93
+ def handle(task, sequence, step)
94
+ log_structured(
95
+ :debug,
96
+ 'Starting step execution',
97
+ step_id: step.workflow_step_id,
98
+ step_name: step.name,
99
+ correlation_id: task.task_id
100
+ )
101
+
102
+ # Store initial results state to detect if developer set them manually
103
+ initial_results = step.results
104
+
105
+ # Call the developer-implemented process method
106
+ process_output = process(task, sequence, step)
107
+
108
+ # Process results using overridable method, respecting developer customization
109
+ process_results(step, process_output, initial_results)
110
+
111
+ log_structured(
112
+ :debug,
113
+ 'Completed step execution',
114
+ step_id: step.workflow_step_id,
115
+ step_name: step.name,
116
+ correlation_id: task.task_id,
117
+ has_results: step.results.present?
118
+ )
119
+
120
+ process_output
121
+ end
122
+
123
+ # Developer extension point - implement your business logic here
124
+ #
125
+ # ✅ ALWAYS IMPLEMENT THIS METHOD IN SUBCLASSES
126
+ # This is where you put your step's business logic. The framework will:
127
+ # - Automatically publish step_started before calling this method
128
+ # - Automatically publish step_completed after this method succeeds
129
+ # - Automatically publish step_failed if this method raises an exception
130
+ #
131
+ # Return your results from this method - they will be stored in step.results
132
+ # automatically via process_results(). You can override process_results() to
133
+ # customize how the return value gets stored.
134
+ #
135
+ # @param task [Tasker::Task] The task being executed
136
+ # @param sequence [Tasker::Types::StepSequence] The sequence of steps
137
+ # @param step [Tasker::WorkflowStep] The current step being handled
138
+ # @return [Object] The results of processing - will be stored in step.results
139
+ # @raise [NotImplementedError] If not implemented by a subclass
140
+ def process(task, sequence, step)
141
+ raise NotImplementedError,
142
+ 'Subclasses must implement the process method. This is your extension point for business logic.'
143
+ end
144
+
145
+ # Process the output from process() method and store in step.results
146
+ #
147
+ # ✅ OVERRIDE THIS METHOD: To customize how process() output is stored
148
+ #
149
+ # This method provides a clean extension point for customizing how the return
150
+ # value from your process() method gets stored in step.results. The default
151
+ # behavior is to store the returned value directly.
152
+ #
153
+ # @param step [Tasker::WorkflowStep] The current step
154
+ # @param process_output [Object] The return value from process() method
155
+ # @param initial_results [Object] The value of step.results before process() was called
156
+ # @return [void]
157
+ def process_results(step, process_output, initial_results)
158
+ # If developer already set step.results in their process() method, respect it
159
+ if step.results != initial_results
160
+ log_structured(
161
+ :debug,
162
+ 'Developer set custom results in process() method',
163
+ step_id: step.workflow_step_id,
164
+ step_name: step.name
165
+ )
166
+ return
167
+ end
168
+
169
+ # Default behavior: store the return value from process()
170
+ step.results = process_output
171
+ end
172
+
173
+ # Class method that step handlers can override to declare custom events
174
+ #
175
+ # ✅ OVERRIDE THIS METHOD: To declare custom events your step handler publishes
176
+ #
177
+ # Return an array of hashes where each hash defines a custom event:
178
+ # - name: The event name (without namespace)
179
+ # - description: Human-readable description of when this event is published
180
+ #
181
+ # Example:
182
+ # def self.custom_event_configuration
183
+ # [
184
+ # {
185
+ # name: 'payment.processed',
186
+ # description: 'Published when payment processing completes successfully'
187
+ # },
188
+ # {
189
+ # name: 'payment.risk_flagged',
190
+ # description: 'Published when payment is flagged for manual review'
191
+ # }
192
+ # ]
193
+ # end
194
+ #
195
+ # @return [Array<Hash>] Array of custom event definitions
196
+ def self.custom_event_configuration
197
+ []
198
+ end
199
+
200
+ protected
201
+
202
+ # Access to configuration passed during initialization
203
+ attr_reader :config
204
+ end
205
+ end
206
+ end