floe 0.14.0 → 0.15.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0e0b86c08d322e2ebc8e115e5193d0493f8257c831f7377b18c32d4284ae6f2
4
- data.tar.gz: 1bf5ed62abafe2e1af6025dbaf47d9faa3b1c93ffec8fd88fbeacd4445efc795
3
+ metadata.gz: 6f21cf2fcc6a7fa9f7536e0cca328d101a867c389f53f768e4746b6e69f643a5
4
+ data.tar.gz: f3d82401842f66504afdeaa4b051db01d5fcb9e79b704af368712d68b4950251
5
5
  SHA512:
6
- metadata.gz: 34f64555f2472c121fcea9dda8baac7fbe853cdbc5e949c831d7b7f070878d3ef2f86ed90d907b5a7033539fcad6aeea64a4dee6475a752d7f89e4f679a4b269
7
- data.tar.gz: 378c5bcd562d4bbeb7e3eb13eb55b1d5d8e7aed0ea4749c2b65f951cf3be0e5f13d3e288cdbf63b4a5729e98c5010656d6dca3db60ffbf8463988008980edd83
6
+ metadata.gz: 3e73dbdd868ec57b8138a9e4b0b529c0f2c44cdb21d43c0ae29ca9094512f4044cc621a38e4ac217bf6b17e13f1ef3a361cf5917023eab87d30c7b4cc90eccfc
7
+ data.tar.gz: e174fabde62d6348f8517f7c2d019600b34612f5b8d822eb83c2c5579f5e224596b6edaec383bfe9cce75c3260e50a4a0b744764119cd96e2a973f92b49b7d5a
data/.codeclimate.yml CHANGED
@@ -1,3 +1,4 @@
1
+ version: '2'
1
2
  prepare:
2
3
  fetch:
3
4
  - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/.rubocop_base.yml
@@ -8,9 +9,28 @@ prepare:
8
9
  path: styles/base.yml
9
10
  - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/styles/cc_base.yml
10
11
  path: styles/cc_base.yml
12
+ checks:
13
+ argument-count:
14
+ enabled: false
15
+ complex-logic:
16
+ enabled: false
17
+ file-lines:
18
+ enabled: false
19
+ method-complexity:
20
+ config:
21
+ threshold: 11
22
+ method-count:
23
+ enabled: false
24
+ method-lines:
25
+ enabled: false
26
+ nested-control-flow:
27
+ enabled: false
28
+ return-statements:
29
+ enabled: false
11
30
  plugins:
12
31
  rubocop:
13
32
  enabled: true
14
33
  config: ".rubocop_cc.yml"
15
34
  channel: rubocop-1-56-3
