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,461 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../../../lib/tasker/state_machine/step_state_machine'
|
5
|
+
|
6
|
+
module Tasker
|
7
|
+
class WorkflowStep < ApplicationRecord
|
8
|
+
PROVIDES_EDGE_NAME = 'provides'
|
9
|
+
|
10
|
+
self.primary_key = :workflow_step_id
|
11
|
+
belongs_to :task
|
12
|
+
belongs_to :named_step
|
13
|
+
# belongs_to :depends_on_step, class_name: 'WorkflowStep', optional: true
|
14
|
+
has_many :incoming_edges,
|
15
|
+
class_name: 'WorkflowStepEdge',
|
16
|
+
foreign_key: :to_step_id,
|
17
|
+
dependent: :destroy,
|
18
|
+
inverse_of: :to_step
|
19
|
+
has_many :outgoing_edges,
|
20
|
+
class_name: 'WorkflowStepEdge',
|
21
|
+
foreign_key:
|
22
|
+
:from_step_id,
|
23
|
+
dependent: :destroy,
|
24
|
+
inverse_of: :from_step
|
25
|
+
has_many :parents, through: :incoming_edges, source: :from_step
|
26
|
+
has_many :children, through: :outgoing_edges, source: :to_step
|
27
|
+
has_many :siblings, through: :outgoing_edges, source: :from_step
|
28
|
+
has_many :workflow_step_transitions, inverse_of: :workflow_step, dependent: :destroy
|
29
|
+
|
30
|
+
validates :named_step_id, uniqueness: { scope: :task_id, message: 'must be unique within the same task' }
|
31
|
+
validate :name_uniqueness_within_task
|
32
|
+
|
33
|
+
delegate :name, to: :named_step
|
34
|
+
|
35
|
+
has_one :step_dag_relationship, class_name: 'Tasker::StepDagRelationship', primary_key: :workflow_step_id
|
36
|
+
# NOTE: step_readiness_status is now accessed via function-based approach, not ActiveRecord association
|
37
|
+
|
38
|
+
# Optimized scopes for efficient querying using state machine transitions
|
39
|
+
scope :completed, lambda {
|
40
|
+
joins(:workflow_step_transitions)
|
41
|
+
.where(
|
42
|
+
workflow_step_transitions: {
|
43
|
+
most_recent: true,
|
44
|
+
to_state: [
|
45
|
+
Constants::WorkflowStepStatuses::COMPLETE,
|
46
|
+
Constants::WorkflowStepStatuses::RESOLVED_MANUALLY
|
47
|
+
]
|
48
|
+
}
|
49
|
+
)
|
50
|
+
}
|
51
|
+
|
52
|
+
scope :failed, lambda {
|
53
|
+
joins(:workflow_step_transitions)
|
54
|
+
.where(
|
55
|
+
workflow_step_transitions: {
|
56
|
+
most_recent: true,
|
57
|
+
to_state: Constants::WorkflowStepStatuses::ERROR
|
58
|
+
}
|
59
|
+
)
|
60
|
+
}
|
61
|
+
|
62
|
+
scope :pending, lambda {
|
63
|
+
# Include steps with no transitions (initial state) AND steps with pending/in_progress transitions
|
64
|
+
where.missing(:workflow_step_transitions)
|
65
|
+
.or(
|
66
|
+
joins(:workflow_step_transitions)
|
67
|
+
.where(
|
68
|
+
workflow_step_transitions: {
|
69
|
+
most_recent: true,
|
70
|
+
to_state: [
|
71
|
+
Constants::WorkflowStepStatuses::PENDING,
|
72
|
+
Constants::WorkflowStepStatuses::IN_PROGRESS
|
73
|
+
]
|
74
|
+
}
|
75
|
+
)
|
76
|
+
)
|
77
|
+
}
|
78
|
+
|
79
|
+
scope :for_task, lambda { |task|
|
80
|
+
where(task_id: task.task_id)
|
81
|
+
}
|
82
|
+
|
83
|
+
# Scopes workflow steps by their current state using state machine transitions
|
84
|
+
#
|
85
|
+
# @scope class
|
86
|
+
# @param state [String, nil] The state to filter by. If nil, returns all steps with current state information
|
87
|
+
# @return [ActiveRecord::Relation] Steps with current state, optionally filtered by specific state
|
88
|
+
scope :by_current_state, lambda { |state = nil|
|
89
|
+
relation = joins(<<-SQL.squish)
|
90
|
+
INNER JOIN (
|
91
|
+
SELECT DISTINCT ON (workflow_step_id) workflow_step_id, to_state
|
92
|
+
FROM tasker_workflow_step_transitions
|
93
|
+
WHERE most_recent = true
|
94
|
+
ORDER BY workflow_step_id, sort_key DESC
|
95
|
+
) current_transitions ON current_transitions.workflow_step_id = tasker_workflow_steps.workflow_step_id
|
96
|
+
SQL
|
97
|
+
|
98
|
+
if state.present?
|
99
|
+
relation.where(current_transitions: { to_state: state })
|
100
|
+
else
|
101
|
+
relation
|
102
|
+
end
|
103
|
+
}
|
104
|
+
|
105
|
+
# Scopes workflow steps completed since a specific time
|
106
|
+
#
|
107
|
+
# @scope class
|
108
|
+
# @param since_time [Time] The earliest completion time to include
|
109
|
+
# @return [ActiveRecord::Relation] Steps completed since the specified time
|
110
|
+
scope :completed_since, lambda { |since_time|
|
111
|
+
joins(:workflow_step_transitions)
|
112
|
+
.where('tasker_workflow_step_transitions.most_recent = ? AND tasker_workflow_step_transitions.to_state = ?', true, 'complete')
|
113
|
+
.where('tasker_workflow_step_transitions.created_at > ?', since_time)
|
114
|
+
}
|
115
|
+
|
116
|
+
# Scopes workflow steps that failed since a specific time
|
117
|
+
#
|
118
|
+
# @scope class
|
119
|
+
# @param since_time [Time] The earliest failure time to include
|
120
|
+
# @return [ActiveRecord::Relation] Steps that failed since the specified time
|
121
|
+
scope :failed_since, lambda { |since_time|
|
122
|
+
joins(:workflow_step_transitions)
|
123
|
+
.where('tasker_workflow_step_transitions.most_recent = ? AND tasker_workflow_step_transitions.to_state = ?', true, 'error')
|
124
|
+
.where('tasker_workflow_step_transitions.created_at > ?', since_time)
|
125
|
+
}
|
126
|
+
|
127
|
+
# Scopes workflow steps for tasks created since a specific time
|
128
|
+
#
|
129
|
+
# @scope class
|
130
|
+
# @param since_time [Time] The earliest task creation time to include
|
131
|
+
# @return [ActiveRecord::Relation] Steps for tasks created since the specified time
|
132
|
+
scope :for_tasks_since, lambda { |since_time|
|
133
|
+
joins(:task).where('tasker_tasks.created_at > ?', since_time)
|
134
|
+
}
|
135
|
+
|
136
|
+
# Efficient method to get task completion statistics using ActiveRecord scopes
|
137
|
+
# This avoids the N+1 query problem while working with the state machine system
|
138
|
+
#
|
139
|
+
# @param task [Task] The task to analyze
|
140
|
+
# @return [Hash] Hash with completion statistics and latest completion time
|
141
|
+
def self.task_completion_stats(task)
|
142
|
+
# Use efficient ActiveRecord queries with the state machine
|
143
|
+
task_steps = for_task(task)
|
144
|
+
|
145
|
+
# Get completion statistics with optimized queries
|
146
|
+
total_steps = task_steps.count
|
147
|
+
completed_steps = task_steps.completed
|
148
|
+
failed_steps = task_steps.failed
|
149
|
+
|
150
|
+
# Calculate counts
|
151
|
+
completed_count = completed_steps.count
|
152
|
+
failed_count = failed_steps.count
|
153
|
+
|
154
|
+
# For pending count, calculate as total minus completed and failed
|
155
|
+
# This handles the case where new steps don't have transitions yet
|
156
|
+
pending_count = total_steps - completed_count - failed_count
|
157
|
+
|
158
|
+
# Get latest completion time from completed steps
|
159
|
+
latest_completion_time = completed_steps.maximum(:processed_at)
|
160
|
+
|
161
|
+
{
|
162
|
+
total_steps: total_steps,
|
163
|
+
completed_steps: completed_count,
|
164
|
+
failed_steps: failed_count,
|
165
|
+
pending_steps: pending_count,
|
166
|
+
latest_completion_time: latest_completion_time,
|
167
|
+
all_complete: completed_count == total_steps && total_steps.positive?
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
# State machine integration
|
172
|
+
def state_machine
|
173
|
+
@state_machine ||= Tasker::StateMachine::StepStateMachine.new(
|
174
|
+
self,
|
175
|
+
transition_class: Tasker::WorkflowStepTransition,
|
176
|
+
association_name: :workflow_step_transitions
|
177
|
+
)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Status is now entirely managed by the state machine
|
181
|
+
def status
|
182
|
+
if new_record?
|
183
|
+
# For new records, return the initial state
|
184
|
+
Tasker::Constants::WorkflowStepStatuses::PENDING
|
185
|
+
else
|
186
|
+
# For persisted records, use state machine
|
187
|
+
state_machine.current_state
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Finds a WorkflowStep with the given name by traversing the DAG efficiently
|
192
|
+
# @param steps [Array<WorkflowStep>] Collection of steps to search through
|
193
|
+
# @param name [String] Name of the step to find
|
194
|
+
# @return [WorkflowStep, nil] The first matching step found or nil if none exists
|
195
|
+
def self.find_step_by_name(steps, name)
|
196
|
+
StepFinder.find_by_name(steps, name)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Service class to find steps by name
|
200
|
+
# Reduces complexity by organizing step search logic
|
201
|
+
class StepFinder
|
202
|
+
class << self
|
203
|
+
# Find step by name in provided collection or task hierarchy
|
204
|
+
#
|
205
|
+
# @param steps [Array<WorkflowStep>] Collection of steps to search through
|
206
|
+
# @param name [String] Name of the step to find
|
207
|
+
# @return [WorkflowStep, nil] The first matching step found or nil if none exists
|
208
|
+
def find_by_name(steps, name)
|
209
|
+
return nil if steps.empty? || name.nil?
|
210
|
+
|
211
|
+
# First check direct match in provided steps
|
212
|
+
direct_match = find_direct_match(steps, name)
|
213
|
+
return direct_match if direct_match
|
214
|
+
|
215
|
+
# Fall back to task-wide search using DAG relationships
|
216
|
+
find_in_task_hierarchy(steps, name)
|
217
|
+
end
|
218
|
+
|
219
|
+
private
|
220
|
+
|
221
|
+
# Find direct match in provided steps
|
222
|
+
#
|
223
|
+
# @param steps [Array<WorkflowStep>] Collection of steps
|
224
|
+
# @param name [String] Name to search for
|
225
|
+
# @return [WorkflowStep, nil] Matching step or nil
|
226
|
+
def find_direct_match(steps, name)
|
227
|
+
steps.find { |step| step.name == name }
|
228
|
+
end
|
229
|
+
|
230
|
+
# Find step in task hierarchy using efficient DAG traversal
|
231
|
+
#
|
232
|
+
# @param steps [Array<WorkflowStep>] Collection of steps to get task context
|
233
|
+
# @param name [String] Name to search for
|
234
|
+
# @return [WorkflowStep, nil] Matching step or nil
|
235
|
+
def find_in_task_hierarchy(steps, name)
|
236
|
+
task_ids = extract_task_ids(steps)
|
237
|
+
|
238
|
+
task_ids.each do |task_id|
|
239
|
+
found_step = find_in_single_task(task_id, name)
|
240
|
+
return found_step if found_step
|
241
|
+
end
|
242
|
+
|
243
|
+
nil
|
244
|
+
end
|
245
|
+
|
246
|
+
# Extract unique task IDs from steps
|
247
|
+
#
|
248
|
+
# @param steps [Array<WorkflowStep>] Collection of steps
|
249
|
+
# @return [Array<Integer>] Unique task IDs
|
250
|
+
def extract_task_ids(steps)
|
251
|
+
steps.map(&:task_id).uniq
|
252
|
+
end
|
253
|
+
|
254
|
+
# Find step by name in a single task
|
255
|
+
#
|
256
|
+
# @param task_id [Integer] Task ID to search in
|
257
|
+
# @param name [String] Name to search for
|
258
|
+
# @return [WorkflowStep, nil] Matching step or nil
|
259
|
+
def find_in_single_task(task_id, name)
|
260
|
+
# Get all workflow steps for this task with their DAG relationships
|
261
|
+
all_task_steps = WorkflowStep.joins(:named_step)
|
262
|
+
.includes(:step_dag_relationship)
|
263
|
+
.where(task_id: task_id)
|
264
|
+
|
265
|
+
# Find step by name using simple lookup instead of recursive traversal
|
266
|
+
all_task_steps.joins(:named_step)
|
267
|
+
.find_by(named_steps: { name: name })
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def self.get_steps_for_task(task, templates)
|
273
|
+
named_steps = NamedStep.create_named_steps_from_templates(templates)
|
274
|
+
steps =
|
275
|
+
templates.map do |template|
|
276
|
+
named_step = named_steps.find { |ns| template.name == ns.name }
|
277
|
+
NamedTasksNamedStep.associate_named_step_with_named_task(task.named_task, template, named_step)
|
278
|
+
step = where(task_id: task.task_id, named_step_id: named_step.named_step_id).first
|
279
|
+
step ||= build_default_step!(task, template, named_step)
|
280
|
+
step
|
281
|
+
end
|
282
|
+
set_up_dependent_steps(steps, templates)
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.set_up_dependent_steps(steps, templates)
|
286
|
+
templates.each do |template|
|
287
|
+
next if template.all_dependencies.empty?
|
288
|
+
|
289
|
+
dependent_step = steps.find { |step| step.name == template.name }
|
290
|
+
template.all_dependencies.each do |dependency|
|
291
|
+
provider_step = steps.find { |step| step.name == dependency }
|
292
|
+
unless provider_step.outgoing_edges.exists?(to_step: dependent_step)
|
293
|
+
provider_step.add_provides_edge!(dependent_step)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
steps
|
298
|
+
end
|
299
|
+
|
300
|
+
def self.build_default_step!(task, template, named_step)
|
301
|
+
# Create the step first without status
|
302
|
+
step_attributes = {
|
303
|
+
task_id: task.task_id,
|
304
|
+
named_step_id: named_step.named_step_id,
|
305
|
+
retryable: template.default_retryable,
|
306
|
+
retry_limit: template.default_retry_limit,
|
307
|
+
skippable: template.skippable,
|
308
|
+
in_process: false,
|
309
|
+
inputs: task.context,
|
310
|
+
processed: false,
|
311
|
+
attempts: 0,
|
312
|
+
results: {}
|
313
|
+
}
|
314
|
+
|
315
|
+
step = new(step_attributes)
|
316
|
+
step.save!
|
317
|
+
|
318
|
+
# REMOVED: Automatic state machine initialization to prevent duplicate key violations
|
319
|
+
# The state machine will initialize naturally when accessed, and factories may
|
320
|
+
# have already created transitions through their own setup
|
321
|
+
# step.state_machine.initialize_state_machine!
|
322
|
+
|
323
|
+
step
|
324
|
+
end
|
325
|
+
|
326
|
+
def self.get_viable_steps(task, sequence)
|
327
|
+
# Get step IDs from sequence
|
328
|
+
step_ids = sequence.steps.map(&:workflow_step_id)
|
329
|
+
|
330
|
+
# Use function-based approach for high-performance readiness checking
|
331
|
+
ready_statuses = StepReadinessStatus.for_task(task.task_id, step_ids)
|
332
|
+
ready_step_ids = ready_statuses.select(&:ready_for_execution).map(&:workflow_step_id)
|
333
|
+
|
334
|
+
# Return WorkflowStep objects for ready steps
|
335
|
+
WorkflowStep.where(workflow_step_id: ready_step_ids)
|
336
|
+
.includes(:named_step)
|
337
|
+
end
|
338
|
+
|
339
|
+
def add_provides_edge!(to_step)
|
340
|
+
outgoing_edges.create!(to_step: to_step, name: PROVIDES_EDGE_NAME)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Helper method to get step readiness status using function-based approach
|
344
|
+
def step_readiness_status
|
345
|
+
@step_readiness_status ||= StepReadinessStatus.for_task(task_id, [workflow_step_id]).first
|
346
|
+
end
|
347
|
+
|
348
|
+
def complete?
|
349
|
+
# Use function-based approach for consistent state checking
|
350
|
+
step_readiness_status&.current_state&.in?([
|
351
|
+
Constants::WorkflowStepStatuses::COMPLETE,
|
352
|
+
Constants::WorkflowStepStatuses::RESOLVED_MANUALLY
|
353
|
+
]) || false
|
354
|
+
end
|
355
|
+
|
356
|
+
def in_progress?
|
357
|
+
# Use function-based approach for consistent state checking
|
358
|
+
step_readiness_status&.current_state == Constants::WorkflowStepStatuses::IN_PROGRESS
|
359
|
+
end
|
360
|
+
|
361
|
+
def pending?
|
362
|
+
# Use function-based approach for consistent state checking
|
363
|
+
step_readiness_status&.current_state == Constants::WorkflowStepStatuses::PENDING
|
364
|
+
end
|
365
|
+
|
366
|
+
def in_error?
|
367
|
+
# Use function-based approach for consistent state checking
|
368
|
+
step_readiness_status&.current_state == Constants::WorkflowStepStatuses::ERROR
|
369
|
+
end
|
370
|
+
|
371
|
+
def cancelled?
|
372
|
+
# Use function-based approach for consistent state checking
|
373
|
+
step_readiness_status&.current_state == Constants::WorkflowStepStatuses::CANCELLED
|
374
|
+
end
|
375
|
+
|
376
|
+
def ready_status?
|
377
|
+
# Use function-based approach for efficient ready status checking
|
378
|
+
Constants::UNREADY_WORKFLOW_STEP_STATUSES.exclude?(
|
379
|
+
step_readiness_status&.current_state || Constants::WorkflowStepStatuses::PENDING
|
380
|
+
)
|
381
|
+
end
|
382
|
+
|
383
|
+
def ready?
|
384
|
+
# Use function-based approach's comprehensive readiness calculation
|
385
|
+
step_readiness_status&.ready_for_execution || false
|
386
|
+
end
|
387
|
+
|
388
|
+
# Function-based predicate methods
|
389
|
+
def dependencies_satisfied?
|
390
|
+
# Use function-based approach's pre-calculated dependency analysis
|
391
|
+
step_readiness_status&.dependencies_satisfied || false
|
392
|
+
end
|
393
|
+
|
394
|
+
def retry_eligible?
|
395
|
+
# Use function-based approach's retry/backoff calculation
|
396
|
+
step_readiness_status&.retry_eligible || false
|
397
|
+
end
|
398
|
+
|
399
|
+
def has_retry_attempts?
|
400
|
+
# Check if step has made retry attempts
|
401
|
+
(step_readiness_status&.attempts || 0).positive?
|
402
|
+
end
|
403
|
+
|
404
|
+
def retry_exhausted?
|
405
|
+
# Check if step has exhausted retry attempts
|
406
|
+
return false unless step_readiness_status
|
407
|
+
|
408
|
+
attempts = step_readiness_status.attempts || 0
|
409
|
+
retry_limit = step_readiness_status.retry_limit || 3
|
410
|
+
attempts >= retry_limit
|
411
|
+
end
|
412
|
+
|
413
|
+
def waiting_for_backoff?
|
414
|
+
# Check if step is waiting for backoff period to expire
|
415
|
+
return false unless step_readiness_status&.next_retry_at
|
416
|
+
|
417
|
+
step_readiness_status.next_retry_at > Time.current
|
418
|
+
end
|
419
|
+
|
420
|
+
def can_retry_now?
|
421
|
+
# Comprehensive check if step can be retried right now
|
422
|
+
return false unless in_error?
|
423
|
+
return false unless retry_eligible?
|
424
|
+
return false if waiting_for_backoff?
|
425
|
+
|
426
|
+
true
|
427
|
+
end
|
428
|
+
|
429
|
+
def root_step?
|
430
|
+
# Check if this is a root step (no dependencies)
|
431
|
+
(step_readiness_status&.total_parents || 0).zero?
|
432
|
+
end
|
433
|
+
|
434
|
+
def leaf_step?
|
435
|
+
# Check if this is a leaf step using DAG relationship view
|
436
|
+
step_dag_relationship&.child_count&.zero?
|
437
|
+
end
|
438
|
+
|
439
|
+
def reload
|
440
|
+
# Override reload to ensure step readiness status is refreshed
|
441
|
+
super.tap do
|
442
|
+
@step_readiness_status = nil # Reset cached readiness status
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
private
|
447
|
+
|
448
|
+
# Custom validation to ensure step names are unique within a task
|
449
|
+
def name_uniqueness_within_task
|
450
|
+
return unless named_step && task
|
451
|
+
|
452
|
+
# Find all steps within the same task that have the same name
|
453
|
+
matching_steps = self.class.where(task_id: task_id)
|
454
|
+
.joins(:named_step)
|
455
|
+
.where(named_step: { name: name })
|
456
|
+
.where.not(workflow_step_id: workflow_step_id) # Exclude self when updating
|
457
|
+
|
458
|
+
errors.add(:base, "Step name '#{name}' must be unique within the same task") if matching_steps.exists?
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tasker
|
4
|
+
class WorkflowStepEdge < ApplicationRecord
|
5
|
+
belongs_to :from_step, class_name: 'WorkflowStep'
|
6
|
+
belongs_to :to_step, class_name: 'WorkflowStep'
|
7
|
+
|
8
|
+
validates :name, presence: true
|
9
|
+
|
10
|
+
before_create :ensure_no_cycles!
|
11
|
+
|
12
|
+
scope :children_of, ->(step) { where(from_step: step) }
|
13
|
+
scope :parents_of, ->(step) { where(to_step: step) }
|
14
|
+
scope :siblings_of, ->(step) { find_by_sql(sibling_sql(step.id)) }
|
15
|
+
scope :provides_edges, -> { where(name: WorkflowStep::PROVIDES_EDGE_NAME) }
|
16
|
+
scope :provides_to_children, -> { where(name: WorkflowStep::PROVIDES_EDGE_NAME, to_step: children_of(from_step)) }
|
17
|
+
|
18
|
+
def self.create_edge!(from_step, to_step, name)
|
19
|
+
create!(from_step: from_step, to_step: to_step, name: name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.sibling_sql(step_id)
|
23
|
+
<<~SQL.squish
|
24
|
+
WITH step_parents AS (
|
25
|
+
SELECT from_step_id
|
26
|
+
FROM tasker_workflow_step_edges
|
27
|
+
WHERE to_step_id = #{step_id}
|
28
|
+
),
|
29
|
+
potential_siblings AS (
|
30
|
+
SELECT to_step_id
|
31
|
+
FROM tasker_workflow_step_edges
|
32
|
+
WHERE from_step_id IN (SELECT from_step_id FROM step_parents)
|
33
|
+
AND to_step_id != #{step_id}
|
34
|
+
),
|
35
|
+
siblings AS (
|
36
|
+
SELECT to_step_id
|
37
|
+
FROM tasker_workflow_step_edges
|
38
|
+
WHERE to_step_id IN (SELECT to_step_id FROM potential_siblings)
|
39
|
+
GROUP BY to_step_id
|
40
|
+
HAVING ARRAY_AGG(from_step_id ORDER BY from_step_id) =
|
41
|
+
(SELECT ARRAY_AGG(from_step_id ORDER BY from_step_id) FROM step_parents)
|
42
|
+
)
|
43
|
+
SELECT e.*
|
44
|
+
FROM tasker_workflow_step_edges e
|
45
|
+
JOIN siblings ON e.to_step_id = siblings.to_step_id
|
46
|
+
SQL
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def ensure_no_cycles!
|
52
|
+
return unless from_step && to_step
|
53
|
+
|
54
|
+
# Check for direct cycles first (A->B, B->A)
|
55
|
+
if self.class.exists?(from_step: to_step, to_step: from_step)
|
56
|
+
raise ActiveRecord::RecordInvalid.new(self), 'Adding this edge would create a cycle in the workflow'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check for indirect cycles (A->B->C->A)
|
60
|
+
# Use a recursive CTE that includes our new edge
|
61
|
+
cycle_sql = <<~SQL
|
62
|
+
WITH RECURSIVE all_edges AS (
|
63
|
+
-- Combine existing edges with our new edge
|
64
|
+
SELECT from_step_id, to_step_id
|
65
|
+
FROM tasker_workflow_step_edges
|
66
|
+
UNION ALL
|
67
|
+
SELECT #{from_step.id}::bigint, #{to_step.id}::bigint
|
68
|
+
),
|
69
|
+
path AS (
|
70
|
+
-- Start with edges from to_step
|
71
|
+
SELECT from_step_id, to_step_id, ARRAY[from_step_id] as path
|
72
|
+
FROM all_edges
|
73
|
+
WHERE from_step_id = #{to_step.id}::bigint
|
74
|
+
|
75
|
+
UNION ALL
|
76
|
+
|
77
|
+
-- Follow edges recursively
|
78
|
+
SELECT e.from_step_id, e.to_step_id, p.path || e.from_step_id
|
79
|
+
FROM all_edges e
|
80
|
+
JOIN path p ON e.from_step_id = p.to_step_id
|
81
|
+
WHERE NOT e.from_step_id = ANY(p.path) -- Avoid cycles in traversal
|
82
|
+
)
|
83
|
+
SELECT COUNT(*) as cycle_count
|
84
|
+
FROM path
|
85
|
+
WHERE to_step_id = #{from_step.id}::bigint
|
86
|
+
SQL
|
87
|
+
|
88
|
+
result = self.class.connection.execute(cycle_sql).first
|
89
|
+
if result['cycle_count'].to_i > 0 # rubocop:disable Style/NumericPredicate,Style/GuardClause
|
90
|
+
raise ActiveRecord::RecordInvalid.new(self), 'Adding this edge would create a cycle in the workflow'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|