Module: Eco::API::UseCases::GraphQL::Compat::OozeRedirect

Defined in:
lib/eco/api/usecases/graphql/compat/ooze_redirect.rb,
lib/eco/api/usecases/graphql/compat/ooze_redirect/dirty_array.rb,
lib/eco/api/usecases/graphql/compat/ooze_redirect/force_compat.rb,
lib/eco/api/usecases/graphql/compat/ooze_redirect/field_patches.rb

Overview

Transparently redirects an OozeSamples-based use case from the APIv2 ooze path to the GraphQL compat layer. Include it in the case class and nothing else needs to change in the script.

What it redirects

  • Page iteration: with_each_entry fetches via graphql.pages.get
  • api_v2 / apiv2: returns the graphql compat client
  • stage(name): resolves via target.stages[name] (GraphQL stage model)
  • with_fields(ooz, label:, type:): reads from ooz.components (GraphQL)
  • update_ooze(ooz): saves via graphql.pages.update
  • target.submit! / target.sign_off!: captured and forwarded to graphql.pages.update

What it patches (one-time, guarded)

  • V2 type classes (PlainTextField, SelectionField, etc.) recognise GraphQL instances in case/when dispatch via ===
  • GraphQL People field: people_ids << triggers dirty tracking
  • GraphQL CrossReference field: add, clear, reference_ids
  • GraphQL Select field: select, deselect, values, options (options returns Struct objects with .name / .value)
  • All GraphQL fields: ooze stub returns a minimal object for warning messages
  • Interface::BasePage: submit!(stage_id:) queues a submit-only flag (completes the fill-in task); sign_off!(stage_id:) queues sign-off. Tasks are sequential (fill-in → review): sign-off on a not-yet-submitted stage submits + signs off in one go (completePageTask{ signOff: true }). submit!(force: true) maps to completePageTask{ forcedComplete: true } (admin/superuser; skips the required-field check).

Per-run debug logging

Each type of redirection logs once per run (not per page) at :debug level. Filter with ECOPORTAL_LOG_LEVEL=debug or the session logger config.

Usage

class Custom::UseCase::TOOCSCoding < Eco::API::UseCases::OozeSamples::TargetOozesUpdateCase
include Eco::API::UseCases::GraphQL::Compat::OozeRedirect
# ... rest of class unchanged ...
end

Limitations

  • Force / binding operations (target.forces, force.bindings) are not available in GraphQL — those calls will raise NoMethodError.
  • Field property setters (fld.label=, fld.required=) are template mutations; they are not supported and will raise NoMethodError.
  • page_result.membranes (on PreviewPage search results) is not supported directly; use register search via graphql.registers.search instead.

TODO: Force / binding support (blocked on GraphQL endpoint)

Engineering is working on exposing "legacy forces" via the GraphQL API. Once that endpoint is available, extend this module with a ForceCompat sub-module (same include pattern — nothing changes in end scripts) covering:

target.forces.get_by_name(name)      → query forces by name on a page
force.bindings.get_by_name(name)     → field bindings on a force
force.bindings.add(field, name:)     → add a binding
force.bindings.delete!(binding)      → remove a binding
force.custom_script                  → read the LISP script
force.custom_script = new_script     → write the LISP script
force.script                         → raw script content (alias)

Affected cases currently blocked (from multi_org_api survey, ~50% of all ooze cases):

act-gov:        5 x 20240130_act_*_case, rearrage_page_sites_case
briscoes:       remove_induction_sections, 310524_Briscoes_Remove_Tasks
chorus:         4 x audit_update cases
hcc:            update_enterprise_risk_case
lic:            update_life_cycle_force_case
mitre10:        rich_text_update, update_location_force, updating_template
npdc:           contractor_title_force, risk_titile_force, fix_title_syncing,
              reminder_date_fields, 10092024_NPDC_CP_Add_Force
profile-group:  int_training_review, 20231026_profile_wellness
turners-growers: event_changes, inj_cost_calc, remove_line_force
twg:            hide_attached_risks, add_new_force

Implementation sketch (to be built when the endpoint lands):

module ForceCompat
# Patch GraphQL BasePage to respond to .forces
# Returns a proxy whose #get_by_name queries the GraphQL force endpoint
# and returns ForceProxy objects wrapping the response.
# ForceProxy exposes #bindings (BindingsProxy), #custom_script, #script.
# BindingsProxy exposes #get_by_name, #add, #delete!.
end

OozeRedirect.include(ForceCompat)  # or extend OozeRedirect::FieldPatches

Defined Under Namespace

Modules: FieldPatches, ForceCompat, Infrastructure Classes: DirtyArray

Class Method Summary collapse

Class Method Details

.force_support?Boolean

Returns true when the installed ecoportal-api-graphql gem exposes Query::PageWithForces (the forces-aware page query). Forces are NOT usable on GraphQL yet: Query::PageWithForces is a WIP whose query currently fails schema validation (selections on PageUnion; id on DataFieldBinding/ SectionBinding; unused ForceFields) and the backend forces endpoint is still in progress. The class merely being defined is not a readiness signal — activating ForceCompat on that basis makes EVERY OozeRedirect case fetch via the broken force query (e.g. toocs, which doesn't even use forces). Keep OFF until forces actually work; then restore a real readiness check.

Returns:

  • (Boolean)


120
121
122
# File 'lib/eco/api/usecases/graphql/compat/ooze_redirect.rb', line 120

def self.force_support?
  false
end

.included(base) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/eco/api/usecases/graphql/compat/ooze_redirect.rb', line 95

def self.included(base)
  FieldPatches.apply!
  # Provide `graphql` (lazy, memoized session.api(version: :graphql)) regardless of
  # the host's base class. OozeSamples cases (cans/toocs) don't include the GraphQL
  # helpers that Custom::UseCase paths pick up, yet Infrastructure#api_v2 /
  # #with_each_entry / #update_ooze all call `graphql` — without this they raise
  # NameError: undefined `graphql`.
  base.include(Eco::API::UseCases::GraphQL::Helpers::Base::GraphQLEnv)
  # Prepend Infrastructure first so it sits below ForceCompat in the MRO.
  # With Ruby's prepend, last-prepended wins — so ForceCompat::Infrastructure
  # must be prepended AFTER Infrastructure to be first in the lookup chain:
  #   MRO: ForceCompat::Infrastructure → Infrastructure → base class
  base.prepend(Infrastructure)
  base.prepend(ForceCompat::Infrastructure) if force_support?
end