statelint 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3084f3b4eea83b19e2d4bf60acf70ecf6e8bd151c1f86007203bfd8139c7c6d
4
- data.tar.gz: e3f789d73ddde2eaa63f215598e907df32058bea101a608d68f6a66801e8703c
3
+ metadata.gz: 5262124338dfbdc1d35c192fdd7489f32fa033b00d0d770fd4401b65f867f7af
4
+ data.tar.gz: ffa8f53eb75b50b9f19e3ffe9921bbf21b375ba46f9bca34d33f26bd1f07f60e
5
5
  SHA512:
6
- metadata.gz: b59e83552e82a3e5333d79d99bfd8d19f4e416ab14ed7b1677f78ad48624d98d9384f048836b57f78ecb4480230816f9019218c1e2e46ef104d9e331140da024
7
- data.tar.gz: 81173298fa0eb6dc22d02dbcf957dca86343f1b6713b5b2e3cef9809eb5a377de4e727189a4ef1ca1d32eddf3e416bf9f51de8d95ea3c073254265f718442ac1
6
+ metadata.gz: 7d90e211e7e76d11b1086a07df8b57c6a364abd36ddaa0132d9cae3282e4eca57e087fed77edb977303369edd094b8c8aa839166ec506095449e6819c2ecdc95
7
+ data.tar.gz: 381c044574782f87de89426762fb235dbf209c0e9e89b5af9d4ab843db78cecc4be9ff2fe7e26deb1ba324eb373a2369331f37dc4dfd8bf40bc642eb417a6de0
@@ -20,22 +20,33 @@ A State whose "End" field's value is true is a "Terminal State".
20
20
  Each of a Succeed State and a Fail State is a "Terminal State".
21
21
  A State which is not a Terminal State or a Choice State MUST have a string field named "Next".
22
22
  A Terminal State MUST NOT have a field named "Next".
23
- A State MAY have a nullable-JSONPath field named "InputPath".
23
+ A State MAY have a field named "InputPath".
24
24
  Each of a Pass State, a Task State, a Parallel State, and a Map State MAY have a nullable-referencePath field named "ResultPath".
25
- A State MAY have a nullable-JSONPath field named "OutputPath".
25
+ Each of a Task State, a Parallel State, and a Map State MAY have a field named "ResultSelector".
26
+ A State MAY have a field named "OutputPath".
26
27
  A Pass State MAY have a field named "Result".
27
28
  A Fail State MUST NOT have a field named "InputPath".
28
29
  A Fail State MUST NOT have a field named "OutputPath".
29
30
  A Fail State MAY have a string field named "Cause".
31
+ A Fail State MAY have a field named "CausePath".
32
+ A Fail State MAY have only one of "Cause" and "CausePath".
30
33
  A Fail State MAY have a string field named "Error".
34
+ A Fail State MAY have a field named "ErrorPath".
35
+ A Fail State MAY have only one of "Error" and "ErrorPath".
31
36
  Each of a Task State, a Parallel State, and a Map State MAY have an object-array field named "Retry"; each element is a "Retrier".
32
37
  A Task State MUST have a URI field named "Resource".
33
38
  A Task State MAY have a positive-integer field named "TimeoutSeconds" whose value MUST be less than 99999999.
34
39
  A Task State MAY have a positive-integer field named "HeartbeatSeconds" whose value MUST be less than 99999999.
40
+ A Task State MAY have a referencePath field named "TimeoutSecondsPath".
41
+ A Task State MAY have a referencePath field named "HeartbeatSecondsPath".
42
+ A Task State MAY have only one of "TimeoutSeconds" and "TimeoutSecondsPath".
43
+ A Task State MAY have only one of "HeartbeatSeconds" and "HeartbeatSecondsPath".
35
44
  A Retrier MUST have a nonempty-string-array field named "ErrorEquals".
36
45
  A Retrier MAY have an positive-integer field named "IntervalSeconds".
37
46
  A Retrier MAY have a nonnegative-integer field named "MaxAttempts" whose value MUST be less than 99999999.
