orkestr 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +832 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/builds/orkestr/orkestr-editor.js +72 -0
  6. data/app/assets/stylesheets/orkestr/application.css +15 -0
  7. data/app/assets/stylesheets/orkestr/theme.css +62 -0
  8. data/app/controllers/orkestr/api/base_controller.rb +45 -0
  9. data/app/controllers/orkestr/api/executions_controller.rb +50 -0
  10. data/app/controllers/orkestr/api/human_tasks_controller.rb +48 -0
  11. data/app/controllers/orkestr/api/registry_controller.rb +40 -0
  12. data/app/controllers/orkestr/api/workflows_controller.rb +53 -0
  13. data/app/controllers/orkestr/application_controller.rb +4 -0
  14. data/app/controllers/orkestr/human_tasks_controller.rb +30 -0
  15. data/app/controllers/orkestr/ui_controller.rb +8 -0
  16. data/app/controllers/orkestr/webhooks_controller.rb +35 -0
  17. data/app/helpers/orkestr/application_helper.rb +4 -0
  18. data/app/helpers/orkestr/ui_helper.rb +34 -0
  19. data/app/javascript/orkestr-ui/index.html +19 -0
  20. data/app/javascript/orkestr-ui/package-lock.json +2050 -0
  21. data/app/javascript/orkestr-ui/package.json +23 -0
  22. data/app/javascript/orkestr-ui/src/OrkestrApp.tsx +152 -0
  23. data/app/javascript/orkestr-ui/src/api/client.ts +59 -0
  24. data/app/javascript/orkestr-ui/src/api/executions.ts +23 -0
  25. data/app/javascript/orkestr-ui/src/api/humanTasks.ts +32 -0
  26. data/app/javascript/orkestr-ui/src/api/index.ts +5 -0
  27. data/app/javascript/orkestr-ui/src/api/registry.ts +10 -0
  28. data/app/javascript/orkestr-ui/src/api/workflows.ts +33 -0
  29. data/app/javascript/orkestr-ui/src/components/Editor/ActionsBuilder.tsx +213 -0
  30. data/app/javascript/orkestr-ui/src/components/Editor/CustomNode.tsx +31 -0
  31. data/app/javascript/orkestr-ui/src/components/Editor/EditorToolbar.tsx +153 -0
  32. data/app/javascript/orkestr-ui/src/components/Editor/FormSchemaBuilder.tsx +390 -0
  33. data/app/javascript/orkestr-ui/src/components/Editor/NodeConfigPanel.tsx +274 -0
  34. data/app/javascript/orkestr-ui/src/components/Editor/NodePalette.tsx +43 -0
  35. data/app/javascript/orkestr-ui/src/components/Editor/RunDialog.tsx +52 -0
  36. data/app/javascript/orkestr-ui/src/components/Editor/WorkflowEditor.tsx +299 -0
  37. data/app/javascript/orkestr-ui/src/components/Executions/ExecutionDetail.tsx +155 -0
  38. data/app/javascript/orkestr-ui/src/components/Executions/ExecutionList.tsx +74 -0
  39. data/app/javascript/orkestr-ui/src/components/HumanTasks/TaskForm.tsx +216 -0
  40. data/app/javascript/orkestr-ui/src/components/HumanTasks/TaskFormEmbed.tsx +117 -0
  41. data/app/javascript/orkestr-ui/src/components/HumanTasks/TaskList.tsx +110 -0
  42. data/app/javascript/orkestr-ui/src/components/Workflows/NewWorkflowDialog.tsx +64 -0
  43. data/app/javascript/orkestr-ui/src/components/Workflows/WorkflowList.tsx +94 -0
  44. data/app/javascript/orkestr-ui/src/components/shared/EntryConditionsEditor.tsx +138 -0
  45. data/app/javascript/orkestr-ui/src/components/shared/ExpressionEditor.tsx +206 -0
  46. data/app/javascript/orkestr-ui/src/components/shared/HumanTaskFormRenderer.tsx +321 -0
  47. data/app/javascript/orkestr-ui/src/components/shared/JsonSchemaForm.tsx +376 -0
  48. data/app/javascript/orkestr-ui/src/components/shared/Loading.tsx +5 -0
  49. data/app/javascript/orkestr-ui/src/components/shared/StatusBadge.tsx +9 -0
  50. data/app/javascript/orkestr-ui/src/fieldRegistry.ts +74 -0
  51. data/app/javascript/orkestr-ui/src/hooks/useApi.ts +30 -0
  52. data/app/javascript/orkestr-ui/src/hooks/useRegistry.ts +35 -0
  53. data/app/javascript/orkestr-ui/src/main.tsx +75 -0
  54. data/app/javascript/orkestr-ui/src/styles/editor.css +445 -0
  55. data/app/javascript/orkestr-ui/src/styles/index.css +478 -0
  56. data/app/javascript/orkestr-ui/src/types/execution.ts +37 -0
  57. data/app/javascript/orkestr-ui/src/types/humanTask.ts +30 -0
  58. data/app/javascript/orkestr-ui/src/types/index.ts +4 -0
  59. data/app/javascript/orkestr-ui/src/types/registry.ts +22 -0
  60. data/app/javascript/orkestr-ui/src/types/workflow.ts +64 -0
  61. data/app/javascript/orkestr-ui/src/vite-env.d.ts +6 -0
  62. data/app/javascript/orkestr-ui/tsconfig.json +21 -0
  63. data/app/javascript/orkestr-ui/tsconfig.tsbuildinfo +1 -0
  64. data/app/javascript/orkestr-ui/vite.config.ts +30 -0
  65. data/app/jobs/orkestr/application_job.rb +4 -0
  66. data/app/jobs/orkestr/execute_workflow_job.rb +10 -0
  67. data/app/jobs/orkestr/resume_execution_job.rb +15 -0
  68. data/app/mailers/orkestr/application_mailer.rb +6 -0
  69. data/app/models/concerns/orkestr/assignable.rb +28 -0
  70. data/app/models/concerns/orkestr/contextualizable.rb +9 -0
  71. data/app/models/orkestr/application_record.rb +6 -0
  72. data/app/models/orkestr/assignee.rb +40 -0
  73. data/app/models/orkestr/context.rb +42 -0
  74. data/app/models/orkestr/edge.rb +58 -0
  75. data/app/models/orkestr/execution.rb +45 -0
  76. data/app/models/orkestr/execution_log.rb +38 -0
  77. data/app/models/orkestr/human_task.rb +63 -0
  78. data/app/models/orkestr/node.rb +48 -0
  79. data/app/models/orkestr/node_execution.rb +59 -0
  80. data/app/models/orkestr/workflow.rb +39 -0
  81. data/app/orkestr_nodes/action/node.rb +77 -0
  82. data/app/orkestr_nodes/condition/node.rb +67 -0
  83. data/app/orkestr_nodes/http_request/node.rb +88 -0
  84. data/app/orkestr_nodes/human_action/node.rb +103 -0
  85. data/app/orkestr_nodes/transform/node.rb +48 -0
  86. data/app/orkestr_nodes/wait/node.rb +18 -0
  87. data/app/orkestr_triggers/manual/trigger.rb +12 -0
  88. data/app/orkestr_triggers/scheduled/trigger.rb +26 -0
  89. data/app/orkestr_triggers/webhook/trigger.rb +23 -0
  90. data/app/serializers/orkestr/assignee_serializer.rb +30 -0
  91. data/app/serializers/orkestr/context_serializer.rb +30 -0
  92. data/app/serializers/orkestr/edge_serializer.rb +33 -0
  93. data/app/serializers/orkestr/execution_collection_serializer.rb +7 -0
  94. data/app/serializers/orkestr/execution_log_serializer.rb +31 -0
  95. data/app/serializers/orkestr/execution_serializer.rb +37 -0
  96. data/app/serializers/orkestr/human_task_serializer.rb +46 -0
  97. data/app/serializers/orkestr/node_execution_serializer.rb +43 -0
  98. data/app/serializers/orkestr/node_serializer.rb +29 -0
  99. data/app/serializers/orkestr/workflow_collection_serializer.rb +11 -0
  100. data/app/serializers/orkestr/workflow_serializer.rb +30 -0
  101. data/app/services/orkestr/entry_condition_evaluator.rb +68 -0
  102. data/app/services/orkestr/execution_service/complete.rb +64 -0
  103. data/app/services/orkestr/execution_service/join_resolver.rb +56 -0
  104. data/app/services/orkestr/execution_service/node_runner.rb +56 -0
  105. data/app/services/orkestr/execution_service/runner.rb +162 -0
  106. data/app/services/orkestr/execution_service/start.rb +90 -0
  107. data/app/services/orkestr/expression_resolver.rb +72 -0
  108. data/app/services/orkestr/human_task_service/complete.rb +62 -0
  109. data/app/services/orkestr/workflow_service/duplicate.rb +30 -0
  110. data/app/services/orkestr/workflow_service/export.rb +26 -0
  111. data/app/services/orkestr/workflow_service/import.rb +29 -0
  112. data/app/services/orkestr/workflow_synchronizer.rb +102 -0
  113. data/app/views/layouts/orkestr/application.html.erb +17 -0
  114. data/app/views/layouts/orkestr/ui.html.erb +18 -0
  115. data/app/views/orkestr/human_tasks/show.html.erb +17 -0
  116. data/app/views/orkestr/ui/index.html.erb +8 -0
  117. data/config/routes.rb +27 -0
  118. data/db/migrate/20260308204133_enable_pgcrypto_extension.rb +5 -0
  119. data/db/migrate/20260308204558_create_orkestr_workflows.rb +12 -0
  120. data/db/migrate/20260308204703_create_orkestr_nodes.rb +12 -0
  121. data/db/migrate/20260308204807_create_orkestr_edges.rb +12 -0
  122. data/db/migrate/20260308204931_create_orkestr_executions.rb +13 -0
  123. data/db/migrate/20260308205023_create_orkestr_node_executions.rb +16 -0
  124. data/db/migrate/20260308205119_add_react_flow_id_to_nodes.rb +6 -0
  125. data/db/migrate/20260308205123_add_react_flow_id_to_edges.rb +6 -0
  126. data/db/migrate/20260308205745_add_workflow_to_executions.rb +5 -0
  127. data/db/migrate/20260308205940_make_executable_nullable_on_executions.rb +6 -0
  128. data/db/migrate/20260308220730_create_orkestr_human_tasks.rb +15 -0
  129. data/db/migrate/20260308220900_create_orkestr_assignees.rb +13 -0
  130. data/db/migrate/20260308234115_add_unique_index_to_node_executions.rb +7 -0
  131. data/db/migrate/20260309075336_create_orkestr_contexts.rb +10 -0
  132. data/db/migrate/20260309075343_replace_executable_with_context_on_executions.rb +11 -0
  133. data/db/migrate/20260309080416_add_status_key_deadline_to_orkestr_assignees.rb +7 -0
  134. data/db/migrate/20260309082815_add_status_and_workflow_to_orkestr_contexts.rb +7 -0
  135. data/db/migrate/20260309082816_add_status_default_context_and_reuse_context_to_orkestr_workflows.rb +7 -0
  136. data/db/migrate/20260309083328_create_orkestr_execution_logs.rb +14 -0
  137. data/db/migrate/20260310223204_replace_node_executions_unique_index_for_cycle_support.rb +18 -0
  138. data/lib/orkestr/configuration.rb +16 -0
  139. data/lib/orkestr/engine.rb +48 -0
  140. data/lib/orkestr/nodes/base.rb +74 -0
  141. data/lib/orkestr/nodes/loader.rb +32 -0
  142. data/lib/orkestr/nodes/registry.rb +34 -0
  143. data/lib/orkestr/nodes/schema_dsl.rb +73 -0
  144. data/lib/orkestr/triggers/base.rb +49 -0
  145. data/lib/orkestr/triggers/loader.rb +29 -0
  146. data/lib/orkestr/triggers/registry.rb +34 -0
  147. data/lib/orkestr/triggers/schema_dsl.rb +45 -0
  148. data/lib/orkestr/version.rb +3 -0
  149. data/lib/orkestr.rb +27 -0
  150. data/lib/tasks/annotate_rb.rake +10 -0
  151. data/lib/tasks/orkestr_tasks.rake +19 -0
  152. metadata +251 -0
