Problem/Motivation

Draft mode returns wrong revision of includes. I get default revision of second level paragraph instead of latest revision.

Technical problem:
When trying to access the working revision entity data with resourceVersion=rel:working-copy and using includes, the current revision of the referenced entities is being returned by the method "resolveIncludeTree" in IncludeResolver class. This method should return the data as per the revision being passed in the API query or the working revision.

Steps to reproduce

Install Content moderation;
Add support version negotiation for any entity type: "https://git.drupalcode.org/project/drupal/-/merge_requests/1365.patch"
Create paragraph type A;
Create paragraph type B;
Add paragraph reference field to paragraph A;
Create node with paragraph A (+ paragraph B inside A)
Publish node;
Create draft with changed B;
Check node json data with resourceVersion=rel:working-copy. Returned wrong revision of includes.

screenshot

Proposed resolution

web/core/modules/jsonapi/src/IncludeResolver.php
loads included entity by id
$targeted_entities = $entity_storage->loadMultiple(array_unique($ids));

I suggest to load entities by revision id:
$targeted_entities = $entity_storage->loadMultipleRevisions(array_unique($revision_ids));

Issue fork drupal-3088239

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

neelam.chaudhary created an issue. See original summary.

druellan’s picture

I'm having the same issue. Using ?resourceVersion=rel:working-copy or ?resourceVersion=id:1110 JSON:API returns the correct revision of the node and all the fields, BUT all referenced entities including paragraphs are returned as the "Current Version".

Wim Leers’s picture

Project: JSON:API » Drupal core
Version: 8.x-2.x-dev » 8.9.x-dev
Component: Code » jsonapi.module
Issue tags: +API-First Initiative
druellan’s picture

This is our quick fix for this problem, near the line 145, replacing the Entity::loadMultiple, making sure we are using target_revision_id for revisions.

Sorry I have no patch for this, we messed a lot with the file, since we faced also a permissions problem, but this snippet is actually working.

      foreach ($references as $target_type => $targets) {
        $entity_storage = $this->entityTypeManager->getStorage($target_type);
        $targeted_entities = [];
        foreach ($targets as $target) {
          if ($target['type'] == 'target_revision_id') {
            $targeted_entities[] = $entity_storage->loadRevision($target['value']);
          }
          else {
            $targeted_entities[] = $entity_storage->load($target['value']);
          }
        }
      }
pratik_kamble’s picture

Assigned: Unassigned » pratik_kamble
abhisekmazumdar’s picture

I will work on this.

abhisekmazumdar’s picture

Assigned: abhisekmazumdar » Unassigned

After some digging around I found out that there an issue Add a version negotiation to revisionable resource types

An admin/authenticated doesn't have the proper permissions to get the revision data.

johnennew’s picture

This patch is only a temporary solution but may be of use to people that need something now before the API first initiative can provide the standard entity revision access checks.

This patch allows revisions of included entities to be loaded, so the correct revision based on what is referenced by the revision of the entity being requested.

This patch also provides a new hook which allows module developers the ability to dangerously provide their own custom revision access logic for entities which are not nodes or media. By default other entities, for example the popular paragraphs module, will always come back as an access denied via JSON API unless its referencing the default version.

After applying this patch, you may need to implement your own `hook_jsonapi_revision_view_access()` function. You will see that the patch adds the paragraphs module example into the jsonapi.api.php file which you can copy straight into one of your own custom modules (renaming the function appropriately and clearing caches first of course!)

This patch is for 8.8.x branch.

johnennew’s picture

