eco-helpers 3.2.19 → 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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -0
  3. data/lib/eco/api/common/session/base_session.rb +4 -0
  4. data/lib/eco/api/common/session/environment.rb +5 -0
  5. data/lib/eco/api/custom/cli.rb +3 -0
  6. data/lib/eco/api/session/config/api.rb +33 -14
  7. data/lib/eco/api/usecases/graphql/helpers/access_logs/base/reader.rb +59 -0
  8. data/lib/eco/api/usecases/graphql/helpers/access_logs/base.rb +17 -0
  9. data/lib/eco/api/usecases/graphql/helpers/access_logs.rb +7 -0
  10. data/lib/eco/api/usecases/graphql/helpers/base/connection_reader.rb +70 -0
  11. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +6 -2
  12. data/lib/eco/api/usecases/graphql/helpers/base.rb +1 -0
  13. data/lib/eco/api/usecases/graphql/helpers/contractors/base/manager_settings.rb +64 -0
  14. data/lib/eco/api/usecases/graphql/helpers/contractors/base.rb +2 -0
  15. data/lib/eco/api/usecases/graphql/helpers/dashboards/base/reader.rb +30 -0
  16. data/lib/eco/api/usecases/graphql/helpers/dashboards/base.rb +20 -0
  17. data/lib/eco/api/usecases/graphql/helpers/dashboards.rb +7 -0
  18. data/lib/eco/api/usecases/graphql/helpers/pages/activities.rb +51 -0
  19. data/lib/eco/api/usecases/graphql/helpers/pages.rb +1 -0
  20. data/lib/eco/api/usecases/graphql/helpers.rb +2 -0
  21. data/lib/eco/api/usecases/graphql/samples/contractors/dsl.rb +16 -0
  22. data/lib/eco/api/usecases/graphql/samples/pages/template/base.rb +46 -37
  23. data/lib/eco/version.rb +1 -1
  24. metadata +17 -27
  25. data/.ai-assistance/conventions/code-working-tree-protocol.md +0 -176
  26. data/.ai-assistance/scripts/token-logger.js +0 -220
  27. data/.ai-assistance/scripts/token-report.ts +0 -158
  28. data/.ai-assistance/scripts/token-session-start.js +0 -66
  29. data/.ai-assistance/skills/ep-ai-manager/SKILL.md +0 -417
  30. data/.ai-assistance/skills/ruby-scripting/SKILL.md +0 -215
  31. data/.ai-assistance/standards-version.json +0 -10
  32. data/.ai-assistance/token-budget.json +0 -39
  33. data/.claude/settings.json +0 -103
  34. data/.gitignore +0 -25
  35. data/.idea/.gitignore +0 -10
  36. data/.markdownlint.json +0 -4
  37. data/.rspec +0 -3
  38. data/.rubocop.yml +0 -103
  39. data/.ruby-version +0 -1
  40. data/.yardopts +0 -10
  41. data/CLAUDE.md +0 -83
  42. data/Gemfile +0 -8
  43. data/Rakefile +0 -38
  44. data/eco-helpers.gemspec +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e64f4c0a2c6dc0b63299469ad23910bd227a3a8aa95f040f8cf0c9019baeccea
4
- data.tar.gz: d34e7ef09189ef29233f81263399f5af0b2fb36fdb86365427449c9da9c75fef
3
+ metadata.gz: 2c1f4723827d6dabfa094fd943b573a00bfe19c6d16b4ea0cb528c01d23d74d0
4
+ data.tar.gz: 6cb1e6109c1d4150f2a52c0359941b02be893065cc6b732932af117ad6bbd08c
5
5
  SHA512:
