foobara 0.3.0 → 0.4.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.
Files changed (483) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +15 -111
  4. data/lib/foobara/all.rb +22 -0
  5. data/projects/command/lib/foobara/command.rb +6 -3
  6. data/projects/command/src/command_pattern_implementation/concerns/subcommands.rb +3 -1
  7. data/projects/command/src/command_pattern_implementation/concerns/transactions.rb +2 -0
  8. data/projects/command/src/extensions/domain/domain_module_extension.rb +16 -0
  9. data/projects/command/src/extensions/domain/domain_module_extension_extension.rb +22 -0
  10. data/projects/command/src/extensions/foobara.rb +7 -0
  11. data/projects/command_connectors/Guardfile +6 -0
  12. data/projects/command_connectors/lib/foobara/command_connectors.rb +1 -1
  13. data/projects/command_connectors/spec/ambiguous_lookups_spec.rb +143 -0
  14. data/projects/command_connectors/spec/command_connector_spec.rb +2443 -0
  15. data/projects/command_connectors/spec/command_registry_spec.rb +9 -0
  16. data/projects/command_connectors/spec/commands/describe_spec.rb +83 -0
  17. data/projects/command_connectors/spec/commands/ping_spec.rb +14 -0
  18. data/projects/command_connectors/spec/commands/query_git_commit_info_spec.rb +55 -0
  19. data/projects/command_connectors/spec/fixtures/command_connectors/manifest_with_errors_with_missing_command_parents.yaml +1662 -0
  20. data/projects/command_connectors/spec/not_found_error_spec.rb +17 -0
  21. data/projects/command_connectors/spec/pre_commit_transformers/load_atoms_pre_commit_transformer_spec.rb +105 -0
  22. data/projects/command_connectors/spec/request_spec.rb +95 -0
  23. data/projects/command_connectors/spec/serializer_spec.rb +9 -0
  24. data/projects/command_connectors/spec/serializers/aggregate_serializer_spec.rb +45 -0
  25. data/projects/command_connectors/spec/serializers/atomic_serializer_spec.rb +68 -0
  26. data/projects/command_connectors/spec/serializers/entities_to_primary_keys_serializer_spec.rb +36 -0
  27. data/projects/command_connectors/spec/serializers/json_serializer_spec.rb +22 -0
  28. data/projects/command_connectors/spec/serializers/noop_serializer_spec.rb +22 -0
  29. data/projects/command_connectors/spec/serializers/yaml_serializer_spec.rb +22 -0
  30. data/projects/command_connectors/spec/spec_helper.rb +51 -0
  31. data/projects/command_connectors/spec/support/rubyprof.rb +32 -0
  32. data/projects/command_connectors/spec/support/term_trap.rb +6 -0
  33. data/projects/command_connectors/spec/transformers/entity_to_primary_key_inputs_transformer_spec.rb +119 -0
  34. data/projects/command_connectors/src/command_connector/commands/describe.rb +4 -2
  35. data/projects/command_connectors/src/command_connector.rb +54 -15
  36. data/projects/command_connectors/src/transformed_command.rb +62 -14
  37. data/projects/domain_mapper/lib/foobara/domain_mapper.rb +4 -0
  38. data/projects/domain_mapper/src/domain_mapper.rb +2 -0
  39. data/projects/{detached_entity → entities/projects/detached_entity}/lib/foobara/detached_entity.rb +4 -0
  40. data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/primary_key.rb +3 -1
  41. data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/types.rb +9 -1
  42. data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/builtin_types/detached_entity/validators/model_instance_is_valid.rb +1 -1
  43. data/projects/{entity → entities/projects/entity}/lib/foobara/entity.rb +4 -0
  44. data/projects/{entity → entities/projects/entity}/src/concerns/callbacks.rb +2 -0
  45. data/projects/{entity → entities/projects/entity}/src/concerns/transactions.rb +1 -5
  46. data/projects/entities/projects/entity/src/extensions/domain/domain_module_extension.rb +111 -0
  47. data/projects/{entity → entities/projects/entity}/src/not_found_error.rb +11 -1
  48. data/projects/{in_memory_crud_driver → entities/projects/in_memory_crud_driver}/lib/foobara/in_memory_crud_driver.rb +4 -0
  49. data/projects/entities/projects/in_memory_crud_driver_minimal/lib/foobara/in_memory_crud_driver_minimal.rb +6 -0
  50. data/projects/{in_memory_crud_driver_minimal → entities/projects/in_memory_crud_driver_minimal}/src/in_memory_minimal.rb +6 -4
  51. data/projects/{model → entities/projects/model}/lib/foobara/model.rb +4 -0
  52. data/projects/{model → entities/projects/model}/src/concerns/classes.rb +1 -1
  53. data/projects/{model → entities/projects/model}/src/concerns/types.rb +4 -2
  54. data/projects/entities/projects/model/src/extensions/domain/module_extension.rb +20 -0
  55. data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/to_type_transformer.rb +8 -3
  56. data/projects/{model → entities/projects/model}/src/model.rb +25 -1
  57. data/projects/{model_attribute_helpers → entities/projects/model_attribute_helpers}/lib/foobara/model_attribute_helpers.rb +2 -0
  58. data/projects/{model_attribute_helpers → entities/projects/model_attribute_helpers}/src/attribute_helper_aliases.rb +1 -1
  59. data/projects/{nested_transactionable → entities/projects/nested_transactionable}/lib/foobara/nested_transactionable.rb +2 -0
  60. data/projects/{persistence → entities/projects/persistence}/lib/foobara/persistence.rb +4 -0
  61. data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction/concerns/transaction_tracking.rb +2 -0
  62. data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction/state_machine.rb +2 -0
  63. data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction_table/concerns/record_tracking.rb +2 -0
  64. data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction_table.rb +2 -2
  65. data/projects/entities/projects/weak_object_set/lib/foobara/weak_object_set.rb +6 -0
  66. data/projects/entities/spec/builtin_types/detached_entity_spec.rb +300 -0
  67. data/projects/entities/spec/builtin_types/entity_spec.rb +501 -0
  68. data/projects/entities/spec/builtin_types/model_spec.rb +420 -0
  69. data/projects/entities/spec/detached_entity/casting_spec.rb +31 -0
  70. data/projects/entities/spec/entity/concerns/associations_spec.rb +73 -0
  71. data/projects/entities/spec/entity/concerns/attribute_helpers_spec.rb +220 -0
  72. data/projects/entities/spec/entity/concerns/callbacks_spec.rb +61 -0
  73. data/projects/entities/spec/entity/concerns/initialization_spec.rb +213 -0
  74. data/projects/entities/spec/entity/concerns/mutations_spec.rb +38 -0
  75. data/projects/entities/spec/entity/concerns/queries_spec.rb +184 -0
  76. data/projects/entities/spec/entity/entity_spec.rb +240 -0
  77. data/projects/entities/spec/entity/extending_existing_spec.rb +137 -0
  78. data/projects/entities/spec/entity/extensions/domain_module_extension_spec.rb +42 -0
  79. data/projects/entities/spec/entity/from_type_declaration_spec.rb +34 -0
  80. data/projects/entities/spec/entity/that_owns_spec.rb +243 -0
  81. data/projects/entities/spec/entity/weak_object_set_spec.rb +91 -0
  82. data/projects/entities/spec/model/model_spec.rb +135 -0
  83. data/projects/entities/spec/model/types_spec.rb +393 -0
  84. data/projects/entities/spec/nested_transactionable/nested_transactionable_spec.rb +67 -0
  85. data/projects/entities/spec/persistence/entity_attributes_crud_drivers/in_memory_minimal_spec.rb +13 -0
  86. data/projects/entities/spec/persistence/entity_base/transaction_spec.rb +509 -0
  87. data/projects/entities/spec/persistence/entity_base/transaction_table_spec.rb +58 -0
  88. data/projects/entities/spec/persistence/persistence_spec.rb +148 -0
  89. data/projects/entities/spec/spec_helper.rb +52 -0
  90. data/projects/entities/spec/support/rubyprof.rb +32 -0
  91. data/projects/entities/spec/support/term_trap.rb +6 -0
  92. data/projects/entities/spec/type_declarations/sensitive_type_removers_spec.rb +90 -0
  93. data/projects/manifest/lib/foobara/manifest.rb +2 -0
  94. data/projects/manifest/spec/manifest_spec.rb +378 -0
  95. data/projects/manifest/spec/model_spec.rb +74 -0
  96. data/projects/manifest/spec/spec_helper.rb +50 -0
  97. data/projects/manifest/spec/type_declaration_spec.rb +39 -0
  98. data/projects/manifest/src/{foobara/manifest/array.rb → array.rb} +1 -1
  99. data/projects/manifest/src/{foobara/manifest/attributes.rb → attributes.rb} +1 -1
  100. data/projects/manifest/src/{foobara/manifest/base_manifest.rb → base_manifest.rb} +14 -6
  101. data/projects/manifest/src/{foobara/manifest/command.rb → command.rb} +3 -3
  102. data/projects/manifest/src/{foobara/manifest/detached_entity.rb → detached_entity.rb} +1 -1
  103. data/projects/manifest/src/{foobara/manifest/model.rb → model.rb} +1 -1
  104. data/projects/manifest/src/{foobara/manifest/possible_error.rb → possible_error.rb} +2 -0
  105. data/projects/manifest/src/{foobara/manifest/type.rb → type.rb} +4 -4
  106. data/projects/manifest/src/{foobara/manifest/type_declaration.rb → type_declaration.rb} +5 -5
  107. data/projects/model_plumbing/lib/foobara/model_plumbing.rb +13 -0
  108. data/projects/{namespace/lib/foobara/namespace.rb → model_plumbing/src/model_plumbing.rb} +1 -1
  109. data/projects/{builtin_types → typesystem/projects/builtin_types}/lib/foobara/builtin_types.rb +4 -0
  110. data/projects/{builtin_types → typesystem/projects/builtin_types}/src/builtin_types.rb +1 -1
  111. data/projects/typesystem/projects/callback/lib/foobara/callback.rb +3 -0
  112. data/projects/{callback → typesystem/projects/callback}/src/block/concerns/type.rb +2 -0
  113. data/projects/{callback → typesystem/projects/callback}/src/set.rb +3 -1
  114. data/projects/{common → typesystem/projects/common}/lib/foobara/common.rb +2 -0
  115. data/projects/{common → typesystem/projects/common}/src/data_path.rb +1 -1
  116. data/projects/{common → typesystem/projects/common}/src/error.rb +33 -9
  117. data/projects/{common → typesystem/projects/common}/src/error_key.rb +1 -1
  118. data/projects/{common → typesystem/projects/common}/src/outcome.rb +9 -5
  119. data/projects/typesystem/projects/concerns/lib/foobara/concerns.rb +3 -0
  120. data/projects/typesystem/projects/delegate/lib/foobara/delegate.rb +6 -0
  121. data/projects/{delegate → typesystem/projects/delegate}/src/extensions/module.rb +4 -0
  122. data/projects/{domain → typesystem/projects/domain}/lib/foobara/domain.rb +2 -0
  123. data/projects/{domain → typesystem/projects/domain}/src/domain.rb +35 -7
  124. data/projects/{domain → typesystem/projects/domain}/src/domain_module_extension.rb +5 -155
  125. data/projects/{domain → typesystem/projects/domain}/src/extensions/foobara.rb +0 -10
  126. data/projects/{domain → typesystem/projects/domain}/src/global_domain.rb +1 -0
  127. data/projects/{domain → typesystem/projects/domain}/src/global_organization.rb +1 -0
  128. data/projects/{domain → typesystem/projects/domain}/src/is_manifestable.rb +1 -0
  129. data/projects/{domain → typesystem/projects/domain}/src/manifestable.rb +1 -0
  130. data/projects/{domain → typesystem/projects/domain}/src/module_extension.rb +14 -10
  131. data/projects/typesystem/projects/enumerated/lib/foobara/enumerated.rb +3 -0
  132. data/projects/{enumerated → typesystem/projects/enumerated}/src/accessors.rb +2 -0
  133. data/projects/typesystem/projects/namespace/lib/foobara/namespace.rb +6 -0
  134. data/projects/{namespace → typesystem/projects/namespace}/src/namespace.rb +2 -0
  135. data/projects/{namespace → typesystem/projects/namespace}/src/namespace_helpers.rb +1 -0
  136. data/projects/typesystem/projects/project/lib/foobara/project.rb +10 -0
  137. data/projects/{foobara → typesystem/projects/project}/src/foobara.rb +1 -6
  138. data/projects/{foobara → typesystem/projects/project}/src/project.rb +3 -0
  139. data/projects/typesystem/projects/state_machine/lib/foobara/state_machine.rb +3 -0
  140. data/projects/{state_machine → typesystem/projects/state_machine}/src/callbacks.rb +37 -8
  141. data/projects/{state_machine → typesystem/projects/state_machine}/src/sugar.rb +2 -0
  142. data/projects/{state_machine → typesystem/projects/state_machine}/src/transition_log.rb +2 -0
  143. data/projects/{type_declarations → typesystem/projects/type_declarations}/lib/foobara/type_declarations.rb +10 -7
  144. data/projects/{type_declarations → typesystem/projects/type_declarations}/src/dsl/attributes.rb +2 -0
  145. data/projects/{type_declarations → typesystem/projects/type_declarations}/src/error_extension.rb +9 -1
  146. data/projects/{type_declarations → typesystem/projects/type_declarations}/src/lazy_element_types/array.rb +2 -0
  147. data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declaration_handler.rb +21 -5
  148. data/projects/{type_declarations → typesystem/projects/type_declarations}/src/with_registries.rb +31 -8
  149. data/projects/{types → typesystem/projects/types}/lib/foobara/types.rb +2 -0
  150. data/projects/{types → typesystem/projects/types}/src/type/concerns/supported_processor_registration.rb +2 -0
  151. data/projects/{types → typesystem/projects/types}/src/type.rb +72 -68
  152. data/projects/typesystem/projects/value/lib/foobara/value.rb +12 -0
  153. data/projects/{value → typesystem/projects/value}/src/processor.rb +19 -6
  154. data/projects/typesystem/spec/builtin_types/allow_nil_spec.rb +138 -0
  155. data/projects/typesystem/spec/builtin_types/array_spec.rb +140 -0
  156. data/projects/typesystem/spec/builtin_types/associative_array_spec.rb +228 -0
  157. data/projects/typesystem/spec/builtin_types/attributes_spec.rb +453 -0
  158. data/projects/typesystem/spec/builtin_types/big_decimal_spec.rb +42 -0
  159. data/projects/typesystem/spec/builtin_types/boolean_spec.rb +43 -0
  160. data/projects/typesystem/spec/builtin_types/class_type_desugarizer_spec.rb +17 -0
  161. data/projects/typesystem/spec/builtin_types/custom_type_spec.rb +430 -0
  162. data/projects/typesystem/spec/builtin_types/date_spec.rb +37 -0
  163. data/projects/typesystem/spec/builtin_types/datetime_spec.rb +49 -0
  164. data/projects/typesystem/spec/builtin_types/email_spec.rb +44 -0
  165. data/projects/typesystem/spec/builtin_types/float_spec.rb +59 -0
  166. data/projects/typesystem/spec/builtin_types/instance_of_spec.rb +75 -0
  167. data/projects/typesystem/spec/builtin_types/one_of_spec.rb +73 -0
  168. data/projects/typesystem/spec/builtin_types/string_spec.rb +117 -0
  169. data/projects/typesystem/spec/builtin_types/symbol_spec.rb +24 -0
  170. data/projects/typesystem/spec/builtin_types/tuple_spec.rb +125 -0
  171. data/projects/typesystem/spec/callback/callback_spec.rb +155 -0
  172. data/projects/typesystem/spec/callback/registry/conditioned_spec.rb +152 -0
  173. data/projects/typesystem/spec/callback/registry/multiple_action_spec.rb +144 -0
  174. data/projects/typesystem/spec/callback/registry/single_action_spec.rb +114 -0
  175. data/projects/typesystem/spec/common/data_path_spec.rb +270 -0
  176. data/projects/typesystem/spec/common/error_collection_spec.rb +77 -0
  177. data/projects/typesystem/spec/common/error_key_spec.rb +56 -0
  178. data/projects/typesystem/spec/common/outcome_spec.rb +65 -0
  179. data/projects/typesystem/spec/common/possible_error_spec.rb +17 -0
  180. data/projects/typesystem/spec/concerns/concern_spec.rb +122 -0
  181. data/projects/typesystem/spec/enumerated/accessors_spec.rb +182 -0
  182. data/projects/typesystem/spec/enumerated/enumerated_spec.rb +32 -0
  183. data/projects/typesystem/spec/enumerated/values_spec.rb +67 -0
  184. data/projects/typesystem/spec/foobara/foobara_spec.rb +45 -0
  185. data/projects/typesystem/spec/namespace/ambiguous_registry_spec.rb +70 -0
  186. data/projects/typesystem/spec/namespace/foobara_simulation_spec.rb +254 -0
  187. data/projects/typesystem/spec/namespace/is_namespace_spec.rb +103 -0
  188. data/projects/typesystem/spec/namespace/namespace_helpers_spec.rb +67 -0
  189. data/projects/typesystem/spec/namespace/namespace_spec.rb +335 -0
  190. data/projects/typesystem/spec/namespace/prefixless_registry_spec.rb +30 -0
  191. data/projects/typesystem/spec/namespace/unambiguous_registry_spec.rb +50 -0
  192. data/projects/typesystem/spec/spec_helper.rb +50 -0
  193. data/projects/typesystem/spec/state_machine/callbacks_spec.rb +144 -0
  194. data/projects/typesystem/spec/state_machine/state_machine_spec.rb +224 -0
  195. data/projects/typesystem/spec/state_machine/target_attribute_spec.rb +46 -0
  196. data/projects/typesystem/spec/support/rubyprof.rb +32 -0
  197. data/projects/typesystem/spec/support/term_trap.rb +6 -0
  198. data/projects/typesystem/spec/type_declarations/attributes_spec.rb +14 -0
  199. data/projects/typesystem/spec/type_declarations/attributes_transformer_spec.rb +95 -0
  200. data/projects/typesystem/spec/type_declarations/dsl/attributes_spec.rb +107 -0
  201. data/projects/typesystem/spec/type_declarations/handlers/extend_registered_type_declaration_spec.rb +13 -0
  202. data/projects/typesystem/spec/type_declarations/sensitive_type_removers_spec.rb +210 -0
  203. data/projects/typesystem/spec/type_declarations/type_builder_spec.rb +47 -0
  204. data/projects/typesystem/spec/type_declarations/type_declaration_spec.rb +252 -0
  205. data/projects/typesystem/spec/type_declarations/type_declarations_spec.rb +11 -0
  206. data/projects/typesystem/spec/type_declarations/typed_transformer_spec.rb +65 -0
  207. data/projects/typesystem/spec/types/sensitive_spec.rb +36 -0
  208. data/projects/typesystem/spec/types/type_spec.rb +102 -0
  209. data/projects/typesystem/spec/value/caster_spec.rb +23 -0
  210. data/projects/typesystem/spec/value/processor/runner_spec.rb +34 -0
  211. data/projects/typesystem/spec/value/processor/selection_spec.rb +71 -0
  212. data/projects/typesystem/spec/value/processor_spec.rb +76 -0
  213. data/projects/typesystem/spec/value/transformer_spec.rb +27 -0
  214. data/version.rb +1 -1
  215. metadata +487 -365
  216. data/projects/callback/lib/foobara/callback.rb +0 -1
  217. data/projects/concerns/lib/foobara/concerns.rb +0 -1
  218. data/projects/delegate/lib/foobara/delegate.rb +0 -1
  219. data/projects/entity/src/extensions/domain/domain_module_extension.rb +0 -15
  220. data/projects/enumerated/lib/foobara/enumerated.rb +0 -1
  221. data/projects/foobara/lib/foobara/all.rb +0 -48
  222. data/projects/in_memory_crud_driver_minimal/lib/foobara/in_memory_crud_driver_minimal.rb +0 -1
  223. data/projects/state_machine/lib/foobara/state_machine.rb +0 -1
  224. data/projects/value/lib/foobara/value.rb +0 -7
  225. data/projects/weak_object_set/lib/foobara/weak_object_set.rb +0 -3
  226. /data/{projects/foobara/lib → lib}/foobara.rb +0 -0
  227. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/aliases.rb +0 -0
  228. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/associations.rb +0 -0
  229. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/attributes.rb +0 -0
  230. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/equality.rb +0 -0
  231. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/initialization.rb +0 -0
  232. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/persistence.rb +0 -0
  233. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/reflection.rb +0 -0
  234. /data/projects/{detached_entity → entities/projects/detached_entity}/src/concerns/serialize.rb +0 -0
  235. /data/projects/{detached_entity → entities/projects/detached_entity}/src/detached_entity.rb +0 -0
  236. /data/projects/{detached_entity → entities/projects/detached_entity}/src/detached_entity_type.rb +0 -0
  237. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/builtin_types/detached_entity/casters/hash.rb +0 -0
  238. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/builtin_types/detached_entity/casters/primary_key.rb +0 -0
  239. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/builtin_types/detached_entity.rb +0 -0
  240. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/attributes_handler_desugarizer.rb +0 -0
  241. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/hash_desugarizer.rb +0 -0
  242. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/model_class_desugarizer.rb +0 -0
  243. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/primary_key_desugarizer.rb +0 -0
  244. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/to_type_transformer.rb +0 -0
  245. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_is_symbol.rb +0 -0
  246. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_present.rb +0 -0
  247. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_references_attribute.rb +0 -0
  248. /data/projects/{detached_entity → entities/projects/detached_entity}/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration.rb +0 -0
  249. /data/projects/{detached_entity → entities/projects/detached_entity}/src/remove_sensitive_values_transformer_extensions.rb +0 -0
  250. /data/projects/{detached_entity → entities/projects/detached_entity}/src/sensitive_type_removers/detached_entity.rb +0 -0
  251. /data/projects/{detached_entity → entities/projects/detached_entity}/src/sensitive_value_removers/detached_entity.rb +0 -0
  252. /data/projects/{entity → entities/projects/entity}/src/association_depth.rb +0 -0
  253. /data/projects/{entity → entities/projects/entity}/src/concerns/attributes.rb +0 -0
  254. /data/projects/{entity → entities/projects/entity}/src/concerns/initialization.rb +0 -0
  255. /data/projects/{entity → entities/projects/entity}/src/concerns/mutations.rb +0 -0
  256. /data/projects/{entity → entities/projects/entity}/src/concerns/persistence.rb +0 -0
  257. /data/projects/{entity → entities/projects/entity}/src/concerns/queries.rb +0 -0
  258. /data/projects/{entity → entities/projects/entity}/src/concerns/types.rb +0 -0
  259. /data/projects/{entity → entities/projects/entity}/src/entity.rb +0 -0
  260. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity/casters/hash.rb +0 -0
  261. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity/casters/primary_key.rb +0 -0
  262. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity/casters/record_from_closed_transaction.rb +0 -0
  263. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity/casters/record_from_current_transaction.rb +0 -0
  264. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity/validators/model_instance_is_valid.rb +0 -0
  265. /data/projects/{entity → entities/projects/entity}/src/extensions/builtin_types/entity.rb +0 -0
  266. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/attributes_handler_desugarizer.rb +0 -0
  267. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/hash_desugarizer.rb +0 -0
  268. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/model_class_desugarizer.rb +0 -0
  269. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/primary_key_desugarizer.rb +0 -0
  270. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/to_type_transformer.rb +0 -0
  271. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_is_symbol.rb +0 -0
  272. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_present.rb +0 -0
  273. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_references_attribute.rb +0 -0
  274. /data/projects/{entity → entities/projects/entity}/src/extensions/type_declarations/handlers/extend_entity_type_declaration.rb +0 -0
  275. /data/projects/{entity → entities/projects/entity}/src/new_prepend.rb +0 -0
  276. /data/projects/{entity → entities/projects/entity}/src/sensitive_type_removers/entity.rb +0 -0
  277. /data/projects/{entity → entities/projects/entity}/src/sensitive_value_removers/entity.rb +0 -0
  278. /data/projects/{in_memory_crud_driver → entities/projects/in_memory_crud_driver}/src/in_memory.rb +0 -0
  279. /data/projects/{model → entities/projects/model}/src/concerns/aliases.rb +0 -0
  280. /data/projects/{model → entities/projects/model}/src/concerns/reflection.rb +0 -0
  281. /data/projects/{model → entities/projects/model}/src/extensions/builtin_types/model/casters/hash.rb +0 -0
  282. /data/projects/{model → entities/projects/model}/src/extensions/builtin_types/model/supported_transformers/mutable.rb +0 -0
  283. /data/projects/{model → entities/projects/model}/src/extensions/builtin_types/model/validators/model_instance_is_valid.rb +0 -0
  284. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/array_with_symbolic_elements.rb +0 -0
  285. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/attributes_handler_desugarizer.rb +0 -0
  286. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_desugarizer.rb +0 -0
  287. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_validator.rb +0 -0
  288. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/hash_desugarizer.rb +0 -0
  289. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/model_class_desugarizer.rb +0 -0
  290. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/move_private_from_element_types_to_root.rb +0 -0
  291. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/symbolize_private.rb +0 -0
  292. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration/valid_attribute_names.rb +0 -0
  293. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_model_type_declaration.rb +0 -0
  294. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/hash_desugarizer.rb +0 -0
  295. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/model_class_type_desugarizer.rb +0 -0
  296. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/mutable_validator.rb +0 -0
  297. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/normalize_mutable_attributes_desugarizer.rb +0 -0
  298. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/to_type_transformer.rb +0 -0
  299. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration.rb +0 -0
  300. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/handlers/registered_type_declaration/model_class_desugarizer.rb +0 -0
  301. /data/projects/{model → entities/projects/model}/src/extensions/type_declarations/lazy_element_types/model.rb +0 -0
  302. /data/projects/{model → entities/projects/model}/src/sensitive_type_removers/extended_model.rb +0 -0
  303. /data/projects/{model → entities/projects/model}/src/sensitive_type_removers/model.rb +0 -0
  304. /data/projects/{model → entities/projects/model}/src/sensitive_value_removers/model.rb +0 -0
  305. /data/projects/{model_attribute_helpers → entities/projects/model_attribute_helpers}/src/attribute_helpers.rb +0 -0
  306. /data/projects/{persistence → entities/projects/persistence}/src/entity_attributes_crud_driver.rb +0 -0
  307. /data/projects/{persistence → entities/projects/persistence}/src/entity_base/table.rb +0 -0
  308. /data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction/concerns/entity_callback_handling.rb +0 -0
  309. /data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction/concerns/state_transitions.rb +0 -0
  310. /data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction.rb +0 -0
  311. /data/projects/{persistence → entities/projects/persistence}/src/entity_base/transaction_table/concerns/queries.rb +0 -0
  312. /data/projects/{persistence → entities/projects/persistence}/src/entity_base.rb +0 -0
  313. /data/projects/{persistence → entities/projects/persistence}/src/persistence.rb +0 -0
  314. /data/projects/{weak_object_set → entities/projects/weak_object_set}/src/weak_object_set.rb +0 -0
  315. /data/projects/manifest/src/{foobara/manifest/domain.rb → domain.rb} +0 -0
  316. /data/projects/manifest/src/{foobara/manifest/entity.rb → entity.rb} +0 -0
  317. /data/projects/manifest/src/{foobara/manifest/error.rb → error.rb} +0 -0
  318. /data/projects/manifest/src/{foobara/manifest/organization.rb → organization.rb} +0 -0
  319. /data/projects/manifest/src/{foobara/manifest/processor.rb → processor.rb} +0 -0
  320. /data/projects/manifest/src/{foobara/manifest/processor_class.rb → processor_class.rb} +0 -0
  321. /data/projects/manifest/src/{foobara/manifest/root_manifest.rb → root_manifest.rb} +0 -0
  322. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/README.md +0 -0
  323. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/array/casters/arrayable.rb +0 -0
  324. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/array/supported_processors/element_type_declaration.rb +0 -0
  325. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/array/supported_validators/size.rb +0 -0
  326. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/associative_array/casters/array.rb +0 -0
  327. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/associative_array/supported_processors/key_type_declaration.rb +0 -0
  328. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/associative_array/supported_processors/value_type_declaration.rb +0 -0
  329. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/atomic_duck.rb +0 -0
  330. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/casters/array.rb +0 -0
  331. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/casters/hash.rb +0 -0
  332. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_processors/element_type_declarations.rb +0 -0
  333. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/desugarizers/move_defaults_from_element_types_to_root.rb +0 -0
  334. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/desugarizers/symbolize_defaults.rb +0 -0
  335. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/type_declaration_validators/hash_with_symbolic_keys.rb +0 -0
  336. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/type_declaration_validators/valid_attribute_names.rb +0 -0
  337. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_transformers/defaults.rb +0 -0
  338. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/desugarizers/alphabetize_required.rb +0 -0
  339. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/desugarizers/move_required_from_element_types_to_root.rb +0 -0
  340. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/type_declaration_validators/array_of_symbols.rb +0 -0
  341. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/type_declaration_validators/array_with_valid_attribute_names.rb +0 -0
  342. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/supported_validators/required.rb +0 -0
  343. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/attributes/transformers/remove_unexpected_attributes.rb +0 -0
  344. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/big_decimal/casters/integer.rb +0 -0
  345. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/big_decimal/casters/string.rb +0 -0
  346. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/boolean/casters/numeric.rb +0 -0
  347. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/boolean/casters/string_or_symbol.rb +0 -0
  348. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/date/casters/hash.rb +0 -0
  349. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/date/casters/string.rb +0 -0
  350. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/datetime/casters/date.rb +0 -0
  351. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/datetime/casters/hash.rb +0 -0
  352. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/datetime/casters/seconds_since_epoch.rb +0 -0
  353. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/datetime/casters/string.rb +0 -0
  354. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_casters/allow_nil.rb +0 -0
  355. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/class_desugarizer.rb +0 -0
  356. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/class_type_desugarizer.rb +0 -0
  357. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/instance_of_class_desugarizer.rb +0 -0
  358. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/instance_of_symbol_desugarizer.rb +0 -0
  359. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/type_declaration_validators/is_valid_class.rb +0 -0
  360. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/instance_of.rb +0 -0
  361. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/one_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/cast_one_of.rb +0 -0
  362. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/one_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/module_desugarizer.rb +0 -0
  363. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck/supported_validators/one_of.rb +0 -0
  364. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duck.rb +0 -0
  365. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/duckture.rb +0 -0
  366. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/email/transformers/downcase.rb +0 -0
  367. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/email/validator_base.rb +0 -0
  368. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/float/casters/integer.rb +0 -0
  369. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/float/casters/string.rb +0 -0
  370. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/integer/casters/string.rb +0 -0
  371. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/number/supported_validators/max.rb +0 -0
  372. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/number/supported_validators/min.rb +0 -0
  373. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/string/casters/numeric.rb +0 -0
  374. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/string/casters/symbol.rb +0 -0
  375. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/string/supported_transformers/downcase.rb +0 -0
  376. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/string/supported_validators/matches.rb +0 -0
  377. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/string/supported_validators/max_length.rb +0 -0
  378. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/symbol/casters/string.rb +0 -0
  379. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/tuple/supported_processors/element_type_declarations/type_declaration_extension/extend_tuple_type_declaration/desugarizers/set_size.rb +0 -0
  380. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/tuple/supported_processors/element_type_declarations/type_declaration_extension/extend_tuple_type_declaration/type_declaration_validators/size_matches.rb +0 -0
  381. /data/projects/{builtin_types → typesystem/projects/builtin_types}/src/tuple/supported_processors/element_type_declarations.rb +0 -0
  382. /data/projects/{callback → typesystem/projects/callback}/src/block/after.rb +0 -0
  383. /data/projects/{callback → typesystem/projects/callback}/src/block/around.rb +0 -0
  384. /data/projects/{callback → typesystem/projects/callback}/src/block/before.rb +0 -0
  385. /data/projects/{callback → typesystem/projects/callback}/src/block/concerns/block_parameter_not_allowed.rb +0 -0
  386. /data/projects/{callback → typesystem/projects/callback}/src/block/concerns/block_parameter_required.rb +0 -0
  387. /data/projects/{callback → typesystem/projects/callback}/src/block/concerns/keyword_argumentable_block.rb +0 -0
  388. /data/projects/{callback → typesystem/projects/callback}/src/block/concerns/single_argument_block.rb +0 -0
  389. /data/projects/{callback → typesystem/projects/callback}/src/block/error.rb +0 -0
  390. /data/projects/{callback → typesystem/projects/callback}/src/block.rb +0 -0
  391. /data/projects/{callback → typesystem/projects/callback}/src/registry/base.rb +0 -0
  392. /data/projects/{callback → typesystem/projects/callback}/src/registry/chained_conditioned.rb +0 -0
  393. /data/projects/{callback → typesystem/projects/callback}/src/registry/chained_multiple_action.rb +0 -0
  394. /data/projects/{callback → typesystem/projects/callback}/src/registry/conditioned.rb +0 -0
  395. /data/projects/{callback → typesystem/projects/callback}/src/registry/joined_conditioned.rb +0 -0
  396. /data/projects/{callback → typesystem/projects/callback}/src/registry/multiple_action.rb +0 -0
  397. /data/projects/{callback → typesystem/projects/callback}/src/registry/single_action.rb +0 -0
  398. /data/projects/{callback → typesystem/projects/callback}/src/runner.rb +0 -0
  399. /data/projects/{common → typesystem/projects/common}/src/error_collection.rb +0 -0
  400. /data/projects/{common → typesystem/projects/common}/src/possible_error.rb +0 -0
  401. /data/projects/{common → typesystem/projects/common}/src/runtime_error.rb +0 -0
  402. /data/projects/{concerns → typesystem/projects/concerns}/src/concern.rb +0 -0
  403. /data/projects/{domain → typesystem/projects/domain}/src/organization.rb +0 -0
  404. /data/projects/{domain → typesystem/projects/domain}/src/organization_module_extension.rb +0 -0
  405. /data/projects/{enumerated → typesystem/projects/enumerated}/src/enumerated.rb +0 -0
  406. /data/projects/{enumerated → typesystem/projects/enumerated}/src/values.rb +0 -0
  407. /data/projects/{namespace → typesystem/projects/namespace}/src/ambiguous_registry.rb +0 -0
  408. /data/projects/{namespace → typesystem/projects/namespace}/src/base_registry.rb +0 -0
  409. /data/projects/{namespace → typesystem/projects/namespace}/src/extensions/module.rb +0 -0
  410. /data/projects/{namespace → typesystem/projects/namespace}/src/is_namespace.rb +0 -0
  411. /data/projects/{namespace → typesystem/projects/namespace}/src/namespace/lookup_mode.rb +0 -0
  412. /data/projects/{namespace → typesystem/projects/namespace}/src/prefixless_registry.rb +0 -0
  413. /data/projects/{namespace → typesystem/projects/namespace}/src/scoped.rb +0 -0
  414. /data/projects/{namespace → typesystem/projects/namespace}/src/unambiguous_registry.rb +0 -0
  415. /data/projects/{state_machine → typesystem/projects/state_machine}/src/log_entry.rb +0 -0
  416. /data/projects/{state_machine → typesystem/projects/state_machine}/src/state_machine.rb +0 -0
  417. /data/projects/{state_machine → typesystem/projects/state_machine}/src/transitions.rb +0 -0
  418. /data/projects/{state_machine → typesystem/projects/state_machine}/src/validations.rb +0 -0
  419. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes.rb +0 -0
  420. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes_transformers/from_yaml.rb +0 -0
  421. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes_transformers/only.rb +0 -0
  422. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes_transformers/reject.rb +0 -0
  423. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes_transformers/set.rb +0 -0
  424. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/attributes_transformers.rb +0 -0
  425. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/caster.rb +0 -0
  426. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/desugarizer.rb +0 -0
  427. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/desugarizer_pipeline.rb +0 -0
  428. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/element_processor.rb +0 -0
  429. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_array_type_declaration/array_desugarizer.rb +0 -0
  430. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_array_type_declaration/element_type_declaration_desugarizer.rb +0 -0
  431. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_array_type_declaration/to_type_transformer.rb +0 -0
  432. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_array_type_declaration/type_set_to_array_desugarizer.rb +0 -0
  433. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_array_type_declaration.rb +0 -0
  434. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_associative_array_type_declaration/key_type_declaration_desugarizer.rb +0 -0
  435. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_associative_array_type_declaration/to_type_transformer.rb +0 -0
  436. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_associative_array_type_declaration/value_type_declaration_desugarizer.rb +0 -0
  437. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_associative_array_type_declaration.rb +0 -0
  438. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_attributes_type_declaration/dsl_desugarizer.rb +0 -0
  439. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_attributes_type_declaration/element_type_declarations_desugarizer.rb +0 -0
  440. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_attributes_type_declaration/hash_desugarizer.rb +0 -0
  441. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_attributes_type_declaration/to_type_transformer.rb +0 -0
  442. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_attributes_type_declaration.rb +0 -0
  443. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_registered_type_declaration/to_type_transformer.rb +0 -0
  444. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_registered_type_declaration.rb +0 -0
  445. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_tuple_type_declaration/array_desugarizer.rb +0 -0
  446. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_tuple_type_declaration/to_type_transformer.rb +0 -0
  447. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/extend_tuple_type_declaration.rb +0 -0
  448. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/registered_type_declaration/to_type_transformer.rb +0 -0
  449. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/handlers/registered_type_declaration.rb +0 -0
  450. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/lazy_element_types/attributes.rb +0 -0
  451. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/lazy_element_types/hash.rb +0 -0
  452. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/lazy_element_types/tuple.rb +0 -0
  453. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/processor.rb +0 -0
  454. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/remove_sensitive_values_transformer.rb +0 -0
  455. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/sensitive_type_remover.rb +0 -0
  456. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/sensitive_type_removers/array.rb +0 -0
  457. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/sensitive_type_removers/attributes.rb +0 -0
  458. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/sensitive_value_removers/array.rb +0 -0
  459. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/sensitive_value_removers/attributes.rb +0 -0
  460. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/to_type_transformer.rb +0 -0
  461. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/transformer.rb +0 -0
  462. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_builder.rb +0 -0
  463. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declaration.rb +0 -0
  464. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declaration_error.rb +0 -0
  465. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declaration_handler_registry.rb +0 -0
  466. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declaration_validator.rb +0 -0
  467. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/type_declarations.rb +0 -0
  468. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/typed_transformer.rb +0 -0
  469. /data/projects/{type_declarations → typesystem/projects/type_declarations}/src/validator.rb +0 -0
  470. /data/projects/{types → typesystem/projects/types}/src/element_processor.rb +0 -0
  471. /data/projects/{types → typesystem/projects/types}/src/extensions/error.rb +0 -0
  472. /data/projects/{types → typesystem/projects/types}/src/type/concerns/reflection.rb +0 -0
  473. /data/projects/{types → typesystem/projects/types}/src/types.rb +0 -0
  474. /data/projects/{value → typesystem/projects/value}/src/caster.rb +0 -0
  475. /data/projects/{value → typesystem/projects/value}/src/data_error.rb +0 -0
  476. /data/projects/{value → typesystem/projects/value}/src/mutator.rb +0 -0
  477. /data/projects/{value → typesystem/projects/value}/src/processor/casting.rb +0 -0
  478. /data/projects/{value → typesystem/projects/value}/src/processor/multi.rb +0 -0
  479. /data/projects/{value → typesystem/projects/value}/src/processor/pipeline.rb +0 -0
  480. /data/projects/{value → typesystem/projects/value}/src/processor/runner.rb +0 -0
  481. /data/projects/{value → typesystem/projects/value}/src/processor/selection.rb +0 -0
  482. /data/projects/{value → typesystem/projects/value}/src/transformer.rb +0 -0
  483. /data/projects/{value → typesystem/projects/value}/src/validator.rb +0 -0
