ecoportal-api-graphql 1.3.4 → 1.3.9

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 (513) hide show
  1. checksums.yaml +4 -4
  2. data/.ai-assistance/bridge/CLAUDE.md +338 -0
  3. data/.ai-assistance/bridge/archive/.gitkeep +0 -0
  4. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +29 -0
  5. data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +18 -0
  6. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +42 -0
  7. data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +115 -0
  8. data/.ai-assistance/bridge/context/gemini-review-prompt.txt +48 -0
  9. data/.ai-assistance/bridge/context/gemini-review-response.md +104 -0
  10. data/.ai-assistance/bridge/context/project.md +42 -0
  11. data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
  12. data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
  13. data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +48 -0
  14. data/.ai-assistance/bridge/queue/.gitkeep +1 -0
  15. data/.ai-assistance/capabilities/CLAUDE.md +27 -0
  16. data/.ai-assistance/capabilities/assumptions-log.md +80 -0
  17. data/.ai-assistance/capabilities/code.md +47 -0
  18. data/.ai-assistance/capabilities/connectors.md +37 -0
  19. data/.ai-assistance/capabilities/cowork.md +55 -0
  20. data/.ai-assistance/code/OVERVIEW.md +155 -0
  21. data/.ai-assistance/code/data_fields.md +242 -0
  22. data/.ai-assistance/code/dependencies.md +151 -0
  23. data/.ai-assistance/code/diff_as_input.md +234 -0
  24. data/.ai-assistance/code/diff_service_deep_dive.md +192 -0
  25. data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +55 -0
  26. data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +181 -0
  27. data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +192 -0
  28. data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +147 -0
  29. data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +277 -0
  30. data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +200 -0
  31. data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +228 -0
  32. data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +197 -0
  33. data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +243 -0
  34. data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +196 -0
  35. data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +132 -0
  36. data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +187 -0
  37. data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +236 -0
  38. data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +183 -0
  39. data/.ai-assistance/code/ecoportal_schema_reference.md +240 -0
  40. data/.ai-assistance/code/graphql_domain_knowledge.md +230 -0
  41. data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +71 -0
  42. data/.ai-assistance/code/refactoring/opportunities.md +251 -0
  43. data/.ai-assistance/code/schema_analysis.md +321 -0
  44. data/.ai-assistance/code/search_filters.md +868 -0
  45. data/.ai-assistance/code/spec_coverage.md +73 -0
  46. data/.ai-assistance/code/workflow-command-guide.md +438 -0
  47. data/.ai-assistance/code/workflow-space.md +353 -0
  48. data/.ai-assistance/conventions/CLAUDE.md +30 -0
  49. data/.ai-assistance/conventions/code-working-tree-protocol.md +199 -0
  50. data/.ai-assistance/conventions/gitignore-rules.md +42 -0
  51. data/.ai-assistance/conventions/permission-guidance.md +120 -0
  52. data/.ai-assistance/integrations/README.md +70 -0
  53. data/.ai-assistance/integrations/gitkraken-mcp.md +107 -0
  54. data/.ai-assistance/integrations/gitlab-mcp.md +123 -0
  55. data/.ai-assistance/integrations/local-git.md +60 -0
  56. data/.ai-assistance/local_paths.example.md +17 -0
  57. data/.ai-assistance/projects/TODO.md +97 -0
  58. data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +168 -0
  59. data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +60 -0
  60. data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +267 -0
  61. data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +53 -0
  62. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +102 -0
  63. data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +139 -0
  64. data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +93 -0
  65. data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +244 -0
  66. data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +266 -0
  67. data/.ai-assistance/projects/eco-helpers-compat/TODO.md +86 -0
  68. data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +101 -0
  69. data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +177 -0
  70. data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +161 -0
  71. data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +125 -0
  72. data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +126 -0
  73. data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +256 -0
  74. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +122 -0
  75. data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +148 -0
  76. data/.ai-assistance/projects/page-model/DECISIONS.md +245 -0
  77. data/.ai-assistance/projects/page-model/TODO.md +190 -0
  78. data/.ai-assistance/projects/search-filter-builder/INTENT.md +107 -0
  79. data/.ai-assistance/projects/search-filter-builder/TODO.md +131 -0
  80. data/.ai-assistance/projects/template-maintenance/DESIGN.md +134 -0
  81. data/.ai-assistance/projects/workflow-space/TODO.md +213 -0
  82. data/.ai-assistance/reinstall-claude-desktop-windows.md +136 -0
  83. data/.ai-assistance/scripts/CLAUDE.md +150 -0
  84. data/.ai-assistance/scripts/bridge-init.sh +86 -0
  85. data/.ai-assistance/scripts/bridge-status.sh +44 -0
  86. data/.ai-assistance/scripts/capabilities-check.ts +104 -0
  87. data/.ai-assistance/scripts/check-outbox.sh +43 -0
  88. data/.ai-assistance/scripts/dep_graph.rb +91 -0
  89. data/.ai-assistance/scripts/lock-acquire.sh +103 -0
  90. data/.ai-assistance/scripts/lock-multi.sh +124 -0
  91. data/.ai-assistance/scripts/lock-queue.sh +94 -0
  92. data/.ai-assistance/scripts/setup-mcps.test.ts +188 -0
  93. data/.ai-assistance/scripts/setup-mcps.ts +234 -0
  94. data/.ai-assistance/scripts/task-complete.ts +74 -0
  95. data/.ai-assistance/scripts/task-create.ts +75 -0
  96. data/.ai-assistance/scripts/task-read.ts +125 -0
  97. data/.ai-assistance/scripts/token-logger.js +220 -0
  98. data/.ai-assistance/scripts/token-report.ts +158 -0
  99. data/.ai-assistance/scripts/token-session-start.js +66 -0
  100. data/.ai-assistance/skills/ai-instructions/SKILL.md +48 -0
  101. data/.ai-assistance/skills/code-specs/SKILL.md +69 -0
  102. data/.ai-assistance/skills/corporate-policies/SKILL.md +201 -0
  103. data/.ai-assistance/skills/dep-graph/SKILL.md +139 -0
  104. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +417 -0
  105. data/.ai-assistance/skills/gemini-assist/SKILL.md +63 -0
  106. data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +205 -0
  107. data/.ai-assistance/skills/gemini-assist/gemini_ask.py +1 -0
  108. data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +240 -0
  109. data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +25 -0
  110. data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +261 -0
  111. data/.ai-assistance/skills/project-cycle/SKILL.md +177 -0
  112. data/.ai-assistance/skills/refactor/SKILL.md +62 -0
  113. data/.ai-assistance/skills/rubocop/SKILL.md +93 -0
  114. data/.ai-assistance/skills/ruby-scripting/SKILL.md +215 -0
  115. data/.ai-assistance/skills/spec-generation/SKILL.md +72 -0
  116. data/.ai-assistance/standards-version.json +21 -0
  117. data/.ai-assistance/token-budget.json +32 -0
  118. data/.ai-assistance/version.json +39 -0
  119. data/.claude/settings.json +146 -0
  120. data/.env.example +18 -0
  121. data/.gitattributes +15 -0
  122. data/.gitignore +13 -0
  123. data/.rubocop.yml +121 -97
  124. data/CHANGELOG.md +673 -470
  125. data/CLAUDE.md +232 -0
  126. data/Gemfile +30 -6
  127. data/Rakefile +90 -38
  128. data/docs/worklog.md +574 -0
  129. data/ecoportal-api-graphql.gemspec +40 -40
  130. data/lib/ecoportal/api/common/graphql/CLAUDE.md +36 -0
  131. data/lib/ecoportal/api/common/graphql/auth_service.rb +12 -14
  132. data/lib/ecoportal/api/common/graphql/client.rb +11 -3
  133. data/lib/ecoportal/api/common/graphql/http_client.rb +117 -29
  134. data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
  135. data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
  136. data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
  137. data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
  138. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
  139. data/lib/ecoportal/api/common/graphql.rb +1 -1
  140. data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
  141. data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
  142. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
  143. data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
  144. data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
  145. data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
  146. data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
  147. data/lib/ecoportal/api/graphql/base/force.rb +65 -0
  148. data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
  149. data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
  150. data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
  151. data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
  152. data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
  153. data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
  154. data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
  155. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
  156. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
  157. data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
  158. data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
  159. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
  160. data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
  161. data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
  162. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
  163. data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
  164. data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
  165. data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
  166. data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
  167. data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
  168. data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
  169. data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
  170. data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
  171. data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
  172. data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
  173. data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
  174. data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -0
  175. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -0
  176. data/lib/ecoportal/api/graphql/base/page/phased.rb +15 -0
  177. data/lib/ecoportal/api/graphql/base/page/section.rb +48 -0
  178. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
  179. data/lib/ecoportal/api/graphql/base/page.rb +7 -1
  180. data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
  181. data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
  182. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
  183. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
  184. data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
  185. data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
  186. data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
  187. data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
  188. data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
  189. data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
  190. data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
  191. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
  192. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
  193. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
  194. data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
  195. data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
  196. data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
  197. data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
  198. data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
  199. data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
  200. data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
  201. data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
  202. data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
  203. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
  204. data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
  205. data/lib/ecoportal/api/graphql/base/register.rb +18 -0
  206. data/lib/ecoportal/api/graphql/base.rb +26 -15
  207. data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
  208. data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
  209. data/lib/ecoportal/api/graphql/builder/page.rb +210 -0
  210. data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
  211. data/lib/ecoportal/api/graphql/builder/register.rb +49 -0
  212. data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
  213. data/lib/ecoportal/api/graphql/builder.rb +7 -3
  214. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
  215. data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
  216. data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
  217. data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
  218. data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
  219. data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
  220. data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
  221. data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
  222. data/lib/ecoportal/api/graphql/compat.rb +17 -0
  223. data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
  224. data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
  225. data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +68 -0
  226. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
  227. data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
  228. data/lib/ecoportal/api/graphql/concerns.rb +14 -0
  229. data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
  230. data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
  231. data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
  232. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
  233. data/lib/ecoportal/api/graphql/connection.rb +8 -4
  234. data/lib/ecoportal/api/graphql/error.rb +3 -3
  235. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
  236. data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
  237. data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
  238. data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
  239. data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
  240. data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
  241. data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
  242. data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
  243. data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
  244. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
  245. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
  246. data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
  247. data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
  248. data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
  249. data/lib/ecoportal/api/graphql/fragment.rb +37 -71
  250. data/lib/ecoportal/api/graphql/helpers/locations_tree.rb +1 -0
  251. data/lib/ecoportal/api/graphql/helpers.rb +1 -1
  252. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
  253. data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
  254. data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
  255. data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
  256. data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
  257. data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
  258. data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
  259. data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
  260. data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
  261. data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
  262. data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
  263. data/lib/ecoportal/api/graphql/input/page.rb +26 -0
  264. data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
  265. data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
  266. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
  267. data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
  268. data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
  269. data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
  270. data/lib/ecoportal/api/graphql/input/register.rb +13 -0
  271. data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
  272. data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
  273. data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
  274. data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
  275. data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
  276. data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
  277. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
  278. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
  279. data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
  280. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
  281. data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
  282. data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
  283. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
  284. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
  285. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
  286. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
  287. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
  288. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
  289. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
  290. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
  291. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
  292. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
  293. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
  294. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
  295. data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
  296. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
  297. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
  298. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
  299. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
  300. data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
  301. data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
  302. data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
  303. data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
  304. data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
  305. data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
  306. data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
  307. data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
  308. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
  309. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
  310. data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
  311. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
  312. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
  313. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
  314. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
  315. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
  316. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
  317. data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
  318. data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
  319. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
  320. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
  321. data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
  322. data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
  323. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
  324. data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
  325. data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
  326. data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
  327. data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
  328. data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
  329. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
  330. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
  331. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
  332. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
  333. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
  334. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
  335. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
  336. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
  337. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
  338. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
  339. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
  340. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
  341. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
  342. data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
  343. data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
  344. data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
  345. data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
  346. data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
  347. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
  348. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
  349. data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
  350. data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
  351. data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
  352. data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
  353. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
  354. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
  355. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
  356. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
  357. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
  358. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
  359. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
  360. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
  361. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
  362. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
  363. data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
  364. data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
  365. data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
  366. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
  367. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
  368. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
  369. data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
  370. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
  371. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
  372. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
  373. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
  374. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
  375. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
  376. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
  377. data/lib/ecoportal/api/graphql/input.rb +15 -7
  378. data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
  379. data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
  380. data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
  381. data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
  382. data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
  383. data/lib/ecoportal/api/graphql/logic/mutation.rb +6 -1
  384. data/lib/ecoportal/api/graphql/logic/query_array.rb +1 -0
  385. data/lib/ecoportal/api/graphql/logic/query_connection.rb +2 -0
  386. data/lib/ecoportal/api/graphql/logic.rb +9 -9
  387. data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
  388. data/lib/ecoportal/api/graphql/model/organization.rb +65 -52
  389. data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
  390. data/lib/ecoportal/api/graphql/model/page/data_field.rb +12 -0
  391. data/lib/ecoportal/api/graphql/model/page/phased/stage.rb +14 -0
  392. data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -0
  393. data/lib/ecoportal/api/graphql/model/page/section.rb +12 -0
  394. data/lib/ecoportal/api/graphql/model/page.rb +6 -7
  395. data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
  396. data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
  397. data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
  398. data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
  399. data/lib/ecoportal/api/graphql/model/register.rb +10 -0
  400. data/lib/ecoportal/api/graphql/model/resource.rb +1 -1
  401. data/lib/ecoportal/api/graphql/model.rb +21 -13
  402. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
  403. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
  404. data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
  405. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
  406. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
  407. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
  408. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
  409. data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
  410. data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
  411. data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
  412. data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
  413. data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
  414. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
  415. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
  416. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
  417. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
  418. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
  419. data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
  420. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
  421. data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
  422. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
  423. data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
  424. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
  425. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
  426. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
  427. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
  428. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
  429. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
  430. data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
  431. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
  432. data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
  433. data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
  434. data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
  435. data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
  436. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
  437. data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
  438. data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
  439. data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
  440. data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
  441. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
  442. data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
  443. data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
  444. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
  445. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
  446. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
  447. data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
  448. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
  449. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
  450. data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
  451. data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
  452. data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
  453. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
  454. data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
  455. data/lib/ecoportal/api/graphql/mutation.rb +11 -3
  456. data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
  457. data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
  458. data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
  459. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
  460. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
  461. data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
  462. data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
  463. data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
  464. data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
  465. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
  466. data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
  467. data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
  468. data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
  469. data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
  470. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
  471. data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
  472. data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
  473. data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
  474. data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
  475. data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
  476. data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
  477. data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
  478. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
  479. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
  480. data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
  481. data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
  482. data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
  483. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
  484. data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
  485. data/lib/ecoportal/api/graphql/payload.rb +14 -3
  486. data/lib/ecoportal/api/graphql/query/action.rb +1 -1
  487. data/lib/ecoportal/api/graphql/query/action_categories.rb +2 -1
  488. data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
  489. data/lib/ecoportal/api/graphql/query/contractor_entities.rb +2 -1
  490. data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
  491. data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
  492. data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
  493. data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
  494. data/lib/ecoportal/api/graphql/query/page.rb +45 -0
  495. data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
  496. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
  497. data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
  498. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
  499. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
  500. data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
  501. data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
  502. data/lib/ecoportal/api/graphql/query.rb +18 -7
  503. data/lib/ecoportal/api/graphql.rb +70 -2
  504. data/lib/ecoportal/api/graphql_version.rb +5 -5
  505. data/scripts/auto-worker-scheduler.sh +386 -0
  506. data/tests/contractor_entity_create.rb +19 -19
  507. data/tests/contractor_entity_udpate.rb +20 -20
  508. data/tests/dump_page_model.rb +74 -0
  509. data/tests/loc_structure_get.rb +1 -2
  510. data/tests/loc_structure_update.rb +51 -51
  511. data/tests/loc_structures_get.rb +15 -15
  512. data/tests/local_libs.rb +7 -7
  513. metadata +450 -11