6
- metadata.gz: f334bce839ff57df6dedcd64891bc2b4ddb230419d617746e8c65ab4033ca7df2d65c5b0716aa823b43625138a2f9e829a060cabba4f71b02a444661e72652e1
7
- data.tar.gz: 047b80c9620899819844f1e2822fe991487b41b0f05b440836fbbccfda8a190b0f9da2ee8a19841852fe7e3ef3d15edb600c81aac5c350341e52e715ad926f3b
6
+ metadata.gz: 515a129e3e75c42d0c16de5154a0e06e7d5854b65e766cf7e144b7097b6e55f2d2db5130ee05828f3c672da57f443517198816df18523ff952aefd8d93f9a645
7
+ data.tar.gz: aa5c685e34346dfe3bc41c006c89790eba83470327d950eeb531b0416b14228616d8cee57ba6d9f82d351db5ef3d4d6aa6a45aa147c6702821a1454a730fe879
data/CHANGELOG.md CHANGED
@@ -2,6 +2,95 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
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.
84
+
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.
93
+
5
94
  ## [3.2.19] - 2026-07-16
6
95
 
7
96
  Farmers / `cans-upsert` reliability adoption + an ooze KPI counter fix. **Backwards-compatible.**
@@ -36,6 +36,10 @@ module Eco::API::Common::Session
36
36
  enviro.api?(version: version)
37
37
  end
38
38
 
39
+ def missing_api_params(version: nil)
40
+ enviro.missing_api_params(version: version)
41
+ end
42
+
39
43
  def file_manager
40
44
  enviro.file_manager
41
45
  end
@@ -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)
@@ -0,0 +1,3 @@
1
+ # Helper class to create a custom `Cli` config
2
+ class Eco::API::Custom::Cli < Eco::API::Common::Loaders::CliConfig
3
+ end
@@ -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
- kargs = {
228
- host: host,
229
- org_id: org_id,
230
- email: email,
231
- pass: pass
232
- }
233
-
234
- kargs.merge!({no_schema: true}) if ENV['GRAPHQL_FETCH_SCHEMA'] == "false"
235
- klass.new(**kargs)
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 && host
270
+ {internal_key: internal_key, host: host}
254
271
  when :v1
255
- external_key && host
272
+ {external_key: external_key, host: host}
256
273
  when :v2
257
- external_key && user_key && host
274
+ {external_key: external_key, user_key: user_key, host: host}
258
275
  when :graphql
259
- email && pass && org_id && host
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,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
@@ -0,0 +1,17 @@
1
+ require_relative 'base/reader'
2
+
3
+ module Eco::API::UseCases::GraphQL::Helpers::AccessLogs
4
+ # System Access Logs helper mixin — NATIVE reader over `activityLogs`.
5
+ module Base
6
+ include Eco::API::UseCases::GraphQL::Helpers::Base
7
+
8
+ private
9
+
10
+ # Build a lazy reader over the org's system access logs (ActivityLogUnion).
11
+ # @see Reader#initialize for the supported filters.
12
+ # @return [Reader]
13
+ def access_logs(**filters)
14
+ Reader.new(graphql, **filters)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers
2
+ # NATIVE System Access Logs readers (currentOrganization.activityLogs).
3
+ module AccessLogs
4
+ end
5
+ end
6
+
7
+ require_relative 'access_logs/base'
@@ -0,0 +1,70 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Base
2
+ # Shared substrate for NATIVE GraphQL connection readers.
3
+ #
4
+ # The gem's `Logic::QueryConnection` is already `Enumerable` and paginates lazily over
5
+ # `first`/`after` using `pageInfo.endCursor`/`hasNextPage` (see its `#each`). This mixin wraps
6
+ # that machinery behind a small, uniform reader surface so every native reader (access logs,
7
+ # page activities, dashboards, ...) exposes the same `each` / `to_a` / `first(n)` behaviour and
8
+ # a consistent `page_size` without re-implementing cursor pagination.
9
+ #
10
+ # A concrete reader supplies a `#query` (a gem query object, e.g.
11
+ # `graphql.currentOrganization.activityLogs`) and the per-call filter/argument hash via
12
+ # `#reader_params`. Everything else (lazy iteration, batching, materialising) is inherited.
13
+ module ConnectionReader
14
+ include Enumerable
15
+
16
+ # Default number of nodes fetched per network round-trip.
17
+ DEFAULT_PAGE_SIZE = 100
18
+
19
+ # Iterate over every node across all pages (lazy — one network page at a time).
20
+ # @yieldparam node [Object] a gem read model (e.g. Model::ActivityLog / Model::Activity).
21
+ # @return [Enumerator] when no block is given.
22
+ def each(&block)
23
+ return to_enum(:each) unless block
24
+
25
+ connection_query.each(**each_params, &block)
26
+ end
27
+
28
+ # Materialise a bounded number of nodes without draining the whole connection.
29
+ # @param limit [Integer, nil] maximum nodes to return; nil = all pages.
30
+ # @return [Array]
31
+ def to_a(limit: nil)
32
+ return super() if limit.nil?
33
+
34
+ take(limit)
35
+ end
36
+
37
+ # @return [Integer] nodes fetched per page.
38
+ def page_size
39
+ @page_size ||= DEFAULT_PAGE_SIZE
40
+ end
41
+
42
+ # @param size [Integer]
43
+ def page_size=(size)
44
+ @page_size = Integer(size)
45
+ end
46
+
47
+ private
48
+
49
+ # The gem query object to iterate (must respond to `#each(**kargs)` — i.e. a
50
+ # `Logic::QueryConnection`). Concrete readers MUST override.
51
+ # @return [Ecoportal::API::GraphQL::Logic::QueryConnection]
52
+ def connection_query
53
+ raise NotImplementedError, "#{self.class} must implement #connection_query"
54
+ end
55
+
56
+ # Per-call arguments (filters + page id + ...) merged into every page request.
57
+ # Concrete readers override to add their own filters. `first` defaults to `page_size`.
58
+ # @return [Hash]
59
+ def reader_params
60
+ {}
61
+ end
62
+
63
+ # Final kargs handed to the gem's `QueryConnection#each` — reader_params with a
64
+ # default `first`, and nils stripped so unset filters are not sent.
65
+ # @return [Hash]
66
+ def each_params
67
+ { first: page_size }.merge(reader_params).compact
68
+ end
69
+ end
70
+ end
@@ -6,8 +6,12 @@ module Eco::API::UseCases::GraphQL::Helpers::Base
6
6
  private
