ecoportal-api-graphql 1.3.17 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +763 -11
  3. data/lib/ecoportal/api/common/graphql/http_client.rb +85 -2
  4. data/lib/ecoportal/api/common/graphql/model/diffable/leaf_diff_service.rb +97 -0
  5. data/lib/ecoportal/api/common/graphql/model/diffable.rb +1 -0
  6. data/lib/ecoportal/api/graphql/base/action.rb +1 -1
  7. data/lib/ecoportal/api/graphql/base/action_event.rb +23 -0
  8. data/lib/ecoportal/api/graphql/base/action_event_page.rb +16 -0
  9. data/lib/ecoportal/api/graphql/base/activity.rb +38 -0
  10. data/lib/ecoportal/api/graphql/base/activity_log.rb +38 -0
  11. data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +12 -7
  12. data/lib/ecoportal/api/graphql/base/auth_log.rb +24 -0
  13. data/lib/ecoportal/api/graphql/base/authorization_result.rb +20 -0
  14. data/lib/ecoportal/api/graphql/base/contractor_entity/member_changes.rb +17 -5
  15. data/lib/ecoportal/api/graphql/base/file_container.rb +12 -4
  16. data/lib/ecoportal/api/graphql/base/location_classification_type.rb +2 -0
  17. data/lib/ecoportal/api/graphql/base/location_structure.rb +3 -0
  18. data/lib/ecoportal/api/graphql/base/minimal_dashboard.rb +25 -0
  19. data/lib/ecoportal/api/graphql/base/organization.rb +2 -0
  20. data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +25 -2
  21. data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +46 -7
  22. data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +5 -2
  23. data/lib/ecoportal/api/graphql/base/page/data_field/people.rb +50 -12
  24. data/lib/ecoportal/api/graphql/base/page/data_field.rb +12 -18
  25. data/lib/ecoportal/api/graphql/base/page/phased/stage.rb +51 -0
  26. data/lib/ecoportal/api/graphql/base/permissions.rb +15 -0
  27. data/lib/ecoportal/api/graphql/base/person_member_changelog.rb +23 -0
  28. data/lib/ecoportal/api/graphql/base/preset_view.rb +135 -0
  29. data/lib/ecoportal/api/graphql/base/register.rb +5 -0
  30. data/lib/ecoportal/api/graphql/base/template/change_log_entry.rb +118 -0
  31. data/lib/ecoportal/api/graphql/base/template.rb +2 -0
  32. data/lib/ecoportal/api/graphql/base.rb +9 -0
  33. data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +10 -0
  34. data/lib/ecoportal/api/graphql/builder/page.rb +0 -16
  35. data/lib/ecoportal/api/graphql/builder/template.rb +46 -0
  36. data/lib/ecoportal/api/graphql/builder/template_builder.rb +21 -2
  37. data/lib/ecoportal/api/graphql/compat/pages.rb +7 -2
  38. data/lib/ecoportal/api/graphql/compat/response.rb +14 -0
  39. data/lib/ecoportal/api/graphql/concerns/threadable.rb +58 -0
  40. data/lib/ecoportal/api/graphql/concerns.rb +1 -0
  41. data/lib/ecoportal/api/graphql/connection/action.rb +1 -1
  42. data/lib/ecoportal/api/graphql/connection/action_category.rb +1 -1
  43. data/lib/ecoportal/api/graphql/connection/activity.rb +11 -0
  44. data/lib/ecoportal/api/graphql/connection/activity_log.rb +11 -0
  45. data/lib/ecoportal/api/graphql/connection/auth_log.rb +11 -0
  46. data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +1 -1
  47. data/lib/ecoportal/api/graphql/connection/minimal_dashboard.rb +11 -0
  48. data/lib/ecoportal/api/graphql/connection/page.rb +1 -1
  49. data/lib/ecoportal/api/graphql/connection/pages_workflow_command.rb +1 -1
  50. data/lib/ecoportal/api/graphql/connection/person_member.rb +1 -1
  51. data/lib/ecoportal/api/graphql/connection/person_member_changelog.rb +11 -0
  52. data/lib/ecoportal/api/graphql/connection/preview_page.rb +1 -1
  53. data/lib/ecoportal/api/graphql/connection/template_change_log.rb +19 -0
  54. data/lib/ecoportal/api/graphql/connection.rb +6 -0
  55. data/lib/ecoportal/api/graphql/diff/change.rb +7 -2
  56. data/lib/ecoportal/api/graphql/diff/command_synthesizer.rb +19 -1
  57. data/lib/ecoportal/api/graphql/diff/pairing/signals.rb +21 -1
  58. data/lib/ecoportal/api/graphql/diff/version_diff.rb +13 -6
  59. data/lib/ecoportal/api/graphql/file_upload/client.rb +198 -139
  60. data/lib/ecoportal/api/graphql/fragment/activity.rb +84 -0
  61. data/lib/ecoportal/api/graphql/fragment/activity_log.rb +75 -0
  62. data/lib/ecoportal/api/graphql/fragment/auth_log.rb +29 -0
  63. data/lib/ecoportal/api/graphql/fragment/authorization_result.rb +14 -0
  64. data/lib/ecoportal/api/graphql/fragment/location_node.rb +1 -0
  65. data/lib/ecoportal/api/graphql/fragment/minimal_dashboard.rb +31 -0
  66. data/lib/ecoportal/api/graphql/fragment/page.rb +1 -0
  67. data/lib/ecoportal/api/graphql/fragment/pages/common_page_union.rb +23 -0
  68. data/lib/ecoportal/api/graphql/fragment/permissions.rb +96 -0
  69. data/lib/ecoportal/api/graphql/fragment/person_member_changelog.rb +33 -0
  70. data/lib/ecoportal/api/graphql/fragment/template_change_log.rb +102 -0
  71. data/lib/ecoportal/api/graphql/fragment.rb +8 -0
  72. data/lib/ecoportal/api/graphql/input/action/update.rb +12 -0
  73. data/lib/ecoportal/api/graphql/input/contractor_entity/manager_settings.rb +20 -0
  74. data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +6 -27
  75. data/lib/ecoportal/api/graphql/input/contractor_entity.rb +1 -0
  76. data/lib/ecoportal/api/graphql/input/date_range.rb +16 -0
  77. data/lib/ecoportal/api/graphql/input/location_structure/apply_commands.rb +2 -2
  78. data/lib/ecoportal/api/graphql/input/location_structure/draft/add_commands.rb +2 -2
  79. data/lib/ecoportal/api/graphql/input/location_structure/insert_command.rb +8 -0
  80. data/lib/ecoportal/api/graphql/input/location_structure/update_command.rb +6 -2
  81. data/lib/ecoportal/api/graphql/input/page/review_task.rb +14 -1
  82. data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -1
  83. data/lib/ecoportal/api/graphql/input/workflow_command/add_checklist_field_item.rb +19 -0
  84. data/lib/ecoportal/api/graphql/input/workflow_command/add_field.rb +10 -9
  85. data/lib/ecoportal/api/graphql/input/workflow_command/add_select_field_option.rb +11 -5
  86. data/lib/ecoportal/api/graphql/input/workflow_command/add_smart_fill_config.rb +23 -0
  87. data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +3 -2
  88. data/lib/ecoportal/api/graphql/input/workflow_command/add_viewable_field.rb +45 -0
  89. data/lib/ecoportal/api/graphql/input/workflow_command/archive_public_config.rb +19 -0
  90. data/lib/ecoportal/api/graphql/input/workflow_command/create_public_config.rb +19 -0
  91. data/lib/ecoportal/api/graphql/input/workflow_command/edit_checklist_field_item.rb +19 -0
  92. data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +17 -2
  93. data/lib/ecoportal/api/graphql/input/workflow_command/edit_force.rb +5 -2
  94. data/lib/ecoportal/api/graphql/input/workflow_command/edit_public_config.rb +19 -0
  95. data/lib/ecoportal/api/graphql/input/workflow_command/edit_select_field_option.rb +6 -2
  96. data/lib/ecoportal/api/graphql/input/workflow_command/edit_smart_fill_config.rb +21 -0
  97. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_advanced_settings.rb +19 -0
  98. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_basic_settings.rb +32 -0
  99. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_container_uid.rb +45 -0
  100. data/lib/ecoportal/api/graphql/input/workflow_command/{edit_page_creator_permissions.rb → edit_template_creator_permissions.rb} +3 -2
  101. data/lib/ecoportal/api/graphql/input/workflow_command/edit_template_descriptions.rb +19 -0
  102. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/action_list.rb +31 -0
  103. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/contractor_entities.rb +6 -2
  104. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +9 -1
  105. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +15 -2
  106. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/file.rb +21 -0
  107. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +3 -2
  108. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +27 -3
  109. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +9 -2
  110. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +3 -2
  111. data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +3 -2
  112. data/lib/ecoportal/api/graphql/input/workflow_command/manage_copy_page_configuration.rb +47 -0
  113. data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +3 -2
  114. data/lib/ecoportal/api/graphql/input/workflow_command/move_section_to_stage.rb +33 -0
  115. data/lib/ecoportal/api/graphql/input/workflow_command/move_viewable_field.rb +33 -0
  116. data/lib/ecoportal/api/graphql/input/workflow_command/remove_checklist_field_item.rb +19 -0
  117. data/lib/ecoportal/api/graphql/input/workflow_command/remove_smart_fill_config.rb +19 -0
  118. data/lib/ecoportal/api/graphql/input/workflow_command/remove_viewable_field.rb +21 -0
  119. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +3 -2
  120. data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +3 -2
  121. data/lib/ecoportal/api/graphql/input/workflow_command.rb +48 -5
  122. data/lib/ecoportal/api/graphql/input.rb +1 -0
  123. data/lib/ecoportal/api/graphql/interface/base_page.rb +50 -3
  124. data/lib/ecoportal/api/graphql/interface/location_structure/command.rb +2 -1
  125. data/lib/ecoportal/api/graphql/logic/base_query.rb +72 -5
  126. data/lib/ecoportal/api/graphql/logic/connection.rb +17 -0
  127. data/lib/ecoportal/api/graphql/logic/input.rb +127 -0
  128. data/lib/ecoportal/api/graphql/model/action_event.rb +14 -0
  129. data/lib/ecoportal/api/graphql/model/action_event_page.rb +10 -0
  130. data/lib/ecoportal/api/graphql/model/activity.rb +14 -0
  131. data/lib/ecoportal/api/graphql/model/activity_log.rb +31 -0
  132. data/lib/ecoportal/api/graphql/model/auth_log.rb +11 -0
  133. data/lib/ecoportal/api/graphql/model/authorization_result.rb +12 -0
  134. data/lib/ecoportal/api/graphql/model/minimal_dashboard.rb +14 -0
  135. data/lib/ecoportal/api/graphql/model/organization.rb +36 -0
  136. data/lib/ecoportal/api/graphql/model/page/phased.rb +17 -0
  137. data/lib/ecoportal/api/graphql/model/permissions.rb +185 -0
  138. data/lib/ecoportal/api/graphql/model/person_member.rb +7 -0
  139. data/lib/ecoportal/api/graphql/model/person_member_changelog.rb +15 -0
  140. data/lib/ecoportal/api/graphql/model/template/change_log_entry.rb +19 -0
  141. data/lib/ecoportal/api/graphql/model/template.rb +4 -1
  142. data/lib/ecoportal/api/graphql/model.rb +9 -0
  143. data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +20 -10
  144. data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +25 -10
  145. data/lib/ecoportal/api/graphql/mutation/contractor_entity/update_manager_settings.rb +37 -0
  146. data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +1 -0
  147. data/lib/ecoportal/api/graphql/mutation/file_container/upload.rb +78 -0
  148. data/lib/ecoportal/api/graphql/mutation/file_container.rb +15 -0
  149. data/lib/ecoportal/api/graphql/mutation/template/update_related_page.rb +79 -0
  150. data/lib/ecoportal/api/graphql/mutation/template.rb +1 -0
  151. data/lib/ecoportal/api/graphql/mutation.rb +1 -1
  152. data/lib/ecoportal/api/graphql/payload/contractor_entity/manager_settings.rb +20 -0
  153. data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +1 -0
  154. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_jobs.rb +2 -2
  155. data/lib/ecoportal/api/graphql/payload/kickstand/bulk_update_workflows.rb +2 -2
  156. data/lib/ecoportal/api/graphql/payload/template/update_related_page.rb +15 -0
  157. data/lib/ecoportal/api/graphql/payload/template.rb +1 -0
  158. data/lib/ecoportal/api/graphql/payload/upload.rb +12 -0
  159. data/lib/ecoportal/api/graphql/payload.rb +1 -0
  160. data/lib/ecoportal/api/graphql/query/activity_logs.rb +78 -0
  161. data/lib/ecoportal/api/graphql/query/auth_logs.rb +51 -0
  162. data/lib/ecoportal/api/graphql/query/dashboard_search.rb +57 -0
  163. data/lib/ecoportal/api/graphql/query/location_structure.rb +2 -0
  164. data/lib/ecoportal/api/graphql/query/location_structures.rb +2 -0
  165. data/lib/ecoportal/api/graphql/query/page.rb +6 -1
  166. data/lib/ecoportal/api/graphql/query/page_activities.rb +56 -0
  167. data/lib/ecoportal/api/graphql/query/permissions.rb +35 -0
  168. data/lib/ecoportal/api/graphql/query/person_member_changelogs.rb +53 -0
  169. data/lib/ecoportal/api/graphql/query/person_members.rb +76 -0
  170. data/lib/ecoportal/api/graphql/query/template_change_log.rb +118 -0
  171. data/lib/ecoportal/api/graphql/query.rb +8 -0
  172. data/lib/ecoportal/api/graphql_version.rb +1 -1
  173. metadata +83 -14
  174. data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +0 -28
  175. data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +0 -36
  176. data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +0 -40
  177. data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +0 -13