38
47
  A Retrier MAY have a float field named "BackoffRate" whose value MUST be greater than or equal to 1.0.
48
+ A Retrier MAY have a positive-integer field named "MaxDelaySeconds".
49
+ A Retrier MAY have a string field named "JitterStrategy" whose value MUST be one of "FULL" or "NONE".
39
50
  Each of a Task State, a Parallel State, and a Map State MAY have an object-array field named "Catch"; each element is a "Catcher".
40
51
  A Catcher MUST have an nonempty-string-array field named "ErrorEquals".
41
52
  A Catcher MUST have a string field named "Next".
@@ -51,9 +62,9 @@ A Choice Rule MUST have a string field named "Next".
51
62
  A Choice Rule with an "And" field is a "Boolean".
52
63
  A Choice Rule with an "Or" field is a "Boolean".
53
64
  A Choice Rule with a "Not" field is a "Boolean".
54
- A Choice Rule MAY have a referencePath field named "Variable".
65
+ A Choice Rule MAY have a field named "Variable".
55
66
  A Choice Rule with a "Variable" field is a "Comparison".
56
- A Comparison MUST have a field named one of "StringEquals", "StringLessThan", "StringGreaterThan", "StringLessThanEquals", "StringGreaterThanEquals", "NumericEquals", "NumericLessThan", "NumericGreaterThan", "NumericLessThanEquals", "NumericGreaterThanEquals", "BooleanEquals", "TimestampEquals", "TimestampLessThan", "TimestampGreaterThan", "TimestampLessThanEquals", or "TimestampGreaterThanEquals".
67
+ A Comparison MUST have a field named one of "StringEquals", "StringLessThan", "StringGreaterThan", "StringLessThanEquals", "StringGreaterThanEquals", "NumericEquals", "NumericLessThan", "NumericGreaterThan", "NumericLessThanEquals", "NumericGreaterThanEquals", "BooleanEquals", "TimestampEquals", "TimestampLessThan", "TimestampGreaterThan", "TimestampLessThanEquals", "TimestampGreaterThanEquals", "StringEqualsPath", "StringLessThanPath", "StringGreaterThanPath", "StringLessThanEqualsPath", "StringGreaterThanEqualsPath", "NumericEqualsPath", "NumericLessThanPath", "NumericGreaterThanPath", "NumericLessThanEqualsPath", "NumericGreaterThanEqualsPath", "BooleanEqualsPath", "TimestampEqualsPath", "TimestampLessThanPath", "TimestampGreaterThanPath", "TimestampLessThanEqualsPath", "TimestampGreaterThanEqualsPath", "IsNull", "IsPresent", "IsNumeric", "IsString", "IsBoolean", "IsTimestamp", or "StringMatches".
57
68
  A Comparison MAY have a string field named "StringEquals".
58
69
  A Comparison MAY have a string field named "StringLessThan".
59
70
  A Comparison MAY have a string field named "StringGreaterThan".
@@ -70,6 +81,29 @@ A Comparison MAY have a timestamp field named "TimestampLessThan".
70
81
  A Comparison MAY have a timestamp field named "TimestampGreaterThan".
71
82
  A Comparison MAY have a timestamp field named "TimestampLessThanEquals".
72
83
  A Comparison MAY have a timestamp field named "TimestampGreaterThanEquals".
