bpmn 0.0.1 → 0.0.3
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 +4 -4
- data/README.md +6 -6
- data/lib/{spot_flow → bpmn}/context.rb +5 -5
- data/lib/bpmn/definitions.rb +53 -0
- data/lib/bpmn/element.rb +42 -0
- data/lib/bpmn/event.rb +193 -0
- data/lib/bpmn/event_definition.rb +133 -0
- data/lib/{spot_flow → bpmn}/execution.rb +8 -8
- data/lib/bpmn/extension_elements.rb +27 -0
- data/lib/bpmn/extensions.rb +75 -0
- data/lib/bpmn/flow.rb +45 -0
- data/lib/bpmn/gateway.rb +83 -0
- data/lib/bpmn/process.rb +177 -0
- data/lib/bpmn/step.rb +56 -0
- data/lib/bpmn/task.rb +126 -0
- data/lib/bpmn/version.rb +5 -0
- data/lib/{spot_flow.rb → bpmn.rb} +19 -8
- metadata +32 -22
- data/lib/spot_flow/bpmn/definitions.rb +0 -55
- data/lib/spot_flow/bpmn/element.rb +0 -44
- data/lib/spot_flow/bpmn/event.rb +0 -195
- data/lib/spot_flow/bpmn/event_definition.rb +0 -135
- data/lib/spot_flow/bpmn/extension_elements.rb +0 -29
- data/lib/spot_flow/bpmn/extensions.rb +0 -77
- data/lib/spot_flow/bpmn/flow.rb +0 -47
- data/lib/spot_flow/bpmn/gateway.rb +0 -85
- data/lib/spot_flow/bpmn/process.rb +0 -179
- data/lib/spot_flow/bpmn/step.rb +0 -58
- data/lib/spot_flow/bpmn/task.rb +0 -128
- data/lib/spot_flow/bpmn.rb +0 -18
- data/lib/spot_flow/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 833e8fd13dc9a05339b2b627ae312cdd9e48718263bbd3ebf07996eab83bcdc0
|
4
|
+
data.tar.gz: 32bf5785f28bd1807aa636da7efac8fa8e8cd77ccd8eea5fd7ec53cfa8444ade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42252aa851d8ce634c7a606e5f7fe4443934109171f645f409110275444791a587c283946fe7e42887b5d4966d2151a77471f2a22feadbc8da3f5026c9a7d29c
|
7
|
+
data.tar.gz: e8968a2da91a88bab1ee5c554ca0960716af06a5ecdb79d98b145a08a6fe744494d4dc5e5dc146679e9f0892ba5832db9bbfec6cbddadca58211085998115921
|
data/README.md
CHANGED
@@ -15,14 +15,14 @@ Processes are made of a series of tasks. Tasks can be manual (require `signal` t
|
|
15
15
|
- BusinessRuleTask (automated): evaluates the decision_id (expects dmn source to be included in the context).
|
16
16
|
- ScriptTask (automated): evaluates the FEEL expression in the script property.
|
17
17
|
|
18
|
-
To start the process, initialize
|
18
|
+
To start the process, initialize with the BPMN and DMN source files, then call `start`.
|
19
19
|
|
20
20
|
```ruby
|
21
21
|
sources = [
|
22
22
|
File.read("hello_world.bpmn"),
|
23
23
|
File.read("choose_greeting.dmn")
|
24
24
|
]
|
25
|
-
execution =
|
25
|
+
execution = BPMN.new(sources).start
|
26
26
|
```
|
27
27
|
|
28
28
|
It's often useful to print the process state to the console.
|
@@ -39,7 +39,7 @@ HelloWorld started * Flow_080794y
|
|
39
39
|
2 BoundaryEvent EggTimer: waiting
|
40
40
|
```
|
41
41
|
|
42
|
-
The HelloWorld process began at the Start event and is _waiting_ at the IntroduceYourself user task. This is an important concept in the
|
42
|
+
The HelloWorld process began at the Start event and is _waiting_ at the IntroduceYourself user task. This is an important concept in the BPMN engine. It's designed to be used in a Rails application where a process might be waiting for a user to complete a form, or a background job to complete. It's common to save the state the process until a task is complete. Calling `serialize` on a process will return the execution state so it can be continued later.
|
43
43
|
|
44
44
|
```ruby
|
45
45
|
# Returns a hash of the process state.
|
@@ -49,7 +49,7 @@ execution_state = execution.serialize
|
|
49
49
|
# or a background job completes (ServiceTask)
|
50
50
|
|
51
51
|
# Restores the process from the execution state.
|
52
|
-
execution =
|
52
|
+
execution = BPMN.restore(sources, execution_state:)
|
53
53
|
|
54
54
|
# Now we can continue the process by `signaling` the waiting task.
|
55
55
|
step = execution.step_by_element_id("IntroduceYourself")
|
@@ -162,13 +162,13 @@ HelloWorld completed *
|
|
162
162
|
Execute:
|
163
163
|
|
164
164
|
```bash
|
165
|
-
$ bundle add
|
165
|
+
$ bundle add bpmn
|
166
166
|
```
|
167
167
|
|
168
168
|
Or install it directly:
|
169
169
|
|
170
170
|
```bash
|
171
|
-
$ gem install
|
171
|
+
$ gem install bpmn
|
172
172
|
```
|
173
173
|
|
174
174
|
## Development
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module BPMN
|
4
4
|
class Context
|
5
5
|
attr_reader :sources, :processes, :decisions, :services, :executions
|
6
6
|
attr_reader :bpmn_definitions, :dmn_definitions
|
@@ -11,15 +11,15 @@ module SpotFlow
|
|
11
11
|
@bpmn_definitions = []
|
12
12
|
@dmn_definitions = []
|
13
13
|
@decisions = Array.wrap(decisions)
|
14
|
-
@services = HashWithIndifferentAccess.new((
|
14
|
+
@services = HashWithIndifferentAccess.new((BPMN.config.services || {}).merge(services))
|
15
15
|
|
16
16
|
@sources.each do |source|
|
17
17
|
if source.include?("http://www.omg.org/spec/DMN/20180521/DC/")
|
18
|
-
definitions =
|
18
|
+
definitions = DMN.definitions_from_xml(source)
|
19
19
|
@dmn_definitions << definitions
|
20
20
|
@decisions += definitions.decisions
|
21
21
|
else
|
22
|
-
@processes +=
|
22
|
+
@processes += BPMN.processes_from_xml(source)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -55,7 +55,7 @@ module SpotFlow
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def notify_listener(*args)
|
58
|
-
|
58
|
+
BPMN.config.listener&.call(args)
|
59
59
|
end
|
60
60
|
|
61
61
|
def default_process
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Definitions
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
attr_accessor :id, :name, :target_namespace, :exporter, :exporter_version, :execution_platform, :execution_platform_version
|
8
|
+
attr_reader :messages, :signals, :errors, :processes
|
9
|
+
|
10
|
+
def self.from_xml(xml)
|
11
|
+
XmlHasher.configure do |config|
|
12
|
+
config.snakecase = true
|
13
|
+
config.ignore_namespaces = true
|
14
|
+
config.string_keys = false
|
15
|
+
end
|
16
|
+
hash = XmlHasher.parse(xml)
|
17
|
+
Definitions.new(hash[:definitions].except(:bpmn_diagram)).tap do |definitions|
|
18
|
+
definitions.processes.each do |process|
|
19
|
+
process.wire_references(definitions)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(attributes={})
|
25
|
+
super(attributes.except(:message, :signal, :error, :process))
|
26
|
+
|
27
|
+
@messages = Array.wrap(attributes[:message]).map { |atts| Message.new(atts) }
|
28
|
+
@signals = Array.wrap(attributes[:signal]).map { |atts| Signal.new(atts) }
|
29
|
+
@errors = Array.wrap(attributes[:error]).map { |atts| Error.new(atts) }
|
30
|
+
@processes = Array.wrap(attributes[:process]).map { |atts| Process.new(atts) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def message_by_id(id)
|
34
|
+
messages.find { |message| message.id == id }
|
35
|
+
end
|
36
|
+
|
37
|
+
def signal_by_id(id)
|
38
|
+
signals.find { |signal| signal.id == id }
|
39
|
+
end
|
40
|
+
|
41
|
+
def error_by_id(id)
|
42
|
+
errors.find { |error| error.id == id }
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_by_id(id)
|
46
|
+
processes.find { |process| process.id == id }
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect
|
50
|
+
"#<BPMN::Definitions @id=#{id.inspect} @name=#{name.inspect} @messages=#{messages.inspect} @signals=#{signals.inspect} @errors=#{errors.inspect} @processes=#{processes.inspect}>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/bpmn/element.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Element
|
5
|
+
include ActiveModel::Model
|
6
|
+
|
7
|
+
attr_accessor :id, :name, :extension_elements
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
super(attributes.slice(:id, :name))
|
11
|
+
|
12
|
+
@extension_elements = ExtensionElements.new(attributes[:extension_elements]) if attributes[:extension_elements].present?
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"#<#{self.class.name.gsub(/BPMN::/, "")} @id=#{id.inspect} @name=#{name.inspect}>"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Message < Element
|
21
|
+
end
|
22
|
+
|
23
|
+
class Signal < Element
|
24
|
+
end
|
25
|
+
|
26
|
+
class Error < Element
|
27
|
+
end
|
28
|
+
|
29
|
+
class Collaboration < Element
|
30
|
+
end
|
31
|
+
|
32
|
+
class LaneSet < Element
|
33
|
+
end
|
34
|
+
|
35
|
+
class Participant < Element
|
36
|
+
attr_accessor :process_ref, :process
|
37
|
+
|
38
|
+
def initialize(attributes = {})
|
39
|
+
super(attributes.except(:process_ref))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/bpmn/event.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Event < Step
|
5
|
+
attr_accessor :event_definitions
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
super(attributes.except(:message_event_definition, :signal_event_definition, :error_event_definition, :terminate_event_definition, :timer_event_definition))
|
9
|
+
|
10
|
+
@event_definitions = []
|
11
|
+
|
12
|
+
Array.wrap(attributes[:message_event_definition]).each do |med|
|
13
|
+
@event_definitions.push MessageEventDefinition.new(med)
|
14
|
+
end if attributes[:message_event_definition].present?
|
15
|
+
|
16
|
+
Array.wrap(attributes[:signal_event_definition]).each do |sed|
|
17
|
+
@event_definitions.push SignalEventDefinition.new(sed)
|
18
|
+
end if attributes[:signal_event_definition].present?
|
19
|
+
|
20
|
+
Array.wrap(attributes[:error_event_definition]).each do |eed|
|
21
|
+
@event_definitions.push ErrorEventDefinition.new(eed)
|
22
|
+
end if attributes[:error_event_definition].present?
|
23
|
+
|
24
|
+
Array.wrap(attributes[:terminate_event_definition]).each do |ted|
|
25
|
+
@event_definitions.push TerminateEventDefinition.new(ted)
|
26
|
+
end if attributes[:terminate_event_definition].present?
|
27
|
+
|
28
|
+
Array.wrap(attributes[:timer_event_definition]).each do |ted|
|
29
|
+
@event_definitions.push TimerEventDefinition.new(ted)
|
30
|
+
end if attributes[:timer_event_definition].present?
|
31
|
+
end
|
32
|
+
|
33
|
+
def event_definition_ids
|
34
|
+
event_definitions.map(&:id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_catching?
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_throwing?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def is_none?
|
46
|
+
event_definitions.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def is_conditional?
|
50
|
+
conditional_event_definition.present?
|
51
|
+
end
|
52
|
+
|
53
|
+
def is_escalation?
|
54
|
+
escalation_event_definition.present?
|
55
|
+
end
|
56
|
+
|
57
|
+
def is_error?
|
58
|
+
error_event_definition.present?
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_message?
|
62
|
+
!message_event_definitions.empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_signal?
|
66
|
+
!signal_event_definitions.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
def is_terminate?
|
70
|
+
terminate_event_definition.present?
|
71
|
+
end
|
72
|
+
|
73
|
+
def is_timer?
|
74
|
+
timer_event_definition.present?
|
75
|
+
end
|
76
|
+
|
77
|
+
def conditional_event_definition
|
78
|
+
event_definitions.find { |ed| ed.is_a?(ConditionalEventDefinition) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def escalation_event_definition
|
82
|
+
event_definitions.find { |ed| ed.is_a?(EscalationEventDefinition) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def error_event_definitions
|
86
|
+
event_definitions.select { |ed| ed.is_a?(ErrorEventDefinition) }
|
87
|
+
end
|
88
|
+
|
89
|
+
def error_event_definition
|
90
|
+
event_definitions.find { |ed| ed.is_a?(ErrorEventDefinition) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def message_event_definitions
|
94
|
+
event_definitions.select { |ed| ed.is_a?(MessageEventDefinition) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def signal_event_definitions
|
98
|
+
event_definitions.select { |ed| ed.is_a?(SignalEventDefinition) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def terminate_event_definition
|
102
|
+
event_definitions.find { |ed| ed.is_a?(TerminateEventDefinition) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def timer_event_definition
|
106
|
+
event_definitions.find { |ed| ed.is_a?(TimerEventDefinition) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def execute(execution)
|
110
|
+
event_definitions.each { |event_definition| event_definition.execute(execution) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class StartEvent < Event
|
115
|
+
|
116
|
+
def is_catching?
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def execute(execution)
|
121
|
+
super
|
122
|
+
leave(execution)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class IntermediateThrowEvent < Event
|
127
|
+
|
128
|
+
def is_throwing?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def execute(execution)
|
133
|
+
super
|
134
|
+
leave(execution)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class IntermediateCatchEvent < Event
|
139
|
+
|
140
|
+
def is_catching?
|
141
|
+
true
|
142
|
+
end
|
143
|
+
|
144
|
+
def execute(execution)
|
145
|
+
super
|
146
|
+
execution.wait
|
147
|
+
end
|
148
|
+
|
149
|
+
def signal(execution)
|
150
|
+
leave(execution)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class BoundaryEvent < Event
|
155
|
+
attr_accessor :attached_to_ref, :attached_to, :cancel_activity
|
156
|
+
|
157
|
+
def initialize(attributes = {})
|
158
|
+
super(attributes.except(:attached_to_ref, :cancel_activity))
|
159
|
+
|
160
|
+
@attached_to_ref = attributes[:attached_to_ref]
|
161
|
+
@cancel_activity = true
|
162
|
+
if attributes[:cancel_activity].present?
|
163
|
+
@cancel_activity = attributes[:cancel_activity] == "false" ? false : true
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def is_catching?
|
168
|
+
true
|
169
|
+
end
|
170
|
+
|
171
|
+
def execute(execution)
|
172
|
+
super
|
173
|
+
execution.wait
|
174
|
+
end
|
175
|
+
|
176
|
+
def signal(execution)
|
177
|
+
execution.attached_to.terminate if cancel_activity
|
178
|
+
leave(execution)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class EndEvent < Event
|
183
|
+
|
184
|
+
def is_throwing?
|
185
|
+
true
|
186
|
+
end
|
187
|
+
|
188
|
+
def execute(execution)
|
189
|
+
super
|
190
|
+
execution.end(true)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class EventDefinition < Element
|
5
|
+
def execute(execution)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class ConditionalEventDefinition < EventDefinition
|
10
|
+
attr_accessor :variable_name, :variable_events, :condition
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
super(attributes.except(:variable_name, :variable_events, :condition))
|
14
|
+
|
15
|
+
@variable_name = moddle[:variable_name] # "var1"
|
16
|
+
@variable_events = moddle[:variable_events] # "create, update"
|
17
|
+
@condition = moddle[:condition] # var1 = 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class EscalationEventDefinition < EventDefinition
|
22
|
+
end
|
23
|
+
|
24
|
+
class ErrorEventDefinition < EventDefinition
|
25
|
+
attr_accessor :error_ref, :error
|
26
|
+
attr_accessor :error_code_variable, :error_message_variable
|
27
|
+
|
28
|
+
def initialize(attributes = {})
|
29
|
+
super(attributes.except(:error_ref, :error_code_variable, :error_message_variable))
|
30
|
+
|
31
|
+
@error_ref = attributes[:error_ref]
|
32
|
+
@error_code_variable = attributes[:error_code_variable]
|
33
|
+
@error_message_variable = attributes[:error_message_variable]
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute(execution)
|
37
|
+
if execution.step.is_throwing?
|
38
|
+
execution.throw_error(error_name)
|
39
|
+
else
|
40
|
+
execution.error_names.push error_name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def error_id
|
45
|
+
error&.id
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_name
|
49
|
+
error&.name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class MessageEventDefinition < EventDefinition
|
54
|
+
attr_accessor :message_ref, :message
|
55
|
+
|
56
|
+
def initialize(attributes = {})
|
57
|
+
super(attributes.except(:message_ref))
|
58
|
+
|
59
|
+
@message_ref = attributes[:message_ref]
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute(execution)
|
63
|
+
if execution.step.is_throwing?
|
64
|
+
execution.throw_message(message_name)
|
65
|
+
else
|
66
|
+
execution.message_names.push message_name
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def message_id
|
71
|
+
message&.id
|
72
|
+
end
|
73
|
+
|
74
|
+
def message_name
|
75
|
+
message&.name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class SignalEventDefinition < EventDefinition
|
80
|
+
attr_accessor :signal_ref, :signal
|
81
|
+
|
82
|
+
def initialize(attributes = {})
|
83
|
+
super(attributes.except(:signal_ref))
|
84
|
+
|
85
|
+
@signal_ref = moddle[:signal_ref]
|
86
|
+
end
|
87
|
+
|
88
|
+
def signal_id
|
89
|
+
signal&.id
|
90
|
+
end
|
91
|
+
|
92
|
+
def signal_name
|
93
|
+
signal&.name
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class TerminateEventDefinition < EventDefinition
|
98
|
+
|
99
|
+
def execute(execution)
|
100
|
+
execution.parent&.terminate
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class TimerEventDefinition < EventDefinition
|
105
|
+
attr_accessor :time_date, :time_duration_type, :time_duration, :time_cycle
|
106
|
+
|
107
|
+
def initialize(attributes = {})
|
108
|
+
super(attributes.except(:time_date, :time_duration, :time_cycle))
|
109
|
+
|
110
|
+
@time_duration_type = attributes[:time_duration_type]
|
111
|
+
@time_duration = attributes[:time_duration]
|
112
|
+
end
|
113
|
+
|
114
|
+
def execute(execution)
|
115
|
+
if execution.step.is_catching?
|
116
|
+
execution.timer_expires_at = time_due
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def time_due
|
123
|
+
# Return the next time the timer is due
|
124
|
+
if time_date
|
125
|
+
return Date.parse(time_date)
|
126
|
+
elsif time_duration
|
127
|
+
return Time.zone.now + ActiveSupport::Duration.parse(time_duration)
|
128
|
+
else
|
129
|
+
return Time.zone.now # time_cycle not yet implemented
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module BPMN
|
4
4
|
class Execution
|
5
5
|
attr_accessor :id, :status, :started_at, :ended_at, :variables, :tokens_in, :tokens_out, :start_event_id, :timer_expires_at, :message_names, :error_names, :condition
|
6
6
|
attr_accessor :step, :parent, :children, :context, :attached_to_id
|
@@ -29,7 +29,7 @@ module SpotFlow
|
|
29
29
|
step = step_type == "Process" ? context.process_by_id(step_id) : context.element_by_id(step_id)
|
30
30
|
child_attributes = attributes.delete("children")
|
31
31
|
Execution.new(attributes.merge(step: step, context:)).tap do |execution|
|
32
|
-
execution.children = child_attributes.map do |ca|
|
32
|
+
execution.children = child_attributes.map do |ca|
|
33
33
|
Execution.from_json(ca, context:).tap { |child| child.parent = execution }
|
34
34
|
end if child_attributes
|
35
35
|
end
|
@@ -97,7 +97,7 @@ module SpotFlow
|
|
97
97
|
@started_at = Time.zone.now
|
98
98
|
map_input_variables if step&.input_mappings&.present?
|
99
99
|
context.notify_listener(:execution_started, execution: self)
|
100
|
-
step.attachments.each { |attachment| parent.execute_step(attachment, attached_to: self) } if step.is_a?(
|
100
|
+
step.attachments.each { |attachment| parent.execute_step(attachment, attached_to: self) } if step.is_a?(BPMN::Activity)
|
101
101
|
continue
|
102
102
|
end
|
103
103
|
|
@@ -147,7 +147,7 @@ module SpotFlow
|
|
147
147
|
def throw_message(message_name, variables: {})
|
148
148
|
waiting_children.each do |child|
|
149
149
|
step = child.step
|
150
|
-
if step.is_a?(
|
150
|
+
if step.is_a?(BPMN::Event) && step.message_event_definitions.any? { |message_event_definition| message_event_definition.message_name == message_name }
|
151
151
|
child.signal(variables)
|
152
152
|
break
|
153
153
|
end
|
@@ -158,7 +158,7 @@ module SpotFlow
|
|
158
158
|
def throw_error(error_name, variables: {})
|
159
159
|
waiting_children.each do |child|
|
160
160
|
step = child.step
|
161
|
-
if step.is_a?(
|
161
|
+
if step.is_a?(BPMN::Event) && step.error_event_definitions.any? { |error_event_definition| error_event_definition.error_name == error_name }
|
162
162
|
child.signal(variables)
|
163
163
|
break
|
164
164
|
end
|
@@ -175,7 +175,7 @@ module SpotFlow
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def evaluate_expression(expression, variables: parent&.variables || {}.with_indifferent_access)
|
178
|
-
|
178
|
+
DMN.evaluate(expression.delete_prefix("="), variables:)
|
179
179
|
end
|
180
180
|
|
181
181
|
def run_automated_tasks
|
@@ -202,7 +202,7 @@ module SpotFlow
|
|
202
202
|
# Called by the child step executors when they have ended
|
203
203
|
#
|
204
204
|
def has_ended(_child)
|
205
|
-
step.leave(self) if step.is_a?(
|
205
|
+
step.leave(self) if step.is_a?(BPMN::SubProcess) || step.is_a?(BPMN::CallActivity)
|
206
206
|
self.end(true)
|
207
207
|
end
|
208
208
|
|
@@ -219,7 +219,7 @@ module SpotFlow
|
|
219
219
|
end
|
220
220
|
|
221
221
|
def waiting_tasks
|
222
|
-
waiting_children.select { |child| child.step.is_a?(
|
222
|
+
waiting_children.select { |child| child.step.is_a?(BPMN::Task) }
|
223
223
|
end
|
224
224
|
|
225
225
|
def waiting_automated_tasks
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class ExtensionElements
|
5
|
+
VALID_EXTENSION_NAMESPACES = %w[zeebe]
|
6
|
+
|
7
|
+
attr_accessor :assignment_definition, :called_element, :called_decision, :form_definition, :io_mapping, :properties, :script, :subscription, :task_definition, :task_headers, :task_schedule
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
if attributes[:properties].present?
|
11
|
+
@properties = HashWithIndifferentAccess.new
|
12
|
+
Array.wrap(attributes[:properties][:property]).each { |property_moddle| @properties[property_moddle[:name]] = property_moddle[:value] }
|
13
|
+
end
|
14
|
+
|
15
|
+
@assignment_definition = Zeebe::AssignmentDefinition.new(attributes[:assignment_definition]) if attributes[:assignment_definition].present?
|
16
|
+
@called_element = Zeebe::CalledElement.new(attributes[:called_element]) if attributes[:called_element].present?
|
17
|
+
@called_decision = Zeebe::CalledDecision.new(attributes[:called_decision]) if attributes[:called_decision].present?
|
18
|
+
@form_definition = Zeebe::FormDefinition.new(attributes[:form_definition]) if attributes[:form_definition].present?
|
19
|
+
@io_mapping = Zeebe::IoMapping.new(attributes[:io_mapping]) if attributes[:io_mapping].present?
|
20
|
+
@script = Zeebe::Script.new(attributes[:script]) if attributes[:script].present?
|
21
|
+
@subscription = Zeebe::Subscription.new(attributes[:subscription]) if attributes[:subscription].present?
|
22
|
+
@task_definition = Zeebe::TaskDefinition.new(attributes[:task_definition]) if attributes[:task_definition].present?
|
23
|
+
@task_headers = Zeebe::TaskHeaders.new(attributes[:task_headers]) if attributes[:task_headers].present?
|
24
|
+
@task_schedule = Zeebe::TaskSchedule.new(attributes[:task_schedule]) if attributes[:task_schedule].present?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BPMN
|
4
|
+
class Extension
|
5
|
+
include ActiveModel::Model
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Zeebe
|
10
|
+
class AssignmentDefinition < BPMN::Extension
|
11
|
+
attr_accessor :assignee, :candidate_groups, :candidate_users
|
12
|
+
end
|
13
|
+
|
14
|
+
class CalledElement < BPMN::Extension
|
15
|
+
attr_accessor :process_id, :propagate_all_child_variables, :propagate_all_parent_variables
|
16
|
+
|
17
|
+
def initialize(attributes = {})
|
18
|
+
super(attributes.except(:propagate_all_child_variables))
|
19
|
+
|
20
|
+
@propagate_all_parent_variables = true
|
21
|
+
@propagate_all_parent_variables = attributes[:propagate_all_parent_variables] == "true" if attributes[:propagate_all_parent_variables].present?
|
22
|
+
@propagate_all_child_variables = attributes[:propagate_all_child_variables] == "true"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class CalledDecision < BPMN::Extension
|
27
|
+
attr_accessor :decision_id, :result_variable
|
28
|
+
end
|
29
|
+
|
30
|
+
class FormDefinition < BPMN::Extension
|
31
|
+
attr_accessor :form_key
|
32
|
+
end
|
33
|
+
|
34
|
+
class IoMapping < BPMN::Extension
|
35
|
+
attr_reader :inputs, :outputs
|
36
|
+
|
37
|
+
def initialize(attributes = {})
|
38
|
+
super(attributes.except(:input, :output))
|
39
|
+
|
40
|
+
@inputs = Array.wrap(attributes[:input]).map { |atts| Parameter.new(atts) } if attributes[:input].present?
|
41
|
+
@outputs = Array.wrap(attributes[:output]).map { |atts| Parameter.new(atts) } if attributes[:output].present?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Parameter < BPMN::Extension
|
46
|
+
attr_accessor :source, :target
|
47
|
+
end
|
48
|
+
|
49
|
+
class Script < BPMN::Extension
|
50
|
+
attr_accessor :expression, :result_variable
|
51
|
+
end
|
52
|
+
|
53
|
+
class Subscription < BPMN::Extension
|
54
|
+
attr_accessor :correlation_key
|
55
|
+
end
|
56
|
+
|
57
|
+
class TaskDefinition < BPMN::Extension
|
58
|
+
attr_accessor :type, :retries
|
59
|
+
end
|
60
|
+
|
61
|
+
class TaskHeaders < BPMN::Extension
|
62
|
+
attr_accessor :headers
|
63
|
+
|
64
|
+
def initialize(attributes = {})
|
65
|
+
super(attributes.except(:header))
|
66
|
+
|
67
|
+
@headers = HashWithIndifferentAccess.new
|
68
|
+
Array.wrap(attributes[:header]).each { |header| @headers[header[:key]] = header[:value] }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class TaskSchedule < BPMN::Extension
|
73
|
+
attr_accessor :due_date, :follow_up_date
|
74
|
+
end
|
75
|
+
end
|