ecoportal-api-graphql 1.3.11 → 1.3.12
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/.ai-assistance/code/filter_contract_matrix.md +177 -0
- data/.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md +10 -0
- data/.ai-assistance/projects/template-automatic-build-maintenance/TODO.md +11 -0
- data/.ai-assistance/skills/procedural-memory/SKILL.md +319 -0
- data/.ai-assistance/standards-version.json +21 -20
- data/CHANGELOG.md +32 -0
- data/CLAUDE.md +10 -0
- data/docs/self-docs/ARCHITECTURE.md +88 -0
- data/docs/self-docs/CHANGES.jsonl +7 -0
- data/docs/self-docs/CONVENTIONS.md +74 -0
- data/docs/self-docs/INTEGRATIONS.md +63 -0
- data/docs/self-docs/OVERVIEW.md +51 -0
- data/docs/self-docs/STATUS.md +69 -0
- data/docs/self-docs/self-docs-index.json +39 -0
- data/docs/worklog.md +0 -23
- data/lib/ecoportal/api/common/graphql/model/diffable.rb +54 -54
- data/lib/ecoportal/api/graphql/base/action.rb +43 -43
- data/lib/ecoportal/api/graphql/base/contractor_entity/member_changes.rb +67 -67
- data/lib/ecoportal/api/graphql/base/page/data_field/collection.rb +7 -0
- data/lib/ecoportal/api/graphql/base/page/data_field/contractor_entities.rb +28 -28
- data/lib/ecoportal/api/graphql/base/page/data_field/file_field.rb +25 -25
- data/lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb +24 -24
- data/lib/ecoportal/api/graphql/base/page/section_collection.rb +85 -79
- data/lib/ecoportal/api/graphql/base/preset_view.rb +17 -17
- data/lib/ecoportal/api/graphql/base/register.rb +18 -18
- data/lib/ecoportal/api/graphql/compat/filter_translator.rb +63 -28
- data/lib/ecoportal/api/graphql/concerns/page_compat.rb +51 -51
- data/lib/ecoportal/api/graphql/concerns.rb +14 -14
- data/lib/ecoportal/api/graphql/file_upload/client.rb +181 -181
- data/lib/ecoportal/api/graphql/fragment/page.rb +85 -85
- data/lib/ecoportal/api/graphql/input/action/update.rb +14 -14
- data/lib/ecoportal/api/graphql/input/contractor_entity/update.rb +41 -41
- data/lib/ecoportal/api/graphql/input/location_structure/apply_commands.rb +47 -47
- data/lib/ecoportal/api/graphql/input/location_structure/draft/add_commands.rb +49 -49
- data/lib/ecoportal/api/graphql/input/location_structure/update_command.rb +27 -27
- data/lib/ecoportal/api/graphql/input/search_conf.rb +436 -367
- data/lib/ecoportal/api/graphql/interface/location_structure/command.rb +30 -30
- data/lib/ecoportal/api/graphql/logic/input.rb +26 -26
- data/lib/ecoportal/api/graphql_version.rb +1 -1
- metadata +10 -1
data/CLAUDE.md
CHANGED
|
@@ -11,8 +11,17 @@ docs/worklog.md ← current state, blockers, open quest
|
|
|
11
11
|
.ai-assistance/projects/<slug>/TODO.md ← active project task list
|
|
12
12
|
.ai-assistance/local/paths.json ← local paths to sibling repos (gitignored — machine-specific)
|
|
13
13
|
.ai-assistance/bridge/CLAUDE.md ← bridge protocol: format, lifecycle, cross-repo requests
|
|
14
|
+
.ai-assistance/code/ecoPortal_architecture/ ← VERIFIED PLATFORM KNOWLEDGE — how the platform behaves
|
|
15
|
+
(data model, API layers, page/stage workflows, fields, search).
|
|
16
|
+
Read 00_overview_and_index first, then the area file for the
|
|
17
|
+
task at hand, BEFORE implementing platform logic. See ADR-012.
|
|
14
18
|
```
|
|
15
19
|
|
|
20
|
+
> Platform behaviour is documented in `ecoPortal_architecture/` for a reason: do not infer a
|
|
21
|
+
> stage/workflow/field rule from code alone when a verified note exists. If you confirm a new
|
|
22
|
+
> platform fact, add it to the relevant doc with its evidence (`file:line` or "confirmed by
|
|
23
|
+
> <user> on <date>") in the same session -- that tree is the durable, shared home.
|
|
24
|
+
|
|
16
25
|
---
|
|
17
26
|
|
|
18
27
|
## Repository Role
|
|
@@ -95,6 +104,7 @@ the developer explicitly requests it for a specific commit.
|
|
|
95
104
|
| `spec-generation` | `.ai-assistance/skills/spec-generation/SKILL.md` | Generate RSpec specs for backwards-compat |
|
|
96
105
|
| `refactor` | `.ai-assistance/skills/refactor/SKILL.md` | Identify and log refactoring opportunities |
|
|
97
106
|
| `project-cycle` | `.ai-assistance/skills/project-cycle/SKILL.md` | Project planning, TODOs, decisions |
|
|
107
|
+
| `procedural-memory` | `.ai-assistance/skills/procedural-memory/SKILL.md` | Distill recurring work routines and surface likely next steps |
|
|
98
108
|
|
|
99
109
|
---
|
|
100
110
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: "1.0"
|
|
3
|
+
repo: "ecoportal-api-graphql"
|
|
4
|
+
doc: architecture
|
|
5
|
+
last_generated: "2026-07-06"
|
|
6
|
+
source_head: "2abd6fc"
|
|
7
|
+
review_status: draft
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Architecture -- ecoportal-api-graphql
|
|
11
|
+
|
|
12
|
+
**Scope:** High-level structure only. Deep per-area detail lives in `.ai-assistance/code/`
|
|
13
|
+
(`code-specs`); link to it rather than duplicating.
|
|
14
|
+
|
|
15
|
+
## Top-level structure
|
|
16
|
+
|
|
17
|
+
| Directory | Role |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `lib/` | The gem source. `lib/ecoportal/api/graphql/` holds the layered client; `lib/ecoportal/api/common/graphql/` holds shared utilities (HTTP client, base model, helpers, patches). |
|
|
20
|
+
| `spec/` | RSpec specs, mirroring the `lib/` structure. |
|
|
21
|
+
| `tests/` | Manual integration test scripts (live-API characterization, not the RSpec suite). |
|
|
22
|
+
| `bin/` | Development console / helper executables. |
|
|
23
|
+
| `scripts/` | Token-offload and tooling scripts (see `scripts/CLAUDE.md`). |
|
|
24
|
+
| `docs/` | Human docs, worklog, and this self-docs set. |
|
|
25
|
+
| `.ai-assistance/` | AI agent resources: code-specs, project intent/TODOs, skills, bridge, conventions. |
|
|
26
|
+
| `.claude/` | Claude Code settings and hooks for this repo. |
|
|
27
|
+
|
|
28
|
+
## Key components
|
|
29
|
+
|
|
30
|
+
Reused from `.ai-assistance/code/OVERVIEW.md` and `.ai-assistance/code/ecoPortal_architecture/`;
|
|
31
|
+
not re-analysed here.
|
|
32
|
+
|
|
33
|
+
- **Main class `Ecoportal::API::GraphQL`** (`lib/ecoportal/api/graphql.rb`) -- the public entry
|
|
34
|
+
point, instantiated with credentials (email, pass, org_id, host). Exposes builders and
|
|
35
|
+
convenience shortcuts per domain.
|
|
36
|
+
- **`Common::GraphQL::Client`** (`lib/ecoportal/api/common/graphql/client.rb`) -- the HTTP layer,
|
|
37
|
+
extending `Graphlient::Client`. Handles auth (`AuthService`), org context, and timeouts;
|
|
38
|
+
`no_schema: true` is always passed, so schema introspection is disabled and queries are not
|
|
39
|
+
validated against a schema at build time.
|
|
40
|
+
- **Layered namespace** under `Ecoportal::API::GraphQL::` (folder mirrors module exactly):
|
|
41
|
+
`Builder` (developer-facing facade per domain) -> `Query` / `Mutation` (operation definitions)
|
|
42
|
+
-> `Logic` (execution: base_query, base_model, payload, input, connection) -> `Fragment`
|
|
43
|
+
(fragment strings) -> `Model` (read-side response models), `Input` / `Payload` (write side) ->
|
|
44
|
+
`Base` (shared base classes), `Interface` (GraphQL interface types) -> `Common::GraphQL::Client`.
|
|
45
|
+
- **`Compat::` layer** -- v2 compatibility shims (`Pages`, `Registers`, `StageView`,
|
|
46
|
+
`StageCollection`, etc.) that let `eco-helpers` treat `graphql` as a drop-in for the older `apiv2`
|
|
47
|
+
interface.
|
|
48
|
+
- **`Diff::` and `Builder::TemplateBuilder`** -- the template build-and-maintenance engine
|
|
49
|
+
(self-version and cross-object diff, pairing engine + ledger, `Change`-to-`WorkflowCommand`
|
|
50
|
+
synthesis, unified BUILD emitter). This is where current development is concentrated.
|
|
51
|
+
|
|
52
|
+
## How they fit
|
|
53
|
+
|
|
54
|
+
A consumer instantiates `Ecoportal::API::GraphQL` with credentials, then calls a `Builder` for the
|
|
55
|
+
domain it wants (page, template, action, contractor entity, location structure). The builder
|
|
56
|
+
composes `Query`/`Mutation` operations, which the `Logic` layer executes through
|
|
57
|
+
`Common::GraphQL::Client` over HTTP. Fragments are assembled at query build time
|
|
58
|
+
(`BaseQuery#assemble_fragments` scans query strings for `...FragmentName` spreads). Responses parse
|
|
59
|
+
into `Model` objects (read side); mutations take `Input` objects and return `Payload` objects
|
|
60
|
+
(write side). The `Compat` layer re-exposes this so downstream `eco-helpers` scripts keep working
|
|
61
|
+
unchanged.
|
|
62
|
+
|
|
63
|
+
## Entry points
|
|
64
|
+
|
|
65
|
+
- `require 'ecoportal/api-graphql'` loads `ecoportal-api-v2` (and transitively `ecoportal-api`),
|
|
66
|
+
the version file, `ecoportal/api/common.graphql`, then `ecoportal/api/graphql`.
|
|
67
|
+
- Runtime entry is `Ecoportal::API::GraphQL.new(email:, pass:, org_id:, host:)`.
|
|
68
|
+
|
|
69
|
+
## Build / test tooling
|
|
70
|
+
|
|
71
|
+
- Build: RubyGems / Bundler. Gem spec: `ecoportal-api-graphql.gemspec`. Version constant:
|
|
72
|
+
`Ecoportal::API::GRAPQL_VERSION`.
|
|
73
|
+
- Tests: RSpec (`bundle exec rspec`). Live AI tests are `--tag live` and require `ANTHROPIC_API_KEY`
|
|
74
|
+
loaded from `.env` via `dotenv`.
|
|
75
|
+
- CI: no CI config file was detected at the repo root by the scanner (`ci_files: []`).
|
|
76
|
+
<!-- TODO(human): confirm whether CI runs elsewhere (e.g. GitLab CI defined outside root) or is absent. -->
|
|
77
|
+
|
|
78
|
+
## Links to detailed docs
|
|
79
|
+
|
|
80
|
+
- `.ai-assistance/code/OVERVIEW.md` -- gem overview, entry points, namespace convention.
|
|
81
|
+
- `.ai-assistance/code/ecoPortal_architecture/00_overview_and_index.md` -- verified platform
|
|
82
|
+
knowledge index (data model, API layers, page/stage workflows, fields, search).
|
|
83
|
+
- `.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md` -- the integration gem stack.
|
|
84
|
+
- `.ai-assistance/code/dependencies.md` -- full dependency map (remotes, fork relationships).
|
|
85
|
+
- `.ai-assistance/code/diff_pairing_engine.md`, `.ai-assistance/code/diff_service_deep_dive.md`,
|
|
86
|
+
`.ai-assistance/code/template_diff_pairing_domain.md` -- the template diff/build engine.
|
|
87
|
+
- `.ai-assistance/code/data_fields.md`, `.ai-assistance/code/graphql_domain_knowledge.md`,
|
|
88
|
+
`.ai-assistance/code/schema_analysis.md` -- data-field and schema detail.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/OVERVIEW.md", "change": "created", "from_hash": null, "to_hash": "a43c976a85ebfad6"}
|
|
2
|
+
{"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/ARCHITECTURE.md", "change": "created", "from_hash": null, "to_hash": "b0989977a912aab1"}
|
|
3
|
+
{"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/CONVENTIONS.md", "change": "created", "from_hash": null, "to_hash": "1649159eb799a7b8"}
|
|
4
|
+
{"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/INTEGRATIONS.md", "change": "created", "from_hash": null, "to_hash": "bae9f91748e33af1"}
|
|
5
|
+
{"ts": "2026-07-06T11:14:43Z", "file": "docs/self-docs/STATUS.md", "change": "created", "from_hash": null, "to_hash": "08841d1d45eea2d6"}
|
|
6
|
+
{"ts": "2026-07-06T11:15:00Z", "file": "docs/self-docs/OVERVIEW.md", "change": "updated", "from_hash": "a43c976a85ebfad6", "to_hash": "391055c9a6a687b9"}
|
|
7
|
+
{"ts": "2026-07-06T11:15:00Z", "file": "docs/self-docs/ARCHITECTURE.md", "change": "updated", "from_hash": "b0989977a912aab1", "to_hash": "ad05f25b95b49c00"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: "1.0"
|
|
3
|
+
repo: "ecoportal-api-graphql"
|
|
4
|
+
doc: conventions
|
|
5
|
+
last_generated: "2026-07-06"
|
|
6
|
+
source_head: "2abd6fc"
|
|
7
|
+
review_status: draft
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Conventions -- ecoportal-api-graphql
|
|
11
|
+
|
|
12
|
+
**Source of truth:** `.rubocop.yml` (repo root) and `CLAUDE.md`. There is no `ai-discovery`
|
|
13
|
+
`conventions.md` in this repo; the below is composed from `.rubocop.yml`, `CLAUDE.md`, and observed
|
|
14
|
+
git history, not re-derived from code.
|
|
15
|
+
|
|
16
|
+
## Code style
|
|
17
|
+
|
|
18
|
+
- Indentation: 2 spaces (Ruby default; no override in `.rubocop.yml`).
|
|
19
|
+
- Quotes: not enforced -- `Style/StringLiterals` is disabled, so single and double quotes are both
|
|
20
|
+
accepted.
|
|
21
|
+
- Line length: unlimited -- `Layout/LineLength` is disabled.
|
|
22
|
+
- Frozen string literal comment: not required (`Style/FrozenStringLiteralComment` disabled).
|
|
23
|
+
- Hash alignment: table style for both colon and hash-rocket (`Layout/HashAlignment`).
|
|
24
|
+
- Dot position: trailing (`Layout/DotPosition`).
|
|
25
|
+
- Alias: prefer `alias_method` (`Style/Alias`).
|
|
26
|
+
- Notable relaxed metrics: `ClassLength` 350, `ModuleLength` 400, `MethodLength` 100,
|
|
27
|
+
`AbcSize` 30, `CyclomaticComplexity`/`PerceivedComplexity` 30, `ParameterLists` 5.
|
|
28
|
+
- Naming exceptions: camelCase method/variable names are allowed where they mirror GraphQL/Relay
|
|
29
|
+
schema fields verbatim (`Naming/MethodName` disabled; `clientMutationId` allow-listed). This is
|
|
30
|
+
intentional -- the client mirrors the schema.
|
|
31
|
+
- Rubocop compliance is required for all generated Ruby: run `bundle exec rubocop <file>` to verify.
|
|
32
|
+
|
|
33
|
+
## Branch naming
|
|
34
|
+
|
|
35
|
+
Type-prefixed, slash-separated. Observed prefixes in the branch list and history:
|
|
36
|
+
`feat/<slug>`, `fix/<slug>`, `docs/<slug>`, `chore/<slug>`, and longer-lived `feature/<slug>`
|
|
37
|
+
branches for multi-phase work. Note: `pages` is a long-running primary development branch that
|
|
38
|
+
predates the prefix convention.
|
|
39
|
+
|
|
40
|
+
## Commit message style
|
|
41
|
+
|
|
42
|
+
Conventional Commits: `type(scope): message`. Observed types include `feat`, `fix`, `docs`,
|
|
43
|
+
`chore`, and `merge`. Scopes are area-based, e.g. `feat(diff/builder):`, `docs(worklog):`,
|
|
44
|
+
`chore(release):`, `docs(template-automation):`. Release commits follow
|
|
45
|
+
`chore(release): vX.Y.Z ...`.
|
|
46
|
+
|
|
47
|
+
## Test conventions
|
|
48
|
+
|
|
49
|
+
- Runner: RSpec (`bundle exec rspec`).
|
|
50
|
+
- Placement: spec files mirror the `lib/` structure under `spec/` (e.g.
|
|
51
|
+
`spec/ecoportal/api/graphql/input/search_conf_spec.rb`).
|
|
52
|
+
- Live tests: tagged `--tag live`; `.env` is loaded via `dotenv` in `spec_helper.rb`
|
|
53
|
+
(needs `ANTHROPIC_API_KEY` for live AI tests).
|
|
54
|
+
- Manual integration scripts live under `tests/` (separate from the RSpec suite).
|
|
55
|
+
- Coverage is known to be sparse in subdirectories (see `.ai-assistance/code/spec_coverage.md`).
|
|
56
|
+
|
|
57
|
+
## Project-specific rules
|
|
58
|
+
|
|
59
|
+
From `CLAUDE.md`:
|
|
60
|
+
|
|
61
|
+
- Namespace: `Ecoportal::API::GraphQL` (capital G, Q, L); file paths use lowercase `graphql/`.
|
|
62
|
+
- File/class mirroring: file path mirrors the class namespace exactly.
|
|
63
|
+
- Code-spec docs: for every area analysed, a corresponding `.ai-assistance/code/<area>.md` should
|
|
64
|
+
exist or be created/updated.
|
|
65
|
+
- No silent breaking changes: any change to the gem's public interface must be noted in the active
|
|
66
|
+
project's DECISIONS log and confirmed with the developer. Backwards compatibility with
|
|
67
|
+
`eco-helpers` must be preserved unless explicitly broken.
|
|
68
|
+
- Gemini responses must be verified against actual code before applying.
|
|
69
|
+
- Commit authorship: developer only by default; do not add `Co-Authored-By: Claude ...` unless the
|
|
70
|
+
developer explicitly requests it.
|
|
71
|
+
- Verified Platform Knowledge: consult `.ai-assistance/code/ecoPortal_architecture/` before
|
|
72
|
+
implementing platform logic; record newly confirmed platform facts there with evidence (ADR-012).
|
|
73
|
+
- Working-tree safety: follow `.ai-assistance/conventions/code-working-tree-protocol.md` (bridge
|
|
74
|
+
LOCK, commit overlapping unstaged changes as `wip:` first) before modifying files.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: "1.0"
|
|
3
|
+
repo: "ecoportal-api-graphql"
|
|
4
|
+
doc: integrations
|
|
5
|
+
last_generated: "2026-07-06"
|
|
6
|
+
source_head: "2abd6fc"
|
|
7
|
+
review_status: draft
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Integrations -- ecoportal-api-graphql
|
|
11
|
+
|
|
12
|
+
**Purpose:** the ties this project has to other systems and repos. This is the section the central
|
|
13
|
+
hub uses to trace inter-project dependencies and surface reuse (ADR-015 Section 5).
|
|
14
|
+
|
|
15
|
+
## External services
|
|
16
|
+
|
|
17
|
+
| Service | Purpose | Notes |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| ecoPortal GraphQL API | The API this gem is a client for -- reads/writes pages, templates, location structures, contractor entities, actions, organizations. | Auth via `Common::GraphQL::AuthService` (OAuth), scoped by `org_id`; hosted per environment (e.g. live.ecoportal.com). `no_schema: true` -- no introspection. |
|
|
20
|
+
| ecoPortal REST API (v2) | Underlying REST infrastructure the GraphQL layer builds on and stays compatible with via the `Compat` layer. | Provided by the upstream `ecoportal-api-v2` gem. |
|
|
21
|
+
|
|
22
|
+
## Upstream dependencies (repos we depend on)
|
|
23
|
+
|
|
24
|
+
| Repo / package | What we use it for |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `ecoportal-api` | REST API base: auth, org context, base models, and the `Ecoportal::API::Common` namespace this gem extends. Team-managed. |
|
|
27
|
+
| `ecoportal-api-v2` | REST API v2 layer (Page, Register, etc.); required by the entry point. Team-managed. Being retired over time. |
|
|
28
|
+
| `graphlient` | Friendly GraphQL DSL/client wrapper; `Common::GraphQL::Client` inherits from `Graphlient::Client`. Active fork owned by the team (upstream: `ashkan18/graphlient`). |
|
|
29
|
+
| `graphql-client` | Low-level GraphQL HTTP + parsing, pulled in transitively via graphlient. Fork for upstream contribution only (upstream: `github-community-projects/graphql-client`); not team-maintained. |
|
|
30
|
+
| `graphql` (graphql-ruby) | Schema definition/execution engine (`rmosolgo/graphql-ruby`); external, read-only reference. Used server-side by ecoPortal; client-side usage here is indirect. |
|
|
31
|
+
|
|
32
|
+
## Downstream consumers (who depends on us)
|
|
33
|
+
|
|
34
|
+
- `eco-helpers` -- the primary consumer; an integration-script framework for the ecoPortal
|
|
35
|
+
Integrations Team. It requires this gem (`ecoportal-api-graphql ~> 1.3, >= 1.3.4`) and relies on
|
|
36
|
+
the `Compat` layer to treat `graphql` as a drop-in for the older `apiv2` interface. Backwards
|
|
37
|
+
compatibility with `eco-helpers` must be preserved unless explicitly broken.
|
|
38
|
+
- `ecoportal-qa` -- consumes the gem to verify template deploys (the "verify" step of the template
|
|
39
|
+
build-and-maintenance pipeline; a `DryRunClient` and `Publisher` were built there). The
|
|
40
|
+
template-automation project aims to tighten `ecoportal-qa` to consume the gem's page-model.
|
|
41
|
+
|
|
42
|
+
## Key package dependencies
|
|
43
|
+
|
|
44
|
+
From `ecoportal-api-graphql.gemspec` (runtime):
|
|
45
|
+
|
|
46
|
+
- `ecoportal-api` `~> 0.10, >= 0.10.16`
|
|
47
|
+
- `ecoportal-api-v2` `~> 3.3, >= 3.3.3`
|
|
48
|
+
- `graphlient` `>= 0.8.0, < 0.9`
|
|
49
|
+
|
|
50
|
+
Development: `rspec`, `rubocop` (+ `rubocop-rake`), `rake`, `pry`, `yard`, `redcarpet`.
|
|
51
|
+
Required Ruby: `>= 3.2.2`.
|
|
52
|
+
|
|
53
|
+
## Feature-request-not-rebuild note
|
|
54
|
+
|
|
55
|
+
- The template build-and-maintenance capability is developed here in the gem (mechanics: read
|
|
56
|
+
model, command vocabulary, BUILD emitter, diff, apply, pairing engine + ledger). Orchestration
|
|
57
|
+
(interactive assisted-resolution UX, session flows) belongs in `eco-helpers`, and verification in
|
|
58
|
+
`ecoportal-qa`. Before building template/diff/deploy mechanics elsewhere, raise a feature request
|
|
59
|
+
against this gem rather than duplicating the emission layer.
|
|
60
|
+
- The active template automation is a concrete consumer of the ep-ai-standards
|
|
61
|
+
`security/ai-service-account-scope` standard: it must authenticate as a dedicated per-org SERVICE
|
|
62
|
+
ACCOUNT scoped to templates/org-config only, with page records (customer data + PII) structurally
|
|
63
|
+
out of scope by credential grant. See `.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: "1.0"
|
|
3
|
+
repo: "ecoportal-api-graphql"
|
|
4
|
+
doc: overview
|
|
5
|
+
last_generated: "2026-07-06"
|
|
6
|
+
source_head: "2abd6fc"
|
|
7
|
+
review_status: draft # draft | reviewed (only a human sets 'reviewed')
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Overview -- ecoportal-api-graphql
|
|
11
|
+
|
|
12
|
+
## What this project is
|
|
13
|
+
|
|
14
|
+
`ecoportal-api-graphql` is a Ruby gem that provides a GraphQL client layer for the ecoPortal
|
|
15
|
+
GraphQL API. It wraps `graphlient` (which itself wraps `graphql-client`) and builds a structured
|
|
16
|
+
layer of models, queries, mutations, fragments, inputs, payloads, and builders on top of it. It
|
|
17
|
+
sits between the upstream ecoPortal REST API gems and downstream consumer code, exposing a
|
|
18
|
+
developer-facing facade for reading and writing ecoPortal domain objects (pages, templates,
|
|
19
|
+
location structures, contractor entities, actions, and organizations).
|
|
20
|
+
|
|
21
|
+
## Who it serves
|
|
22
|
+
|
|
23
|
+
Team members -- specifically the ecoPortal Integrations Team and internal script authors. The gem
|
|
24
|
+
is a developer-facing library, not a customer-facing product; its primary consumer is the
|
|
25
|
+
`eco-helpers` integration-script framework. Because it can read and write live customer data
|
|
26
|
+
(page records) through the API, AI caution levels are elevated: the active template-automation
|
|
27
|
+
work is deliberately scoped to a service account covering templates/org-config only, keeping page
|
|
28
|
+
records (customer data + PII) structurally out of scope.
|
|
29
|
+
|
|
30
|
+
## Current priority
|
|
31
|
+
|
|
32
|
+
Template Automatic Build and Maintenance (the #1-priority project). This merges the former
|
|
33
|
+
`template-diff-deploy` and `template-maintenance` efforts into one lifecycle: build/maintain 300+
|
|
34
|
+
templates programmatically (a CSV-to-templates pipeline, deadline approximately September 2026)
|
|
35
|
+
and make UAT-to-PROD change replication smooth via a diff/pairing/deploy/verify pipeline. Both
|
|
36
|
+
halves share one emission layer (an ordered `WorkflowCommand` batch with `placeholderId`
|
|
37
|
+
threading).
|
|
38
|
+
|
|
39
|
+
## At a glance
|
|
40
|
+
|
|
41
|
+
- Repo type: ruby-gem
|
|
42
|
+
- Primary language / runtime: Ruby (>= 3.2.2)
|
|
43
|
+
- Entry points: `require 'ecoportal/api-graphql'` -> `lib/ecoportal/api-graphql.rb` ->
|
|
44
|
+
`Ecoportal::API::GraphQL` (`lib/ecoportal/api/graphql.rb`)
|
|
45
|
+
|
|
46
|
+
## Related self-docs
|
|
47
|
+
|
|
48
|
+
- Architecture: `ARCHITECTURE.md`
|
|
49
|
+
- Conventions: `CONVENTIONS.md`
|
|
50
|
+
- Integrations: `INTEGRATIONS.md`
|
|
51
|
+
- Status: `STATUS.md`
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: "1.0"
|
|
3
|
+
repo: "ecoportal-api-graphql"
|
|
4
|
+
doc: status
|
|
5
|
+
last_generated: "2026-07-06"
|
|
6
|
+
source_head: "2abd6fc"
|
|
7
|
+
review_status: draft
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Status -- ecoportal-api-graphql
|
|
11
|
+
|
|
12
|
+
**Note:** this is the most time-sensitive self-doc. Refresh it at cycle-end and after structural
|
|
13
|
+
changes. Staleness is detectable via `source_head` vs the current git head.
|
|
14
|
+
|
|
15
|
+
## Active work
|
|
16
|
+
|
|
17
|
+
Hot paths from the scanner (`lib`, `.ai-assistance`, `spec`, `docs`) plus the active project
|
|
18
|
+
intent. The #1-priority project is Template Automatic Build and Maintenance (see
|
|
19
|
+
`.ai-assistance/projects/template-automatic-build-maintenance/INTENT.md`), which merges the former
|
|
20
|
+
`template-diff-deploy` and `template-maintenance` projects into one lifecycle sharing a single
|
|
21
|
+
`WorkflowCommand` emission layer:
|
|
22
|
+
|
|
23
|
+
- BUILD: build/maintain 300+ templates programmatically (CSV-to-templates pipeline, deadline
|
|
24
|
+
approximately September 2026) via a declarative spec -> `Builder::TemplateBuilder` -> ordered
|
|
25
|
+
command batch -> apply -> verify. Emitter is ready; live characterization is pending sandbox
|
|
26
|
+
service-account credentials.
|
|
27
|
+
- MAINTAIN / DIFF-DEPLOY: UAT-to-PROD change replication -- detect (diff) -> review -> deploy
|
|
28
|
+
(delta commands) -> verify -> monitor. Self-version diff, the pairing engine + ledger, diff
|
|
29
|
+
modalities/`Diff::Strategy`, the cross-object diff path, and `Change`-to-`WorkflowCommand`
|
|
30
|
+
synthesis are done; the interactive assisted-pairing UX (in `eco-helpers`) is pending.
|
|
31
|
+
- Recent fragment-convention work: Action/ContractorEntity mutations migrated to the spread
|
|
32
|
+
convention; the query validator now covers mutations.
|
|
33
|
+
|
|
34
|
+
## Migration state (if any)
|
|
35
|
+
|
|
36
|
+
- ooze/pages GraphQL-native migration and the api-v2-to-graphql migration are ongoing themes (see
|
|
37
|
+
`.ai-assistance/projects/ooze-graphql-native-migration/` and `.../api-v2-to-graphql-migration/`).
|
|
38
|
+
`ecoportal-api-v2` is being retired over time; the `Compat` layer preserves the v2 interface for
|
|
39
|
+
`eco-helpers` during the transition.
|
|
40
|
+
- Recent releases addressed stale-release drift: v1.3.11 raised the `ecoportal-api-v2` floor to
|
|
41
|
+
`>= 3.3.3` to fix a Ruby-3.x cascade after three live crashes were traced to version drift.
|
|
42
|
+
|
|
43
|
+
## Known risks
|
|
44
|
+
|
|
45
|
+
- Live customer data exposure: the gem can read/write page records (PII). The template automation
|
|
46
|
+
is the mitigation lever -- it MUST run under a service account scoped to templates/org-config
|
|
47
|
+
only, enforced by credential grant, not by instruction (ep-ai-standards
|
|
48
|
+
`security/ai-service-account-scope`). Provisioning that concrete grant is now this repo's work,
|
|
49
|
+
not a platform blocker.
|
|
50
|
+
- Backwards compatibility with `eco-helpers` is a hard constraint; public-interface changes must be
|
|
51
|
+
logged in the active project's DECISIONS log and confirmed with the developer.
|
|
52
|
+
- `no_schema: true` means queries are not validated against a schema at build time -- a query
|
|
53
|
+
validator (now covering mutations) is the compensating control.
|
|
54
|
+
- Spec coverage is sparse in subdirectories (`.ai-assistance/code/spec_coverage.md`).
|
|
55
|
+
|
|
56
|
+
## Open questions
|
|
57
|
+
|
|
58
|
+
- No CI config was detected at the repo root; whether CI runs elsewhere or is absent needs human
|
|
59
|
+
confirmation.
|
|
60
|
+
- The cross-object equivalence-matching (pairing) problem has no stable identity across independent
|
|
61
|
+
Mongo objects; it depends partly on Product's forthcoming template-entity-id, which this gem
|
|
62
|
+
bridges and feeds but does not own.
|
|
63
|
+
- Workflow-config-per-stage diff is out of scope until the `PagesWorkflow.stages` read model is
|
|
64
|
+
completed.
|
|
65
|
+
|
|
66
|
+
## Recent changes
|
|
67
|
+
|
|
68
|
+
First generation of the self-docs set (draft, for the EP Projects Register pilot). Generated at
|
|
69
|
+
git head `2abd6fc` on branch `main`. The machine-readable delta is in `CHANGES.jsonl`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"generated_at": "2026-07-06T11:15:00Z",
|
|
4
|
+
"repo_name": "ecoportal-api-graphql",
|
|
5
|
+
"repo_type": "ruby-gem",
|
|
6
|
+
"head": "2abd6fc",
|
|
7
|
+
"docs": [
|
|
8
|
+
{
|
|
9
|
+
"file": "docs/self-docs/OVERVIEW.md",
|
|
10
|
+
"purpose": "What the project is, who it serves, current priority",
|
|
11
|
+
"exists": true,
|
|
12
|
+
"content_hash": "391055c9a6a687b9"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"file": "docs/self-docs/ARCHITECTURE.md",
|
|
16
|
+
"purpose": "Top-level structure, key components, how they fit",
|
|
17
|
+
"exists": true,
|
|
18
|
+
"content_hash": "ad05f25b95b49c00"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"file": "docs/self-docs/CONVENTIONS.md",
|
|
22
|
+
"purpose": "Coding style, branch naming, commit style, test conventions",
|
|
23
|
+
"exists": true,
|
|
24
|
+
"content_hash": "1649159eb799a7b8"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"file": "docs/self-docs/INTEGRATIONS.md",
|
|
28
|
+
"purpose": "External services, upstream/downstream repos, dependencies",
|
|
29
|
+
"exists": true,
|
|
30
|
+
"content_hash": "bae9f91748e33af1"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"file": "docs/self-docs/STATUS.md",
|
|
34
|
+
"purpose": "Active work, migration state, known risks, open questions",
|
|
35
|
+
"exists": true,
|
|
36
|
+
"content_hash": "08841d1d45eea2d6"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
data/docs/worklog.md
CHANGED
|
@@ -8,29 +8,6 @@ This file tracks cross-project session state and the overall repo status.
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## ▶ SESSION 2026-07-06 — RELEASED the fixed stack; act-gov tagtree + toocs live-green
|
|
12
|
-
|
|
13
|
-
The 2026-07-05 crash-fixing arc shipped. **All three live-verified green** on act-gov (direct
|
|
14
|
-
`-no-email` runs): **tagtree-update** (created draft → 8 insert / 3 archive → published, no customer
|
|
15
|
-
email) and **toocs-coding** (Oscar confirmed). Released, in dependency order:
|
|
16
|
-
- **ecoportal-api-v2 `v3.3.3`** — Ruby-3.x cascade block-param fix + regression spec.
|
|
17
|
-
- **ecoportal-api-graphql `v1.3.11`** — Diff build+maintenance engine; fragment-convention + Kickstand
|
|
18
|
-
DateTime fixes; requires v2 >= 3.3.3; validate_queries now covers mutations + hard-fails on render.
|
|
19
|
-
- **eco-helpers `v3.2.17`** — native template build+maintenance + ooze-native; location fragment
|
|
20
|
-
(`spread :LocationDraft`) + `worfklow` no-email typo fixes; requires gem >= 1.3.11 + v2 >= 3.3.3.
|
|
21
|
-
- **multi_org_api** (`master` + `api-deprecation`) — `-no-email` now forwarded through the rake
|
|
22
|
-
wrapper + `worfklow` typo fixed (stops customer emails during rake/scheduled tests).
|
|
23
|
-
|
|
24
|
-
Oscar git-pushed all repos + gem-published; **deploying via server `bundle update ecoportal-api-v2
|
|
25
|
-
ecoportal-api-graphql eco-helpers`** (production Gemfile floors gem>=1.3.9/eco-helpers>=3.2.16, but the
|
|
26
|
-
gemspec floors force v2>=3.3.3 + gem>=1.3.11 transitively). Systemic guard against the recurring
|
|
27
|
-
stale-release drift: raised gemspec dependency FLOORS everywhere + extended `validate_queries.rb`.
|
|
28
|
-
|
|
29
|
-
**STILL OPEN (non-blocking):** rake `notify_exception` emails the customer on task error regardless of
|
|
30
|
-
`-no-email` — decision pending (suppress / route-to-dev / keep). See [[no-email-leak-paths]].
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
11
|
## ▶ SESSION 2026-07-05 — 3 live act-gov crashes = ONE root cause (stale released gems); fixed-stack floors
|
|
35
12
|
|
|
36
13
|
Three live crashes on act-gov (`-toocs-coding` + org-structure sync), all the **same class**: the
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
module Ecoportal
|
|
2
|
-
module API
|
|
3
|
-
module Common
|
|
4
|
-
module GraphQL
|
|
5
|
-
class Model
|
|
6
|
-
module Diffable
|
|
7
|
-
require_relative 'diffable/hash_diff_nesting'
|
|
8
|
-
require_relative 'diffable/classic_diff_service'
|
|
9
|
-
require_relative 'diffable/diff_service'
|
|
10
|
-
|
|
11
|
-
DIFF_CLASS = DiffService
|
|
12
|
-
|
|
13
|
-
# INSTANCE METHODS
|
|
14
|
-
|
|
15
|
-
# @note **cascaded callbacks**
|
|
16
|
-
# 1. Can **skip** rooted objects (where `root?` is `true`).
|
|
17
|
-
# This is because nested rooted objects are usually look-ups
|
|
18
|
-
# (not really part of the target model).
|
|
19
|
-
# 2. Allow to redefine `as_update` on each model.
|
|
20
|
-
# @param flat [Boolean] whether it should NOT perform a cascaded callback
|
|
21
|
-
# througout all the instance objects of the model hierarchy (nested).
|
|
22
|
-
# @param ignored [Array<String>] the keys that should be ignored, not part
|
|
23
|
-
# of the result of `as_update`
|
|
24
|
-
# @return [nil, Hash] the patch `Hash` model including only
|
|
25
|
-
# the changes between `original_doc` and `doc`.
|
|
26
|
-
def as_update(**kargs)
|
|
27
|
-
diff_class.new(
|
|
28
|
-
self,
|
|
29
|
-
**kargs
|
|
30
|
-
).diff
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# @note `cascaded` will be more accurate in a `GraphQL` scenario.
|
|
34
|
-
# @return [Boolean] stating if there are changes.
|
|
35
|
-
def dirty?(...)
|
|
36
|
-
au = as_update(...)
|
|
37
|
-
|
|
38
|
-
return false if au.nil?
|
|
39
|
-
return false if au == {}
|
|
40
|
-
|
|
41
|
-
true
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def diff_class
|
|
47
|
-
self.class::DIFF_CLASS
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
1
|
+
module Ecoportal
|
|
2
|
+
module API
|
|
3
|
+
module Common
|
|
4
|
+
module GraphQL
|
|
5
|
+
class Model
|
|
6
|
+
module Diffable
|
|
7
|
+
require_relative 'diffable/hash_diff_nesting'
|
|
8
|
+
require_relative 'diffable/classic_diff_service'
|
|
9
|
+
require_relative 'diffable/diff_service'
|
|
10
|
+
|
|
11
|
+
DIFF_CLASS = DiffService
|
|
12
|
+
|
|
13
|
+
# INSTANCE METHODS
|
|
14
|
+
|
|
15
|
+
# @note **cascaded callbacks**
|
|
16
|
+
# 1. Can **skip** rooted objects (where `root?` is `true`).
|
|
17
|
+
# This is because nested rooted objects are usually look-ups
|
|
18
|
+
# (not really part of the target model).
|
|
19
|
+
# 2. Allow to redefine `as_update` on each model.
|
|
20
|
+
# @param flat [Boolean] whether it should NOT perform a cascaded callback
|
|
21
|
+
# througout all the instance objects of the model hierarchy (nested).
|
|
22
|
+
# @param ignored [Array<String>] the keys that should be ignored, not part
|
|
23
|
+
# of the result of `as_update`
|
|
24
|
+
# @return [nil, Hash] the patch `Hash` model including only
|
|
25
|
+
# the changes between `original_doc` and `doc`.
|
|
26
|
+
def as_update(**kargs)
|
|
27
|
+
diff_class.new(
|
|
28
|
+
self,
|
|
29
|
+
**kargs
|
|
30
|
+
).diff
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @note `cascaded` will be more accurate in a `GraphQL` scenario.
|
|
34
|
+
# @return [Boolean] stating if there are changes.
|
|
35
|
+
def dirty?(...)
|
|
36
|
+
au = as_update(...)
|
|
37
|
+
|
|
38
|
+
return false if au.nil?
|
|
39
|
+
return false if au == {}
|
|
40
|
+
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def diff_class
|
|
47
|
+
self.class::DIFF_CLASS
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
module Ecoportal
|
|
2
|
-
module API
|
|
3
|
-
class GraphQL
|
|
4
|
-
module Base
|
|
5
|
-
class Action < Logic::BaseModel
|
|
6
|
-
root!
|
|
7
|
-
|
|
8
|
-
passkey :id
|
|
9
|
-
passthrough :altId
|
|
10
|
-
passthrough :name, :description
|
|
11
|
-
passthrough :age
|
|
12
|
-
|
|
13
|
-
passarray :locationIds
|
|
14
|
-
|
|
15
|
-
passthrough :status, :relativeStatus
|
|
16
|
-
passboolean :archived, :attached, :standaloneAction
|
|
17
|
-
passthrough :actionCategoryId
|
|
18
|
-
|
|
19
|
-
passarray :fileContainerIds
|
|
20
|
-
|
|
21
|
-
passarray :assignedPersonMemberIds
|
|
22
|
-
passthrough :creatorId, :creatorUserId
|
|
23
|
-
passthrough :updaterId, :updaterUserId
|
|
24
|
-
passthrough :completerId
|
|
25
|
-
|
|
26
|
-
passthrough :timeZone
|
|
27
|
-
embeds_one :dueDate, klass: Base::DateTime
|
|
28
|
-
embeds_one :reminderDate, klass: Base::DateTime
|
|
29
|
-
embeds_one :createdAt, klass: Base::DateTime
|
|
30
|
-
embeds_one :updatedAt, klass: Base::DateTime
|
|
31
|
-
embeds_one :completedAt, klass: Base::DateTime
|
|
32
|
-
embeds_one :openededAt, klass: Base::DateTime
|
|
33
|
-
embeds_one :closedAt, klass: Base::DateTime
|
|
34
|
-
embeds_one :dueOrClosedDate, klass: Base::DateTime
|
|
35
|
-
|
|
36
|
-
passboolean :completed
|
|
37
|
-
passthrough :closeDetail
|
|
38
|
-
#passthrough :dueIn # only for templates
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
1
|
+
module Ecoportal
|
|
2
|
+
module API
|
|
3
|
+
class GraphQL
|
|
4
|
+
module Base
|
|
5
|
+
class Action < Logic::BaseModel
|
|
6
|
+
root!
|
|
7
|
+
|
|
8
|
+
passkey :id
|
|
9
|
+
passthrough :altId
|
|
10
|
+
passthrough :name, :description
|
|
11
|
+
passthrough :age
|
|
12
|
+
|
|
13
|
+
passarray :locationIds
|
|
14
|
+
|
|
15
|
+
passthrough :status, :relativeStatus
|
|
16
|
+
passboolean :archived, :attached, :standaloneAction
|
|
17
|
+
passthrough :actionCategoryId
|
|
18
|
+
|
|
19
|
+
passarray :fileContainerIds
|
|
20
|
+
|
|
21
|
+
passarray :assignedPersonMemberIds
|
|
22
|
+
passthrough :creatorId, :creatorUserId
|
|
23
|
+
passthrough :updaterId, :updaterUserId
|
|
24
|
+
passthrough :completerId
|
|
25
|
+
|
|
26
|
+
passthrough :timeZone
|
|
27
|
+
embeds_one :dueDate, klass: Base::DateTime
|
|
28
|
+
embeds_one :reminderDate, klass: Base::DateTime
|
|
29
|
+
embeds_one :createdAt, klass: Base::DateTime
|
|
30
|
+
embeds_one :updatedAt, klass: Base::DateTime
|
|
31
|
+
embeds_one :completedAt, klass: Base::DateTime
|
|
32
|
+
embeds_one :openededAt, klass: Base::DateTime
|
|
33
|
+
embeds_one :closedAt, klass: Base::DateTime
|
|
34
|
+
embeds_one :dueOrClosedDate, klass: Base::DateTime
|
|
35
|
+
|
|
36
|
+
passboolean :completed
|
|
37
|
+
passthrough :closeDetail
|
|
38
|
+
#passthrough :dueIn # only for templates
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|