7
7
 
8
8
  def graphql
9
- msg = "The credentials or basic graphql config are missing for the active environment"
10
- raise msg unless session.api?(version: :graphql)
9
+ unless session.api?(version: :graphql)
10
+ missing = session.missing_api_params(version: :graphql)
11
+ msg = "The credentials or basic graphql config are missing for the active environment"
12
+ msg << " (missing params: #{missing.map {|param| ":#{param}"}.join(', ')})" if missing.any?
13
+ raise msg
14
+ end
11
15
 
12
16
  @graphql ||= session.api(version: :graphql)
13
17
  end
@@ -3,6 +3,7 @@ module Eco::API::UseCases::GraphQL::Helpers
3
3
  require_relative 'base/error_handling'
4
4
  require_relative 'base/case_env'
5
5
  require_relative 'base/graphql_env'
6
+ require_relative 'base/connection_reader'
6
7
 
7
8
  include CaseEnv
8
9
  include GraphQLEnv
@@ -0,0 +1,64 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Contractors
2
+ module Base
3
+ # NATIVE contractor **write** helper — `updateContractorManagerSettings`.
4
+ #
5
+ # Drives the gem's `Builder::ContractorEntity#update_manager_settings` to set the
6
+ # organisation's contractor-manager auto-approval settings and whether contractor admins
7
+ # may add other admins.
8
+ #
9
+ # Guards (aligned with the repo's write discipline):
10
+ # - **dry-run / simulate:** when `simulate?` (or `dry_run:` is passed), NO mutation is sent;
11
+ # the intended input is logged and returned so callers can inspect it.
12
+ # - **-no-email:** honoured — when email is suppressed (`options.dig(:workflow, :no_email)`
13
+ # or an explicit `no_email:`), the helper does not emit its own success notification. The
14
+ # server-side mutation itself sends no member email for this settings change.
15
+ module ManagerSettings
16
+ include Eco::API::UseCases::GraphQL::Helpers::Base
17
+
18
+ private
19
+
20
+ # Update the org's contractor manager settings.
21
+ # @param cm_auto_approvals_settings [Hash, nil] AutoApprovalsInput
22
+ # ({ enabled:, approvalConditions: }). Required by the schema for a real update.
23
+ # @param allow_contractor_admins_to_add_admins [Boolean, nil]
24
+ # @param dry_run [Boolean, nil] force simulate for this call (in addition to `simulate?`).
25
+ # @param no_email [Boolean, nil] suppress this helper's own notification for this call.
26
+ # @return [Hash] when simulated: the intended input hash. Otherwise: the mutation payload.
27
+ def update_contractor_manager_settings(
28
+ cm_auto_approvals_settings: nil,
29
+ allow_contractor_admins_to_add_admins: nil,
30
+ dry_run: nil,
31
+ no_email: nil
32
+ )
33
+ input = {}
34
+ input[:cmAutoApprovalsSettings] = cm_auto_approvals_settings unless cm_auto_approvals_settings.nil?
35
+ unless allow_contractor_admins_to_add_admins.nil?
36
+ input[:allowContractorAdminsToAddAdmins] =
37
+ allow_contractor_admins_to_add_admins
38
+ end
39
+
40
+ if dry_run || simulate?
41
+ log(:info) { "[dry-run] updateContractorManagerSettings input: #{input}" }
42
+ return input
43
+ end
44
+
45
+ graphql.contractorEntity.update_manager_settings(input: input).tap do
46
+ notify_manager_settings_updated(input) unless suppress_email?(no_email)
47
+ end
48
+ end
49
+
50
+ # @return [Boolean] whether this helper's own notification email is suppressed.
51
+ def suppress_email?(no_email)
52
+ return true if no_email
53
+ return true if respond_to?(:options, true) && options.dig(:workflow, :no_email)
54
+
55
+ false
56
+ end
57
+
58
+ # Hook for a success notification (kept minimal; overridable). Logs by default.
59
+ def notify_manager_settings_updated(input)
60
+ log(:info) { "Updated contractor manager settings: #{input}" }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,8 +1,10 @@
1
1
  require_relative 'base/load'
