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
@@ -0,0 +1,54 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Cross-reference field — links to pages in another register.
4
+ class CrossReference < DataField
5
+ passarray :pages # staged writes; the READ response uses `references` (a connection)
6
+
7
+ # Current reference ids. The GraphQL response carries them under `references.nodes`
8
+ # (CommonPageUnion's crossReferenceField), NOT `pages` — so read from there. After a
9
+ # page_ids= write, prefer the staged `pages` value.
10
+ def page_ids
11
+ ref_ids_from(doc)
12
+ end
13
+
14
+ def page_ids=(ids)
15
+ doc['pages'] = Array(ids).map { |id_val| { 'id' => id_val } }
16
+ end
17
+
18
+ # Dirty only when the effective reference-id SET actually changed.
19
+ #
20
+ # The field is READ into `references.nodes` but WRITTEN via `pages` (page_ids=). The
21
+ # default key-level leaf diff therefore sees a brand-new `pages` key the moment any
22
+ # setter runs and flags a change — even when the set is unchanged. The common offender
23
+ # is `clear` on an already-empty field ([] -> []): eco-helpers scripts clear a reference
24
+ # whenever the source value is blank, which under APIv2 was a no-op but on GraphQL
25
+ # emitted a phantom `referenceIds: []` update. Compare id SETS against the server
26
+ # baseline (`original_doc`) instead — restoring v2 parity (empty->empty / same->same
27
+ # is not dirty).
28
+ def dirty?
29
+ ref_ids_from(doc).sort != ref_ids_from(original_doc).sort
30
+ end
31
+
32
+ def as_input
33
+ return nil unless dirty?
34
+
35
+ # CrossReferenceInput uses `referenceIds` (schema-confirmed) — NOT pageIds.
36
+ { crossReference: { id: id, referenceIds: page_ids } }
37
+ end
38
+
39
+ private
40
+
41
+ # Extract the reference id set from a doc snapshot. `original_doc` is the server baseline
42
+ # (the diff prev_doc — see Diffable::ClassicDiffService), `doc` carries client changes.
43
+ # Both prefer the staged `pages` (write shape) and fall back to `references.nodes` (read
44
+ # shape) — so the comparison is shape-agnostic and stays correct after `consolidate!`
45
+ # copies `doc` (with `pages`) into `original_doc`.
46
+ def ref_ids_from(source_doc)
47
+ return [] unless source_doc
48
+
49
+ src = source_doc.key?('pages') ? source_doc['pages'] : Array(source_doc.dig('references', 'nodes'))
50
+ Array(src).map { |r| r.is_a?(Hash) ? r['id'] : r }.compact
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Wraps Date data field. Mapped from __typename 'Date'.
4
+ # value is an ISO8601DateTime string (e.g. "2024-11-25T08:30:00+13:00").
5
+ class DateField < DataField
6
+ # value is nested: { dateTime: "...", timeZone: "..." } or raw string
7
+ def value
8
+ v = doc['value']
9
+ v.is_a?(Hash) ? v['dateTime'] : v
10
+ end
11
+
12
+ def value=(iso8601_string)
13
+ doc['value'] = iso8601_string
14
+ end
15
+
16
+ def as_input
17
+ return nil unless dirty?
18
+
19
+ { date: { id: id, value: value } }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # File attachment field.
4
+ # Writing requires a prior file upload to the REST upload endpoint
5
+ # to obtain fileContainerIds — see the file-upload TODO in projects/TODO.md.
6
+ # The file_container_ids= setter can be used once IDs are available.
7
+ class FileField < DataField
8
+ passarray :fileContainers # each: { id:, fileName:, ... }
9
+
10
+ def file_container_ids
11
+ Array(fileContainers).map { |c| c.is_a?(Hash) ? c['id'] : c }.compact
12
+ end
13
+
14
+ def file_container_ids=(ids)
15
+ doc['fileContainers'] = Array(ids).map { |id_val| { 'id' => id_val } }
16
+ end
17
+
18
+ def as_input
19
+ return nil unless dirty?
20
+
21
+ { file: { id: id, fileContainerIds: file_container_ids } }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class Gauge < DataField
4
+ passthrough :value, :max
5
+
6
+ # Blank (empty / whitespace CSV cell) → nil, NOT 0.0 — see DataField#numeric_or_nil
7
+ # (mirrors Number; `"".to_f` would otherwise silently write a real zero).
8
+ def value=(numeric)
9
+ doc['value'] = numeric_or_nil(numeric)
10
+ end
11
+
12
+ def as_input
13
+ return nil unless dirty?
14
+
15
+ { gauge: { id: id, value: value } }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class Geo < DataField
4
+ passthrough :address, :coordinates # coordinates: { lat:, lon: }
5
+
6
+ def address=(new_address)
7
+ doc['address'] = new_address
8
+ end
9
+
10
+ def coordinates=(lat_lon_hash)
11
+ doc['coordinates'] = lat_lon_hash
12
+ end
13
+
14
+ def as_input
15
+ return nil unless dirty?
16
+
17
+ payload = { id: id }
18
+ payload[:address] = address if address
19
+ payload[:coordinates] = coordinates if coordinates
20
+ { geo: payload }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Image gallery field.
4
+ # Writing requires a prior file upload to the REST upload endpoint
5
+ # to obtain fileContainerIds — see the file-upload TODO in projects/TODO.md.
6
+ class ImageGallery < DataField
7
+ passarray :fileContainers # each: { id:, fileName:, url:, ... }
8
+
9
+ def file_container_ids
10
+ Array(fileContainers).map { |c| c.is_a?(Hash) ? c['id'] : c }.compact
11
+ end
12
+
13
+ def file_container_ids=(ids)
14
+ doc['fileContainers'] = Array(ids).map { |id_val| { 'id' => id_val } }
15
+ end
16
+
17
+ def as_input
18
+ return nil unless dirty?
19
+
20
+ { imageGallery: { id: id, fileContainerIds: file_container_ids } }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Legal obligation / law snippets field — read access only.
4
+ class Law < DataField
5
+ passarray :laws # each: { id:, content: }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Mailbox field — email inbox linked to this field.
4
+ # Managed by the system; not settable via script.
5
+ class Mailbox < DataField
6
+ passthrough :email, :enabled
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class Number < DataField
4
+ passthrough :value
5
+
6
+ # Blank (empty / whitespace CSV cell) → nil, NOT 0.0 — see DataField#numeric_or_nil.
7
+ # Whether a blank should CLEAR the field or be LEFT UNTOUCHED is the caller's policy;
8
+ # this only guarantees a blank never becomes the value 0.0.
9
+ def value=(numeric)
10
+ doc['value'] = numeric_or_nil(numeric)
11
+ end
12
+
13
+ def as_input
14
+ return nil unless dirty?
15
+
16
+ { number: { id: id, value: value } }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class People < DataField
4
+ passarray :peopleIds
5
+
6
+ def people_ids
7
+ ids = Array(peopleIds)
8
+ return ids unless ids.empty?
9
+
10
+ # Fallback: the read fragment populates `peopleIds`, but if a response ever returns it
11
+ # null/empty while the expanded `people { id }` nodes ARE present (a partial / non-
12
+ # $content read), seed from the nodes. The backend write replaces peopleIds wholesale,
13
+ # so a `people_ids << x` append must start from the existing set or it would WIPE the
14
+ # field. Mirrors CrossReference's references.nodes fallback. Read key == write key
15
+ # (peopleIds), so this only widens the read; it never creates phantom-dirty.
16
+ Array(doc['people']).map { |p| p.is_a?(Hash) ? p['id'] : p }.compact
17
+ end
18
+
19
+ def people_ids=(ids)
20
+ doc['peopleIds'] = Array(ids).compact
21
+ end
22
+
23
+ # v2 compat: scripts may set via people_ids= or direct assignment
24
+ def value=(ids)
25
+ self.people_ids = ids
26
+ end
27
+
28
+ def as_input
29
+ return nil unless dirty?
30
+
31
+ { people: { id: id, peopleIds: Array(peopleIds) } }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class PlainText < DataField
4
+ passthrough :value
5
+
6
+ def value=(new_value)
7
+ doc['value'] = new_value
8
+ end
9
+
10
+ def as_input
11
+ return nil unless dirty?
12
+
13
+ { plainText: { id: id, value: value } }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class RichText < DataField
4
+ passthrough :content
5
+
6
+ def content=(new_content)
7
+ doc['content'] = new_content
8
+ end
9
+
10
+ # v2 compat alias — RichText value is stored as content
11
+ def value
12
+ content
13
+ end
14
+
15
+ def value=(new_value)
16
+ self.content = new_value
17
+ end
18
+
19
+ def as_input
20
+ return nil unless dirty?
21
+
22
+ { richText: { id: id, content: content } }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class Select < DataField
4
+ passarray :options # each: { id:, name:, value:, selected: }
5
+ passthrough :other, :otherDesc, :multiple, :dataType
6
+
7
+ # Select an option by id, name, or value. Deselects all others (single-select).
8
+ # For multi-select fields, call with multiple values.
9
+ def select_option(*id_or_values)
10
+ targets = id_or_values.flatten.map(&:to_s)
11
+ (doc['options'] || []).each do |opt|
12
+ opt['selected'] = targets.include?(opt['id'].to_s) ||
13
+ targets.include?(opt['name'].to_s) ||
14
+ targets.include?(opt['value'].to_s)
15
+ end
16
+ end
17
+
18
+ # Deselect all options.
19
+ def clear_selection
20
+ (doc['options'] || []).each { |opt| opt['selected'] = false }
21
+ end
22
+
23
+ def selected_options
24
+ (doc['options'] || []).select { |opt| opt['selected'] }
25
+ end
26
+
27
+ def as_input
28
+ return nil unless dirty?
29
+
30
+ {
31
+ select: {
32
+ id: id,
33
+ # rubocop:disable Style/DoubleNegation
34
+ options: (doc['options'] || []).map { |opt| { id: opt['id'], selected: !!opt['selected'] } }
35
+ # rubocop:enable Style/DoubleNegation
36
+ }
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Digital signature capture field — read access only.
4
+ # Signatures are captured through the UI; not settable via script.
5
+ class Signature < DataField
6
+ passthrough :signedAt, :signerName
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # SmartFill field — AI-auto-populated field using a template-defined prompt.
4
+ # Value is generated server-side via Bedrock; trigger_type controls when.
5
+ #
6
+ # Read-only for DataFieldInput — use the generateSmartFill GraphQL mutation
7
+ # (or its trigger) to populate. The instructions field is template-configured
8
+ # and can be reviewed/updated via template management.
9
+ class SmartFill < DataField
10
+ passthrough :status # pending | generating | success | failed
11
+ passthrough :trigger_type # manual | snapshot | live
12
+ passthrough :instructions # the AI prompt configured on the template field
13
+ passboolean :edit_enabled
14
+ passthrough :generated_at
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Table / embedded structure field — read access only.
4
+ # Complex structured data managed via its own mutation path.
5
+ class Table < DataField
6
+ passarray :rows
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Location/tag field — mapped from __typename 'TagField'.
4
+ class TagField < DataField
5
+ passarray :locations # each: { id:, name: }
6
+
7
+ def location_ids
8
+ Array(locations).map { |l| l['id'] }.compact
9
+ end
10
+
11
+ def location_ids=(ids)
12
+ doc['locations'] = Array(ids).map { |id_val| { 'id' => id_val } }
13
+ end
14
+
15
+ # NOTE: LocationFieldInput has NO location-value field — page locations are set at the
16
+ # PAGE level (UpdatePageInput.page.locations, via Interface::BasePage#location_ids=),
17
+ # not via the data field. So this field contributes no value to dataFields.updates;
18
+ # it inherits DataField#as_input (nil). location_ids/= stay as field-level read/staging.
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,137 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Page
6
+ # Base class for all data field types.
7
+ # Subclasses expose typed value accessors and serialise to DataFieldInput format.
8
+ class DataField < Logic::BaseModel
9
+ # Diff strategy for leaf data fields.
10
+ #
11
+ # A DataField's array attributes (`options`, `peopleIds`, `contractors`,
12
+ # `pages`, `fileContainers`, `items`, ...) are declared with `passarray`,
13
+ # which registers them as *cascaded* attributes. The default `DiffService`
14
+ # strips cascaded keys from the flat classic diff (`doc_with_non_cascaded_attributes`)
15
+ # on the assumption that a later `diff_reduce` pass re-adds them by cascading
16
+ # into nested `GraphQL::Model` children. DataField arrays are plain hashes /
17
+ # strings, not `GraphQL::Model` objects, so that re-add never happens and the
18
+ # array change becomes invisible to the diff (scalar `passthrough` fields are
19
+ # unaffected). For a leaf model we want every doc key diffed inline, so this
20
+ # service keeps cascaded keys in place.
21
+ class LeafDiffService < Ecoportal::API::Common::GraphQL::Model::Diffable::DiffService
22
+ private
23
+
24
+ # Keep all doc keys: leaf-field arrays are data, not nested models.
25
+ def doc_with_non_cascaded_attributes(json)
26
+ json
27
+ end
28
+ end
29
+
30
+ DIFF_CLASS = LeafDiffService
31
+ # Map GraphQL __typename values to subclass names.
32
+ TYPE_MAP = {
33
+ 'PlainText' => 'PlainText',
34
+ 'RichText' => 'RichText',
35
+ 'Date' => 'DateField',
36
+ 'Number' => 'Number',
37
+ 'Gauge' => 'Gauge',
38
+ 'People' => 'People',
39
+ 'Select' => 'Select',
40
+ 'Checklist' => 'Checklist',
41
+ 'TagField' => 'TagField',
42
+ 'Geo' => 'Geo',
43
+ 'Signature' => 'Signature',
44
+ 'ContractorEntities' => 'ContractorEntities',
45
+ 'CrossReference' => 'CrossReference',
46
+ 'ImageGallery' => 'ImageGallery',
47
+ 'File' => 'FileField',
48
+ 'Mailbox' => 'Mailbox',
49
+ 'ActionsList' => 'ActionsList',
50
+ 'Law' => 'Law',
51
+ 'AiSummary' => 'AiSummary',
52
+ 'Table' => 'Table',
53
+ 'SmartFill' => 'SmartFill'
54
+ }.freeze
55
+
56
+ passkey :id
57
+ passthrough :label, :deindex
58
+ # __typename is stored under the 'type' key after mapping
59
+ passthrough :type
60
+
61
+ class << self
62
+ # Build the correct subclass from a raw field doc.
63
+ def from_doc(raw_doc)
64
+ type = raw_doc.is_a?(Hash) ? (raw_doc['__typename'] || raw_doc['type']) : nil
65
+ subname = TYPE_MAP[type]
66
+ klass = subname ? const_get(subname) : self
67
+ klass.new(raw_doc)
68
+ rescue NameError
69
+ new(raw_doc)
70
+ end
71
+ end
72
+
73
+ def initialize(raw_doc = {})
74
+ # Normalise __typename → type for passthrough accessor
75
+ raw_doc = raw_doc.dup if raw_doc.is_a?(Hash)
76
+ raw_doc['type'] ||= raw_doc['__typename'] if raw_doc.is_a?(Hash)
77
+ super
78
+ end
79
+
80
+ # DataField is a leaf model: it has no nested GraphQL::Model children.
81
+ # flat: true skips diff_reduce — required here, since diff_reduce would
82
+ # dig_delete! the (cascaded) array keys and never re-add them. The flat
83
+ # classic diff still sees those keys because LeafDiffService keeps them.
84
+ def dirty?
85
+ super(flat: true)
86
+ end
87
+
88
+ # Produces the DataFieldInput hash for this field.
89
+ # Returns nil if the field has not been modified.
90
+ # Subclasses override to produce typed payloads.
91
+ def as_input
92
+ nil
93
+ end
94
+
95
+ private
96
+
97
+ # Coerce a value to Float for numeric fields (Number, Gauge), treating a BLANK
98
+ # (nil / empty / whitespace-only string) as nil — NOT 0.0. Ruby's `"".to_f` is 0.0,
99
+ # so a naive `numeric&.to_f` silently wrote a real zero whenever a CSV cell was an
100
+ # empty string (Eco::CSV keeps quoted-empty `""` as "", not nil). A genuine number,
101
+ # or a numeric string like "0" / "15", still becomes a Float. Shared here so Number
102
+ # and Gauge cannot drift apart.
103
+ def numeric_or_nil(value)
104
+ return value.to_f if value.is_a?(Numeric)
105
+
106
+ str = value.to_s.strip
107
+ str.empty? ? nil : str.to_f
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ require_relative 'data_field/plain_text'
117
+ require_relative 'data_field/rich_text'
118
+ require_relative 'data_field/date_field'
119
+ require_relative 'data_field/number'
120
+ require_relative 'data_field/gauge'
121
+ require_relative 'data_field/people'
122
+ require_relative 'data_field/select'
123
+ require_relative 'data_field/checklist'
124
+ require_relative 'data_field/tag_field'
125
+ require_relative 'data_field/geo'
126
+ require_relative 'data_field/signature'
127
+ require_relative 'data_field/contractor_entities'
128
+ require_relative 'data_field/cross_reference'
129
+ require_relative 'data_field/image_gallery'
130
+ require_relative 'data_field/file_field'
131
+ require_relative 'data_field/mailbox'
132
+ require_relative 'data_field/actions_list'
133
+ require_relative 'data_field/law'
134
+ require_relative 'data_field/ai_summary'
135
+ require_relative 'data_field/table'
136
+ require_relative 'data_field/smart_fill'
137
+ require_relative 'data_field/collection'
@@ -0,0 +1,68 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Page
6
+ class Phased
7
+ # Model for a single stage on a PhasedPage.
8
+ # Fields match those returned by the stageAttrs fragment in CommonPageUnion.
9
+ class Stage < Logic::BaseModel
10
+ passkey :id
11
+ passthrough :name, :ordering, :state
12
+ passboolean :active, :started
13
+
14
+ class_resolver :callback_type_class, 'Ecoportal::API::GraphQL::Base::PagesWorkflow::CallbackType'
15
+
16
+ # Injected by Model::Page::Phased so this stage's #components returns the page's
17
+ # canonical field objects (filtered to this stage) rather than fresh dups.
18
+ attr_writer :page_field_collection
19
+
20
+ def workflow_callbacks
21
+ @workflow_callbacks ||= Array(doc['workflowCallbacks'] || []).map { |cb| callback_type_class.from_doc(cb) }
22
+ end
23
+
24
+ # Predicate aliases — eco-helpers scripts call stage.active? / stage.started?
25
+ def active?
26
+ !!doc['active']
27
+ end
28
+
29
+ def started?
30
+ !!doc['started']
31
+ end
32
+
33
+ # Sections in this stage as a SectionCollection.
34
+ def sections
35
+ @sections ||= SectionCollection.from_doc(doc['sections'] || [])
36
+ end
37
+
38
+ # All data fields across all sections in this stage (v2 compat: stage.components).
39
+ # If the parent page injected its canonical field_collection, return THOSE field
40
+ # objects (filtered to this stage by id) so mutations made via the stage are the
41
+ # same objects the page serialises in data_fields_updates. Else build from docs.
42
+ def components
43
+ @components ||= if @page_field_collection
44
+ ids = stage_field_ids
45
+ DataField::Collection.new(@page_field_collection.select { |f| ids.include?(f.id) })
46
+ else
47
+ sections.components
48
+ end
49
+ end
50
+
51
+ # Field ids belonging to this stage (across its sections' data/left/right fields).
52
+ def stage_field_ids
53
+ (doc['sections'] || []).flat_map do |sec|
54
+ (sec['dataFields'] || []) + (sec['leftDataFields'] || []) + (sec['rightDataFields'] || [])
55
+ end.map { |f| f['id'] }.compact
56
+ end
57
+
58
+ # Hash-style access to raw doc — for backwards-compatible scripts.
59
+ def [](key)
60
+ doc[key.to_s]
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,15 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Page
6
+ class Phased < Interface::BasePage
7
+ require_relative 'phased/stage'
8
+
9
+ embeds_many :stages, klass: Stage, read_only: true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Page
6
+ # Wraps a SectionUnion doc (ContentSection or SplitSection).
7
+ # Provides access to heading, type, and data fields within the section.
8
+ class Section < Logic::BaseModel
9
+ read_only!
10
+
11
+ passkey :id
12
+ passthrough :heading
13
+
14
+ # __typename normalised to 'type' during initialisation
15
+ passthrough :type
16
+
17
+ def initialize(raw_doc = {})
18
+ raw_doc = raw_doc.dup if raw_doc.is_a?(Hash)
19
+ raw_doc['type'] ||= raw_doc['__typename'] if raw_doc.is_a?(Hash)
20
+ super
21
+ end
22
+
23
+ # All data fields in this section.
24
+ # ContentSection → dataFields
25
+ # SplitSection → leftDataFields + rightDataFields (merged)
26
+ def components
27
+ all = (doc['dataFields'] || []) +
28
+ (doc['leftDataFields'] || []) +
29
+ (doc['rightDataFields'] || [])
30
+ DataField::Collection.from_doc(all)
31
+ end
32
+
33
+ # v2 compat alias
34
+ alias_method :data_fields, :components
35
+
36
+ def content_section?
37
+ type.to_s == 'ContentSection'
38
+ end
39
+
40
+ def split_section?
41
+ type.to_s == 'SplitSection'
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end