@@ -0,0 +1,2443 @@
1
+ RSpec.describe Foobara::CommandConnector do
2
+ after do
3
+ Foobara.reset_alls
4
+ end
5
+
6
+ let(:command_class) do
7
+ sc = ->(*args, &block) { stub_class(*args, &block) }
8
+
9
+ stub_class(:ComputeExponent, Foobara::Command) do
10
+ error_klass = sc.call(:SomeRuntimeError, Foobara::RuntimeError) do
11
+ context :duck
12
+ end
13
+
14
+ input_error_class = sc.call(:SomeInputError, Foobara::Value::DataError) do
15
+ class << self
16
+ def context_type_declaration
17
+ :duck
18
+ end
19
+ end
20
+ end
21
+
22
+ possible_error error_klass
23
+ possible_input_error :base, input_error_class
24
+
25
+ inputs exponent: :integer,
26
+ base: :integer
27
+ result :integer
28
+
29
+ attr_accessor :exponential
30
+
31
+ def execute
32
+ compute
33
+
34
+ exponential
35
+ end
36
+
37
+ def compute
38
+ self.exponential = 1
39
+
40
+ exponent.times do
41
+ self.exponential *= base
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ let(:command_connector) do
48
+ described_class.new(authenticator:, default_serializers:, default_pre_commit_transformers:)
49
+ end
50
+
51
+ let(:command_registry) { command_connector.command_registry }
52
+
53
+ let(:authenticator) { nil }
54
+ let(:default_serializers) do
55
+ [Foobara::CommandConnectors::Serializers::ErrorsSerializer,
56
+ Foobara::CommandConnectors::Serializers::JsonSerializer]
57
+ end
58
+ let(:default_pre_commit_transformers) { nil }
59
+
60
+ let(:base) { 2 }
61
+ let(:exponent) { 3 }
62
+
63
+ let(:response) { command_connector.run(full_command_name:, action:, inputs:) }
64
+ let(:parsed_response) { JSON.parse(response.body) }
65
+
66
+ let(:action) { "run" }
67
+ let(:full_command_name) { "ComputeExponent" }
68
+ let(:inputs) do
69
+ {
70
+ base:,
71
+ exponent:
72
+ }
73
+ end
74
+ let(:inputs_transformers) { nil }
75
+ let(:result_transformers) { nil }
76
+ let(:inputs_transformers_sugar) { nil }
77
+ let(:result_transformers_sugar) { nil }
78
+ let(:errors_transformers) { nil }
79
+ let(:pre_commit_transformers) { nil }
80
+ let(:serializers) { nil }
81
+ let(:response_mutators) { nil }
82
+ let(:request_mutators) { nil }
83
+ let(:allowed_rule) { nil }
84
+ let(:allowed_rules) { nil }
85
+ let(:requires_authentication) { nil }
86
+ let(:capture_unknown_error) { false }
87
+ let(:aggregate_entities) { nil }
88
+ let(:atomic_entities) { nil }
89
+
90
+ describe "#connect" do
91
+ context "when command is connected before it is loaded" do
92
+ let(:command_class) do
93
+ stub_module :SomeOrg do
94
+ foobara_organization!
95
+ end
96
+
97
+ stub_module("SomeOrg::SomeDomain") do
98
+ foobara_domain!
99
+ end
100
+
101
+ stub_class "SomeOrg::SomeDomain::SomeCommand", Foobara::Command do
102
+ description "just some command"
103
+ end
104
+ end
105
+
106
+ it "registers the command" do
107
+ command_connector.connect("SomeOrg::SomeDomain::SomeCommand")
108
+
109
+ command_class
110
+
111
+ exposed_commands = command_connector.all_exposed_commands
112
+ expect(exposed_commands.size).to eq(1)
113
+ exposed_command = exposed_commands.first
114
+ transformed_command = exposed_command.transformed_command_class
115
+
116
+ expect(transformed_command.command_class).to eq(command_class)
117
+ expect(transformed_command.new.inputs).to eq({})
118
+ end
119
+ end
120
+
121
+ context "when command is in an organization" do
122
+ let!(:org_module) do
123
+ stub_module :SomeOrg do
124
+ foobara_organization!
125
+ end
126
+ end
127
+
128
+ let!(:domain_module) do
129
+ stub_module("SomeOrg::SomeDomain") do
130
+ foobara_domain!
131
+ end
132
+ end
133
+
134
+ let!(:command_class) do
135
+ stub_module "SomeOtherOrg" do
136
+ foobara_organization!
137
+ end
138
+ stub_module "SomeOtherOrg::SomeOtherDomain" do
139
+ foobara_domain!
140
+ end
141
+ stub_class "SomeOtherOrg::SomeOtherDomain::SomeOtherCommand", Foobara::Command do
142
+ inputs email: :email
143
+ end
144
+ stub_class "SomeOrg::SomeDomain::SomeCommand", Foobara::Command do
145
+ description "just some command"
146
+ depends_on SomeOtherOrg::SomeOtherDomain::SomeOtherCommand
147
+ end
148
+ end
149
+
150
+ it "registers the command" do
151
+ command_connector.connect(org_module)
152
+
153
+ expect(command_connector.command_connected?(SomeOrg::SomeDomain::SomeCommand)).to be true
154
+
155
+ exposed_commands = command_connector.all_exposed_commands
156
+ expect(exposed_commands.size).to eq(1)
157
+ exposed_command = exposed_commands.first
158
+
159
+ expect(exposed_command.full_command_symbol).to eq(:"some_org::some_domain::some_command")
160
+
161
+ transformed_command = exposed_command.transformed_command_class
162
+ expect(transformed_command.command_class).to eq(command_class)
163
+
164
+ command_classes = []
165
+
166
+ command_registry.each_transformed_command_class do |klass|
167
+ command_classes << klass
168
+ end
169
+
170
+ expect(command_classes).to eq([transformed_command])
171
+ expect(command_registry.all_transformed_command_classes).to eq([transformed_command])
172
+ end
173
+
174
+ context "when registering via domain" do
175
+ before do
176
+ command_connector.connect(domain_module)
177
+ end
178
+
179
+ it "registers the command" do
180
+ transformed_commands = command_connector.all_transformed_command_classes
181
+ expect(transformed_commands.size).to eq(1)
182
+ expect(transformed_commands.first.command_class).to eq(command_class)
183
+ end
184
+
185
+ context "when generating a manifest" do
186
+ it "includes the organization" do
187
+ manifest = command_connector.foobara_manifest
188
+
189
+ expect(manifest[:organization].keys).to contain_exactly(:SomeOrg, :global_organization)
190
+ expect(manifest[:command][:"SomeOrg::SomeDomain::SomeCommand"][:description]).to eq("just some command")
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ context "when connecting a command that returns sensitive data" do
197
+ before do
198
+ stub_module :SomeOrg do
199
+ foobara_organization!
200
+ end
201
+
202
+ stub_module("SomeOrg::SomeDomain") do
203
+ foobara_domain!
204
+ end
205
+
206
+ stub_class "SomeOrg::SomeDomain::User", Foobara::Entity do
207
+ attributes do
208
+ id :integer
209
+ password :string, :allow_nil, :sensitive
210
+ username :string, :required
211
+ foo :array do
212
+ bar :string, :sensitive, :allow_nil
213
+ baz :string
214
+ end
215
+ end
216
+
217
+ primary_key :id
218
+ end
219
+
220
+ stub_class "SomeOrg::SomeDomain::CreateUser", Foobara::Command do
221
+ inputs do
222
+ username :string, :required
223
+ password :string, :sensitive
224
+ foo :array do
225
+ bar :string, :sensitive, :allow_nil
226
+ baz :string
227
+ end
228
+ end
229
+
230
+ result SomeOrg::SomeDomain::User
231
+
232
+ def execute
233
+ SomeOrg::SomeDomain::User.create(inputs)
234
+ end
235
+ end
236
+
237
+ stub_class "SomeOrg::SomeDomain::Login", Foobara::Command do
238
+ inputs do
239
+ username :string, :required
240
+ password :string, :required, :sensitive
241
+ end
242
+
243
+ result :string
244
+ end
245
+ end
246
+
247
+ it "does not include sensitive input types in the manifest" do
248
+ command_connector.connect(SomeOrg::SomeDomain::CreateUser)
249
+ command_connector.connect(SomeOrg::SomeDomain::Login)
250
+
251
+ manifest = command_connector.foobara_manifest
252
+
253
+ user_manifest = manifest[:type][:"SomeOrg::SomeDomain::User"]
254
+ declaration_data = user_manifest[:declaration_data]
255
+
256
+ expect(
257
+ declaration_data[:attributes_declaration][:element_type_declarations].keys
258
+ ).to contain_exactly(:username, :id, :foo)
259
+ end
260
+
261
+ context "when running a command that returns sensitive values" do
262
+ let(:full_command_name) { "SomeOrg::SomeDomain::CreateUser" }
263
+ let(:inputs) do
264
+ {
265
+ username: "foo",
266
+ password: "bar",
267
+ foo: [{ bar: "bar", baz: "baz" }, { baz: "baz2" }]
268
+ }
269
+ end
270
+
271
+ let(:serializers) { Foobara::CommandConnectors::Serializers::AggregateSerializer }
272
+ let(:pre_commit_transformers) { Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer }
273
+
274
+ before do
275
+ Foobara::Persistence.default_crud_driver = Foobara::Persistence::CrudDrivers::InMemory.new
276
+ command_connector.connect(SomeOrg::SomeDomain::CreateUser, serializers:, pre_commit_transformers:)
277
+ end
278
+
279
+ it "does not include sensitive output values in the result" do
280
+ expect(response.status).to be(0)
281
+ expect(response.error).to be_nil
282
+ expect(JSON.parse(response.body)).to eq(
283
+ "id" => 1,
284
+ "username" => "foo",
285
+ "foo" => [
286
+ { "baz" => "baz" },
287
+ { "baz" => "baz2" }
288
+ ]
289
+ )
290
+ end
291
+
292
+ context "when command can return a thunk" do
293
+ let(:serializers) { [] }
294
+ let(:full_command_name) { "SomeOrg::SomeDomain::FindUser" }
295
+ let(:user) do
296
+ SomeOrg::SomeDomain::CreateUser.run!(
297
+ username: "foo",
298
+ password: "bar",
299
+ foo: [{ bar: "bar", baz: "baz" }, { baz: "baz2" }]
300
+ )
301
+ end
302
+ let(:user_id) do
303
+ user.id
304
+ end
305
+ let(:some_other_user) do
306
+ SomeOrg::SomeDomain::CreateUser.run!(
307
+ username: "baz",
308
+ password: "baz",
309
+ foo: [{ bar: "baz", baz: "baz" }, { baz: "baz" }]
310
+ )
311
+ end
312
+ let(:inputs) do
313
+ { user: user_id }
314
+ end
315
+ let(:pre_commit_transformers) { [] }
316
+
317
+ before do
318
+ some_other_user_id = some_other_user.id
319
+
320
+ stub_class "SomeOrg::SomeDomain::FindUser", Foobara::Command do
321
+ inputs do
322
+ user SomeOrg::SomeDomain::User
323
+ end
324
+
325
+ result SomeOrg::SomeDomain::User
326
+
327
+ define_method :execute do
328
+ SomeOrg::SomeDomain::User.thunk(some_other_user_id)
329
+ end
330
+ end
331
+
332
+ command_connector.connect(SomeOrg::SomeDomain::FindUser)
333
+ end
334
+
335
+ it "returns a thunk but in the command connector's namespace" do
336
+ expect(response.status).to be(0)
337
+ expect(JSON.parse(response.body)).to eq(some_other_user.id)
338
+ end
339
+ end
340
+
341
+ context "when nothing in the array needs to be changed" do
342
+ let(:inputs) do
343
+ {
344
+ username: "foo",
345
+ password: "bar",
346
+ foo: [{ baz: "baz" }, { baz: "baz2" }]
347
+ }
348
+ end
349
+
350
+ it "does not include sensitive output values in the result" do
351
+ expect(response.status).to be(0)
352
+ expect(JSON.parse(response.body)).to eq(
353
+ "id" => 1,
354
+ "username" => "foo",
355
+ "foo" => [
356
+ { "baz" => "baz" },
357
+ { "baz" => "baz2" }
358
+ ]
359
+ )
360
+ end
361
+ end
362
+
363
+ context "when nothing in the record needs to be changed" do
364
+ let(:inputs) do
365
+ {
366
+ username: "foo",
367
+ foo: []
368
+ }
369
+ end
370
+
371
+ it "does not include sensitive output values in the result" do
372
+ expect(response.status).to be(0)
373
+ expect(JSON.parse(response.body)).to eq(
374
+ "id" => 1,
375
+ "username" => "foo",
376
+ "foo" => []
377
+ )
378
+ end
379
+ end
380
+
381
+ context "when entity is a detached entity" do
382
+ let(:full_command_name) { "BuildSomeDetachedEntity" }
383
+ let(:inputs) do
384
+ {
385
+ id: 1,
386
+ username: "foo",
387
+ password: "bar",
388
+ foo: [{ bar: "bar", baz: "baz" }, { baz: "baz2" }]
389
+ }
390
+ end
391
+
392
+ before do
393
+ stub_class "SomeDetachedEntity", Foobara::DetachedEntity do
394
+ attributes do
395
+ id :integer
396
+ password :string, :allow_nil, :sensitive
397
+ username :string, :required
398
+ foo :array do
399
+ bar :string, :sensitive, :allow_nil
400
+ baz :string
401
+ end
402
+ end
403
+
404
+ primary_key :id
405
+ end
406
+
407
+ stub_class "BuildSomeDetachedEntity", Foobara::Command do
408
+ inputs do
409
+ id :integer, :required
410
+ username :string, :required
411
+ password :string, :sensitive
412
+ foo :array do
413
+ bar :string, :sensitive, :allow_nil
414
+ baz :string
415
+ end
416
+ end
417
+
418
+ result SomeDetachedEntity
419
+
420
+ def execute
421
+ SomeDetachedEntity.new(inputs)
422
+ end
423
+ end
424
+
425
+ command_connector.connect(BuildSomeDetachedEntity, serializers:, pre_commit_transformers:)
426
+ end
427
+
428
+ it "does not include sensitive output values in the result" do
429
+ expect(response.status).to be(0)
430
+ expect(JSON.parse(response.body)).to eq(
431
+ "id" => 1,
432
+ "username" => "foo",
433
+ "foo" => [
434
+ { "baz" => "baz" },
435
+ { "baz" => "baz2" }
436
+ ]
437
+ )
438
+ end
439
+ end
440
+ end
441
+
442
+ describe "#all_exposed_command_names" do
443
+ it "includes all of the exposed command names" do
444
+ command_connector.connect(SomeOrg::SomeDomain::CreateUser)
445
+ expect(command_connector.all_exposed_command_names).to eq(["SomeOrg::SomeDomain::CreateUser"])
446
+ end
447
+ end
448
+
449
+ describe "#all_exposed_type_names" do
450
+ it "includes the entity names" do
451
+ command_connector.connect(SomeOrg::SomeDomain::CreateUser)
452
+ expect(command_connector.all_exposed_type_names).to include("SomeOrg::SomeDomain::User")
453
+ end
454
+ end
455
+ end
456
+ end
457
+
458
+ describe "#run_command" do
459
+ before do
460
+ if allowed_rules
461
+ command_connector.allowed_rules(allowed_rules)
462
+ end
463
+
464
+ command_connector.connect(
465
+ command_class,
466
+ inputs: inputs_transformers_sugar,
467
+ result: result_transformers_sugar,
468
+ inputs_transformers:,
469
+ result_transformers:,
470
+ errors_transformers:,
471
+ serializers:,
472
+ response: response_mutators,
473
+ request: request_mutators,
474
+ allowed_rule:,
475
+ requires_authentication:,
476
+ pre_commit_transformers:,
477
+ capture_unknown_error:,
478
+ aggregate_entities:,
479
+ atomic_entities:,
480
+ suffix:
481
+ )
482
+ end
483
+
484
+ let(:suffix) { nil }
485
+
486
+ it "runs the command" do
487
+ expect(response.status).to be(0)
488
+ expect(response.body).to eq("8")
489
+ end
490
+
491
+ context "with default transformers" do
492
+ before do
493
+ identity = proc { |x| x }
494
+
495
+ command_connector.add_default_inputs_transformer(identity)
496
+ command_connector.add_default_result_transformer(identity)
497
+ command_connector.add_default_errors_transformer(identity)
498
+ command_connector.add_default_pre_commit_transformer(identity)
499
+ end
500
+
501
+ it "runs the command" do
502
+ expect(response.status).to be(0)
503
+ expect(response.body).to eq("8")
504
+ end
505
+ end
506
+
507
+ context "without serializers" do
508
+ let(:default_serializers) { nil }
509
+ # Setting a suffix guarantees it will be transformed
510
+ let(:suffix) { "Whatever" }
511
+ let(:full_command_name) { "ComputeExponentWhatever" }
512
+
513
+ it "runs the command" do
514
+ expect(response.status).to be(0)
515
+ expect(response.body).to eq(8)
516
+ end
517
+ end
518
+
519
+ context "when inputs are bad" do
520
+ let(:inputs) { { some_bad_input: 10, exponent: } }
521
+
522
+ let(:default_serializers) { Foobara::CommandConnectors::Serializers::JsonSerializer }
523
+ let(:serializers) { Foobara::CommandConnectors::Serializers::ErrorsSerializer }
524
+
525
+ it "fails" do
526
+ expect(response.status).to be(1)
527
+
528
+ error = JSON.parse(response.body).find { |e| e["key"] == "data.unexpected_attributes" }
529
+ unexpected_attributes = error["context"]["unexpected_attributes"]
530
+
531
+ expect(unexpected_attributes).to eq(["some_bad_input"])
532
+ end
533
+ end
534
+
535
+ context "when command doesn't exist" do
536
+ let(:full_command_name) { "DoesNotExist" }
537
+
538
+ it "raises" do
539
+ expect {
540
+ response
541
+ }.to raise_error(Foobara::CommandConnector::NoCommandFoundError)
542
+ end
543
+ end
544
+
545
+ context "when unknown error" do
546
+ let(:capture_unknown_error) { true }
547
+ let(:default_serializers) do
548
+ [
549
+ Foobara::CommandConnectors::Serializers::ErrorsSerializer,
550
+ Foobara::CommandConnectors::Serializers::JsonSerializer
551
+ ]
552
+ end
553
+
554
+ before do
555
+ command_class.define_method :execute do
556
+ raise "kaboom!"
557
+ end
558
+ end
559
+
560
+ it "fails" do
561
+ expect(response.status).to be(1)
562
+
563
+ error = JSON.parse(response.body).find { |e| e["key"] == "runtime.unknown" }
564
+
565
+ expect(error["message"]).to eq("kaboom!")
566
+ expect(error["is_fatal"]).to be(true)
567
+ end
568
+
569
+ context "when not capturing unknown errors when connecting the command" do
570
+ let(:capture_unknown_error) { false }
571
+
572
+ it "explodes" do
573
+ expect { response }.to raise_error("kaboom!")
574
+ end
575
+
576
+ context "when capturing all unknown errors" do
577
+ let(:command_connector) do
578
+ described_class.new(authenticator:, default_serializers:, capture_unknown_error: true)
579
+ end
580
+
581
+ it "fails" do
582
+ expect(response.status).to be(1)
583
+
584
+ error = JSON.parse(response.body).find { |e| e["key"] == "runtime.unknown" }
585
+
586
+ expect(error["message"]).to eq("kaboom!")
587
+ expect(error["is_fatal"]).to be(true)
588
+ end
589
+ end
590
+ end
591
+ end
592
+
593
+ context "with a response mutator" do
594
+ let(:full_command_name) { "SomeCommand" }
595
+ let(:response_mutators) { [response_mutator] }
596
+ let(:response_mutator) { response_mutator_class }
597
+
598
+ let(:command_class) do
599
+ stub_class(:SomeCommand, Foobara::Command) do
600
+ inputs value: :string
601
+ result foo: :string, bar: :string
602
+
603
+ def execute
604
+ { foo: "foo #{value}", bar: "bar #{value}" }
605
+ end
606
+ end
607
+ end
608
+
609
+ let(:inputs) { { value: "some value" } }
610
+
611
+ let(:response_mutator_class) do
612
+ stub_class("ChangeBarToBazMutator", Foobara::CommandConnectors::ResponseMutator) do
613
+ def result_type_declaration_from(result_type)
614
+ new_declaration = Foobara::Util.deep_dup(result_type.declaration_data)
615
+ element_type_declarations = new_declaration[:element_type_declarations]
616
+
617
+ old_bar = element_type_declarations.delete(:bar)
618
+
619
+ element_type_declarations[:baz] = old_bar
620
+
621
+ new_declaration
622
+ end
623
+
624
+ def mutate(response)
625
+ bar = response.body.delete(:bar)
626
+ response.body[:baz] = bar
627
+ end
628
+ end
629
+ end
630
+
631
+ it "mutates the response and gives an expected mutated result type in the manifest" do
632
+ manifest = command_connector.foobara_manifest
633
+
634
+ expect(manifest[:command][:SomeCommand][:response_mutators]).to eq(["ChangeBarToBazMutator"])
635
+
636
+ expect(manifest[:command][:SomeCommand][:result_type]).to eq(
637
+ type: :attributes,
638
+ element_type_declarations: {
639
+ baz: :string,
640
+ foo: :string
641
+ }
642
+ )
643
+ expect(JSON.parse(response.body)).to eq("foo" => "foo some value", "baz" => "bar some value")
644
+ end
645
+
646
+ describe "#foobara_manifest" do
647
+ context "when mutator is an instance" do
648
+ let(:response_mutator) { response_mutator_class.new(true) }
649
+
650
+ it "mutates the response and gives an expected mutated result type in the manifest" do
651
+ manifest = command_connector.foobara_manifest
652
+ expect(manifest[:command][:SomeCommand][:response_mutators]).to eq(["ChangeBarToBazMutator"])
653
+ end
654
+
655
+ context "when mutator is an anonymous instance" do
656
+ let(:response_mutator_class) do
657
+ Class.new(Foobara::CommandConnectors::ResponseMutator) do
658
+ def result_type_declaration_from(result_type)
659
+ result_type
660
+ end
661
+ end
662
+ end
663
+
664
+ it "mutates the response and gives an expected mutated result type in the manifest" do
665
+ manifest = command_connector.foobara_manifest
666
+ expect(manifest[:command][:SomeCommand][:response_mutators].first).to be_a(String)
667
+ end
668
+ end
669
+ end
670
+ end
671
+
672
+ context "with two mutators" do
673
+ let(:response_mutators) { [response_mutator_class, another_mutator] }
674
+
675
+ let(:another_mutator) do
676
+ stub_class("RemoveFooMutator", Foobara::CommandConnectors::ResponseMutator) do
677
+ def result_type_declaration_from(result_type)
678
+ Foobara::TypeDeclarations::Attributes.reject(result_type.declaration_data, :foo)
679
+ end
680
+
681
+ def mutate(response)
682
+ response.body.delete(:foo)
683
+ end
684
+ end
685
+ end
686
+
687
+ it "mutates the response and gives an expected mutated result type in the manifest" do
688
+ manifest = command_connector.foobara_manifest
689
+
690
+ expect(manifest[:command][:SomeCommand][:result_type]).to eq(
691
+ type: :attributes,
692
+ element_type_declarations: {
693
+ baz: :string
694
+ }
695
+ )
696
+ expect(JSON.parse(response.body)).to eq("baz" => "bar some value")
697
+ end
698
+ end
699
+ end
700
+
701
+ context "with a request mutator" do
702
+ let(:full_command_name) { "SomeCommand" }
703
+ let(:request_mutators) { [request_mutator_class] }
704
+
705
+ let(:command_class) do
706
+ stub_class(:SomeCommand, Foobara::Command) do
707
+ inputs foo: :string, baz: :string
708
+ result :duck
709
+
710
+ def execute
711
+ inputs
712
+ end
713
+ end
714
+ end
715
+
716
+ let(:inputs) { { foo: "foo some value", bar: "bar some value" } }
717
+
718
+ let(:request_mutator_class) do
719
+ stub_class("ChangeBarToBazMutator", Foobara::CommandConnectors::RequestMutator) do
720
+ def inputs_type_declaration_from(inputs_type)
721
+ new_declaration = Foobara::Util.deep_dup(inputs_type.declaration_data)
722
+ element_type_declarations = new_declaration[:element_type_declarations]
723
+
724
+ old_bar = element_type_declarations.delete(:baz)
725
+
726
+ element_type_declarations[:bar] = old_bar
727
+
728
+ new_declaration
729
+ end
730
+
731
+ def mutate(request)
732
+ inputs = Foobara::Util.deep_dup(request.inputs)
733
+ bar = inputs.delete(:bar)
734
+ request.inputs = inputs.merge(baz: bar)
735
+ end
736
+ end
737
+ end
738
+
739
+ it "mutates the response and gives an expected mutated inputs type in the manifest" do
740
+ manifest = command_connector.foobara_manifest
741
+
742
+ expect(manifest[:command][:SomeCommand][:inputs_type]).to eq(
743
+ type: :attributes,
744
+ element_type_declarations: {
745
+ bar: :string,
746
+ foo: :string
747
+ }
748
+ )
749
+
750
+ expect(JSON.parse(response.body)).to eq("foo" => "foo some value", "baz" => "bar some value")
751
+ end
752
+
753
+ context "with two mutators" do
754
+ let(:request_mutators) { [request_mutator_class, another_mutator] }
755
+
756
+ let(:another_mutator) do
757
+ stub_class("RemoveFoo2Mutator", Foobara::CommandConnectors::RequestMutator) do
758
+ def inputs_type_declaration_from(inputs_type)
759
+ with_foo2 = Foobara::Domain.current.foobara_type_from_declaration(foo2: :integer)
760
+ Foobara::TypeDeclarations::Attributes.merge(inputs_type.declaration_data, with_foo2.declaration_data)
761
+ end
762
+
763
+ def mutate(response)
764
+ inputs = response.inputs.dup
765
+ inputs.delete(:foo2)
766
+ response.inputs = inputs
767
+ end
768
+ end
769
+ end
770
+
771
+ it "mutates the request and gives an expected mutated inputs type in the manifest" do
772
+ manifest = Foobara::TypeDeclarations.with_manifest_context(include_processors: true) do
773
+ command_connector.foobara_manifest
774
+ end
775
+
776
+ expect(manifest[:command][:SomeCommand][:inputs_type]).to eq(
777
+ type: :attributes,
778
+ element_type_declarations: {
779
+ foo2: :integer,
780
+ foo: :string,
781
+ bar: :string
782
+ }
783
+ )
784
+
785
+ expect(JSON.parse(response.body)).to eq("baz" => "bar some value", "foo" => "foo some value")
786
+ end
787
+ end
788
+ end
789
+
790
+ context "with various transformers" do
791
+ let(:inputs) { { bbaassee: base, exponent: } }
792
+
793
+ let(:inputs_transformers) { [inputs_transformer] }
794
+ let(:inputs_transformer) do
795
+ stub_class(:RandomTransformer, Foobara::Value::Transformer) do
796
+ def transform(inputs)
797
+ {
798
+ base: inputs[:bbaassee],
799
+ exponent: inputs[:exponent]
800
+ }
801
+ end
802
+ end
803
+ end
804
+
805
+ let(:result_transformers) { [->(result) { result * 2 }] }
806
+ let(:errors_transformers) { [->(errors) { errors }] }
807
+
808
+ it "runs the command" do
809
+ expect(response.status).to be(0)
810
+ expect(response.body).to eq("16")
811
+ end
812
+
813
+ context "when error" do
814
+ let(:inputs) { { foo: "bar", exponent: } }
815
+
816
+ it "is not success" do
817
+ expect(response.status).to be(1)
818
+ expect(response.body).to include("cannot_cast")
819
+ end
820
+ end
821
+
822
+ context "with multiple transformers" do
823
+ let(:identity) { ->(x) { x } }
824
+
825
+ let(:inputs_transformers) { [inputs_transformer] }
826
+ let(:inputs_transformers_sugar) { identity }
827
+ let(:result_transformers) { [->(result) { result * 2 }, identity] }
828
+ let(:errors_transformers) { [identity, identity] }
829
+ let(:pre_commit_transformers) { [identity, identity] }
830
+
831
+ it "runs the command and has the expected inputs type" do
832
+ expect(response.status).to be(0)
833
+ expect(response.body).to eq("16")
834
+
835
+ transformed_command = command_connector.transformed_command_from_name("ComputeExponent")
836
+ expect(transformed_command.inputs_type.declaration_data).to eq(
837
+ type: :attributes,
838
+ element_type_declarations: {
839
+ base: :integer,
840
+ exponent: :integer
841
+ }
842
+ )
843
+ end
844
+
845
+ context "with a typed transformer" do
846
+ let(:identity) do
847
+ stub_class "IdentityTransformer", Foobara::TypeDeclarations::TypedTransformer do
848
+ def from_type_declaration
849
+ to_type
850
+ end
851
+
852
+ def transform(value)
853
+ value
854
+ end
855
+ end
856
+ end
857
+
858
+ it "runs the command and has the expected inputs type" do
859
+ expect(response.status).to be(0)
860
+ expect(response.body).to eq("16")
861
+
862
+ transformed_command = command_connector.transformed_command_from_name("ComputeExponent")
863
+ expect(transformed_command.inputs_type.declaration_data).to eq(
864
+ type: :attributes,
865
+ element_type_declarations: {
866
+ base: :integer,
867
+ exponent: :integer
868
+ }
869
+ )
870
+ end
871
+ end
872
+
873
+ context "when error" do
874
+ let(:inputs) { { foo: "bar", exponent: } }
875
+
876
+ it "is not success" do
877
+ expect(response.status).to be(1)
878
+ expect(response.body).to include("cannot_cast")
879
+ end
880
+ end
881
+ end
882
+
883
+ context "with transformer instance instead of class" do
884
+ let(:inputs_transformers) { [inputs_transformer.instance] }
885
+
886
+ it "runs the command" do
887
+ expect(response.status).to be(0)
888
+ expect(response.body).to eq("16")
889
+ end
890
+ end
891
+ end
892
+
893
+ context "with allowed rule" do
894
+ context "when declared with a hash" do
895
+ let(:allowed_rule) do
896
+ logic = proc {
897
+ raise unless respond_to?(:base)
898
+
899
+ base == 2
900
+ }
901
+
902
+ {
903
+ logic:,
904
+ symbol: :must_be_base_2
905
+ }
906
+ end
907
+
908
+ context "when allowed" do
909
+ it "runs the command" do
910
+ expect(response.status).to be(0)
911
+ expect(response.body).to eq("8")
912
+ end
913
+ end
914
+
915
+ context "when not allowed" do
916
+ let(:allowed_rule) do
917
+ logic = proc { base == 1900 }
918
+
919
+ {
920
+ logic:,
921
+ symbol: :must_be_base_1900,
922
+ explanation: proc { "Must be 1900 but was #{base}" }
923
+ }
924
+ end
925
+
926
+ it "fails with 403 and relevant error" do
927
+ expect(response.status).to be(1)
928
+ expect(JSON.parse(response.body).find { |e|
929
+ e["key"] == "runtime.not_allowed"
930
+ }["message"]).to eq(
931
+ "Not allowed: Must be 1900 but was 2"
932
+ )
933
+ end
934
+ end
935
+ end
936
+
937
+ context "when declared with the rule registry" do
938
+ let(:allowed_rules) do
939
+ {
940
+ must_be_base_2: {
941
+ logic: proc { base == 2 },
942
+ explanation: "Must be base 2"
943
+ },
944
+ must_be_base_1900: {
945
+ logic: proc { base == 1900 },
946
+ explanation: proc { "Must be base 1900 but was #{base}" }
947
+ }
948
+ }
949
+ end
950
+
951
+ context "when allowed" do
952
+ let(:allowed_rule) { [:must_be_base_1900, "must_be_base_2"] }
953
+
954
+ it "runs the command" do
955
+ expect(response.status).to be(0)
956
+ expect(response.body).to eq("8")
957
+ end
958
+
959
+ describe "#manifest" do
960
+ it "contains the errors for not allowed" do
961
+ error_manifest = command_connector.foobara_manifest[:command][:ComputeExponent][:possible_errors]
962
+
963
+ expect(error_manifest.keys).to include("runtime.not_allowed")
964
+ end
965
+ end
966
+ end
967
+
968
+ context "when not allowed" do
969
+ let(:allowed_rule) do
970
+ :must_be_base_1900
971
+ end
972
+
973
+ it "fails with 401 and relevant error" do
974
+ expect(command_connector.command_registry[ComputeExponent].command_class).to eq(ComputeExponent)
975
+
976
+ expect(response.status).to be(1)
977
+ expect(JSON.parse(response.body).find { |e|
978
+ e["key"] == "runtime.not_allowed"
979
+ }["message"]).to eq(
980
+ "Not allowed: Must be base 1900 but was 2"
981
+ )
982
+ end
983
+ end
984
+ end
985
+
986
+ context "when declared with a proc" do
987
+ context "without explanation" do
988
+ let(:allowed_rule) do
989
+ proc { base == 1900 }
990
+ end
991
+ let(:authenticator) do
992
+ # normally we would return a user but we'll just use junk
993
+ proc { Object.new }
994
+ end
995
+
996
+ it "fails with 401 and relevant error" do
997
+ expect(response.status).to be(1)
998
+ expect(response.outcome).to_not be_success
999
+ expect(
1000
+ JSON.parse(response.body).find { |e| e["key"] == "runtime.not_allowed" }["message"]
1001
+ ).to match(/not.?allowed/i)
1002
+ end
1003
+
1004
+ describe "#foobara_manifest" do
1005
+ it "contains info about the authenticator" do
1006
+ manifest = command_connector.foobara_manifest
1007
+ expect(manifest[:command][:ComputeExponent][:authenticator]).to eq(
1008
+ symbol: :authenticator,
1009
+ explanation: :authenticator
1010
+ )
1011
+ end
1012
+ end
1013
+ end
1014
+ end
1015
+
1016
+ context "when authentication required" do
1017
+ let(:requires_authentication) { true }
1018
+ let(:authenticator) do
1019
+ proc {}
1020
+ end
1021
+
1022
+ describe "#manifest" do
1023
+ it "contains the errors for not allowed" do
1024
+ error_manifest = command_connector.foobara_manifest[:command][:ComputeExponent][:possible_errors]
1025
+
1026
+ expect(error_manifest.keys).to include("runtime.unauthenticated")
1027
+ end
1028
+ end
1029
+
1030
+ context "when unauthenticated" do
1031
+ it "is 1" do
1032
+ expect(response.status).to be(1)
1033
+ expect(JSON.parse(response.body).map { |e| e["key"] }).to include("runtime.unauthenticated")
1034
+ end
1035
+ end
1036
+
1037
+ context "when authenticated" do
1038
+ let(:authenticator) do
1039
+ # normally we would return a user but we'll just generate a pointless integer
1040
+ # to test proxying to the request
1041
+ proc { full_command_name.length }
1042
+ end
1043
+
1044
+ let(:default_serializers) do
1045
+ [Foobara::CommandConnectors::Serializers::JsonSerializer]
1046
+ end
1047
+
1048
+ it "is 200" do
1049
+ expect(response.status).to be(0)
1050
+ expect(JSON.parse(response.body)).to eq(8)
1051
+ end
1052
+ end
1053
+ end
1054
+
1055
+ context "with an entity input" do
1056
+ before do
1057
+ Foobara::Persistence.default_crud_driver = Foobara::Persistence::CrudDrivers::InMemory.new
1058
+ end
1059
+
1060
+ let(:default_pre_commit_transformers) do
1061
+ [
1062
+ Foobara::CommandConnectors::Transformers::LoadAtomsPreCommitTransformer
1063
+ ]
1064
+ end
1065
+
1066
+ let(:command_class) do
1067
+ user_class
1068
+
1069
+ stub_class(:QueryUser, Foobara::Command) do
1070
+ inputs user: User
1071
+ result :User
1072
+
1073
+ load_all
1074
+
1075
+ def execute
1076
+ user
1077
+ end
1078
+ end
1079
+ end
1080
+
1081
+ let(:full_command_name) { "QueryUser" }
1082
+ let(:inputs) { { user: user_id } }
1083
+
1084
+ let(:user_class) do
1085
+ stub_class(:User, Foobara::Entity) do
1086
+ attributes id: :integer,
1087
+ name: :string,
1088
+ ratings: [:integer],
1089
+ junk: { type: :associative_array, value_type_declaration: :array }
1090
+ primary_key :id
1091
+ end
1092
+ end
1093
+
1094
+ context "when user exists" do
1095
+ let(:user_id) do
1096
+ User.transaction do
1097
+ User.create(name: :whatever)
1098
+ end.id
1099
+ end
1100
+
1101
+ let(:result_transformers) { [proc(&:attributes)] }
1102
+
1103
+ it "finds the user" do
1104
+ expect(response.status).to be(0)
1105
+ expect(JSON.parse(response.body)).to eq("id" => user_id, "name" => "whatever")
1106
+ end
1107
+ end
1108
+
1109
+ context "when not found error" do
1110
+ let(:user_id) { 100 }
1111
+
1112
+ it "fails" do
1113
+ expect(response.status).to be(1)
1114
+
1115
+ errors = JSON.parse(response.body)
1116
+
1117
+ expect(errors.size).to eq(1)
1118
+ expect(errors.map { |e| e["key"] }).to include("runtime.user_not_found")
1119
+ end
1120
+ end
1121
+
1122
+ context "with an association" do
1123
+ let(:point_class) do
1124
+ stub_class :Point, Foobara::Model do
1125
+ attributes x: :integer, y: :integer
1126
+ end
1127
+ end
1128
+
1129
+ let(:referral_class) do
1130
+ stub_class(:Referral, Foobara::Entity) do
1131
+ attributes id: :integer, email: :email
1132
+ primary_key :id
1133
+ end
1134
+ end
1135
+
1136
+ before do
1137
+ User.attributes referral: referral_class, point: point_class
1138
+ end
1139
+
1140
+ context "with atomic_entities: true" do
1141
+ let(:atomic_entities) { true }
1142
+
1143
+ it "includes the AtomicSerializer" do
1144
+ command_manifest = command_connector.foobara_manifest[:command][:QueryUser]
1145
+ expect(
1146
+ command_manifest[:serializers]
1147
+ ).to include("Foobara::CommandConnectors::Serializers::AtomicSerializer")
1148
+ end
1149
+ end
1150
+
1151
+ context "with AtomicSerializer" do
1152
+ let(:serializers) { Foobara::CommandConnectors::Serializers::AtomicSerializer }
1153
+
1154
+ context "when user exists with a referral" do
1155
+ let(:user) do
1156
+ User.transaction do
1157
+ referral = referral_class.create(email: "Some@email.com")
1158
+ User.create(name: "Some Name", referral:, ratings: [1, 2, 3], point: { x: 1, y: 2 })
1159
+ end
1160
+ end
1161
+
1162
+ let(:user_id) { user.id }
1163
+
1164
+ let(:referral_id) { user.referral.id }
1165
+
1166
+ it "serializes as an atom" do
1167
+ expect(response.status).to be(0)
1168
+ expect(JSON.parse(response.body)).to eq(
1169
+ "id" => user_id,
1170
+ "name" => "Some Name",
1171
+ "referral" => referral_id,
1172
+ "ratings" => [1, 2, 3],
1173
+ "point" => { "x" => 1, "y" => 2 }
1174
+ )
1175
+ end
1176
+ end
1177
+ end
1178
+
1179
+ context "with AggregateSerializer" do
1180
+ let(:serializers) { Foobara::CommandConnectors::Serializers::AggregateSerializer }
1181
+ let(:pre_commit_transformers) {
1182
+ Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer
1183
+ }
1184
+
1185
+ context "when user exists with a referral" do
1186
+ let(:command_class) do
1187
+ user_class
1188
+ referral_class
1189
+
1190
+ stub_class :QueryUser, Foobara::Command do
1191
+ inputs user: User
1192
+ result stuff: [User, Referral]
1193
+
1194
+ load_all
1195
+
1196
+ def execute
1197
+ {
1198
+ stuff: [user, user.referral]
1199
+ }
1200
+ end
1201
+ end
1202
+ end
1203
+
1204
+ let(:user) do
1205
+ User.transaction do
1206
+ referral = referral_class.create(email: "Some@email.com")
1207
+ User.create(
1208
+ name: "Some Name",
1209
+ referral:,
1210
+ ratings: [1, 2, 3],
1211
+ point: { x: 1, y: 2 },
1212
+ junk: { [1, 2, 3] => [1, 2, 3] }
1213
+ )
1214
+ end
1215
+ end
1216
+
1217
+ let(:user_id) { user.id }
1218
+ let(:referral_id) { user.referral.id }
1219
+
1220
+ it "serializes as an aggregate" do
1221
+ expect(response.status).to be(0)
1222
+ expect(JSON.parse(response.body)).to eq(
1223
+ "stuff" => [
1224
+ {
1225
+ "id" => 1,
1226
+ # TODO: This is kind of crazy that we can only have strings as keys. Should raise exception.
1227
+ "junk" => { "[1, 2, 3]" => [1, 2, 3] },
1228
+ "name" => "Some Name",
1229
+ "point" => { "x" => 1, "y" => 2 },
1230
+ "ratings" => [1, 2, 3],
1231
+ "referral" => { "email" => "some@email.com", "id" => 1 }
1232
+ },
1233
+ {
1234
+ "email" => "some@email.com", "id" => 1
1235
+ }
1236
+ ]
1237
+ )
1238
+ end
1239
+
1240
+ it "contains pre_commit_transformers in its manifest" do
1241
+ command_manifest = Foobara::TypeDeclarations.with_manifest_context(include_processor: true) do
1242
+ command_connector.foobara_manifest[:command][:QueryUser]
1243
+ end
1244
+ expect(command_manifest[:pre_commit_transformers]).to include(
1245
+ "Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer"
1246
+ )
1247
+ expect(command_manifest[:serializers]).to include(
1248
+ "Foobara::CommandConnectors::Serializers::AggregateSerializer"
1249
+ )
1250
+ end
1251
+
1252
+ context "with aggregate serializer as default serializer" do
1253
+ let(:aggregate_entities) { nil }
1254
+ let(:pre_commit_transformers) { nil }
1255
+ let(:serializers) { nil }
1256
+
1257
+ let(:default_serializers) do
1258
+ [
1259
+ Foobara::CommandConnectors::Serializers::AggregateSerializer,
1260
+ Foobara::CommandConnectors::Serializers::ErrorsSerializer,
1261
+ Foobara::CommandConnectors::Serializers::JsonSerializer
1262
+ ]
1263
+ end
1264
+
1265
+ let(:default_pre_commit_transformers) do
1266
+ Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer
1267
+ end
1268
+
1269
+ it "contains pre_commit_transformers in its manifest" do
1270
+ command_manifest = command_connector.foobara_manifest[:command][:QueryUser]
1271
+ expect(command_manifest[:pre_commit_transformers]).to include(
1272
+ "Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer"
1273
+ )
1274
+ expect(command_manifest[:serializers]).to include(
1275
+ "Foobara::CommandConnectors::Serializers::AggregateSerializer"
1276
+ )
1277
+ end
1278
+
1279
+ context "when disabled via aggregate_entities: false" do
1280
+ let(:aggregate_entities) { false }
1281
+
1282
+ it "does not contain pre_commit_transformers in its manifest" do
1283
+ command_manifest = command_connector.foobara_manifest[:command][:QueryUser]
1284
+ expect(command_manifest[:pre_commit_transformers]).to be_nil
1285
+
1286
+ expect(
1287
+ command_manifest[:serializers]
1288
+ ).to_not include("CommandConnectors::Serializers::AggregateSerializer")
1289
+ end
1290
+ end
1291
+ end
1292
+ end
1293
+ end
1294
+
1295
+ context "with RecordStoreSerializer" do
1296
+ let(:serializers) { Foobara::CommandConnectors::Serializers::RecordStoreSerializer }
1297
+ let(:aggregate_entities) { true }
1298
+
1299
+ context "when user exists with a referral" do
1300
+ let(:user) do
1301
+ User.transaction do
1302
+ referral = referral_class.create(email: "Some@email.com")
1303
+ User.create(name: "Some Name", referral:, ratings: [1, 2, 3], point: { x: 1, y: 2 })
1304
+ end
1305
+ end
1306
+
1307
+ let(:user_id) { user.id }
1308
+
1309
+ let(:referral_id) { user.referral.id }
1310
+
1311
+ it "serializes as a record store" do
1312
+ expect(response.status).to be(0)
1313
+ expect(JSON.parse(response.body)).to eq(
1314
+ "User" => {
1315
+ "1" => {
1316
+ "id" => 1,
1317
+ "name" => "Some Name",
1318
+ "referral" => 1,
1319
+ "ratings" => [1, 2, 3],
1320
+ "point" => { "x" => 1, "y" => 2 }
1321
+ }
1322
+ },
1323
+ "Referral" => {
1324
+ "1" => {
1325
+ "id" => 1,
1326
+ "email" => "some@email.com"
1327
+ }
1328
+ }
1329
+ )
1330
+ end
1331
+ end
1332
+ end
1333
+ end
1334
+ end
1335
+
1336
+ describe "#manifest" do
1337
+ context "when various transformers" do
1338
+ let(:inputs) { { bbaassee: base, exponent: } }
1339
+
1340
+ let(:inputs_transformers) { [inputs_transformer] }
1341
+ let(:inputs_transformer) do
1342
+ stub_class "SomeTransformer", Foobara::TypeDeclarations::TypedTransformer do
1343
+ def from_type_declaration
1344
+ {
1345
+ bbaassee: :string,
1346
+ exponent: :string
1347
+ }
1348
+ end
1349
+
1350
+ def transform(inputs)
1351
+ {
1352
+ base: inputs[:bbaassee],
1353
+ exponent: inputs[:exponent]
1354
+ }
1355
+ end
1356
+ end
1357
+ end
1358
+
1359
+ let(:result_transformers) { [result_transformer] }
1360
+ let(:result_transformer) do
1361
+ stub_class :SomeOtherTransformer, Foobara::TypeDeclarations::TypedTransformer do
1362
+ to(answer: :string)
1363
+
1364
+ def transform(result)
1365
+ { answer: result.to_s }
1366
+ end
1367
+ end
1368
+ end
1369
+
1370
+ it "runs the command" do
1371
+ expect(response.status).to be(0)
1372
+ expect(response.command.raw_inputs).to eq(bbaassee: 2, exponent: 3)
1373
+ expect(JSON.parse(response.body)).to eq("answer" => "8")
1374
+ end
1375
+
1376
+ context "with bad inputs" do
1377
+ let(:inputs_transformer) do
1378
+ stub_class "SomeTransformer", Foobara::TypeDeclarations::TypedTransformer do
1379
+ def from_type_declaration
1380
+ Foobara::Domain.current.foobara_type_from_declaration do
1381
+ bbaassee :string, :required
1382
+ exponent :string, :required
1383
+ end
1384
+ end
1385
+ end
1386
+ end
1387
+
1388
+ let(:inputs) { { exponent: } }
1389
+
1390
+ it "is not success" do
1391
+ expect(response.status).to be(1)
1392
+ end
1393
+ end
1394
+
1395
+ describe "#manifest" do
1396
+ let(:manifest) do
1397
+ Foobara::TypeDeclarations.with_manifest_context(include_processors: true) do
1398
+ command_connector.foobara_manifest
1399
+ end
1400
+ end
1401
+
1402
+ it "uses types from the transformers" do
1403
+ h = manifest[:command][:ComputeExponent]
1404
+
1405
+ inputs_type = h[:inputs_type]
1406
+ result_type = h[:result_type]
1407
+ error_types = h[:possible_errors]
1408
+
1409
+ expect(inputs_type).to eq(
1410
+ type: :attributes,
1411
+ element_type_declarations: {
1412
+ exponent: :string,
1413
+ bbaassee: :string
1414
+ }
1415
+ )
1416
+ expect(result_type).to eq(
1417
+ type: :attributes,
1418
+ element_type_declarations: {
1419
+ answer: :string
1420
+ }
1421
+ )
1422
+ expect(error_types).to eq(
1423
+ "runtime.some_runtime" => {
1424
+ category: :runtime,
1425
+ symbol: :some_runtime,
1426
+ key: "runtime.some_runtime",
1427
+ error: "SomeRuntimeError"
1428
+ },
1429
+ "data.base.some_input" => {
1430
+ path: [:base],
1431
+ category: :data,
1432
+ symbol: :some_input,
1433
+ key: "data.base.some_input",
1434
+ error: "SomeInputError",
1435
+ manually_added: true
1436
+ },
1437
+ "data.cannot_cast" => {
1438
+ category: :data,
1439
+ symbol: :cannot_cast,
1440
+ key: "data.cannot_cast",
1441
+ error: "Foobara::Value::Processor::Casting::CannotCastError",
1442
+ processor_class: "Foobara::Value::Processor::Casting",
1443
+ processor_manifest_data: {
1444
+ casting: { cast_to: { type: :attributes,
1445
+ element_type_declarations: {
1446
+ bbaassee: :string, exponent: :string
1447
+ } } }
1448
+ }
1449
+ },
1450
+ "data.unexpected_attributes" => {
1451
+ category: :data,
1452
+ symbol: :unexpected_attributes,
1453
+ key: "data.unexpected_attributes",
1454
+ error: "attributes::SupportedProcessors::ElementTypeDeclarations::UnexpectedAttributesError",
1455
+ processor_class: "attributes::SupportedProcessors::ElementTypeDeclarations",
1456
+ processor_manifest_data: { element_type_declarations: { bbaassee: :string,
1457
+ exponent: :string } }
1458
+ },
1459
+ "data.bbaassee.cannot_cast" => {
1460
+ path: [:bbaassee],
1461
+ category: :data,
1462
+ symbol: :cannot_cast,
1463
+ key: "data.bbaassee.cannot_cast",
1464
+ error: "Foobara::Value::Processor::Casting::CannotCastError",
1465
+ processor_manifest_data: { casting: { cast_to: :string } }
1466
+ },
1467
+ "data.exponent.cannot_cast" => {
1468
+ path: [:exponent],
1469
+ category: :data,
1470
+ symbol: :cannot_cast,
1471
+ key: "data.exponent.cannot_cast",
1472
+ error: "Foobara::Value::Processor::Casting::CannotCastError",
1473
+ processor_manifest_data: { casting: { cast_to: :string } }
1474
+ }
1475
+ )
1476
+ end
1477
+ end
1478
+
1479
+ describe "#possible_errors" do
1480
+ it "contains paths matching the transformed inputs" do
1481
+ transformed_command = command_connector.transformed_command_from_name("ComputeExponent")
1482
+ expect(transformed_command.possible_errors.map(&:key).map(&:to_s)).to contain_exactly(
1483
+ "runtime.some_runtime",
1484
+ "data.base.some_input",
1485
+ "data.cannot_cast",
1486
+ "data.unexpected_attributes",
1487
+ "data.bbaassee.cannot_cast",
1488
+ "data.exponent.cannot_cast"
1489
+ )
1490
+ end
1491
+ end
1492
+ end
1493
+ end
1494
+
1495
+ context "with describe path" do
1496
+ let(:action) { "describe" }
1497
+ let(:full_command_name) { "ComputeExponent" }
1498
+ let(:inputs) { {} }
1499
+
1500
+ it "describes the command" do
1501
+ expect(response.status).to be(0)
1502
+ json = JSON.parse(response.body)
1503
+ expect(json["inputs_type"]["element_type_declarations"]["base"]).to eq("integer")
1504
+ end
1505
+
1506
+ context "with describe path" do
1507
+ let(:action) { "describe_command" }
1508
+ let(:full_command_name) { "ComputeExponent" }
1509
+
1510
+ it "describes the command" do
1511
+ expect(response.status).to be(0)
1512
+ json = JSON.parse(response.body)
1513
+ expect(json["inputs_type"]["element_type_declarations"]["base"]).to eq("integer")
1514
+ end
1515
+ end
1516
+ end
1517
+
1518
+ context "with help path" do
1519
+ let(:org_module) do
1520
+ stub_module :SomeOrg do
1521
+ foobara_organization!
1522
+ end
1523
+ end
1524
+ let(:action) { "help" }
1525
+
1526
+ let(:domain_module) do
1527
+ org_module
1528
+ stub_module("SomeOrg::SomeDomain") do
1529
+ foobara_domain!
1530
+ end
1531
+ end
1532
+
1533
+ let(:another_command_class) do
1534
+ domain_module
1535
+ stub_module "SomeOtherOrg" do
1536
+ foobara_organization!
1537
+ end
1538
+ stub_module "SomeOtherOrg::SomeOtherDomain" do
1539
+ foobara_domain!
1540
+ end
1541
+ stub_class "SomeOtherOrg::SomeOtherDomain::SomeOtherCommand", Foobara::Command do
1542
+ inputs email: :email
1543
+ end
1544
+ stub_class "SomeOrg::SomeDomain::SomeCommand", Foobara::Command do
1545
+ description "just some command"
1546
+ depends_on SomeOtherOrg::SomeOtherDomain::SomeOtherCommand
1547
+ end
1548
+ end
1549
+
1550
+ before do
1551
+ command_connector.connect(another_command_class)
1552
+
1553
+ stub_class("Foobara::CommandConnector::Commands::Help", Foobara::Command) do
1554
+ inputs request: Foobara::CommandConnector::Request
1555
+ result :string
1556
+
1557
+ def execute
1558
+ "HELP!!!"
1559
+ end
1560
+ end
1561
+ end
1562
+
1563
+ it "gives some help" do
1564
+ expect(response.status).to be(0)
1565
+ expect(response.body).to match(/HELP!!!/)
1566
+ end
1567
+
1568
+ context "when asking for help with a specific element" do
1569
+ let(:action) { "help" }
1570
+ let(:full_command_name) { "ComputeExponent" }
1571
+
1572
+ it "gives some help" do
1573
+ expect(response.status).to be(0)
1574
+ expect(response.body).to match(/HELP!!!/)
1575
+ end
1576
+ end
1577
+
1578
+ context "when it is something accessible through GlobalOrganization but not the connector" do
1579
+ before do
1580
+ command_connector.connect(new_command)
1581
+
1582
+ stub_class("Foobara::CommandConnector::Commands::Help", Foobara::Command) do
1583
+ inputs request: Foobara::CommandConnector::Request
1584
+ result :string
1585
+
1586
+ def execute
1587
+ "HELP!!!"
1588
+ end
1589
+ end
1590
+ end
1591
+
1592
+ let(:new_command) do
1593
+ stub_class(:NewCommand, Foobara::Command) do
1594
+ inputs do
1595
+ count :integer, min: 0
1596
+ log [:string]
1597
+ end
1598
+ end
1599
+ end
1600
+
1601
+ context "when command" do
1602
+ let(:action) { "help" }
1603
+ let(:full_command_name) { "NewCommand" }
1604
+
1605
+ it "gives some help" do
1606
+ expect(response.status).to be(0)
1607
+ expect(response.body).to match("HELP!!!")
1608
+ end
1609
+ end
1610
+ end
1611
+ end
1612
+
1613
+ context "when the command returns a model from a domain it depends on (bad form but let's support it for now)" do
1614
+ let(:command_class) do
1615
+ user_class
1616
+
1617
+ stub_module("DomainA") do
1618
+ foobara_domain!
1619
+ end
1620
+ stub_class("DomainA::MakeUser", Foobara::Command) do
1621
+ result DomainB::User
1622
+ end
1623
+ end
1624
+
1625
+ let(:full_command_name) { "DomainA::MakeUser" }
1626
+
1627
+ let(:user_class) do
1628
+ stub_module("DomainB") do
1629
+ foobara_domain!
1630
+ end
1631
+
1632
+ stub_class("DomainB::User", Foobara::Model) do
1633
+ attributes name: :string
1634
+ end
1635
+ end
1636
+
1637
+ it "includes the model and its domain in the manifest" do
1638
+ manifest = command_connector.foobara_manifest
1639
+
1640
+ expect(manifest[:domain].keys).to include(:DomainB)
1641
+ expect(manifest[:type].keys).to include(:"DomainB::User")
1642
+ end
1643
+ end
1644
+
1645
+ context "when command returns a model that has sensitive attributes nested within it" do
1646
+ before do
1647
+ Foobara::Persistence.default_crud_driver = Foobara::Persistence::CrudDrivers::InMemory.new
1648
+ end
1649
+
1650
+ let(:default_serializers) do
1651
+ [
1652
+ Foobara::CommandConnectors::Serializers::AggregateSerializer,
1653
+ Foobara::CommandConnectors::Serializers::ErrorsSerializer,
1654
+ Foobara::CommandConnectors::Serializers::JsonSerializer
1655
+ ]
1656
+ end
1657
+ # TODO: make this automatic if AggregateSerializer is being used
1658
+ let(:pre_commit_transformers) { Foobara::CommandConnectors::Transformers::LoadAggregatesPreCommitTransformer }
1659
+
1660
+ let(:command_class) do
1661
+ user_class
1662
+
1663
+ stub_class(:QueryUser, Foobara::Command) do
1664
+ inputs user: User
1665
+ result :User
1666
+
1667
+ load_all
1668
+
1669
+ def execute
1670
+ user
1671
+ end
1672
+ end
1673
+ end
1674
+
1675
+ let(:full_command_name) { "QueryUser" }
1676
+ let(:inputs) { { user: user_id } }
1677
+
1678
+ let(:user_class) do
1679
+ rating_class
1680
+ stub_class(:User, Foobara::Entity) do
1681
+ attributes do
1682
+ id :integer
1683
+ name :string
1684
+ ssn :string, :sensitive
1685
+ ratings [:Rating]
1686
+ end
1687
+
1688
+ primary_key :id
1689
+ end
1690
+ end
1691
+
1692
+ let(:rating_class) do
1693
+ stub_class(:Rating, Foobara::Entity) do
1694
+ attributes do
1695
+ id :integer
1696
+ rating :integer
1697
+ secret :string, :sensitive
1698
+ end
1699
+
1700
+ primary_key :id
1701
+ end
1702
+ end
1703
+
1704
+ context "when user exists with a rating" do
1705
+ let(:user_id) do
1706
+ User.transaction do
1707
+ User.create(name: :whatever, ssn: "ssn", ratings: [Rating.create(rating: 1, secret: "secret")])
1708
+ end.id
1709
+ end
1710
+
1711
+ it "finds the user" do
1712
+ expect(response.status).to be(0)
1713
+ expect(JSON.parse(response.body)).to eq(
1714
+ "id" => 1,
1715
+ "name" => "whatever",
1716
+ "ratings" => [{
1717
+ "id" => 1,
1718
+ "rating" => 1
1719
+ }]
1720
+ )
1721
+ end
1722
+ end
1723
+ end
1724
+
1725
+ context "when command returns an entity that has delegated attributes" do
1726
+ before do
1727
+ Foobara::Persistence.default_crud_driver = Foobara::Persistence::CrudDrivers::InMemory.new
1728
+ end
1729
+
1730
+ let(:default_serializers) do
1731
+ [
1732
+ Foobara::CommandConnectors::Serializers::ErrorsSerializer,
1733
+ Foobara::CommandConnectors::Serializers::AtomicSerializer,
1734
+ Foobara::CommandConnectors::Serializers::JsonSerializer
1735
+ ]
1736
+ end
1737
+
1738
+ # TODO: make this a default based on a flag
1739
+ let(:pre_commit_transformers) do
1740
+ Foobara::CommandConnectors::Transformers::LoadDelegatedAttributesEntitiesPreCommitTransformer
1741
+ end
1742
+
1743
+ let(:command_class) do
1744
+ user_class
1745
+
1746
+ stub_class(:CreateUser, Foobara::Command) do
1747
+ inputs User.attributes_for_create
1748
+ result :User
1749
+
1750
+ def execute
1751
+ User.create(inputs)
1752
+ end
1753
+ end
1754
+
1755
+ stub_class(:QueryUser, Foobara::Command) do
1756
+ inputs do
1757
+ stuff do
1758
+ things :array do
1759
+ auth_user AuthUser, :required
1760
+ end
1761
+ end
1762
+ end
1763
+
1764
+ result do
1765
+ stuff2 do
1766
+ things2 :array do
1767
+ user User, :required
1768
+ end
1769
+ end
1770
+ end
1771
+
1772
+ load_all
1773
+
1774
+ def execute
1775
+ user = User.that_owns(stuff[:things][0][:auth_user])
1776
+ { stuff2: { things2: [{ user: }] } }
1777
+ end
1778
+ end
1779
+ end
1780
+
1781
+ let(:full_command_name) { "QueryUser" }
1782
+ let(:inputs) do
1783
+ {
1784
+ stuff: {
1785
+ things: [{ auth_user: auth_user_id }]
1786
+ }
1787
+ }
1788
+ end
1789
+
1790
+ let(:user_class) do
1791
+ auth_user_class
1792
+ stub_class(:User, Foobara::Entity) do
1793
+ attributes do
1794
+ id :integer
1795
+ name :string
1796
+ ssn :string, :sensitive
1797
+ ssn2 :string, :private
1798
+ foo :string
1799
+ stuff :private do
1800
+ things :array do
1801
+ auth_user AuthUser, :required
1802
+ end
1803
+ end
1804
+ end
1805
+
1806
+ primary_key :id
1807
+ delegate_attribute :username, [:stuff, :things, :"0", :auth_user, :username]
1808
+ end
1809
+ end
1810
+
1811
+ let(:auth_user_class) do
1812
+ stub_class(:AuthUser, Foobara::Entity) do
1813
+ attributes do
1814
+ id :integer
1815
+ username :string
1816
+ end
1817
+
1818
+ primary_key :id
1819
+ end
1820
+ end
1821
+
1822
+ context "when user exists" do
1823
+ let(:user_id) do
1824
+ CreateUser.run!(
1825
+ name: :whatever,
1826
+ ssn: "123",
1827
+ ssn2: "789",
1828
+ foo: "bar",
1829
+ stuff: {
1830
+ things: [
1831
+ { auth_user: auth_user_id }
1832
+ ]
1833
+ }
1834
+ ).id
1835
+ end
1836
+
1837
+ let(:username) { "some_username" }
1838
+ let(:auth_user_id) do
1839
+ AuthUser.transaction do
1840
+ AuthUser.create(username:)
1841
+ end.id
1842
+ end
1843
+
1844
+ before do
1845
+ user_id
1846
+ end
1847
+
1848
+ it "finds the user and includes delegated attributes" do
1849
+ expect(response.status).to be(0)
1850
+ expect(JSON.parse(response.body)).to eq(
1851
+ "stuff2" => {
1852
+ "things2" => [
1853
+ "user" => {
1854
+ "id" => user_id,
1855
+ "name" => "whatever",
1856
+ "foo" => "bar",
1857
+ "username" => "some_username"
1858
+ }
1859
+ ]
1860
+ }
1861
+ )
1862
+ end
1863
+
1864
+ context "with a simple entity return type" do
1865
+ let(:command_class) do
1866
+ user_class
1867
+
1868
+ stub_class(:CreateUser, Foobara::Command) do
1869
+ inputs User.attributes_for_create
1870
+ result :User
1871
+
1872
+ def execute
1873
+ User.create(inputs)
1874
+ end
1875
+ end
1876
+
1877
+ stub_class(:QueryUser, Foobara::Command) do
1878
+ inputs do
1879
+ stuff do
1880
+ things :array do
1881
+ auth_user AuthUser, :required
1882
+ end
1883
+ end
1884
+ end
1885
+
1886
+ result User
1887
+
1888
+ load_all
1889
+
1890
+ def execute
1891
+ User.that_owns(stuff[:things][0][:auth_user])
1892
+ end
1893
+ end
1894
+ end
1895
+
1896
+ it "responds with attributes that include the delegated attributes" do
1897
+ expect(response.status).to be(0)
1898
+ expect(JSON.parse(response.body)).to eq(
1899
+ "id" => 1,
1900
+ "name" => "whatever",
1901
+ "username" => "some_username",
1902
+ "foo" => "bar"
1903
+ )
1904
+ end
1905
+ end
1906
+ end
1907
+ end
1908
+
1909
+ describe "connector manifest" do
1910
+ describe "#manifest" do
1911
+ let(:manifest) { command_connector.foobara_manifest }
1912
+
1913
+ it "returns metadata about the commands" do
1914
+ expect(
1915
+ manifest[:command][:ComputeExponent][:result_type]
1916
+ ).to eq(:integer)
1917
+ end
1918
+
1919
+ context "with an entity input" do
1920
+ before do
1921
+ Foobara::Persistence.default_crud_driver = Foobara::Persistence::CrudDrivers::InMemory.new
1922
+ end
1923
+
1924
+ after do
1925
+ Foobara.reset_alls
1926
+ end
1927
+
1928
+ let(:command_class) do
1929
+ user_class
1930
+
1931
+ stub_class :QueryUser, Foobara::Command do
1932
+ description "Queries a user"
1933
+ inputs user: User.entity_type
1934
+ result :User
1935
+ end
1936
+ end
1937
+
1938
+ let(:full_command_name) { "QueryUser" }
1939
+ let(:inputs) { { user: user_id } }
1940
+
1941
+ let(:serializers) {
1942
+ [proc(&:attributes)]
1943
+ }
1944
+
1945
+ let(:user_class) do
1946
+ stub_class :User, Foobara::Entity do
1947
+ attributes id: :integer, name: :string
1948
+ primary_key :id
1949
+ end
1950
+ end
1951
+
1952
+ it "returns metadata about the types referenced in the commands" do
1953
+ expect(
1954
+ manifest[:type].keys
1955
+ ).to contain_exactly(:User, :array, :associative_array, :atomic_duck, :attributes, :detached_entity,
1956
+ :duck, :duckture, :entity, :integer, :model, :number, :string, :symbol)
1957
+ end
1958
+
1959
+ context "with manifest path" do
1960
+ let(:inputs) { {} }
1961
+ let(:action) { "manifest" }
1962
+
1963
+ it "includes types" do
1964
+ expect(response.status).to be(0)
1965
+ json = JSON.parse(response.body)
1966
+ expect(json["type"].keys).to include("User")
1967
+ end
1968
+ end
1969
+
1970
+ context "with list path" do
1971
+ let(:inputs) { {} }
1972
+ let(:action) { "list" }
1973
+
1974
+ it "lists commands" do
1975
+ expect(response.status).to be(0)
1976
+ json = JSON.parse(response.body)
1977
+
1978
+ expect(json).to eq([["QueryUser", nil]])
1979
+ end
1980
+
1981
+ context "when verbose" do
1982
+ # TODO: would be nice to not have to do =true here...
1983
+ let(:inputs) { { verbose: true } }
1984
+
1985
+ it "lists commands" do
1986
+ expect(response.status).to be(0)
1987
+ json = JSON.parse(response.body)
1988
+
1989
+ expect(json).to eq([["QueryUser", "Queries a user"]])
1990
+ end
1991
+ end
1992
+ end
1993
+
1994
+ context "with describe_type path" do
1995
+ let(:inputs) { {} }
1996
+ let(:full_command_name) { "User" }
1997
+ let(:action) { "describe_type" }
1998
+
1999
+ it "includes types" do
2000
+ expect(response.status).to be(0)
2001
+ json = JSON.parse(response.body)
2002
+ expect(json["declaration_data"]["name"]).to eq("User")
2003
+ end
2004
+ end
2005
+ end
2006
+ end
2007
+ end
2008
+ end
2009
+
2010
+ describe "#patch_up_broken_parents_for_errors_with_missing_command_parents" do
2011
+ let(:fixture_path) {
2012
+ "#{__dir__}/fixtures/command_connectors/manifest_with_errors_with_missing_command_parents.yaml"
2013
+ }
2014
+ let(:manifest_hash) do
2015
+ YAML.load_file(fixture_path, aliases: true)
2016
+ end
2017
+
2018
+ it "fixes busted parents by leapfrogging them and patching up scoped paths/names" do
2019
+ error_manifest = manifest_hash[:error][:"Foobara::Auth::FindUser::UserNotFoundError"]
2020
+
2021
+ expect(error_manifest[:scoped_path]).to eq(["UserNotFoundError"])
2022
+ expect(error_manifest[:parent]).to eq([:command, "Foobara::Auth::FindUser"])
2023
+ expect(error_manifest[:scoped_prefix]).to be_nil
2024
+ expect(error_manifest[:scoped_name]).to eq("UserNotFoundError")
2025
+
2026
+ # TODO: figure out how to set up this situation instead of directly passing a manifest
2027
+ # hash to a private method
2028
+ patched_up_manifest = command_connector.send(
2029
+ :patch_up_broken_parents_for_errors_with_missing_command_parents,
2030
+ manifest_hash
2031
+ )
2032
+
2033
+ patched_up_error_manifest = patched_up_manifest[:error][:"Foobara::Auth::FindUser::UserNotFoundError"]
2034
+
2035
+ expect(patched_up_error_manifest[:scoped_path]).to eq(["FindUser", "UserNotFoundError"])
2036
+ expect(patched_up_error_manifest[:parent]).to eq([:domain, "Foobara::Auth"])
2037
+ expect(patched_up_error_manifest[:scoped_prefix]).to eq(["FindUser"])
2038
+ expect(patched_up_error_manifest[:scoped_name]).to eq("FindUser::UserNotFoundError")
2039
+ end
2040
+ end
2041
+
2042
+ context "with multiple layers of inheritance with allowed_rule entries" do
2043
+ let(:allowed_rule_a) do
2044
+ proc { "a" }
2045
+ end
2046
+ # intentionally skipping b
2047
+ let(:allowed_rule_c) do
2048
+ proc {} # will not be allowed
2049
+ end
2050
+ let(:allowed_rule_d) do
2051
+ proc { "d" }
2052
+ end
2053
+
2054
+ let(:command_connector_class_a) do
2055
+ rule = allowed_rule_a
2056
+ stub_class "CommandConnectorA", described_class do
2057
+ register_allowed_rule :a, rule
2058
+ end
2059
+ end
2060
+
2061
+ let(:command_connector_class_b) do
2062
+ stub_class "CommandConnectorB", command_connector_class_a
2063
+ end
2064
+
2065
+ let(:command_connector_class_c) do
2066
+ rule = allowed_rule_c
2067
+ stub_class "CommandConnectorC", command_connector_class_b do
2068
+ register_allowed_rule :c, rule
2069
+ end
2070
+ end
2071
+
2072
+ let(:command_connector_class_d) do
2073
+ rule = allowed_rule_d
2074
+ stub_class "CommandConnectorD", command_connector_class_c do
2075
+ register_allowed_rule :d, rule
2076
+ end
2077
+ end
2078
+ let(:command_connector) { command_connector_class_d.new }
2079
+
2080
+ it "puts the expected allowed rules on the command connector" do
2081
+ command_connector.connect(command_class, suffix: "A", allow_if: :a)
2082
+ command_connector.connect(command_class, suffix: "B")
2083
+ command_connector.connect(command_class, suffix: "C", allowed_rule: :c)
2084
+ command_connector.connect(command_class, suffix: "D", allowed_rule: :d)
2085
+
2086
+ response = command_connector.run(full_command_name: "ComputeExponentA", action:, inputs:)
2087
+ expect(response.status).to be(0)
2088
+ expect(response.command.allowed_rule.symbol).to be(:a)
2089
+
2090
+ response = command_connector.run(full_command_name: "ComputeExponentB", action:, inputs:)
2091
+ expect(response.status).to be(0)
2092
+ expect(response.command.allowed_rule).to be_nil
2093
+
2094
+ response = command_connector.run(full_command_name: "ComputeExponentC", action:, inputs:)
2095
+ expect(response.status).to be(1)
2096
+ expect(response.command.allowed_rule.symbol).to be(:c)
2097
+
2098
+ response = command_connector.run(full_command_name: "ComputeExponentD", action:, inputs:)
2099
+ expect(response.status).to be(0)
2100
+ expect(response.command.allowed_rule.symbol).to be(:d)
2101
+ end
2102
+ end
2103
+
2104
+ context "with multiple authenticators to choose from" do
2105
+ let(:authenticator_a) do
2106
+ stub_class("NotApplicableAuthenticator", Foobara::CommandConnector::Authenticator) do
2107
+ def initialize
2108
+ super(symbol: :a, &proc { "a" })
2109
+ end
2110
+
2111
+ def applicable?(_request)
2112
+ false
2113
+ end
2114
+ end
2115
+ end
2116
+
2117
+ let(:authenticator_b) do
2118
+ stub_class("ApplicableAuthenticator", Foobara::CommandConnector::Authenticator) do
2119
+ def initialize
2120
+ super(symbol: :b, &proc { "b" })
2121
+ end
2122
+ end
2123
+ end
2124
+
2125
+ let(:authenticator) { [authenticator_a, authenticator_b] }
2126
+ let(:requires_authentication) { true }
2127
+
2128
+ it "chooses the applicable authenticator" do
2129
+ expect(response.status).to be(0)
2130
+ expect(response.command.authenticated_user).to eq("b")
2131
+ end
2132
+
2133
+ describe "#foobara_manifest" do
2134
+ it "contains combined info about the authenticator" do
2135
+ manifest = command_connector.foobara_manifest
2136
+
2137
+ expect(manifest[:command][:ComputeExponent][:authenticator]).to eq(
2138
+ symbol: :a_or_b,
2139
+ explanation: "a, or b"
2140
+ )
2141
+ end
2142
+ end
2143
+ end
2144
+
2145
+ context "with multiple layers of inheritance with authenticator entries" do
2146
+ let(:authenticator_a) do
2147
+ stub_class("SomeAuthenticator", Foobara::CommandConnector::Authenticator) do
2148
+ def initialize
2149
+ super(symbol: :a, &proc { ["a", "acred"] })
2150
+ end
2151
+ end
2152
+ end
2153
+ # intentionally skipping b
2154
+ let(:authenticator_c) do
2155
+ proc {} # will not be allowed
2156
+ end
2157
+ let(:authenticator_d) do
2158
+ proc { "d" }
2159
+ end
2160
+ let(:authenticator_e) do
2161
+ proc { "e" }
2162
+ end
2163
+
2164
+ let(:command_connector_class_a) do
2165
+ authenticator = authenticator_a
2166
+ stub_class "CommandConnectorA", described_class do
2167
+ register_authenticator :a, authenticator
2168
+ end
2169
+ end
2170
+
2171
+ let(:command_connector_class_b) do
2172
+ stub_class "CommandConnectorB", command_connector_class_a
2173
+ end
2174
+
2175
+ let(:command_connector_class_c) do
2176
+ authenticator = authenticator_c
2177
+ stub_class "CommandConnectorC", command_connector_class_b do
2178
+ register_authenticator :c, authenticator
2179
+ end
2180
+ end
2181
+
2182
+ let(:command_connector_class_d) do
2183
+ authenticator = authenticator_d
2184
+ stub_class "CommandConnectorD", command_connector_class_c do
2185
+ register_authenticator :d, authenticator
2186
+ end
2187
+ end
2188
+
2189
+ let(:command_connector_class_e) do
2190
+ stub_class "CommandConnectorE", command_connector_class_d
2191
+ end
2192
+
2193
+ let(:command_connector_a) { command_connector_class_a.new(authenticator: authenticator_a) }
2194
+ let(:command_connector_b) { command_connector_class_b.new(authenticator: "a") }
2195
+ let(:command_connector_c) { command_connector_class_c.new(authenticator: :a) }
2196
+ let(:command_connector_d) { command_connector_class_d.new(authenticator: :c) }
2197
+ let(:command_connector_e) { command_connector_class_e.new(authenticator: :d) }
2198
+
2199
+ it "puts the expected allowed rules on the command connector" do
2200
+ command_connector_a.connect(command_class, :requires_authentication)
2201
+ command_connector_b.connect(command_class, :auth)
2202
+ command_connector_c.connect(command_class, requires_authentication: true)
2203
+ command_connector_d.connect(command_class, requires_authentication: true)
2204
+ command_connector_e.connect(command_class, requires_authentication: true, authenticator: [authenticator_e])
2205
+
2206
+ response = command_connector_a.run(full_command_name:, action:, inputs:)
2207
+ expect(response.status).to be(0)
2208
+ expect(response.command.authenticated_user).to eq("a")
2209
+ expect(response.command.authenticator.symbol).to be(:a)
2210
+
2211
+ response = command_connector_b.run(full_command_name:, action:, inputs:)
2212
+ expect(response.status).to be(0)
2213
+ expect(response.command.authenticated_user).to eq("a")
2214
+ expect(response.command.authenticated_credential).to eq("acred")
2215
+ expect(response.command.authenticator.symbol).to be(:a)
2216
+
2217
+ response = command_connector_c.run(full_command_name:, action:, inputs:)
2218
+ expect(response.status).to be(0)
2219
+ expect(response.command.authenticated_user).to eq("a")
2220
+ expect(response.command.authenticator.symbol).to be(:a)
2221
+
2222
+ response = command_connector_d.run(full_command_name:, action:, inputs:)
2223
+ expect(response.status).to be(1)
2224
+ expect(response.error.key).to eq("runtime.unauthenticated")
2225
+ expect(response.command.authenticated_user).to be_nil
2226
+ expect(response.command.authenticator.symbol).to be(:c)
2227
+
2228
+ response = command_connector_e.run(full_command_name:, action:, inputs:)
2229
+ expect(response.status).to be(0)
2230
+ expect(response.command.authenticated_user).to eq("e")
2231
+ end
2232
+
2233
+ describe "#foobara_manifest" do
2234
+ it "contains info about the authenticator" do
2235
+ command_connector_a.connect(command_class, requires_authentication: true)
2236
+ manifest = command_connector_a.foobara_manifest
2237
+ expect(manifest[:command][:ComputeExponent][:authenticator]).to eq(
2238
+ symbol: :a,
2239
+ explanation: :a
2240
+ )
2241
+ end
2242
+ end
2243
+ end
2244
+
2245
+ context "when using sugar" do
2246
+ let(:command_class) do
2247
+ stub_class("SomeCommand", Foobara::Command) do
2248
+ inputs do
2249
+ foo :string, default: "defaultfoo"
2250
+ bar :string, default: "defaultbar"
2251
+ baz :string, default: "defaultbaz"
2252
+ end
2253
+ result do
2254
+ foo :string
2255
+ bar :string
2256
+ baz :string
2257
+ end
2258
+
2259
+ def execute
2260
+ inputs
2261
+ end
2262
+ end
2263
+ end
2264
+ let(:full_command_name) { command_class.full_command_name }
2265
+
2266
+ context "when only" do
2267
+ context "with non-array" do
2268
+ let(:inputs_transformers_sugar) do
2269
+ { only: :foo }
2270
+ end
2271
+ let(:result_transformers_sugar) do
2272
+ { only: :bar }
2273
+ end
2274
+
2275
+ let(:inputs) do
2276
+ { foo: "foo" }
2277
+ end
2278
+
2279
+ it "can add/remove inputs/results" do
2280
+ expect(response.status).to be(0)
2281
+ expect(parsed_response).to eq("bar" => "defaultbar")
2282
+ end
2283
+ end
2284
+
2285
+ context "when arrays" do
2286
+ let(:inputs_transformers_sugar) do
2287
+ { only: [:foo, :bar] }
2288
+ end
2289
+ let(:result_transformers_sugar) do
2290
+ { only: [:bar, :baz] }
2291
+ end
2292
+ let(:inputs) do
2293
+ {
2294
+ foo: "foo",
2295
+ bar: "bar"
2296
+ }
2297
+ end
2298
+
2299
+ it "can add/remove inputs/results" do
2300
+ expect(response.status).to be(0)
2301
+ expect(parsed_response).to eq("bar" => "bar", "baz" => "defaultbaz")
2302
+ end
2303
+ end
2304
+ end
2305
+
2306
+ context "when reject" do
2307
+ context "with non-array" do
2308
+ let(:inputs_transformers_sugar) do
2309
+ { reject: :foo }
2310
+ end
2311
+ let(:result_transformers_sugar) do
2312
+ { reject: :bar }
2313
+ end
2314
+
2315
+ let(:inputs) do
2316
+ {
2317
+ bar: "bar",
2318
+ baz: "baz"
2319
+ }
2320
+ end
2321
+
2322
+ it "can add/remove inputs/results" do
2323
+ expect(response.status).to be(0)
2324
+ expect(parsed_response).to eq("foo" => "defaultfoo", "baz" => "baz")
2325
+ end
2326
+ end
2327
+
2328
+ context "when arrays" do
2329
+ let(:inputs_transformers_sugar) do
2330
+ { reject: [:foo, :bar] }
2331
+ end
2332
+ let(:result_transformers_sugar) do
2333
+ { reject: [:bar, :baz] }
2334
+ end
2335
+ let(:inputs) do
2336
+ { baz: "baz" }
2337
+ end
2338
+
2339
+ it "can add/remove inputs/results" do
2340
+ expect(response.status).to be(0)
2341
+ expect(parsed_response).to eq("foo" => "defaultfoo")
2342
+ end
2343
+ end
2344
+ end
2345
+
2346
+ context "when set" do
2347
+ context "with non-array" do
2348
+ let(:inputs_transformers_sugar) do
2349
+ { set: { foo: -> { "setfoo" } } }
2350
+ end
2351
+
2352
+ let(:inputs) do
2353
+ {
2354
+ bar: "bar",
2355
+ baz: "baz"
2356
+ }
2357
+ end
2358
+
2359
+ it "can add/remove inputs/results" do
2360
+ expect(response.status).to be(0)
2361
+ expect(parsed_response).to eq("foo" => "setfoo", "baz" => "baz", "bar" => "bar")
2362
+ end
2363
+ end
2364
+ end
2365
+
2366
+ context "when mixture of sugar and non sugar transformers" do
2367
+ let(:inputs_transformers_sugar) do
2368
+ [
2369
+ Foobara::AttributesTransformers.only(:bar, :baz),
2370
+ { only: [:bar] }
2371
+ ]
2372
+ end
2373
+ let(:result_transformers_sugar) do
2374
+ { only: [:bar, :baz] }
2375
+ end
2376
+ let(:inputs) do
2377
+ {
2378
+ bar: "bar"
2379
+ }
2380
+ end
2381
+
2382
+ it "can add/remove inputs/results" do
2383
+ expect(response.status).to be(0)
2384
+ expect(parsed_response).to eq("bar" => "bar", "baz" => "defaultbaz")
2385
+ end
2386
+ end
2387
+
2388
+ context "when using yaml sugar" do
2389
+ let(:command_class) do
2390
+ stub_class("SomeCommand", Foobara::Command) do
2391
+ inputs do
2392
+ foo :required do
2393
+ foo :string, :required
2394
+ bar :string, :required
2395
+ end
2396
+ bar :required do
2397
+ foo :string, :required
2398
+ bar :string, :required
2399
+ end
2400
+ baz :required do
2401
+ foo :string, :required
2402
+ bar :string, :required
2403
+ end
2404
+ end
2405
+ result :duck
2406
+
2407
+ def execute
2408
+ inputs
2409
+ end
2410
+ end
2411
+ end
2412
+
2413
+ let(:inputs_transformers) do
2414
+ { yaml: [:foo, :bar] }
2415
+ end
2416
+
2417
+ let(:inputs) do
2418
+ {
2419
+ foo: "---\n:foo: foofoo\n:bar: foofoofoo\n",
2420
+ bar: "---\n:foo: barbar\n:bar: barbarbar\n",
2421
+ baz: { foo: "bazbaz", bar: "bazbazbaz" }
2422
+ }
2423
+ end
2424
+
2425
+ it "sets the type to a string for the yaml inputs" do
2426
+ transformed_command = command_connector.transformed_command_from_name("SomeCommand")
2427
+ inputs_type = transformed_command.inputs_type
2428
+
2429
+ expect(inputs_type.element_types[:foo].declaration_data).to eq(:string)
2430
+ expect(inputs_type.element_types[:bar].declaration_data).to eq(:string)
2431
+ expect(inputs_type.element_types[:baz].declaration_data[:type]).to eq(:attributes)
2432
+
2433
+ expect(response.status).to be(0)
2434
+ expect(parsed_response).to eq(
2435
+ "foo" => { "foo" => "foofoo", "bar" => "foofoofoo" },
2436
+ "bar" => { "foo" => "barbar", "bar" => "barbarbar" },
2437
+ "baz" => { "foo" => "bazbaz", "bar" => "bazbazbaz" }
2438
+ )
2439
+ end
2440
+ end
2441
+ end
2442
+ end
2443
+ end