I have a custom entity type, "task", which has an entity reference field to a content type (nodes) "project".
I'm using a progressively-decoupled setup. Listing tasks using views and using jsonAPI have both always worked fine.
I recently implemented node_grants, and simply declaring the hook (even without returning anything) breaks my ability to query tasks using jsonAPI.
The error message is the dreaded:
LogicException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\jsonapi\ResourceResponse. in Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (line 154 of /var/www/dev-1/XX/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php).
The custom entity type is fairly vanilla - it was originally set up using Drupal Console. And this breaks even without fully implementing node_grants; declaring it is enough to break it.
I've spent a long time digging into this and the only way I've been able to get around the issue is deleting the (premature?) rendering of nodes:
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 9fdabf32b4..dcda495519 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
function node_query_node_access_alter(AlterableInterface $query) {
$request = \Drupal::requestStack()->getCurrentRequest();
$renderer = \Drupal::service('renderer');
if ($request->isMethodCacheable() && $renderer->hasRenderContext()) {
$build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]];
- $renderer->render($build);
}
}
I know that's a security risk because it prevents cache tags from bubbling up / risk of users seeing each other's data / bypassing access.
What I would *like* to do, but can't find a way to is something like this:
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 9fdabf32b4..dcda495519 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1119,8 +1119,9 @@ function node_query_node_access_alter(AlterableInterface $query) {
$request = \Drupal::requestStack()->getCurrentRequest();
$renderer = \Drupal::service('renderer');
if ($request->isMethodCacheable() && $renderer->hasRenderContext()) {
+ $render_context = $renderer->getCurrentRenderContext();
$build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]];
- $renderer->render($build);
+ $render_context->update($build);
}
}
That isn't possible because getCurrentRenderContext is a private method.
Am I totally off-base here? Any other solution? Like I said, everything works perfectly fine until I create a *literally empty* (or even properly populated) node_grants function. If I rename that function to something else (so that I am not using node_grants anymore), all of the tasks load fine.
-----
EDIT/UPDATE: See the attached patch "3032041-node_grant_premature_render-2.patch" for an updated potential solution which the above led me to.
I now believe the issue is with Renderer::hasRenderContext() returning true even if the context returns a count of 0.