ecoportal-api-graphql 2.0.0 → 3.0.0

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 (393) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +793 -14
  3. data/LICENSE +21 -21
  4. data/README.gem.md +53 -0
  5. data/lib/ecoportal/api/common/graphql/auth_service.rb +115 -115
  6. data/lib/ecoportal/api/common/graphql/class_helpers.rb +46 -46
  7. data/lib/ecoportal/api/common/graphql/client.rb +107 -69
  8. data/lib/ecoportal/api/common/graphql/hash_helpers.rb +105 -105
  9. data/lib/ecoportal/api/common/graphql/http_client.rb +124 -8
  10. data/lib/ecoportal/api/common/graphql/model/as_input.rb +54 -54
  11. data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +49 -49
  12. data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +188 -188
  13. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff.rb +55 -55
  14. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +295 -295
  15. data/lib/ecoportal/api/common/graphql/model/diffable/leaf_diff_service.rb +1 -1
  16. data/lib/ecoportal/api/common/graphql/model.rb +17 -17
  17. data/lib/ecoportal/api/common/graphql/patches.rb +1 -1
  18. data/lib/ecoportal/api/common/graphql/query_integration.rb +45 -45
  19. data/lib/ecoportal/api/common/graphql.rb +16 -16
  20. data/lib/ecoportal/api/common.graphql.rb +8 -8
  21. data/lib/ecoportal/api/graphql/base/action_category.rb +15 -15
  22. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +1 -1
  23. data/lib/ecoportal/api/graphql/base/contractor_entity.rb +24 -24
  24. data/lib/ecoportal/api/graphql/base/date_time.rb +12 -12
  25. data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -16
  26. data/lib/ecoportal/api/graphql/base/field.rb +12 -12
  27. data/lib/ecoportal/api/graphql/base/file_attachment.rb +15 -15
  28. data/lib/ecoportal/api/graphql/base/location_node.rb +14 -14
  29. data/lib/ecoportal/api/graphql/base/location_structure/draft/structure.rb +37 -37
  30. data/lib/ecoportal/api/graphql/base/location_structure/draft.rb +93 -93
  31. data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -9
  32. data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -18
  33. data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -29
  34. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +1 -1
  35. data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +1 -1
  36. data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -23
  37. data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -24
  38. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +160 -12
  39. data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -8
  40. data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -9
  41. data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -17
  42. data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -26
  43. data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -9
  44. data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -17
  45. data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -9
  46. data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -21
  47. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +51 -0
  48. data/lib/ecoportal/api/graphql/base/page/phased.rb +15 -15
  49. data/lib/ecoportal/api/graphql/base/page/section.rb +48 -48
  50. data/lib/ecoportal/api/graphql/base/page_info.rb +12 -12
  51. data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -39
  52. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -27
  53. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -30
  54. data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -34
  55. data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -35
  56. data/lib/ecoportal/api/graphql/base/person_member.rb +22 -22
  57. data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -23
  58. data/lib/ecoportal/api/graphql/base/register.rb +1 -1
  59. data/lib/ecoportal/api/graphql/base/resource.rb +13 -13
  60. data/lib/ecoportal/api/graphql/base/temp_image.rb +28 -0
  61. data/lib/ecoportal/api/graphql/base/template/change_log_entry.rb +118 -0
  62. data/lib/ecoportal/api/graphql/base/template.rb +2 -0
  63. data/lib/ecoportal/api/graphql/base.rb +1 -0
  64. data/lib/ecoportal/api/graphql/builder/action.rb +41 -41
  65. data/lib/ecoportal/api/graphql/builder/location_structure/draft.rb +64 -64
  66. data/lib/ecoportal/api/graphql/builder/location_structure.rb +40 -40
  67. data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -84
  68. data/lib/ecoportal/api/graphql/builder/register.rb +49 -49
  69. data/lib/ecoportal/api/graphql/builder/template.rb +37 -4
  70. data/lib/ecoportal/api/graphql/builder/template_builder.rb +21 -2
  71. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +1 -1
  72. data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -23
  73. data/lib/ecoportal/api/graphql/compat/registers.rb +84 -84
  74. data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -33
  75. data/lib/ecoportal/api/graphql/compat.rb +17 -17
  76. data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -71
  77. data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +68 -68
  78. data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -60
  79. data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -11
  80. data/lib/ecoportal/api/graphql/connection/template_change_log.rb +19 -0
  81. data/lib/ecoportal/api/graphql/connection.rb +1 -0
  82. data/lib/ecoportal/api/graphql/diff/change.rb +7 -2
  83. data/lib/ecoportal/api/graphql/diff/command_synthesizer.rb +19 -1
  84. data/lib/ecoportal/api/graphql/diff/version_diff.rb +13 -6
  85. data/lib/ecoportal/api/graphql/error/locations_error/fetch_nested.rb +51 -51
  86. data/lib/ecoportal/api/graphql/error/locations_error.rb +28 -28
  87. data/lib/ecoportal/api/graphql/error/locations_validation_error.rb +20 -20
  88. data/lib/ecoportal/api/graphql/error/validation_errors.rb +28 -28
  89. data/lib/ecoportal/api/graphql/error.rb +12 -12
  90. data/lib/ecoportal/api/graphql/file_upload/client.rb +140 -35
  91. data/lib/ecoportal/api/graphql/file_upload.rb +10 -10
  92. data/lib/ecoportal/api/graphql/fragment/action.rb +65 -65
  93. data/lib/ecoportal/api/graphql/fragment/action_category.rb +17 -17
  94. data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +20 -20
  95. data/lib/ecoportal/api/graphql/fragment/locations_error.rb +19 -19
  96. data/lib/ecoportal/api/graphql/fragment/page.rb +1 -0
  97. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +10 -3
  98. data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -15
  99. data/lib/ecoportal/api/graphql/fragment/pagination.rb +17 -17
  100. data/lib/ecoportal/api/graphql/fragment/permissions.rb +0 -2
  101. data/lib/ecoportal/api/graphql/fragment/template_change_log.rb +102 -0
  102. data/lib/ecoportal/api/graphql/fragment.rb +1 -0
  103. data/lib/ecoportal/api/graphql/helpers/locations_tree.rb +41 -41
  104. data/lib/ecoportal/api/graphql/helpers.rb +10 -10
  105. data/lib/ecoportal/api/graphql/input/action/archive.rb +14 -14
  106. data/lib/ecoportal/api/graphql/input/action/create.rb +13 -13
  107. data/lib/ecoportal/api/graphql/input/action.rb +14 -14
  108. data/lib/ecoportal/api/graphql/input/contractor_entity/create.rb +14 -14
  109. data/lib/ecoportal/api/graphql/input/contractor_entity/destroy.rb +13 -13
  110. data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -16
  111. data/lib/ecoportal/api/graphql/input/id_diff.rb +11 -11
  112. data/lib/ecoportal/api/graphql/input/location_structure/archive_command.rb +13 -13
  113. data/lib/ecoportal/api/graphql/input/location_structure/delete_command.rb +13 -13
  114. data/lib/ecoportal/api/graphql/input/location_structure/draft/create.rb +15 -15
  115. data/lib/ecoportal/api/graphql/input/location_structure/draft/delete.rb +15 -15
  116. data/lib/ecoportal/api/graphql/input/location_structure/draft/drop_bad_commands.rb +16 -16
  117. data/lib/ecoportal/api/graphql/input/location_structure/draft/publish.rb +16 -16
  118. data/lib/ecoportal/api/graphql/input/location_structure/draft.rb +19 -19
  119. data/lib/ecoportal/api/graphql/input/location_structure/insert_command.rb +21 -13
  120. data/lib/ecoportal/api/graphql/input/location_structure/move_command.rb +14 -14
  121. data/lib/ecoportal/api/graphql/input/location_structure/reorder_command.rb +13 -13
  122. data/lib/ecoportal/api/graphql/input/location_structure/unarchive_command.rb +13 -13
  123. data/lib/ecoportal/api/graphql/input/location_structure.rb +20 -20
  124. data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -14
  125. data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -13
  126. data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -13
  127. data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -18
  128. data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -13
  129. data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -13
  130. data/lib/ecoportal/api/graphql/input/page/review_task.rb +27 -14
  131. data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -14
  132. data/lib/ecoportal/api/graphql/input/page/update.rb +109 -6
  133. data/lib/ecoportal/api/graphql/input/page.rb +26 -26
  134. data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -18
  135. data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -16
  136. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +1 -1
  137. data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -14
  138. data/lib/ecoportal/api/graphql/input/register/create.rb +18 -18
  139. data/lib/ecoportal/api/graphql/input/register/update.rb +15 -15
  140. data/lib/ecoportal/api/graphql/input/register.rb +13 -13
  141. data/lib/ecoportal/api/graphql/input/search.rb +13 -13
  142. data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -234
  143. data/lib/ecoportal/api/graphql/input/search_conf.rb +1 -1
  144. data/lib/ecoportal/api/graphql/input/search_filter.rb +12 -12
  145. data/lib/ecoportal/api/graphql/input/search_sorter.rb +11 -11
  146. data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -18
  147. data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -18
  148. data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -18
  149. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -18
  150. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -18
  151. data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -18
  152. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +10 -9
  153. data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -18
  154. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -23
  155. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -18
  156. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -18
  157. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -18
  158. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -18
  159. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -18
  160. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -18
  161. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -18
  162. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -18
  163. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -18
  164. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -23
  165. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -18
  166. data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -18
  167. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +25 -19
  168. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -18
  169. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -18
  170. data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -18
  171. data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -18
  172. data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -18
  173. data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -18
  174. data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -18
  175. data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -18
  176. data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -18
  177. data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -18
  178. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +7 -2
  179. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +21 -18
  180. data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -19
  181. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -19
  182. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -18
  183. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -18
  184. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -18
  185. data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -18
  186. data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -18
  187. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -18
  188. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -18
  189. data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -22
  190. data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -18
  191. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +23 -19
  192. data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -18
  193. data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -18
  194. data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -18
  195. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_basic_settings.rb +7 -2
  196. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_container_uid.rb +1 -1
  197. data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -18
  198. data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -18
  199. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +28 -24
  200. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +8 -2
  201. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -20
  202. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -24
  203. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +8 -2
  204. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -20
  205. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -25
  206. data/lib/ecoportal/api/graphql/input/workflow_command/manage_copy_page_configuration.rb +47 -0
  207. data/lib/ecoportal/api/graphql/input/workflow_command/move_section_to_stage.rb +33 -0
  208. data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -18
  209. data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -18
  210. data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -18
  211. data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -18
  212. data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -18
  213. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -18
  214. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -18
  215. data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -18
  216. data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -18
  217. data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -18
  218. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -18
  219. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -18
  220. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -18
  221. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -18
  222. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -18
  223. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -18
  224. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -18
  225. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -18
  226. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -18
  227. data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -18
  228. data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -18
  229. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -18
  230. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -18
  231. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -18
  232. data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -18
  233. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -18
  234. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -18
  235. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -18
  236. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -18
  237. data/lib/ecoportal/api/graphql/input/workflow_command.rb +5 -0
  238. data/lib/ecoportal/api/graphql/interface/base_page.rb +14 -0
  239. data/lib/ecoportal/api/graphql/interface/location_structure/node.rb +43 -43
  240. data/lib/ecoportal/api/graphql/interface/location_structure.rb +14 -14
  241. data/lib/ecoportal/api/graphql/interface.rb +11 -11
  242. data/lib/ecoportal/api/graphql/logic/base_model.rb +15 -15
  243. data/lib/ecoportal/api/graphql/logic/base_query.rb +260 -193
  244. data/lib/ecoportal/api/graphql/logic/mutation.rb +59 -59
  245. data/lib/ecoportal/api/graphql/logic/payload.rb +42 -42
  246. data/lib/ecoportal/api/graphql/logic/query.rb +23 -23
  247. data/lib/ecoportal/api/graphql/logic/query_array.rb +30 -30
  248. data/lib/ecoportal/api/graphql/logic/query_connection.rb +48 -48
  249. data/lib/ecoportal/api/graphql/logic.rb +18 -18
  250. data/lib/ecoportal/api/graphql/model/account.rb +15 -15
  251. data/lib/ecoportal/api/graphql/model/action.rb +22 -22
  252. data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -10
  253. data/lib/ecoportal/api/graphql/model/contractor_entity.rb +12 -12
  254. data/lib/ecoportal/api/graphql/model/field.rb +10 -10
  255. data/lib/ecoportal/api/graphql/model/file_attachment.rb +10 -10
  256. data/lib/ecoportal/api/graphql/model/file_container.rb +11 -11
  257. data/lib/ecoportal/api/graphql/model/location_node.rb +14 -14
  258. data/lib/ecoportal/api/graphql/model/location_structure/draft/structure.rb +15 -15
  259. data/lib/ecoportal/api/graphql/model/location_structure/draft.rb +15 -15
  260. data/lib/ecoportal/api/graphql/model/location_structure.rb +16 -16
  261. data/lib/ecoportal/api/graphql/model/organization.rb +9 -0
  262. data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -14
  263. data/lib/ecoportal/api/graphql/model/page/data_field.rb +12 -12
  264. data/lib/ecoportal/api/graphql/model/page/phased/stage.rb +14 -14
  265. data/lib/ecoportal/api/graphql/model/page/phased.rb +17 -0
  266. data/lib/ecoportal/api/graphql/model/page/section.rb +12 -12
  267. data/lib/ecoportal/api/graphql/model/page_union.rb +21 -21
  268. data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -20
  269. data/lib/ecoportal/api/graphql/model/permissions.rb +6 -0
  270. data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -10
  271. data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -10
  272. data/lib/ecoportal/api/graphql/model/register.rb +10 -10
  273. data/lib/ecoportal/api/graphql/model/resource.rb +13 -13
  274. data/lib/ecoportal/api/graphql/model/temp_image.rb +10 -0
  275. data/lib/ecoportal/api/graphql/model/template/binding.rb +60 -0
  276. data/lib/ecoportal/api/graphql/model/template/change_log_entry.rb +19 -0
  277. data/lib/ecoportal/api/graphql/model/template/command_grouper.rb +107 -0
  278. data/lib/ecoportal/api/graphql/model/template/command_normalizer.rb +116 -0
  279. data/lib/ecoportal/api/graphql/model/template/command_synthesis.rb +262 -0
  280. data/lib/ecoportal/api/graphql/model/template/field.rb +68 -0
  281. data/lib/ecoportal/api/graphql/model/template/force.rb +65 -0
  282. data/lib/ecoportal/api/graphql/model/template/helper.rb +32 -0
  283. data/lib/ecoportal/api/graphql/model/template/instance.rb +202 -0
  284. data/lib/ecoportal/api/graphql/model/template/node.rb +78 -0
  285. data/lib/ecoportal/api/graphql/model/template/option.rb +49 -0
  286. data/lib/ecoportal/api/graphql/model/template/read.rb +164 -0
  287. data/lib/ecoportal/api/graphql/model/template/section.rb +82 -0
  288. data/lib/ecoportal/api/graphql/model/template/stage.rb +49 -0
  289. data/lib/ecoportal/api/graphql/model/template/staged_executor.rb +236 -0
  290. data/lib/ecoportal/api/graphql/model/template.rb +37 -1
  291. data/lib/ecoportal/api/graphql/model/user.rb +14 -14
  292. data/lib/ecoportal/api/graphql/model.rb +1 -0
  293. data/lib/ecoportal/api/graphql/mutation/action/archive.rb +41 -41
  294. data/lib/ecoportal/api/graphql/mutation/action/create.rb +40 -40
  295. data/lib/ecoportal/api/graphql/mutation/action/update.rb +40 -40
  296. data/lib/ecoportal/api/graphql/mutation/action.rb +14 -14
  297. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +1 -1
  298. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +1 -1
  299. data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -13
  300. data/lib/ecoportal/api/graphql/mutation/contractor_entity/create.rb +40 -40
  301. data/lib/ecoportal/api/graphql/mutation/contractor_entity/destroy.rb +40 -40
  302. data/lib/ecoportal/api/graphql/mutation/contractor_entity/update.rb +40 -40
  303. data/lib/ecoportal/api/graphql/mutation/file_container/upload.rb +11 -4
  304. data/lib/ecoportal/api/graphql/mutation/image/upload.rb +88 -0
  305. data/lib/ecoportal/api/graphql/mutation/image.rb +14 -0
  306. data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +54 -54
  307. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +55 -55
  308. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +54 -54
  309. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/delete.rb +43 -43
  310. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +56 -56
  311. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +66 -66
  312. data/lib/ecoportal/api/graphql/mutation/location_structure/draft.rb +18 -18
  313. data/lib/ecoportal/api/graphql/mutation/location_structure.rb +13 -13
  314. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +1 -1
  315. data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -40
  316. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +1 -1
  317. data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -50
  318. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +1 -1
  319. data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -43
  320. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +1 -1
  321. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +1 -1
  322. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +1 -1
  323. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +1 -1
  324. data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -40
  325. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +1 -1
  326. data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -40
  327. data/lib/ecoportal/api/graphql/mutation/page.rb +28 -28
  328. data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -35
  329. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +1 -1
  330. data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -37
  331. data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -35
  332. data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -15
  333. data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -35
  334. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +1 -1
  335. data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -35
  336. data/lib/ecoportal/api/graphql/mutation/register.rb +14 -14
  337. data/lib/ecoportal/api/graphql/mutation/template/create.rb +35 -3
  338. data/lib/ecoportal/api/graphql/mutation/template/update.rb +4 -2
  339. data/lib/ecoportal/api/graphql/mutation.rb +1 -0
  340. data/lib/ecoportal/api/graphql/payload/action/archive.rb +13 -13
  341. data/lib/ecoportal/api/graphql/payload/action/create.rb +13 -13
  342. data/lib/ecoportal/api/graphql/payload/action/update.rb +13 -13
  343. data/lib/ecoportal/api/graphql/payload/action.rb +14 -14
  344. data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -12
  345. data/lib/ecoportal/api/graphql/payload/contractor_entity/create.rb +13 -13
  346. data/lib/ecoportal/api/graphql/payload/contractor_entity/destroy.rb +12 -12
  347. data/lib/ecoportal/api/graphql/payload/contractor_entity/update.rb +12 -12
  348. data/lib/ecoportal/api/graphql/payload/images_upload.rb +14 -0
  349. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +2 -2
  350. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +2 -2
  351. data/lib/ecoportal/api/graphql/payload/location_structure/apply_commands.rb +51 -51
  352. data/lib/ecoportal/api/graphql/payload/location_structure/command_execution_result.rb +50 -50
  353. data/lib/ecoportal/api/graphql/payload/location_structure/draft/add_commands.rb +30 -30
  354. data/lib/ecoportal/api/graphql/payload/location_structure/draft/delete.rb +30 -30
  355. data/lib/ecoportal/api/graphql/payload/location_structure/draft/drop_bad_commands.rb +43 -43
  356. data/lib/ecoportal/api/graphql/payload/location_structure/draft/publish.rb +63 -63
  357. data/lib/ecoportal/api/graphql/payload/location_structure/draft.rb +18 -18
  358. data/lib/ecoportal/api/graphql/payload/location_structure.rb +14 -14
  359. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +1 -1
  360. data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -13
  361. data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -13
  362. data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -13
  363. data/lib/ecoportal/api/graphql/payload/page/draft.rb +1 -1
  364. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +1 -1
  365. data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -13
  366. data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -13
  367. data/lib/ecoportal/api/graphql/payload/preset_view.rb +1 -1
  368. data/lib/ecoportal/api/graphql/payload/register.rb +1 -1
  369. data/lib/ecoportal/api/graphql/payload.rb +1 -0
  370. data/lib/ecoportal/api/graphql/query/action.rb +33 -33
  371. data/lib/ecoportal/api/graphql/query/action_categories.rb +53 -53
  372. data/lib/ecoportal/api/graphql/query/actions.rb +43 -43
  373. data/lib/ecoportal/api/graphql/query/contractor_entities.rb +53 -53
  374. data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -76
  375. data/lib/ecoportal/api/graphql/query/location_classifications.rb +39 -39
  376. data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -47
  377. data/lib/ecoportal/api/graphql/query/pages.rb +59 -59
  378. data/lib/ecoportal/api/graphql/query/permissions.rb +1 -1
  379. data/lib/ecoportal/api/graphql/query/person_members.rb +76 -0
  380. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +1 -1
  381. data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -83
  382. data/lib/ecoportal/api/graphql/query/template_change_log.rb +118 -0
  383. data/lib/ecoportal/api/graphql/query/templates.rb +53 -53
  384. data/lib/ecoportal/api/graphql/query.rb +2 -0
  385. data/lib/ecoportal/api/graphql_version.rb +1 -1
  386. data/lib/ecoportal/api-graphql.rb +10 -10
  387. metadata +45 -9
  388. data/README.md +0 -24
  389. data/lib/ecoportal/api/common/graphql/CLAUDE.md +0 -36
  390. data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +0 -28
  391. data/lib/ecoportal/api/graphql/CLAUDE.md +0 -37
  392. data/lib/ecoportal/api/graphql/base/CLAUDE.md +0 -50
  393. data/lib/ecoportal/api/graphql/builder/CLAUDE.md +0 -65