2
+ require_relative 'base/manager_settings'
2
3
 
3
4
  module Eco::API::UseCases::GraphQL::Helpers::Contractors
4
5
  module Base
5
6
  include Eco::API::UseCases::GraphQL::Helpers::Base
6
7
  include Load
8
+ include ManagerSettings
7
9
  end
8
10
  end
@@ -0,0 +1,30 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Dashboards
2
+ module Base
3
+ # NATIVE reader over `currentOrganization.dashboardSearch` (MinimalDashboardConnection).
4
+ # Lazily paginates and yields `Model::MinimalDashboard` records.
5
+ #
6
+ # Use via `Helpers::Dashboards::Base#dashboards`:
7
+ # dashboards(query: 'safety').each { |d| ... }
8
+ # dashboards.to_a
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 search_conf [Hash, nil] a `Search` input hash ({ query:, filters:, sorters: }).
14
+ def initialize(graphql, search_conf: nil)
15
+ @graphql = graphql
16
+ @search_conf = search_conf
17
+ end
18
+
19
+ private
20
+
21
+ def connection_query
22
+ @graphql.currentOrganization.dashboardSearch
23
+ end
24
+
25
+ def reader_params
26
+ { searchConf: @search_conf }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'base/reader'
2
+
3
+ module Eco::API::UseCases::GraphQL::Helpers::Dashboards
4
+ # Dashboards helper mixin — NATIVE reader over `dashboardSearch`.
5
+ module Base
6
+ include Eco::API::UseCases::GraphQL::Helpers::Base
7
+
8
+ private
9
+
10
+ # Build a lazy reader over the org's dashboards (MinimalDashboardConnection).
11
+ # @param query [String, nil] free-text search shorthand (→ search_conf { query: }).
12
+ # @param search_conf [Hash, nil] full Search input (takes precedence over `query`).
13
+ # @return [Reader]
14
+ def dashboards(query: nil, search_conf: nil)
15
+ conf = search_conf
16
+ conf ||= { query: query } if query
17
+ Reader.new(graphql, search_conf: conf)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers
2
+ # NATIVE dashboard readers (currentOrganization.dashboardSearch).
3
+ module Dashboards
4
+ end
5
+ end
6
+
7
+ require_relative 'dashboards/base'
@@ -0,0 +1,51 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Pages
2
+ # NATIVE reader over `page(id:).activities` (the ActivityInterface behind a page's history /
3
+ # changelog feed). Lazily paginates and yields `Model::Activity` records.
4
+ #
5
+ # Use via `Helpers::Pages::Activities#page_activities`:
6
+ # page_activities(page_id).each { |a| ... }
7
+ # page_activities(page_id, sorters: [{ key: 'updated_at', direction: 'desc' }]).to_a(limit: 50)
8
+ module Activities
9
+ # Lazy activity-feed reader for a single page.
10
+ class Reader
11
+ include Eco::API::UseCases::GraphQL::Helpers::Base::ConnectionReader
12
+
13
+ # @param graphql [Ecoportal::API::GraphQL] the active gem client.
14
+ # @param page_id [String] the page id whose activities to read.
15
+ # @param search_conf [Hash, nil] a `Search` input hash (typically `{ sorters: [...] }`).
16
+ # Defaults to the web feature's ordering: newest updates first.
17
+ def initialize(graphql, page_id, search_conf: nil)
18
+ @graphql = graphql
19
+ @page_id = page_id
20
+ @search_conf = search_conf || DEFAULT_SEARCH_CONF
21
+ end
22
+
23
+ DEFAULT_SEARCH_CONF = { sorters: [{ direction: 'desc', key: 'updated_at' }] }.freeze
24
+
25
+ private
26
+
27
+ def connection_query
28
+ @graphql.currentOrganization.pageActivities
29
+ end
30
+
31
+ def reader_params
32
+ { id: @page_id, searchConf: @search_conf }
33
+ end
34
+ end
35
+
36
+ include Eco::API::UseCases::GraphQL::Helpers::Base
37
+
38
+ private
39
+
40
+ # Build a lazy reader over a page's activity/history feed.
41
+ # @param page_id [String]
42
+ # @param sorters [Array<Hash>, nil] shorthand for `search_conf: { sorters: ... }`.
43
+ # @param search_conf [Hash, nil] full Search input (takes precedence over `sorters`).
44
+ # @return [Reader]
45
+ def page_activities(page_id, sorters: nil, search_conf: nil)
46
+ conf = search_conf
47
+ conf ||= { sorters: sorters } if sorters
48
+ Reader.new(graphql, page_id, search_conf: conf)
49
+ end
50
+ end
51
+ end
@@ -13,6 +13,7 @@ module Eco::API::UseCases::GraphQL::Helpers
13
13
  end
