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' =&gt; new ResourceTypeAttribute('roles', NULL, TRUE, FALSE), // ... // @todo Add role entities as a relatable resource type. $resource_type-&gt;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-&gt;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-&gt;resourceTypeRepository-&gt;get('user', 'user'); return $this-&gt;entityResource-&gt;getRelated( $user_resource_type, $this-&gt;getCurrentUser(), 'roles', $request ); } public function getRolesRelationship(Request $request): ResourceResponse { $user_resource_type = $this-&gt;resourceTypeRepository-&gt;get('user', 'user'); return $this-&gt;entityResource-&gt;getRelationship( $user_resource_type, $this-&gt;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-&gt;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