@@ -1,105 +1,105 @@
1
- module Ecoportal
2
- module API
3
- module Common
4
- module GraphQL
5
- module HashHelpers
6
- class << self
7
- def included(base)
8
- base.send(:include, InstanceMethods)
9
- end
10
- end
11
-
12
- module InstanceMethods
13
- def keys_to_sym_deep(value)
14
- transform_keys_deep(value, &:to_sym)
15
- end
16
-
17
- def keys_to_s_deep(value)
18
- transform_keys_deep(value, &:to_s)
19
- end
20
-
21
- def except_keys(value, *keys)
22
- return value unless value.is_a?(Hash)
23
-
24
- deep_dup(value).tap do |out|
25
- keys.each {|key| out.delete(key)}
26
- end
27
- end
28
-
29
- # Targetted removal of keys in `target` that are `nil`
30
- def remove_nil_keys_deep(value, target: [])
31
- remove_keys_deep_if(value, target: target, &:nil?)
32
- end
33
-
34
- # Based on Rails support.
35
- def deep_dup(data)
36
- case data
37
- when Hash
38
- data.each_with_object({}) do |(k, v), copy|
39
- if k.is_a?(::String) || k.is_a?(::Symbol)
40
- copy[k] = deep_dup(v)
41
- else
42
- copy.delete(k)
43
- copy[deep_dup(k)] = deep_dup(v)
44
- end
45
- end
46
- when Enumerable
47
- data.map {|d| deep_dup(d)}
48
- else
49
- data.dup
50
- end
51
- end
52
-
53
- private
54
-
55
- # Iterator
56
- # @param value [Variadic]
57
- # @return [value.class]
58
- def transform_keys_deep(value, &block)
59
- case value
60
- when Hash
61
- value.dup.each_with_object({}) do |(k, v), out|
62
- k_val = block_given?? yield(k) : k
63
- out[k_val] = transform_keys_deep(v, &block)
64
- end
65
- when Enumerable
66
- value.map {|v| transform_keys_deep(v, &block)}
67
- else
68
- value
69
- end
70
- end
71
-
72
- # Targetted removal of keys in `target` that meet
73
- # the `block` condition.
74
- # @param value [Variadic]
75
- # @param target [Array<String>, Array<Symbol>] the target
76
- # `keys` to yield `block` onto.
77
- # @return [value.class]
78
- def remove_keys_deep_if(value, target: [], &block)
79
- msg = 'Expected block. None given.'
80
- raise ArgumentError, msg unless block_given?
81
-
82
- target = [target].flatten.compact
83
- return value if target.empty?
84
-
85
- case value
86
- when Hash
87
- value.dup.each_with_object({}) do |(k, v), out|
88
- next if yield(v) && target.include?(k) # skip
89
-
90
- out[k] = remove_nil_keys_deep(v, target: target, &block)
91
- end
92
- when Enumerable
93
- value.map {|v| remove_nil_keys_deep(v, target: target, &block)}
94
- else
95
- value
96
- end
97
- end
98
- end
99
-
100
- extend InstanceMethods
101
- end
102
- end
103
- end
104
- end
105
- end
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module GraphQL
5
+ module HashHelpers
6
+ class << self
7
+ def included(base)
8
+ base.send(:include, InstanceMethods)
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def keys_to_sym_deep(value)
14
+ transform_keys_deep(value, &:to_sym)
15
+ end
16
+
17
+ def keys_to_s_deep(value)
18
+ transform_keys_deep(value, &:to_s)
19
+ end
20
+
21
+ def except_keys(value, *keys)
22
+ return value unless value.is_a?(Hash)
23
+
24
+ deep_dup(value).tap do |out|
25
+ keys.each {|key| out.delete(key)}
26
+ end
27
+ end
28
+
29
+ # Targetted removal of keys in `target` that are `nil`
30
+ def remove_nil_keys_deep(value, target: [])
31
+ remove_keys_deep_if(value, target: target, &:nil?)
32
+ end
33
+
34
+ # Based on Rails support.
35
+ def deep_dup(data)
36
+ case data
37
+ when Hash
38
+ data.each_with_object({}) do |(k, v), copy|
39
+ if k.is_a?(::String) || k.is_a?(::Symbol)
40
+ copy[k] = deep_dup(v)
41
+ else
42
+ copy.delete(k)
43
+ copy[deep_dup(k)] = deep_dup(v)
44
+ end
45
+ end
46
+ when Enumerable
47
+ data.map {|d| deep_dup(d)}
48
+ else
49
+ data.dup
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Iterator
56
+ # @param value [Variadic]
57
+ # @return [value.class]
58
+ def transform_keys_deep(value, &block)
59
+ case value
60
+ when Hash
61
+ value.dup.each_with_object({}) do |(k, v), out|
62
+ k_val = block_given?? yield(k) : k
63
+ out[k_val] = transform_keys_deep(v, &block)
64
+ end
65
+ when Enumerable
66
+ value.map {|v| transform_keys_deep(v, &block)}
67
+ else
68
+ value
69
+ end
70
+ end
71
+
72
+ # Targetted removal of keys in `target` that meet
73
+ # the `block` condition.
74
+ # @param value [Variadic]
75
+ # @param target [Array<String>, Array<Symbol>] the target
76
+ # `keys` to yield `block` onto.
77
+ # @return [value.class]
78
+ def remove_keys_deep_if(value, target: [], &block)
79
+ msg = 'Expected block. None given.'
80
+ raise ArgumentError, msg unless block_given?
81
+
82
+ target = [target].flatten.compact
83
+ return value if target.empty?
84
+
85
+ case value
86
+ when Hash
87
+ value.dup.each_with_object({}) do |(k, v), out|
88
+ next if yield(v) && target.include?(k) # skip
89
+
90
+ out[k] = remove_nil_keys_deep(v, target: target, &block)
91
+ end
92
+ when Enumerable
93
+ value.map {|v| remove_nil_keys_deep(v, target: target, &block)}
94
+ else
95
+ value
96
+ end
97
+ end
98
+ end
99
+
100
+ extend InstanceMethods
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -2,7 +2,88 @@ module Ecoportal
2
2
  module API
