eco-helpers 3.2.23 → 3.3.0
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 +92 -39
- data/lib/eco/api/common/session/base_session.rb +4 -0
- data/lib/eco/api/common/session/environment.rb +5 -0
- data/lib/eco/api/custom/cli.rb +3 -0
- data/lib/eco/api/session/config/api.rb +33 -14
- data/lib/eco/api/usecases/CLAUDE.md +78 -0
- data/lib/eco/api/usecases/graphql/CLAUDE.md +120 -0
- data/lib/eco/api/usecases/graphql/compat/ooze_redirect.rb +13 -2
- data/lib/eco/api/usecases/graphql/helpers/CLAUDE.md +79 -0
- data/lib/eco/api/usecases/graphql/helpers/access_logs/base/reader.rb +59 -0
- data/lib/eco/api/usecases/graphql/helpers/access_logs/base.rb +17 -0
- data/lib/eco/api/usecases/graphql/helpers/access_logs.rb +7 -0
- data/lib/eco/api/usecases/graphql/helpers/base/connection_reader.rb +70 -0
- data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +6 -2
- data/lib/eco/api/usecases/graphql/helpers/base.rb +1 -0
- data/lib/eco/api/usecases/graphql/helpers/contractors/base/manager_settings.rb +64 -0
- data/lib/eco/api/usecases/graphql/helpers/contractors/base.rb +2 -0
- data/lib/eco/api/usecases/graphql/helpers/dashboards/base/reader.rb +30 -0
- data/lib/eco/api/usecases/graphql/helpers/dashboards/base.rb +20 -0
- data/lib/eco/api/usecases/graphql/helpers/dashboards.rb +7 -0
- data/lib/eco/api/usecases/graphql/helpers/pages/activities.rb +51 -0
- data/lib/eco/api/usecases/graphql/helpers/pages.rb +2 -1
- data/lib/eco/api/usecases/graphql/helpers.rb +2 -0
- data/lib/eco/api/usecases/graphql/samples/CLAUDE.md +76 -0
- data/lib/eco/api/usecases/graphql/samples/contractors/dsl.rb +16 -0
- data/lib/eco/api/usecases/graphql/samples/pages/CLAUDE.md +59 -0
- data/lib/eco/api/usecases/graphql/samples/pages/template/base.rb +46 -37
- data/lib/eco/api/usecases/graphql/samples/pages/template.rb +1 -1
- data/lib/eco/version.rb +1 -1
- metadata +22 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c1f4723827d6dabfa094fd943b573a00bfe19c6d16b4ea0cb528c01d23d74d0
|
|
4
|
+
data.tar.gz: 6cb1e6109c1d4150f2a52c0359941b02be893065cc6b732932af117ad6bbd08c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 515a129e3e75c42d0c16de5154a0e06e7d5854b65e766cf7e144b7097b6e55f2d2db5130ee05828f3c672da57f443517198816df18523ff952aefd8d93f9a645
|
|
7
|
+
data.tar.gz: aa5c685e34346dfe3bc41c006c89790eba83470327d950eeb531b0416b14228616d8cee57ba6d9f82d351db5ef3d4d6aa6a45aa147c6702821a1454a730fe879
|
data/CHANGELOG.md
CHANGED
|
@@ -2,45 +2,98 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## [3.
|
|
5
|
+
## [3.3.0] - 2026-08-13
|
|
6
|
+
|
|
7
|
+
Adopts **`ecoportal-api-graphql` 2.0.0** (published 2026-08-13): floor raised from
|
|
8
|
+
`~> 1.3, >= 1.3.14` to `~> 2.0, >= 2.0.0`. Minor rather than major here — 2.0.0's breaking set
|
|
9
|
+
removed surfaces that either targeted mutations the live schema never had or were dead accessors,
|
|
10
|
+
and a sweep of this repo finds **no consumer of any of them** (`editPage`,
|
|
11
|
+
`editPageCreatorPermissions`, the AI-summary mutations, the SmartFill pair — zero hits outside
|
|
12
|
+
this repo's own readiness notes). Nothing in `lib/` changes behaviour as a result of the bump.
|
|
13
|
+
|
|
14
|
+
★ **What starts WORKING, which matters more here than what broke.** Several paths were sending
|
|
15
|
+
documents the schema rejected, so they always errored; on 2.0.0 they really write. For this gem
|
|
16
|
+
the one to watch is the **`OozeRedirect` force path** — malformed `executeWorkflowCommands` until
|
|
17
|
+
graphql 1.3.15 fixed it, so its behaviour flips from "always errored" to "really writes" with no
|
|
18
|
+
signature change to warn anyone. **Re-verify it on a sandbox org before trusting it in a customer
|
|
19
|
+
run.** Also newly live via the gem: the page-level `executeWorkflowCommands` bus, File-field
|
|
20
|
+
attach writes, `updatePresetView` column edits, viewable-field management and Date-field renewals.
|
|
21
|
+
|
|
22
|
+
Consumers pinned to `~> 3.2` do not pick this up automatically, so adopting graphql 2.0.0 stays a
|
|
23
|
+
deliberate per-repo decision.
|
|
24
|
+
|
|
25
|
+
★ **The published gem is now `lib/` only.** `spec.files` excluded just `test|spec|features`, so
|
|
26
|
+
every top-level directory added since shipped by default — `.ai-assistance/` internal notes,
|
|
27
|
+
`docs/` (including the rolling worklog) and `.claude/`. `spec.files` is now an allowlist
|
|
28
|
+
(`lib/`, `exe/`, README, LICENSE, CHANGELOG). All `lib/` files are unchanged; nothing outside
|
|
29
|
+
`lib/` was ever loaded at runtime. Same fix as `ecoportal-api-graphql` 2.0.0.
|
|
30
|
+
|
|
31
|
+
## [3.2.20] - 2026-08-11
|
|
32
|
+
|
|
33
|
+
Reunites the released `3.2.19` hotfix line with `master`. `3.2.19` was cut from the `v3.2.18` tag
|
|
34
|
+
and **never merged back**, so two halves were split across branches: `master` carried
|
|
35
|
+
`Eco::API::Custom::Cli` (unreleased since 2026-07-25, and the reason `mns/config/cli.rb` could not
|
|
36
|
+
load on any published gem) while `3.2.19` carried the ooze KPI counter fix and the graphql floor.
|
|
37
|
+
This release carries both. Dependency floors are the max of each side.
|
|
38
|
+
|
|
39
|
+
### Native GraphQL activity/dashboard readers + contractor manager-settings write
|
|
40
|
+
|
|
41
|
+
Additive, all **NATIVE** (no `Compat::` layer). Depends on the `ecoportal-api-graphql`
|
|
42
|
+
`feat/graphql-activity-logs` branch (polymorphic `activityLogs` / `page.activities` models);
|
|
43
|
+
during development it is wired via a temporary Gemfile `path:` override that a gem version bump
|
|
44
|
+
replaces on release.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- `ecoportal-api` dependency floor raised to `0.10.17`: inherits the `WithRetry` fix making
|
|
49
|
+
`HTTP::TimeoutError` retryable (transient timeouts no longer kill batch loops).
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **Shared connection-reader substrate** — `Helpers::Base::ConnectionReader`: an `Enumerable`
|
|
54
|
+
mixin that wraps the gem's lazy `QueryConnection#each` (cursor pagination over
|
|
55
|
+
`pageInfo.endCursor`/`hasNextPage`) behind a uniform `each` / `to_a(limit:)` / `page_size`
|
|
56
|
+
surface. All new readers build on it.
|
|
57
|
+
- **System Access Logs reader** — `Helpers::AccessLogs::Base#access_logs` over
|
|
58
|
+
`currentOrganization.activityLogs`, exposing the full filter set (user_ids, resource_ids,
|
|
59
|
+
filter_superusers, activity_type, resource_type, source, date_filter) → yields `Model::ActivityLog`.
|
|
60
|
+
- **Page history reader** — `Helpers::Pages::Activities#page_activities` over `page(id:).activities`
|
|
61
|
+
(page id + `sorters:`/`search_conf:` + lazy pagination) → yields `Model::Activity`.
|
|
62
|
+
- **Dashboards reader** — `Helpers::Dashboards::Base#dashboards` over `dashboardSearch` → yields
|
|
63
|
+
`Model::MinimalDashboard`.
|
|
64
|
+
- **Contractor manager-settings WRITE** — `Helpers::Contractors::Base::ManagerSettings#update_contractor_manager_settings`
|
|
65
|
+
(also exposed via `Samples::Contractors::DSL#update_manager_settings`), driving the gem's
|
|
66
|
+
`Builder::ContractorEntity#update_manager_settings`. Guarded: **dry-run/simulate** short-circuits
|
|
67
|
+
before any mutation (returns the intended input); **-no-email** discipline honoured
|
|
68
|
+
(`options.workflow.no_email` or explicit `no_email:` suppresses the helper's own notification).
|
|
69
|
+
- **Thin live drivers** under `tests/` (read-only for the three readers; dry-run-only for the
|
|
70
|
+
contractor write) plus a shared `tests/env_graphql.rb` harness.
|
|
71
|
+
- Offline specs for all of the above (23 examples).
|
|
72
|
+
- `Eco::API::Common::Loaders::CliConfig` exposed via `Eco::API::Custom::Cli`.
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
|
|
76
|
+
- **`Template::Base` could never run — `CommandEmitter` was unresolvable.** The class was declared
|
|
77
|
+
with the compact `class Template::Base` form inside `module ...::Pages`, which leaves `Template`
|
|
78
|
+
out of the file's lexical scope, so the bare `CommandEmitter` reference in `#desired_commands`
|
|
79
|
+
raised `NameError` on every call — meaning every build-from-scratch template use case failed
|
|
80
|
+
before emitting a single command. Now nested as `module Template` + `class Base`, matching the
|
|
81
|
+
sibling files (`command_emitter.rb`, `csv_build/builder.rb`) whose bare cross-references already
|
|
82
|
+
worked for exactly that reason. Public constant path is unchanged. Found by the first spec ever
|
|
83
|
+
written against this class.
|
|
6
84
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
`CLAUDE.md` files that 3.2.22 packaged under `lib/` no longer ship.
|
|
16
|
-
- Packaging: the three runtime JSON files are declared in `.release-smoke-allow`, and the release
|
|
17
|
-
tasks accept `release/*` maintenance branches.
|
|
18
|
-
|
|
19
|
-
## [3.2.22] - 2026-08-14
|
|
20
|
-
|
|
21
|
-
### Fixed
|
|
22
|
-
|
|
23
|
-
- Republish of `3.2.21`: its `lib/eco/version.rb` shipped with doubled carriage returns
|
|
24
|
-
(`
|
|
25
|
-
` line endings), causing a `warning: encountered in middle of line` on every
|
|
26
|
-
load. Functionally identical otherwise; `3.2.21` will be yanked.
|
|
27
|
-
|
|
28
|
-
## [3.2.21] - 2026-08-14
|
|
29
|
-
|
|
30
|
-
### Fixed
|
|
31
|
-
|
|
32
|
-
- Packaging-only republish of `3.2.19` with an allowlisted gemspec (backported from `3.3.0`).
|
|
33
|
-
Versions `3.2.16`-`3.2.19` shipped internal repository content (the repo's internal docs tooling,
|
|
34
|
-
`.claude/settings.json`) to rubygems.org via the old denylist `spec.files`; `3.2.19` is
|
|
35
|
-
yanked and `3.2.16`/`3.2.18` are queued for deletion by RubyGems support. `3.2.21` is the
|
|
36
|
-
identical `lib/` code packaged clean, so constraints like `'~> 3.2.0', '>= 3.2.19'` keep
|
|
37
|
-
resolving on the 3.2 line (its graphql dependency stays `~> 1.3`, satisfied by the clean
|
|
38
|
-
`1.3.16`). The version number `3.2.20` is intentionally skipped: it exists as a
|
|
39
|
-
tagged-but-deliberately-unpublished version (see the 3.3.0-era changelog corrections).
|
|
85
|
+
- **Ooze update KPI counters now count GraphQL updates.** `RegisterUpdateCase` tallied
|
|
86
|
+
`updated`/`failed` only when the result `is_a?(Ecoportal::API::Common::Response)`, but the
|
|
87
|
+
GraphQL compat layer returns an `Ecoportal::API::GraphQL::Compat::Response` (duck-types
|
|
88
|
+
`success?`/`status`, not in that class hierarchy) — so GraphQL updates were silently
|
|
89
|
+
uncounted (`Updated 0 (attempted: N)`, `Failed 0`) even when the write applied. Guard is now
|
|
90
|
+
`respond_to?(:success?)`; `false`/`nil` (dry-run / no-op) still skip. Inherited by
|
|
91
|
+
`TargetOozesUpdateCase` → TOOCS / cans-upsert. Also shipped as the maintenance release
|
|
92
|
+
`3.2.19` off `v3.2.18`; regression spec added.
|
|
40
93
|
|
|
41
94
|
## [3.2.19] - 2026-07-16
|
|
42
95
|
|
|
43
|
-
|
|
96
|
+
Farmers / `cans-upsert` reliability adoption + an ooze KPI counter fix. **Backwards-compatible.**
|
|
44
97
|
Cut from the `v3.2.18` tag (not `master`, which carries the native GraphQL activity/dashboard
|
|
45
98
|
readers depending on the unreleased gem `1.4.0`), so this ships needing only the published
|
|
46
99
|
`ecoportal-api-graphql 1.3.14`.
|
|
@@ -73,7 +126,7 @@ guard so the dead-fragment class of bug can't reach production again.
|
|
|
73
126
|
on 2026-07-04 (`243822b9`), then the live-crash fix `352a9657` (LocationDraft dead-fragment
|
|
74
127
|
convention) landed on 2026-07-05 — keeping the same `3.2.17` label. Because `3.2.17` is installed
|
|
75
128
|
from git/path (unpublished), a consumer bundled in that ~26h window reports `3.2.17` yet lacks the
|
|
76
|
-
fix. This crashed the **live
|
|
129
|
+
fix. This crashed the **live act-gov** org-structure sync again on 2026-07-09 with the exact
|
|
77
130
|
`uninitialized constant …Fragment::LocationDraft (NameError)`. Bumping to `3.2.18` makes the fixed
|
|
78
131
|
build unambiguously identifiable: any consumer on `>= 3.2.18` provably has the fix.
|
|
79
132
|
|
|
@@ -82,7 +135,7 @@ build unambiguously identifiable: any consumer on `>= 3.2.18` provably has the f
|
|
|
82
135
|
- **Regression-guard spec for the dead-fragment convention.**
|
|
83
136
|
`spec/…/helpers/location/command/end_points/optimizations_spec.rb` scans every file under
|
|
84
137
|
`usecases/graphql/` and fails if any references a fragment via the removed `___Const__Fragment` /
|
|
85
|
-
`::Fragment::<Name>` constant convention (the exact NameError that crashed
|
|
138
|
+
`::Fragment::<Name>` constant convention (the exact NameError that crashed act-gov), and asserts
|
|
86
139
|
the three Location command payload procs still route fragments through the `spread :Name` registry
|
|
87
140
|
DSL. It is a SOURCE lint, not a full offline render: rendering needs the graphlient fork's
|
|
88
141
|
`to_query_string`/`spread` DSL, but eco-helpers' own bundle resolves stock graphlient `0.8.0` (the
|
|
@@ -107,7 +160,7 @@ to the gem's `Diff` module (gem v1.3.11), which is tagged but unpublished.
|
|
|
107
160
|
`___Const__Fragment` constant convention (`___Ecoportal__API__GraphQL__Fragment__LocationDraft` /
|
|
108
161
|
`…__LocationsError`), which the gem's registry-based fragments no longer expose as Ruby constants →
|
|
109
162
|
`uninitialized constant … Fragment::LocationDraft (NameError)` when building a locations-draft
|
|
110
|
-
`addCommands`/`create`/`publish` request. Crashed
|
|
163
|
+
`addCommands`/`create`/`publish` request. Crashed the live act-gov tagtree / org-structure sync.
|
|
111
164
|
Now `spread :LocationDraft` / `spread :LocationsError`, matching the gem.
|
|
112
165
|
|
|
113
166
|
### Dependencies
|
|
@@ -115,7 +168,7 @@ to the gem's `Diff` module (gem v1.3.11), which is tagged but unpublished.
|
|
|
115
168
|
- **Raised floors to the fixed stack: `ecoportal-api-graphql >= 1.3.11`, `ecoportal-api-v2 >= 3.3.3`.**
|
|
116
169
|
Stops the stale gem 1.3.9 (missing the LocationStructure `updatedAt`/`createdAt` selection fixes from
|
|
117
170
|
1.3.10) and the buggy v2 3.3.2 (Ruby-3.x `DoubleModel` cascade `TypeError`) from resolving on
|
|
118
|
-
consumers — both crashed
|
|
171
|
+
consumers — both crashed the live act-gov integration.
|
|
119
172
|
|
|
120
173
|
### Added
|
|
121
174
|
|
|
@@ -63,6 +63,11 @@ module Eco::API::Common::Session
|
|
|
63
63
|
config.apis.active_api.version_available?(version)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# @return [Array<Symbol>] required parameters for `version` that are missing.
|
|
67
|
+
def missing_api_params(version:)
|
|
68
|
+
config.apis.active_api.missing_api_params(version)
|
|
69
|
+
end
|
|
70
|
+
|
|
66
71
|
# Shortcut to logger.
|
|
67
72
|
def log(level, &block)
|
|
68
73
|
return unless logger.respond_to?(:level)
|
|
@@ -104,8 +104,10 @@ module Eco
|
|
|
104
104
|
return current if current && !switch_logger
|
|
105
105
|
|
|
106
106
|
unless api_params?(version)
|
|
107
|
+
missing = missing_api_params(version)
|
|
107
108
|
msg = "The api configuration for #{description} "
|
|
108
|
-
msg << "is missing data for the api version '#{self.version(version)}'"
|
|
109
|
+
msg << "is missing data for the api version '#{self.version(version)}': "
|
|
110
|
+
msg << "missing params #{missing.map {|param| ":#{param}"}.join(', ')}"
|
|
109
111
|
raise ArgumentError, msg
|
|
110
112
|
end
|
|
111
113
|
|
|
@@ -196,6 +198,12 @@ module Eco
|
|
|
196
198
|
api_params?(version)
|
|
197
199
|
end
|
|
198
200
|
|
|
201
|
+
# @return [Array<Symbol>] the required parameters for `version` that are
|
|
202
|
+
# currently missing (empty when all of them are present).
|
|
203
|
+
def missing_api_params(version)
|
|
204
|
+
required_api_params(version).reject {|_param, value| value}.keys
|
|
205
|
+
end
|
|
206
|
+
|
|
199
207
|
def version(value = nil)
|
|
200
208
|
self.class.to_version(value || self['version'])
|
|
201
209
|
end
|
|
@@ -224,15 +232,18 @@ module Eco
|
|
|
224
232
|
when :v2
|
|
225
233
|
klass.new(user_key: user_key, org_key: external_key, host: host, logger: logger)
|
|
226
234
|
when :graphql
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
235
|
+
# NOTE: `GRAPHQL_FETCH_SCHEMA` is a documented no-op. `Ecoportal::API::GraphQL#initialize`
|
|
236
|
+
# (email:, pass:, org_id:, host:, api_key:) has never accepted a `no_schema:` keyword —
|
|
237
|
+
# it always builds its internal client with `no_schema: true` and has no public way to
|
|
238
|
+
# turn schema introspection on. Passing the unknown keyword raised ArgumentError, so the
|
|
239
|
+
# flag was worse than a no-op: setting `GRAPHQL_FETCH_SCHEMA=false` crashed api() instead
|
|
240
|
+
# of skipping a fetch that the gem's public API never performs regardless.
|
|
241
|
+
if ENV['GRAPHQL_FETCH_SCHEMA'] == "false"
|
|
242
|
+
warn "[eco-helpers] GRAPHQL_FETCH_SCHEMA is a no-op: " \
|
|
243
|
+
"Ecoportal::API::GraphQL never fetches the schema (no_schema: true, hardcoded)."
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
klass.new(host: host, org_id: org_id, email: email, pass: pass)
|
|
236
247
|
end.tap do |api|
|
|
237
248
|
next unless api
|
|
238
249
|
next if log_connection? # prevent over-logging
|
|
@@ -248,15 +259,23 @@ module Eco
|
|
|
248
259
|
|
|
249
260
|
# Checks if the necessary parameters for a specific `API` version are available.
|
|
250
261
|
def api_params?(version)
|
|
262
|
+
missing_api_params(version).empty?
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Required parameters (as `name => value` pairs) for a specific `API` version.
|
|
266
|
+
# @note `to_version` always normalises to one of the known versions below.
|
|
267
|
+
def required_api_params(version)
|
|
251
268
|
case self.class.to_version(version)
|
|
252
269
|
when :v0
|
|
253
|
-
internal_key
|
|
270
|
+
{internal_key: internal_key, host: host}
|
|
254
271
|
when :v1
|
|
255
|
-
external_key
|
|
272
|
+
{external_key: external_key, host: host}
|
|
256
273
|
when :v2
|
|
257
|
-
external_key
|
|
274
|
+
{external_key: external_key, user_key: user_key, host: host}
|
|
258
275
|
when :graphql
|
|
259
|
-
email
|
|
276
|
+
{email: email, pass: pass, org_id: org_id, host: host}
|
|
277
|
+
else
|
|
278
|
+
{}
|
|
260
279
|
end
|
|
261
280
|
end
|
|
262
281
|
|
|
@@ -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,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.
|
|
@@ -62,8 +62,19 @@ module Eco::API::UseCases::GraphQL::Compat
|
|
|
62
62
|
# force.custom_script = new_script → write the LISP script
|
|
63
63
|
# force.script → raw script content (alias)
|
|
64
64
|
#
|
|
65
|
-
# Affected cases currently blocked
|
|
66
|
-
#
|
|
65
|
+
# Affected cases currently blocked (from multi_org_api survey, ~50% of all
|
|
66
|
+
# ooze cases):
|
|
67
|
+
# act-gov: 5 x 20240130_act_*_case, rearrage_page_sites_case
|
|
68
|
+
# briscoes: remove_induction_sections, 310524_Briscoes_Remove_Tasks
|
|
69
|
+
# chorus: 4 x audit_update cases
|
|
70
|
+
# hcc: update_enterprise_risk_case
|
|
71
|
+
# lic: update_life_cycle_force_case
|
|
72
|
+
# mitre10: rich_text_update, update_location_force, updating_template
|
|
73
|
+
# npdc: contractor_title_force, risk_titile_force, fix_title_syncing,
|
|
74
|
+
# reminder_date_fields, 10092024_NPDC_CP_Add_Force
|
|
75
|
+
# profile-group: int_training_review, 20231026_profile_wellness
|
|
76
|
+
# turners-growers: event_changes, inj_cost_calc, remove_line_force
|
|
77
|
+
# twg: hide_attached_risks, add_new_force
|
|
67
78
|
#
|
|
68
79
|
# Implementation sketch (to be built when the endpoint lands):
|
|
69
80
|
#
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# usecases/graphql/helpers
|
|
2
|
+
|
|
3
|
+
Mixin modules providing domain-specific helper methods for GraphQL use cases.
|
|
4
|
+
All modules are ultimately included via `Helpers::Base` into `GraphQL::Base`.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Include chain
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
GraphQL::Base
|
|
12
|
+
includes Helpers::Base
|
|
13
|
+
includes CaseEnv → session, options, config, simulate?, log, ErrorHandling
|
|
14
|
+
includes GraphQLEnv → graphql (lazy, memoized)
|
|
15
|
+
includes Helpers (loader)
|
|
16
|
+
includes Helpers::Location → location tree helpers
|
|
17
|
+
includes Helpers::Contractors → contractor entity helpers
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Helpers::Base (`helpers/base.rb`)
|
|
23
|
+
|
|
24
|
+
Core environment — included in every GraphQL use case.
|
|
25
|
+
|
|
26
|
+
| Method | Source | Description |
|
|
27
|
+
|--------|--------|-------------|
|
|
28
|
+
| `session` | `CaseEnv` | Current `Eco::API::Session` |
|
|
29
|
+
| `options` | `CaseEnv` | Options hash from CLI/runner |
|
|
30
|
+
| `config` | `CaseEnv` | `session.config` shortcut |
|
|
31
|
+
| `simulate?` | `CaseEnv` | `options[:simulate] \|\| options[:dry_run]` |
|
|
32
|
+
| `log(level)` | `CaseEnv` | Logger proxy |
|
|
33
|
+
| `graphql` | `GraphQLEnv` | Lazy-loaded `Ecoportal::API::GraphQL` instance |
|
|
34
|
+
| `backup(data, type:)` | `Helpers::Base` | Save JSON to requests folder |
|
|
35
|
+
| `exit_error(msg)` | `Helpers::Base` | Log error and `exit(1)` |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Helpers::Location (`helpers/location/`)
|
|
40
|
+
|
|
41
|
+
Location tree access, tag remapping, classification parsing.
|
|
42
|
+
|
|
43
|
+
- `helpers/location/base.rb` — `Location::Base` mixin
|
|
44
|
+
- `helpers/location/base/tree_tracking.rb` — track tree mutations
|
|
45
|
+
- `helpers/location/base/classifications_parser.rb` — parse location classifications
|
|
46
|
+
- `helpers/location/tags_remap/` — remapping tags across location changes
|
|
47
|
+
- `helpers/location/command/` — apply/diff location structure commands
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Helpers::Contractors (`helpers/contractors/`)
|
|
52
|
+
|
|
53
|
+
Contractor entity loading helpers.
|
|
54
|
+
|
|
55
|
+
- `helpers/contractors/base.rb` — base contractor helpers
|
|
56
|
+
- `helpers/contractors/base/load.rb` — batch load contractor entities
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Adding a new helper
|
|
61
|
+
|
|
62
|
+
1. Create `helpers/my_domain/base.rb`:
|
|
63
|
+
```ruby
|
|
64
|
+
module Eco::API::UseCases::GraphQL::Helpers
|
|
65
|
+
module MyDomain
|
|
66
|
+
module Base
|
|
67
|
+
private
|
|
68
|
+
def my_helper_method
|
|
69
|
+
graphql.myDomainQuery(...)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
2. Create `helpers/my_domain.rb` as a loader that includes `Base`
|
|
76
|
+
3. Add `require_relative 'my_domain'` to `helpers.rb`
|
|
77
|
+
|
|
78
|
+
The helper is then available in all cases that include `Helpers::Base` (i.e., all
|
|
79
|
+
subclasses of `GraphQL::Base` including `PageCase` and `OrgPageCase`).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Eco::API::UseCases::GraphQL::Helpers::AccessLogs
|
|
2
|
+
module Base
|
|
3
|
+
# NATIVE reader over `currentOrganization.activityLogs` (the ActivityLogUnion behind the web
|
|
4
|
+
# "System Access Logs" page). Lazily paginates and yields `Model::ActivityLog` records.
|
|
5
|
+
#
|
|
6
|
+
# Exposes the full server filter set. Construct via `Helpers::AccessLogs::Base#access_logs`:
|
|
7
|
+
# access_logs(source: 'web', resource_type: 'view_dashboard').each { |log| ... }
|
|
8
|
+
# access_logs(date_filter: { from: from_iso, to: to_iso }).to_a(limit: 500)
|
|
9
|
+
class Reader
|
|
10
|
+
include Eco::API::UseCases::GraphQL::Helpers::Base::ConnectionReader
|
|
11
|
+
|
|
12
|
+
# @param graphql [Ecoportal::API::GraphQL] the active gem client.
|
|
13
|
+
# @param user_ids [Array<String>, nil]
|
|
14
|
+
# @param resource_ids [Array<String>, nil]
|
|
15
|
+
# @param filter_superusers [Boolean, nil]
|
|
16
|
+
# @param activity_type [String, nil] free-text activity type filter.
|
|
17
|
+
# @param resource_type [String, nil] ActivityLogTypesEnum value (e.g. 'view_dashboard').
|
|
18
|
+
# @param source [String, nil] SourceEnum value (e.g. 'web', 'api').
|
|
19
|
+
# @param date_filter [Hash, nil] `{ from:, to: }` (ISO8601), maps to DateRange.
|
|
20
|
+
def initialize(
|
|
21
|
+
graphql,
|
|
22
|
+
user_ids: nil,
|
|
23
|
+
resource_ids: nil,
|
|
24
|
+
filter_superusers: nil,
|
|
25
|
+
activity_type: nil,
|
|
26
|
+
resource_type: nil,
|
|
27
|
+
source: nil,
|
|
28
|
+
date_filter: nil
|
|
29
|
+
)
|
|
30
|
+
@graphql = graphql
|
|
31
|
+
@user_ids = user_ids
|
|
32
|
+
@resource_ids = resource_ids
|
|
33
|
+
@filter_superusers = filter_superusers
|
|
34
|
+
@activity_type = activity_type
|
|
35
|
+
@resource_type = resource_type
|
|
36
|
+
@source = source
|
|
37
|
+
@date_filter = date_filter
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def connection_query
|
|
43
|
+
@graphql.currentOrganization.activityLogs
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def reader_params
|
|
47
|
+
{
|
|
48
|
+
userIds: @user_ids,
|
|
49
|
+
resourceIds: @resource_ids,
|
|
50
|
+
filterSuperusers: @filter_superusers,
|
|
51
|
+
activityType: @activity_type,
|
|
52
|
+
resourceType: @resource_type,
|
|
53
|
+
source: @source,
|
|
54
|
+
dateFilter: @date_filter
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|