14
14
 
15
15
  require_relative 'pages/shortcuts'
16
+ require_relative 'pages/activities'
16
17
  require_relative 'pages/filters'
17
18
  require_relative 'pages/ooze_handlers'
18
19
  require_relative 'pages/rescuable'
@@ -7,3 +7,5 @@ require_relative 'helpers/base'
7
7
  require_relative 'helpers/location'
8
8
  require_relative 'helpers/contractors'
9
9
  require_relative 'helpers/pages'
10
+ require_relative 'helpers/access_logs'
11
+ require_relative 'helpers/dashboards'
@@ -1,5 +1,21 @@
1
1
  module Eco::API::UseCases::GraphQL::Samples
2
2
  module Contractors::DSL
3
3
  include Eco::API::UseCases::GraphQL::Helpers::Contractors::Base
4
+
5
+ private
6
+
7
+ # Sample command: update the org's contractor manager settings (auto-approvals +
8
+ # whether contractor admins may add other admins). Delegates to the guarded helper
9
+ # `#update_contractor_manager_settings` (dry-run/simulate + -no-email honoured).
10
+ #
11
+ # @param auto_approvals [Hash, nil] AutoApprovalsInput ({ enabled:, approvalConditions: }).
12
+ # @param allow_admins_add_admins [Boolean, nil]
13
+ # @return [Hash] the intended input when simulated, else the mutation payload.
14
+ def update_manager_settings(auto_approvals: nil, allow_admins_add_admins: nil)
15
+ update_contractor_manager_settings(
16
+ cm_auto_approvals_settings: auto_approvals,
17
+ allow_contractor_admins_to_add_admins: allow_admins_add_admins
18
+ )
19
+ end
4
20
  end
5
21
  end