Block Editor: hydrate Category/Tag fields for nested contentlets in StoryBlock responses#35413
Block Editor: hydrate Category/Tag fields for nested contentlets in StoryBlock responses#35413
Conversation
Agent-Logs-Url: https://github.com/dotCMS/core/sessions/ab0fe92d-836d-4a63-bd79-d33a9b5f82a9 Co-authored-by: fmontes <751424+fmontes@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotCMS/core/sessions/ab0fe92d-836d-4a63-bd79-d33a9b5f82a9 Co-authored-by: fmontes <751424+fmontes@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotCMS/core/sessions/ab0fe92d-836d-4a63-bd79-d33a9b5f82a9 Co-authored-by: fmontes <751424+fmontes@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotCMS/core/sessions/c6ec11c9-5dae-427e-bfa4-72ee43948754 Co-authored-by: fmontes <751424+fmontes@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR enhances StoryBlock (Block Editor) nested contentlet hydration so Category/Tag fields are included in the refreshed attrs.data map, aligning nested content rendering with expected field semantics.
Changes:
- Add Category/Tag field hydration into
StoryBlockAPIImpl.loadCommonContentletProps(...). - Implement helper methods to load Tag fields (
setTags()+ map population) and Category fields (resolve selected categories under the field’s configured parent). - Add an integration test validating Category field hydration for nested contentlets in StoryBlock refresh results.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
dotCMS/src/main/java/com/dotcms/contenttype/business/StoryBlockAPIImpl.java |
Adds Category/Tag hydration into the common contentlet-to-map logic used by StoryBlock refresh/hydration. |
dotcms-integration/src/test/java/com/dotcms/contenttype/business/StoryBlockAPITest.java |
Adds an integration test to validate Category field hydration for nested contentlets returned by refreshReferences(...). |
…kAPIImpl.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…StoryBlockAPITest Agent-Logs-Url: https://github.com/dotCMS/core/sessions/4835ac6b-c024-4c6d-b950-cf04cb19a698 Co-authored-by: fmontes <751424+fmontes@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@claude review again |
|
Claude finished @fmontes's task in 2m 34s —— View job Code Review (Re-review)
What Was Fixed Since Last Review✅ Remaining Issues
The // current: one big try wrapping all fields — mid-loop exception leaves partial state
try {
final List<Category> categories = categoryAPI.getParents(contentlet, user, true);
for (final Field categoryField : categoryFields) {
final List<Map<String, Object>> childCategories = new ArrayList<>();
// ... build childCategories ...
dataMap.put(categoryField.variable(), Map.of("categories", childCategories));
}
} catch (final DotDataException | DotSecurityException e) { ... }
// suggested: getParents() once (can throw), then per-field try so write always happens
final List<Category> categories;
try {
categories = categoryAPI.getParents(contentlet, user, true);
} catch (final DotDataException | DotSecurityException e) {
Logger.warn(this, ...);
return;
}
for (final Field categoryField : categoryFields) {
final List<Map<String, Object>> childCategories = new ArrayList<>();
try {
final Category parentCategory = categoryAPI.find(categoryField.values(), user, true);
if (parentCategory != null && categories != null) {
for (final Category category : categories) {
if (categoryAPI.isParent(category, parentCategory, user, true)) {
childCategories.add(this.toCategoryMap(category));
}
}
}
} catch (final DotDataException | DotSecurityException e) {
Logger.warn(this, String.format("Could not hydrate category field '%s' for contentlet '%s': %s",
categoryField.variable(), contentlet.getIdentifier(), ExceptionUtil.getErrorMessage(e)));
}
dataMap.put(categoryField.variable(), Map.of("categories", childCategories)); // always written
}
For predictable frontend consumption, Tag test leaks a persisted new TagDataGen().name(tagName).nextPersisted(); // tag is persisted...
// ...
} finally {
// ...but never deleted here
}No reference is kept and no cleanup runs for the created tag. Add a N+1 query concern (carry-over, low-medium) — Not new since the last review, but SummaryThe core issues from the previous review have been addressed: |
category.mp4
Proposed Changes
List<TagField>→List<Field>inloadTagFieldsto resolve compilation error (incompatible types)List<CategoryField>→List<Field>inloadCategoryFieldsto resolve compilation error (incompatible types){ "categories": [...] }todataMapfor everyCategoryField(even when no categories match), ensuring a consistent output type regardless of whether categories are assignedtest_loadCommonContentletProps_with_tag_fieldto verify Tag field hydration in StoryBlockrefreshReferences(...)resultstest_loadCommonContentletProps_with_category_fieldto also deletechildCategoryin thefinallyblock before deletingparentCategory, preventing test data leaksChecklist
Additional Info
ContentType.fields(Class<? extends Field>)always returnsList<Field>— not a typed sublist — so the local variable declarations in both helper methods were updated to match the actual return type. All methods called on the iterated field elements (variable(),values()) are defined on the baseFieldclass, so no behaviour changes result from the type correction.CategoryAPI.delete(...)does not cascade-delete child categories; it only removes the category itself and its tree relations. The test now trackschildCategoryoutside thetryblock and explicitly deletes it infinallybeforeparentCategoryto avoid orphaned test data.Screenshots