3
3
  module Common
4
4
  module GraphQL
5
- class ResponseError < StandardError; end
5
+ # Raised when a 200 response carries a top-level `errors[]` array.
6
+ #
7
+ # ★ Carries the RAW error entries, so `extensions` survives. Before this, the
8
+ # class collapsed the response into the joined `message` text and threw the rest
9
+ # away — which made it impossible for Ruby tooling to tell a permission problem
10
+ # from a request problem, the exact diagnosis we tell customers to perform.
11
+ # Tracked as Q-27 in `docs/open-questions.md`.
12
+ #
13
+ # The backend's error contract (`app/graphql/eco_portal_schema.rb`, verified
14
+ # 2026-09-01) puts these keys under `extensions`:
15
+ #
16
+ # | key | when | shape |
17
+ # |----------------|--------------------------|-------|
18
+ # | `code` | always | string: `unauthorized`, `document_not_found`, `unprocessable_entity`, `write_conflict`, `internal_server_error`, … |
19
+ # | `fullMessages` | `unauthorized` | array of human strings (`exp.result.reasons.full_messages`) |
20
+ # | `details` | `unauthorized` | ActionPolicy `reasons.details` — usually a Hash whose **key names the failing policy** and whose value lists the rules |
21
+ # | `problems` | validation, and only when the server runs with `SHOW_GRAPHQL_EXCEPTION` | record errors |
22
+ #
23
+ # ⚠ `details` is NOT reliably a Hash. Two call sites emit an Array instead
24
+ # (`mutations/pages/send_as_email.rb` sends inaccessible ids;
25
+ # `queries/pages/templates/fetcher.rb` sends `[msg]`). Accessors here therefore
26
+ # return whatever the server sent, unwrapped but uncoerced — callers must not
27
+ # assume a shape.
28
+ class ResponseError < StandardError
29
+ # @return [Array<Hash>] the raw `errors[]` entries, exactly as received.
30
+ attr_reader :errors
31
+
32
+ # @param msg [String, nil] explicit message; defaults to the joined error messages.
33
+ # @param errors [Array<Hash>] raw top-level `errors[]` entries.
34
+ def initialize(msg = nil, errors: [])
35
+ @errors = Array(errors)
36
+ super(msg || build_message)
37
+ end
38
+
39
+ # @return [Array<Hash>] the `extensions` object of each error that carried one.
40
+ def extensions
41
+ errors.filter_map { |err| err['extensions'] }
42
+ end
43
+
44
+ # @return [Array<String>] every `extensions.code` present, in order.
45
+ def codes
46
+ extensions.filter_map { |ext| ext['code'] }
47
+ end
48
+
49
+ # ★ The fastest way to tell a permission problem from a request problem.
50
+ # @return [Array] every `extensions.details` present. Shape is the server's —
51
+ # typically a Hash keyed by the failing policy, but sometimes an Array.
52
+ def details
53
+ extensions.filter_map { |ext| ext['details'] }
54
+ end
55
+
56
+ # @return [Array<String>] flattened `extensions.fullMessages`.
57
+ def full_messages
58
+ extensions.flat_map { |ext| Array(ext['fullMessages']) }
59
+ end
60
+
61
+ # @return [Boolean] whether ANY error was an authorization refusal. Use this to
62
+ # branch on "you cannot" vs "that request was wrong" — do not pattern-match
63
+ # the message text, which is prose and drifts.
64
+ def unauthorized?
65
+ codes.include?('unauthorized')
66
+ end
67
+
68
+ private
69
+
70
+ # The joined messages stay FIRST and unchanged, so existing callers matching on
71
+ # the message text keep working; the diagnosis is appended after them.
72
+ def build_message
73
+ base = errors.filter_map { |err| err['message'] }.join('; ')
74
+ base = 'GraphQL request failed' if base.empty?
75
+ [base, diagnosis].compact.join(' | ')
76
+ end
77
+
78
+ def diagnosis
79
+ parts = []
80
+ parts << "code: #{codes.uniq.join(', ')}" if codes.any?
81
+ parts << "details: #{details.inspect}" if details.any?
82
+ return if parts.empty?
83
+
84
+ parts.join(' | ')
85
+ end
86
+ end
6
87
 
