Class: Ecoportal::API::GraphQL::Base::PresetView

Inherits:
Logic::BaseModel show all
Defined in:
lib/ecoportal/api/graphql/base/preset_view.rb

Overview

A register preset view — saved column/sort/filter configuration for previewPages. Scripts use the id as presetViewId in register.previewPages queries.

Direct Known Subclasses

Model::PresetView

Constant Summary collapse

DIFF_CLASS =

fieldConfigurations is a passarray (leaf) invisible to the default DiffService; LeafDiffService detects it as a full-set replace, which is what makes a columns-only change reach #as_input at all (before it, the array was dropped from the flat diff and no mutation was issued). The read-key/shape conversion that change then needs is handled below — the GAP-P1 that comment referred to as a separate pending converter is closed by it. See Diffable::LeafDiffService.

Ecoportal::API::Common::GraphQL::Model::Diffable::LeafDiffService
READ_KEY =

READ key vs WRITE key — and, on update, read SHAPE vs write SHAPE.

The API RETURNS the collection as fieldConfigurations, a plain list (see the passarray below — that stays as-is, consumers read it). The mutations call it fieldConfigs, and the two mutations do NOT agree on its shape. Schema-verified against the live introspection dump (20260730):

CreatePresetViewInput.fieldConfigs : [FieldConfigurationInput!]!
UpdatePresetViewInput.fieldConfigs : FieldConfigurationOneToManyInput!
                                   { additions:, deletions:, updates: }

Both are NON_NULL with no default value — i.e. REQUIRED on every call — while the FieldConfigurationOneToManyInput members each default to [], so {} is a valid no-op value for an update that does not touch the columns.

Emitting the read key fieldConfigurations is an unknown-argument error, and emitting the read LIST under fieldConfigs is a type error: either one rejects the WHOLE mutation, a name-only edit included.

#as_input emits the UPDATE shape. Logic::Mutation#as_input hands a read model straight to model.as_input without telling it which mutation it feeds, and the only model-driven path is register.preset_view.update: a read model carries an id, which CreatePresetViewInput does not even accept. Create is built from a caller-supplied Hash (see Builder::Register::PresetView) whose fieldConfigs is already the plain array the schema wants — it needs no conversion.

'fieldConfigurations'.freeze
FIELD_CONFIG_INPUT_KEYS =

The fields FieldConfigurationInput accepts (same dump). The read nodes also carry __typename — the read side is a union, see Fragment::FieldConfiguration — which the input type does NOT accept, so nodes are whitelisted through this list rather than passed along verbatim.

%i[
  id key name weight dataFieldLabel type showTime valueToShow mode displayUid
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::SnakeCamelAccess

#method_missing, #respond_to_missing?

Methods included from Common::GraphQL::Model::AsInput

included

Methods included from Common::GraphQL::Model::Diffable

#as_update, #dirty?

Methods included from Common::GraphQL::ClassHelpers

included

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Ecoportal::API::GraphQL::Concerns::SnakeCamelAccess

Class Method Details

.build_update_input(model, input) ⇒ Hash?

Turns a raw mutation-input diff into a valid UpdatePresetViewInput: drops the read key and sets fieldConfigs to the one-to-many delta.

Idempotent — the read key is deleted and the delta is recomputed from doc/original_doc, so a second application yields the same Hash. That is what makes it safe for both seams (#as_input and Input::PresetView::Update .from_model) to call it on the same Hash.

Parameters:

  • model (PresetView)

    the subject (read) model.

  • input (Hash, nil)

    the mutation input hash so far (nil when nothing in the flat diff changed).

Returns:

  • (Hash, nil)

    input with a schema-valid fieldConfigs, or nil when neither the flat diff nor the columns changed (no mutation to send).



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ecoportal/api/graphql/base/preset_view.rb', line 72

def build_update_input(model, input)
  return input unless model.respond_to?(:field_configs_delta)

  delta = model.field_configs_delta
  return input if input.nil? && delta.empty?

  input ||= {}
  input.delete(:fieldConfigurations)
  input.delete(READ_KEY)
  input[:id]         ||= model.id if model.respond_to?(:id)
  input[:fieldConfigs] = delta
  input
end

.field_config_input(node) ⇒ Hash

Returns the node reduced to the keys FieldConfigurationInput accepts, symbol-keyed. Keys the read selection did not ask for are simply absent.

Parameters:

  • node (Hash)

    one fieldConfigurations read node.

Returns:

  • (Hash)

    the node reduced to the keys FieldConfigurationInput accepts, symbol-keyed. Keys the read selection did not ask for are simply absent.



89
90
91
92
93
94
95
# File 'lib/ecoportal/api/graphql/base/preset_view.rb', line 89

def field_config_input(node)
  FIELD_CONFIG_INPUT_KEYS.each_with_object({}) do |key, out|
    str      = key.to_s
    out[key] = node[str] if node.key?(str)
    out[key] = node[key] if node.key?(key)
  end
end

Instance Method Details

#as_input(target_class: nil, clientMutationId: '') ⇒ Object

Mutation input for this preset view, shaped for updatePresetView.

See Also:



127
128
129
130
131
132
133
134
135
# File 'lib/ecoportal/api/graphql/base/preset_view.rb', line 127

def as_input(target_class: nil, clientMutationId: '')
  input = self.class.build_update_input(self, super)
  return input if input.nil? || target_class

  # `super` merges it only when it built the Hash itself; a columns-only change
  # synthesises the Hash here, so keep the wire payload identical either way.
  input[:clientMutationId] = clientMutationId.to_s unless input.key?(:clientMutationId)
  input
end

#field_configs_deltaHash

The FieldConfigurationOneToManyInput delta between the server baseline (original_doc) and the current doc, matched on the config id: additions = nodes with no known id, deletions = baseline ids that are gone, updates = known nodes whose (input-visible) content changed. Empty members are omitted, so an untouched collection yields {} — the valid no-op.

Computed DIRECTLY from the docs rather than from the flat diff, for the same reason as Logic::Input::IdDiffFields: passarray leaves are dropped by the cascaded DiffService.

Returns:

  • (Hash)

    { additions:, deletions:, updates: }, empty members omitted.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ecoportal/api/graphql/base/preset_view.rb', line 109

def field_configs_delta
  current  = field_config_nodes(doc)
  original = field_config_nodes(original_doc)
  known    = original.filter_map {|node| node[:id]}

  additions = current.reject {|node| node[:id] && known.include?(node[:id])}
  updates   = (current - additions) - original
  deletions = known - current.filter_map {|node| node[:id]}

  {}.tap do |delta|
    delta[:additions] = additions unless additions.empty?
    delta[:deletions] = deletions unless deletions.empty?
    delta[:updates]   = updates   unless updates.empty?
  end
end