function ResourceTestBase::testRevisions
Same name in other branches
- 9 core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::testRevisions()
- 8.9.x core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::testRevisions()
- 10 core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php \Drupal\Tests\jsonapi\Functional\ResourceTestBase::testRevisions()
Tests individual and collection revisions.
1 method overrides ResourceTestBase::testRevisions()
- MessageTest::testRevisions in core/
modules/ jsonapi/ tests/ src/ Functional/ MessageTest.php - Tests individual and collection revisions.
File
-
core/
modules/ jsonapi/ tests/ src/ Functional/ ResourceTestBase.php, line 2785
Class
- ResourceTestBase
- Subclass this for every JSON:API resource type.
Namespace
Drupal\Tests\jsonapi\FunctionalCode
public function testRevisions() : void {
if (!$this->entity
->getEntityType()
->isRevisionable() || !$this->entity instanceof FieldableEntityInterface) {
return;
}
assert($this->entity instanceof RevisionableInterface);
// Add a field to modify in order to test revisions.
FieldStorageConfig::create([
'entity_type' => static::$entityTypeId,
'field_name' => 'field_revisionable_number',
'type' => 'integer',
])->setCardinality(1)
->save();
FieldConfig::create([
'entity_type' => static::$entityTypeId,
'field_name' => 'field_revisionable_number',
'bundle' => $this->entity
->bundle(),
])
->setLabel('Revisionable text field')
->setTranslatable(FALSE)
->save();
// Reload entity so that it has the new field.
$entity = $this->entityLoadUnchanged($this->entity
->id());
// Set up test data.
/** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
$entity->set('field_revisionable_number', 42);
$entity->save();
$original_revision_id = (int) $entity->getRevisionId();
$entity->set('field_revisionable_number', 99);
$entity->setNewRevision();
$entity->save();
$latest_revision_id = (int) $entity->getRevisionId();
// @todo Remove line below in favor of commented line in https://www.drupal.org/project/drupal/issues/2878463.
$url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), [
'entity' => $this->entity
->uuid(),
])
->setAbsolute();
// $url = $this->entity->toUrl('jsonapi');
$collection_url = Url::fromRoute(sprintf('jsonapi.%s.collection', static::$resourceTypeName))->setAbsolute();
$relationship_url = Url::fromRoute(sprintf('jsonapi.%s.%s.relationship.get', static::$resourceTypeName, 'field_jsonapi_test_entity_ref'), [
'entity' => $this->entity
->uuid(),
])
->setAbsolute();
$related_url = Url::fromRoute(sprintf('jsonapi.%s.%s.related', static::$resourceTypeName, 'field_jsonapi_test_entity_ref'), [
'entity' => $this->entity
->uuid(),
])
->setAbsolute();
$original_revision_id_url = clone $url;
$original_revision_id_url->setOption('query', [
'resourceVersion' => "id:{$original_revision_id}",
]);
$original_revision_id_relationship_url = clone $relationship_url;
$original_revision_id_relationship_url->setOption('query', [
'resourceVersion' => "id:{$original_revision_id}",
]);
$original_revision_id_related_url = clone $related_url;
$original_revision_id_related_url->setOption('query', [
'resourceVersion' => "id:{$original_revision_id}",
]);
$latest_revision_id_url = clone $url;
$latest_revision_id_url->setOption('query', [
'resourceVersion' => "id:{$latest_revision_id}",
]);
$latest_revision_id_relationship_url = clone $relationship_url;
$latest_revision_id_relationship_url->setOption('query', [
'resourceVersion' => "id:{$latest_revision_id}",
]);
$latest_revision_id_related_url = clone $related_url;
$latest_revision_id_related_url->setOption('query', [
'resourceVersion' => "id:{$latest_revision_id}",
]);
$rel_latest_version_url = clone $url;
$rel_latest_version_url->setOption('query', [
'resourceVersion' => 'rel:latest-version',
]);
$rel_latest_version_relationship_url = clone $relationship_url;
$rel_latest_version_relationship_url->setOption('query', [
'resourceVersion' => 'rel:latest-version',
]);
$rel_latest_version_related_url = clone $related_url;
$rel_latest_version_related_url->setOption('query', [
'resourceVersion' => 'rel:latest-version',
]);
$rel_latest_version_collection_url = clone $collection_url;
$rel_latest_version_collection_url->setOption('query', [
'resourceVersion' => 'rel:latest-version',
]);
$rel_working_copy_url = clone $url;
$rel_working_copy_url->setOption('query', [
'resourceVersion' => 'rel:working-copy',
]);
$rel_working_copy_relationship_url = clone $relationship_url;
$rel_working_copy_relationship_url->setOption('query', [
'resourceVersion' => 'rel:working-copy',
]);
$rel_working_copy_related_url = clone $related_url;
$rel_working_copy_related_url->setOption('query', [
'resourceVersion' => 'rel:working-copy',
]);
$rel_working_copy_collection_url = clone $collection_url;
$rel_working_copy_collection_url->setOption('query', [
'resourceVersion' => 'rel:working-copy',
]);
$rel_invalid_collection_url = clone $collection_url;
$rel_invalid_collection_url->setOption('query', [
'resourceVersion' => 'rel:invalid',
]);
$revision_id_key = 'drupal_internal__' . $this->entity
->getEntityType()
->getKey('revision');
$published_key = $this->entity
->getEntityType()
->getKey('published');
$revision_translation_affected_key = $this->entity
->getEntityType()
->getKey('revision_translation_affected');
$amend_relationship_urls = function (array &$document, $revision_id) {
if (!empty($document['data']['relationships'])) {
foreach ($document['data']['relationships'] as &$relationship) {
$pattern = '/resourceVersion=id%3A\\d/';
$replacement = 'resourceVersion=' . urlencode("id:{$revision_id}");
$relationship['links']['self']['href'] = preg_replace($pattern, $replacement, $relationship['links']['self']['href']);
$relationship['links']['related']['href'] = preg_replace($pattern, $replacement, $relationship['links']['related']['href']);
}
}
};
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
$request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
// Ensure 403 forbidden on typical GET.
$actual_response = $this->request('GET', $url, $request_options);
$expected_cacheability = $this->getExpectedUnauthorizedAccessCacheability();
$result = $entity->access('view', $this->account, TRUE);
$detail = 'The current user is not allowed to GET the selected resource.';
if ($result instanceof AccessResultReasonInterface && ($reason = $result->getReason()) && !empty($reason)) {
$detail .= ' ' . $reason;
}
$this->assertResourceErrorResponse(403, $detail, $url, $actual_response, '/data', $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Ensure that targeting a revision does not bypass access.
$actual_response = $this->request('GET', $original_revision_id_url, $request_options);
$expected_cacheability = $this->getExpectedUnauthorizedAccessCacheability();
$detail = 'The current user is not allowed to GET the selected resource. The user does not have access to the requested version.';
if ($result instanceof AccessResultReasonInterface && ($reason = $result->getReason()) && !empty($reason)) {
$detail .= ' ' . $reason;
}
$this->assertResourceErrorResponse(403, $detail, $url, $actual_response, '/data', $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
$this->setUpRevisionAuthorization('GET');
// Ensure that the URL without a `resourceVersion` query parameter returns
// the default revision. This is always the latest revision when
// content_moderation is not installed.
$actual_response = $this->request('GET', $url, $request_options);
$expected_document = $this->alterExpectedDocumentForRevision($this->getExpectedDocument());
// The resource object should always links to the specific revision it
// represents.
$expected_document['data']['links']['self']['href'] = $latest_revision_id_url->setAbsolute()
->toString();
$amend_relationship_urls($expected_document, $latest_revision_id);
// Resource objects always link to their specific revision by revision ID.
$expected_document['data']['attributes'][$revision_id_key] = $latest_revision_id;
$expected_document['data']['attributes']['field_revisionable_number'] = 99;
$expected_cache_tags = $this->getExpectedCacheTags();
$expected_cache_contexts = $this->getExpectedCacheContexts();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Fetch the same revision using its revision ID.
$actual_response = $this->request('GET', $latest_revision_id_url, $request_options);
// The top-level document object's `self` link should always link to the
// request URL.
$expected_document['links']['self']['href'] = $latest_revision_id_url->setAbsolute()
->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Ensure dynamic cache HIT on second request when using a version
// negotiator.
$actual_response = $this->request('GET', $latest_revision_id_url, $request_options);
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, $this->generateDynamicPageCacheExpectedHeaderValue($expected_cache_contexts) === 'MISS' ? 'HIT' : 'UNCACHEABLE');
// Fetch the same revision using the `latest-version` link relation type
// negotiator. Without content_moderation, this is always the most recent
// revision.
$actual_response = $this->request('GET', $rel_latest_version_url, $request_options);
$expected_document['links']['self']['href'] = $rel_latest_version_url->setAbsolute()
->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Fetch the same revision using the `working-copy` link relation type
// negotiator. Without content_moderation, this is always the most recent
// revision.
$actual_response = $this->request('GET', $rel_working_copy_url, $request_options);
$expected_document['links']['self']['href'] = $rel_working_copy_url->setAbsolute()
->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Fetch the prior revision.
$actual_response = $this->request('GET', $original_revision_id_url, $request_options);
$expected_document['data']['attributes'][$revision_id_key] = $original_revision_id;
$expected_document['data']['attributes']['field_revisionable_number'] = 42;
$expected_document['links']['self']['href'] = $original_revision_id_url->setAbsolute()
->toString();
// The resource object should always links to the specific revision it
// represents.
$expected_document['data']['links']['self']['href'] = $original_revision_id_url->setAbsolute()
->toString();
$amend_relationship_urls($expected_document, $original_revision_id);
// When the resource object is not the latest version or the working copy,
// a link should be provided that links to those versions. Therefore, the
// presence or absence of these links communicates the state of the resource
// object.
$expected_document['data']['links']['latest-version']['href'] = $rel_latest_version_url->setAbsolute()
->toString();
$expected_document['data']['links']['working-copy']['href'] = $rel_working_copy_url->setAbsolute()
->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, Cache::mergeTags($expected_cache_tags, $this->getExtraRevisionCacheTags()), $expected_cache_contexts, NULL, TRUE);
// Install content_moderation module.
$this->assertTrue($this->container
->get('module_installer')
->install([
'content_moderation',
], TRUE), 'Installed modules.');
if (!\Drupal::service('content_moderation.moderation_information')->canModerateEntitiesOfEntityType($this->entity
->getEntityType())) {
return;
}
// Set up an editorial workflow.
$workflow = $this->createEditorialWorkflow();
$workflow->getTypePlugin()
->addEntityTypeAndBundle(static::$entityTypeId, $this->entity
->bundle());
$workflow->save();
$this->grantPermissionsToTestedRole([
'use editorial transition publish',
]);
// Ensure the test entity has content_moderation fields attached to it.
/** @var \Drupal\Core\Entity\FieldableEntityInterface|\Drupal\Core\Entity\TranslatableRevisionableInterface $entity */
$entity = $this->entityStorage
->load($entity->id());
// Set the published moderation state on the test entity.
$entity->set('moderation_state', 'published');
$entity->setNewRevision();
$entity->save();
$default_revision_id = (int) $entity->getRevisionId();
// Fetch the published revision by using the `rel` version negotiator and
// the `latest-version` version argument. With content_moderation, this is
// now the most recent revision where the moderation state was the 'default'
// one.
$actual_response = $this->request('GET', $rel_latest_version_url, $request_options);
$expected_document['data']['attributes'][$revision_id_key] = $default_revision_id;
$expected_document['data']['attributes']['moderation_state'] = 'published';
$expected_document['data']['attributes'][$published_key] = TRUE;
$expected_document['data']['attributes']['field_revisionable_number'] = 99;
$expected_document['links']['self']['href'] = $rel_latest_version_url->toString();
$expected_document['data']['attributes'][$revision_translation_affected_key] = $entity->isRevisionTranslationAffected();
// The resource object now must link to the new revision.
$default_revision_id_url = clone $url;
$default_revision_id_url = $default_revision_id_url->setOption('query', [
'resourceVersion' => "id:{$default_revision_id}",
]);
$expected_document['data']['links']['self']['href'] = $default_revision_id_url->setAbsolute()
->toString();
$amend_relationship_urls($expected_document, $default_revision_id);
// Since the requested version is the latest version and working copy, there
// should be no links.
unset($expected_document['data']['links']['latest-version']);
unset($expected_document['data']['links']['working-copy']);
$expected_document = $this->alterExpectedDocumentForRevision($expected_document);
$expected_cache_tags = array_unique([
$expected_cache_tags,
$workflow->getCacheTags(),
]);
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Fetch the collection URL using the `latest-version` version argument.
$actual_response = $this->request('GET', $rel_latest_version_collection_url, $request_options);
$expected_response = $this->getExpectedCollectionResponse([
$entity,
], $rel_latest_version_collection_url->toString(), $request_options);
$expected_collection_document = $expected_response->getResponseData();
$expected_cacheability = $expected_response->getCacheableMetadata();
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Fetch the published revision by using the `working-copy` version
// argument. With content_moderation, this is always the most recent
// revision regardless of moderation state.
$actual_response = $this->request('GET', $rel_working_copy_url, $request_options);
$expected_document['links']['self']['href'] = $rel_working_copy_url->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// Fetch the collection URL using the `working-copy` version argument.
$actual_response = $this->request('GET', $rel_working_copy_collection_url, $request_options);
$expected_collection_document['links']['self']['href'] = $rel_working_copy_collection_url->toString();
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// @todo Remove the next assertion when Drupal core supports entity query access control on revisions.
$rel_working_copy_collection_url_filtered = clone $rel_working_copy_collection_url;
$rel_working_copy_collection_url_filtered->setOption('query', [
'filter[foo]' => 'bar',
] + $rel_working_copy_collection_url->getOption('query'));
$actual_response = $this->request('GET', $rel_working_copy_collection_url_filtered, $request_options);
$filtered_collection_expected_cache_contexts = [
'url.path',
'url.query_args',
'url.site',
];
$this->assertResourceErrorResponse(501, 'JSON:API does not support filtering on revisions other than the latest version because a secure Drupal core API does not yet exist to do so.', $rel_working_copy_collection_url_filtered, $actual_response, FALSE, [
'http_response',
], $filtered_collection_expected_cache_contexts);
// Fetch the collection URL using an invalid version identifier.
$actual_response = $this->request('GET', $rel_invalid_collection_url, $request_options);
$invalid_version_expected_cache_contexts = [
'url.path',
'url.query_args',
'url.site',
];
$this->assertResourceErrorResponse(400, 'Collection resources only support the following resource version identifiers: rel:latest-version, rel:working-copy', $rel_invalid_collection_url, $actual_response, FALSE, [
'4xx-response',
'http_response',
], $invalid_version_expected_cache_contexts);
// Move the entity to its draft moderation state.
$entity->set('field_revisionable_number', 42);
// Change a relationship field so revisions can be tested on related and
// relationship routes.
$new_user = $this->createUser();
$new_user->save();
$entity->set('field_jsonapi_test_entity_ref', [
'target_id' => $new_user->id(),
]);
$entity->set('moderation_state', 'draft');
$entity->setNewRevision();
$entity->save();
$forward_revision_id = (int) $entity->getRevisionId();
// The `latest-version` link should *still* reference the same revision
// since a draft is not a default revision.
$actual_response = $this->request('GET', $rel_latest_version_url, $request_options);
$expected_document['links']['self']['href'] = $rel_latest_version_url->toString();
// Since the latest version is no longer also the working copy, a
// `working-copy` link is required to indicate that there is a forward
// revision available.
$expected_document['data']['links']['working-copy']['href'] = $rel_working_copy_url->setAbsolute()
->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// And the same should be true for collections.
$actual_response = $this->request('GET', $rel_latest_version_collection_url, $request_options);
$expected_collection_document['data'][0] = $expected_document['data'];
$expected_collection_document['links']['self']['href'] = $rel_latest_version_collection_url->toString();
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Ensure that the `latest-version` response is same as the default link,
// aside from the document's `self` link.
$actual_response = $this->request('GET', $url, $request_options);
$expected_document['links']['self']['href'] = $url->toString();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// And the same should be true for collections.
$actual_response = $this->request('GET', $collection_url, $request_options);
$expected_collection_document['links']['self']['href'] = $collection_url->toString();
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Now, the `working-copy` link should reference the draft revision. This
// is significant because without content_moderation, the two responses
// would still been the same.
//
// Access is checked before any special permissions are granted. This
// asserts a 403 forbidden if the user is not allowed to see unpublished
// content.
$result = $entity->access('view', $this->account, TRUE);
if (!$result->isAllowed()) {
$actual_response = $this->request('GET', $rel_working_copy_url, $request_options);
$expected_cacheability = $this->getExpectedUnauthorizedAccessCacheability();
$expected_cache_tags = Cache::mergeTags($expected_cacheability->getCacheTags(), $entity->getCacheTags());
$expected_cache_contexts = $expected_cacheability->getCacheContexts();
$detail = 'The current user is not allowed to GET the selected resource. The user does not have access to the requested version.';
$message = $result instanceof AccessResultReasonInterface ? trim($detail . ' ' . $result->getReason()) : $detail;
$this->assertResourceErrorResponse(403, $message, $url, $actual_response, '/data', $expected_cache_tags, $expected_cache_contexts, NULL, TRUE);
// On the collection URL, we should expect to see the draft omitted from
// the collection.
$actual_response = $this->request('GET', $rel_working_copy_collection_url, $request_options);
$expected_response = static::getExpectedCollectionResponse([
$entity,
], $rel_working_copy_collection_url->toString(), $request_options);
$expected_collection_document = $expected_response->getResponseData();
$expected_collection_document['data'] = [];
$expected_cacheability = $expected_response->getCacheableMetadata();
$access_denied_response = static::getAccessDeniedResponse($entity, $result, $url, NULL, $detail)->getResponseData();
static::addOmittedObject($expected_collection_document, static::errorsToOmittedObject($access_denied_response['errors']));
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, $expected_cacheability->getCacheTags(), $expected_cacheability->getCacheContexts(), NULL, TRUE);
}
// Since additional permissions are required to see 'draft' entities,
// grant those permissions.
$this->grantPermissionsToTestedRole($this->getEditorialPermissions());
// Now, the `working-copy` link should be latest revision and be accessible.
$actual_response = $this->request('GET', $rel_working_copy_url, $request_options);
$expected_document['data']['attributes'][$revision_id_key] = $forward_revision_id;
$expected_document['data']['attributes']['moderation_state'] = 'draft';
$expected_document['data']['attributes'][$published_key] = FALSE;
$expected_document['data']['attributes']['field_revisionable_number'] = 42;
$expected_document['links']['self']['href'] = $rel_working_copy_url->setAbsolute()
->toString();
$expected_document['data']['attributes'][$revision_translation_affected_key] = $entity->isRevisionTranslationAffected();
// The resource object now must link to the forward revision.
$forward_revision_id_url = clone $url;
$forward_revision_id_url = $forward_revision_id_url->setOption('query', [
'resourceVersion' => "id:{$forward_revision_id}",
]);
$expected_document['data']['links']['self']['href'] = $forward_revision_id_url->setAbsolute()
->toString();
$amend_relationship_urls($expected_document, $forward_revision_id);
// Since the working copy is not the default revision. A `latest-version`
// link is required to indicate that the requested version is not the
// default revision.
unset($expected_document['data']['links']['working-copy']);
$expected_document['data']['links']['latest-version']['href'] = $rel_latest_version_url->setAbsolute()
->toString();
$expected_cache_tags = $this->getExpectedCacheTags();
$expected_cache_contexts = $this->getExpectedCacheContexts();
$expected_cache_tags = array_unique([
$expected_cache_tags,
$workflow->getCacheTags(),
]);
$this->assertResourceResponse(200, $expected_document, $actual_response, Cache::mergeTags($expected_cache_tags, $this->getExtraRevisionCacheTags()), $expected_cache_contexts, NULL, TRUE);
// And the collection response should also have the latest revision.
$actual_response = $this->request('GET', $rel_working_copy_collection_url, $request_options);
$expected_response = static::getExpectedCollectionResponse([
$entity,
], $rel_working_copy_collection_url->toString(), $request_options);
$expected_collection_document = $expected_response->getResponseData();
$expected_collection_document['data'] = [
$expected_document['data'],
];
$expected_cacheability = $expected_response->getCacheableMetadata();
$this->assertResourceResponse(200, $expected_collection_document, $actual_response, Cache::mergeTags($expected_cacheability->getCacheTags(), $this->getExtraRevisionCacheTags()), $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Test relationship responses.
// Fetch the prior revision's relationship URL.
$test_relationship_urls = [
'canonical' => [
NULL,
$relationship_url,
$related_url,
],
'original' => [
$original_revision_id,
$original_revision_id_relationship_url,
$original_revision_id_related_url,
],
'latest' => [
$latest_revision_id,
$latest_revision_id_relationship_url,
$latest_revision_id_related_url,
],
'default' => [
$default_revision_id,
$rel_latest_version_relationship_url,
$rel_latest_version_related_url,
],
'forward' => [
$forward_revision_id,
$rel_working_copy_relationship_url,
$rel_working_copy_related_url,
],
];
$default_revision_types = [
'canonical',
'default',
];
foreach ($test_relationship_urls as $relationship_type => $revision_case) {
[
$revision_id,
$relationship_url,
$related_url,
] = $revision_case;
// Load the revision that will be requested.
$this->entityStorage
->resetCache([
$entity->id(),
]);
if ($revision_id === NULL) {
$revision = $this->entityStorage
->load($entity->id());
}
else {
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
$storage = $this->entityStorage;
$revision = $storage->loadRevision($revision_id);
}
// Request the relationship resource without access to the relationship
// field.
$actual_response = $this->request('GET', $relationship_url, $request_options);
$expected_response = $this->getExpectedGetRelationshipResponse('field_jsonapi_test_entity_ref', $revision);
$expected_document = $expected_response->getResponseData();
$expected_cacheability = $expected_response->getCacheableMetadata();
$expected_document['errors'][0]['links']['via']['href'] = $relationship_url->toString();
// Only add node type check tags for non-default revisions.
$expected_cache_tags = !in_array($relationship_type, $default_revision_types, TRUE) ? Cache::mergeTags($expected_cacheability->getCacheTags(), $this->getExtraRevisionCacheTags()) : $expected_cacheability->getCacheTags();
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3451483.
$actual_response = $actual_response->withoutHeader('X-Drupal-Dynamic-Cache');
$this->assertResourceResponse(403, $expected_document, $actual_response, $expected_cache_tags, $expected_cacheability->getCacheContexts());
// Request the related route.
$actual_response = $this->request('GET', $related_url, $request_options);
// @todo Refactor self::getExpectedRelatedResponses() into a function which returns a single response.
$expected_response = $this->getExpectedRelatedResponses([
'field_jsonapi_test_entity_ref',
], $request_options, $revision)['field_jsonapi_test_entity_ref'];
$expected_document = $expected_response->getResponseData();
$expected_cacheability = $expected_response->getCacheableMetadata();
$expected_document['errors'][0]['links']['via']['href'] = $related_url->toString();
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3451483.
$actual_response = $actual_response->withoutHeader('X-Drupal-Dynamic-Cache');
$this->assertResourceResponse(403, $expected_document, $actual_response, $expected_cache_tags, $expected_cacheability->getCacheContexts());
}
$this->grantPermissionsToTestedRole([
'field_jsonapi_test_entity_ref view access',
]);
foreach ($test_relationship_urls as $relationship_type => $revision_case) {
[
$revision_id,
$relationship_url,
$related_url,
] = $revision_case;
// Load the revision that will be requested.
$this->entityStorage
->resetCache([
$entity->id(),
]);
if ($revision_id === NULL) {
$revision = $this->entityStorage
->load($entity->id());
}
else {
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
$storage = $this->entityStorage;
$revision = $storage->loadRevision($revision_id);
}
// Request the relationship resource after granting access to the
// relationship field.
$actual_response = $this->request('GET', $relationship_url, $request_options);
$expected_response = $this->getExpectedGetRelationshipResponse('field_jsonapi_test_entity_ref', $revision);
$expected_document = $expected_response->getResponseData();
$expected_document['links']['self']['href'] = $relationship_url->setAbsolute()
->toString();
$expected_cacheability = $expected_response->getCacheableMetadata();
// Only add node type check tags for non-default revisions.
$expected_cache_tags = !in_array($relationship_type, $default_revision_types, TRUE) ? Cache::mergeTags($expected_cacheability->getCacheTags(), $this->getExtraRevisionCacheTags()) : $expected_cacheability->getCacheTags();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cacheability->getCacheContexts(), NULL, TRUE);
// Request the related route.
$actual_response = $this->request('GET', $related_url, $request_options);
$expected_response = $this->getExpectedRelatedResponse('field_jsonapi_test_entity_ref', $request_options, $revision);
$expected_document = $expected_response->getResponseData();
$expected_cacheability = $expected_response->getCacheableMetadata();
$expected_document['links']['self']['href'] = $related_url->toString();
$expected_cache_tags = !in_array($relationship_type, $default_revision_types, TRUE) ? Cache::mergeTags($expected_cacheability->getCacheTags(), $this->getExtraRevisionCacheTags()) : $expected_cacheability->getCacheTags();
$this->assertResourceResponse(200, $expected_document, $actual_response, $expected_cache_tags, $expected_cacheability->getCacheContexts(), NULL, TRUE);
}
$this->config('jsonapi.settings')
->set('read_only', FALSE)
->save(TRUE);
// Ensures that PATCH and DELETE on individual resources with a
// `resourceVersion` query parameter is not supported.
$individual_urls = [
$original_revision_id_url,
$latest_revision_id_url,
$rel_latest_version_url,
$rel_working_copy_url,
];
$request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json';
foreach ($individual_urls as $url) {
foreach ([
'PATCH',
'DELETE',
] as $method) {
$actual_response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(400, sprintf('%s requests with a `%s` query parameter are not supported.', $method, 'resourceVersion'), $url, $actual_response);
}
}
// Ensures that PATCH, POST and DELETE on relationship resources with a
// `resourceVersion` query parameter is not supported.
$relationship_urls = [
$original_revision_id_relationship_url,
$latest_revision_id_relationship_url,
$rel_latest_version_relationship_url,
$rel_working_copy_relationship_url,
];
foreach ($relationship_urls as $url) {
foreach ([
'PATCH',
'POST',
'DELETE',
] as $method) {
$actual_response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(400, sprintf('%s requests with a `%s` query parameter are not supported.', $method, 'resourceVersion'), $url, $actual_response);
}
}
// Ensures that POST on collection resources with a `resourceVersion` query
// parameter is not supported.
$collection_urls = [
$rel_latest_version_collection_url,
$rel_working_copy_collection_url,
];
foreach ($collection_urls as $url) {
foreach ([
'POST',
] as $method) {
$actual_response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(400, sprintf('%s requests with a `%s` query parameter are not supported.', $method, 'resourceVersion'), $url, $actual_response);
}
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.