@@ -0,0 +1,33 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ # `WorkflowMoveSectionToStageInput` (20260929) -- moves an existing section from one
7
+ # stage to another, optionally placed relative to an anchor section. Unlike
8
+ # `reorderSection` (same stage), the source and target stages are both required.
9
+ module MoveSectionToStage
10
+ SCHEMA_VERSION = '20260929'.freeze
11
+ VALID_KEYS = %i[sourceStageId targetStageId sectionId anchorId anchorPosition].freeze
12
+ ANCHOR_POSITIONS = AddViewableField::ANCHOR_POSITIONS
13
+
14
+ def self.build(source_stage_id:, target_stage_id:, section_id:, **kwargs)
15
+ position = kwargs[:anchorPosition]
16
+ unless position.nil? || ANCHOR_POSITIONS.include?(position.to_s)
17
+ raise ArgumentError,
18
+ "Invalid anchorPosition #{position.inspect}: " \
19
+ "expected one of #{ANCHOR_POSITIONS.join(', ')}"
20
+ end
21
+
22
+ {
23
+ sourceStageId: source_stage_id,
24
+ targetStageId: target_stage_id,
25
+ sectionId: section_id
26
+ }.merge(kwargs.slice(:anchorId, :anchorPosition).compact)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ # `WorkflowMoveViewableFieldInput` — reorders a viewable field on a People data field.
7
+ # Same shape as AddViewableField minus `schemaFieldType` (the field already exists, so
8
+ # its type is known server-side). See AddViewableField for why these three commands
9
+ # replaced the removed `people.add/removeViewableFields` arrays.
10
+ module MoveViewableField
11
+ SCHEMA_VERSION = '20260725'.freeze
12
+ VALID_KEYS = %i[dataFieldId schemaFieldId anchorId anchorPosition].freeze
13
+ ANCHOR_POSITIONS = AddViewableField::ANCHOR_POSITIONS
14
+
15
+ def self.build(data_field_id:, schema_field_id:, **kwargs)
16
+ position = kwargs[:anchorPosition]
17
+ unless position.nil? || ANCHOR_POSITIONS.include?(position.to_s)
18
+ raise ArgumentError,
19
+ "Invalid anchorPosition #{position.inspect}: " \
20
+ "expected one of #{ANCHOR_POSITIONS.join(', ')}"
21
+ end
22
+
23
+ {
24
+ dataFieldId: data_field_id,
25
+ schemaFieldId: schema_field_id
26
+ }.merge(kwargs.slice(:anchorId, :anchorPosition).compact)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ # 20260713
7
+ module RemoveChecklistFieldItem
8
+ SCHEMA_VERSION = '20260713'.freeze
9
+ VALID_KEYS = %i[dataFieldId itemId].freeze
10
+
11
+ def self.build(**kwargs)
12
+ kwargs.slice(*VALID_KEYS).compact
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ # 20260713
7
+ module RemoveSmartFillConfig
8
+ SCHEMA_VERSION = '20260713'.freeze
9
+ VALID_KEYS = %i[dataFieldId].freeze
10
+
11
+ def self.build(**kwargs)
12
+ kwargs.slice(*VALID_KEYS).compact
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Input
5
+ class WorkflowCommand
6
+ # `WorkflowRemoveViewableFieldInput` — removes a viewable field from a People data
7
+ # field. Both keys are required (`ID!`). Replaces the removed
8
+ # `people.removeViewableFields` array — see AddViewableField.
9
+ module RemoveViewableField
10
+ SCHEMA_VERSION = '20260725'.freeze
11
+ VALID_KEYS = %i[dataFieldId schemaFieldId].freeze
12
+
13
+ def self.build(data_field_id:, schema_field_id:)
14
+ { dataFieldId: data_field_id, schemaFieldId: schema_field_id }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,8 +4,9 @@ module Ecoportal
4
4
  module Input