84
+ A Comparison MAY have a boolean field named "IsNull".
85
+ A Comparison MAY have a boolean field named "IsPresent".
86
+ A Comparison MAY have a boolean field named "IsNumeric".
87
+ A Comparison MAY have a boolean field named "IsString".
88
+ A Comparison MAY have a boolean field named "IsBoolean".
89
+ A Comparison MAY have a boolean field named "IsTimestamp".
90
+ A Comparison MAY have a string field named "StringMatches".
91
+ A Comparison MAY have a referencePath field named "StringEqualsPath".
92
+ A Comparison MAY have a referencePath field named "StringLessThanPath".
93
+ A Comparison MAY have a referencePath field named "StringGreaterThanPath".
94
+ A Comparison MAY have a referencePath field named "StringLessThanEqualsPath".
95
+ A Comparison MAY have a referencePath field named "StringGreaterThanEqualsPath".
96
+ A Comparison MAY have a referencePath field named "NumericEqualsPath".
97
+ A Comparison MAY have a referencePath field named "NumericLessThanPath".
98
+ A Comparison MAY have a referencePath field named "NumericGreaterThanPath".
99
+ A Comparison MAY have a referencePath field named "NumericLessThanEqualsPath".
100
+ A Comparison MAY have a referencePath field named "NumericGreaterThanEqualsPath".
101
+ A Comparison MAY have a referencePath field named "BooleanEqualsPath".
102
+ A Comparison MAY have a referencePath field named "TimestampEqualsPath".
103
+ A Comparison MAY have a referencePath field named "TimestampLessThanPath".
104
+ A Comparison MAY have a referencePath field named "TimestampGreaterThanPath".
105
+ A Comparison MAY have a referencePath field named "TimestampLessThanEqualsPath".
106
+ A Comparison MAY have a referencePath field named "TimestampGreaterThanEqualsPath".
73
107
  A Comparison MUST NOT have a field named "And".
74
108
  A Comparison MUST NOT have a field named "Or".
75
109
  A Comparison MUST NOT have a field named "Not".
@@ -80,7 +114,7 @@ A Nested Rule with an "And" field is a "Nested Boolean".
80
114
  A Nested Rule with an "Or" field is a "Nested Boolean".
81
115
  A Nested Rule with a "Not" field is a "Nested Boolean".
82
116
  A Nested Rule MUST NOT have a field named "Next".
83
- A Nested Rule MAY have a referencePath field named "Variable".
117
+ A Nested Rule MAY have a field named "Variable".
84
118
  A Nested Rule with a "Variable" field is a "Nested Comparison".
85
119
  A Nested Comparison MAY have a string field named "StringEquals".
86
120
  A Nested Comparison MAY have a string field named "StringLessThan".
@@ -98,6 +132,29 @@ A Nested Comparison MAY have a timestamp field named "TimestampLessThan".
98
132
  A Nested Comparison MAY have a timestamp field named "TimestampGreaterThan".
99
133
  A Nested Comparison MAY have a timestamp field named "TimestampLessThanEquals".
100
134
  A Nested Comparison MAY have a timestamp field named "TimestampGreaterThanEquals".
135
+ A Nested Comparison MAY have a boolean field named "IsNull".
136
+ A Nested Comparison MAY have a boolean field named "IsPresent".
137
+ A Nested Comparison MAY have a boolean field named "IsNumeric".
138
+ A Nested Comparison MAY have a boolean field named "IsString".
139
+ A Nested Comparison MAY have a boolean field named "IsBoolean".
140
+ A Nested Comparison MAY have a boolean field named "IsTimestamp".
141
+ A Nested Comparison MAY have a string field named "StringMatches".
142
+ A Nested Comparison MAY have a referencePath field named "StringEqualsPath".
143
+ A Nested Comparison MAY have a referencePath field named "StringLessThanPath".
144
+ A Nested Comparison MAY have a referencePath field named "StringGreaterThanPath".
145
+ A Nested Comparison MAY have a referencePath field named "StringLessThanEqualsPath".
146
+ A Nested Comparison MAY have a referencePath field named "StringGreaterThanEqualsPath".
147
+ A Nested Comparison MAY have a referencePath field named "NumericEqualsPath".
148
+ A Nested Comparison MAY have a referencePath field named "NumericLessThanPath".
149
+ A Nested Comparison MAY have a referencePath field named "NumericGreaterThanPath".
150
+ A Nested Comparison MAY have a referencePath field named "NumericLessThanEqualsPath".
151
+ A Nested Comparison MAY have a referencePath field named "NumericGreaterThanEqualsPath".
152
+ A Nested Comparison MAY have a referencePath field named "BooleanEqualsPath".
153
+ A Nested Comparison MAY have a referencePath field named "TimestampEqualsPath".
154
+ A Nested Comparison MAY have a referencePath field named "TimestampLessThanPath".
155
+ A Nested Comparison MAY have a referencePath field named "TimestampGreaterThanPath".
156
+ A Nested Comparison MAY have a referencePath field named "TimestampLessThanEqualsPath".
157
+ A Nested Comparison MAY have a referencePath field named "TimestampGreaterThanEqualsPath".
101
158
  A Nested Comparison MUST NOT have a field named "And".
