Class: Ecoportal::API::GraphQL::Base::Page::DataField::ImageGallery
- Inherits:
-
DataField
- Object
- DataField
- Ecoportal::API::GraphQL::Base::Page::DataField::ImageGallery
- Defined in:
- lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb
Overview
Image gallery field.
★ FIXED 2026-09 (previously broken -- see spec/.../image_gallery_characterization_spec.rb
git history for the prior "WRITE PATH BROKEN" documentation of this defect). Confirmed
against the LIVE ecoPortal server source (read-only, /tmp/work/ecoPortal) that a
gallery image is genuinely NOT a FileContainer:
* `app/graphql/types/fill_in_page/inputs/data_fields/image_gallery_input.rb:5-11` --
`ImageGalleryInput` has NO `fileContainerIds`; it takes `images: [ImageInput]`.
* `.../image_galleries/image_input.rb:6-15` -- `ImageInput` takes `sourceId` (a
`TempImage` id, from the SEPARATE `uploadImage` mutation -- see
`Mutation::Image::Upload` / `FileUpload::Client#upload_image`), never
`fileContainerId`.
* `app/services/new_ep/pages/assign_attributes_service.rb:152-176`
(`set_image_gallery_attrs`) -- full-replace-by-omission, the SAME trap as
`FileField`: any current image whose `id` is not echoed back in the write is
destroyed (`format_removed_ids`).
* `app/graphql/types/pages/data_fields/image_galleries/image_type.rb` -- `fileName`/
`fileSize` are NULLABLE even on a successful upload -- a documented upstream bug
("images are successfully uploaded but the file name and size are not set in the
database", per the live schema's own field comment). Any comparison built on these
two fields must treat a nil on either side as "not a confident match", never a
positive one.
READ: images { id weight downloadUrl caption fileName fileSize uploadId }
(imageGalleryField fragment, fragment/pages/common_page_union.rb -- id/weight
were ADDED alongside this fix; the fragment previously omitted both, which is why no
correct reader was possible before now).
WRITE: ImageGalleryInput { id images: [ImageInput] }; ImageInput { id weight sourceId caption fileName sensitiveContent inaccurateDescription inaccurateExtractedText }. as_input sends every KEPT image back with its FULL
ImageInput field set (#kept_image_input) -- NOT a bare {id:} -- and every NEW one
as {sourceId:, weight:, fileName:} -- see #add_source_images, the merge-safe writer
(mirrors FileField#file_container_ids='s kept-vs-new split).
★ CORRECTED 2026-09 (pre-release, MR !278 unreleased): the bare {id:} echo for a kept
image, above, silently dropped every OTHER attribute on that image on the NEXT write
server-side -- assign_attributes_service.rb#set_image_gallery_attrs applies images
as a FULL REPLACE (see the class header above), so a bare {id:} is indistinguishable
from "clear caption/sensitiveContent/etc back to their defaults" for that image, not
"leave it as it is". Matches the org-side reference implementation (a downstream script repo,
commit 482db9c) and the live schema's own ImageInput argument list -- the shipped
ep-api-collections sample only demonstrates the NEW-image shape (no existing images to
keep), so this shape is pinned to server source + that proven reference, not a captured
kept-echo payload (see EVIDENCE PRECEDENCE note on #kept_image_input).
images is deliberately a PLAIN doc reader, not passarray -- same reasoning as
FileField#items (see that class's header): materialising an ArrayModel writes
images: [] into the doc of a field read without $content, and a phantom-dirty empty
images here doesn't always no-op (only the empty-vs-missing-key case is neutralised
by Diffable::LeafDiffService's array-diff fix, MR !106) -- it can still synthesise an
unwanted write for a non-empty phantom read. A plain reader has no such hazard.
Instance Method Summary collapse
-
#add_source_images(sources) ⇒ Array<Hash>
Merge-safe write: adds NEW images (each referenced by
source_id, theTempImageid fromFileUpload::Client#upload_image) while KEEPING every image already loaded on this field. - #as_input ⇒ Object
-
#image_ids ⇒ Array<String>
Every image's own item id (its server-assigned id, NOT the
uploadId/TempImageid it was created from). -
#images ⇒ Object
each: { 'id' =>, 'weight' =>, 'downloadUrl' =>, 'caption' =>, 'fileName' =>, 'fileSize' =>, 'uploadId' => }.
-
#next_weight ⇒ Integer
One past the highest
weightcurrently on the field (0if the field is empty).
Instance Method Details
#add_source_images(sources) ⇒ Array<Hash>
Merge-safe write: adds NEW images (each referenced by source_id, the TempImage
id from FileUpload::Client#upload_image) while KEEPING every image already loaded
on this field. images on ImageGalleryInput is a FULL REPLACE server-side -- any
current image whose id is not echoed back is destroyed -- so this always keeps
every currently-loaded image's FULL doc entry (untouched -- #images_input picks the
ImageInput-writable subset out of it later; kept HERE in full so that subset still
has every field to read, rather than collapsing to a bare {'id' => ...} that would
throw the other attributes away before #as_input ever runs) and appends the new
ones after it, at sequential weights starting at #next_weight. Marks the field dirty
(same underlying mechanism as FileField#file_container_ids= -- a raw doc mutation,
picked up by the generic diff regardless of the passarray/plain-reader choice above).
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb', line 96 def add_source_images(sources) kept = images.select { |image| image.is_a?(Hash) && image['id'] } start = next_weight added = Array(sources).each_with_index.map do |source, index| { 'sourceId' => fetch_key(source, :source_id, 'sourceId'), 'weight' => start + index, 'fileName' => fetch_key(source, :file_name, 'fileName') }.compact end doc['images'] = kept + added end |
#as_input ⇒ Object
109 110 111 112 113 |
# File 'lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb', line 109 def as_input return nil unless dirty? {imageGallery: {id: id, images: images_input}} end |
#image_ids ⇒ Array<String>
Returns every image's own item id (its server-assigned id, NOT the
uploadId/TempImage id it was created from).
64 65 66 |
# File 'lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb', line 64 def image_ids images.filter_map { |image| image['id'] if image.is_a?(Hash) } end |
#images ⇒ Object
each: { 'id' =>, 'weight' =>, 'downloadUrl' =>, 'caption' =>, 'fileName' =>, 'fileSize' =>, 'uploadId' => }
58 59 60 |
# File 'lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb', line 58 def images Array(doc['images']) end |
#next_weight ⇒ Integer
Returns one past the highest weight currently on the field (0 if the
field is empty). There is no server-side auto-increment for weight (a plain
Integer field, default 0) -- callers that want sequential ordering for several new
images in one write should still call this ONCE and increment locally
(#add_source_images already does).
73 74 75 76 77 |
# File 'lib/ecoportal/api/graphql/base/page/data_field/image_gallery.rb', line 73 def next_weight return 0 if images.empty? images.map { |image| image.is_a?(Hash) ? image['weight'].to_i : 0 }.max + 1 end |