Skip to content

fix(content-drive): truncate long-text field values in listing rows (#37185) - #37396

Open
ihoffmann-dot wants to merge 5 commits into
mainfrom
issue-37185-content-drive-listing-longtext-projection-impl
Open

fix(content-drive): truncate long-text field values in listing rows (#37185)#37396
ihoffmann-dot wants to merge 5 commits into
mainfrom
issue-37185-content-drive-listing-longtext-projection-impl

Conversation

@ihoffmann-dot

@ihoffmann-dot ihoffmann-dot commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds TransformOptions.LONG_TEXT_PREVIEW (declared after STORY_BLOCK_VIEW/JSON_VIEW so EnumSet iteration order runs it last) backed by a new LongTextPreviewStrategy.
  • Replaces WYSIWYG/TextArea (Jsoup-extracted plain text) and Story Block (new recursive JSON-tree traversal) field values in a listing row with a ≤150-character preview, instead of the full raw value.
  • Wired opt-in only at BrowserAPIImpl#dotContentMap via a new DotTransformerBuilder#longTextPreview() chain method — never added to defaultOptions, so no other transformer consumer (Content Editor, ContentResource, GraphQL, asset picker) is affected (AC-007).
  • Bug found and fixed while writing the AC-008 test: if a content type's title-source field is itself WYSIWYG/TextArea (its variable is literally title), the strategy would have overwritten the already-correct, untruncated title COMMON_PROPS computes. Fixed by explicitly skipping the title key.
  • Updates the @Schema description on ContentDriveResource#search (AC-005; endpoint is @Hidden, no openapi.yaml regen needed) and removes a dead item.body assertion in the Postman collection (AC-006, a listing row never carried that key).

Test plan

Branched off the approved spec branch per this repo's Spec-Kit flow (spec.md-only in PR1, not merged to main yet).

🤖 Generated with Claude Code

This PR fixes: #37185

Verification (2026-09-04, local)

  • 12/12 unit tests (LongTextPreviewStrategyTest) pass.
  • 45/45 integration tests (BrowserAPITest) pass.
  • 87/87 Postman assertions (ContentDriveResource collection) pass.

Bugs found and fixed in the test code along the way (not the production fix): a package-private field access across packages (fixed via reflection), a test title exceeding the contentlet.title column's varchar(255) limit, an incorrect expected key name (__icon__ vs. the real icon) plus two File-Asset-only keys (mimeType/extension) wrongly expected on a generic-Content row.

Red confirmed: LongTextPreviewStrategy/TransformOptions.LONG_TEXT_PREVIEW
don't exist yet, so the test class doesn't compile.
…37185)

Adds TransformOptions.LONG_TEXT_PREVIEW (declared after STORY_BLOCK_VIEW/
JSON_VIEW so EnumSet order runs it last) backed by LongTextPreviewStrategy,
which replaces WYSIWYG/TextArea/Story Block values with a <=150-char
extracted plain-text preview. Wired opt-in only at
BrowserAPIImpl#dotContentMap via a new DotTransformerBuilder#longTextPreview()
chain method -- never added to defaultOptions, so no other transformer
consumer is affected.
…preview (#37185, AC-008)

If a content type's title-source field is itself WYSIWYG/TextArea (its
variable is literally 'title'), LongTextPreviewStrategy would match it by
field type and clobber the value COMMON_PROPS already populated from
Contentlet#getTitle() with a truncated/HTML-stripped preview. Skip the
'title' key explicitly.
…w trim (#37185)

T030-T033: generic-Content row shape from getPaginatedContents (Drive) and
getFolderContent (Site Browser) both carry AC-002's required keys and
reduced long-text values; Show In List (AC-003) renders a readable
preview; a WYSIWYG title-source field (AC-008) keeps an untruncated title.

Also: AC-005 Schema description update on ContentDriveResource#search
(endpoint is @hidden, no openapi.yaml regen needed), and AC-006 Postman
fix removing the dead item.body read (a listing row never carried that
key) in favor of an assertion that actually runs.
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ihoffmann-dot's task in 2m 26s —— View job


Code Review — PR #37396

  • Read changed files
  • Analyze for bugs / security / conventions
  • Post review

The design is sound: LONG_TEXT_PREVIEW is opt-in only at BrowserAPIImpl#dotContentMap, ordinal-last so it runs after STORY_BLOCK_VIEW/JSON_VIEW (verified — both are before it in TransformOptions, and defaultOptions contains both), and JSON_VIEW only touches JSONField so it never re-stringifies the story-block map before this strategy walks it. The title-key skip (AC-008) and the "never in defaultOptions" guard are both test-pinned. No security, transaction, System.*, or convention issues in the diff.

A few non-blocking items:

New Issues

  • 🟡 Medium: LongTextPreviewStrategy.java:100-127extractStoryBlockPreview/collectText walk the entire story-block tree and concatenate all text before truncate throws away everything past 150 chars. Likewise extractHtmlPreview:85 runs Jsoup.parse over the full HTML document. On the listing path — the exact place this feature exists to keep cheap — a content type with a large story block or long WYSIWYG body does full-payload work per row just to yield a 150-char preview. Short-circuit collectText once out.length() >= MAX_PREVIEW_LENGTH. Fix this →

  • 🟡 Medium: LongTextPreviewStrategy.java:133text.substring(0, MAX_PREVIEW_LENGTH) can split a UTF-16 surrogate pair when char 149/150 is an astral codepoint (emoji, some CJK), leaving a lone surrogate / replacement glyph at the end of the preview. Back off one char when Character.isHighSurrogate(text.charAt(149)). Fix this →

  • 🟡 Medium: LongTextPreviewStrategy.java:74 — for an empty/absent long-text field, map.get(var) is null → the extractors return ""map.put(var, ""). This injects an empty-string key for every WYSIWYG/TextArea/StoryBlock field even when it had no value (or wasn't in the map at all), slightly changing the listing-row shape versus the plain defaultOptions() output. Assumption: harmless for the content-drive consumer. What to verify: no UI/consumer distinguishes "absent field" from "". Consider skipping the put when the extracted preview is blank and the key wasn't already present.

No blocking issues — the Medium items are polish/perf on an already-correct implementation.
· issue-37185-content-drive-listing-longtext-projection-impl

…gyTest/BrowserAPITest (#37185)

- defaultOptions_neverIncludesLongTextPreview referenced
  DotContentletTransformerImpl.defaultOptions directly across packages
  (...transform vs. this test's ...transform.strategy) -- the field is
  package-private, so it doesn't compile. Read it via reflection instead.
- The wysiwygTitleField test's long title HTML (400+ chars) exceeded the
  contentlet.title column's varchar(255) limit. Reduced while keeping
  the stripped plain text well over the 150-char preview bound.
- REQUIRED_LISTING_KEYS listed the actual icon key as '__icon__'
  (it's 'icon') and included mimeType/extension, which are File
  Asset-specific and legitimately absent on a generic-Content row.
Base automatically changed from issue-37185-content-drive-listing-longtext-projection to main September 5, 2026 03:56
@ihoffmann-dot ihoffmann-dot self-assigned this Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Content Drive: listing payload carries long-text field values the grid never renders

1 participant