Expose roles as a JSON:API relationship in the CurrentUserInfo test resource
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3591411. -->
Reported by: [mglaman](https://www.drupal.org/user/2416470)
Related to !30
>>>
<h3>Problem/Motivation</h3>
<p>The <code>CurrentUserInfo</code> test resource in <code>tests/modules/jsonapi_resources_test</code> exposes user roles as a plain <code>ResourceTypeAttribute</code>, storing them as a raw array of role IDs. There are two <code>@todo</code> comments in the code acknowledging this:</p>
<pre>
// @todo convert this to a ResourceTypeRelationship.
'roles' => new ResourceTypeAttribute('roles', NULL, TRUE, FALSE),
// ...
// @todo Add role entities as a relatable resource type.
$resource_type->setRelatableResourceTypes([]);
</pre><p>This means the test module does not demonstrate how to correctly expose an entity reference field as a JSON:API relationship from a custom resource, nor how to add <code>related</code> and <code>relationship</code> endpoints alongside it.</p>
<h3>Proposed resolution</h3>
<p>Update <code>CurrentUserInfo</code> to fulfill the existing <code>@todo</code> items and demonstrate two patterns that module developers need:</p>
<h4>1. Entity reference field as a JSON:API relationship</h4>
<p>Declare <code>roles</code> as a <code>ResourceTypeRelationship</code> and pass the actual entity reference field list (<code>$user->getFields()['roles']</code>) as the field value instead of <code>getRoles(TRUE)</code>. This lets the core JSON:API normalizer serialize it correctly as a relationship with <code>type</code>, <code>id</code>, and <code>meta.drupal_internal__target_id</code>.</p>
<h4>2. Related and relationship endpoints via <code>EntityResource</code> delegation</h4>
<p>Add <code>/jsonapi/me/roles</code> (related) and <code>/jsonapi/me/relationships/roles</code> (relationship) routes that delegate to the core <code>EntityResource</code> controller:</p>
<pre>
public function getRelatedRoles(Request $request): ResourceResponse {
$user_resource_type = $this->resourceTypeRepository->get('user', 'user');
return $this->entityResource->getRelated(
$user_resource_type,
$this->getCurrentUser(),
'roles',
$request
);
}
public function getRolesRelationship(Request $request): ResourceResponse {
$user_resource_type = $this->resourceTypeRepository->get('user', 'user');
return $this->entityResource->getRelationship(
$user_resource_type,
$this->getCurrentUser(),
'roles',
$request
);
}
</pre><p>A <code>RouteSubscriber</code> is needed to replace the <code>/%jsonapi%</code> placeholder in these routes with the actual JSON:API base path, since these are not standard JSON:API Resources routes and the replacement does not happen automatically.</p>
<h3>Remaining tasks</h3>
<ul>
<li>Update <code>CurrentUserInfo::process()</code> to use <code>$user->getFields()['roles']</code></li>
<li>Update <code>CurrentUserInfo::getRouteResourceTypes()</code> to declare <code>roles</code> as <code>ResourceTypeRelationship</code> and call <code>setRelatableResourceTypes()</code></li>
<li>Add <code>getRelatedRoles()</code> and <code>getRolesRelationship()</code> methods</li>
<li>Add <code>RouteSubscriber</code> to fix <code>/%jsonapi%</code> path substitution for the two new routes</li>
<li>Add <code>jsonapi_resources_test.services.yml</code> to wire the route subscriber</li>
<li>Add the two new routes to <code>jsonapi_resources_test.routing.yml</code></li>
<li>Add functional tests covering: relationship serialization, <code>?include=roles</code>, access-denied handling for roles, and CSRF token caching</li>
</ul>
<h3>Original issue</h3>
<p>This was originally developed as part of <a href="https://www.drupal.org/project/jsonapi_resources/issues/3591364">#3591364</a> but split out because it is independent of the <code>ResourceObjectRelationship</code> feature.</p>
issue