ecoportal-api-graphql 1.3.5 → 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 (499) 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 -477
  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/client.rb +1 -1
  132. data/lib/ecoportal/api/common/graphql/http_client.rb +35 -3
  133. data/lib/ecoportal/api/common/graphql/model/CLAUDE.md +28 -0
  134. data/lib/ecoportal/api/common/graphql/model/as_input.rb +8 -5
  135. data/lib/ecoportal/api/common/graphql/model/diffable/classic_diff_service.rb +3 -1
  136. data/lib/ecoportal/api/common/graphql/model/diffable/diff_service.rb +31 -23
  137. data/lib/ecoportal/api/common/graphql/model/diffable/hash_diff_nesting.rb +54 -1
  138. data/lib/ecoportal/api/graphql/CLAUDE.md +37 -0
  139. data/lib/ecoportal/api/graphql/base/CLAUDE.md +50 -0
  140. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -0
  141. data/lib/ecoportal/api/graphql/base/delta_result.rb +16 -0
  142. data/lib/ecoportal/api/graphql/base/force/binding.rb +19 -0
  143. data/lib/ecoportal/api/graphql/base/force/binding_collection.rb +62 -0
  144. data/lib/ecoportal/api/graphql/base/force/collection.rb +47 -0
  145. data/lib/ecoportal/api/graphql/base/force.rb +65 -0
  146. data/lib/ecoportal/api/graphql/base/kickstand/job.rb +17 -0
  147. data/lib/ecoportal/api/graphql/base/kickstand/workflow.rb +18 -0
  148. data/lib/ecoportal/api/graphql/base/kickstand.rb +13 -0
  149. data/lib/ecoportal/api/graphql/base/page/basic.rb +38 -0
  150. data/lib/ecoportal/api/graphql/base/page/data_field/actions_list.rb +9 -0
  151. data/lib/ecoportal/api/graphql/base/page/data_field/ai_summary.rb +18 -0
  152. data/lib/ecoportal/api/graphql/base/page/data_field/checklist.rb +29 -0
  153. data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +112 -0
  154. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -0
  155. data/lib/ecoportal/api/graphql/base/page/data_field/cross_reference.rb +54 -0
  156. data/lib/ecoportal/api/graphql/base/page/data_field/date_field.rb +23 -0
  157. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -0
  158. data/lib/ecoportal/api/graphql/base/page/data_field/gauge.rb +19 -0
  159. data/lib/ecoportal/api/graphql/base/page/data_field/geo.rb +24 -0
  160. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -0
  161. data/lib/ecoportal/api/graphql/base/page/data_field/law.rb +8 -0
  162. data/lib/ecoportal/api/graphql/base/page/data_field/mailbox.rb +9 -0
  163. data/lib/ecoportal/api/graphql/base/page/data_field/number.rb +20 -0
  164. data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +35 -0
  165. data/lib/ecoportal/api/graphql/base/page/data_field/plain_text.rb +17 -0
  166. data/lib/ecoportal/api/graphql/base/page/data_field/rich_text.rb +26 -0
  167. data/lib/ecoportal/api/graphql/base/page/data_field/select.rb +41 -0
  168. data/lib/ecoportal/api/graphql/base/page/data_field/signature.rb +9 -0
  169. data/lib/ecoportal/api/graphql/base/page/data_field/smart_fill.rb +17 -0
  170. data/lib/ecoportal/api/graphql/base/page/data_field/table.rb +9 -0
  171. data/lib/ecoportal/api/graphql/base/page/data_field/tag_field.rb +21 -0
  172. data/lib/ecoportal/api/graphql/base/page/data_field.rb +137 -12
  173. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +68 -14
  174. data/lib/ecoportal/api/graphql/base/page/phased.rb +1 -0
  175. data/lib/ecoportal/api/graphql/base/page/section.rb +36 -0
  176. data/lib/ecoportal/api/graphql/base/page/section_collection.rb +79 -0
  177. data/lib/ecoportal/api/graphql/base/page.rb +2 -0
  178. data/lib/ecoportal/api/graphql/base/pages_workflow/action_type_selection.rb +17 -0
  179. data/lib/ecoportal/api/graphql/base/pages_workflow/callback_type.rb +28 -0
  180. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change.rb +17 -0
  181. data/lib/ecoportal/api/graphql/base/pages_workflow/command_change_message.rb +15 -0
  182. data/lib/ecoportal/api/graphql/base/pages_workflow/command_es_change.rb +17 -0
  183. data/lib/ecoportal/api/graphql/base/pages_workflow/command_interface.rb +36 -0
  184. data/lib/ecoportal/api/graphql/base/pages_workflow/email_config.rb +18 -0
  185. data/lib/ecoportal/api/graphql/base/pages_workflow/escalation_level.rb +19 -0
  186. data/lib/ecoportal/api/graphql/base/pages_workflow/in_system_config.rb +16 -0
  187. data/lib/ecoportal/api/graphql/base/pages_workflow/mailbox_field_selection.rb +17 -0
  188. data/lib/ecoportal/api/graphql/base/pages_workflow/operation_interface.rb +39 -0
  189. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/assign_to.rb +27 -0
  190. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/create_page.rb +21 -0
  191. data/lib/ecoportal/api/graphql/base/pages_workflow/operations/send_notification.rb +30 -0
  192. data/lib/ecoportal/api/graphql/base/pages_workflow/people_field_selection.rb +17 -0
  193. data/lib/ecoportal/api/graphql/base/pages_workflow/recipient_config.rb +34 -0
  194. data/lib/ecoportal/api/graphql/base/pages_workflow/register_field.rb +17 -0
  195. data/lib/ecoportal/api/graphql/base/pages_workflow/task_config_selection.rb +17 -0
  196. data/lib/ecoportal/api/graphql/base/pages_workflow/time_delay_config.rb +16 -0
  197. data/lib/ecoportal/api/graphql/base/pages_workflow/trigger_interface.rb +26 -0
  198. data/lib/ecoportal/api/graphql/base/pages_workflow/triggers/conditional_logic.rb +17 -0
  199. data/lib/ecoportal/api/graphql/base/pages_workflow/user_selection.rb +16 -0
  200. data/lib/ecoportal/api/graphql/base/pages_workflow.rb +35 -0
  201. data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -0
  202. data/lib/ecoportal/api/graphql/base/preview_page.rb +23 -0
  203. data/lib/ecoportal/api/graphql/base/register.rb +18 -0
  204. data/lib/ecoportal/api/graphql/base.rb +11 -0
  205. data/lib/ecoportal/api/graphql/builder/CLAUDE.md +65 -0
  206. data/lib/ecoportal/api/graphql/builder/kickstand.rb +73 -0
  207. data/lib/ecoportal/api/graphql/builder/page.rb +210 -41
  208. data/lib/ecoportal/api/graphql/builder/register/preset_view.rb +84 -0
  209. data/lib/ecoportal/api/graphql/builder/register.rb +27 -19
  210. data/lib/ecoportal/api/graphql/builder/template.rb +80 -0
  211. data/lib/ecoportal/api/graphql/builder.rb +2 -0
  212. data/lib/ecoportal/api/graphql/compat/filter_translator.rb +107 -0
  213. data/lib/ecoportal/api/graphql/compat/page_reference.rb +23 -0
  214. data/lib/ecoportal/api/graphql/compat/pages.rb +212 -0
  215. data/lib/ecoportal/api/graphql/compat/registers.rb +84 -0
  216. data/lib/ecoportal/api/graphql/compat/response.rb +35 -0
  217. data/lib/ecoportal/api/graphql/compat/search_results.rb +33 -0
  218. data/lib/ecoportal/api/graphql/compat/stage_collection.rb +70 -0
  219. data/lib/ecoportal/api/graphql/compat/stage_view.rb +76 -0
  220. data/lib/ecoportal/api/graphql/compat.rb +17 -0
  221. data/lib/ecoportal/api/graphql/concerns/data_field_access.rb +71 -0
  222. data/lib/ecoportal/api/graphql/concerns/deprecation.rb +20 -0
  223. data/lib/ecoportal/api/graphql/concerns/fragment_definitions.rb +21 -28
  224. data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -0
  225. data/lib/ecoportal/api/graphql/concerns/snake_camel_access.rb +60 -0
  226. data/lib/ecoportal/api/graphql/concerns.rb +4 -0
  227. data/lib/ecoportal/api/graphql/connection/page.rb +11 -0
  228. data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +13 -0
  229. data/lib/ecoportal/api/graphql/connection/preset_view.rb +11 -0
  230. data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -0
  231. data/lib/ecoportal/api/graphql/connection.rb +4 -0
  232. data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -0
  233. data/lib/ecoportal/api/graphql/file_upload.rb +10 -0
  234. data/lib/ecoportal/api/graphql/fragment/action.rb +1 -1
  235. data/lib/ecoportal/api/graphql/fragment/action_category.rb +1 -1
  236. data/lib/ecoportal/api/graphql/fragment/contractor_entity.rb +1 -1
  237. data/lib/ecoportal/api/graphql/fragment/force.rb +30 -0
  238. data/lib/ecoportal/api/graphql/fragment/location_draft.rb +2 -2
  239. data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -1
  240. data/lib/ecoportal/api/graphql/fragment/locations_error.rb +1 -1
  241. data/lib/ecoportal/api/graphql/fragment/page.rb +85 -0
  242. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +395 -0
  243. data/lib/ecoportal/api/graphql/fragment/pages.rb +15 -0
  244. data/lib/ecoportal/api/graphql/fragment/pages_workflow.rb +172 -0
  245. data/lib/ecoportal/api/graphql/fragment/pagination.rb +1 -1
  246. data/lib/ecoportal/api/graphql/fragment.rb +37 -27
  247. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +25 -0
  248. data/lib/ecoportal/api/graphql/input/delta_input.rb +16 -0
  249. data/lib/ecoportal/api/graphql/input/page/archive.rb +14 -0
  250. data/lib/ecoportal/api/graphql/input/page/build_from_template.rb +13 -0
  251. data/lib/ecoportal/api/graphql/input/page/create_draft.rb +13 -0
  252. data/lib/ecoportal/api/graphql/input/page/create_from_template.rb +18 -0
  253. data/lib/ecoportal/api/graphql/input/page/delete_draft.rb +13 -0
  254. data/lib/ecoportal/api/graphql/input/page/publish_draft.rb +13 -0
  255. data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -0
  256. data/lib/ecoportal/api/graphql/input/page/unarchive.rb +14 -0
  257. data/lib/ecoportal/api/graphql/input/page/update.rb +140 -0
  258. data/lib/ecoportal/api/graphql/input/page.rb +26 -0
  259. data/lib/ecoportal/api/graphql/input/preset_view/create.rb +18 -0
  260. data/lib/ecoportal/api/graphql/input/preset_view/permission.rb +16 -0
  261. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -0
  262. data/lib/ecoportal/api/graphql/input/preset_view.rb +14 -0
  263. data/lib/ecoportal/api/graphql/input/register/create.rb +18 -0
  264. data/lib/ecoportal/api/graphql/input/register/update.rb +15 -0
  265. data/lib/ecoportal/api/graphql/input/register.rb +13 -0
  266. data/lib/ecoportal/api/graphql/input/search_conf/ai_generator.rb +234 -0
  267. data/lib/ecoportal/api/graphql/input/search_conf.rb +367 -0
  268. data/lib/ecoportal/api/graphql/input/variable_binding.rb +20 -0
  269. data/lib/ecoportal/api/graphql/input/workflow_command/add_action_tag.rb +18 -0
  270. data/lib/ecoportal/api/graphql/input/workflow_command/add_binding.rb +18 -0
  271. data/lib/ecoportal/api/graphql/input/workflow_command/add_comment_tagging_user_group.rb +18 -0
  272. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_direct_strategy_user.rb +18 -0
  273. data/lib/ecoportal/api/graphql/input/workflow_command/add_default_strategy.rb +18 -0
  274. data/lib/ecoportal/api/graphql/input/workflow_command/add_direct_strategy_user.rb +18 -0
  275. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +18 -0
  276. data/lib/ecoportal/api/graphql/input/workflow_command/add_force.rb +18 -0
  277. data/lib/ecoportal/api/graphql/input/workflow_command/add_gauge_field_stop.rb +19 -0
  278. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_field_config.rb +23 -0
  279. data/lib/ecoportal/api/graphql/input/workflow_command/add_linked_helper.rb +18 -0
  280. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation.rb +18 -0
  281. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_direct_strategy_user.rb +18 -0
  282. data/lib/ecoportal/api/graphql/input/workflow_command/add_operation_strategy.rb +18 -0
  283. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_action_type.rb +18 -0
  284. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_filter.rb +18 -0
  285. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_people_field.rb +18 -0
  286. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_task_config.rb +18 -0
  287. data/lib/ecoportal/api/graphql/input/workflow_command/add_recipient_user.rb +18 -0
  288. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback.rb +23 -0
  289. data/lib/ecoportal/api/graphql/input/workflow_command/add_scheduled_callback_action.rb +18 -0
  290. data/lib/ecoportal/api/graphql/input/workflow_command/add_section.rb +18 -0
  291. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +19 -0
  292. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -0
  293. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_section.rb +18 -0
  294. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage_tag.rb +18 -0
  295. data/lib/ecoportal/api/graphql/input/workflow_command/add_strategy.rb +18 -0
  296. data/lib/ecoportal/api/graphql/input/workflow_command/add_task.rb +18 -0
  297. data/lib/ecoportal/api/graphql/input/workflow_command/add_task_assignment_user_group.rb +18 -0
  298. data/lib/ecoportal/api/graphql/input/workflow_command/add_workflow_callback.rb +18 -0
  299. data/lib/ecoportal/api/graphql/input/workflow_command/collapse_section.rb +18 -0
  300. data/lib/ecoportal/api/graphql/input/workflow_command/edit_binding.rb +18 -0
  301. data/lib/ecoportal/api/graphql/input/workflow_command/edit_creator_permissions.rb +18 -0
  302. data/lib/ecoportal/api/graphql/input/workflow_command/edit_default_strategy.rb +18 -0
  303. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -0
  304. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +18 -0
  305. data/lib/ecoportal/api/graphql/input/workflow_command/edit_gauge_field_stop.rb +19 -0
  306. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_field_config.rb +19 -0
  307. data/lib/ecoportal/api/graphql/input/workflow_command/edit_linked_helper.rb +18 -0
  308. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation.rb +18 -0
  309. data/lib/ecoportal/api/graphql/input/workflow_command/edit_operation_strategy.rb +18 -0
  310. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -0
  311. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -0
  312. data/lib/ecoportal/api/graphql/input/workflow_command/edit_reminder.rb +18 -0
  313. data/lib/ecoportal/api/graphql/input/workflow_command/edit_required_sign_offs.rb +18 -0
  314. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_comment_tagging.rb +18 -0
  315. data/lib/ecoportal/api/graphql/input/workflow_command/edit_restrict_task_assignment.rb +18 -0
  316. data/lib/ecoportal/api/graphql/input/workflow_command/edit_scheduled_callback.rb +22 -0
  317. data/lib/ecoportal/api/graphql/input/workflow_command/edit_section_header.rb +18 -0
  318. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +19 -0
  319. data/lib/ecoportal/api/graphql/input/workflow_command/edit_stage.rb +18 -0
  320. data/lib/ecoportal/api/graphql/input/workflow_command/edit_strategy.rb +18 -0
  321. data/lib/ecoportal/api/graphql/input/workflow_command/edit_task_due.rb +18 -0
  322. data/lib/ecoportal/api/graphql/input/workflow_command/edit_trigger.rb +18 -0
  323. data/lib/ecoportal/api/graphql/input/workflow_command/expand_section.rb +18 -0
  324. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +24 -0
  325. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -0
  326. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -0
  327. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/gauge.rb +20 -0
  328. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -0
  329. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/location_field.rb +24 -0
  330. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -0
  331. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -0
  332. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -0
  333. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -0
  334. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/signature.rb +20 -0
  335. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/table.rb +25 -0
  336. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -0
  337. data/lib/ecoportal/api/graphql/input/workflow_command/move_stage.rb +18 -0
  338. data/lib/ecoportal/api/graphql/input/workflow_command/remove_action_tag.rb +18 -0
  339. data/lib/ecoportal/api/graphql/input/workflow_command/remove_binding.rb +18 -0
  340. data/lib/ecoportal/api/graphql/input/workflow_command/remove_callback.rb +18 -0
  341. data/lib/ecoportal/api/graphql/input/workflow_command/remove_comment_tagging_user_group.rb +18 -0
  342. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_direct_strategy_user.rb +18 -0
  343. data/lib/ecoportal/api/graphql/input/workflow_command/remove_default_strategy.rb +18 -0
  344. data/lib/ecoportal/api/graphql/input/workflow_command/remove_direct_strategy_user.rb +18 -0
  345. data/lib/ecoportal/api/graphql/input/workflow_command/remove_field.rb +18 -0
  346. data/lib/ecoportal/api/graphql/input/workflow_command/remove_force.rb +18 -0
  347. data/lib/ecoportal/api/graphql/input/workflow_command/remove_gauge_field_stop.rb +17 -0
  348. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_field_config.rb +17 -0
  349. data/lib/ecoportal/api/graphql/input/workflow_command/remove_linked_helper.rb +18 -0
  350. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation.rb +18 -0
  351. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_direct_strategy_user.rb +18 -0
  352. data/lib/ecoportal/api/graphql/input/workflow_command/remove_operation_strategy.rb +18 -0
  353. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_action_type.rb +18 -0
  354. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_filter.rb +18 -0
  355. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_people_field.rb +18 -0
  356. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_task_config.rb +18 -0
  357. data/lib/ecoportal/api/graphql/input/workflow_command/remove_recipient_user.rb +18 -0
  358. data/lib/ecoportal/api/graphql/input/workflow_command/remove_scheduled_callback.rb +18 -0
  359. data/lib/ecoportal/api/graphql/input/workflow_command/remove_section.rb +18 -0
  360. data/lib/ecoportal/api/graphql/input/workflow_command/remove_select_field_option.rb +17 -0
  361. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage.rb +18 -0
  362. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_section.rb +18 -0
  363. data/lib/ecoportal/api/graphql/input/workflow_command/remove_stage_tag.rb +18 -0
  364. data/lib/ecoportal/api/graphql/input/workflow_command/remove_strategy.rb +18 -0
  365. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task.rb +18 -0
  366. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_assignment_user_group.rb +18 -0
  367. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_due.rb +18 -0
  368. data/lib/ecoportal/api/graphql/input/workflow_command/remove_task_priority_level.rb +18 -0
  369. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -0
  370. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -0
  371. data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -0
  372. data/lib/ecoportal/api/graphql/input.rb +8 -0
  373. data/lib/ecoportal/api/graphql/interface/base_page.rb +100 -58
  374. data/lib/ecoportal/api/graphql/interface/location_structure/nodes.rb +27 -28
  375. data/lib/ecoportal/api/graphql/logic/base_model.rb +2 -0
  376. data/lib/ecoportal/api/graphql/logic/base_query.rb +45 -2
  377. data/lib/ecoportal/api/graphql/logic/input.rb +15 -0
  378. data/lib/ecoportal/api/graphql/model/ai_summary_version.rb +10 -0
  379. data/lib/ecoportal/api/graphql/model/organization.rb +65 -55
  380. data/lib/ecoportal/api/graphql/model/page/basic.rb +14 -0
  381. data/lib/ecoportal/api/graphql/model/page/phased.rb +82 -20
  382. data/lib/ecoportal/api/graphql/model/page.rb +1 -0
  383. data/lib/ecoportal/api/graphql/model/page_union.rb +21 -0
  384. data/lib/ecoportal/api/graphql/model/pages_workflow.rb +20 -0
  385. data/lib/ecoportal/api/graphql/model/preset_view.rb +10 -0
  386. data/lib/ecoportal/api/graphql/model/preview_page.rb +10 -0
  387. data/lib/ecoportal/api/graphql/model/register.rb +10 -0
  388. data/lib/ecoportal/api/graphql/model.rb +8 -0
  389. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -0
  390. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -0
  391. data/lib/ecoportal/api/graphql/mutation/ai_summary.rb +13 -0
  392. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_jobs.rb +43 -0
  393. data/lib/ecoportal/api/graphql/mutation/kickstand/bulk_update_workflows.rb +43 -0
  394. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_job.rb +39 -0
  395. data/lib/ecoportal/api/graphql/mutation/kickstand/fail_workflow.rb +39 -0
  396. data/lib/ecoportal/api/graphql/mutation/kickstand/start_job.rb +39 -0
  397. data/lib/ecoportal/api/graphql/mutation/kickstand/start_workflow.rb +39 -0
  398. data/lib/ecoportal/api/graphql/mutation/kickstand/stop_workflow.rb +39 -0
  399. data/lib/ecoportal/api/graphql/mutation/kickstand.rb +18 -0
  400. data/lib/ecoportal/api/graphql/mutation/location_structure/apply_commands.rb +3 -3
  401. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/add_commands.rb +2 -2
  402. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/create.rb +2 -2
  403. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/drop_bad_commands.rb +3 -3
  404. data/lib/ecoportal/api/graphql/mutation/location_structure/draft/publish.rb +3 -3
  405. data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -0
  406. data/lib/ecoportal/api/graphql/mutation/page/archive.rb +40 -0
  407. data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +40 -0
  408. data/lib/ecoportal/api/graphql/mutation/page/build_from_template.rb +50 -0
  409. data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +40 -0
  410. data/lib/ecoportal/api/graphql/mutation/page/create_from_template.rb +43 -0
  411. data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +40 -0
  412. data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +69 -0
  413. data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +51 -0
  414. data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +40 -0
  415. data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -0
  416. data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -0
  417. data/lib/ecoportal/api/graphql/mutation/page/unarchive.rb +40 -0
  418. data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -0
  419. data/lib/ecoportal/api/graphql/mutation/page/update.rb +40 -0
  420. data/lib/ecoportal/api/graphql/mutation/page/update_variable_bindings.rb +44 -0
  421. data/lib/ecoportal/api/graphql/mutation/page.rb +28 -0
  422. data/lib/ecoportal/api/graphql/mutation/preset_view/create.rb +35 -0
  423. data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +35 -0
  424. data/lib/ecoportal/api/graphql/mutation/preset_view/permission.rb +37 -0
  425. data/lib/ecoportal/api/graphql/mutation/preset_view/update.rb +35 -0
  426. data/lib/ecoportal/api/graphql/mutation/preset_view.rb +15 -0
  427. data/lib/ecoportal/api/graphql/mutation/register/create.rb +35 -0
  428. data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -0
  429. data/lib/ecoportal/api/graphql/mutation/register/update.rb +35 -0
  430. data/lib/ecoportal/api/graphql/mutation/register.rb +14 -0
  431. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -0
  432. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -0
  433. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -0
  434. data/lib/ecoportal/api/graphql/mutation/template/create.rb +39 -0
  435. data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -0
  436. data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +43 -0
  437. data/lib/ecoportal/api/graphql/mutation/template/publish.rb +39 -0
  438. data/lib/ecoportal/api/graphql/mutation/template/unpublish.rb +39 -0
  439. data/lib/ecoportal/api/graphql/mutation/template/update.rb +43 -0
  440. data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +43 -0
  441. data/lib/ecoportal/api/graphql/mutation/template.rb +18 -0
  442. data/lib/ecoportal/api/graphql/mutation.rb +8 -0
  443. data/lib/ecoportal/api/graphql/payload/ai_summary_generate.rb +12 -0
  444. data/lib/ecoportal/api/graphql/payload/execute_workflow_commands.rb +36 -0
  445. data/lib/ecoportal/api/graphql/payload/force_commands.rb +31 -0
  446. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +36 -0
  447. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +36 -0
  448. data/lib/ecoportal/api/graphql/payload/kickstand/job.rb +13 -0
  449. data/lib/ecoportal/api/graphql/payload/kickstand/workflow.rb +13 -0
  450. data/lib/ecoportal/api/graphql/payload/kickstand.rb +15 -0
  451. data/lib/ecoportal/api/graphql/payload/location_structure/draft/create.rb +33 -34
  452. data/lib/ecoportal/api/graphql/payload/ok_payload.rb +21 -0
  453. data/lib/ecoportal/api/graphql/payload/page/archive.rb +13 -0
  454. data/lib/ecoportal/api/graphql/payload/page/build_from_template.rb +13 -0
  455. data/lib/ecoportal/api/graphql/payload/page/create_from_template.rb +13 -0
  456. data/lib/ecoportal/api/graphql/payload/page/draft.rb +13 -0
  457. data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -0
  458. data/lib/ecoportal/api/graphql/payload/page/unarchive.rb +13 -0
  459. data/lib/ecoportal/api/graphql/payload/page/update.rb +13 -0
  460. data/lib/ecoportal/api/graphql/payload/page/update_variable_bindings.rb +13 -0
  461. data/lib/ecoportal/api/graphql/payload/page.rb +19 -0
  462. data/lib/ecoportal/api/graphql/payload/preset_view.rb +11 -0
  463. data/lib/ecoportal/api/graphql/payload/register.rb +11 -0
  464. data/lib/ecoportal/api/graphql/payload/template/create.rb +13 -0
  465. data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +13 -0
  466. data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +13 -0
  467. data/lib/ecoportal/api/graphql/payload/template/publish.rb +13 -0
  468. data/lib/ecoportal/api/graphql/payload/template/unpublish.rb +13 -0
  469. data/lib/ecoportal/api/graphql/payload/template/update.rb +13 -0
  470. data/lib/ecoportal/api/graphql/payload/template/update_information.rb +13 -0
  471. data/lib/ecoportal/api/graphql/payload/template.rb +18 -0
  472. data/lib/ecoportal/api/graphql/payload.rb +11 -0
  473. data/lib/ecoportal/api/graphql/query/action.rb +1 -1
  474. data/lib/ecoportal/api/graphql/query/action_categories.rb +1 -1
  475. data/lib/ecoportal/api/graphql/query/actions.rb +2 -2
  476. data/lib/ecoportal/api/graphql/query/contractor_entities.rb +1 -1
  477. data/lib/ecoportal/api/graphql/query/file_upload_signature.rb +76 -0
  478. data/lib/ecoportal/api/graphql/query/location_structure/draft.rb +2 -2
  479. data/lib/ecoportal/api/graphql/query/location_structure.rb +1 -1
  480. data/lib/ecoportal/api/graphql/query/location_structures.rb +1 -1
  481. data/lib/ecoportal/api/graphql/query/page.rb +45 -0
  482. data/lib/ecoportal/api/graphql/query/page_delta.rb +47 -0
  483. data/lib/ecoportal/api/graphql/query/page_with_forces.rb +43 -0
  484. data/lib/ecoportal/api/graphql/query/pages.rb +59 -0
  485. data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +59 -0
  486. data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -0
  487. data/lib/ecoportal/api/graphql/query/register_preview_pages.rb +83 -0
  488. data/lib/ecoportal/api/graphql/query/templates.rb +53 -0
  489. data/lib/ecoportal/api/graphql/query.rb +11 -0
  490. data/lib/ecoportal/api/graphql.rb +60 -2
  491. data/lib/ecoportal/api/graphql_version.rb +5 -5
  492. data/scripts/auto-worker-scheduler.sh +386 -0
  493. data/tests/contractor_entity_create.rb +19 -19
  494. data/tests/contractor_entity_udpate.rb +20 -20
  495. data/tests/dump_page_model.rb +74 -0
  496. data/tests/loc_structure_get.rb +1 -2
  497. data/tests/loc_structure_update.rb +51 -51
  498. data/tests/loc_structures_get.rb +15 -15
  499. metadata +436 -5
