Skip to content

fix(content): use contentlet language for relationship hydration instead of session language#35168

Merged
oidacra merged 2 commits intomainfrom
issue-34289-defect-relationship-fields-return-default-language
Apr 1, 2026
Merged

fix(content): use contentlet language for relationship hydration instead of session language#35168
oidacra merged 2 commits intomainfrom
issue-34289-defect-relationship-fields-return-default-language

Conversation

@oidacra
Copy link
Copy Markdown
Member

@oidacra oidacra commented Mar 31, 2026

Summary

  • Fixes relationship fields returning default language content instead of localized content when using the depth parameter on GET /api/v1/content/{inodeOrIdentifier}
  • Root cause: When fetching content by inode without an explicit language query param, the backend derived languageId from the session (defaulting to language 1). The contentlet itself was resolved correctly via inode, but relationship hydration used the session language instead of the contentlet's actual language.
  • Fix: Use contentlet.getLanguageId() instead of the request-derived languageId when calling ContentUtils.addRelationships, ensuring relationships are always hydrated in the same language as the contentlet being returned.
CleanShot 2026-04-01 at 13 01 59@2x

Closes #34289

Acceptance Criteria

  • API response respects the language of the requested content item when hydrating relationships
  • When fetching by inode (no explicit language param), relationships match the contentlet's language
  • When fetching by identifier with explicit language param, behavior is unchanged (contentlet resolves to the requested language, relationships match)
  • Default language queries are not affected (backward compatible)

Test Plan

  • Create a content type with a relationship field
  • Create content item "Parent" with English and Spanish versions
  • Create related content "Child EN" (English) and "Child ES" (Spanish)
  • Assign "Child EN" as relationship for English parent, "Child ES" for Spanish parent
  • Fetch English parent by inode with ?depth=2 → verify relationship returns English child
  • Fetch Spanish parent by inode with ?depth=2 → verify relationship returns Spanish child (previously returned English)
  • Fetch by identifier with ?language=2&depth=2 → verify relationship returns Spanish child

Changed Files

  • dotCMS/src/main/java/com/dotcms/rest/api/v1/content/ContentResource.java — Line 415: Changed languageIdcontentlet.getLanguageId()

This PR fixes: #34289

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 31, 2026

Claude finished @oidacra's task in 1m 38s —— View job


Code Review

The fix is correct. One line change, right diagnosis. A few things worth flagging:


Missing test for the reported scenario (inode lookup)

The bug title and description specifically call out "fetching by inode without an explicit language query param." Neither test exercises this path — both tests use parentEN.getIdentifier() + explicit language param. The inode lookup path in resolveContentlet goes through a different branch (via versionableAPI) and the languageId derivation differs. The fix is correct either way, but the exact reported scenario has no regression coverage.

PageRenderUtil.java:422 has the same pattern

// PageRenderUtil.java line 422
ContentUtils.addRelationships(contentlet, user, mode, languageId);

Here languageId is the page render language (constructor parameter), not contentlet.getLanguageId(). If the page is rendering in a language that differs from a fallback-resolved contentlet's actual language, relationships would be hydrated in the wrong language. This is a sibling issue — out of scope here, but worth a follow-up. Note that StoryBlockAPIImpl already does this correctly (contentlet.getLanguageId() at line 552).

No test teardown

Neither test has @AfterClass cleanup. Content types and contentlets created in the tests persist in the DB. Other integration tests in the same suite run could be affected if they happen to query for content of these types. The first test wraps config restore in try/finally which is correct, but data cleanup is missing.

Second test relies on implicit config state

test_getContent_withMultiLanguageContent_* doesn't set DEFAULT_CONTENT_TO_DEFAULT_LANGUAGE and doesn't need to (no fallback involved). But it also doesn't guard against it being set to false globally by a prior test run. Not a bug, just fragile if test ordering changes.


The core fix is sound and the tests verify the right behavior. The gaps above are worth addressing before merge.

@oidacra oidacra self-assigned this Apr 1, 2026
@oidacra oidacra marked this pull request as ready for review April 1, 2026 16:03
…ead of session language

When fetching content by inode with depth parameter, the relationship hydration
was using the session/request language (defaulting to language 1) instead of the
actual contentlet's language. This caused relationship fields to return content
in the default language even when viewing a localized version.

Closes #34289
@oidacra oidacra force-pushed the issue-34289-defect-relationship-fields-return-default-language branch from a6823ac to 0bd551b Compare April 1, 2026 16:04
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 1, 2026

Rollback Safety Analysis

Result: ✅ Safe To Rollback

I analyzed the diff against all rollback-unsafe categories in docs/core/ROLLBACK_UNSAFE_CATEGORIES.md.

Change reviewed:

  • dotCMS/src/main/java/com/dotcms/rest/api/v1/content/ContentResource.java line 415: languageIdcontentlet.getLanguageId()

Category assessment:

Category Match?
C-1 Structural Data Model Change ✅ No
C-2 Elasticsearch Mapping Change ✅ No
C-3 Content JSON Model Version Bump ✅ No
C-4 DROP TABLE / DROP Column ✅ No
H-1 One-Way Data Migration ✅ No
H-2 RENAME TABLE / RENAME COLUMN ✅ No
H-3 PK Restructuring ✅ No
H-4 New Field Type ✅ No
H-5 Storage Provider Change ✅ No
H-6 DROP PROCEDURE / FUNCTION ✅ No
H-7 NOT NULL column without default ✅ No
M-1 Non-Broadening Column Type Change ✅ No
M-2 Push Publishing Bundle Format Change ✅ No
M-3 REST/GraphQL API Contract Change ✅ No — response structure unchanged; this is a bug fix (correct data, same shape)
M-4 OSGi Plugin API Breakage ✅ No

Conclusion: This is a pure in-memory logic fix. No DB migrations, no ES mapping changes, no API contract changes, and no serialization model changes. Rolling back to N-1 is safe — the only effect would be reverting to the previous (buggy) behavior where relationship fields used session language instead of contentlet language.

Label AI: Safe To Rollback has been applied.

View job run

…lationship hydration with fallback language

This commit introduces a new integration test class, `ContentResourceIntegrationTest`, which verifies that relationships are correctly hydrated using the contentlet's language when fetching content with a non-default language. The test ensures that the fallback mechanism works as intended, addressing issues where relationships were previously hydrated using the request's language instead. Additionally, the `MainSuite1b` test suite is updated to include this new test class.

Closes #34289
@oidacra oidacra enabled auto-merge April 1, 2026 17:36
@oidacra oidacra added this pull request to the merge queue Apr 1, 2026
Merged via the queue into main with commit 2102f1b Apr 1, 2026
49 checks passed
@oidacra oidacra deleted the issue-34289-defect-relationship-fields-return-default-language branch April 1, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[DEFECT] Relationship fields return default language content instead of localized content when using depth parameter

2 participants