102
159
  A Nested Comparison MUST NOT have a field named "Or".
103
160
  A Nested Comparison MUST NOT have a field named "Not".
@@ -109,7 +166,7 @@ A Wait State MUST have only one of "Seconds", "SecondsPath", "Timestamp", and "T
109
166
  A Wait State MUST have a field named one of "Seconds", "SecondsPath", "Timestamp", or "TimestampPath".
110
167
  A Parallel State MUST have an object-array field named "Branches"; each element is a "Branch".
111
168
  A Map State MUST have an object field named "Iterator"; its value is a "Branch".
112
- A Map State MAY have a referencePath field named "ItemsPath".
169
+ A Map State MAY have a field named "ItemsPath".
113
170
  A Map State MAY have a numeric field named "MaxConcurrency".
114
171
  A Branch MUST have an object field named "States"; each field is a "State".
115
172
  A Branch MUST have a string field named "StartAt".
@@ -32,6 +32,11 @@ module StateMachineLint
32
32
  # We keep track of all the state names and complain about
33
33
  # dupes
34
34
  @all_state_names = {}
35
+ @payload_builder_fields = ["Parameters", "ResultSelector"]
36
+ @context_object_access_fields = [{"field"=> "InputPath", "nullable"=> true}, {"field"=> "OutputPath", "nullable"=> true}, {"field"=> "ItemsPath", "nullable"=> false}]
37
+ @choice_state_nested_operators = ["And", "Or", "Not"]
38
+ @intrinsic_invocation_regex = /^States\.(Format|Array|ArrayPartition|ArrayContains|ArrayRange|ArrayGetItem|ArrayLength|ArrayUnique|Base64Encode|Base64Decode|Hash|JsonMerge|JsonToString|StringToJson|MathRandom|MathAdd|StringSplit)\(.+\)$/
39
+ @intrinsic_uuid_invocation_regex = /^States\.UUID\(\)$/
35
40
  end
36
41
 
37
42
  def check(node, path, problems)
@@ -57,8 +62,17 @@ module StateMachineLint
57
62
  states = node['States']
58
63
  states.keys.each do |name|
59
64
  child = states[name]
60
- if child.is_a?(Hash) && child.key?('Parameters')
61
- probe_parameters(child, path + '.' + name, problems)
65
+ if child.is_a?(Hash)
66
+ child_path = path + '.' + name
67
+ probe_context_object_access(child, child_path, problems)
68
+ @payload_builder_fields.each do |field_name|
69
+ if child.key?(field_name)
70
+ probe_payload_builder(child[field_name], child_path, problems, field_name)
71
+ end
72
+ end
73
+ if child.key?("Type") && child["Type"] == "Choice" && child.key?("Choices")
74
+ probe_choice_state(child["Choices"], child_path + '.Choices' , problems)
75
+ end
62
76
  end
63
77
 
64
78
  if @all_state_names[name]
@@ -121,25 +135,66 @@ module StateMachineLint
121
135
  end
122
136
  end
123
137
 