@@ -0,0 +1,47 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ class Force
6
+ # Wraps a page's forces array. Provides the v2 scripting interface:
7
+ # page.forces.get_by_name(name) → [Force, ...]
8
+ class Collection
9
+ include Enumerable
10
+
11
+ def initialize(raw)
12
+ @forces = Array(raw).map { |f| Force.new(f) }
13
+ end
14
+
15
+ # Filter forces by name (case-insensitive).
16
+ def get_by_name(name)
17
+ target = name.to_s.downcase
18
+ @forces.select { |f| f.name.to_s.downcase == target }
19
+ end
20
+
21
+ # True if any force has uncommitted script changes or pending binding commands.
22
+ def dirty?
23
+ @forces.any?(&:dirty?)
24
+ end
25
+
26
+ # All pending commands across all forces (editForce, addBinding, removeBinding).
27
+ def pending_commands
28
+ @forces.flat_map(&:all_pending_commands)
29
+ end
30
+
31
+ def each(&block)
32
+ @forces.each(&block)
33
+ end
34
+
35
+ def size
36
+ @forces.size
37
+ end
38
+
39
+ def empty?
40
+ @forces.empty?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,65 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ # Represents a Force — a AngularJS/LISP snippet that implements conditional
6
+ # field behaviour on a page stage. Forces are queried as part of a page
7
+ # when forces are explicitly included in the query fragment.
8
+ #
9
+ # Read access is via the page's force collection:
10
+ # force = page.forces.get_by_name('Workflow (S1)').first
11
+ # force.custom_script # → current LISP script
12
+ # force.bindings.get_by_name('top-txt') # → [Binding, ...]
13
+ #
14
+ # Write access queues workflow commands executed after process_ooze:
15
+ # force.custom_script = new_script # → queues editForce command
16
+ # force.bindings.add(field, name: 'bind-name') # → queues addBinding
17
+ # force.bindings.delete!(binding) # → queues removeBinding
18
+ class Force < Logic::BaseModel
19
+ passkey :id
20
+ passthrough :name, :script, :customScript, :weight, :url
21
+ passarray :globalBindingIds
22
+
23
+ require_relative 'force/binding'
24
+ require_relative 'force/binding_collection'
25
+ require_relative 'force/collection'
26
+
27
+ def bindings
28
+ @bindings ||= Force::BindingCollection.new(doc['bindings'] || [], force: self)
29
+ end
30
+
31
+ # v2 compat: scripts read and write custom_script directly.
32
+ # Writing queues an editForce workflow command consumed by ForceCompat.
33
+ def custom_script=(new_script)
34
+ return if doc['customScript'] == new_script
35
+
36
+ doc['customScript'] = new_script
37
+ @_custom_script_dirty = true
38
+ end
39
+
40
+ def dirty?
41
+ @_custom_script_dirty || pending_commands.any?
42
+ end
43
+
44
+ # Accumulate workflow commands (editForce, addBinding, removeBinding).
45
+ def queue_command(command_hash)
46
+ pending_commands << command_hash
47
+ end
48
+
49
+ # All pending commands for this force (editForce if script changed + binding ops).
50
+ def pending_commands
51
+ @pending_commands ||= []
52
+ end
53
+
54
+ # Collect all commands including the script edit if dirty.
55
+ def all_pending_commands
56
+ cmds = []
57
+ cmds << { editForce: { id: id, customScript: doc['customScript'] } } if @_custom_script_dirty
58
+ cmds.concat(pending_commands)
59
+ cmds
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,17 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Kickstand
6
+ class Job < Logic::BaseModel
7
+ SCHEMA_VERSION = '20260605'.freeze
8
+
9
+ passkey :id
10
+ passthrough :status
11
+ passthrough :createdAt, :updatedAt
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Kickstand
6
+ class Workflow < Logic::BaseModel
7
+ SCHEMA_VERSION = '20260605'.freeze
8
+
9
+ passkey :id
10
+ passthrough :name, :status
11
+ passthrough :createdAt, :updatedAt
12
+ passthrough :startedAt, :stoppedAt, :failedAt, :finishedAt
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Kickstand
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ require_relative 'kickstand/workflow'
13
+ require_relative 'kickstand/job'
@@ -0,0 +1,38 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Base
5
+ module Page
6
+ class Basic < Interface::BasePage
7
+ private
8
+
9
+ # v2-compat read flatten: a basic page's fields live nested under
10
+ # sections (CommonPageUnion fetches `sections { ...sectionUnion }` with the
11
+ # data fields inside each section — it does NOT select a top-level
12
+ # `dataFields`). v2 exposed fields flat at the page (page.components), so
13
+ # gather them from sections here, mirroring Model::Page::Phased#raw_data_fields.
14
+ # Field docs are shared by reference, so mutations forward to the section doc.
15
+ #
16
+ # `raw_sections` keeps the DataFieldAccess default (doc['sections']) — basic
17
+ # pages already store sections at the page root.
18
+ def raw_data_fields
19
+ from_sections = raw_sections.flat_map { |section| field_docs_in(section) }.compact.uniq { |fld| fld['id'] }
20
+ return from_sections unless from_sections.empty?
21
+
22
+ # Create/flat path: fields may be supplied at the page root (top-level
23
+ # `dataFields`, no sections) — fall back to the DataFieldAccess default
24
+ # so create input + flat-built basic pages still expose their fields.
25
+ doc['dataFields'] || []
26
+ end
27
+
28
+ def field_docs_in(section)
29
+ (section['dataFields'] || []) +
30
+ (section['leftDataFields'] || []) +
31
+ (section['rightDataFields'] || [])
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Actions list field — embedded action items linked to this page.
4
+ # Actions are managed through Mutation::Action::*; not via DataFieldInput.
5
+ class ActionsList < DataField
6
+ passarray :actions # each: { id:, name:, status: }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # AI-generated summary field.
4
+ # Content is generated server-side via generateAiSummary mutation (Bedrock Claude).
5
+ # Not settable via DataFieldInput — use the dedicated mutation instead.
6
+ #
7
+ # instructions: the AI prompt configured on this field in the template.
8
+ # trigger: "submitted" | "approved" — when generation fires.
9
+ # trigger_stage_ids: stages that trigger generation (empty = all stages).
10
+ # text_format: "simple_text" | "bullet_points" | "numbered_list" | "headings"
11
+ class AiSummary < DataField
12
+ passthrough :aiSummary, :generatedAt, :instructions
13
+ passthrough :trigger, :textFormat
14
+ passarray :triggerStageIds, :fieldConfigurations
15
+ passboolean :alreadyRated
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ class Checklist < DataField
4
+ passarray :items # each: { id:, label:, checked: }
5
+ passthrough :complete
6
+
7
+ # Check/uncheck an item by label or id.
8
+ def check(label_or_id, checked: true)
9
+ item = (doc['items'] || []).find { |i| i['label'] == label_or_id.to_s || i['id'] == label_or_id.to_s }
10
+ return unless item
11
+
12
+ item['checked'] = checked
13
+ end
14
+
15
+ def as_input
16
+ return nil unless dirty?
17
+
18
+ {
19
+ checklist: {
20
+ id: id,
21
+ # rubocop:disable Style/DoubleNegation
22
+ items: (doc['items'] || []).map { |i| { id: i['id'], checked: !!i['checked'] } }
23
+ # rubocop:enable Style/DoubleNegation
24
+ }
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,112 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Wraps an array of raw data field docs as typed DataField objects.
4
+ # Provides v2-compatible access patterns used in eco-helpers scripts:
5
+ # components.get_by_type(:plain_text) — filter by field type
6
+ # components.get_by_name('FieldLabel') — find by label
7
+ # components.doc — raw doc array
8
+ # components.dirty_inputs — changed field hashes for mutation
9
+ class Collection
10
+ include Enumerable
11
+
12
+ # Accepts raw field docs (→ built via from_doc) OR already-built DataField objects
13
+ # (kept as-is). Preserving existing objects lets a phased page's Stage#components share
14
+ # the SAME field objects as the page's canonical field_collection (so stage-level
15
+ # mutations land in the page's data_fields_updates — phased write-path identity).
16
+ def initialize(fields)
17
+ @fields = Array(fields).map { |f| f.is_a?(DataField) ? f : DataField.from_doc(f) }
18
+ end
19
+
20
+ # Build from a raw docs array.
21
+ def self.from_doc(raw_array)
22
+ new(Array(raw_array))
23
+ end
24
+
25
+ # Filter by field type. Accepts symbol (:plain_text), string ('PlainText'), or
26
+ # the GraphQL __typename directly. Matching is case-insensitive.
27
+ def get_by_type(type_key)
28
+ target = normalise_type(type_key)
29
+ @fields.select { |f| normalise_type(f.type) == target }
30
+ end
31
+
32
+ # Find first field matching label (case-insensitive). Optional type: restricts to
33
+ # that field type first (v2 compat: components.get_by_name(name, type:) — used by
34
+ # the farmers cutover use case which calls it directly, not via OozeRedirect).
35
+ def get_by_name(label, type: nil)
36
+ pool = type ? get_by_type(type) : @fields
37
+ pool.find { |f| f.label.to_s.downcase == label.to_s.downcase }
38
+ end
39
+
40
+ # Raw doc array (v2 compat — scripts iterate secs_doc / flds_doc).
41
+ def doc
42
+ @fields.map(&:doc)
43
+ end
44
+
45
+ # Add a new field to this collection (v2 compat: components.add).
46
+ # The field must reference an existing server-side ID (from buildFromTemplate).
47
+ # Yields the new field object to the block for configuration.
48
+ # Returns the field object.
49
+ #
50
+ # Usage:
51
+ # components.add(doc: raw_doc) { |f| f.value = 'X' }
52
+ # components.add(id: 'f1', label: 'Name', type: 'PlainText') { |f| f.value = 'X' }
53
+ def add(doc: nil, id: nil, label: nil, type: nil, **opts)
54
+ raw = doc || opts.merge('id' => id, 'label' => label, '__typename' => type.to_s).compact
55
+ field = DataField.from_doc(raw)
56
+ @additions ||= []
57
+ @additions << field
58
+ yield field if block_given?
59
+ field
60
+ end
61
+
62
+ # Mark a field for deletion by field ID or field object.
63
+ # The field ID is sent in dataFields.deletions on the next update.
64
+ def mark_for_deletion(id_or_field)
65
+ field_id = id_or_field.respond_to?(:id) ? id_or_field.id : id_or_field.to_s
66
+ @deletions ||= []
67
+ @deletions |= [field_id]
68
+ end
69
+
70
+ # Returns DataFieldInput hashes for fields modified via value setters.
71
+ def dirty_inputs
72
+ @fields.filter_map(&:as_input)
73
+ end
74
+
75
+ # Returns DataFieldInput hashes for fields added via #add.
76
+ def dirty_additions
77
+ (@additions || []).filter_map(&:as_input)
78
+ end
79
+
80
+ # Returns field IDs queued for deletion via #mark_for_deletion.
81
+ def dirty_deletions
82
+ @deletions || []
83
+ end
84
+
85
+ def dirty?
86
+ @fields.any?(&:dirty?) ||
87
+ (@additions || []).any?(&:dirty?) ||
88
+ !dirty_deletions.empty?
89
+ end
90
+
91
+ def each(&block)
92
+ @fields.each(&block)
93
+ end
94
+
95
+ def empty?
96
+ @fields.empty?
97
+ end
98
+
99
+ def length
100
+ @fields.length
101
+ end
102
+
103
+ private
104
+
105
+ # Normalise type key to lowercase no-underscore string for comparison.
106
+ # :plain_text → 'plaintext', 'PlainText' → 'plaintext'
107
+ def normalise_type(type_key)
108
+ type_key.to_s.downcase.delete('_') if type_key
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,28 @@
1
+ module Ecoportal::API::GraphQL::Base::Page
2
+ class DataField
3
+ # Contractor entity selector field.
4
+ # Stores references to ContractorEntity records.
5
+ class ContractorEntities < DataField
6
+ passarray :contractors # each: { id:, name: }
7
+
8
+ def contractor_entity_ids
9
+ Array(contractors).map { |c| c.is_a?(Hash) ? c['id'] : c }.compact
10
+ end
11
+
12
+ def contractor_entity_ids=(ids)
13
+ doc['contractors'] = Array(ids).map { |id_val| { 'id' => id_val } }
14
+ end
15
+
16
+ # v2 compat alias
17
+ def value=(ids)
18
+ self.contractor_entity_ids = ids
19
+ end
20
+
21
+ def as_input
22
+ return nil unless dirty?
23
+
24
+ { contractorEntities: { id: id, contractorEntityIds: contractor_entity_ids } }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -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