ecoportal-api-graphql 1.3.13 → 1.3.16
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -0
- data/lib/ecoportal/api/common/graphql/client.rb +0 -2
- data/lib/ecoportal/api/common/graphql/http_client.rb +6 -0
- data/lib/ecoportal/api/graphql/base/ai_summary_version.rb +17 -17
- data/lib/ecoportal/api/graphql/base/file_container.rb +14 -14
- data/lib/ecoportal/api/graphql/base/location_classification_type.rb +15 -15
- data/lib/ecoportal/api/graphql/base/location_structure.rb +27 -27
- data/lib/ecoportal/api/graphql/base/organization.rb +15 -15
- data/lib/ecoportal/api/graphql/base/page/task.rb +24 -0
- data/lib/ecoportal/api/graphql/base/page.rb +17 -16
- data/lib/ecoportal/api/graphql/base/template.rb +35 -0
- data/lib/ecoportal/api/graphql/base.rb +37 -35
- data/lib/ecoportal/api/graphql/builder/contractor_entity.rb +41 -41
- data/lib/ecoportal/api/graphql/compat/response.rb +35 -35
- data/lib/ecoportal/api/graphql/connection/action.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/action_category.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/contractor_entity.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/person_member.rb +11 -11
- data/lib/ecoportal/api/graphql/connection/preview_page.rb +11 -11
- data/lib/ecoportal/api/graphql/connection.rb +17 -17
- data/lib/ecoportal/api/graphql/file_upload/client.rb +21 -10
- data/lib/ecoportal/api/graphql/fragment/field_configuration.rb +38 -0
- data/lib/ecoportal/api/graphql/fragment/force.rb +14 -10
- data/lib/ecoportal/api/graphql/fragment/location_node.rb +26 -26
- data/lib/ecoportal/api/graphql/fragment/page_task.rb +39 -0
- data/lib/ecoportal/api/graphql/fragment/template.rb +51 -0
- data/lib/ecoportal/api/graphql/fragment.rb +3 -0
- data/lib/ecoportal/api/graphql/input/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/input/preset_view/update.rb +16 -16
- data/lib/ecoportal/api/graphql/input/workflow_command/add_stage.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_field_configuration.rb +21 -21
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page.rb +28 -28
- data/lib/ecoportal/api/graphql/input/workflow_command/edit_page_creator_permissions.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/cross_reference.rb +23 -23
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/date.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/image_gallery.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/people.rb +24 -24
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/plain_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/rich_text.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/field_config/select.rb +20 -20
- data/lib/ecoportal/api/graphql/input/workflow_command/move_field.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_forces.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command/reorder_section.rb +18 -18
- data/lib/ecoportal/api/graphql/input/workflow_command.rb +251 -251
- data/lib/ecoportal/api/graphql/input.rb +24 -24
- data/lib/ecoportal/api/graphql/logic/connection.rb +34 -34
- data/lib/ecoportal/api/graphql/model/page/task.rb +16 -0
- data/lib/ecoportal/api/graphql/model/page.rb +16 -15
- data/lib/ecoportal/api/graphql/model/person_member.rb +15 -15
- data/lib/ecoportal/api/graphql/model/template.rb +15 -0
- data/lib/ecoportal/api/graphql/model.rb +32 -30
- data/lib/ecoportal/api/graphql/mutation/ai_summary/generate.rb +45 -45
- data/lib/ecoportal/api/graphql/mutation/ai_summary/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/mutation/page/approve_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/batch_update_review_task.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/create_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/delete_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/execute_force_commands.rb +7 -7
- data/lib/ecoportal/api/graphql/mutation/page/execute_workflow_commands.rb +8 -7
- data/lib/ecoportal/api/graphql/mutation/page/publish_draft.rb +38 -40
- data/lib/ecoportal/api/graphql/mutation/page/reject_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/restart_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/page/undo_review_task.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/preset_view/destroy.rb +34 -35
- data/lib/ecoportal/api/graphql/mutation/register/destroy.rb +35 -35
- data/lib/ecoportal/api/graphql/mutation/smart_fill/generate.rb +36 -36
- data/lib/ecoportal/api/graphql/mutation/smart_fill/submit_feedback.rb +40 -40
- data/lib/ecoportal/api/graphql/mutation/smart_fill.rb +13 -13
- data/lib/ecoportal/api/graphql/mutation/template/create_related_page.rb +46 -46
- data/lib/ecoportal/api/graphql/mutation/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/mutation.rb +20 -20
- data/lib/ecoportal/api/graphql/payload/contractor_entity.rb +14 -14
- data/lib/ecoportal/api/graphql/payload/ok_payload.rb +36 -21
- data/lib/ecoportal/api/graphql/payload/page/draft.rb +26 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task.rb +13 -13
- data/lib/ecoportal/api/graphql/payload/page/review_task_batch.rb +23 -0
- data/lib/ecoportal/api/graphql/payload/page.rb +20 -19
- data/lib/ecoportal/api/graphql/payload/preset_view.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/register.rb +15 -11
- data/lib/ecoportal/api/graphql/payload/template/create_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/destroy_related_page.rb +1 -1
- data/lib/ecoportal/api/graphql/payload/template/update_information.rb +1 -1
- data/lib/ecoportal/api/graphql/payload.rb +23 -23
- data/lib/ecoportal/api/graphql/query/location_structures.rb +57 -57
- data/lib/ecoportal/api/graphql/query/page.rb +45 -45
- data/lib/ecoportal/api/graphql/query/page_with_forces.rb +9 -3
- data/lib/ecoportal/api/graphql/query/pages_workflow_commands.rb +9 -3
- data/lib/ecoportal/api/graphql/query/register_preset_views.rb +78 -78
- data/lib/ecoportal/api/graphql/query.rb +27 -27
- data/lib/ecoportal/api/graphql.rb +9 -5
- data/lib/ecoportal/api/graphql_version.rb +1 -1
- metadata +9 -180
- data/.ai-assistance/bridge/CLAUDE.md +0 -338
- data/.ai-assistance/bridge/archive/.gitkeep +0 -0
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.inbox.md +0 -29
- data/.ai-assistance/bridge/archive/oscar-a1b2c3d-gitlab-mcp-doc-update.outbox.md +0 -18
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.inbox.md +0 -42
- data/.ai-assistance/bridge/archive/oscar-c912c25-gemini-design-review.outbox.md +0 -115
- data/.ai-assistance/bridge/context/gemini-review-prompt.txt +0 -48
- data/.ai-assistance/bridge/context/gemini-review-response.md +0 -104
- data/.ai-assistance/bridge/context/project.md +0 -42
- data/.ai-assistance/bridge/inbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/.gitkeep +0 -0
- data/.ai-assistance/bridge/outbox/request-for-standards-discovery.md +0 -48
- data/.ai-assistance/bridge/queue/.gitkeep +0 -1
- data/.ai-assistance/capabilities/CLAUDE.md +0 -27
- data/.ai-assistance/capabilities/assumptions-log.md +0 -80
- data/.ai-assistance/capabilities/code.md +0 -47
- data/.ai-assistance/capabilities/connectors.md +0 -37
- data/.ai-assistance/capabilities/cowork.md +0 -55
- data/.ai-assistance/code/OVERVIEW.md +0 -155
- data/.ai-assistance/code/data_fields.md +0 -242
- data/.ai-assistance/code/dependencies.md +0 -151
- data/.ai-assistance/code/diff_as_input.md +0 -234
- data/.ai-assistance/code/diff_pairing_engine.md +0 -243
- data/.ai-assistance/code/diff_service_deep_dive.md +0 -192
- data/.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md +0 -55
- data/.ai-assistance/code/ecoPortal_architecture/01_terminology_dictionary.md +0 -181
- data/.ai-assistance/code/ecoPortal_architecture/02_data_model.md +0 -192
- data/.ai-assistance/code/ecoPortal_architecture/03_api_layers.md +0 -147
- data/.ai-assistance/code/ecoPortal_architecture/04_graphql_queries_mutations.md +0 -277
- data/.ai-assistance/code/ecoPortal_architecture/05_page_workflows.md +0 -200
- data/.ai-assistance/code/ecoPortal_architecture/06_search_and_filters.md +0 -228
- data/.ai-assistance/code/ecoPortal_architecture/07_data_fields.md +0 -197
- data/.ai-assistance/code/ecoPortal_architecture/08_stages_sections.md +0 -243
- data/.ai-assistance/code/ecoPortal_architecture/09_people_contractors_locations.md +0 -196
- data/.ai-assistance/code/ecoPortal_architecture/10_forces_workflow_builder.md +0 -132
- data/.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md +0 -187
- data/.ai-assistance/code/ecoPortal_architecture/12_ai_documentation_sources_gaps.md +0 -236
- data/.ai-assistance/code/ecoPortal_architecture/13_ai_infrastructure.md +0 -183
- data/.ai-assistance/code/ecoportal_schema_reference.md +0 -240
- data/.ai-assistance/code/filter_contract_matrix.md +0 -177
- data/.ai-assistance/code/graphql_domain_knowledge.md +0 -240
- data/.ai-assistance/code/refactoring/datafield-readwrite-shape-asymmetry.md +0 -71
- data/.ai-assistance/code/refactoring/opportunities.md +0 -251
- data/.ai-assistance/code/schema_analysis.md +0 -321
- data/.ai-assistance/code/search_filters.md +0 -868
- data/.ai-assistance/code/spec_coverage.md +0 -73
- data/.ai-assistance/code/template_diff_pairing_domain.md +0 -175
- data/.ai-assistance/code/workflow-command-guide.md +0 -466
- data/.ai-assistance/code/workflow-space.md +0 -353
- data/.ai-assistance/conventions/CLAUDE.md +0 -30
- data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -199
- data/.ai-assistance/conventions/gitignore-rules.md +0 -42
- data/.ai-assistance/conventions/permission-guidance.md +0 -120
- data/.ai-assistance/integrations/README.md +0 -70
- data/.ai-assistance/integrations/gitkraken-mcp.md +0 -107
- data/.ai-assistance/integrations/gitlab-mcp.md +0 -123
- data/.ai-assistance/integrations/local-git.md +0 -60
- data/.ai-assistance/local_paths.example.md +0 -17
- data/.ai-assistance/projects/TODO.md +0 -118
- data/.ai-assistance/projects/api-v2-to-graphql-migration/DECISIONS.md +0 -168
- data/.ai-assistance/projects/api-v2-to-graphql-migration/INTENT.md +0 -60
- data/.ai-assistance/projects/api-v2-to-graphql-migration/TODO.md +0 -267
- data/.ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md +0 -53
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md +0 -102
- data/.ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md +0 -139
- data/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +0 -244
- data/.ai-assistance/projects/dynamic-model-generation/INTENT.md +0 -93
- data/.ai-assistance/projects/eco-helpers-compat/INTENT.md +0 -244
- data/.ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md +0 -266
- data/.ai-assistance/projects/eco-helpers-compat/TODO.md +0 -86
- data/.ai-assistance/projects/ecoportal-api-v2-doublemodel-review/INTENT.md +0 -101
- data/.ai-assistance/projects/graphql-agent/GAP_ANALYSIS.md +0 -177
- data/.ai-assistance/projects/ooze-graphql-native-migration/DECISIONS.md +0 -161
- data/.ai-assistance/projects/ooze-graphql-native-migration/INTENT.md +0 -125
- data/.ai-assistance/projects/ooze-graphql-native-migration/INVENTORY.md +0 -136
- data/.ai-assistance/projects/ooze-graphql-native-migration/RISKS.md +0 -126
- data/.ai-assistance/projects/ooze-graphql-native-migration/TODO.md +0 -261
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-06-30-cutover-workflow-deep-review.md +0 -122
- data/.ai-assistance/projects/ooze-graphql-native-migration/analysis/2026-07-01-forces-via-workflow-commands-miss-rca.md +0 -148
- data/.ai-assistance/projects/page-model/DECISIONS.md +0 -245
- data/.ai-assistance/projects/page-model/TODO.md +0 -190
- data/.ai-assistance/projects/qa-services-delivery/DECISIONS.md +0 -93
- data/.ai-assistance/projects/qa-services-delivery/INTENT.md +0 -76
- data/.ai-assistance/projects/qa-services-delivery/PHASE3-SCOPE.md +0 -115
- data/.ai-assistance/projects/qa-services-delivery/ROADMAP.md +0 -99
- data/.ai-assistance/projects/qa-services-delivery/TODO.md +0 -81
- data/.ai-assistance/projects/search-filter-builder/INTENT.md +0 -107
- data/.ai-assistance/projects/search-filter-builder/TODO.md +0 -131
- data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +0 -87
- data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +0 -108
- data/.ai-assistance/projects/template-diff-deploy/INTENT.md +0 -12
- data/.ai-assistance/projects/template-diff-deploy/TODO.md +0 -9
- data/.ai-assistance/projects/template-maintenance/DESIGN.md +0 -134
- data/.ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md +0 -93
- data/.ai-assistance/projects/template-maintenance/README.md +0 -14
- data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +0 -236
- data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +0 -243
- data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +0 -154
- data/.ai-assistance/projects/workflow-space/TODO.md +0 -213
- data/.ai-assistance/reinstall-claude-desktop-windows.md +0 -136
- data/.ai-assistance/scripts/CLAUDE.md +0 -150
- data/.ai-assistance/scripts/bridge-inbox-check.sh +0 -75
- data/.ai-assistance/scripts/bridge-init.sh +0 -86
- data/.ai-assistance/scripts/bridge-status.sh +0 -44
- data/.ai-assistance/scripts/capabilities-check.ts +0 -104
- data/.ai-assistance/scripts/check-outbox.sh +0 -43
- data/.ai-assistance/scripts/dep_graph.rb +0 -91
- data/.ai-assistance/scripts/lock-acquire.sh +0 -103
- data/.ai-assistance/scripts/lock-multi.sh +0 -124
- data/.ai-assistance/scripts/lock-queue.sh +0 -94
- data/.ai-assistance/scripts/setup-mcps.test.ts +0 -188
- data/.ai-assistance/scripts/setup-mcps.ts +0 -234
- data/.ai-assistance/scripts/task-complete.ts +0 -74
- data/.ai-assistance/scripts/task-create.ts +0 -75
- data/.ai-assistance/scripts/task-read.ts +0 -125
- data/.ai-assistance/scripts/token-logger.js +0 -220
- data/.ai-assistance/scripts/token-report.ts +0 -158
- data/.ai-assistance/scripts/token-session-start.js +0 -66
- data/.ai-assistance/skills/ai-instructions/SKILL.md +0 -48
- data/.ai-assistance/skills/code-specs/SKILL.md +0 -69
- data/.ai-assistance/skills/corporate-policies/SKILL.md +0 -201
- data/.ai-assistance/skills/dep-graph/SKILL.md +0 -139
- data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
- data/.ai-assistance/skills/gemini-assist/SKILL.md +0 -63
- data/.ai-assistance/skills/gemini-assist/gemini-mcp-server.js +0 -205
- data/.ai-assistance/skills/gemini-assist/gemini_ask.py +0 -1
- data/.ai-assistance/skills/gemini-assist/gemini_ask.rb +0 -240
- data/.ai-assistance/skills/gemini-assist/prompts/cycle_end_review.txt +0 -25
- data/.ai-assistance/skills/graphql-schema-analysis/SKILL.md +0 -261
- data/.ai-assistance/skills/procedural-memory/SKILL.md +0 -319
- data/.ai-assistance/skills/project-cycle/SKILL.md +0 -177
- data/.ai-assistance/skills/project-self-docs/SKILL.md +0 -181
- data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +0 -378
- data/.ai-assistance/skills/refactor/SKILL.md +0 -62
- data/.ai-assistance/skills/rubocop/SKILL.md +0 -93
- data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
- data/.ai-assistance/skills/spec-generation/SKILL.md +0 -72
- data/.ai-assistance/standards-version.json +0 -22
- data/.ai-assistance/token-budget.json +0 -32
- data/.ai-assistance/version.json +0 -39
- data/.claude/settings.json +0 -150
- data/.env.example +0 -18
- data/.gitattributes +0 -15
- data/.gitignore +0 -37
- data/.gitlab-ci.yml +0 -45
- data/.markdownlint.json +0 -4
- data/.rspec +0 -3
- data/.rubocop.yml +0 -121
- data/.ruby-version +0 -1
- data/.yardopts +0 -10
- data/CLAUDE.md +0 -243
- data/Gemfile +0 -30
- data/Rakefile +0 -90
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/setup.ps1 +0 -6
- data/docs/self-docs/ARCHITECTURE.md +0 -88
- data/docs/self-docs/CHANGES.jsonl +0 -12
- data/docs/self-docs/COMPLIANCE.md +0 -79
- data/docs/self-docs/CONVENTIONS.md +0 -74
- data/docs/self-docs/INTEGRATIONS.md +0 -65
- data/docs/self-docs/OPERATIONS.md +0 -76
- data/docs/self-docs/OVERVIEW.md +0 -64
- data/docs/self-docs/STATUS.md +0 -73
- data/docs/self-docs/self-docs-index.json +0 -51
- data/docs/worklog.md +0 -977
- data/ecoportal-api-graphql.gemspec +0 -40
- data/scripts/auto-worker-scheduler.sh +0 -386
- data/tests/actions_get.rb +0 -7
- data/tests/contractor_entities_get.rb +0 -20
- data/tests/contractor_entity_create.rb +0 -19
- data/tests/contractor_entity_udpate.rb +0 -20
- data/tests/dump_page_model.rb +0 -74
- data/tests/dump_template_model.rb +0 -90
- data/tests/loc_structure_get.rb +0 -10
- data/tests/loc_structure_update.rb +0 -51
- data/tests/loc_structures_get.rb +0 -15
- data/tests/local_libs.rb +0 -20
- data/tests/validate_queries.rb +0 -127
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
# EcoPortal Data Fields
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Data fields are the form inputs within a page. Each field is an INSTANCE of a field
|
|
8
|
-
definition from the template. Field instances have:
|
|
9
|
-
- A MongoDB ObjectId (`id`) assigned on page creation — NOT predictable from template
|
|
10
|
-
- The field type (`__typename` in GraphQL)
|
|
11
|
-
- Label (the question/field name shown to users)
|
|
12
|
-
- Value(s) — the user's response
|
|
13
|
-
- `ref` — the ES index key (Field ID / System ID future)
|
|
14
|
-
- `deindex: Boolean` — if true, field is excluded from Elasticsearch index
|
|
15
|
-
- Linked field configuration (cross-reference from another register)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## All Field Types (`DataFieldUnion`)
|
|
20
|
-
|
|
21
|
-
The GraphQL `DataFieldUnion` has 20+ concrete types:
|
|
22
|
-
|
|
23
|
-
| GraphQL type | eco-helpers type | Description |
|
|
24
|
-
|---|---|---|
|
|
25
|
-
| `PlainText` | `:plain_text` | Single or multi-line text |
|
|
26
|
-
| `RichText` | `:rich_text` | HTML rich text editor |
|
|
27
|
-
| `Date` | `:date` | Date/datetime picker |
|
|
28
|
-
| `Number` | `:number` | Numeric input |
|
|
29
|
-
| `Gauge` | `:gauge` | Numeric with min/max (risk matrix output) |
|
|
30
|
-
| `Select` | `:select` | Single or multi-select dropdown |
|
|
31
|
-
| `Checklist` | `:checklist` | Checkbox list (each item: label + checked) |
|
|
32
|
-
| `Signature` | `:signature` | Digital signature capture |
|
|
33
|
-
| `TagField` | `:tag_field` | Location tag selector |
|
|
34
|
-
| `People` | `:people` | Person selector (linked to PersonMember) |
|
|
35
|
-
| `ContractorEntities` | `:contractor_entities` | Contractor selector |
|
|
36
|
-
| `CrossReference` | `:cross_reference` | Reference to pages in another register |
|
|
37
|
-
| `Geo` | `:geo` | Geographic coordinates + address |
|
|
38
|
-
| `ImageGallery` | `:image_gallery` | Image upload gallery |
|
|
39
|
-
| `File` | `:file` | File attachment |
|
|
40
|
-
| `Mailbox` | `:mailbox` | Email inbox linked to the field |
|
|
41
|
-
| `ActionsList` | `:actions_list` | Embedded actions list |
|
|
42
|
-
| `Law` | `:law` | Legal obligation snippets |
|
|
43
|
-
| `AiSummary` | `:ai_summary` | AI-generated summary field |
|
|
44
|
-
| `EmbeddedStructure` | `:embedded_structure` | Embedded reporting structure |
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## DataFieldInput Structure
|
|
49
|
-
|
|
50
|
-
When updating or adding field values, each field uses a typed input within `DataFieldInput`:
|
|
51
|
-
|
|
52
|
-
```json
|
|
53
|
-
{
|
|
54
|
-
"plainText": { "id": "FIELD_ID", "value": "Job description text" },
|
|
55
|
-
"richText": { "id": "FIELD_ID", "content": "<p>HTML content</p>" },
|
|
56
|
-
"date": { "id": "FIELD_ID", "value": "2024-11-25T08:30:00+13:00" },
|
|
57
|
-
"number": { "id": "FIELD_ID", "value": 42.5 },
|
|
58
|
-
"gauge": { "id": "FIELD_ID", "value": 14.0 },
|
|
59
|
-
"select": { "id": "FIELD_ID", "options": [{ "id": "OPT_ID", "selected": true }] },
|
|
60
|
-
"people": { "id": "FIELD_ID", "peopleIds": ["PERSON_ID_1", "PERSON_ID_2"] },
|
|
61
|
-
"geo": { "id": "FIELD_ID", "address": "...", "coordinates": { "lat": -36.86, "lon": 174.76 } },
|
|
62
|
-
"checklist": { "id": "FIELD_ID", "items": [{ "id": "ITEM_ID", "checked": true }] },
|
|
63
|
-
"locationField": { "id": "FIELD_ID", "locationIds": ["LOC_ID"] }
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Only ONE field type key is used per `DataFieldInput` object.
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## DataFieldOneToManyInput
|
|
72
|
-
|
|
73
|
-
Used in both `createPageFromTemplate` and `updatePage`:
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"additions": [<DataFieldInput>, ...],
|
|
78
|
-
"updates": [<DataFieldInput>, ...],
|
|
79
|
-
"deletions": ["FIELD_ID_TO_DELETE", ...]
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
- **`updates`** — change values of existing fields (require their current `id`)
|
|
84
|
-
- **`additions`** — add new field data (for fields that exist in template but have no instance data yet)
|
|
85
|
-
- **`deletions`** — remove field instances
|
|
86
|
-
|
|
87
|
-
**For `createFromTemplate`:** Use `updates` — the `buildFromTemplate` response gives
|
|
88
|
-
instance field IDs, and `updates` sets their values on creation.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Field Access in the Gem
|
|
93
|
-
|
|
94
|
-
### Getting fields from a page
|
|
95
|
-
|
|
96
|
-
```ruby
|
|
97
|
-
page = graphql.pages.get(page_id) # fetches CommonPageUnion with $fields: true
|
|
98
|
-
|
|
99
|
-
# As DataField::Collection
|
|
100
|
-
fields = page.components # or page.field_collection
|
|
101
|
-
|
|
102
|
-
# Filter by type
|
|
103
|
-
plain_texts = fields.get_by_type(:plain_text)
|
|
104
|
-
selects = fields.get_by_type('Select')
|
|
105
|
-
|
|
106
|
-
# Find by label
|
|
107
|
-
field = fields.get_by_name('Summary')
|
|
108
|
-
|
|
109
|
-
# Access raw docs (v2 compat)
|
|
110
|
-
fields.doc # → [raw_hash, ...]
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Modifying fields
|
|
114
|
-
|
|
115
|
-
```ruby
|
|
116
|
-
# PlainText
|
|
117
|
-
field = page.components.get_by_name('Summary')
|
|
118
|
-
field.value = 'New summary text'
|
|
119
|
-
|
|
120
|
-
# Select — by option name, value, or id
|
|
121
|
-
field = page.components.get_by_name('Status')
|
|
122
|
-
field.select_option('Active') # deselects all others
|
|
123
|
-
|
|
124
|
-
# People
|
|
125
|
-
field = page.components.get_by_name('Assigned To')
|
|
126
|
-
field.people_ids = ['person_id_1', 'person_id_2']
|
|
127
|
-
|
|
128
|
-
# Date (ISO8601 string)
|
|
129
|
-
field = page.components.get_by_name('Due Date')
|
|
130
|
-
field.value = '2025-06-30T09:00:00+12:00'
|
|
131
|
-
|
|
132
|
-
# Checklist
|
|
133
|
-
field = page.components.get_by_name('Checklist')
|
|
134
|
-
field.check('Step A')
|
|
135
|
-
field.check('Step B', checked: false)
|
|
136
|
-
|
|
137
|
-
# Rich text (HTML)
|
|
138
|
-
field = page.components.get_by_name('Description')
|
|
139
|
-
field.content = '<p>Updated description</p>'
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Adding a new field (additions)
|
|
143
|
-
|
|
144
|
-
```ruby
|
|
145
|
-
# When a field exists in the template but has no data in this instance:
|
|
146
|
-
draft = graphql.pages.get_new(template_id)
|
|
147
|
-
draft.components.add(id: 'f1_from_draft', type: 'PlainText') { |f| f.value = 'X' }
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Stage-Level Field Access
|
|
153
|
-
|
|
154
|
-
For PhasedPage, fields are within stages → sections → fields:
|
|
155
|
-
|
|
156
|
-
```ruby
|
|
157
|
-
# Get stage by name
|
|
158
|
-
stage = page.stages['Risk Assessment']
|
|
159
|
-
|
|
160
|
-
# Stage sections
|
|
161
|
-
stage.sections.each do |section|
|
|
162
|
-
puts "Section: #{section.heading}"
|
|
163
|
-
section.components.each { |f| puts " Field: #{f.label}" }
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# All fields in stage (flat)
|
|
167
|
-
stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## Field Visibility: deindex and Hidden
|
|
173
|
-
|
|
174
|
-
- `deindex: Boolean` — if true, field value is NOT sent to Elasticsearch. Used for
|
|
175
|
-
sensitive data. Affects: search, filters, analytics, exports.
|
|
176
|
-
- Fields can be hidden (template configuration). `showHiddenData: true` in queries
|
|
177
|
-
includes hidden fields in the response.
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## Field Reference (ref) — ES Index Key
|
|
182
|
-
|
|
183
|
-
The `ref` property identifies a field in the search index (ES). It is:
|
|
184
|
-
- Composed of: `<type_shorthand>.<hash_of_label>`
|
|
185
|
-
- Used in: sorters (`key:` field), filters, chart configurations, linked field config
|
|
186
|
-
- Different from the MongoDB instance `id`
|
|
187
|
-
- Breaks if the field label changes (hash of label changes)
|
|
188
|
-
|
|
189
|
-
**Formula:** `Ecoportal::API::Common::Content::StringDigest` in `ecoportal-api-v2`
|
|
190
|
-
|
|
191
|
-
**Examples:**
|
|
192
|
-
- Plain text field "Job Number": `plain_text.job_nu` (approximate hash)
|
|
193
|
-
- Date field "Due Date": `date.due_da`
|
|
194
|
-
|
|
195
|
-
**Known issue:** If a field was added via a tech script without copying from the template,
|
|
196
|
-
its `ref` may be unique per instance rather than consistent across all instances of the
|
|
197
|
-
same template. This causes analytics issues (missing data, duplicate columns in exports).
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
# EcoPortal Stages and Sections
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Stage Lifecycle
|
|
6
|
-
|
|
7
|
-
### Stage States (`StageStateEnum`)
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
pending → inprogress → complete
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
- **`pending`** — Stage not yet started. No fill-in task exists yet.
|
|
14
|
-
- **`inprogress`** — Stage is active. A fill-in task (`CompletePage`) exists and is
|
|
15
|
-
assigned to someone (per workflow rules). The assignee can enter data.
|
|
16
|
-
- **`complete`** — All required tasks are done. Stage is locked.
|
|
17
|
-
|
|
18
|
-
**Transition triggers:**
|
|
19
|
-
- `pending → inprogress`: Previous stage completes (its task is done/signed off)
|
|
20
|
-
- `inprogress → complete`: Fill-in task completed, OR review task signed off (if review configured)
|
|
21
|
-
|
|
22
|
-
### Active vs Started
|
|
23
|
-
|
|
24
|
-
- `active: Boolean` — Stage is currently active for input (the "current" stage)
|
|
25
|
-
- `started: Boolean` — Stage has had at least some input (includes all past stages)
|
|
26
|
-
|
|
27
|
-
For a PhasedPage: typically ONE stage is `active` at a time. Previous stages are
|
|
28
|
-
`started` but not `active`. Future stages are neither.
|
|
29
|
-
|
|
30
|
-
### Stage-Specific Submit
|
|
31
|
-
|
|
32
|
-
When submitting a stage via GraphQL, ALWAYS include `stageId`:
|
|
33
|
-
|
|
34
|
-
```graphql
|
|
35
|
-
mutation SubmitStage($input: UpdatePageInput!) {
|
|
36
|
-
updatePage(input: $input) {
|
|
37
|
-
item { ...PhasedPageFields }
|
|
38
|
-
errors { details fullMessages }
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```json
|
|
44
|
-
{
|
|
45
|
-
"input": {
|
|
46
|
-
"id": "PAGE_ID",
|
|
47
|
-
"patchVer": 5,
|
|
48
|
-
"stageId": "STAGE_ID",
|
|
49
|
-
"submit": true,
|
|
50
|
-
"dataFields": {
|
|
51
|
-
"updates": [
|
|
52
|
-
{ "plainText": { "id": "FIELD_ID", "value": "Final value" } }
|
|
53
|
-
]
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Without `stageId`, the submit may not correctly associate with the right stage and
|
|
60
|
-
may not trigger the expected task assignments in the next stage.
|
|
61
|
-
|
|
62
|
-
**Why a stage is mandatory (verified — Oscar, 2026-06-30):** stage tasks, and the
|
|
63
|
-
permissions granted through them, are **stage-scoped**. A people field can grant the user
|
|
64
|
-
attached to it permission to *only the stage where they were attached* — so a save / submit /
|
|
65
|
-
sign-off MUST be executed against that specific stage as the target. The web/mobile front-ends
|
|
66
|
-
always operate within a stage for this reason. Inferring a "current/active" stage from the
|
|
67
|
-
page is therefore not just unreliable, it is semantically wrong: on a multi-stage page it may
|
|
68
|
-
name a different stage than the one the work was done on. The submit must carry the stage the
|
|
69
|
-
caller actually worked on. ⇒ **submit / sign_off without a target stage is invalid.**
|
|
70
|
-
|
|
71
|
-
### Gem enforcement & stageId resolution (compat layer)
|
|
72
|
-
|
|
73
|
-
The gem treats "a submit must name its stage" as a hard invariant:
|
|
74
|
-
|
|
75
|
-
- `Input::Page::Update.from_model` **raises** (`ArgumentError`) when `submit: true` or a stage
|
|
76
|
-
`task` is requested with no `stage_id`. This replaced a silent failure: the operation hash's
|
|
77
|
-
nil-guard used to *drop* a nil `stageId`, so a stage-less submit went out unnoticed (the
|
|
78
|
-
act-gov TOOCS bug, 2026-06-30). A plain field update / publish needs no stage and is exempt.
|
|
79
|
-
- `Compat::Pages#compat_stage_id` resolves the target most-explicit-first:
|
|
80
|
-
1. **pinned** — `submit!(stage_id:)` / `sign_off!(stage_id:)` from the script;
|
|
81
|
-
2. **active** — `page._compat_active_stage_id`, recorded when a `Compat::StageView` is taken
|
|
82
|
-
over the page (`pages.get(id, stage_id:)` or eco-helpers `OozeRedirect#stage`). This is the
|
|
83
|
-
reliable source on the OozeSamples path, where the base loop enqueues and updates the FULL
|
|
84
|
-
page (first-wins by id — the StageView is never the queued object), so the chosen stage must
|
|
85
|
-
live on the page itself;
|
|
86
|
-
3. **current** — `Phased#current_stage_id` server-state inference (`state=='inprogress'` /
|
|
87
|
-
`active==true`). Last resort only, and guarded: if it yields nil the `from_model` guard fires.
|
|
88
|
-
|
|
89
|
-
So the canonical way to submit a specific stage from a script is to work *through a stage view*
|
|
90
|
-
(`with_stage` → `stage()` → `StageView`), which records the stage; the later `target.submit!`
|
|
91
|
-
then resolves to it automatically.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Stage Permissions
|
|
96
|
-
|
|
97
|
-
Each stage can have restricted access:
|
|
98
|
-
- **User group restrictions** — only members of specified groups can edit the stage
|
|
99
|
-
- **People field access** — if a stage has a people field configured to "grant stage access",
|
|
100
|
-
the people assigned to that field get editing access to that stage only
|
|
101
|
-
|
|
102
|
-
**Key insight:** The configuration enum on a people field determines access scope:
|
|
103
|
-
- `stage_only` — grant access only to the stage where the people field is embedded
|
|
104
|
-
- `page_wide` — grant access to the entire page
|
|
105
|
-
|
|
106
|
-
**GraphQL behaviour:** If a user doesn't have access to a stage, the stage is simply
|
|
107
|
-
ABSENT from the response (`stages`, `stagesIndex`, `activeStages`). No error is thrown.
|
|
108
|
-
This is different from APIv2 which returned explicit access errors per stage.
|
|
109
|
-
|
|
110
|
-
**Scripting implication:** Scripts should check whether an expected stage is present
|
|
111
|
-
in the response rather than catching errors. If a stage is missing, the authenticated
|
|
112
|
-
user may not have access to it.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Section Types (HasSectionsInterface)
|
|
117
|
-
|
|
118
|
-
Both BasicPage (directly) and Stage (within PhasedPage) implement `HasSectionsInterface`.
|
|
119
|
-
|
|
120
|
-
### `ContentSection`
|
|
121
|
-
|
|
122
|
-
Standard section — one column of fields:
|
|
123
|
-
```graphql
|
|
124
|
-
fragment on ContentSection {
|
|
125
|
-
id
|
|
126
|
-
heading
|
|
127
|
-
dataFields { ...dataField }
|
|
128
|
-
}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### `SplitSection`
|
|
132
|
-
|
|
133
|
-
Two-column section (left + right panels):
|
|
134
|
-
```graphql
|
|
135
|
-
fragment on SplitSection {
|
|
136
|
-
id
|
|
137
|
-
heading
|
|
138
|
-
leftDataFields { ...dataField }
|
|
139
|
-
rightDataFields { ...dataField }
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
When accessing a split section's fields, always merge left + right:
|
|
144
|
-
```ruby
|
|
145
|
-
section.components # → DataField::Collection merging left + right
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## Stage-Level Field Access (PhasedPage)
|
|
151
|
-
|
|
152
|
-
In GraphQL, all stage data is returned in one request via `CommonPageUnion`.
|
|
153
|
-
The response has stages → sections → fields.
|
|
154
|
-
|
|
155
|
-
```ruby
|
|
156
|
-
# Gem access pattern
|
|
157
|
-
page = graphql.pages.get(page_id)
|
|
158
|
-
|
|
159
|
-
# All stages
|
|
160
|
-
page.stages # → StageCollection
|
|
161
|
-
page.stages['Risk Assessment'] # → Stage (by name or ID)
|
|
162
|
-
page.stages.ordered # → [Stage, ...] sorted by ordering
|
|
163
|
-
|
|
164
|
-
# Stage contents
|
|
165
|
-
stage = page.stages['Risk Assessment']
|
|
166
|
-
stage.sections # → SectionCollection
|
|
167
|
-
stage.components # → DataField::Collection (all fields in stage)
|
|
168
|
-
stage.sections.get_by_type(:content_section)
|
|
169
|
-
stage.sections.get_by_heading('Details')
|
|
170
|
-
|
|
171
|
-
# Field access in a stage
|
|
172
|
-
stage.components.get_by_name('Hazard Description').value = 'Exposure to...'
|
|
173
|
-
stage.components.get_by_type(:select).find { |f| f.label == 'Risk Level' }.select_option('High')
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## APIv2 vs GraphQL Stage Rendering Comparison
|
|
179
|
-
|
|
180
|
-
| Concern | APIv2 | GraphQL |
|
|
181
|
-
|---------|-------|---------|
|
|
182
|
-
| Request count for PhasedPage | One per stage | One for entire page |
|
|
183
|
-
| Stage data isolation | Strict — each request scoped to one stage | Full page returned, client filters |
|
|
184
|
-
| Stage-specific mutations | Via stage_id on URL | Via `stageId:` in `UpdatePageInput` |
|
|
185
|
-
| Permission handling | Access error per forbidden stage | Forbidden stages absent from response |
|
|
186
|
-
| Template stages | Full render | Full render |
|
|
187
|
-
|
|
188
|
-
**Compat layer approach:**
|
|
189
|
-
`graphql.pages.get(page_id, stage_id: 's1')` returns a `StageView` — a client-side
|
|
190
|
-
wrapper that shows only the specified stage's sections and components, while delegating
|
|
191
|
-
all other page accessors (id, name, patchVer, as_update, etc.) to the full page.
|
|
192
|
-
This matches what v2 scripts expect from a per-stage fetch.
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## StageIndex (Lightweight Stage Summary)
|
|
197
|
-
|
|
198
|
-
For displaying workflow progress without loading full stage content:
|
|
199
|
-
|
|
200
|
-
```graphql
|
|
201
|
-
fragment PhasedPageFields on PhasedPage {
|
|
202
|
-
stagesIndex {
|
|
203
|
-
id
|
|
204
|
-
name
|
|
205
|
-
ordering
|
|
206
|
-
state
|
|
207
|
-
started
|
|
208
|
-
hasCompleteTask
|
|
209
|
-
hasReviewTask
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
`hasCompleteTask` — whether a fill-in task is configured for this stage
|
|
215
|
-
`hasReviewTask` — whether a review/sign-off task is configured for this stage
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## Task Types on Stages
|
|
220
|
-
|
|
221
|
-
| Task type | GraphQL type | Trigger | When complete |
|
|
222
|
-
|-----------|-------------|---------|---------------|
|
|
223
|
-
| Fill-in | `CompletePage` | Stage becomes active | Moves to review (if configured) or completes stage |
|
|
224
|
-
| Review/sign-off | `ReviewPage` | Fill-in task completed | Completes stage → activates next stage |
|
|
225
|
-
|
|
226
|
-
Task fields: `id`, `complete`, `completedAt`, `completedBy`, `due`, `overdue`, `users`
|
|
227
|
-
|
|
228
|
-
**Stage close-out sequence:**
|
|
229
|
-
1. Fill-in task assigned to user(s) (per workflow rules)
|
|
230
|
-
2. User submits: `updatePage(stageId:, submit: true)` triggers `CompletePage` completion
|
|
231
|
-
3. If review configured: `ReviewPage` task created and assigned
|
|
232
|
-
4. Review approved: stage state → `complete`, next stage activated
|
|
233
|
-
5. Next fill-in task created per workflow rules
|
|
234
|
-
|
|
235
|
-
**Scripting a stage submit:**
|
|
236
|
-
```ruby
|
|
237
|
-
input = Input::Page::Update.from_model(page,
|
|
238
|
-
stage_id: 'STAGE_ID',
|
|
239
|
-
submit: true
|
|
240
|
-
)
|
|
241
|
-
# Input will include stageId + submit even if no page/field changes
|
|
242
|
-
Builder::Page.new(client).update(input: input)
|
|
243
|
-
```
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
# EcoPortal People, Contractors, and Locations
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## People (PersonMember)
|
|
6
|
-
|
|
7
|
-
### GraphQL Type
|
|
8
|
-
|
|
9
|
-
`PersonMember` — represents a user in the ecoPortal platform.
|
|
10
|
-
|
|
11
|
-
Key fields: `id`, `name`, `email`, `externalId`, `archived`, `publicFields`, `privateFields`,
|
|
12
|
-
`supervisor_id`, `organizationHierarchy`
|
|
13
|
-
|
|
14
|
-
### Query
|
|
15
|
-
|
|
16
|
-
```graphql
|
|
17
|
-
query GetPeople($filters: [SearchFilter!], $includeArchived: Boolean) {
|
|
18
|
-
currentOrganization {
|
|
19
|
-
personMembers(
|
|
20
|
-
first: 20,
|
|
21
|
-
includeArchived: $includeArchived,
|
|
22
|
-
searchConf: {
|
|
23
|
-
filters: $filters,
|
|
24
|
-
sorters: [{ key: "created_at", direction: "desc" }]
|
|
25
|
-
}
|
|
26
|
-
) {
|
|
27
|
-
totalCount
|
|
28
|
-
pageInfo { hasNextPage endCursor }
|
|
29
|
-
nodes { id name email externalId publicFields privateFields }
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Finding a Person by externalId
|
|
36
|
-
|
|
37
|
-
```json
|
|
38
|
-
{
|
|
39
|
-
"filters": [
|
|
40
|
-
{ "operation": "exact_filter", "params": { "key": "external_id", "value": "FRODO" } }
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Note: The `searchConf` for `personMembers` uses the ARRAY format for `filters` and
|
|
46
|
-
sorters use `key:` not `fieldName:`.
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Contractor Entities
|
|
51
|
-
|
|
52
|
-
### GraphQL Type
|
|
53
|
-
|
|
54
|
-
`ContractorEntity` — represents an external contractor/supplier in the system.
|
|
55
|
-
|
|
56
|
-
Key fields: `id`, `externalId`, `name`, `schemaId`, `active`, `approved`,
|
|
57
|
-
`associatedPeopleIds`, `leadContractorIds`
|
|
58
|
-
|
|
59
|
-
### Query
|
|
60
|
-
|
|
61
|
-
```graphql
|
|
62
|
-
query Contractors($searchConf: Search) {
|
|
63
|
-
currentOrganization {
|
|
64
|
-
contractorEntities(searchConf: $searchConf) {
|
|
65
|
-
totalCount
|
|
66
|
-
nodes { id externalId name active approved associatedPeopleIds leadContractorIds }
|
|
67
|
-
pageInfo { endCursor hasNextPage }
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Find by externalId
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{ "searchConf": { "filters": [{ "operation": "exact_filter", "params": { "key": "external_id", "value": "9429041280072" } }] } }
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Update: IdDiffInput Pattern
|
|
80
|
-
|
|
81
|
-
Contractor's `associatedPeopleIds` and `leadContractorIds` use the **IdDiff pattern**
|
|
82
|
-
for updates — instead of sending the full array, send additions and removals:
|
|
83
|
-
|
|
84
|
-
```json
|
|
85
|
-
{
|
|
86
|
-
"associatedPeopleIds": { "additions": ["new_person_id"], "removals": ["old_person_id"] }
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
**Gem class:** `Input::ContractorEntity::Update` handles this via `from_model`
|
|
91
|
-
which reads `doc` vs `original_doc` to compute the diff.
|
|
92
|
-
|
|
93
|
-
### Mutations
|
|
94
|
-
|
|
95
|
-
```graphql
|
|
96
|
-
mutation CreateContractor($input: CreateContractorEntityInput!) {
|
|
97
|
-
createContractorEntity(input: $input) {
|
|
98
|
-
item { id externalId name schemaId active approved associatedPeopleIds leadContractorIds }
|
|
99
|
-
errors { details fullMessages }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
mutation UpdateContractor($input: UpdateContractorEntityInput!) {
|
|
104
|
-
updateContractorEntity(input: $input) {
|
|
105
|
-
item { ... }
|
|
106
|
-
errors { ... }
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Location Structure (Reporting Structure / RS)
|
|
114
|
-
|
|
115
|
-
### Concept
|
|
116
|
-
|
|
117
|
-
The location structure is a hierarchical tree used to tag pages and scope analytics.
|
|
118
|
-
Also called "Reporting Structure" (RS) internally.
|
|
119
|
-
|
|
120
|
-
Used for:
|
|
121
|
-
- Assigning pages to locations/departments (`locations: [LocationNodeInterface]`)
|
|
122
|
-
- Filtering searches by location (`location_ids` filter)
|
|
123
|
-
- Analytics and dashboard scoping
|
|
124
|
-
- Permission scoping (some permissions are location-based)
|
|
125
|
-
|
|
126
|
-
### GraphQL Access
|
|
127
|
-
|
|
128
|
-
```graphql
|
|
129
|
-
query GetLocationStructure($id: ID!) {
|
|
130
|
-
currentOrganization {
|
|
131
|
-
locationStructure(id: $id) {
|
|
132
|
-
id name
|
|
133
|
-
nodes {
|
|
134
|
-
id name weight archived
|
|
135
|
-
classifications { name }
|
|
136
|
-
ancestors { id name }
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Draft-Based Edits
|
|
144
|
-
|
|
145
|
-
Location structure changes go through a **draft** system:
|
|
146
|
-
1. `createLocationDraft(...)` — create an edit draft
|
|
147
|
-
2. Apply commands: `insertNode`, `moveNode`, `archiveNode`, `renameNode`, etc.
|
|
148
|
-
3. `publishDraft(...)` — apply to production
|
|
149
|
-
|
|
150
|
-
This is the most complex part of the GraphQL API. The gem has full coverage in
|
|
151
|
-
`Mutation::LocationStructure::Draft::*` and `Input::LocationStructure::Draft::*`.
|
|
152
|
-
|
|
153
|
-
### LocationNode Type
|
|
154
|
-
|
|
155
|
-
```graphql
|
|
156
|
-
type LocationNodeInterface {
|
|
157
|
-
id: ID!
|
|
158
|
-
name: String!
|
|
159
|
-
weight: Int!
|
|
160
|
-
archived: Boolean!
|
|
161
|
-
archivedToken: ID
|
|
162
|
-
parent: LocationNodeInterface
|
|
163
|
-
children: [LocationNodeInterface!]!
|
|
164
|
-
classifications: [LocationClassificationType!]!
|
|
165
|
-
ancestors: [LocationNodeInterface!]!
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### TagField (Location Picker Data Field)
|
|
170
|
-
|
|
171
|
-
`TagField` on a page is the location tag input. It stores `locations: [LocationNodeInterface]`
|
|
172
|
-
— the selected location nodes for that field. Multiple pages can have different location
|
|
173
|
-
tags from the same RS tree.
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## IdDiff Pattern
|
|
178
|
-
|
|
179
|
-
Used whenever an array relationship is updated. Instead of sending the full new array,
|
|
180
|
-
send only what changed:
|
|
181
|
-
|
|
182
|
-
```graphql
|
|
183
|
-
input IdDiffInput {
|
|
184
|
-
additions: [ID!]!
|
|
185
|
-
removals: [ID!]!
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
Used in:
|
|
190
|
-
- `ContractorEntity` `associatedPeopleIds` and `leadContractorIds` updates
|
|
191
|
-
- Potentially other relationship updates
|
|
192
|
-
|
|
193
|
-
**Gem support:**
|
|
194
|
-
- `Input::IdDiff` — the input class
|
|
195
|
-
- `Input::ContractorEntity::Update.from_model` — auto-computes additions/removals
|
|
196
|
-
from `doc` vs `original_doc` comparison
|