5
5
  class WorkflowCommand
6
6
  module ReorderForces
7
- SCHEMA_VERSION = '20260605'.freeze
8
- VALID_KEYS = %i[forceId after before].freeze
7
+ SCHEMA_VERSION = '20260713'.freeze
8
+ # 20260713: `after`/`before` replaced by anchor-based positioning.
9
+ VALID_KEYS = %i[forceId anchorId anchorPosition].freeze
9
10
 
10
11
  def self.build(**kwargs)
11
12
  kwargs.slice(*VALID_KEYS).compact
@@ -4,8 +4,9 @@ module Ecoportal
4
4
  module Input
5
5
  class WorkflowCommand
6
6
  module ReorderSection
7
- SCHEMA_VERSION = '20260605'.freeze
8
- VALID_KEYS = %i[sectionId stageId after before].freeze
7
+ SCHEMA_VERSION = '20260713'.freeze
8
+ # 20260713: `after`/`before` replaced by anchor-based positioning.
9
+ VALID_KEYS = %i[sectionId stageId anchorId anchorPosition].freeze
9
10
 
10
11
  def self.build(**kwargs)
11
12
  kwargs.slice(*VALID_KEYS).compact
@@ -6,9 +6,19 @@ module Ecoportal
6
6
  SCHEMA_VERSION = '20260605'.freeze