@@ -1,40 +1,40 @@
1
- # rubocop:disable Gemspec/DevelopmentDependencies
2
- lib = File.expand_path('lib', __dir__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'ecoportal/api/graphql_version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'ecoportal-api-graphql'
8
- spec.version = Ecoportal::API::GRAPQL_VERSION
9
- spec.authors = ['Oscar Segura']
10
- spec.email = ['oscar@ecoportal.co.nz']
11
-
12
- spec.summary = 'A collection of helpers for interacting with the ecoPortal GraphQL API'
13
- spec.homepage = 'https://www.ecoportal.com'
14
- spec.licenses = %w[MIT]
15
-
16
- spec.metadata['rubygems_mfa_required'] = 'true'
17
-
18
- spec.required_ruby_version = '>= 3.2.2'
19
-
20
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
21
- f.match(%r{^(test|spec|features)/})
22
- end
23
- spec.bindir = 'exe'
24
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
- spec.require_paths = ['lib']
26
-
27
- spec.add_development_dependency 'pry', '>= 0.14'
28
- spec.add_development_dependency 'rake', '>= 13.0.3', '< 14'
29
- spec.add_development_dependency 'redcarpet', '>= 3.6.0', '< 4'
30
- spec.add_development_dependency 'rspec', '>= 3.12.0', '< 4'
31
- spec.add_development_dependency 'rubocop', '~> 1'
32
- spec.add_development_dependency 'rubocop-rake', '~> 0'
33
- spec.add_development_dependency 'yard', '>= 0.9.34', '< 1'
34
-
35
- spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.14'
36
- spec.add_dependency 'ecoportal-api-v2', '~> 3.3', '>= 3.3.1'
37
- spec.add_dependency 'graphlient', '>= 0.8.0', '< 0.9'
38
- end
39
-
40
- # rubocop:enable Gemspec/DevelopmentDependencies
1
+ # rubocop:disable Gemspec/DevelopmentDependencies
2
+ lib = File.expand_path('lib', __dir__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ecoportal/api/graphql_version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ecoportal-api-graphql'
8
+ spec.version = Ecoportal::API::GRAPQL_VERSION
9
+ spec.authors = ['Oscar Segura']
10
+ spec.email = ['oscar@ecoportal.co.nz']
11
+
12
+ spec.summary = 'A collection of helpers for interacting with the ecoPortal GraphQL API'
13
+ spec.homepage = 'https://www.ecoportal.com'
14
+ spec.licenses = %w[MIT]
15
+
16
+ spec.metadata['rubygems_mfa_required'] = 'true'
17
+
18
+ spec.required_ruby_version = '>= 3.2.2'
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
21
+ f.match(/^(test|spec|features)\//)
22
+ end
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(/^exe\//) { |f| File.basename(f) }
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_development_dependency 'pry', '>= 0.14'
28
+ spec.add_development_dependency 'rake', '>= 13.0.3', '< 14'
29
+ spec.add_development_dependency 'redcarpet', '>= 3.6.0', '< 4'
30
+ spec.add_development_dependency 'rspec', '>= 3.12.0', '< 4'
31
+ spec.add_development_dependency 'rubocop', '~> 1'
32
+ spec.add_development_dependency 'rubocop-rake', '~> 0'
33
+ spec.add_development_dependency 'yard', '>= 0.9.34', '< 1'
34
+
35
+ spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.16'
36
+ spec.add_dependency 'ecoportal-api-v2', '~> 3.3', '>= 3.3.2'
37
+ spec.add_dependency 'graphlient', '>= 0.8.0', '< 0.9'
38
+ end
39
+
40
+ # rubocop:enable Gemspec/DevelopmentDependencies
@@ -0,0 +1,36 @@
1
+ # common/graphql
2
+
3
+ Shared HTTP transport, authentication, and base model infrastructure used by the gem — nothing here is EcoPortal-domain-specific.
4
+
5
+ ## Contents
6
+
7
+ | File / class | Purpose |
8
+ |---|---|
9
+ | `auth_service.rb` / `AuthService` | OAuth2 password + refresh-token flow; included in both client classes |
10
+ | `client.rb` / `Client` | Graphlient-based GraphQL client (builds bearer-token headers, constructs endpoint URL) |
11
+ | `http_client.rb` / `HttpClient` | Raw HTTP client (extends `Common::Client`); `execute` sends query strings + variables; supports v0–v3 + graphql API version modes |
12
+ | `class_helpers.rb` / `ClassHelpers` | `becomes(klass)` instance casting + `const?` class helper |
13
+ | `hash_helpers.rb` / `HashHelpers` | Deep key transforms (`keys_to_sym_deep`, `deep_dup`, `remove_nil_keys_deep`) — used by diff and input serialisation |
14
+ | `query_integration.rb` / `QueryIntegration` | Class macro `query(:method, query_klass:)` that wires a named query method on `Model::Organization` |
15
+ | `model.rb` / `Model` | Base model class: `DoubleModel` + `Diffable` + `AsInput`; all GraphQL models ultimately inherit this |
16
+ | `patches.rb` | Reserved placeholder (empty) |
17
+ | `model/diffable.rb` | `as_update` / `dirty?` — delegates to `DiffService` |
18
+ | `model/as_input.rb` | `as_input` (instance) and `as_input(hash)` (class method) — converts diff to GraphQL mutation variables |
19
+ | `model/diffable/diff_service.rb` | Main diff engine: flat classic diff + cascaded reduce through nested `GraphQL::Model` objects |
20
+ | `model/diffable/classic_diff_service.rb` | Low-level change-data extraction (compares `curr_doc` vs `prev_doc`) |
21
+ | `model/diffable/hash_diff.rb` | Hash equality / change detection helpers |
22
+ | `model/diffable/hash_diff_nesting.rb` | Nested key path helpers (`dig_set!`, `dig_delete!`, `dig_path?`) |
23
+
24
+ ## Conventions
25
+
26
+ - `Client` is graphlient-based and used for schema introspection; `HttpClient` is the actual request sender used in production (set as `client.http_client`).
27
+ - `HashHelpers` is used as both a module mixin (include) and directly via `HashHelpers.keys_to_sym_deep(...)`.
28
+ - `DiffService` handles the `flat:` flag: `flat: true` does a single-level diff; `flat: false` (default) cascades through nested `GraphQL::Model` objects.
29
+ - Root-class objects (`root!` declared) are excluded from cascaded diffs — they represent independent server entities.
30
+ - `AsInput#as_input(target_class:)` delegates to `Input::X.from_model(self)` when a target class is given.
31
+
32
+ ## Related
33
+
34
+ - `../graphql/logic/base_model.rb` — adds `SnakeCamelAccess` on top of `Common::GraphQL::Model`
35
+ - `../graphql/logic/input.rb` — `Logic::Input` base class built on top of `Model`
36
+ - `../graphql/logic/base_query.rb` — consumes `HttpClient#execute` and `HashHelpers`
@@ -7,14 +7,13 @@ module Ecoportal
7
7
  TOKEN_AUTORENEW = 90 # minutes
8
8
 
9
9
  module InstanceMethods
10
- def session_token(host: server, version: nil, auto_renew: true)
11
- session_token_data(host: host, version: version).then do |body|
10
+ def session_token(host: server, auto_renew: true)
11
+ session_token_data(host: host).then do |body|
12
12
  next unless body
13
13
 
14
14
  if auto_renew && token_renew?(body['expires_in'])
15
15
  session_token_renewed(
16
16
  host: host,
17
- version: version,
18
17
  refresh_token: body['refresh_token']
19
18
  )
20
19
  else
@@ -23,15 +22,14 @@ module Ecoportal
23
22
  end
24
23
  end
25
24
 
26
- def session_token_renewed(host: server, version: nil, refresh_token: nil)
25
+ def session_token_renewed(host: server, refresh_token: nil)
27
26
  unless refresh_token
28
- return unless (body = session_token_data(host: host, version: version))
27
+ return unless (body = session_token_data(host: host))
29
28
  return unless (refresh_token = body['resfresh_token'])
30
29
  end
31
30
 
32
31
  session_refresh_token_data(
33
32
  host: host,
34
- version: version,
35
33
  refresh_token: refresh_token
36
34
  ).then do |bdy|
37
35
  bdy['access_token'] if bdy
@@ -40,8 +38,8 @@ module Ecoportal
40
38
 
41
39
  private
42
40
 
43
- def session_token_data(host: server, version: nil)
44
- http_client(host: host, version: version).post(
41
+ def session_token_data(host: server)
42
+ auth_http_client(host: host).post(
45
43
  '/oauth/token',
46
44
  data: {
47
45
  'grant_type' => 'password',
@@ -53,8 +51,8 @@ module Ecoportal
53
51
  end
54
52
  end
55
53
 
56
- def session_refresh_token_data(refresh_token:, host: server, version: nil)
57
- http_client(host: host, version: version).post(
54
+ def session_refresh_token_data(refresh_token:, host: server)
55
+ auth_http_client(host: host).post(
58
56
  '/oauth/token',
59
57
  data: {
60
58
  'grant_type' => 'refresh_token',
@@ -69,10 +67,10 @@ module Ecoportal
69
67
  (TOKEN_AUTORENEW * 60) > seconds
70
68
  end
71
69
 
72
- def http_client(host: server, version: nil)
73
- @http_client ||= Ecoportal::API::Common::GraphQL::HttpClient.new(
70
+ def auth_http_client(host: server)
71
+ @auth_http_client ||= Ecoportal::API::Common::GraphQL::HttpClient.new(
74
72
  host: host,
75
- version: version
73
+ version: 'http'
76
74
  )
77
75
  end
78
76
 
@@ -89,7 +87,7 @@ module Ecoportal
89
87
  end
90
88
 
91
89
  def fetch_env_required(name)
92
- abort("Missing ENV '#{name}'") unless ENV.key?(name)
90
+ raise "Missing ENV '#{name}'" unless ENV.key?(name)
93
91
  ENV[name]
94
92
  end
95
93
 
@@ -4,7 +4,9 @@ module Ecoportal
4
4
  module Common
5
5
  module GraphQL
6
6
  class Client < ::Graphlient::Client
7
- attr_accessor :host
7
+ ENDPOINT_PATH = 'external/graphql'.freeze
8
+
9
+ attr_accessor :host, :http_client
8
10
 
9
11
  READ_TIMEOUT = 90
10
12
  WRITE_TIMEOUT = 90
@@ -42,7 +44,12 @@ module Ecoportal
42
44
  end
43
45
 
44
46
  def new(org_id: self.org_id, host: self.host, schema_path: host, no_schema: @no_schema)
45
- self.class.new(org_id: org_id, host: host, schema_path: schema_path, no_schema: no_schema)
47
+ self.class.new(
48
+ org_id: org_id,
49
+ host: host,
50
+ schema_path: schema_path,
51
+ no_schema: no_schema
52
+ )
46
53
  end
47
54
 
48
55
  def org_id
@@ -52,7 +59,8 @@ module Ecoportal
52
59
  private
53
60
 
54
61
  def url
55
- "#{Ecoportal::API::Common::GraphQL::HttpClient.base_url(host)}/api/#{org_id}/graphql"
62
+ base_url = Ecoportal::API::Common::GraphQL::HttpClient.base_url(host)
63
+ "#{base_url}/api/#{org_id}/#{ENDPOINT_PATH}"
56
64
  end
57
65
  end
58
66
  end
@@ -2,6 +2,8 @@ module Ecoportal
2
2
  module API
3
3
  module Common
4
4
  module GraphQL
5
+ class ResponseError < StandardError; end
6
+
5
7
  class HttpClient < Common::Client
6
8
  class << self
7
9
  def base_url(host)
@@ -13,20 +15,37 @@ module Ecoportal
13
15
  end
14
16
  end
15
17
 
18
+ ENDPOINT_PATH = 'external/graphql'.freeze
19
+ READ_TIMEOUT = 90
20
+ WRITE_TIMEOUT = 90
21
+
16
22
  attr_reader :host, :version
17
23
 
24
+ include Ecoportal::API::Common::GraphQL::AuthService
25
+
18
26
  def initialize(
27
+ email: nil,
28
+ pass: nil,
29
+ org_id: nil,
19
30
  api_key: nil,
20
- version: nil,
31
+ version: 'graphql',
21
32
  host: 'live.ecoportal.com',
22
33
  logger: ::Logger.new(IO::NULL),
23
- deep_logging: false
34
+ deep_logging: false,
35
+ no_schema: true
24
36
  )
25
- super
26
- end
37
+ @org_id = org_id
38
+ @user_email = email
39
+ @user_pass = pass
40
+ @no_schema = no_schema
41
+ @version = version
27
42
 
28
- def refresh_key(value)
29
- @api_key = value
43
+ super(
44
+ api_key: api_key,
45
+ host: host,
46
+ logger: logger,
47
+ deep_logging: deep_logging
48
+ )
30
49
  end
31
50
 
32
51
  # Creates a HTTP object adding the `X-ApiKey` or `X-ECOPORTAL-API-KEY` param to the header, depending on the API version.
@@ -35,52 +54,121 @@ module Ecoportal
35
54
  def base_request
36
55
  @base_request ||=
37
56
  case @version
38
- when NilClass
39
- HTTP.accept(:json)
40
- when 'v2'
41
- HTTP.headers('X-ECOPORTAL-API-KEY' => key_token).accept(:json)
57
+ when NilClass, 'http'
58
+ HTTP
59
+ when 'v1', 'v0'
60
+ HTTP.headers('X-ApiKey' => key_token)
61
+ when 'v2', 'v3'
62
+ HTTP.headers('X-ECOPORTAL-API-KEY' => key_token)
42
63
  when 'graphql'
43
- HTTP.headers('Authorization' => "Bearer #{key_token}").accept(:json)
44
- else
45
- HTTP.headers('X-ApiKey' => key_token).accept(:json)
64
+ HTTP.headers('Authorization' => "Bearer #{session_token(host: host)}")
65
+ end.then do |request|
66
+ request ||= HTTP
67
+ request.accept(:json).timeout(
68
+ read: READ_TIMEOUT,
69
+ write: WRITE_TIMEOUT
70
+ )
46
71
  end
47
72
  end
48
73
 
74
+ def org_id
75
+ @org_id || fetch_env_required('ORGANIZATION_ID')
76
+ end
77
+
78
+ # Executes a GraphQL query or mutation over HTTP.
79
+ # @param query_string [String] full GraphQL query document.
80
+ # @param variables [Hash] variable values to pass to the operation.
81
+ # @param operation_name [String, nil] when the document has multiple operations.
82
+ # @return [Hash] parsed response body (string-keyed, includes 'data' and optionally 'errors').
83
+ # @raise [Ecoportal::API::Common::Client::Error::UnexpectedServerError] on non-2xx HTTP status.
84
+ # @raise [Ecoportal::API::Common::GraphQL::ResponseError] when the response body contains GraphQL errors.
85
+ def execute(query_string, variables: {}, operation_name: nil)
86
+ body = { query: query_string, variables: variables }
87
+ body[:operationName] = operation_name if operation_name
88
+ response = base_request.post(url_for(''), json: body)
89
+ handle_http_response(response)
90
+ end
91
+
49
92
  # Full URl builder of the request
50
93
  # @param path [String] the tail that completes the url of the request.
51
94
  # @return [String] the final url.
52
95
  def url_for(path)
53
- version? ? "#{base_url_api}#{path}" : "#{base_url}#{path}"
96
+ "#{api_url}#{path}"
54
97
  end
55
98
 
56
99
  private
57
100
 
58
- def version?
59
- !!@version
101
+ def key_token
102
+ return if http?
103
+ return session_token(host: host) if graphql?
104
+ return @api_key if api_key?
105
+ return ENV['ORG_INT_KEY'] if v0?
106
+
107
+ puts 'Api-key missing!'
60
108
  end
61
109
 
62
- def protocol
63
- @protocol ||= self.class.protocol(host)
110
+ def api_url
111
+ base_url.then do |url|
112
+ next "#{url}/api/#{version}" if v1_0? || v2_3?
113
+ next "#{url}/api/#{org_id}/#{ENDPOINT_PATH}" if graphql?
114
+
115
+ url
116
+ end
64
117
  end
65
118
 
66
119
  def base_url
67
120
  @base_url ||= self.class.base_url(host)
68
121
  end
69
122
 
70
- def base_url_api
71
- @base_url_api ||= "#{base_url}/api/#{version}"
123
+ def protocol
124
+ @protocol ||= self.class.protocol(host)
72
125
  end
73
126
 
74
- def key_token
75
- if @api_key.nil? || @api_key.match(/\A\W*\z/)
76
- if (version == 'v0') && (key = ENV['ORG_INT_KEY'])
77
- key
78
- elsif !version.nil?
79
- puts 'Api-key missing!'
80
- end
81
- else
82
- @api_key
127
+ def http?
128
+ return true if @version.nil?
129
+
130
+ @version == 'http'
131
+ end
132
+
133
+ def api_key?
134
+ @api_key && !@api_key.match(/\A\W*\z/)
135
+ end
136
+
137
+ def v1_0?
138
+ v1? || v0?
139
+ end
140
+
141
+ def v1?
142
+ @version == 'v1'
143
+ end
144
+
145
+ def v0?
146
+ @version == 'v0'
147
+ end
148
+
149
+ def v2_3?
150
+ return true if @version == 'v2'
151
+ return true if @version == 'v3'
152
+
153
+ false
154
+ end
155
+
156
+ def graphql?
157
+ @version == 'graphql'
158
+ end
159
+
160
+ def handle_http_response(response)
161
+ unless response.status.success?
162
+ raise Ecoportal::API::Common::Client::Error::UnexpectedServerError.new(
163
+ response.body.to_s, code: response.status.code
164
+ )
83
165
  end
166
+
167
+ parsed = response.parse(:json)
168
+ errors = parsed['errors']
169
+ raise ResponseError, errors.map { |e| e['message'] }.join('; ') if errors&.any?
170
+
171
+ parsed
84
172
  end
85
173
  end
86
174
  end
@@ -0,0 +1,28 @@
1
+ # common/graphql/model
2
+
3
+ Concerns that add dirty-tracking diff (`Diffable`) and mutation-input serialisation (`AsInput`) to the base `Common::GraphQL::Model`.
4
+
5
+ ## Contents
6
+
7
+ | File / class | Purpose |
8
+ |---|---|
9
+ | `diffable.rb` / `Model::Diffable` | Provides `as_update` (returns changed-field hash or nil) and `dirty?`; delegates to `DIFF_CLASS` (defaults to `DiffService`) |
10
+ | `as_input.rb` / `Model::AsInput` | Instance `as_input` (wraps diff as mutation variables) + class-level `as_input(hash)` (key-sym + nil-id cleanup) |
11
+ | `diffable/diff_service.rb` / `DiffService` | Primary diff engine: flat classic diff then cascaded reduce over nested `GraphQL::Model` children |
12
+ | `diffable/classic_diff_service.rb` / `ClassicDiffService` | Low-level diff: compares `curr_doc` vs `prev_doc`, returns `{ id:, changed_field: new_value }` or nil |
13
+ | `diffable/hash_diff.rb` / `HashDiff` | Primitive equality helpers used by `ClassicDiffService` |
14
+ | `diffable/hash_diff_nesting.rb` / `HashDiffNesting` | Nested path helpers: `dig_set!`, `dig_delete!`, `dig_path?` used when building cascaded diff results |
15
+
16
+ ## Conventions
17
+
18
+ - `DIFF_CLASS = DiffService` is a constant on `Diffable` — subclasses can override it to swap diff strategies.
19
+ - `DiffService` `flat: true` produces a single-level diff (current model only); `flat: false` (default) recurses via `cascaded_reduce` through nested `GraphQL::Model` objects.
20
+ - Objects declared with `root!` in their class are skipped during cascaded diff — they are independent server entities (look-ups only).
21
+ - `as_input` on a model calls `as_update` first; returns `nil` when nothing has changed so callers can skip the mutation.
22
+ - `as_input(target_class:)` is the preferred path when a corresponding `Input::X` class exists — delegates to `target_class.from_model(self)`.
23
+
24
+ ## Related
25
+
26
+ - `../hash_helpers.rb` — `HashHelpers` used inside `DiffService` for key transforms
27
+ - `../../graphql/logic/input.rb` — `Logic::Input.from_model` is the canonical consumer of `as_update`
28
+ - `../../graphql/logic/base_model.rb` — all concrete models inherit this chain
@@ -37,11 +37,14 @@ module Ecoportal
37
37
 
38
38
  # INSTANCE METHODS
39
39
 
40
- def as_input(clientMutationId: '')
41
- self.class.as_input(
42
- as_update,
43
- clientMutationId: clientMutationId
44
- )
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)
45
48
  end
46
49
  end
47
50
  end
@@ -38,10 +38,12 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
38
38
  raise "Can't fetch underlying document for instance of #{target.class}."
39
39
  end
40
40
 
41
- def prev_doc(target = subject, flat: flat?)
41
+ def prev_doc(target = subject)
42
42
  return target.original_doc if target.respond_to?(:original_doc)
43
43
 
44
44
  raise "Can't fetch original underlying model for instance of #{target.class}."
45
+ rescue NoMethodError
46
+ nil
45
47
  end
46
48
  end
47
49
  end
@@ -34,12 +34,17 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
34
34
 
35
35
  protected
36
36
 
37
+ # Override: GraphQL mutations expect flat changed-prop values, not patch-op wrappers.
38
+ # Returns { id: ..., changed_field: new_value, ... } or nil when nothing changed.
37
39
  def classic_diff(flat: flat?, ignore: [])
38
- change_diff(
39
- curr_doc(flat: flat),
40
- prev_doc(flat: flat),
41
- ignore: to_a(ignored, ignore)
42
- )
40
+ curr = keys_to_sym_deep(curr_doc(flat: flat) || {})
41
+ prev = keys_to_sym_deep(prev_doc(flat: flat) || {})
42
+
43
+ data = change_data(curr, prev, ignore: to_a(ignored, ignore))
44
+ return nil if data == NO_CHANGES
45
+
46
+ id = get_id(curr, exception: false)
47
+ id ? { id: id }.merge(data) : data
43
48
  end
44
49
 
45
50
  # @note it calls `as_update` for nested / cascaded attributes, which in turn
@@ -49,26 +54,28 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
49
54
  # `#as_update` and `DiffService#diff`.
50
55
  # - The `result` object regards, therefore, to the **current level** alone.
51
56
  # With the aim of building it up (accumulating through the call chain).
52
- def diff_reduce(init = {}, ignore: [])
57
+ def diff_reduce(init, ignore: [])
53
58
  subject.cascaded_reduce(init, recurs: false) do |result, obj, _key, key_path, _trace|
54
- next result if key_path.empty? # first iteration already done
55
- next result if obj == subject # first call had done it (same as above: path relative)
59
+ next result if key_path.empty?
60
+ next result if obj == subject
56
61
 
57
- dig_delete!(result, key_path) if dig_path?(result, key_path)
62
+ sym_path = key_path.map(&:to_sym)
58
63
 
59
- # discard rooted (i.e. read_only) objects from the diff.
64
+ # Remove any flat-diff placeholder for this key before deciding its fate.
65
+ dig_delete!(result, sym_path) if result && dig_path?(result, sym_path)
66
+
67
+ # Discard root-class objects — they are independent entities, not mutation payload.
60
68
  next result if root?(obj)
61
- next result unless dig_path?(result, key_path)
62
- next result unless obj.respond_to?(:as_update)
69
+ # Only cascade-diff GraphQL model objects; ecoportal-api-v2 DoubleModel
70
+ # subclasses (ArrayModel, etc.) have a different as_update signature.
71
+ next result unless obj.is_a?(Ecoportal::API::Common::GraphQL::Model)
63
72
 
64
73
  value = obj.as_update(ignore: ignore)
74
+ next result if value.nil?
65
75
 
66
- dig_set!(
67
- result,
68
- key_path,
69
- value
70
- )
71
-
76
+ result ||= {}
77
+ result[:id] ||= get_id(keys_to_sym_deep(curr_doc(flat: true) || {}), exception: false)
78
+ dig_set!(result, sym_path, value)
72
79
  result
73
80
  end
74
81
  end
@@ -147,9 +154,10 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
147
154
  attr_reader :ignored
148
155
 
149
156
  def root?(obj)
150
- return false unless obj.respond_to?(:root?)
151
-
152
- obj.root?
157
+ # root? is a CLASS method (via Rootable::ClassMethods). The instance root?
158
+ # is protected and checks parent position, not the class declaration.
159
+ # Check the ancestor chain for any class that declared root!.
160
+ obj.class.ancestors.any? { |k| k.respond_to?(:root?) && k.root? }
153
161
  end
154
162
 
155
163
  def curr_doc(target = subject, flat: flat?)
@@ -171,8 +179,8 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
171
179
 
172
180
  # Remove from `json` doc all the nested properties.
173
181
  def doc_with_non_cascaded_attributes(json)
174
- return json unless subject.respond_to?(:_cascaded_doc_keys)
175
- return json unless (excluded = subject._cascaded_doc_keys)
182
+ return json unless subject.class.respond_to?(:_cascaded_doc_keys)
183
+ return json if (excluded = subject.class._cascaded_doc_keys).empty?
176
184
 
177
185
  except_keys(json, *excluded)
178
186
  end
@@ -192,7 +192,7 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
192
192
  end
193
193
 
194
194
  def any_array?(a, b)
195
- [a, b].any? {|item| item.is_a?(Array)}
195
+ [a, b].any?(Array)
196
196
  end
197
197
 
198
198
  def get_id(doc, exception: true)
@@ -207,6 +207,30 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
207
207
  id
208
208
  end
209
209
 
210
+ # @param arr [Array] the source Array of id-bearing items
211
+ # @return [Array<String>] the `id`s thereof
212
+ def array_ids(arr)
213
+ return [] if !arr.is_a?(Array) || arr.empty?
214
+
215
+ arr.map {|item| get_id(item, exception: false)}
216
+ end
217
+
218
+ # @param arr [Array] the source Array
219
+ # @return [Integer, nil] the position of the item with the given `id`
220
+ def array_id_index(arr, id)
221
+ return unless arr.is_a?(Array)
222
+
223
+ arr.index {|item| get_id(item, exception: false) == id}
224
+ end
225
+
226
+ # @param arr [Array] the source Array
227
+ # @return [Object, nil] the item with the given `id`
228
+ def array_id_item(arr, id)
229
+ return unless (idx = array_id_index(arr, id))
230
+
231
+ arr[idx]
232
+ end
233
+
210
234
  def meta_keys
211
235
  return self::META_KEYS if respond_to?(:const_get)
212
236
 
@@ -233,6 +257,35 @@ module Ecoportal::API::Common::GraphQL::Model::Diffable
233
257
  def to_a(*values)
234
258
  [values].flatten.compact.uniq
235
259
  end
260
+
261
+ # Hash path helpers — used by DiffService#diff_reduce to navigate
262
+ # and modify nested result hashes during cascaded diffing.
263
+
264
+ def dig_path?(obj, keys)
265
+ return false unless obj.is_a?(Hash)
266
+ return obj.key?(keys.first) if keys.length == 1
267
+ return false unless obj.key?(keys.first)
268
+
269
+ dig_path?(obj[keys.first], keys[1..])
270
+ end
271
+
272
+ def dig_set!(obj, keys, value)
273
+ if keys.length == 1
274
+ obj[keys.first] = value
275
+ else
276
+ dig_set!(obj[keys.first], keys[1..], value)
277
+ end
278
+ end
279
+
280
+ def dig_delete!(obj, keys)
281
+ if keys.length == 1
282
+ return unless obj.respond_to?(:delete)
283
+
284
+ obj.delete(keys.first)
285
+ else
286
+ dig_delete!(obj[keys.first], keys[1..])
287
+ end
288
+ end
236
289
  end
237
290
 
238
291
  extend InstanceMethods