138
+ def probe_context_object_access(node, path, problems)
139
+ @context_object_access_fields.each do |field|
140
+ field_name = field["field"]
141
+ nullable = field["nullable"]
142
+ if node.key?(field_name)
143
+ if !nullable && node[field_name].nil?
144
+ problems << "Field \"#{field_name}\" defined at \"#{path}\" should be non-null"
145
+ return
146
+ end
147
+ if !node[field_name].nil? and !is_valid_parameters_path?(node[field_name])
148
+ problems << "Field \"#{field_name}\" defined at \"#{path}\" is not a JSONPath"
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ def probe_choice_state(node, path, problems)
155
+ if node.is_a?(Hash)
156
+ if node.key?("Variable") && !is_valid_parameters_path?(node["Variable"])
157
+ problems << "Field \"Variable\" of Choice state at \"#{path}\" is not a JSONPath"
158
+ end
159
+ @choice_state_nested_operators.each do |operator|
160
+ if node.key?(operator)
161
+ probe_choice_state(node[operator], path + '.' + operator, problems)
162
+ end
163
+ end
164
+ elsif node.is_a?(Array)
165
+ node.size.times {|i| probe_choice_state(node[i], "#{path}[#{i}]", problems) }
166
+ end
167
+ end
168
+
124
169
  # Search through Parameters for object nodes and check field semantics
125
- def probe_parameters(node, path, problems)
170
+ def probe_payload_builder(node, path, problems, field_name)
126
171
  if node.is_a?(Hash)
127
172
  node.each do |name, val|
128
173
  if name.end_with? '.$'
129
- if (!val.is_a?(String)) || (!is_valid_parameters_path?(val))
130
- problems << "Field \"#{name}\" of Parameters at \"#{path}\" is not a JSONPath"
174
+ if !is_intrinsic_invocation?(val) && !is_valid_parameters_path?(val)
175
+ problems << "Field \"#{name}\" of #{field_name} at \"#{path}\" is not a JSONPath or intrinsic function expression"
131
176
  end
132
177
  else
133
- probe_parameters(val, "#{path}.#{name}", problems)
178
+ probe_payload_builder(val, "#{path}.#{name}", problems, field_name)
134
179
  end
135
180
  end
136
181
  elsif node.is_a?(Array)
137
- node.size.times {|i| probe_parameters(node[i], "#{path}[#{i}]", problems) }
182
+ node.size.times {|i| probe_payload_builder(node[i], "#{path}[#{i}]", problems, field_name) }
138
183
  end
139
184
  end
140
185
 
186
+ def is_intrinsic_invocation?(val)
187
+ if val.is_a?(String) && (val.match?(@intrinsic_invocation_regex) || val.match?(@intrinsic_uuid_invocation_regex))
188
+ return true
189
+ end
190
+ return false
191
+ end
192
+
141
193
  # Check if a string that ends with ".$" is a valid path
142
194
  def is_valid_parameters_path?(val)
195
+ if !val.is_a?(String)
196
+ return false
197
+ end
143
198
  # If the value begins with “$$”, the first dollar character is stripped off and the remainder MUST be a Path.
144
199
  if val.start_with?("$$")
145
200
  path_to_check = val.gsub(/^\$/, "")
data/statelint.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'statelint'
3
- s.version = '0.4.1'
3
+ s.version = '0.6.0'
4
4
  s.summary = "State Machine JSON validator"
5
5
  s.description = "Validates a JSON object representing a State Machine"
6
6
  s.authors = ["Tim Bray"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statelint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Bray
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-11 00:00:00.000000000 Z
11
+ date: 2023-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: j2119
@@ -54,7 +54,7 @@ licenses:
54
54
  metadata:
55
55
  source_code_uri: https://github.com/awslabs/statelint
56
56
  bug_tracker_uri: https://github.com/awslabs/statelint/issues
57
- post_install_message:
57
+ post_install_message:
58
58
  rdoc_options: []
59
59
  require_paths:
60
60
  - lib
@@ -69,9 +69,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  requirements: []
72
- rubyforge_project:
73
- rubygems_version: 2.7.4
74
- signing_key:
72
+ rubygems_version: 3.4.10
73
+ signing_key:
75
74
  specification_version: 4
76
75
  summary: State Machine JSON validator
77
76
  test_files: []