7
88
  class HttpClient < Common::Client
8
89
  class << self
@@ -15,9 +96,11 @@ module Ecoportal
15
96
  end
16
97
  end
17
98
 
18
- ENDPOINT_PATH = 'external/graphql'.freeze
19
- READ_TIMEOUT = 90
20
- WRITE_TIMEOUT = 90
99
+ ENDPOINT_PATH = 'external/graphql'.freeze
100
+ CONNECT_TIMEOUT = 30
101
+ READ_TIMEOUT = 90
102
+ WRITE_TIMEOUT = 90
103
+ KEEP_ALIVE_TIMEOUT = 5
21
104
 
22
105
  attr_reader :host, :version
23
106
 
@@ -56,6 +139,36 @@ module Ecoportal
56
139
 
57
140
  # Creates a HTTP object adding the `X-ApiKey` or `X-ECOPORTAL-API-KEY` param to the header, depending on the API version.
58
141
  # @note It configures HTTP so it only allows body data in json format.
142
+ # @note This is the ONLY place in the gem's dependency chain that talks to the
143
+ # server via http.rb (`require 'http'`, pulled in by `Ecoportal::API::Common::Client`
144
+ # from the `ecoportal-api` gem) -- every live GraphQL query goes through `#execute`
145
+ # below, called from `Logic::BaseQuery#graphql_query` via `client.http_client.execute`,
146
+ # and `AuthService#auth_http_client` (`version: 'http'`) reuses this same
147
+ # `base_request` for the OAuth token POST. `Common::GraphQL::Client` (graphlient) is a
148
+ # separate, unrelated path: it never reaches http.rb at all.
149
+ #
150
+ # Without an explicit `connect:`, http.rb's `HTTP::Timeout::PerOperation` falls back
151
+ # to its own default of 0.25s for CONNECT even though read/write were already given
152
+ # explicitly (per_operation.rb: `options.fetch(:connect_timeout, CONNECT_TIMEOUT)`,
153
+ # `CONNECT_TIMEOUT = 0.25`). `connect_ssl` (`http/timeout/per_operation.rb`) uses
154
+ # that same `@connect_timeout` for the TLS handshake via `rescue_readable`/
155
+ # `rescue_writable` (`http/timeout/null.rb`) -- whose error text is unconditionally
156
+ # "Read timed out after #{@read_timeout} seconds", so a slow TLS handshake on the EU
157
+ # instance surfaced as `HTTP::TimeoutError: Read timed out after 0.25 seconds`
158
+ # even though `@read_timeout` itself was correctly 90 the whole time. Live-confirmed
159
+ # 2026-09-08 via backtrace: `connect_ssl` -> `start_tls` (`http/connection.rb`) ->
160
+ # `Common::Client#post` -> `AuthService#session_token_data` ->
161
+ # `Common::GraphQL::Client#initialize`. Read/write were never the problem; only
162
+ # connect was missing.
163
+ # @note The client is made PERSISTENT (`HTTP.persistent`) to `base_url`, not a
164
+ # plain one-shot client. Confirmed live 2026-09-08: eu.live.ecoportal.com
165
+ # closes an HTTP/1.1 request carrying `Connection: close` -- what a
166
+ # non-persistent http.rb client always sends -- without ever responding. A
167
+ # persistent client sends `Connection: keep-alive` and reuses the TCP/TLS
168
+ # connection instead, which the SAME Ruby stack proved fine against EU. This
169
+ # also matches Cloudflare keep-alive hygiene; Sydney is fine either way. One
170
+ # `HttpClient` is bound to one `host`, so `HTTP::StateError` (raised only on a
171
+ # cross-origin request against a persistent client) cannot occur here.
59
172
  # @return [HTTP] HTTP object.
