Guest User

Untitled

a guest
Jul 21st, 2018
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.88 KB | None | 0 0
  1. require 'rubygems'
  2. require 'ruote'
  3. require 'flexmock'
  4. require 'spec'
  5.  
  6. # Workitem fields validation
  7. # This participant validates the fields of the current workitem
  8. # Especially useful to validate the initial workitem
  9. # An input definition may include the following:
  10. # - name: Input name, compulsory
  11. # - kind: Input type, compulsory
  12. # - obligation: Whether input is required or optional, required by default.
  13. # - default: Input default value, optional.
  14. #
  15. # The kind of an input consists of a string corresponding to the Ruby class
  16. # name, one of:
  17. # - String
  18. # - Fixnum
  19. # - Bool (note: not a ruby class per se)
  20. # - DateTime
  21. # - Array<KIND> (KIND must recursively take on the values in this list)
  22. # - Hash<KIND, KIND) (same note as above)
  23. #
  24. # The participant expects the 'fields_definitions' field to contain the
  25. # definitions as hashes indexed by field name
  26. #
  27. # e.g.:
  28. # fields_definitions 'names' => [ 'Array<String>', :required ],
  29. # 'operation' => [ 'String', :default => 'run' ],
  30. # 'operation_timeout' => [ 'Fixnum', :default => 300 ],
  31. # 'arguments' => [ 'Hash<String, String>', :default => {} ],
  32. # 'result_field' => [ 'String', :default => '__result__' ]
  33. # error '${f:fields_errors}', :if => '${f:fields_errors}'
  34. #
  35. class FieldsValidationParticipant
  36. include Ruote::LocalParticipant
  37.  
  38. # Lookup fields validations and iterate through to validate
  39. #
  40. # === Input Workitem Fields
  41. # fields_definitions<Hash>:: Hash of fields definition indexed by name
  42. #
  43. # === Ouput Workitem Fields
  44. # fields_errors<Array>:: List of error messages, empty if no error
  45. def consume(workitem)
  46. defs = workitem.fields['fields_definitions'] || {}
  47. errors = []
  48.  
  49. # First validate
  50. defs.each { |name, val| errors += validate(name, val, workitem.lookup(name)) }
  51. workitem.set_field('fields_errors', errors)
  52.  
  53. # Then set default values
  54. defaults = defs.select { |_, val| val.is_a?(Hash) && val.include?(:default) }
  55. defaults.each { |name, val| workitem.set_field(name, val[:default]) unless workitem.lookup(name) }
  56.  
  57. reply_to_engine(workitem)
  58. end
  59.  
  60. protected
  61.  
  62. # Validate a given workitem field against its input definition
  63. #
  64. # === Argumnets
  65. # name<String>:: Name of field
  66. # definition<Array>:: Input definition
  67. # field<Object>:: Actual field value
  68. #
  69. # === Return
  70. # errors<Array>:: List of error messages or empty array if no error
  71. def validate(name, definition, field)
  72. errors = []
  73. if field.nil?
  74. if definition[1] == :required
  75. errors << "Missing required workitem field #{name.inspect}"
  76. end
  77. else
  78. errors += check_kind(field, definition[0], name)
  79. end
  80. errors
  81. end
  82.  
  83. # Recursively check that the kind of given field matches given kind
  84. #
  85. # === Arguments
  86. # field<Object>:: Actual workitem field being checked
  87. # kind<String>:: Kind 'field' should match
  88. # name<String>:: Name of field for error message
  89. #
  90. # === Return
  91. # errors<Array>:: List of errors or empty array if no error
  92. def check_kind(field, kind, name)
  93. errors = []
  94. if kind =~ /^Array<(.*)>$/
  95. inner = Regexp.last_match[1]
  96. errors += check_kind(field, 'Array', name)
  97. if errors.empty?
  98. field.each_index do |i|
  99. errors += check_kind(field[i], inner, "element at index #{i} of #{name}")
  100. end
  101. end
  102. elsif kind =~ /^Hash<(.*), *(.*)>$/
  103. inner_key = Regexp.last_match[1]
  104. inner_val = Regexp.last_match[2]
  105. errors += check_kind(field, 'Hash', name)
  106. if errors.empty?
  107. field.each do |k ,v|
  108. errors += check_kind(k, inner_key, "key #{k.inspect} of #{name}")
  109. errors += check_kind(v, inner_val, "value at key #{k.inspect} of #{name}")
  110. end
  111. end
  112. else
  113. if field.class.to_s != kind
  114. errors << "Workitem field #{name.inspect} kind is invalid (should be " +
  115. "#{kind.inspect} but is #{field.class.to_s.inspect})"
  116. end
  117. end
  118. errors
  119. end
  120.  
  121. end
  122.  
  123. config = Spec::Runner.configuration
  124. config.mock_with :flexmock
  125.  
  126. describe Maestro::FieldsValidationParticipant do
  127.  
  128. # Create test workitem
  129. def new_workitem
  130. workitem = Ruote::Workitem.new('fields' =>
  131. { 'fields_definitions' => { 'required_field' => [ 'String', :required ],
  132. 'optional_field' => [ 'String', :optional ],
  133. 'array_field' => [ 'Array<String>', :required ],
  134. 'hash_field' => [ 'Hash<String, Fixnum>', :required ] },
  135. 'required_field' => 'I\'m here',
  136. 'array_field' => [ 'I\'m a string' ],
  137. 'hash_field' => { 'key' => 42 }})
  138. end
  139.  
  140. before(:each) do
  141. @workitem = nil
  142. @validator = Maestro::FieldsValidationParticipant.new
  143. flexmock(@validator).should_receive(:reply_to_engine).and_return { |wi| @workitem = wi }
  144. end
  145.  
  146. it 'should work with no definition' do
  147. workitem = Ruote::Workitem.new('fields' => {'a' => 'b'})
  148. @validator.consume(workitem)
  149. @workitem.should_not be_nil
  150. @workitem.to_h.should == workitem.to_h
  151. end
  152.  
  153. it 'should validate required inputs' do
  154. workitem = new_workitem
  155. @validator.consume(workitem)
  156. @workitem.should_not be_nil
  157. @workitem.to_h.should == workitem.to_h
  158. end
  159.  
  160. it 'should invalidate missing inputs' do
  161. workitem = new_workitem
  162. workitem.set_field('required_field', nil)
  163. @validator.consume(workitem)
  164. @workitem.should_not be_nil
  165. @workitem.lookup('fields_errors').should_not be_nil
  166. @workitem.lookup('fields_errors').class.should == Array
  167. @workitem.lookup('fields_errors').size.should == 1
  168. @workitem.lookup('fields_errors')[0].should =~ /^Missing required workitem field/
  169. end
  170.  
  171. it 'should invalidate incorrect kinds' do
  172. workitem = new_workitem
  173. workitem.set_field('optional_field', 42)
  174. @validator.consume(workitem)
  175. @workitem.should_not be_nil
  176. @workitem.lookup('fields_errors').should_not be_nil
  177. @workitem.lookup('fields_errors').class.should == Array
  178. @workitem.lookup('fields_errors').size.should == 1
  179. @workitem.lookup('fields_errors')[0].should =~ /kind is invalid/
  180. end
  181.  
  182. it 'should invalidate multiple errors' do
  183. workitem = new_workitem
  184. workitem.set_field('optional_field', 42)
  185. workitem.set_field('required_field', 42)
  186. @validator.consume(workitem)
  187. @workitem.should_not be_nil
  188. @workitem.lookup('fields_errors').should_not be_nil
  189. @workitem.lookup('fields_errors').class.should == Array
  190. @workitem.lookup('fields_errors').size.should == 2
  191. @workitem.lookup('fields_errors').all? { |e| e.should =~ /kind is invalid/ }
  192. end
  193.  
  194. it 'should invalidate incorrect array kind' do
  195. workitem = new_workitem
  196. workitem.set_field('array_field', [ 42 ])
  197. @validator.consume(workitem)
  198. @workitem.should_not be_nil
  199. @workitem.lookup('fields_errors').should_not be_nil
  200. @workitem.lookup('fields_errors').class.should == Array
  201. @workitem.lookup('fields_errors').size.should == 1
  202. @workitem.lookup('fields_errors')[0].should =~ /kind is invalid/
  203. end
  204.  
  205. it 'should invalidate incorrect hash key kind' do
  206. workitem = new_workitem
  207. workitem.set_field('hash_field', [ 42 => 42 ])
  208. @validator.consume(workitem)
  209. @workitem.should_not be_nil
  210. @workitem.lookup('fields_errors').should_not be_nil
  211. @workitem.lookup('fields_errors').class.should == Array
  212. @workitem.lookup('fields_errors').size.should == 1
  213. @workitem.lookup('fields_errors')[0].should =~ /kind is invalid/
  214. end
  215.  
  216. it 'should invalidate incorrect hash value kind' do
  217. workitem = new_workitem
  218. workitem.set_field('hash_field', [ 'key' => 'not_an_int' ])
  219. @validator.consume(workitem)
  220. @workitem.should_not be_nil
  221. @workitem.lookup('fields_errors').should_not be_nil
  222. @workitem.lookup('fields_errors').class.should == Array
  223. @workitem.lookup('fields_errors').size.should == 1
  224. @workitem.lookup('fields_errors')[0].should =~ /kind is invalid/
  225. end
  226. end
Add Comment
Please, Sign In to add comment