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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +443 -0
- data/Rakefile +10 -0
- data/app/controllers/tasker/analytics_controller.rb +179 -0
- data/app/controllers/tasker/application_controller.rb +45 -0
- data/app/controllers/tasker/graphql_controller.rb +193 -0
- data/app/controllers/tasker/handlers_controller.rb +217 -0
- data/app/controllers/tasker/health_controller.rb +229 -0
- data/app/controllers/tasker/metrics_controller.rb +111 -0
- data/app/controllers/tasker/page_sort.rb +97 -0
- data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
- data/app/controllers/tasker/tasks_controller.rb +123 -0
- data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
- data/app/graphql/examples/all_tasks.graphql +22 -0
- data/app/graphql/examples/pending_tasks.graphql +23 -0
- data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
- data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
- data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
- data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
- data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
- data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
- data/app/graphql/tasker/graph_ql_types.rb +42 -0
- data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
- data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
- data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
- data/app/graphql/tasker/mutations/create_task.rb +52 -0
- data/app/graphql/tasker/mutations/update_step.rb +36 -0
- data/app/graphql/tasker/mutations/update_task.rb +41 -0
- data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
- data/app/graphql/tasker/queries/all_tasks.rb +23 -0
- data/app/graphql/tasker/queries/base_query.rb +9 -0
- data/app/graphql/tasker/queries/helpers.rb +16 -0
- data/app/graphql/tasker/queries/one_step.rb +24 -0
- data/app/graphql/tasker/queries/one_task.rb +18 -0
- data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
- data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
- data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
- data/app/jobs/tasker/application_job.rb +8 -0
- data/app/jobs/tasker/metrics_export_job.rb +252 -0
- data/app/jobs/tasker/task_runner_job.rb +224 -0
- data/app/models/tasker/annotation_type.rb +26 -0
- data/app/models/tasker/application_record.rb +70 -0
- data/app/models/tasker/dependent_system.rb +26 -0
- data/app/models/tasker/dependent_system_object_map.rb +64 -0
- data/app/models/tasker/diagram/edge.rb +106 -0
- data/app/models/tasker/diagram/flowchart.rb +137 -0
- data/app/models/tasker/diagram/node.rb +99 -0
- data/app/models/tasker/named_step.rb +41 -0
- data/app/models/tasker/named_task.rb +121 -0
- data/app/models/tasker/named_tasks_named_step.rb +82 -0
- data/app/models/tasker/step_dag_relationship.rb +65 -0
- data/app/models/tasker/step_readiness_status.rb +59 -0
- data/app/models/tasker/task.rb +424 -0
- data/app/models/tasker/task_annotation.rb +36 -0
- data/app/models/tasker/task_diagram.rb +332 -0
- data/app/models/tasker/task_execution_context.rb +29 -0
- data/app/models/tasker/task_namespace.rb +41 -0
- data/app/models/tasker/task_transition.rb +235 -0
- data/app/models/tasker/workflow_step.rb +461 -0
- data/app/models/tasker/workflow_step_edge.rb +94 -0
- data/app/models/tasker/workflow_step_transition.rb +434 -0
- data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
- data/app/serializers/tasker/handler_serializer.rb +109 -0
- data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
- data/app/serializers/tasker/task_serializer.rb +168 -0
- data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
- data/app/services/tasker/analytics_service.rb +409 -0
- data/app/views/tasker/task/_diagram.html.erb +32 -0
- data/config/initializers/dry_struct.rb +11 -0
- data/config/initializers/statesman.rb +6 -0
- data/config/initializers/tasker_orchestration.rb +17 -0
- data/config/initializers/time_formats.rb +4 -0
- data/config/routes.rb +34 -0
- data/config/tasker/subscriptions/example_integrations.yml +67 -0
- data/config/tasker/system_events.yml +305 -0
- data/db/functions/calculate_dependency_levels_v01.sql +45 -0
- data/db/functions/get_analytics_metrics_v01.sql +137 -0
- data/db/functions/get_slowest_steps_v01.sql +82 -0
- data/db/functions/get_slowest_tasks_v01.sql +96 -0
- data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
- data/db/functions/get_step_readiness_status_v01.sql +139 -0
- data/db/functions/get_system_health_counts_v01.sql +108 -0
- data/db/functions/get_task_execution_context_v01.sql +108 -0
- data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
- data/db/init/schema.sql +2277 -0
- data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
- data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
- data/docs/APPLICATION_GENERATOR.md +384 -0
- data/docs/AUTH.md +1780 -0
- data/docs/CIRCUIT_BREAKER.md +224 -0
- data/docs/DEVELOPER_GUIDE.md +2665 -0
- data/docs/EVENT_SYSTEM.md +637 -0
- data/docs/EXECUTION_CONFIGURATION.md +341 -0
- data/docs/FLOW_CHART.md +149 -0
- data/docs/HEALTH.md +542 -0
- data/docs/METRICS.md +731 -0
- data/docs/OPTIMIZATION_PLAN.md +1479 -0
- data/docs/OVERVIEW.md +552 -0
- data/docs/QUICK_START.md +270 -0
- data/docs/REGISTRY_SYSTEMS.md +373 -0
- data/docs/REST_API.md +632 -0
- data/docs/ROADMAP.md +221 -0
- data/docs/SQL_FUNCTIONS.md +1408 -0
- data/docs/TASK_DIAGRAM.md +252 -0
- data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
- data/docs/TELEMETRY.md +795 -0
- data/docs/TROUBLESHOOTING.md +756 -0
- data/docs/TaskHandlerGenerator.html +255 -0
- data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
- data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
- data/docs/Tasker/Analysis.html +117 -0
- data/docs/Tasker/AnalyticsController.html +450 -0
- data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
- data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
- data/docs/Tasker/AnalyticsService.html +2221 -0
- data/docs/Tasker/AnnotationType.html +137 -0
- data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
- data/docs/Tasker/ApplicationController.html +147 -0
- data/docs/Tasker/ApplicationJob.html +128 -0
- data/docs/Tasker/ApplicationRecord.html +378 -0
- data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
- data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
- data/docs/Tasker/Authentication/Coordinator.html +242 -0
- data/docs/Tasker/Authentication/Interface.html +560 -0
- data/docs/Tasker/Authentication/InterfaceError.html +124 -0
- data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
- data/docs/Tasker/Authentication.html +119 -0
- data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
- data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
- data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
- data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
- data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
- data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
- data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
- data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
- data/docs/Tasker/Authorization.html +582 -0
- data/docs/Tasker/CacheCapabilities.html +167 -0
- data/docs/Tasker/CacheStrategy.html +1297 -0
- data/docs/Tasker/Concerns/Authenticatable.html +116 -0
- data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
- data/docs/Tasker/Concerns/Authorizable.html +816 -0
- data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
- data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
- data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
- data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
- data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
- data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
- data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
- data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
- data/docs/Tasker/Concerns.html +117 -0
- data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
- data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
- data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
- data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
- data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
- data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
- data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
- data/docs/Tasker/Configuration.html +1669 -0
- data/docs/Tasker/ConfigurationError.html +143 -0
- data/docs/Tasker/ConfiguredTask.html +514 -0
- data/docs/Tasker/Constants/EventDefinitions.html +590 -0
- data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
- data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
- data/docs/Tasker/Constants/RegistryEvents.html +285 -0
- data/docs/Tasker/Constants/StepEvents.html +177 -0
- data/docs/Tasker/Constants/TaskEvents.html +167 -0
- data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
- data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
- data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
- data/docs/Tasker/Constants/TaskExecution.html +126 -0
- data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
- data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
- data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
- data/docs/Tasker/Constants/TaskFinalization.html +126 -0
- data/docs/Tasker/Constants/TaskStatuses.html +223 -0
- data/docs/Tasker/Constants/TestEvents.html +163 -0
- data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
- data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
- data/docs/Tasker/Constants.html +561 -0
- data/docs/Tasker/DependentSystem.html +137 -0
- data/docs/Tasker/DependentSystemObjectMap.html +250 -0
- data/docs/Tasker/DetectorRegistry.html +598 -0
- data/docs/Tasker/Diagram/Edge.html +1191 -0
- data/docs/Tasker/Diagram/Flowchart.html +1539 -0
- data/docs/Tasker/Diagram/Node.html +1165 -0
- data/docs/Tasker/Diagram.html +117 -0
- data/docs/Tasker/Engine.html +215 -0
- data/docs/Tasker/Error.html +139 -0
- data/docs/Tasker/Events/Bus.html +1226 -0
- data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
- data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
- data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
- data/docs/Tasker/Events/Catalog.html +1291 -0
- data/docs/Tasker/Events/CustomRegistry.html +943 -0
- data/docs/Tasker/Events/DefinitionLoader.html +575 -0
- data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
- data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
- data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
- data/docs/Tasker/Events/Publisher.html +365 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
- data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
- data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
- data/docs/Tasker/Events/Subscribers.html +117 -0
- data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
- data/docs/Tasker/Events.html +294 -0
- data/docs/Tasker/EventsGenerator.html +459 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
- data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
- data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
- data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
- data/docs/Tasker/Functions.html +117 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
- data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
- data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
- data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
- data/docs/Tasker/Generators.html +117 -0
- data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
- data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
- data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
- data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
- data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
- data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
- data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
- data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
- data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
- data/docs/Tasker/GraphQLTypes.html +130 -0
- data/docs/Tasker/GraphqlController.html +251 -0
- data/docs/Tasker/HandlerFactory.html +1518 -0
- data/docs/Tasker/HandlerSerializer.html +682 -0
- data/docs/Tasker/HandlersController.html +574 -0
- data/docs/Tasker/HashIdentityStrategy.html +278 -0
- data/docs/Tasker/Health/ReadinessChecker.html +712 -0
- data/docs/Tasker/Health/StatusChecker.html +653 -0
- data/docs/Tasker/Health.html +117 -0
- data/docs/Tasker/HealthController.html +523 -0
- data/docs/Tasker/IdentityStrategy.html +276 -0
- data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
- data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events.html +204 -0
- data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
- data/docs/Tasker/LifecycleEvents.html +799 -0
- data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
- data/docs/Tasker/Logging.html +115 -0
- data/docs/Tasker/MetricsController.html +293 -0
- data/docs/Tasker/MetricsExportJob.html +414 -0
- data/docs/Tasker/Mutations/BaseMutation.html +128 -0
- data/docs/Tasker/Mutations/CancelStep.html +219 -0
- data/docs/Tasker/Mutations/CancelTask.html +221 -0
- data/docs/Tasker/Mutations/CreateTask.html +243 -0
- data/docs/Tasker/Mutations/UpdateStep.html +243 -0
- data/docs/Tasker/Mutations/UpdateTask.html +243 -0
- data/docs/Tasker/Mutations.html +117 -0
- data/docs/Tasker/NamedStep.html +216 -0
- data/docs/Tasker/NamedTask.html +910 -0
- data/docs/Tasker/NamedTasksNamedStep.html +435 -0
- data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
- data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
- data/docs/Tasker/Orchestration/Coordinator.html +641 -0
- data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
- data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
- data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
- data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
- data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
- data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
- data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
- data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
- data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
- data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
- data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
- data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
- data/docs/Tasker/Orchestration.html +130 -0
- data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
- data/docs/Tasker/PageSort.html +247 -0
- data/docs/Tasker/PermanentError.html +518 -0
- data/docs/Tasker/ProceduralError.html +147 -0
- data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
- data/docs/Tasker/Queries/AllTasks.html +221 -0
- data/docs/Tasker/Queries/BaseQuery.html +128 -0
- data/docs/Tasker/Queries/Helpers.html +187 -0
- data/docs/Tasker/Queries/OneStep.html +225 -0
- data/docs/Tasker/Queries/OneTask.html +217 -0
- data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
- data/docs/Tasker/Queries/TasksByStatus.html +233 -0
- data/docs/Tasker/Queries.html +119 -0
- data/docs/Tasker/Railtie.html +124 -0
- data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
- data/docs/Tasker/Registry/EventPublisher.html +667 -0
- data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
- data/docs/Tasker/Registry/RegistrationError.html +132 -0
- data/docs/Tasker/Registry/RegistryError.html +139 -0
- data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
- data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
- data/docs/Tasker/Registry/ValidationError.html +132 -0
- data/docs/Tasker/Registry.html +119 -0
- data/docs/Tasker/RetryableError.html +515 -0
- data/docs/Tasker/StateMachine/Compatibility.html +282 -0
- data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
- data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
- data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
- data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
- data/docs/Tasker/StateMachine.html +602 -0
- data/docs/Tasker/StepDagRelationship.html +657 -0
- data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
- data/docs/Tasker/StepHandler/Api.html +884 -0
- data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
- data/docs/Tasker/StepHandler/Base.html +970 -0
- data/docs/Tasker/StepHandler.html +119 -0
- data/docs/Tasker/StepReadinessStatus.html +836 -0
- data/docs/Tasker/Task.html +2575 -0
- data/docs/Tasker/TaskAnnotation.html +137 -0
- data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
- data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
- data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
- data/docs/Tasker/TaskBuilder.html +764 -0
- data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
- data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
- data/docs/Tasker/TaskDiagram.html +548 -0
- data/docs/Tasker/TaskDiagramsController.html +240 -0
- data/docs/Tasker/TaskExecutionContext.html +469 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
- data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
- data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
- data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
- data/docs/Tasker/TaskHandler.html +271 -0
- data/docs/Tasker/TaskNamespace.html +312 -0
- data/docs/Tasker/TaskRunnerJob.html +406 -0
- data/docs/Tasker/TaskSerializer.html +474 -0
- data/docs/Tasker/TaskTransition.html +1517 -0
- data/docs/Tasker/TaskWorkflowSummary.html +988 -0
- data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
- data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema.html +384 -0
- data/docs/Tasker/TasksController.html +595 -0
- data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
- data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
- data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
- data/docs/Tasker/Telemetry/Events.html +115 -0
- data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
- data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
- data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
- data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
- data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
- data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
- data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
- data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
- data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
- data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
- data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
- data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
- data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
- data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
- data/docs/Tasker/Telemetry/Plugins.html +117 -0
- data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
- data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
- data/docs/Tasker/Telemetry.html +130 -0
- data/docs/Tasker/Types/AuthConfig.html +886 -0
- data/docs/Tasker/Types/BackoffConfig.html +1063 -0
- data/docs/Tasker/Types/BaseConfig.html +227 -0
- data/docs/Tasker/Types/CacheConfig.html +1731 -0
- data/docs/Tasker/Types/DatabaseConfig.html +388 -0
- data/docs/Tasker/Types/DependencyGraph.html +526 -0
- data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
- data/docs/Tasker/Types/EngineConfig.html +1181 -0
- data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
- data/docs/Tasker/Types/GraphEdge.html +517 -0
- data/docs/Tasker/Types/GraphMetadata.html +781 -0
- data/docs/Tasker/Types/GraphNode.html +694 -0
- data/docs/Tasker/Types/HealthConfig.html +784 -0
- data/docs/Tasker/Types/StepSequence.html +353 -0
- data/docs/Tasker/Types/StepTemplate.html +1193 -0
- data/docs/Tasker/Types/TaskRequest.html +1179 -0
- data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
- data/docs/Tasker/Types.html +154 -0
- data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
- data/docs/Tasker/WorkflowStep.html +2724 -0
- data/docs/Tasker/WorkflowStepEdge.html +304 -0
- data/docs/Tasker/WorkflowStepSerializer.html +305 -0
- data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
- data/docs/Tasker/WorkflowStepTransition.html +2201 -0
- data/docs/Tasker/WorkflowStepsController.html +462 -0
- data/docs/Tasker.html +452 -0
- data/docs/VISION.md +584 -0
- data/docs/WHY.md +21 -0
- data/docs/_index.html +2375 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/events/migration_plan_outcomes.md +80 -0
- data/docs/file.README.html +541 -0
- data/docs/file_list.html +59 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +541 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +9182 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/generators/tasker/authenticator_generator.rb +301 -0
- data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
- data/lib/generators/tasker/events_generator.rb +91 -0
- data/lib/generators/tasker/subscriber_generator.rb +107 -0
- data/lib/generators/tasker/task_handler_generator.rb +138 -0
- data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
- data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
- data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
- data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
- data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
- data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
- data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
- data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
- data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
- data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
- data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
- data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
- data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
- data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
- data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
- data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
- data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
- data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
- data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
- data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
- data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
- data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
- data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
- data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
- data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
- data/lib/tasker/authentication/coordinator.rb +78 -0
- data/lib/tasker/authentication/errors.rb +9 -0
- data/lib/tasker/authentication/interface.rb +36 -0
- data/lib/tasker/authentication/none_authenticator.rb +26 -0
- data/lib/tasker/authorization/base_coordinator.rb +112 -0
- data/lib/tasker/authorization/errors.rb +26 -0
- data/lib/tasker/authorization/resource_constants.rb +74 -0
- data/lib/tasker/authorization/resource_registry.rb +143 -0
- data/lib/tasker/authorization.rb +75 -0
- data/lib/tasker/cache_capabilities.rb +131 -0
- data/lib/tasker/cache_strategy.rb +469 -0
- data/lib/tasker/concerns/authenticatable.rb +41 -0
- data/lib/tasker/concerns/authorizable.rb +204 -0
- data/lib/tasker/concerns/controller_authorizable.rb +124 -0
- data/lib/tasker/concerns/event_publisher.rb +716 -0
- data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
- data/lib/tasker/concerns/state_machine_base.rb +218 -0
- data/lib/tasker/concerns/structured_logging.rb +387 -0
- data/lib/tasker/configuration.rb +325 -0
- data/lib/tasker/constants/event_definitions.rb +147 -0
- data/lib/tasker/constants/registry_events.rb +54 -0
- data/lib/tasker/constants.rb +417 -0
- data/lib/tasker/engine.rb +90 -0
- data/lib/tasker/errors.rb +90 -0
- data/lib/tasker/events/catalog.rb +432 -0
- data/lib/tasker/events/custom_registry.rb +175 -0
- data/lib/tasker/events/definition_loader.rb +199 -0
- data/lib/tasker/events/event_payload_builder.rb +461 -0
- data/lib/tasker/events/publisher.rb +149 -0
- data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
- data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
- data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
- data/lib/tasker/events/subscription_loader.rb +161 -0
- data/lib/tasker/events.rb +37 -0
- data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
- data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
- data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
- data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
- data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
- data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
- data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
- data/lib/tasker/functions/function_wrapper.rb +42 -0
- data/lib/tasker/functions.rb +12 -0
- data/lib/tasker/handler_factory.rb +322 -0
- data/lib/tasker/health/readiness_checker.rb +186 -0
- data/lib/tasker/health/status_checker.rb +203 -0
- data/lib/tasker/identity_strategy.rb +38 -0
- data/lib/tasker/logging/correlation_id_generator.rb +120 -0
- data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
- data/lib/tasker/orchestration/connection_builder.rb +122 -0
- data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
- data/lib/tasker/orchestration/coordinator.rb +119 -0
- data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
- data/lib/tasker/orchestration/plugin_integration.rb +124 -0
- data/lib/tasker/orchestration/response_processor.rb +168 -0
- data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
- data/lib/tasker/orchestration/step_executor.rb +941 -0
- data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
- data/lib/tasker/orchestration/task_finalizer.rb +564 -0
- data/lib/tasker/orchestration/task_initializer.rb +140 -0
- data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
- data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
- data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
- data/lib/tasker/orchestration.rb +45 -0
- data/lib/tasker/railtie.rb +9 -0
- data/lib/tasker/registry/base_registry.rb +177 -0
- data/lib/tasker/registry/event_publisher.rb +91 -0
- data/lib/tasker/registry/interface_validator.rb +140 -0
- data/lib/tasker/registry/statistics_collector.rb +381 -0
- data/lib/tasker/registry/subscriber_registry.rb +285 -0
- data/lib/tasker/registry.rb +22 -0
- data/lib/tasker/state_machine/step_state_machine.rb +508 -0
- data/lib/tasker/state_machine/task_state_machine.rb +192 -0
- data/lib/tasker/state_machine.rb +83 -0
- data/lib/tasker/step_handler/api.rb +410 -0
- data/lib/tasker/step_handler/base.rb +206 -0
- data/lib/tasker/task_builder.rb +432 -0
- data/lib/tasker/task_handler/class_methods.rb +324 -0
- data/lib/tasker/task_handler/instance_methods.rb +293 -0
- data/lib/tasker/task_handler/step_group.rb +182 -0
- data/lib/tasker/task_handler.rb +43 -0
- data/lib/tasker/telemetry/event_mapping.rb +126 -0
- data/lib/tasker/telemetry/event_router.rb +318 -0
- data/lib/tasker/telemetry/events/export_events.rb +38 -0
- data/lib/tasker/telemetry/export_coordinator.rb +497 -0
- data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
- data/lib/tasker/telemetry/log_backend.rb +224 -0
- data/lib/tasker/telemetry/metric_types.rb +368 -0
- data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
- data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
- data/lib/tasker/telemetry/plugin_registry.rb +333 -0
- data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
- data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
- data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
- data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
- data/lib/tasker/telemetry/trace_backend.rb +186 -0
- data/lib/tasker/telemetry.rb +59 -0
- data/lib/tasker/types/auth_config.rb +81 -0
- data/lib/tasker/types/backoff_config.rb +142 -0
- data/lib/tasker/types/cache_config.rb +257 -0
- data/lib/tasker/types/database_config.rb +39 -0
- data/lib/tasker/types/dependency_graph.rb +225 -0
- data/lib/tasker/types/dependency_graph_config.rb +149 -0
- data/lib/tasker/types/engine_config.rb +131 -0
- data/lib/tasker/types/execution_config.rb +289 -0
- data/lib/tasker/types/health_config.rb +84 -0
- data/lib/tasker/types/step_sequence.rb +24 -0
- data/lib/tasker/types/step_template.rb +63 -0
- data/lib/tasker/types/task_request.rb +60 -0
- data/lib/tasker/types/telemetry_config.rb +273 -0
- data/lib/tasker/types.rb +64 -0
- data/lib/tasker/version.rb +7 -0
- data/lib/tasker.rb +82 -0
- data/lib/tasks/tasker_tasks.rake +302 -0
- 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
|