Another patch which hopefully will stop the 500 errors from the tests (can't seem to run all the tests locally for some reason).

Wim Leers’s picture

Category: Bug report » Feature request
Status: Active » Postponed (maintainer needs more info)
Issue tags: +entity API, +Needs issue summary update, +Needs title update
Related issues: +#3035113: JSON:API is typehinting LatestRevisionCheck because there is no Entity Revision Access API yet, remove that typehint once such an API exists

I hate to say this but … 😞

+++ b/core/modules/jsonapi/src/Access/EntityAccessChecker.php
@@ -256,10 +256,22 @@ protected function checkRevisionViewAccess(EntityInterface $entity, AccountInter
+        // Allow modules that know what they are doing a way to provide custom revision access.
+        $custom_access_list = \Drupal::moduleHandler()->invokeAll('jsonapi_revision_view_access', [$entity, $account]);

We specifically don't want to add more JSON:API-specific APIs to Drupal core. We need to add the missing APIs in Entity API.

See #3035113: JSON:API is typehinting LatestRevisionCheck because there is no Entity Revision Access API yet, remove that typehint once such an API exists. This is a duplicate of that AFAICT. Or am I mistaken?

johnennew’s picture

Yeah, I didn't expect this to go in - I just needed to host the patch file for people that need a solution now!

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

johnennew’s picture

This still doesn't address the comments in #10.

Patch attached improves 9 by allowing sub referenced paragraphs to come through the json API i.e. where you might have a paragraph referencing a paragraph - JSON API by default seems to forget its dealing with revisions after its gone two deep in the include tree. Can't tell if this is Paragraphs fault or not. Paragraphs seem to implement RevisionableInterface but $resource_type->isVersionable() when wrapping a paragraph always says no :(

drup4lNub’s picture

Alright! I need your help. So I have content types with paragraph types nested inside them. But when I try to access a draft version of the content fields inside the paragraph type it's not returning any data. I came across this thread and applied a few of these patches but I'm not seeing a change in the api call. (I'm using postman for testing this). am I missing something?

johnennew’s picture

Rerolled patch for Drupal 8.9.4 attached.

drup4lNub - did you implement the hook mentioned in the patch?

you'll need something like the code below - note by doing this you are explicitly overriding core behaviour to provide access control to non node / media entities (core blocks access to all revisions of paragraphs via JSON API):-

/**
 * Allow access to revisions of content.
 *
 * Normally JSON API forbids access to all revisions of entities apart from core
 * entity types Node and Media since these have interfaces for properly
 * determining access.
 *
 * If you know what you are doing and understand the risks you can implement
 * this hook to provide custom logic for other Entity types to allow revision
 * access.
 *
 * The example below allows access to revisions of paragraph entities by
 * checking if the user has access to the parent node revision the paragraph
 * revision is on.
 *
 * @return \Drupal\Core\Access\AccessResultInterface
 */
function hook_jsonapi_revision_view_access(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Session\AccountInterface $account) {
  /** @var \Drupal\paragraphs\Entity\Paragraph $entity */
  if (!$entity instanceof \Drupal\paragraphs\ParagraphInterface) {
    return AccessResult::neutral();
  }

  $parent = $entity->getParentEntity();

  if ($parent instanceof \Drupal\node\NodeInterface) {
    $field_name = $entity->get('parent_field_name')[0]->getValue();

    $database = \Drupal::database();
    $node_revision = $database
      ->select('node_revision__' . $field_name['value'], 'r')
      ->fields('r', ['revision_id'])
      ->condition('r.' . $field_name['value'] . '_target_id', $entity->id())
      ->condition('r.' . $field_name['value'] . '_target_revision_id', $entity->getRevisionId())
      ->execute()
      ->fetch();

    if ($parent->getRevisionId() !== $node_revision->revision_id) {
      $parent = node_revision_load($node_revision->revision_id);
    }

    /** @var \Drupal\node\Access\NodeRevisionAccessCheck $node_revision_access_service */
    $node_revision_access_service = \Drupal::service('access_check.node.revision');

    return AccessResult::allowedIf($node_revision_access_service
      ->checkAccess($parent, $account, 'view'))
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  return AccessResult::neutral('Paragraphs revisions are only supported on nodes with JSON:API');
}

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Ara Martirosyan’s picture

Thank you johnennew your patch of comment #13 saved my life

iivanov’s picture

Reapplied patch for Drupal version 9.1.4.
Replaced `\Drupal::moduleHandler()` with Module Handler container - added as a service in `EntityAccessChecker.php` class.
Removed `hook_jsonapi_revision_view_access` from `jsonapi.api.php` file and placed in `jsonapi.module` file - in order to be able to invoke the hook.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

acbramley’s picture

Along with #3031271: Support version negotiation for any entity type (currently only Node & Media are supported) I was able to get forward revisions of Node -> Paragraphs through JSON:API with just the IncludeResolver changes. I agree that these still need finessing but this is a good step forward.

quadrexdev’s picture

I can confirm that the patch from #21 within the changes from #3031271: Support version negotiation for any entity type (currently only Node & Media are supported) issue works for my project.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

bbrala’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

This would be great to get this working as advertised and really happy it already seems to work! To get a clear view of what we are doing here i'd like to see a few things:

  1. An update to the issue summary/title that explains this change and what we are trying to do.
  2. A nice set of minimal usecases that we can translate into tests, preferably using core functionality.

The use cases might just be something like:

Structure: We have a article with 2 reference fields (foo and bar) to pages.
State: A published node with a forward revision with 1 foo reference to a published page, and 1 bar reference to a published page with a forward revision.
Expect:
When asking for the forward revision jsonapi should send the forward revision of the article, current revision of the foo reference, and the forward revision of the bar reference.
When asking for the current revision jsonapi should senf the current revision of the article, the foo reference and bar reference.

This way we can describe this change, and someone with testing skills could write some tests :)

Kristen Pol’s picture

Seems to be a similar issue and patch but I don't know which is better. Ideally, we'd close one and continue work on the other.

#3291095: Content moderation + json api includes wrong revision issue

Rakhi Soni’s picture

Attached rerolled against branch 9.5x,,
Kindly review patch

bbrala’s picture

Status: Needs review » Needs work

Thanks for the reroll, but this issue also needs a few updates as mentioned in #24.

bnjmnm’s picture

FileSize
59.62 KB
189.03 KB

The reroll from @Rakhi Soni was not necessary and no credit will be granted.

Here's how to see if a reroll is necessary:
Find the most recent patch and choose "add test/retest"

In the Add test / retest queue choose the most recent branch and click the "queue" button to run the tests against the current dev branch.

If you don't get an error that the patch didn't apply, then a reroll isn't needed, the patch is able to apply to the current dev branch.

bbrala credited Anna D.

bbrala’s picture

Title: IncludeResolver not returning correct version data for moderated content references » Revisions on relations are not loaded correctly resulting in wrong data in includes
Category: Feature request » Bug report
Issue summary: View changes
Issue tags: -Needs issue summary update, -Needs title update +Needs change record

Adding some credit from #3291095: Content moderation + json api includes wrong revision issue. There is.

Below the feedback on the code of the related issue.

  1. +++ b/core/modules/jsonapi/src/IncludeResolver.php
    @@ -141,12 +142,23 @@ protected function resolveIncludeTree(array $include_tree, Data $data, Data $inc
    +          if ($field_item instanceof EntityReferenceRevisionsItem) {
    +            $references[$target_type]['revision_id'][] = $field_item->get('target_revision_id')->getValue();
    +          }
    +          else {
    +            $references[$target_type]['id'][] = $field_item->get($field_item::mainPropertyName())->getValue();
    +          }
    

    Compared to this issue i like this code better.

We do still need tests, and we will need a change record too.

pooja saraah’s picture

Thanks for your suggestions @bbrala
I have attached Patch Against Drupal 9.5.x

ravi.shankar’s picture

@pooja saraah, now I think $field_item_properties variable is unused and can be removed.

+++ b/core/modules/jsonapi/src/IncludeResolver.php
@@ -141,12 +141,22 @@ protected function resolveIncludeTree(array $include_tree, Data $data, Data $inc
+          if ($field_item instanceof EntityReferenceRevisionsItem) {

also, the use statement should be there for EntityReferenceRevisionsItem class.

varshith’s picture

I had this with paragraph references and fixed it before coming across this issue. While the core idea of the fix is the same, the recent patch is missing a 'use' statement and has an unused variable. So I am attaching my patch anyway for review. Thanks.

ravi.shankar’s picture

@varshith thanks for the patch, please provide the interdiff as well.

varshith’s picture

FileSize
3 KB

Adding interdiff

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Rassoni’s picture

#33 Re-roll against 10.1.x-dev branch. Patch applied successfully, but custom command failing. required NW

ameymudras’s picture

I just had a look at #33 and I'm not sure why we are using

+use Drupal\entity_reference_revisions\Plugin\Field\FieldType\EntityReferenceRevisionsItem;

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

mstrelan made their first commit to this issue’s fork.

mstrelan’s picture

Started to re-roll for 11.x but the changes to \Drupal\jsonapi\IncludeResolver::resolveIncludeTree from #3057545: ResourceTypeRepository wrongly assumes that all entity reference fields have the setting "target_type" have complicated this.

Nadim Hossain’s picture

Re-rolled the patch against 10.2.x, still testing needs to be done on this re-rolled patch.

acbramley’s picture

We can't reference classes from entity_reference_revisions in core, this needs a reroll starting from #21

Nadim Hossain’s picture

FileSize
1.9 KB

Thanks @acbramley
I have re-rolled it from #21 against 10.2.x

mstrelan’s picture

I don't think the re-roll is that simple. Now we're only adding the revision_ids and ids bit to $references if the is_subclass_of condition fails.

mstrelan’s picture

Opened MR against 11.x which is a re-roll of #21, but moves bits out of the is_subclass_of condition. This also applies to 10.2.x.

jberube’s picture

FileSize
3.3 KB

As mentioned in #45, the patch for v10.2.x in #44 doesn't cover both conditions in the if/else. Using that patch, I wasn't seeing the results I expected, so I made this patch that covers both conditions, and now I see the results I expected.

acbramley’s picture

Hiding patches to avoid bot confusion.

jkdaza’s picture

FileSize
3.32 KB

Patch #48 works for me, but

list($target_type, $revision_type) = explode(':', $target_type_and_rev);

assumes that the $target_type_and_rev will always have 2 values. This is not the case, so you end up with tons of warnings>
I've changed it to

list($target_type, $revision_type) = array_pad(explode(':', $target_type_and_rev), 2, NULL);

and no more warnings.

Attaching the revised patch.