eco-helpers 3.2.14 → 3.2.22
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 +207 -0
- data/lib/eco/api/usecases/CLAUDE.md +78 -0
- data/lib/eco/api/usecases/default/pages.rb +30 -0
- data/lib/eco/api/usecases/graphql/CLAUDE.md +120 -0
- data/lib/eco/api/usecases/graphql/compat/ooze_redirect/dirty_array.rb +22 -0
- data/lib/eco/api/usecases/graphql/compat/ooze_redirect/field_patches.rb +241 -0
- data/lib/eco/api/usecases/graphql/compat/ooze_redirect/force_compat.rb +73 -0
- data/lib/eco/api/usecases/graphql/compat/ooze_redirect.rb +234 -0
- data/lib/eco/api/usecases/graphql/compat/parity/comparison.rb +70 -0
- data/lib/eco/api/usecases/graphql/compat/parity/harness.rb +102 -0
- data/lib/eco/api/usecases/graphql/compat/parity/run_result.rb +96 -0
- data/lib/eco/api/usecases/graphql/compat.rb +11 -0
- data/lib/eco/api/usecases/graphql/helpers/CLAUDE.md +79 -0
- data/lib/eco/api/usecases/graphql/helpers/location/command/end_points/optimizations.rb +4 -4
- data/lib/eco/api/usecases/graphql/helpers/pages/copying.rb +71 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/creatable.rb +78 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/filters.rb +114 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/ooze_handlers.rb +112 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/rescuable.rb +52 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/shortcuts.rb +186 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/typed_fields_pairing.rb +303 -0
- data/lib/eco/api/usecases/graphql/helpers/pages.rb +21 -0
- data/lib/eco/api/usecases/graphql/helpers.rb +1 -0
- data/lib/eco/api/usecases/graphql/samples/CLAUDE.md +76 -0
- data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +1 -1
- data/lib/eco/api/usecases/graphql/samples/pages/CLAUDE.md +59 -0
- data/lib/eco/api/usecases/graphql/samples/pages/org_page/base.rb +41 -0
- data/lib/eco/api/usecases/graphql/samples/pages/org_page/dsl.rb +8 -0
- data/lib/eco/api/usecases/graphql/samples/pages/org_page.rb +7 -0
- data/lib/eco/api/usecases/graphql/samples/pages/page/base.rb +148 -0
- data/lib/eco/api/usecases/graphql/samples/pages/page/dsl.rb +38 -0
- data/lib/eco/api/usecases/graphql/samples/pages/page.rb +7 -0
- data/lib/eco/api/usecases/graphql/samples/pages/register/base.rb +181 -0
- data/lib/eco/api/usecases/graphql/samples/pages/register/migration_case.rb +132 -0
- data/lib/eco/api/usecases/graphql/samples/pages/register/target_oozes_update_case.rb +163 -0
- data/lib/eco/api/usecases/graphql/samples/pages/register.rb +8 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/base.rb +70 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/command_emitter.rb +139 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/csv_build/builder.rb +126 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb +108 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/csv_build/parser.rb +98 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/csv_build.rb +17 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/applier.rb +141 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/drift_report.rb +104 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/loop.rb +155 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/recording_executor.rb +58 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/sync_readiness.rb +178 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy/verifier.rb +141 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/deploy.rb +21 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template.rb +11 -0
- data/lib/eco/api/usecases/graphql/samples/pages.rb +9 -0
- data/lib/eco/api/usecases/graphql/samples.rb +1 -0
- data/lib/eco/api/usecases/graphql.rb +1 -0
- data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +4 -0
- data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +13 -3
- data/lib/eco/version.rb +1 -1
- metadata +52 -15
- data/.gitignore +0 -23
- data/.idea/.gitignore +0 -10
- data/.markdownlint.json +0 -4
- data/.rspec +0 -3
- data/.rubocop.yml +0 -103
- data/.ruby-version +0 -1
- data/.yardopts +0 -10
- data/Gemfile +0 -8
- data/Rakefile +0 -38
- data/eco-helpers.gemspec +0 -63
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f79a58827dc2789ddfcc56e108d93520af92c15152b94338ca51e73cc388210
|
|
4
|
+
data.tar.gz: e15e69fdb8fa6bd37202aad7f01bebcda80c9aadacc23333a98846a27aeddffd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 14faa786d74eb1b096cea772f79903df7f16e0dd903ce8586621f4524413795f0720bad631b0ddbc892cee01d12e6445f2af5ff6ff808d5754bb8593959f5d9a
|
|
7
|
+
data.tar.gz: b8b28cba91173036b005e3aa9a8e88e0143a932e1342c4aa91f085c73d4e031191e5f09c8f71928547392e41291c8b0128d0455b1b646ab93422247b9cac57b9
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,213 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [3.2.22] - 2026-08-14
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Republish of `3.2.21`: its `lib/eco/version.rb` shipped with doubled carriage returns
|
|
10
|
+
(`
|
|
11
|
+
` line endings), causing a `warning: encountered
|
|
5
12
|
in middle of line` on every
|
|
13
|
+
load. Functionally identical otherwise; `3.2.21` will be yanked.
|
|
14
|
+
|
|
15
|
+
## [3.2.21] - 2026-08-14
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Packaging-only republish of `3.2.19` with an allowlisted gemspec (backported from `3.3.0`).
|
|
20
|
+
Versions `3.2.16`-`3.2.19` shipped internal repository content (`.ai-assistance/` tooling,
|
|
21
|
+
`.claude/settings.json`) to rubygems.org via the old denylist `spec.files`; `3.2.19` is
|
|
22
|
+
yanked and `3.2.16`/`3.2.18` are queued for deletion by RubyGems support. `3.2.21` is the
|
|
23
|
+
identical `lib/` code packaged clean, so constraints like `'~> 3.2.0', '>= 3.2.19'` keep
|
|
24
|
+
resolving on the 3.2 line (its graphql dependency stays `~> 1.3`, satisfied by the clean
|
|
25
|
+
`1.3.16`). The version number `3.2.20` is intentionally skipped: it exists as a
|
|
26
|
+
tagged-but-deliberately-unpublished version (see the 3.3.0-era changelog corrections).
|
|
27
|
+
|
|
28
|
+
## [3.2.19] - 2026-07-16
|
|
29
|
+
|
|
30
|
+
Farmers / `cans-upsert` reliability adoption + an ooze KPI counter fix. **Backwards-compatible.**
|
|
31
|
+
Cut from the `v3.2.18` tag (not `master`, which carries the native GraphQL activity/dashboard
|
|
32
|
+
readers depending on the unreleased gem `1.4.0`), so this ships needing only the published
|
|
33
|
+
`ecoportal-api-graphql 1.3.14`.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **Floor `ecoportal-api-graphql` to `>= 1.3.14`** (was `>= 1.3.11`) — pulls the HttpClient
|
|
38
|
+
429/1015 resilience fix, so bulk `cans-upsert` / register-update live runs are no longer
|
|
39
|
+
aborted by a single Cloudflare edge rate-limit.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- **Ooze update KPI counters now count GraphQL updates.** `RegisterUpdateCase` tallied
|
|
44
|
+
`updated`/`failed` only when the result `is_a?(Ecoportal::API::Common::Response)`, but the
|
|
45
|
+
GraphQL compat layer returns an `Ecoportal::API::GraphQL::Compat::Response` — it duck-types
|
|
46
|
+
`success?`/`status` yet is not in that class hierarchy, so every GraphQL update was silently
|
|
47
|
+
uncounted (`Updated 0 (attempted: N)`, `Failed 0`) even when the write applied. The guard is
|
|
48
|
+
now a duck-type (`respond_to?(:success?)`); `false`/`nil` returns (dry-run / no-op) still skip.
|
|
49
|
+
**Note:** this fixes the *report* only — the updates themselves were already applying.
|
|
50
|
+
|
|
51
|
+
## [3.2.18] - 2026-07-10
|
|
52
|
+
|
|
53
|
+
Version-identity + regression-guard release. **No behaviour change** vs the fixed `3.2.17` build —
|
|
54
|
+
this bump exists so the LocationDraft fix can be pinned/verified by version, and adds an offline
|
|
55
|
+
guard so the dead-fragment class of bug can't reach production again.
|
|
56
|
+
|
|
57
|
+
### Why this bump
|
|
58
|
+
|
|
59
|
+
`3.2.17` shipped as **two different builds under one version**: the version was bumped to `3.2.17`
|
|
60
|
+
on 2026-07-04 (`243822b9`), then the live-crash fix `352a9657` (LocationDraft dead-fragment
|
|
61
|
+
convention) landed on 2026-07-05 — keeping the same `3.2.17` label. Because `3.2.17` is installed
|
|
62
|
+
from git/path (unpublished), a consumer bundled in that ~26h window reports `3.2.17` yet lacks the
|
|
63
|
+
fix. This crashed the **live act-gov** org-structure sync again on 2026-07-09 with the exact
|
|
64
|
+
`uninitialized constant …Fragment::LocationDraft (NameError)`. Bumping to `3.2.18` makes the fixed
|
|
65
|
+
build unambiguously identifiable: any consumer on `>= 3.2.18` provably has the fix.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
- **Regression-guard spec for the dead-fragment convention.**
|
|
70
|
+
`spec/…/helpers/location/command/end_points/optimizations_spec.rb` scans every file under
|
|
71
|
+
`usecases/graphql/` and fails if any references a fragment via the removed `___Const__Fragment` /
|
|
72
|
+
`::Fragment::<Name>` constant convention (the exact NameError that crashed act-gov), and asserts
|
|
73
|
+
the three Location command payload procs still route fragments through the `spread :Name` registry
|
|
74
|
+
DSL. It is a SOURCE lint, not a full offline render: rendering needs the graphlient fork's
|
|
75
|
+
`to_query_string`/`spread` DSL, but eco-helpers' own bundle resolves stock graphlient `0.8.0` (the
|
|
76
|
+
fork is pinned only in the deployment/gem bundles). If eco-helpers later pins the fork in dev/test,
|
|
77
|
+
promote this to a real render guard like the gem's `tests/validate_queries.rb`.
|
|
78
|
+
|
|
79
|
+
## [3.2.17] - 2026-07-04
|
|
80
|
+
|
|
81
|
+
Native GraphQL template build+maintenance + ooze-native migration (strangler-fig): pure,
|
|
82
|
+
non-breaking additions built ALONGSIDE the OozeRedirect shim. No `OozeSamples::*` name is
|
|
83
|
+
flipped/redirected and the shim is untouched. **Not yet published** — the template Deploy loop binds
|
|
84
|
+
to the gem's `Diff` module (gem v1.3.11), which is tagged but unpublished.
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- **`-no-email` double-send guard typo.** `Location::Command::Service::TreeUpdate#email_digest` set
|
|
89
|
+
`{worfklow: {no_email: true}}` (misspelled key) after sending, so the "don't send the digest twice"
|
|
90
|
+
guard never took effect. Corrected to `workflow`. (Note: the primary `-no-email` check on the same
|
|
91
|
+
method already used the correct key, so a single `-no-email` run was already suppressed.)
|
|
92
|
+
- **Location command optimizations — dead fragment convention (live crash).**
|
|
93
|
+
`Helpers::Location::Command::EndPoints::Optimizations` referenced fragments via the removed
|
|
94
|
+
`___Const__Fragment` constant convention (`___Ecoportal__API__GraphQL__Fragment__LocationDraft` /
|
|
95
|
+
`…__LocationsError`), which the gem's registry-based fragments no longer expose as Ruby constants →
|
|
96
|
+
`uninitialized constant … Fragment::LocationDraft (NameError)` when building a locations-draft
|
|
97
|
+
`addCommands`/`create`/`publish` request. Crashed the live act-gov tagtree / org-structure sync.
|
|
98
|
+
Now `spread :LocationDraft` / `spread :LocationsError`, matching the gem.
|
|
99
|
+
|
|
100
|
+
### Dependencies
|
|
101
|
+
|
|
102
|
+
- **Raised floors to the fixed stack: `ecoportal-api-graphql >= 1.3.11`, `ecoportal-api-v2 >= 3.3.3`.**
|
|
103
|
+
Stops the stale gem 1.3.9 (missing the LocationStructure `updatedAt`/`createdAt` selection fixes from
|
|
104
|
+
1.3.10) and the buggy v2 3.3.2 (Ruby-3.x `DoubleModel` cascade `TypeError`) from resolving on
|
|
105
|
+
consumers — both crashed the live act-gov integration.
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- **`Helpers::Pages::OozeHandlers`** — native re-expression of `OozeSamples::Helpers::OozeHandlers`.
|
|
110
|
+
`merge_values(origin, append, type:, delimiter:)` dispatches on the GraphQL DataField TYPE STRING
|
|
111
|
+
(`PlainText`/`RichText` → newline; `Select`/`CrossReference`/`People`/`Checklist`/`ActionsList`/
|
|
112
|
+
`File`/`ImageGallery`/`Geo`/`Law` → delimiter join; `Date`/`Number`/`Gauge` → keep origin) instead
|
|
113
|
+
of `Ecoportal::API::V2::Page::Component::*` classes. `merge_arrays`/`array_indexes` ported verbatim.
|
|
114
|
+
Included into `Samples::Pages::Register::Base`, which also gains `#merge_field_values(field, append)`
|
|
115
|
+
(reads the field's own `#type` + current `#value`).
|
|
116
|
+
- **`Samples::Pages::Register::TargetOozesUpdateCase`** — native counterpart of
|
|
117
|
+
`OozeSamples::TargetOozesUpdateCase`, subclassing the native `Register::Base`. Reads target page ids
|
|
118
|
+
from `options[:source][:file]` (first CSV column, header dropped), batches them, previews with
|
|
119
|
+
duplicate detection + proceed prompt, fetches each via `graphql.pages.get`, dedups by id, and hands
|
|
120
|
+
each page to `process_page`. `#each_page` (register-search) is disabled here to prevent an
|
|
121
|
+
accidental org scan.
|
|
122
|
+
- **`Compat::Parity`** A/B parity harness — `RunResult` (canonical KPI + normalised per-page update
|
|
123
|
+
payload snapshot), `Comparison` (KPI/page-set/payload diffs + `equivalent?` verdict + report), and
|
|
124
|
+
`Harness` (orchestrates a legacy vs native run and compares; `.record` captures a run's observables).
|
|
125
|
+
Comparison logic is pure/offline-runnable; live capture of the two runs needs a test org + creds.
|
|
126
|
+
- **`Helpers::Pages::TypedFieldsPairing`** — native re-expression of
|
|
127
|
+
`OozeSamples::HelpersMigration::TypedFieldsPairing`. Pairs data fields across two page models by
|
|
128
|
+
SAME type + SAME label, in two screenings (exact label, then mild: case-insensitive a-z with
|
|
129
|
+
bracketed content removed). Groups/dispatches by the GraphQL DataField TYPE STRING
|
|
130
|
+
(`PlainText`/`Select`/`Date`/... per `DataField::TYPE_MAP`) rather than v2 snake_case component
|
|
131
|
+
types; `EXCLUDED_TYPES` = `%w[TagField Chart FrequencyRateChart]` (never-pair). Tracks unpaired
|
|
132
|
+
src/dst fields + multi-candidate sources, with `resolve`/report helpers. Legacy class untouched.
|
|
133
|
+
This is the cross-template field-pairing (migration), DISTINCT from `OozeHandlers#merge_values`
|
|
134
|
+
(value-merge of one already-paired field).
|
|
135
|
+
- **`Helpers::Pages::Copying`** — native counterpart of
|
|
136
|
+
`OozeSamples::HelpersMigration::Copying`. `copy_generic_paired_fields(src, dst)` runs
|
|
137
|
+
`TypedFieldsPairing` and copies each pair's content; `copy_field_content(src, dst)` dispatches on
|
|
138
|
+
the GraphQL DataField type string (`PlainText`/`Number`/`Date`/`Gauge` → `value=`, `RichText` →
|
|
139
|
+
`content=`, `Select` → `select_option`, `CrossReference` → `page_ids=`; other types left untouched).
|
|
140
|
+
The v2 regex/JSON hooked-field mapping layer (`copy_hooked_fields`) is NOT ported (later increment).
|
|
141
|
+
- **`Samples::Pages::Register::MigrationCase`** — native counterpart of
|
|
142
|
+
`OozeSamples::RegisterMigrationCase`, subclassing the native `Register::Base` and mixing in
|
|
143
|
+
`Copying`. Iterates a source register, drafts a NEW entry of `TEMPLATE_ID` (via native `Creatable`),
|
|
144
|
+
pairs the source fields to the draft's fields and copies content across, runs the unpaired /
|
|
145
|
+
multi-pair reports, and hands the draft to `#custom_processing` for finalising. Per-page persistence
|
|
146
|
+
is the native create path (not the v2 batch queue). Native-only; no OozeSamples name flipped.
|
|
147
|
+
|
|
148
|
+
Template automatic build & maintenance (Phase 5): native, offline-safe orchestration on top of the
|
|
149
|
+
released gem's `Diff::Deploy` / `Diff::VersionDiff` / `Builder::Template`. Additive, no case flipped.
|
|
150
|
+
|
|
151
|
+
- **`Samples::Pages::Template::Deploy`** — deploy → verify → monitor loop for template (workflow)
|
|
152
|
+
changes, driven by a gem `Diff::Deploy` command batch. Dry-run is the DEFAULT; live apply is gated
|
|
153
|
+
behind an explicit `commit: true` + executor. All components are session-less and offline-runnable
|
|
154
|
+
(the apply / re-read seams are injected).
|
|
155
|
+
- `Deploy::Applier` — wraps a gem `Diff::Deploy` plan. Dry-run records the batch it WOULD send;
|
|
156
|
+
`commit: true` applies it via any `execute_workflow_commands`-shaped executor (gem `Builder::Page`
|
|
157
|
+
facade adapted by `TargetBoundExecutor`). Refuses to apply while the plan has `unsupported`
|
|
158
|
+
changes unless `allow_partial: true` — nothing is guessed.
|
|
159
|
+
- `Deploy::RecordingExecutor` — offline stand-in that records batches and (with a `mutator`) replays
|
|
160
|
+
them onto an in-memory doc, so the pre/post drift check is runnable with no API call.
|
|
161
|
+
- `Deploy::DriftReport` — compares the APPLIED delta (a pre/post self-version `Diff::VersionDiff` of
|
|
162
|
+
the re-read target) to the INTENDED delta (the source diff the batch came from) on an id-free shape
|
|
163
|
+
signature; reports `missing` (under-applied) and `unexpected` (side-effect) changes honestly.
|
|
164
|
+
- `Deploy::Verifier` — pluggable post-deploy verification. `QaVerifier` wires the `ecoportal-qa`
|
|
165
|
+
check framework through a single seam when the gem is on the load path (NOT a hard dependency);
|
|
166
|
+
otherwise `NullVerifier` keeps the loop runnable and is honest that qa was unavailable.
|
|
167
|
+
- `Deploy::SyncReadiness` — given a register subset + its active template doc, reports which entries
|
|
168
|
+
CAN sync (all REQUIRED fields present, non-empty, and correctly typed) vs not, with per-entry
|
|
169
|
+
reasons.
|
|
170
|
+
- `Deploy::Loop` — orchestrates apply → drift → verify → sync-readiness and yields an aggregate
|
|
171
|
+
`Result` (`ok?` gate + human `report`). The applied-diff builder defaults to the gem's
|
|
172
|
+
`Diff::VersionDiff` (resolved lazily) and is injectable for offline use.
|
|
173
|
+
- **`Samples::Pages::Template::CsvBuild`** — CSV → template BUILD pipeline reusing the existing
|
|
174
|
+
`Template::CommandEmitter` + the released gem `Builder::Template#create(commands:)`.
|
|
175
|
+
- `CsvBuild::FormatMap` — the SINGLE, isolated place the assumed columnar format lives (column names
|
|
176
|
+
+ option delimiters), mapping a CSV row to a format-agnostic `RowSpec`. ⚠ The exact ~300-template
|
|
177
|
+
CSV column format is due ~mid-July 2026 and is NOT yet confirmed — only this file (and its spec)
|
|
178
|
+
changes when the real format lands.
|
|
179
|
+
- `CsvBuild::Parser` — groups rows into a stage → section → field tree preserving first-seen order.
|
|
180
|
+
- `CsvBuild::Builder` — replays the tree onto `CommandEmitter` (same placeholder-id threading), emits
|
|
181
|
+
the ordered `WorkflowCommand` batch (select options → `addSelectFieldOption`), and stamps a hidden
|
|
182
|
+
per-section anchor field for the identity convention (hidden-field + description). Offline/dry-run
|
|
183
|
+
by default (`#commands`, `#preview`); `#create!(template_facade)` is the live path (needs creds).
|
|
184
|
+
- `Template::CommandEmitter#field` gains optional `description:`/`required:` pass-throughs (additive).
|
|
185
|
+
- **HONEST LIMITATIONS / deferred:**
|
|
186
|
+
- The released gem (1.3.9) does NOT yet ship `Diff::Deploy` / `Diff::VersionDiff` (unreleased on the
|
|
187
|
+
gem's main). The deploy loop is written against those APIs but touches them only via lazy/injected
|
|
188
|
+
seams, so it loads and its specs run offline today; a live drift check needs those classes released.
|
|
189
|
+
- The released gem's `addField` input drops any key outside
|
|
190
|
+
placeholderId/fieldType/label/stageId/sectionId/column, so the `description` identity token is NOT
|
|
191
|
+
yet persisted through addField — only the hidden anchor field's presence survives. Wiring is in
|
|
192
|
+
place so identity flows end-to-end once the gem's addField gains a description key.
|
|
193
|
+
- Live deploy/apply and live template creation both need credentials + a test org (the sandbox
|
|
194
|
+
cannot reach live). qa verification is stubbed behind `QaVerifier`'s single seam until qa is pinned.
|
|
195
|
+
|
|
196
|
+
## [3.2.16] - 2026-07-02
|
|
197
|
+
|
|
198
|
+
APIv2→GraphQL cutover — the OozeRedirect compat layer that runs the maintained ooze integrations
|
|
199
|
+
(toocs-coding, cans-upsert, supplier-documents) on GraphQL. Requires ecoportal-api-graphql >= 1.3.9.
|
|
200
|
+
|
|
201
|
+
### Fixed / Changed
|
|
202
|
+
|
|
203
|
+
- **OozeRedirect slimmed onto the base OozeSamples loop** — removed the `with_each_entry` /
|
|
204
|
+
`update_ooze` / `process_ooze` overrides that duplicated and silently bypassed the base loop
|
|
205
|
+
(KPIs, dedup, queue, `dry_run_feedback`). Fetches go through `ooze → apiv2.pages.get` and saves
|
|
206
|
+
through `update_oozes → update_ooze → apiv2.pages.update`; a captured `submit!`/`sign_off!` rides
|
|
207
|
+
along on the single `updatePage` via `Input::Page::Update.from_model` (reads the `_compat_*` flags).
|
|
208
|
+
- **`dirty?`** also treats a pending `submit!`/`sign_off!` as dirty (so a submit-only page still saves).
|
|
209
|
+
- **`OozeBaseCase#dry_run_feedback`** prints an affirmative `[dry-run] would create/update <ref>` line.
|
|
210
|
+
- **`RegisterUpdateCase#enqueue`** accepts any duck-typed entry (`#dirty?` + `#as_update`), not just
|
|
211
|
+
v2 `Page`/`PageStage` — GraphQL compat pages queue correctly under OozeRedirect.
|
|
212
|
+
|
|
6
213
|
## [3.2.15] - 2026-05-xx
|
|
7
214
|
|
|
8
215
|
### Added
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# usecases
|
|
2
|
+
|
|
3
|
+
The use-case registry and all built-in case base classes for scripting against EcoPortal.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What a use case is
|
|
8
|
+
|
|
9
|
+
A use case is a self-contained, named, runnable unit of work. It registers itself with
|
|
10
|
+
the CLI framework, receives `session`, `options`, and `usecase` from the runner, and
|
|
11
|
+
executes its `process` (or `process_ooze` / `process_page`) method.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
CLI invokes rake → rake finds registered case → UseCase#launch → main() → process()
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Directory structure
|
|
20
|
+
|
|
21
|
+
| Path | What lives there |
|
|
22
|
+
|------|-----------------|
|
|
23
|
+
| `graphql/` | GraphQL-native base cases + samples (see `graphql/CLAUDE.md`) |
|
|
24
|
+
| `ooze_samples/` | APIv2/REST base cases: `OozeBaseCase`, `RegisterUpdateCase` |
|
|
25
|
+
| `ooze_cases/` | Concrete built-in ooze cases (export register, etc.) |
|
|
26
|
+
| `default/` | Built-in people, location, and utility cases |
|
|
27
|
+
| `default_cases/` | Loader and samples for default cases |
|
|
28
|
+
| `samples/` | Misc driver samples |
|
|
29
|
+
| `graphql.rb` | GraphQL use case loader |
|
|
30
|
+
| `ooze_samples.rb` | Ooze/REST use case loader |
|
|
31
|
+
| `default.rb` | Default use case loader |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Adding a new use case
|
|
36
|
+
|
|
37
|
+
1. Subclass the appropriate base:
|
|
38
|
+
|
|
39
|
+
| Your use case | Inherit from |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Process pages in a register (update workflow) | `Eco::API::UseCases::GraphQL::PageCase` |
|
|
42
|
+
| Process pages org-wide (cross-register, audit) | `Eco::API::UseCases::GraphQL::OrgPageCase` |
|
|
43
|
+
| Custom GraphQL script (export, report, one-off) | `Eco::API::UseCases::GraphQL::Base` |
|
|
44
|
+
| Legacy APIv2 register update | `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` |
|
|
45
|
+
|
|
46
|
+
2. Set `name` and `type`:
|
|
47
|
+
```ruby
|
|
48
|
+
name 'my-case-name' # CLI identifier: called with -my-case-name
|
|
49
|
+
type :other # :people | :contractors | :other
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. Override the entry point (`process_page`, `process`, or `process_ooze`).
|
|
53
|
+
|
|
54
|
+
4. Register in the org's `config/cli.rb`:
|
|
55
|
+
```ruby
|
|
56
|
+
cases.add('-my-case-name', :other, 'Description')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## How cases are launched
|
|
62
|
+
|
|
63
|
+
`Eco::API::UseCases::UseCase#launch` calls `callback.call(*uio.params)` where the
|
|
64
|
+
callback is bound to `method(:main)`. Before launch, `@session` and `@options` are
|
|
65
|
+
injected into the instance — subclasses access them via the `attr_reader` in `CaseEnv`.
|
|
66
|
+
|
|
67
|
+
The `:other` type passes `(session, options, usecase)` positionally to `main`.
|
|
68
|
+
For `GraphQL::Base` subclasses the signature is `main(*_args)` — `session` and
|
|
69
|
+
`options` are already available via the helpers module before `main` is called.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Related
|
|
74
|
+
|
|
75
|
+
- `graphql/CLAUDE.md` — GraphQL case hierarchy, PageCase/OrgPageCase
|
|
76
|
+
- `ooze_samples/` — legacy v2 cases (RegisterUpdateCase, OozeBaseCase)
|
|
77
|
+
- `eco-helpers/CLAUDE.md` — top-level gem context
|
|
78
|
+
- `ecoportal-api-graphql` — upstream gem providing `SearchConf`, `Compat::Pages`, etc.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class UseCases
|
|
4
|
+
class Default
|
|
5
|
+
# Namespace for CLI-integrated page use cases.
|
|
6
|
+
# These are concrete, fully functional cases exposed to all org environments.
|
|
7
|
+
#
|
|
8
|
+
# == Convention
|
|
9
|
+
# Cases here should:
|
|
10
|
+
# - Inherit from Eco::API::UseCases::GraphQL::Samples::Pages::Page::Base
|
|
11
|
+
# (or OrgPage::Base for org-wide operations)
|
|
12
|
+
# - Be registered in the org's config/cli.rb
|
|
13
|
+
# - Be as org-agnostic as possible (accept register_id, filters via options)
|
|
14
|
+
#
|
|
15
|
+
# == Adding a new default page case
|
|
16
|
+
# 1. Create the file here: default/pages/my_case.rb
|
|
17
|
+
# 2. Inherit from the appropriate pages sample base class
|
|
18
|
+
# 3. Require it below
|
|
19
|
+
# 4. Wire CLI registration in usecases/default_cases/samples.rb or the
|
|
20
|
+
# org's config/cli.rb
|
|
21
|
+
#
|
|
22
|
+
# == Currently
|
|
23
|
+
# No default page cases yet — add them here as common patterns emerge
|
|
24
|
+
# across org implementations.
|
|
25
|
+
module Pages
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# usecases/graphql
|
|
2
|
+
|
|
3
|
+
GraphQL-native use case base classes and helpers. All cases here work directly with
|
|
4
|
+
`ecoportal-api-graphql` — no v2 REST layer, no ooze objects.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Class hierarchy
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Eco::API::Common::Loaders::UseCase (registration + launch)
|
|
12
|
+
↓
|
|
13
|
+
Eco::API::UseCases::GraphQL::Base ← universal GraphQL env
|
|
14
|
+
├── GraphQL::Samples::Pages::Page::Base ← register-scoped pages
|
|
15
|
+
│ ├── GraphQL::Samples::Pages::OrgPage::Base ← org-wide pages
|
|
16
|
+
│ └── your subclass (process_page, search_conf)
|
|
17
|
+
└── your subclass directly (custom scripts: exports, reports, one-offs)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Samples live under `samples/pages/` — NOT in the `graphql/` root. The root only
|
|
21
|
+
has `base.rb`, `helpers.rb`, `utils.rb`, and `samples.rb`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Base — `graphql/base.rb`
|
|
26
|
+
|
|
27
|
+
Universal GraphQL environment. Provides `graphql`, `session`, `options`, `config`,
|
|
28
|
+
`simulate?`, `log`, `backup` via `Helpers::Base` (see `helpers/CLAUDE.md`).
|
|
29
|
+
|
|
30
|
+
Override `process` to write your script:
|
|
31
|
+
```ruby
|
|
32
|
+
class MyCase < Eco::API::UseCases::GraphQL::Base
|
|
33
|
+
name 'my-case'
|
|
34
|
+
def process
|
|
35
|
+
graphql.currentOrganization.contractorEntities.each { |c| puts c.name }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Pages — `samples/pages/`
|
|
43
|
+
|
|
44
|
+
Page processing base cases. Follow the hierarchy: `page/base` → `org_page/base`.
|
|
45
|
+
|
|
46
|
+
### `samples/pages/page/base.rb` — `Samples::Pages::Page::Base`
|
|
47
|
+
|
|
48
|
+
For **register-scoped** page update workflows.
|
|
49
|
+
|
|
50
|
+
**Class methods:** `register_id 'REG_ID'`, `batch_size 50` (default)
|
|
51
|
+
|
|
52
|
+
**Override points:**
|
|
53
|
+
- `process_page(page)` — **required** — transformation for one page
|
|
54
|
+
- `search_conf` — optional — call `super` to keep register scope, then add filters
|
|
55
|
+
|
|
56
|
+
**Protected helpers:** `update_page`, `skip(reason)`, `each_page`
|
|
57
|
+
|
|
58
|
+
**KPI readers:** `total_pages`, `processed_pages`, `updated_pages`, `skipped_pages`, `failed_pages`
|
|
59
|
+
|
|
60
|
+
**DSL (via `samples/pages/page/dsl.rb`):** `sc`, `in_register`, `state_is`, `external_id_eq`, `updated_since`
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
class Custom::UseCase::UpdateStatus < Eco::API::UseCases::GraphQL::Samples::Pages::Page::Base
|
|
64
|
+
name 'update-status'
|
|
65
|
+
register_id 'REG_ABC'
|
|
66
|
+
|
|
67
|
+
def search_conf
|
|
68
|
+
super.filter(state_is(:active))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def process_page(page)
|
|
72
|
+
page.name = page.name.upcase
|
|
73
|
+
update_page(page)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `samples/pages/org_page/base.rb` — `Samples::Pages::OrgPage::Base`
|
|
79
|
+
|
|
80
|
+
Inherits `Page::Base`. `search_conf` starts empty (org-wide, no register scope).
|
|
81
|
+
Use for: archive sweeps, cross-register audits, bulk org operations.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Samples — `graphql/samples/`
|
|
86
|
+
|
|
87
|
+
Built-in ready-to-use case implementations:
|
|
88
|
+
- `samples/location.rb` — location structure management cases
|
|
89
|
+
- `samples/contractors.rb` — contractor entity cases
|
|
90
|
+
|
|
91
|
+
See `samples/CLAUDE.md` for details.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Helpers — `graphql/helpers/`
|
|
96
|
+
|
|
97
|
+
Mixins providing domain-specific access patterns. See `helpers/CLAUDE.md`.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Loader order in `graphql.rb`
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
require 'graphql/helpers' # environment mixins (graphql, session, simulate? etc.)
|
|
105
|
+
require 'graphql/utils' # utility modules (SFTP etc.)
|
|
106
|
+
require 'graphql/base' # GraphQL::Base — universal foundation
|
|
107
|
+
require 'graphql/samples' # sample cases: location, contractors, pages, ...
|
|
108
|
+
# └─ graphql/samples/pages.rb
|
|
109
|
+
# └─ pages/page.rb → page/dsl.rb, page/base.rb
|
|
110
|
+
# └─ pages/org_page.rb → org_page/dsl.rb, org_page/base.rb
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Page base cases are in `samples/pages/` — NOT in the `graphql/` root.
|
|
114
|
+
Custom org cases are NOT loaded here — they live in the implementation repo.
|
|
115
|
+
|
|
116
|
+
## default/pages/
|
|
117
|
+
|
|
118
|
+
CLI-integrated page use cases go in `default/pages/` (mirroring `default/locations/`
|
|
119
|
+
and `default/people/`). Currently empty — add cases there when a pattern is common
|
|
120
|
+
enough to expose to all org environments. See `default/pages.rb` for the convention.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Eco::API::UseCases::GraphQL::Compat::OozeRedirect
|
|
2
|
+
# Array subclass that calls the field's setter when elements are appended,
|
|
3
|
+
# ensuring GraphQL dirty-tracking fires on `fld.people_ids << value`.
|
|
4
|
+
class DirtyArray < Array
|
|
5
|
+
def initialize(field, data)
|
|
6
|
+
@field = field
|
|
7
|
+
super(Array(data))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def <<(value)
|
|
11
|
+
super
|
|
12
|
+
@field.people_ids = to_a
|
|
13
|
+
self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def push(*values)
|
|
17
|
+
super
|
|
18
|
+
@field.people_ids = to_a
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|