60
173
  def base_request
61
174
  @base_request ||=
@@ -70,9 +183,10 @@ module Ecoportal
70
183
  HTTP.headers('Authorization' => "Bearer #{session_token(host: host)}")
71
184
  end.then do |request|
72
185
  request ||= HTTP
73
- request.accept(:json).timeout(
74
- read: READ_TIMEOUT,
75
- write: WRITE_TIMEOUT
186
+ request.persistent(base_url, timeout: KEEP_ALIVE_TIMEOUT).accept(:json).timeout(
187
+ connect: CONNECT_TIMEOUT,
188
+ read: READ_TIMEOUT,
189
+ write: WRITE_TIMEOUT
76
190
  )
77
191
  end
78
192
  end
@@ -184,7 +298,9 @@ module Ecoportal
184
298
 
185
299
  parsed = response.body
186
300
  errors = parsed && parsed['errors']
187
- raise ResponseError, errors.map { |e| e['message'] }.join('; ') if errors&.any?
301
+ # Pass the raw entries through: `extensions` (and therefore the failing
302
+ # policy under `extensions.details`) is the whole point — see Q-27.
303
+ raise ResponseError.new(errors: errors) if errors&.any?
188
304
 
189
305
  parsed
190
306
  end
@@ -1,54 +1,54 @@
1
- module Ecoportal
2
- module API
3
- module Common
4
- module GraphQL
5
- class Model
6
- # @todo
7
- # 1. `#as_input` vs `#to_input` **or** named argument `klass`
8
- # One gets the `input` to be already used in a mutation query.
9
- # The other gets the `Input` class object out of the `Base/Model`.
10
- # - To figure out when `as_update` should be called.
11
- # 2. Each Base/Model should have its `Input` class counterpart.
12
- # This could be auto-inflected (via namespace constant lookup)
13
- # at some point.
14
- # 3. Think if a service class can be used to this purpose
15
- # (i.e. decorator of DiffService).
16
- module AsInput
17
- class << self
18
- def included(base)
19
- super
20
-
21
- base.send(:include, Model::Diffable)
22
- base.extend ClassMethods
23
- end
24
- end
25
-
26
- module ClassMethods
27
- def as_input(hash, clientMutationId: '')
28
- hash_input = Ecoportal::API::Common::GraphQL::HashHelpers.
29
- keys_to_sym_deep(hash)
30
-
31
- hash_input.merge!(clientMutationId: clientMutationId)
32
-
33
- Ecoportal::API::Common::GraphQL::HashHelpers.
34
- remove_nil_keys_deep(hash_input, target: :id)
35
- end
36
- end
37
-
38
- # INSTANCE METHODS
39
-
40
- def as_input(target_class: nil, clientMutationId: '')
41
- return target_class.from_model(self, client_mutation_id: clientMutationId) if target_class
42
-
43
- diff = as_update
44
- return nil if diff.nil?
45
-
46
- diff[:patchVer] = patchVer if respond_to?(:patchVer) && !patchVer.nil?
47
- self.class.as_input(diff, clientMutationId: clientMutationId)
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module GraphQL
5
+ class Model
6
+ # @todo
7
+ # 1. `#as_input` vs `#to_input` **or** named argument `klass`
8
+ # One gets the `input` to be already used in a mutation query.
9
+ # The other gets the `Input` class object out of the `Base/Model`.
10
+ # - To figure out when `as_update` should be called.
11
+ # 2. Each Base/Model should have its `Input` class counterpart.
12
+ # This could be auto-inflected (via namespace constant lookup)
13
+ # at some point.
14
+ # 3. Think if a service class can be used to this purpose
15
+ # (i.e. decorator of DiffService).
16
+ module AsInput
17
+ class << self
18
+ def included(base)
19
+ super
20
+
21
+ base.send(:include, Model::Diffable)
22
+ base.extend ClassMethods
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def as_input(hash, clientMutationId: '')
28
+ hash_input = Ecoportal::API::Common::GraphQL::HashHelpers.
29
+ keys_to_sym_deep(hash)
30
+
31
+ hash_input.merge!(clientMutationId: clientMutationId)
32
+
33
+ Ecoportal::API::Common::GraphQL::HashHelpers.
34
+ remove_nil_keys_deep(hash_input, target: :id)
35
+ end
36
+ end
37
+
38
+ # INSTANCE METHODS
39
+
40
+ def as_input(target_class: nil, clientMutationId: '')
41
+ return target_class.from_model(self, client_mutation_id: clientMutationId) if target_class
42
+
43
+ diff = as_update
44
+ return nil if diff.nil?
45
+
46
+ diff[:patchVer] = patchVer if respond_to?(:patchVer) && !patchVer.nil?
47
+ self.class.as_input(diff, clientMutationId: clientMutationId)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,49 +1,49 @@
1
- module Ecoportal::API::Common::GraphQL::Model::Diffable
2
- class ClassicDiffService
3
- include Ecoportal::API::Common::GraphQL::Model::Diffable::HashDiffNesting
4
-
5
- attr_reader :subject
6
-
7
- def initialize(subject, ignore: [])
8
- msg = 'Expecting Ecoportal::API::Common::Content::DoubleModel. '
9
- msg << "Given: #{subject.class}"
10
- raise ArgumentError, msg unless subject.is_a?(Ecoportal::API::Common::Content::DoubleModel)
11
-
12
- @subject = subject
13
- @ignored = to_a(ignore)
14
- end
15
-
16
- def diff(ignore: [])
17
- classic_diff(ignore: ignore)
18
- end
19
-
20
- protected
21
-
22
- def classic_diff(ignore: [])
23
- change_diff(
24
- curr_doc,
25
- prev_doc,
26
- ignore: to_a(ignored, ignore)
27
- )
28
- end
29
-
30
- private
31
-
32
- attr_reader :ignored
33
-
34
- def curr_doc(target = subject)
35
- return target.as_json if target.respond_to?(:as_json)
36
- return target.doc if target.respond_to?(:doc)
37
-
38
- raise "Can't fetch underlying document for instance of #{target.class}."
39
- end
40
-
41
- def prev_doc(target = subject)
42
- return target.original_doc if target.respond_to?(:original_doc)
43
-
44
- raise "Can't fetch original underlying model for instance of #{target.class}."
45
- rescue NoMethodError
46
- nil
47
- end
48
- end
49
- end
1
+ module Ecoportal::API::Common::GraphQL::Model::Diffable
2
+ class ClassicDiffService
3
+ include Ecoportal::API::Common::GraphQL::Model::Diffable::HashDiffNesting
4
+
5
+ attr_reader :subject
6
+
7
+ def initialize(subject, ignore: [])
8
+ msg = 'Expecting Ecoportal::API::Common::Content::DoubleModel. '
9
+ msg << "Given: #{subject.class}"
10
+ raise ArgumentError, msg unless subject.is_a?(Ecoportal::API::Common::Content::DoubleModel)
11
+
12
+ @subject = subject
13
+ @ignored = to_a(ignore)
14
+ end
15
+
16
+ def diff(ignore: [])
17
+ classic_diff(ignore: ignore)
18
+ end
19
+
20
+ protected
21
+
22
+ def classic_diff(ignore: [])
23
+ change_diff(
24
+ curr_doc,
25
+ prev_doc,
26
+ ignore: to_a(ignored, ignore)
27
+ )
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :ignored
33
+
34
+ def curr_doc(target = subject)
35
+ return target.as_json if target.respond_to?(:as_json)
36
+ return target.doc if target.respond_to?(:doc)
37
+
38
+ raise "Can't fetch underlying document for instance of #{target.class}."
39
+ end
40
+
41
+ def prev_doc(target = subject)
42
+ return target.original_doc if target.respond_to?(:original_doc)
43
+
44
+ raise "Can't fetch original underlying model for instance of #{target.class}."
45
+ rescue NoMethodError
46
+ nil
47
+ end
48
+ end
49
+ end