@@ -0,0 +1,88 @@
1
+ require "net/http"
2
+ require "uri"
3
+
4
+ module Orkestr
5
+ module Nodes
6
+ class HttpRequest < Base
7
+ node_id "http_request"
8
+ label "HTTP Request"
9
+ category "integration"
10
+
11
+ config_schema do
12
+ string :method, enum: %w[GET POST PUT DELETE], description: "HTTP method"
13
+ string :url, description: "Target URL (supports {{variable}} interpolation)"
14
+ object :headers, required: false, description: "HTTP headers" do
15
+ # dynamic keys
16
+ end
17
+ end
18
+
19
+ input_schema do
20
+ string :body, required: false, description: "Request body (supports {{variable}} interpolation)"
21
+ end
22
+
23
+ output_schema do
24
+ number :status_code, description: "HTTP response status code"
25
+ string :response_body, description: "Response body"
26
+ object :response_headers, required: false, description: "Response headers" do
27
+ # dynamic keys
28
+ end
29
+ end
30
+
31
+ TIMEOUT = 30
32
+
33
+ def execute(context, input)
34
+ url = interpolate(config[:url].to_s, context)
35
+ method = (config[:method] || "GET").upcase
36
+ headers = build_headers(config[:headers] || {}, context)
37
+ body = input[:body] ? interpolate(input[:body].to_s, context) : nil
38
+
39
+ perform_request(method, url, headers, body)
40
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
41
+ raise ExecutionError, "HTTP timeout: #{e.message}"
42
+ rescue SocketError, Errno::ECONNREFUSED => e
43
+ raise ExecutionError, "Network error: #{e.message}"
44
+ end
45
+
46
+ class ExecutionError < StandardError; end
47
+
48
+ private
49
+
50
+ def perform_request(method, url, headers, body)
51
+ uri = URI.parse(url)
52
+ http = Net::HTTP.new(uri.host, uri.port)
53
+ http.use_ssl = uri.scheme == "https"
54
+ http.open_timeout = TIMEOUT
55
+ http.read_timeout = TIMEOUT
56
+
57
+ request = build_request(method, uri, headers, body)
58
+ response = http.request(request)
59
+
60
+ {
61
+ status_code: response.code.to_i,
62
+ response_body: response.body,
63
+ response_headers: response.each_header.to_h
64
+ }
65
+ end
66
+
67
+ def build_request(method, uri, headers, body)
68
+ request_class = {
69
+ "GET" => Net::HTTP::Get,
70
+ "POST" => Net::HTTP::Post,
71
+ "PUT" => Net::HTTP::Put,
72
+ "DELETE" => Net::HTTP::Delete
73
+ }.fetch(method)
74
+
75
+ request = request_class.new(uri)
76
+ headers.each { |k, v| request[k] = v }
77
+ request.body = body if body
78
+ request
79
+ end
80
+
81
+ def build_headers(headers_config, context)
82
+ headers_config.each_with_object({}) do |(key, value), hash|
83
+ hash[key.to_s] = interpolate(value.to_s, context)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,103 @@
1
+ module Orkestr
2
+ module Nodes
3
+ class HumanAction < Base
4
+ node_id "human_action"
5
+ label "Human Action"
6
+ category "human"
7
+
8
+ # Assignees: array of { type, id } objects.
9
+ # Resolved by priority: input > context > config
10
+ config_schema do
11
+ string :description, required: false, description: "Description of the task"
12
+ array :assignees, required: false, description: "Array of {type, id} objects" do
13
+ object do
14
+ string :type, description: "Polymorphic type (e.g. User, Group)"
15
+ string :id, description: "Record ID"
16
+ end
17
+ end
18
+ string :assignee_key, required: false, description: "Role key for assignees (e.g. reviewer, approver)"
19
+ string :deadline, required: false, description: "ISO 8601 deadline for the task"
20
+ end
21
+
22
+ def execute(context, input)
23
+ human_task = create_human_task
24
+ create_assignees(human_task, context, input)
25
+ node_execution.update!(status: "waiting")
26
+ {}
27
+ end
28
+
29
+ private
30
+
31
+ def create_human_task
32
+ form_schema = node_execution.node.config_json.dig("form_schema") || {}
33
+ Orkestr::HumanTask.create!(
34
+ node_execution: node_execution,
35
+ schema_json: form_schema,
36
+ status: "pending"
37
+ )
38
+ end
39
+
40
+ def create_assignees(human_task, context, input)
41
+ assignees = resolve_assignees(context, input)
42
+ assignees = exclude_already_responded(assignees)
43
+ return if assignees.empty?
44
+
45
+ deadline = resolve_deadline(context, input)
46
+
47
+ assignees.each do |assignee|
48
+ human_task.assignees.create!(
49
+ assignable_type: assignee[:type],
50
+ assignable_id: assignee[:id].to_s,
51
+ key: config[:assignee_key],
52
+ deadline: deadline
53
+ )
54
+ end
55
+ end
56
+
57
+ # Priority: input > context > config
58
+ def resolve_assignees(context, input)
59
+ raw = (input || {}).deep_symbolize_keys[:assignees] ||
60
+ (context || {}).deep_symbolize_keys[:assignees] ||
61
+ config[:assignees]
62
+ return [] if raw.blank?
63
+
64
+ Array(raw).filter_map do |entry|
65
+ entry = entry.deep_symbolize_keys if entry.respond_to?(:deep_symbolize_keys)
66
+ next unless entry[:type].present? && entry[:id].present?
67
+
68
+ { type: entry[:type].to_s, id: entry[:id].to_s }
69
+ end
70
+ end
71
+
72
+ # In cycle workflows, exclude assignees who already completed a task
73
+ # for this same node in this execution.
74
+ def exclude_already_responded(assignees)
75
+ previous = execution.node_executions
76
+ .where(node_id: node_execution.node_id)
77
+ .where.not(id: node_execution.id)
78
+ .where(status: "completed")
79
+
80
+ return assignees unless previous.exists?
81
+
82
+ responded_ids = Orkestr::Assignee
83
+ .where(status: "completed")
84
+ .joins(:human_task)
85
+ .where(orkestr_human_tasks: { node_execution_id: previous.select(:id) })
86
+ .pluck(:assignable_id)
87
+ .map(&:to_s)
88
+ .to_set
89
+
90
+ assignees.reject { |a| responded_ids.include?(a[:id].to_s) }
91
+ end
92
+
93
+ def resolve_deadline(context, input)
94
+ raw = (input || {}).deep_symbolize_keys[:deadline] ||
95
+ (context || {}).deep_symbolize_keys[:deadline] ||
96
+ config[:deadline]
97
+ raw.present? ? Time.zone.parse(raw.to_s) : nil
98
+ rescue ArgumentError
99
+ nil
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,48 @@
1
+ module Orkestr
2
+ module Nodes
3
+ class Transform < Base
4
+ node_id "transform"
5
+ label "Transform"
6
+ category "data"
7
+
8
+ config_schema do
9
+ array :mappings do
10
+ object do
11
+ string :source, description: "Source path (dot notation) or template with {{variable}}"
12
+ string :target, description: "Target key name"
13
+ end
14
+ end
15
+ end
16
+
17
+ input_schema do
18
+ object :context, description: "Workflow context" do
19
+ # dynamic keys
20
+ end
21
+ end
22
+
23
+ def execute(context, _input)
24
+ mappings = config[:mappings] || []
25
+ result = {}
26
+
27
+ mappings.each do |mapping|
28
+ source = mapping[:source].to_s
29
+ target = mapping[:target].to_s
30
+
31
+ result[target] = if template?(source)
32
+ interpolate(source, context)
33
+ else
34
+ dig_value(context, source)
35
+ end
36
+ end
37
+
38
+ result
39
+ end
40
+
41
+ private
42
+
43
+ def template?(source)
44
+ source.include?("{{") && source.include?("}}")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ module Orkestr
2
+ module Nodes
3
+ class Wait < Base
4
+ node_id "wait"
5
+ label "Wait"
6
+ category "flow"
7
+
8
+ config_schema do
9
+ string :description, required: false, description: "Description of what is being waited for"
10
+ end
11
+
12
+ def execute(_context, _input)
13
+ node_execution.update!(status: "waiting")
14
+ {}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Orkestr
2
+ module Triggers
3
+ class Manual < Base
4
+ trigger_id "manual"
5
+ label "Manual Trigger"
6
+
7
+ def fire(context = {})
8
+ start_execution(context)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require "fugit"
2
+
3
+ module Orkestr
4
+ module Triggers
5
+ class Scheduled < Base
6
+ trigger_id "scheduled"
7
+ label "Scheduled Trigger"
8
+
9
+ config_schema do
10
+ string :cron, description: "Cron expression (e.g. '0 * * * *')"
11
+ end
12
+
13
+ def fire(context = {})
14
+ start_execution(context)
15
+ end
16
+
17
+ def should_fire?(time)
18
+ cron = Fugit::Cron.parse(trigger_config[:cron])
19
+ return false unless cron
20
+
21
+ truncated = Time.at(time.to_i - (time.to_i % 60)).utc
22
+ cron.match?(truncated)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module Orkestr
2
+ module Triggers
3
+ class Webhook < Base
4
+ trigger_id "webhook"
5
+ label "Webhook Trigger"
6
+
7
+ config_schema do
8
+ string :secret, required: false, description: "Shared secret for signature verification"
9
+ end
10
+
11
+ def fire(context = {})
12
+ start_execution(context, async: true)
13
+ end
14
+
15
+ def valid_secret?(provided_secret)
16
+ expected = trigger_config[:secret]
17
+ return true if expected.blank?
18
+
19
+ ActiveSupport::SecurityUtils.secure_compare(expected.to_s, provided_secret.to_s)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_assignees
4
+ #
5
+ # id :uuid not null, primary key
6
+ # assignable_type :string not null
7
+ # deadline :datetime
8
+ # key :string
9
+ # status :string default("pending"), not null
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ # assignable_id :string not null
13
+ # human_task_id :uuid not null
14
+ #
15
+ # Indexes
16
+ #
17
+ # index_orkestr_assignees_on_assignable_type_and_assignable_id (assignable_type,assignable_id)
18
+ # index_orkestr_assignees_on_human_task_id (human_task_id)
19
+ #
20
+ # Foreign Keys
21
+ #
22
+ # fk_rails_... (human_task_id => orkestr_human_tasks.id)
23
+ #
24
+ module Orkestr
25
+ class AssigneeSerializer
26
+ include Alba::Resource
27
+
28
+ attributes :id, :assignable_type, :assignable_id, :key, :status, :deadline, :created_at
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_contexts
4
+ #
5
+ # id :uuid not null, primary key
6
+ # contextualizable_type :string
7
+ # data :jsonb not null
8
+ # status :string default("pending"), not null
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # contextualizable_id :uuid
12
+ # workflow_id :uuid
13
+ #
14
+ # Indexes
15
+ #
16
+ # index_orkestr_contexts_on_contextualizable (contextualizable_type,contextualizable_id)
17
+ # index_orkestr_contexts_on_workflow_id (workflow_id)
18
+ #
19
+ # Foreign Keys
20
+ #
21
+ # fk_rails_... (workflow_id => orkestr_workflows.id)
22
+ #
23
+ module Orkestr
24
+ class ContextSerializer
25
+ include Alba::Resource
26
+
27
+ attributes :id, :contextualizable_type, :contextualizable_id,
28
+ :workflow_id, :status, :data, :created_at, :updated_at
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_edges
4
+ #
5
+ # id :uuid not null, primary key
6
+ # source_handle :string
7
+ # created_at :datetime not null
8
+ # updated_at :datetime not null
9
+ # react_flow_id :string
10
+ # source_node_id :uuid not null
11
+ # target_node_id :uuid not null
12
+ # workflow_id :uuid not null
13
+ #
14
+ # Indexes
15
+ #
16
+ # index_orkestr_edges_on_react_flow_id (react_flow_id)
17
+ # index_orkestr_edges_on_source_node_id (source_node_id)
18
+ # index_orkestr_edges_on_target_node_id (target_node_id)
19
+ # index_orkestr_edges_on_workflow_id (workflow_id)
20
+ #
21
+ # Foreign Keys
22
+ #
23
+ # fk_rails_... (source_node_id => orkestr_nodes.id)
24
+ # fk_rails_... (target_node_id => orkestr_nodes.id)
25
+ # fk_rails_... (workflow_id => orkestr_workflows.id)
26
+ #
27
+ module Orkestr
28
+ class EdgeSerializer
29
+ include Alba::Resource
30
+
31
+ attributes :id, :source_node_id, :target_node_id, :source_handle, :react_flow_id, :created_at, :updated_at
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module Orkestr
2
+ class ExecutionCollectionSerializer
3
+ include Alba::Resource
4
+
5
+ attributes :id, :workflow_id, :status, :started_at, :finished_at, :created_at, :updated_at
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_execution_logs
4
+ #
5
+ # id :uuid not null, primary key
6
+ # level :string default("info"), not null
7
+ # message :text not null
8
+ # metadata :jsonb not null
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # execution_id :uuid not null
12
+ # node_execution_id :uuid
13
+ #
14
+ # Indexes
15
+ #
16
+ # index_orkestr_execution_logs_on_execution_id (execution_id)
17
+ # index_orkestr_execution_logs_on_node_execution_id (node_execution_id)
18
+ #
19
+ # Foreign Keys
20
+ #
21
+ # fk_rails_... (execution_id => orkestr_executions.id)
22
+ # fk_rails_... (node_execution_id => orkestr_node_executions.id)
23
+ #
24
+ module Orkestr
25
+ class ExecutionLogSerializer
26
+ include Alba::Resource
27
+
28
+ attributes :id, :execution_id, :node_execution_id,
29
+ :level, :message, :metadata, :created_at
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_executions
4
+ #
5
+ # id :uuid not null, primary key
6
+ # finished_at :datetime
7
+ # started_at :datetime
8
+ # status :string default("pending"), not null
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # context_id :uuid
12
+ # workflow_id :uuid not null
13
+ #
14
+ # Indexes
15
+ #
16
+ # index_orkestr_executions_on_context_id (context_id)
17
+ # index_orkestr_executions_on_workflow_id (workflow_id)
18
+ #
19
+ # Foreign Keys
20
+ #
21
+ # fk_rails_... (context_id => orkestr_contexts.id)
22
+ # fk_rails_... (workflow_id => orkestr_workflows.id)
23
+ #
24
+ module Orkestr
25
+ class ExecutionSerializer
26
+ include Alba::Resource
27
+
28
+ attributes :id, :workflow_id, :status, :context_id,
29
+ :started_at, :finished_at, :created_at, :updated_at
30
+
31
+ attribute :context_data do |execution|
32
+ execution.context&.data || {}
33
+ end
34
+
35
+ many :node_executions, resource: NodeExecutionSerializer
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_human_tasks
4
+ #
5
+ # id :uuid not null, primary key
6
+ # completed_at :datetime
7
+ # response_json :json
8
+ # schema_json :json not null
9
+ # status :string default("pending"), not null
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ # execution_id :uuid not null
13
+ # node_execution_id :uuid not null
14
+ # node_id :uuid not null
15
+ #
16
+ # Indexes
17
+ #
18
+ # index_orkestr_human_tasks_on_execution_id (execution_id)
19
+ # index_orkestr_human_tasks_on_node_execution_id (node_execution_id)
20
+ # index_orkestr_human_tasks_on_node_id (node_id)
21
+ #
22
+ # Foreign Keys
23
+ #
24
+ # fk_rails_... (execution_id => orkestr_executions.id)
25
+ # fk_rails_... (node_execution_id => orkestr_node_executions.id)
26
+ # fk_rails_... (node_id => orkestr_nodes.id)
27
+ #
28
+ module Orkestr
29
+ class HumanTaskSerializer
30
+ include Alba::Resource
31
+
32
+ attributes :id, :execution_id, :node_execution_id, :node_id,
33
+ :status, :schema_json, :response_json,
34
+ :completed_at, :created_at, :updated_at
35
+
36
+ attribute :description do |task|
37
+ task.node&.config_json&.dig("description")
38
+ end
39
+
40
+ attribute :actions do |task|
41
+ task.node&.config_json&.dig("actions")
42
+ end
43
+
44
+ many :assignees, resource: AssigneeSerializer
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_node_executions
4
+ #
5
+ # id :uuid not null, primary key
6
+ # attempt :integer default(1), not null
7
+ # finished_at :datetime
8
+ # input_json :json not null
9
+ # output_json :json not null
10
+ # started_at :datetime
11
+ # status :string default("pending"), not null
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ # execution_id :uuid not null
15
+ # node_id :uuid not null
16
+ #
17
+ # Indexes
18
+ #
19
+ # index_orkestr_node_executions_on_execution_id (execution_id)
20
+ # index_orkestr_node_executions_on_node_id (node_id)
21
+ # index_orkestr_node_executions_unique_active (execution_id,node_id) UNIQUE WHERE ((status)::text = ANY (ARRAY[('pending'::character varying)::text, ('running'::character varying)::text, ('waiting'::character varying)::text]))
22
+ #
23
+ # Foreign Keys
24
+ #
25
+ # fk_rails_... (execution_id => orkestr_executions.id)
26
+ # fk_rails_... (node_id => orkestr_nodes.id)
27
+ #
28
+ module Orkestr
29
+ class NodeExecutionSerializer
30
+ include Alba::Resource
31
+
32
+ attributes :id, :node_id, :status, :input_json, :output_json, :attempt,
33
+ :started_at, :finished_at, :created_at, :updated_at
34
+
35
+ attribute :node_type do |ne|
36
+ ne.node.node_type
37
+ end
38
+
39
+ attribute :duration do |ne|
40
+ ne.duration
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_nodes
4
+ #
5
+ # id :uuid not null, primary key
6
+ # conditions_json :json not null
7
+ # config_json :json not null
8
+ # node_type :string not null
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # react_flow_id :string
12
+ # workflow_id :uuid not null
13
+ #
14
+ # Indexes
15
+ #
16
+ # index_orkestr_nodes_on_react_flow_id (react_flow_id)
17
+ # index_orkestr_nodes_on_workflow_id (workflow_id)
18
+ #
19
+ # Foreign Keys
20
+ #
21
+ # fk_rails_... (workflow_id => orkestr_workflows.id)
22
+ #
23
+ module Orkestr
24
+ class NodeSerializer
25
+ include Alba::Resource
26
+
27
+ attributes :id, :node_type, :react_flow_id, :config_json, :conditions_json, :created_at, :updated_at
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Orkestr
2
+ class WorkflowCollectionSerializer
3
+ include Alba::Resource
4
+
5
+ attributes :id, :name, :trigger_type, :created_at, :updated_at
6
+
7
+ attribute :executions_count do |workflow|
8
+ workflow.executions.size
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: orkestr_workflows
4
+ #
5
+ # id :uuid not null, primary key
6
+ # default_context :jsonb not null
7
+ # graph_json :json not null
8
+ # name :string not null
9
+ # reuse_context :boolean default(FALSE), not null
10
+ # status :string default("draft"), not null
11
+ # trigger_config :json not null
12
+ # trigger_type :string
13
+ # created_at :datetime not null
14
+ # updated_at :datetime not null
15
+ #
16
+ module Orkestr
17
+ class WorkflowSerializer
18
+ include Alba::Resource
19
+
20
+ attributes :id, :name, :status, :trigger_type, :trigger_config, :graph_json,
21
+ :default_context, :reuse_context, :created_at, :updated_at
22
+
23
+ many :nodes, resource: NodeSerializer
24
+ many :edges, resource: EdgeSerializer
25
+
26
+ attribute :executions_count do |workflow|
27
+ workflow.executions.size
28
+ end
29
+ end
30
+ end