16
- version: '2'
35
+ exclude_patterns:
36
+ - spec/
data/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.15.1] - 2024-11-21
8
+ ### Fixed
9
+ - Fix Map/Parallel States checking container_runner#status! of finished states ([#296](https://github.com/ManageIQ/floe/pull/296))
10
+ - Fix child workflow mixin tight loop ([#297](https://github.com/ManageIQ/floe/pull/297))
11
+
12
+ ## [0.15.0] - 2024-10-28
13
+ ### Added
14
+ - Add WorkflowBase base class for Workflow ([#279](https://github.com/ManageIQ/floe/pull/279))
15
+ - Add tool for using the aws stepfunctions simulator ([#244](https://github.com/ManageIQ/floe/pull/244))
16
+ - Implement Map state ([#184](https://github.com/ManageIQ/floe/pull/184))
17
+ - Add Map State Tolerated Failure ([#282](https://github.com/ManageIQ/floe/pull/282))
18
+ - Run Map iterations in parallel up to MaxConcurrency ([#283](https://github.com/ManageIQ/floe/pull/283))
19
+ - Implement Parallel State ([#291](https://github.com/ManageIQ/floe/pull/291))
20
+
21
+ ### Changed
22
+ - More granular compare_key and determine path at initialization time ([#274](https://github.com/ManageIQ/floe/pull/274))
23
+ - For Choice validation, use instance variables and not payload ([#277](https://github.com/ManageIQ/floe/pull/277))
24
+ - Return ExceedToleratedFailureThreshold if ToleratedFailureCount/Percentage is present ([#285](https://github.com/ManageIQ/floe/pull/285))
25
+
26
+ ### Fixed
27
+ - Fix case on log messages ([#280](https://github.com/ManageIQ/floe/pull/280))
28
+ - Handle either ToleratedFailureCount or ToleratedFailurePercentage ([#284](https://github.com/ManageIQ/floe/pull/284))
29
+
7
30
  ## [0.14.0] - 2024-08-20
8
31
  ### Added
9
32
  - Implement "IsNumeric": false ([#266](https://github.com/ManageIQ/floe/pull/266))
@@ -249,7 +272,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
249
272
  ### Added
250
273
  - Initial release
251
274
 
252
- [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.14.0...HEAD
275
+ [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.15.1...HEAD
276
+ [0.15.1]: https://github.com/ManageIQ/floe/compare/v0.15.0...v0.15.1
277
+ [0.15.0]: https://github.com/ManageIQ/floe/compare/v0.14.0...v0.15.0
253
278
  [0.14.0]: https://github.com/ManageIQ/floe/compare/v0.13.1...v0.14.0
254
279
  [0.13.1]: https://github.com/ManageIQ/floe/compare/v0.13.0...v0.13.1
255
280
  [0.13.0]: https://github.com/ManageIQ/floe/compare/v0.12.0...v0.13.0
data/README.md CHANGED
@@ -197,6 +197,14 @@ Options supported by the kubernetes docker runner are:
197
197
  * `ca_file` - Path to a certificate-authority file for the kubernetes API, only valid if server and token are passed. If present `/run/secrets/kubernetes.io/serviceaccount/ca.crt` will be used
198
198
  * `verify_ssl` - Controls if the kubernetes API certificate-authority should be verified, defaults to "true", only vaild if server and token are passed
199
199
 
200
+ ## Features Not Yet Supported
201
+
202
+ The following are not yet supported:
203
+ - Map State Fields:
204
+ - ItemReader
205
+ - ItemSelector/ItemBatcher
206
+ - ResultWriter
207
+
200
208
  ## Development
201
209
 
202
210
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/examples/map.asl ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "Comment": "Using Map state in Inline mode",
3
+ "StartAt": "Pass",
4
+ "States": {
5
+ "Pass": {
6
+ "Type": "Pass",
7
+ "Next": "Map demo",
8
+ "Result": {
9
+ "foo": "bar",
10
+ "colors": [
11
+ "red",
12
+ "green",
13
+ "blue",
14
+ "yellow",
15
+ "white"
16
+ ]
17
+ }
18
+ },
19
+ "Map demo": {
20
+ "Type": "Map",
21
+ "ItemsPath": "$.colors",
22
+ "MaxConcurrency": 2,
23
+ "ItemProcessor": {
24
+ "ProcessorConfig": {
25
+ "Mode": "INLINE"
26
+ },
27
+ "StartAt": "Generate UUID",
28
+ "States": {
29
+ "Generate UUID": {
30
+ "Type": "Pass",
31
+ "Next": "Sleep",
32
+ "Parameters": {
33
+ "uuid.$": "States.UUID()"
34
+ }
35
+ },
36
+ "Sleep": {
37
+ "Type": "Task",
38
+ "Resource": "docker://docker.io/agrare/sleep:latest",
39
+ "End": true
40
+ }
41
+ }
42
+ },
43
+ "End": true
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "Comment": "Parallel Example.",
3
+ "StartAt": "FunWithMath",
4
+ "States": {
5
+ "FunWithMath": {
6
+ "Type": "Parallel",
7
+ "End": true,
8
+ "Branches": [
9
+ {
10
+ "StartAt": "Add",
11
+ "States": {
12
+ "Add": {
13
+ "Type": "Task",
14
+ "Resource": "docker://docker.io/agrare/sleep:latest",
15
+ "End": true
16
+ }
17
+ }
18
+ },
19
+ {
20
+ "StartAt": "Subtract",
21
+ "States": {
22
+ "Subtract": {
23
+ "Type": "Task",
24
+ "Resource": "docker://docker.io/agrare/sleep:latest",
25
+ "End": true
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }
32
+ }
data/lib/floe/cli.rb CHANGED
@@ -1,9 +1,12 @@
1
+ require "floe"
2
+ require "floe/container_runner"
3
+
1
4
  module Floe
2
5
  class CLI
6
+ include Logging
7
+
3
8
  def initialize
4
9
  require "optimist"
5
- require "floe"
6
- require "floe/container_runner"
7
10
  require "logger"
8
11
 
9
12
  Floe.logger = Logger.new($stdout)
@@ -13,25 +16,29 @@ module Floe
13
16
  def run(args = ARGV)
14
17
  workflows_inputs, opts = parse_options!(args)
15
18
 
16
- credentials =
17
- if opts[:credentials_given]
18
- opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
19
- elsif opts[:credentials_file_given]
20
- File.read(opts[:credentials_file])
21
- end
19
+ credentials = create_credentials(opts)
22
20
 
23
21
  workflows =
24
22
  workflows_inputs.each_slice(2).map do |workflow, input|
25
- context = Floe::Workflow::Context.new(opts[:context], :input => input, :credentials => credentials)
26
- Floe::Workflow.load(workflow, context)
23
+ create_workflow(workflow, opts[:context], input, credentials)
27
24
  end
28
25
 
29
- Floe::Workflow.wait(workflows, &:run_nonblock)
26
+ output_streams = create_loggers(workflows, opts[:segment_output])
27
+
28
+ logger.info("Checking #{workflows.count} workflows...")
29
+ ready = Floe::Workflow.wait(workflows, &:run_nonblock)
30
+ logger.info("Checking #{workflows.count} workflows...Complete - #{ready.count} ready")
30
31
 
31
32
  # Display status
32
33
  workflows.each do |workflow|
33
- puts "", "#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}", "===" if workflows.size > 1
34
- puts workflow.output
34
+ if workflows.size > 1
35
+ logger.info("")
36
+ logger.info("#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}")
37
+ logger.info("===")
38
+ end
39
+
40
+ logger.info(output_streams[workflow].string) if output_streams[workflow]
41
+ logger.info(workflow.output)
35
42
  end
36
43
 
37
44
  workflows.all? { |workflow| workflow.context.success? }
@@ -55,6 +62,7 @@ module Floe
55
62
  opt :context, "JSON payload of the Context", :type => :string
56
63
  opt :credentials, "JSON payload with Credentials", :type => :string
57
64
  opt :credentials_file, "Path to a file with Credentials", :type => :string
65
+ opt :segment_output, "Segment output by each worker", :default => false
58
66
 
59
67
  Floe::ContainerRunner.cli_options(self)
60
68
 
@@ -82,5 +90,30 @@ module Floe
82
90
 
83
91
  return workflows_inputs, opts
84
92
  end
93
+
94
+ def create_credentials(opts)
95
+ if opts[:credentials_given]
96
+ opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
97
+ elsif opts[:credentials_file_given]
98
+ File.read(opts[:credentials_file])
99
+ end
100
+ end
101
+
102
+ def create_workflow(workflow, context_payload, input, credentials)
103
+ context = Floe::Workflow::Context.new(context_payload, :input => input, :credentials => credentials)
104
+ Floe::Workflow.load(workflow, context)
105
+ end
106
+
107
+ def create_loggers(workflows, segment_output)
108
+ if workflows.size == 1 || !segment_output
109
+ # no extra work necessary
110
+ {}
111
+ else
112
+ workflows.each_with_object({}) do |workflow, h|
113
+ workflow.context.logger = Logger.new(output = StringIO.new)
114
+ h[workflow] = output
115
+ end
116
+ end
117
+ end
85
118
  end
86
119
  end
@@ -30,7 +30,7 @@ module Floe
30
30
  end
31
31
 
32
32
  begin
33
- runner_context["container_ref"] = run_container(image, env, execution_id, runner_context["secrets_ref"])
33
+ runner_context["container_ref"] = run_container(image, env, execution_id, runner_context["secrets_ref"], context.logger)
34
34
  runner_context
35
35
  rescue AwesomeSpawn::CommandResultError => err
36
36
  cleanup(runner_context)
@@ -123,7 +123,7 @@ module Floe
123
123
 
124
124
  attr_reader :network
125
125
 
126
- def run_container(image, env, execution_id, secrets_file)
126
+ def run_container(image, env, execution_id, secrets_file, logger)
127
127
  params = run_container_params(image, env, execution_id, secrets_file)
128
128
 
129
129
  logger.debug("Running #{AwesomeSpawn.build_command_line(self.class::DOCKER_COMMAND, params)}")
@@ -182,8 +182,8 @@ module Floe
182
182
 
183
183
  def inspect_container(container_id)
184
184
  JSON.parse(docker!("inspect", container_id).output).first
185
- rescue
186
- nil
185
+ rescue AwesomeSpawn::CommandResultError => err
186
+ raise Floe::ExecutionError.new("Failed to get status for container #{container_id}: #{err}")
187
187
  end
188
188
 
189
189
  def delete_container(container_id)
@@ -155,6 +155,8 @@ module Floe
155
155
 
156
156
  def pod_info(pod_name)
157
157
  kubeclient.get_pod(pod_name, namespace)
158
+ rescue Kubeclient::HttpError => err
159
+ raise Floe::ExecutionError.new("Failed to get status for pod #{namespace}/#{pod_name}: #{err}")
158
160
  end
159
161
 
160
162
  def pod_running?(context)
@@ -285,7 +287,8 @@ module Floe
285
287
  code = notice.object&.code
286
288
  reason = notice.object&.reason
287
289
 
288
- logger.warn("Received [#{code} #{reason}], [#{message}]")
290
+ # This feels like a global concern and not an end user's concern
291
+ Floe.logger.warn("Received [#{code} #{reason}], [#{message}]")
289
292
 
290
293
  true
291
294
  end
data/lib/floe/logging.rb CHANGED
@@ -7,7 +7,11 @@ module Floe
7
7
  end
8
8
 
9
9
  def logger
10
- Floe.logger
10
+ @logger || Floe.logger
11
+ end
12
+
13
+ def logger=(logger)
14
+ @logger = logger
11
15
  end
12
16
  end
13
17
  end
data/lib/floe/runner.rb CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Floe
4
4
  class Runner
5
- include Logging
6
-
7
5
  OUTPUT_MARKER = "__FLOE_OUTPUT__\n"
8
6
 
9
7
  def initialize(_options = {}) # rubocop:disable Style/RedundantInitialize
data/lib/floe/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Floe
4
- VERSION = "0.14.0"
4
+ VERSION = "0.15.1"
5
5
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class Branch < Floe::WorkflowBase
6
+ end
7
+ end
8
+ end
@@ -4,16 +4,20 @@ module Floe
4
4
  class Workflow
5
5
  class ChoiceRule
6
6
  class Data < Floe::Workflow::ChoiceRule
7
- COMPARE_KEYS = %w[IsNull IsPresent IsNumeric IsString IsBoolean IsTimestamp String Numeric Boolean Timestamp].freeze
7
+ TYPES = ["String", "Numeric", "Boolean", "Timestamp", "Present", "Null"].freeze
8
+ COMPARES = ["Equals", "LessThan", "GreaterThan", "LessThanEquals", "GreaterThanEquals", "Matches"].freeze
9
+ # e.g.: (Is)(String), (Is)(Present)
10
+ TYPE_CHECK = /^(Is)(#{TYPES.join("|")})$/.freeze
11
+ # e.g.: (String)(LessThan)(Path), (Numeric)(GreaterThanEquals)()
12
+ OPERATION = /^(#{(TYPES - %w[Null Present]).join("|")})(#{COMPARES.join("|")})(Path)?$/.freeze
8
13
 
9
- attr_reader :variable, :compare_key, :value, :path
14
+ attr_reader :variable, :compare_key, :operation, :type, :compare_predicate, :path
10
15
 
11
16
  def initialize(_workflow, _name, payload)
12
17
  super
13
18
 
14
- @variable = parse_path("Variable", payload)
19
+ @variable = parse_path("Variable")
15
20
  parse_compare_key
16
- @value = path ? parse_path(compare_key, payload) : payload[compare_key]
17
21
  end
18
22
 
19
23
  def true?(context, input)
@@ -21,39 +25,7 @@ module Floe
21
25
 
22
26
  lhs = variable_value(context, input)
23
27
  rhs = compare_value(context, input)
24
-
25
- case compare_key
26
- when "IsNull" then is_null?(lhs, rhs)
27
- when "IsNumeric" then is_numeric?(lhs, rhs)
28
- when "IsString" then is_string?(lhs, rhs)
29
- when "IsBoolean" then is_boolean?(lhs, rhs)
30
- when "IsTimestamp" then is_timestamp?(lhs, rhs)
31
- when "StringEquals", "StringEqualsPath",
32
- "NumericEquals", "NumericEqualsPath",
33
- "BooleanEquals", "BooleanEqualsPath",
34
- "TimestampEquals", "TimestampEqualsPath"
35
- lhs == rhs
36
- when "StringLessThan", "StringLessThanPath",
37
- "NumericLessThan", "NumericLessThanPath",
38
- "TimestampLessThan", "TimestampLessThanPath"
39
- lhs < rhs
40
- when "StringGreaterThan", "StringGreaterThanPath",
41
- "NumericGreaterThan", "NumericGreaterThanPath",
42
- "TimestampGreaterThan", "TimestampGreaterThanPath"
43
- lhs > rhs
44
- when "StringLessThanEquals", "StringLessThanEqualsPath",
45
- "NumericLessThanEquals", "NumericLessThanEqualsPath",
46
- "TimestampLessThanEquals", "TimestampLessThanEqualsPath"
47
- lhs <= rhs
48
- when "StringGreaterThanEquals", "StringGreaterThanEqualsPath",
49
- "NumericGreaterThanEquals", "NumericGreaterThanEqualsPath",
50
- "TimestampGreaterThanEquals", "TimestampGreaterThanEqualsPath"
51
- lhs >= rhs
52
- when "StringMatches"
53
- lhs.match?(Regexp.escape(rhs).gsub('\*', '.*?'))
54
- else
55
- raise Floe::InvalidWorkflowError, "Invalid choice [#{compare_key}]"
56
- end
28
+ send(operation, lhs, rhs)
57
29
  end
58
30
 
59
31
  private
@@ -108,26 +80,101 @@ module Floe
108
80
  # rubocop:enable Naming/PredicateName
109
81
  # rubocop:enable Style/OptionalBooleanParameter
110
82
 
83
+ def equals?(lhs, rhs)
84
+ lhs == rhs
85
+ end
86
+
87
+ def lessthan?(lhs, rhs)
88
+ lhs < rhs
89
+ end
90
+
91
+ def greaterthan?(lhs, rhs)
92
+ lhs > rhs
93
+ end
94
+
95
+ def lessthanequals?(lhs, rhs)
96
+ lhs <= rhs
97
+ end
98
+
99
+ def greaterthanequals?(lhs, rhs)
100
+ lhs >= rhs
101
+ end
102
+
103
+ def matches?(lhs, rhs)
104
+ lhs.match?(Regexp.escape(rhs).gsub('\*', '.*?'))
105
+ end
106
+
107
+ # parse the compare key at initialization time
111
108
  def parse_compare_key
112
- @compare_key = payload.keys.detect { |key| key.match?(/^(#{COMPARE_KEYS.join("|")})/) }
113
- parser_error!("requires a compare key") unless compare_key
109
+ payload.each_key do |key|
110
+ # e.g. (String)(GreaterThan)(Path)
111
+ if (match_values = OPERATION.match(key))
112
+ @compare_key = key
113
+ @type, operator, @path = match_values.captures
114
+ @operation = "#{operator.downcase}?".to_sym
115
+ @compare_predicate = parse_predicate(type)
116
+ break
117
+ end
118
+ # e.g. (Is)(String)
119
+ if (match_value = TYPE_CHECK.match(key))
120
+ @compare_key = key
121
+ _operator, type = match_value.captures
122
+ # type: nil means no runtime type checking.
123
+ @type = @path = nil
124
+ @operation = "is_#{type.downcase}?".to_sym
125
+ @compare_predicate = parse_predicate("Boolean")
126
+ break
127
+ end
128
+ end
129
+ parser_error!("requires a compare key") if compare_key.nil? || operation.nil?
130
+ end
114
131
 
115
- @path = compare_key.end_with?("Path")
132
+ # parse predicate at initilization time
133
+ # @return the right predicate attached to the compare key
134
+ def parse_predicate(data_type)
135
+ path ? parse_path(compare_key) : parse_field(compare_key, data_type)
116
136
  end
117
137
 
138
+ # @return right hand predicate - input path or static payload value)
118
139
  def compare_value(context, input)
119
- path ? value.value(context, input) : value
140
+ path ? fetch_path(compare_key, compare_predicate, context, input) : compare_predicate
120
141
  end
121
142
 
143
+ # feth the variable value at runtime
144
+ # @return variable value (left hand side )
122
145
  def variable_value(context, input)
123
- variable.value(context, input)
146
+ fetch_path("Variable", variable, context, input)
124
147
  end
125
148
 
126
- def parse_path(field_name, payload)
149
+ # parse path at initilization time
150
+ # helper method to parse a path from the payload
151
+ def parse_path(field_name)
127
152
  value = payload[field_name]
128
153
  missing_field_error!(field_name) unless value
129
154
  wrap_parser_error(field_name, value) { Path.new(value) }
130
155
  end
156
+
157
+ # parse predicate field at initialization time
158
+ def parse_field(field_name, data_type)
159
+ value = payload[field_name]
160
+ return value if correct_type?(value, data_type)
161
+
162
+ invalid_field_error!(field_name, value, "required to be a #{data_type}")
163
+ end
164
+
165
+ # fetch a path at runtime
166
+ def fetch_path(field_name, field_path, context, input)
167
+ value = field_path.value(context, input)
168
+ return value if type.nil? || correct_type?(value, type)
169
+
170
+ runtime_field_error!(field_name, field_path.to_s, "required to point to a #{type}")
171
+ end
172
+
173
+ # if we have runtime checking, check against that type
174
+ # otherwise assume checking a TYPE_CHECK predicate and check against Boolean
175
+ def correct_type?(value, data_type)
176
+ send("is_#{data_type.downcase}?".to_sym, value)
177
+ end
131
178
  end
132
179
  end
133
180
  end
@@ -3,11 +3,13 @@
3
3
  module Floe
4
4
  class Workflow
5
5
  class Context
6
+ include Logging
7
+
6
8
  attr_accessor :credentials
7
9
 
8
10
  # @param context [Json|Hash] (default, create another with input and execution params)
9
11
  # @param input [Hash] (default: {})
10
- def initialize(context = nil, input: nil, credentials: {})
12
+ def initialize(context = nil, input: nil, credentials: {}, logger: nil)
11
13
  context = JSON.parse(context) if context.kind_of?(String)
12
14
  input = JSON.parse(input || "{}")
13
15
 
@@ -20,6 +22,8 @@ module Floe
20
22
  self["Task"] ||= {}
21
23
 
22
24
  @credentials = credentials || {}
25
+
26
+ self.logger = logger if logger
23
27
  rescue JSON::ParserError => err
24
28
  raise Floe::InvalidExecutionInput, "Invalid State Machine Execution Input: #{err}: was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')"
25
29
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class ItemProcessor < Floe::WorkflowBase
6
+ attr_reader :processor_config
7
+
8
+ def initialize(payload, name = nil)
9
+ super
10
+ @processor_config = payload.fetch("ProcessorConfig", "INLINE")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,7 +3,6 @@
3
3
  module Floe
4
4
  class Workflow
5
5
  class State
6
- include Logging
7
6
  include ValidationMixin
8
7
 
9
8
  class << self
@@ -56,14 +55,22 @@ module Floe
56
55
  mark_started(context)
57
56
  end
58
57
 
58
+ def started?(context)
59
+ context.state_started?
60
+ end
61
+
59
62
  def finish(context)
60
63
  mark_finished(context)
61
64
  end
62
65
 
66
+ def finished?(context)
67
+ context.state_finished?
68
+ end
69
+
63
70
  def mark_started(context)
64
71
  context.state["EnteredTime"] = Time.now.utc.iso8601
65
72
 
66
- logger.info("Running state: [#{long_name}] with input [#{context.json_input}]...")
73
+ context.logger.info("Running state: [#{long_name}] with input [#{context.json_input}]...")
67
74
  end
68
75
 
69
76
  def mark_finished(context)
@@ -74,7 +81,7 @@ module Floe
74
81
  context.state["Duration"] = finished_time - entered_time
75
82
 
76
83
  level = context.failed? ? :error : :info
77
- logger.public_send(level, "Running state: [#{long_name}] with input [#{context.json_input}]...Complete #{context.next_state ? "- next state [#{context.next_state}]" : "workflow -"} output: [#{context.json_output}]")
84
+ context.logger.public_send(level, "Running state: [#{long_name}] with input [#{context.json_input}]...Complete #{context.next_state ? "- next state [#{context.next_state}]" : "workflow -"} output: [#{context.json_output}]")
78
85
 
79
86
  0
80
87
  end
@@ -88,7 +95,7 @@ module Floe
88
95
  end
89
96
 
90
97
  def ready?(context)
91
- !context.state_started? || !running?(context)
98
+ !started?(context) || !running?(context)
92
99
  end
93
100
 
94
101
  def running?(context)