7
7
 
8
8
  COMMAND_MAP = {
9
- # Page-level
10
- editPage: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditPage',
11
- editPageCreatorPermissions: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditPageCreatorPermissions',
9
+ # Template settings (20260713 — supersede the removed page-level editPage/editPageCreatorPermissions)
10
+ editTemplateBasicSettings: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditTemplateBasicSettings',
11
+ editTemplateAdvancedSettings: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditTemplateAdvancedSettings',
12
+ editTemplateCreatorPermissions: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditTemplateCreatorPermissions',
13
+ editTemplateDescriptions: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditTemplateDescriptions',
14
+ # Cross-version template identity (the series key) — see the input class for the
15
+ # publish-swap rule that makes it usable as an entity id across versions.
16
+ editTemplateContainerUid: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditTemplateContainerUid',
17
+ createPublicConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::CreatePublicConfig',
18
+ editPublicConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditPublicConfig',
19
+ archivePublicConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::ArchivePublicConfig',
20
+ # Copy-page configuration (20260819)
21
+ manageCopyPageConfiguration: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::ManageCopyPageConfiguration',
12
22
  # Stage structural
13
23
  addStage: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddStage',
14
24
  removeStage: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveStage',
@@ -33,6 +43,7 @@ module Ecoportal
33
43
  collapseSection: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::CollapseSection',
34
44
  expandSection: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::ExpandSection',
35
45
  reorderSection: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::ReorderSection',
46
+ moveSectionToStage: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::MoveSectionToStage',
36
47
  # Task
37
48
  addTask: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddTask',
38
49
  removeTask: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveTask',
@@ -91,6 +102,19 @@ module Ecoportal
91
102
  removeField: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveField',
92
103
  # Field config
93
104
  editFieldConfiguration: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditFieldConfiguration',
105
+ # Viewable fields on People data fields (20260725) — replaced the REMOVED
106
+ # `people.addViewableFields`/`removeViewableFields` arrays; see AddViewableField.
107
+ addViewableField: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddViewableField',
108
+ moveViewableField: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::MoveViewableField',
109
+ removeViewableField: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveViewableField',
110
+ # Checklist field items (20260713)
111
+ addChecklistFieldItem: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddChecklistFieldItem',
112
+ editChecklistFieldItem: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditChecklistFieldItem',
113
+ removeChecklistFieldItem: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveChecklistFieldItem',
114
+ # Smart-fill config (20260713 — extracted from field-config inputs)
115
+ addSmartFillConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddSmartFillConfig',
116
+ editSmartFillConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditSmartFillConfig',
117
+ removeSmartFillConfig: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::RemoveSmartFillConfig',
94
118
  # Select options
95
119
  addSelectFieldOption: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::AddSelectFieldOption',
96
120
  editSelectFieldOption: 'Ecoportal::API::GraphQL::Input::WorkflowCommand::EditSelectFieldOption',
@@ -147,8 +171,21 @@ module Ecoportal
147
171
  end
148
172
  end
149
173
 
150
- require_relative 'workflow_command/edit_page'
151
- require_relative 'workflow_command/edit_page_creator_permissions'
174
+ require_relative 'workflow_command/edit_template_basic_settings'
175
+ require_relative 'workflow_command/edit_template_advanced_settings'
176
+ require_relative 'workflow_command/edit_template_creator_permissions'
177
+ require_relative 'workflow_command/edit_template_descriptions'
178
+ require_relative 'workflow_command/edit_template_container_uid'
179
+ require_relative 'workflow_command/create_public_config'
180
+ require_relative 'workflow_command/edit_public_config'
181
+ require_relative 'workflow_command/archive_public_config'
182
+ require_relative 'workflow_command/manage_copy_page_configuration'
183
+ require_relative 'workflow_command/add_checklist_field_item'
184
+ require_relative 'workflow_command/edit_checklist_field_item'
185
+ require_relative 'workflow_command/remove_checklist_field_item'
186
+ require_relative 'workflow_command/add_smart_fill_config'
187
+ require_relative 'workflow_command/edit_smart_fill_config'
188
+ require_relative 'workflow_command/remove_smart_fill_config'
152
189
  require_relative 'workflow_command/add_stage'
153
190
  require_relative 'workflow_command/remove_stage'
154
191
  require_relative 'workflow_command/edit_stage'
@@ -237,9 +274,15 @@ require_relative 'workflow_command/remove_binding'
237
274
  require_relative 'workflow_command/add_linked_helper'
238
275
  require_relative 'workflow_command/edit_linked_helper'
239
276
  require_relative 'workflow_command/remove_linked_helper'
277
+ require_relative 'workflow_command/add_viewable_field'
278
+ require_relative 'workflow_command/move_viewable_field'
279
+ require_relative 'workflow_command/move_section_to_stage'
280
+ require_relative 'workflow_command/remove_viewable_field'
281
+ require_relative 'workflow_command/field_config/action_list'
240
282
  require_relative 'workflow_command/field_config/contractor_entities'
241
283
  require_relative 'workflow_command/field_config/cross_reference'
242
284
  require_relative 'workflow_command/field_config/date'
285
+ require_relative 'workflow_command/field_config/file'
243
286
  require_relative 'workflow_command/field_config/gauge'
244
287
  require_relative 'workflow_command/field_config/image_gallery'
245
288
  require_relative 'workflow_command/field_config/location_field'
@@ -16,6 +16,7 @@ require_relative 'input/contractor_entity'
16
16
  require_relative 'input/action'
17
17
  require_relative 'input/page'
18
18
  require_relative 'input/delta_input'
19
+ require_relative 'input/date_range'
19
20
  require_relative 'input/search_conf'
20
21
 
21
22
  require_relative 'input/register'
@@ -6,6 +6,12 @@ module Ecoportal
6
6
  include Concerns::PageCompat
7
7
  include Concerns::DataFieldAccess
8
8
 
9
+ # Page-level leaf arrays (`otherTags`, whole-set `locations`) are cascaded
10
+ # attributes the default DiffService cannot see. LeafDiffService diffs them
11
+ # inline (full-set replace) while still cascading into real nested models
12
+ # (e.g. a phased page's `stages`). See Diffable::LeafDiffService.
13
+ DIFF_CLASS = Ecoportal::API::Common::GraphQL::Model::Diffable::LeafDiffService
14
+
9
15
  passkey :id
10
16
  passthrough :name, :uid
11
17
  passthrough :icon
@@ -15,7 +21,6 @@ module Ecoportal
15
21
  # class_resolver :force_class, 'Ecoportal::API::GraphQL::Base::Force'
16
22
  # class_resolver :task_class, 'Ecoportal::API::GraphQL::Interface::Task'
17
23
  # class_resolver :theme_class, 'Ecoportal::API::GraphQL::Base::Theme'
18
- # class_resolver :template_class, 'Ecoportal::API::GraphQL::Base::Template'
19
24
  # class_resolver :register_class, 'Ecoportal::API::GraphQL::Base::Register'
20
25
  class_resolver :organization_class, 'Ecoportal::API::GraphQL::Base::Organization'
21
26
 
@@ -32,6 +37,20 @@ module Ecoportal
32
37
  Array(doc['locations']).map { |l| l.is_a?(Hash) ? l['id'] : l }.compact
33
38
  end
34
39
 
40
+ # Compiled tag set (client-side build of the backend-only "compiled_tags").
41
+ # This base implementation covers a non-phased page (and is the fallback for a
42
+ # phased page with no stages): there are no per-stage tags, so the set is just the
43
+ # page-level components, de-duplicated:
44
+ #
45
+ # all_tags = Unique(baseTags + otherTags + location_ids)
46
+ #
47
+ # Model::Page::Phased overrides this to take the union across its stages' #all_tags.
48
+ # Returns a de-duplicated Array of tag strings.
49
+ def all_tags
50
+ (Array(baseTags) + Array(otherTags) + Array(location_ids)).
51
+ compact.uniq
52
+ end
53
+
35
54
  def location_ids=(ids)
36
55
  doc['locations'] = Array(ids).map { |id_val| { 'id' => id_val } }
37
56
  end
@@ -41,7 +60,6 @@ module Ecoportal
41
60
  # embeds_one :taskPriority, klass: 'BasePage::TaskPriorityEnum', read_only: true
42
61
  passthrough :taskPriority
43
62
 
44
-
45
63
  passthrough :patchVer
46
64
 
47
65
  passboolean :draft, :archived
@@ -78,7 +96,36 @@ module Ecoportal
78
96
  # the CreateFromTemplateInput preview. nil for fetched/persisted pages.
79
97
  attr_accessor :_compat_source_template_id
80
98
 
81
- # embeds_one :template, klass: :template_class, read_only: true
99
+ # The narrow `Template` type (NOT the template page — see Base::Template for the
100
+ # asymmetry). Populated only for template pages; nil for records. Carries
101
+ # `templateContainerUid`, the cross-version series key.
102
+ class_resolver :template_class, 'Ecoportal::API::GraphQL::Model::Template::Information'
103
+
104
+ # Deliberately NOT `embeds_one ..., read_only: true`, and this is not a style choice:
105
+ # `read_only` does not keep a mutated child out of the parent's diff. With `embeds_one`,
106
+ # `page.template.templateContainerUid = 'x'` yields
107
+ # `as_update => { id:, template: { templateContainerUid: 'x' } }` — but `UpdatePageInput`
108
+ # has NO `template` member, so that would push a key `updatePage` cannot accept. The
109
+ # only writer for the series key is the `editTemplateContainerUid` workflow command.
110
+ #
111
+ # So this follows the same on-demand read-only pattern as `#forces` above, over a COPY of
112
+ # the sub-doc: the returned model is a genuine dead end — writes to it cannot reach
113
+ # `doc`, cannot dirty the page, and cannot leak into an update.
114
+ def template
115
+ return nil unless (raw = doc['template'])
116
+
117
+ @template ||= template_class.new(
118
+ Ecoportal::API::Common::GraphQL::HashHelpers.deep_dup(raw)
119
+ )
120
+ end
121
+
122
+ # Convenience reader for the cross-version series key — nil unless this page is a
123
+ # template AND the read selected `template { templateContainerUid }` (CommonPageUnion
124
+ # does). Many versions may share one uid; only one may be published at a time.
125
+ def templateContainerUid
126
+ template&.templateContainerUid
127
+ end
128
+
82
129
  # embeds_many :registers, klass: :register_class, read_only: true
83
130
  # embeds_one :organization, klass: :organization_class, read_only: true
84
131
 
@@ -17,7 +17,8 @@ module Ecoportal
17
17
 
18
18
  def set_values(**kargs)
19
19
  kargs.each do |key, value|
20
- send(key, value) if respond_to?(key, true)
20
+ writer = "#{key}="
21
+ send(writer, value) if respond_to?(writer, true)
21
22
  end
22
23
 
23
24
  self
@@ -140,14 +140,81 @@ module Ecoportal
140
140
  raw_response[:data] = response
141
141
  end
142
142
  end
143
- rescue *DETAIL_ERRORS => _err
144
- pp kargs
145
- puts "Internal Error with these params:\n"
146
- pp query_params
147
- pp block
143
+ rescue *DETAIL_ERRORS => err
144
+ report_query_failure(err, query_params)
148
145
  raise
149
146
  end
150
147
 
148
+ # Reports WHERE a query failed, on stderr, and re-raises untouched.
149
+ #
150
+ # ★ This replaces an unconditional `pp kargs` / `pp query_params` / `pp block`
151
+ # to STDOUT. That dump was NOT debug litter — it existed because graphlient
152
+ # masks the origin of a failure: `Client#parse`/`#execute` do
153
+ # `raise Graphlient::Errors::ClientError, e.message`, which leaves graphlient's
154
+ # own `inner_exception` nil AND replaces the backtrace, discarding the source
155
+ # location `graphql-client` deliberately sets via
156
+ # `set_backtrace(["#{filename}:#{line}"])`. With that gone there was no other
157
+ # way to see which query blew up. (Tracked upstream; see
158
+ # the repo's internal docs.)
159
+ #
160
+ # But the location was never actually destroyed. Ruby populates `.cause`
161
+ # automatically when you raise inside a rescue, so the original error — with the
162
+ # location intact — is one hop away. Verified by execution:
163
+ #
164
+ # err.inner_exception => nil (graphlient's api, broken)
165
+ # err.backtrace.first => ".../client.rb:20" (the raise site, useless)
166
+ # err.cause.backtrace.first => "app/queries/x.rb:198" (the QUERY site)
167
+ #
168
+ # So this reports MORE of what the dump was for (the failing query's own source
169
+ # location) and LESS of what it should never have emitted: `query_params` holds
170
+ # GraphQL variables — page ids, emails, field values — and printing them to
171
+ # stdout put customer data into CI logs. Variables are now shown only when the
172
+ # client's existing `deep_logging` flag is on (see `dump_variables?` for why NOT
173
+ # the logger level). `pp block` is dropped outright: inspecting a Proc never
174
+ # identified anything.
175
+ #
176
+ # Emitted with `warn` (stderr, and suppressible via the Warning module) rather than
177
+ # `puts`, so tools that pipe query results keep a clean stdout. Tracked as Q-43.
178
+ def report_query_failure(err, query_params = nil)
179
+ origin = err.cause
180
+ lines = ["[#{self.class.name || self.class}] #{err.class}: #{failure_message(err)}"]
181
+ lines << " origin: #{origin.class}" if origin
182
+ lines << " at: #{origin.backtrace.first}" if origin&.backtrace&.first
183
+ lines << " params: #{query_params.inspect}" if dump_variables?
184
+
185
+ warn(lines.join("\n"))
186
+ end
187
+
188
+ # graphlient's `GraphQLError#to_s` / `ExecutionError#to_s` format from
189
+ # `errors.details` and render an EMPTY string when that is empty — and because
190
+ # they override `to_s`, the fallback text their constructors pass to `super` is
191
+ # unreachable. An exception with a blank message is invisible in a log, so say
192
+ # so rather than printing nothing.
193
+ def failure_message(err)
194
+ msg = err.message.to_s
195
+ return msg unless msg.empty?
196
+
197
+ "(blank — #{err.class} renders its message from errors.details, which was empty)"
198
+ end
199
+
200
+ # Opt-in via the client's EXISTING `deep_logging` flag ("whether or not batch
201
+ # responses should be logged") — the same class of thing as dumping variables,
202
+ # and it defaults to false. No new env var and no new config knob.
203
+ #
204
+ # ⚠ Do NOT gate this on `logger.debug?`, which is the obvious-looking choice and
205
+ # is wrong: `HttpClient`'s default logger is `::Logger.new(IO::NULL)`, and a
206
+ # fresh Logger's level is DEBUG (0) — so `logger.debug?` is `true` out of the
207
+ # box and variables would leak by default, which is the whole thing being fixed.
208
+ # A spec pins this.
209
+ def dump_variables?
210
+ http = client&.http_client
211
+ return false unless http.respond_to?(:deep_logging?, true)
212
+
213
+ !!http.send(:deep_logging?)
214
+ rescue StandardError
215
+ false
216
+ end
217
+
151
218
  def request(*path)
152
219
  response = yield
153
220
  wrap_response(response, path)
@@ -18,6 +18,23 @@ module Ecoportal
18
18
  embeds_many :nodes, klass: :node_class
19
19
  embeds_one :pageInfo, klass: Base::PageInfo
20
20
 
21
+ # Bind a connection subclass to its concrete node type.
22
+ #
23
+ # The base `embeds_many :nodes, klass: :node_class` builds a SINGLE dynamic
24
+ # `CollectionModel` ("nodes::node_class") that every subclass inherits and shares.
25
+ # The v2 collection layer memoizes the resolved class on that shared collection
26
+ # (items_klass.rb:62-63), so the FIRST connection instantiated in a process poisons
27
+ # every other connection's instance `nodes` typing (e.g. a MinimalDashboard connection
28
+ # returning AuthLog nodes). Declaring `embeds_many :nodes` per subclass with the concrete
29
+ # class gives each subclass its OWN collection ("nodes::Model::X") bound to a real Class —
30
+ # no shared symbol, nothing to memoize wrongly.
31
+ #
32
+ # Use `nodes_of Model::X` in a subclass instead of `class_resolver :node_class, Model::X`.
33
+ def self.nodes_of(klass)
34
+ class_resolver :node_class, klass
35
+ embeds_many :nodes, klass: klass
36
+ end
37
+
21
38
  def empty?
22
39
  count < 1
23
40
  end
@@ -5,6 +5,133 @@ module Ecoportal
5
5
  class Input < Logic::BaseModel
6
6
  passthrough :clientMutationId
7
7
 
8
+ # Declarative set-diff macro for id-list fields that the schema exposes as
9
+ # `IdDiffInput { additions: [ID!], removals: [ID!] }` on the *update* mutation.
10
+ #
11
+ # Mixed into any Input class (whether it inherits from {Logic::Input} or from a
12
+ # `Base::` data model) via `extend`. {Logic::Input} extends it for its own subclasses;
13
+ # data-model Input classes call `extend Logic::Input::IdDiffFields` explicitly.
14
+ #
15
+ # Usage:
16
+ # class Update < Base::ContractorEntity
17
+ # extend Logic::Input::IdDiffFields
18
+ # id_diff_fields :associatedPeopleIds, :leadContractorIds
19
+ # end
20
+ #
21
+ # It installs (or augments) a `from_model` that, for each declared field, computes the
22
+ # delta DIRECTLY from the READ model's `doc[key]` vs `original_doc[key]` arrays:
23
+ # additions = current - original
24
+ # removals = original - current
25
+ # A field is omitted when unchanged. The delta is computed directly (NOT via the
26
+ # cascaded DiffService) because these array fields are dropped from the flat diff and
27
+ # the read model is `root!` (so its doc/original_doc resolve without cascade side effects).
28
+ module IdDiffFields
29
+ # @return [Array<Symbol>] the fields declared for set-diff on this class.
30
+ def id_diff_fields_list
31
+ @id_diff_fields_list ||= []
32
+ end
33
+
34
+ # Declares the id-list fields that must be reshaped into IdDiff `{ additions:, removals: }`.
35
+ # @param fields [Array<Symbol>] field names (as they appear in the read model's doc).
36
+ # @return [void]
37
+ def id_diff_fields(*fields)
38
+ id_diff_fields_list.concat(fields.flatten.map(&:to_sym)).uniq!
39
+ end
40
+
41
+ # Computes the IdDiff-shaped deltas for the declared fields and merges them into +diff+.
42
+ # @param model [Common::GraphQL::Model] the subject (read) model.
43
+ # @param diff [Hash, nil] the base mutation input hash to augment (may be nil).
44
+ # @return [Hash, nil] +diff+ with per-field `{ additions:, removals: }` merged in,
45
+ # or the original value when nothing changed.
46
+ def merge_id_diffs(model, diff)
47
+ id_diff_fields_list.each do |field|
48
+ key = field.to_s
49
+ current = Array(model.doc[key])
50
+ original = Array(model.original_doc[key])
51
+ next if current == original
52
+
53
+ diff ||= {}
54
+ diff[:id] ||= model.id if model.respond_to?(:id)
55
+ diff[field] = { additions: current - original, removals: original - current }
56
+ end
57
+ diff
58
+ end
59
+
60
+ # Reshaping `from_model`: base diff (via {Logic::Input.from_model}) + set-diff deltas.
61
+ # @param model [Common::GraphQL::Model] the subject model.
62
+ # @return [Hash, nil] the mutation input hash, or nil when there are no changes.
63
+ def from_model(model, **kargs)
64
+ diff = Logic::Input.from_model(model, **kargs)
65
+ merge_id_diffs(model, diff)
66
+ end
67
+ end
68
+
69
+ # Declarative FULL-ARRAY macro for id-list fields that the schema exposes as a plain
70
+ # `[ID!]` on the *update* mutation (NOT `IdDiffInput`). The server expects the COMPLETE
71
+ # current array, replacing the stored value wholesale — there is no additions/removals
72
+ # delta shape. This is the counterpart to {IdDiffFields}.
73
+ #
74
+ # Mixed into any Input class via `extend`, exactly like {IdDiffFields}. Data-model Input
75
+ # classes call `extend Logic::Input::FullArrayFields` explicitly.
76
+ #
77
+ # Usage:
78
+ # class Update < Base::Action
79
+ # extend Logic::Input::FullArrayFields
80
+ # full_array_fields :assignedPersonMemberIds, :locationIds, :fileContainerIds
81
+ # end
82
+ #
83
+ # It installs (or augments) a `from_model` that, for each declared field, emits the FULL
84
+ # current array taken DIRECTLY from the READ model's `doc[key]` whenever it differs from
85
+ # `original_doc[key]`. A field is omitted when unchanged. The value is read directly (NOT
86
+ # via the cascaded DiffService) because these array fields are dropped from the flat diff
87
+ # and the read model is `root!`.
88
+ #
89
+ # Schema evidence (live introspection 20260605, UpdateActionInput):
90
+ # assignedPersonMemberIds : [ID!]
91
+ # locationIds : [ID!]
92
+ # fileContainerIds : [ID!]
93
+ # None are `IdDiffInput` — hence full-array, not delta.
94
+ module FullArrayFields
95
+ # @return [Array<Symbol>] the fields declared for full-array emission on this class.
96
+ def full_array_fields_list
97
+ @full_array_fields_list ||= []
98
+ end
99
+
100
+ # Declares the id-list fields that must be emitted as their COMPLETE current array.
101
+ # @param fields [Array<Symbol>] field names (as they appear in the read model's doc).
102
+ # @return [void]
103
+ def full_array_fields(*fields)
104
+ full_array_fields_list.concat(fields.flatten.map(&:to_sym)).uniq!
105
+ end
106
+
107
+ # Emits the full current array for each changed declared field and merges it into +diff+.
108
+ # @param model [Common::GraphQL::Model] the subject (read) model.
109
+ # @param diff [Hash, nil] the base mutation input hash to augment (may be nil).
110
+ # @return [Hash, nil] +diff+ with per-field full arrays merged in, or the original
111
+ # value when nothing changed.
112
+ def merge_full_arrays(model, diff)
113
+ full_array_fields_list.each do |field|
114
+ key = field.to_s
115
+ current = Array(model.doc[key])
116
+ original = Array(model.original_doc[key])
117
+ next if current == original
118
+
119
+ diff ||= {}
120
+ diff[:id] ||= model.id if model.respond_to?(:id)
121
+ diff[field] = current
122
+ end
123
+ diff
124
+ end
125
+
126
+ # Reshaping `from_model`: base diff (via {Logic::Input.from_model}) + full arrays.
127
+ # @param model [Common::GraphQL::Model] the subject model.
128
+ # @return [Hash, nil] the mutation input hash, or nil when there are no changes.
129
+ def from_model(model, **kargs)
130
+ diff = Logic::Input.from_model(model, **kargs)
131
+ merge_full_arrays(model, diff)
132
+ end
133
+ end
134
+
8
135
  class << self
9
136
  # Builds a mutation input hash from a model's diff.
10
137
  # Subclasses override this to reshape fields (e.g. array → IdDiffInput).
@@ -0,0 +1,14 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Model
5
+ class ActionEvent < Base::ActionEvent
6
+ # assignedPersonMembers: [PreviewPersonMember!]! — Model::PersonMember's
7
+ # read fields are a compatible superset.
8
+ embeds_many :assignedPersonMembers, klass: Model::PersonMember
9
+ embeds_many :pages, klass: "Ecoportal::API::GraphQL::Model::ActionEventPage"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Model
5
+ class ActionEventPage < Base::ActionEventPage
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Ecoportal
2
+ module API
3
+ class GraphQL
4
+ module Model
5
+ # Concrete read model for a page activity/history entry. Adds the
6
+ # relational embeds on top of the common interface fields in Base::Activity.
7
+ class Activity < Base::Activity
8
+ embeds_one :personMember, klass: Model::PersonMember, nullable: true
9
+ embeds_one :user, klass: Model::User, nullable: true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end