upgrades core to 8.4.2

This commit is contained in:
Bachir Soussi Chiadmi
2017-11-14 16:09:54 +01:00
parent bd60eff9b3
commit c2b4e25be4
3504 changed files with 140306 additions and 38684 deletions
@@ -0,0 +1,63 @@
<?php
/**
* @file
* Test fixture for \Drupal\rest\Tests\Update\RestExportAuthCorrectionUpdateTest.
*/
use Drupal\Core\Database\Database;
use Drupal\Core\Serialization\Yaml;
$connection = Database::getConnection();
// Set the schema version.
$connection->insert('key_value')
->fields([
'collection' => 'system.schema',
'name' => 'rest',
'value' => 'i:8000;',
])
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['rest'] = 0;
$extensions['module']['serialization'] = 0;
$extensions['module']['basic_auth'] = 0;
$connection->update('config')
->fields([
'data' => serialize($extensions),
])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();
$connection->insert('config')
->fields([
'name' => 'rest.settings',
'data' => serialize([
'link_domain' => '~',
]),
'collection' => '',
])
->execute();
$connection->insert('config')
->fields([
'name' => 'views.view.rest_export_with_authorization_correction',
])
->execute();
$connection->merge('config')
->condition('name', 'views.view.rest_export_with_authorization_correction')
->condition('collection', '')
->fields([
'data' => serialize(Yaml::decode(file_get_contents('core/modules/views/tests/modules/views_test_config/test_views/views.view.rest_export_with_authorization_correction.yml'))),
])
->execute();
@@ -6,8 +6,8 @@ package: Testing
dependencies:
- config_test
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
# Information added by Drupal.org packaging script on 2017-11-03
version: '8.4.2'
core: '8.x'
project: 'drupal'
datestamp: 1502903957
datestamp: 1509719929
@@ -7,8 +7,8 @@ package: Testing
dependencies:
- rest
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
# Information added by Drupal.org packaging script on 2017-11-03
version: '8.4.2'
core: '8.x'
project: 'drupal'
datestamp: 1502903957
datestamp: 1509719929
@@ -8,8 +8,8 @@ dependencies:
- rest
- views
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
# Information added by Drupal.org packaging script on 2017-11-03
version: '8.4.2'
core: '8.x'
project: 'drupal'
datestamp: 1502903957
datestamp: 1509719929
@@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\rest\Functional;
/**
* Trait for ResourceTestBase subclasses formatting expected timestamp data.
*/
trait BcTimestampNormalizerUnixTestTrait {
/**
* Formats a UNIX timestamp.
*
* Depending on the 'bc_timestamp_normalizer_unix' setting. The return will be
* an RFC3339 date string or the same timestamp that was passed in.
*
* @param int $timestamp
* The timestamp value to format.
*
* @return string|int
* The formatted RFC3339 date string or UNIX timestamp.
*
* @see \Drupal\serialization\Normalizer\TimestampItemNormalizer
*/
protected function formatExpectedTimestampItemValues($timestamp) {
// If the setting is enabled, just return the timestamp as-is now.
if ($this->config('serialization.settings')->get('bc_timestamp_normalizer_unix')) {
return ['value' => $timestamp];
}
// Otherwise, format the date string to the same that
// \Drupal\serialization\Normalizer\TimestampItemNormalizer will produce.
$date = new \DateTime();
$date->setTimestamp($timestamp);
$date->setTimezone(new \DateTimeZone('UTC'));
// Format is also added to the expected return values.
return [
'value' => $date->format(\DateTime::RFC3339),
'format' => \DateTime::RFC3339,
];
}
}
@@ -71,7 +71,7 @@ trait CookieResourceTestTrait {
$this->sessionCookie = explode(';', $response->getHeader('Set-Cookie')[0], 2)[0];
// Parse and store the CSRF token and logout token.
$data = $this->serializer->decode((string)$response->getBody(), static::$format);
$data = $this->serializer->decode((string) $response->getBody(), static::$format);
$this->csrfToken = $data['csrf_token'];
$this->logoutToken = $data['logout_token'];
}
@@ -122,9 +122,7 @@ abstract class BlockResourceTestBase extends EntityResourceTestBase {
protected function getExpectedCacheTags() {
// Because the 'user.permissions' cache context is missing, the cache tag
// for the anonymous user role is never added automatically.
return array_values(array_filter(parent::getExpectedCacheTags(), function ($tag) {
return $tag !== 'config:user.role.anonymous';
}));
return array_values(array_diff(parent::getExpectedCacheTags(), ['config:user.role.anonymous']));
}
/**
@@ -6,13 +6,14 @@ use Drupal\comment\Entity\Comment;
use Drupal\comment\Entity\CommentType;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class CommentResourceTestBase extends EntityResourceTestBase {
use CommentTestTrait;
use CommentTestTrait, BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
@@ -148,14 +149,10 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
],
],
'created' => [
[
'value' => 123456789,
],
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
[
'value' => $this->entity->getChangedTime(),
],
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'default_langcode' => [
[
@@ -222,7 +219,7 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
],
'entity_id' => [
[
'target_id' => EntityTest::load(1)->id(),
'target_id' => (int) EntityTest::load(1)->id(),
],
],
'field_name' => [
@@ -280,9 +277,9 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
$response = $this->request('POST', $url, $request_options);
// @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
$this->assertSame(500, $response->getStatusCode());
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
$this->assertSame('{"message":"A fatal error occurred: Internal Server Error"}', (string) $response->getBody());
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
$this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Symfony\Component\HttpKernel\Exception\HttpException</em>: Internal Server Error in <em class="placeholder">Drupal\rest\Plugin\rest\resource\EntityResource-&gt;post()</em>', (string) $response->getBody());
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// DX: 422 when missing 'entity_id' field.
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format);
@@ -291,23 +288,22 @@ abstract class CommentResourceTestBase extends EntityResourceTestBase {
try {
$response = $this->request('POST', $url, $request_options);
// This happens on DrupalCI.
//$this->assertSame(500, $response->getStatusCode());
// $this->assertSame(500, $response->getStatusCode());
}
catch (\Exception $e) {
// This happens on Wim's local machine.
//$this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
// $this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
}
//$response = $this->request('POST', $url, $request_options);
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// $response = $this->request('POST', $url, $request_options);
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// DX: 422 when missing 'entity_type' field.
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['field_name' => TRUE]), static::$format);
$response = $this->request('POST', $url, $request_options);
// @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
// @todo Uncomment, remove next 2 lines in https://www.drupal.org/node/2820364.
$this->assertSame(500, $response->getStatusCode());
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
$this->assertSame('{"message":"A fatal error occurred: Field is unknown."}', (string) $response->getBody());
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
}
/**
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class ContactFormJsonAnonTest extends ContactFormResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class ContactFormJsonBasicAuthTest extends ContactFormResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class ContactFormJsonCookieTest extends ContactFormResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,104 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ContactForm;
use Drupal\contact\Entity\ContactForm;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class ContactFormResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['contact'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'contact_form';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [];
/**
* @var \Drupal\contact\Entity\ContactForm
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access site-wide contact form']);
default:
$this->grantPermissionsToTestedRole(['administer contact forms']);
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$contact_form = ContactForm::create([
'id' => 'llama',
'label' => 'Llama',
'message' => 'Let us know what you think about llamas',
'reply' => 'Llamas are indeed awesome!',
'recipients' => [
'llama@example.com',
'contact@example.com',
],
]);
$contact_form->save();
return $contact_form;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'id' => 'llama',
'label' => 'Llama',
'langcode' => 'en',
'message' => 'Let us know what you think about llamas',
'recipients' => [
'llama@example.com',
'contact@example.com',
],
'redirect' => NULL,
'reply' => 'Llamas are indeed awesome!',
'status' => TRUE,
'uuid' => $this->entity->uuid(),
'weight' => 0,
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
return "The 'access site-wide contact form' permission is required.";
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class DateFormatJsonAnonTest extends DateFormatResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class DateFormatJsonBasicAuthTest extends DateFormatResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class DateFormatJsonCookieTest extends DateFormatResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\DateFormat;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
/**
* ResourceTestBase for DateFormat entity.
*/
abstract class DateFormatResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'date_format';
/**
* The DateFormat entity.
*
* @var \Drupal\Core\Datetime\DateFormatInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer site configuration']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a date format.
$date_format = DateFormat::create([
'id' => 'llama',
'label' => 'Llama',
'pattern' => 'F d, Y',
]);
$date_format->save();
return $date_format;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'id' => 'llama',
'label' => 'Llama',
'langcode' => 'en',
'locked' => FALSE,
'pattern' => 'F d, Y',
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class EntityFormModeJsonAnonTest extends EntityFormModeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class EntityFormModeJsonBasicAuthTest extends EntityFormModeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class EntityFormModeJsonCookieTest extends EntityFormModeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityFormMode;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\Core\Entity\Entity\EntityFormMode;
abstract class EntityFormModeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*
* @todo: Remove 'field_ui' when https://www.drupal.org/node/2867266.
*/
public static $modules = ['user', 'field_ui'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'entity_form_mode';
/**
* @var \Drupal\Core\Entity\EntityFormModeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer display modes']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$entity_form_mode = EntityFormMode::create([
'id' => 'user.test',
'label' => 'Test',
'targetEntityType' => 'user',
]);
$entity_form_mode->save();
return $entity_form_mode;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'cache' => TRUE,
'dependencies' => [
'module' => [
'user',
],
],
'id' => 'user.test',
'label' => 'Test',
'langcode' => 'en',
'status' => TRUE,
'targetEntityType' => 'user',
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -4,11 +4,13 @@ namespace Drupal\Tests\rest\Functional\EntityResource;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\rest\ResourceResponseInterface;
use Drupal\Tests\rest\Functional\ResourceTestBase;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;
@@ -149,7 +151,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
// Calculate REST Resource config entity ID.
static::$resourceConfigId = 'entity.' . static::$entityTypeId;
$this->serializer = $this->container->get('serializer');
$this->entityStorage = $this->container->get('entity_type.manager')
->getStorage(static::$entityTypeId);
@@ -229,7 +230,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return $this->getExpectedBCUnauthorizedAccessMessage($method);
return parent::getExpectedUnauthorizedAccessMessage($method);
}
$permission = $this->entity->getEntityType()->getAdminPermission();
@@ -253,13 +254,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
return "$message.";
}
/**
* {@inheritdoc}
*/
protected function getExpectedBcUnauthorizedAccessMessage($method) {
return "The 'restful " . strtolower($method) . " entity:" . $this->entity->getEntityTypeId() . "' permission is required.";
}
/**
* The expected cache tags for the GET/HEAD response of the test entity.
*
@@ -334,7 +328,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$url->setOption('query', []);
// DX: 406 when ?_format is missing, except when requesting a canonical HTML
// route.
$response = $this->request('GET', $url, $request_options);
@@ -379,13 +372,24 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$this->assertArrayNotHasKey('Link', $response->getHeaders());
$this->setUpAuthorization('GET');
// 200 for well-formed HEAD request.
$response = $this->request('HEAD', $url, $request_options);
$this->assertResourceResponse(200, '', $response);
// @todo Entity resources with URLs that begin with '/admin/' are marked as
// administrative (see https://www.drupal.org/node/2874938), which
// excludes them from Dynamic Page Cache (see
// https://www.drupal.org/node/2877528). When either of those issues is
// fixed, remove the if-test and the 'else' block.
if (strpos($this->entity->getEntityType()->getLinkTemplate('canonical'), '/admin/') !== 0) {
$this->assertTrue($response->hasHeader('X-Drupal-Dynamic-Cache'));
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Dynamic-Cache'));
}
else {
$this->assertFalse($response->hasHeader('X-Drupal-Dynamic-Cache'));
}
if (!$this->account) {
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Cache'));
}
@@ -395,13 +399,53 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$head_headers = $response->getHeaders();
// 200 for well-formed GET request. Page Cache hit because of HEAD request.
// Same for Dynamic Page Cache hit.
$response = $this->request('GET', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
if (!static::$auth) {
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Cache'));
// @todo Entity resources with URLs that begin with '/admin/' are marked as
// administrative (see https://www.drupal.org/node/2874938), which
// excludes them from Dynamic Page Cache (see
// https://www.drupal.org/node/2877528). When either of those issues is
// fixed, remove the if-test and the 'else' block.
if (strpos($this->entity->getEntityType()->getLinkTemplate('canonical'), '/admin/') !== 0) {
$this->assertTrue($response->hasHeader('X-Drupal-Dynamic-Cache'));
if (!static::$auth) {
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Cache'));
$this->assertSame(['MISS'], $response->getHeader('X-Drupal-Dynamic-Cache'));
}
else {
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
$this->assertSame(['HIT'], $response->getHeader('X-Drupal-Dynamic-Cache'));
// Assert that Dynamic Page Cache did not store a ResourceResponse object,
// which needs serialization after every cache hit. Instead, it should
// contain a flattened response. Otherwise performance suffers.
// @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber::flattenResponse()
$cache_items = $this->container->get('database')
->query("SELECT cid, data FROM {cache_dynamic_page_cache} WHERE cid LIKE :pattern", [
':pattern' => '%[route]=rest.%',
])
->fetchAllAssoc('cid');
$this->assertCount(2, $cache_items);
$found_cache_redirect = FALSE;
$found_cached_response = FALSE;
foreach ($cache_items as $cid => $cache_item) {
$cached_data = unserialize($cache_item->data);
if (!isset($cached_data['#cache_redirect'])) {
$found_cached_response = TRUE;
$cached_response = $cached_data['#response'];
$this->assertNotInstanceOf(ResourceResponseInterface::class, $cached_response);
$this->assertInstanceOf(CacheableResponseInterface::class, $cached_response);
}
else {
$found_cache_redirect = TRUE;
}
}
$this->assertTrue($found_cache_redirect);
$this->assertTrue($found_cached_response);
}
}
else {
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
$this->assertFalse($response->hasHeader('X-Drupal-Dynamic-Cache'));
}
$cache_tags_header_value = $response->getHeader('X-Drupal-Cache-Tags')[0];
$this->assertEquals($this->getExpectedCacheTags(), empty($cache_tags_header_value) ? [] : explode(' ', $cache_tags_header_value));
@@ -411,9 +455,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
// for the keys with the array order the same (it needs to match with
// identical comparison).
$expected = $this->getExpectedNormalizedEntity();
ksort($expected);
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
ksort($actual);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
// Not only assert the normalization, also assert deserialization of the
@@ -424,11 +468,11 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
if ($this->entity->getEntityType()->getLinkTemplates()) {
$this->assertArrayHasKey('Link', $response->getHeaders());
$link_relation_type_manager = $this->container->get('plugin.manager.link_relation_type');
$expected_link_relation_headers = array_map(function ($rel) use ($link_relation_type_manager) {
$definition = $link_relation_type_manager->getDefinition($rel, FALSE);
return (!empty($definition['uri']))
? $definition['uri']
: $rel;
$expected_link_relation_headers = array_map(function ($relation_name) use ($link_relation_type_manager) {
$link_relation_type = $link_relation_type_manager->createInstance($relation_name);
return $link_relation_type->isRegistered()
? $link_relation_type->getRegisteredName()
: $link_relation_type->getExtensionUri();
}, array_keys($this->entity->getEntityType()->getLinkTemplates()));
$parse_rel_from_link_header = function ($value) use ($link_relation_type_manager) {
$matches = [];
@@ -453,12 +497,14 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
}
$this->assertSame($get_headers, $head_headers);
// BC: serialization_update_8302().
// Only run this for fieldable entities. It doesn't make sense for config
// entities as config values are already casted. They also run through the
// ConfigEntityNormalizer, which doesn't deal with fields individually.
if ($this->entity instanceof FieldableEntityInterface) {
// Test primitive data casting BC (strings).
$this->config('serialization.settings')->set('bc_primitives_as_strings', TRUE)->save(TRUE);
// Rebuild the container so new config is reflected in the removal of the
// Rebuild the container so new config is reflected in the addition of the
// PrimitiveDataNormalizer.
$this->rebuildAll();
@@ -474,10 +520,48 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
// Config entities are not affected.
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
$expected = static::castToString($expected);
ksort($expected);
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
ksort($actual);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
// Reset the config value and rebuild.
$this->config('serialization.settings')->set('bc_primitives_as_strings', FALSE)->save(TRUE);
$this->rebuildAll();
}
// BC: serialization_update_8401().
// Only run this for fieldable entities. It doesn't make sense for config
// entities as config values always use the raw values (as per the config
// schema), returned directly from the ConfigEntityNormalizer, which
// doesn't deal with fields individually.
if ($this->entity instanceof FieldableEntityInterface) {
// Test the BC settings for timestamp values.
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', TRUE)->save(TRUE);
// Rebuild the container so new config is reflected in the addition of the
// TimestampItemNormalizer.
$this->rebuildAll();
$response = $this->request('GET', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// This ensures the BC layer for bc_timestamp_normalizer_unix works as
// expected. This method should be using
// ::formatExpectedTimestampValue() to generate the timestamp value. This
// will take into account the above config setting.
$expected = $this->getExpectedNormalizedEntity();
// Config entities are not affected.
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
static::recursiveKSort($actual);
$this->assertSame($expected, $actual);
// Reset the config value and rebuild.
$this->config('serialization.settings')->set('bc_timestamp_normalizer_unix', FALSE)->save(TRUE);
$this->rebuildAll();
}
@@ -531,17 +615,17 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
// DX: 406 when requesting unsupported format.
$response = $this->request('GET', $url, $request_options);
$this->assert406Response($response);
$this->assertNotSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
$request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
// DX: 406 when requesting unsupported format but specifying Accept header.
// @todo Update in https://www.drupal.org/node/2825347.
// DX: 406 when requesting unsupported format but specifying Accept header:
// should result in a text/plain response.
$response = $this->request('GET', $url, $request_options);
$this->assert406Response($response);
$this->assertSame(['application/json'], $response->getHeader('Content-Type'));
$this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
$url = Url::fromRoute('rest.entity.' . static::$entityTypeId . '.GET.' . static::$format);
@@ -580,6 +664,27 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
return $normalization;
}
/**
* Recursively sorts an array by key.
*
* @param array $array
* An array to sort.
*
* @return array
* The sorted array.
*/
protected static function recursiveKSort(array &$array) {
// First, sort the main array.
ksort($array);
// Then check for child arrays.
foreach ($array as $key => &$value) {
if (is_array($value)) {
static::recursiveKSort($value);
}
}
}
/**
* Tests a POST request for an entity, plus edge cases to ensure good DX.
*/
@@ -635,7 +740,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$response = $this->request('POST', $url, $request_options);
$this->assertSame(415, $response->getStatusCode());
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
$this->assertContains(htmlspecialchars('No "Content-Type" request header specified'), (string) $response->getBody());
$this->assertContains('A client error happened', (string) $response->getBody());
$url->setOption('query', ['_format' => static::$format]);
@@ -741,6 +846,25 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$this->assertSame([], $response->getHeader('Location'));
}
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
// Assert that the entity was indeed created, and that the response body
// contains the serialized created entity.
$created_entity = $this->entityStorage->loadUnchanged(static::$firstCreatedEntityId);
$created_entity_normalization = $this->serializer->normalize($created_entity, static::$format, ['account' => $this->account]);
// @todo Remove this if-test in https://www.drupal.org/node/2543726: execute
// its body unconditionally.
if (static::$entityTypeId !== 'taxonomy_term') {
$this->assertSame($created_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
}
// Assert that the entity was indeed created using the POSTed values.
foreach ($this->getNormalizedPostEntity() as $field_name => $field_normalization) {
// Some top-level keys in the normalization may not be fields on the
// entity (for example '_links' and '_embedded' in the HAL normalization).
if ($created_entity->hasField($field_name)) {
// Subset, not same, because we can e.g. send just the target_id for the
// bundle in a POST request; the response will include more properties.
$this->assertArraySubset(static::castToString($field_normalization), $created_entity->get($field_name)->getValue(), TRUE);
}
}
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
@@ -769,6 +893,17 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$this->assertSame([], $response->getHeader('Location'));
}
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
// BC: old default POST URLs have their path updated by the inbound path
// processor \Drupal\rest\PathProcessor\PathProcessorEntityResourceBC to the
// new URL, which is derived from the 'create' link template if an entity
// type specifies it.
if ($this->entity->getEntityType()->hasLinkTemplate('create')) {
$this->entityStorage->load(static::$secondCreatedEntityId)->delete();
$old_url = Url::fromUri('base:entity/' . static::$entityTypeId);
$response = $this->request('POST', $old_url, $request_options);
$this->assertResourceResponse(201, FALSE, $response);
}
}
/**
@@ -807,6 +942,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$this->assertSame(405, $response->getStatusCode());
$this->assertSame(['GET, POST, HEAD'], $response->getHeader('Allow'));
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
$this->assertContains('A client error happened', (string) $response->getBody());
}
else {
$this->assertSame(404, $response->getStatusCode());
@@ -836,7 +972,7 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$response = $this->request('PATCH', $url, $request_options);
$this->assertSame(415, $response->getStatusCode());
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
$this->assertTrue(FALSE !== strpos((string) $response->getBody(), htmlspecialchars('No "Content-Type" request header specified')));
$this->assertContains('A client error happened', (string) $response->getBody());
$url->setOption('query', ['_format' => static::$format]);
@@ -863,7 +999,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$this->assertResourceErrorResponse(400, 'Syntax error', $response);
$request_options[RequestOptions::BODY] = $parseable_invalid_request_body;
@@ -945,10 +1080,25 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
$this->assertFalse($response->hasHeader('X-Drupal-Cache'));
// Assert that the entity was indeed updated, and that the response body
// contains the serialized updated entity.
$updated_entity = $this->entityStorage->loadUnchanged($this->entity->id());
$updated_entity_normalization = $this->serializer->normalize($updated_entity, static::$format, ['account' => $this->account]);
$this->assertSame($updated_entity_normalization, $this->serializer->decode((string) $response->getBody(), static::$format));
// Assert that the entity was indeed created using the PATCHed values.
foreach ($this->getNormalizedPatchEntity() as $field_name => $field_normalization) {
// Some top-level keys in the normalization may not be fields on the
// entity (for example '_links' and '_embedded' in the HAL normalization).
if ($updated_entity->hasField($field_name)) {
// Subset, not same, because we can e.g. send just the target_id for the
// bundle in a PATCH request; the response will include more properties.
$this->assertArraySubset(static::castToString($field_normalization), $updated_entity->get($field_name)->getValue(), TRUE);
}
}
// Ensure that fields do not get deleted if they're not present in the PATCH
// request. Test this using the configurable field that we added, but which
// is not sent in the PATCH request.
$this->assertSame('All the faith he had had had had no effect on the outcome of his life.', $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test')->value);
$this->assertSame('All the faith he had had had had no effect on the outcome of his life.', $updated_entity->get('field_rest_test')->value);
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
@@ -992,13 +1142,14 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$request_options = [];
// DX: 405 when resource not provisioned, but HTML if canonical route. Plain
// DX: 404 when resource not provisioned, but 405 if canonical route. Plain
// text or HTML response because missing ?_format query string.
$response = $this->request('DELETE', $url, $request_options);
if ($has_canonical_url) {
$this->assertSame(405, $response->getStatusCode());
$this->assertSame(['GET, POST, HEAD'], $response->getHeader('Allow'));
$this->assertSame(['text/html; charset=UTF-8'], $response->getHeader('Content-Type'));
$this->assertContains('A client error happened', (string) $response->getBody());
}
else {
$this->assertSame(404, $response->getStatusCode());
@@ -1099,10 +1250,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 400 when incorrect entity type bundle is specified.
// @todo Change to 422 in https://www.drupal.org/node/2827084.
// DX: 422 when incorrect entity type bundle is specified.
$response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(400, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response);
$this->assertResourceErrorResponse(422, '"bad_bundle_name" is not a valid bundle type for denormalization.', $response);
}
@@ -1110,10 +1260,9 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 400 when no entity type bundle is specified.
// @todo Change to 422 in https://www.drupal.org/node/2827084.
// DX: 422 when no entity type bundle is specified.
$response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(400, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response);
$this->assertResourceErrorResponse(422, sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_field_name), $response);
}
}
@@ -1125,7 +1274,10 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
*/
protected function getEntityResourceUrl() {
$has_canonical_url = $this->entity->hasLinkTemplate('canonical');
return $has_canonical_url ? $this->entity->toUrl() : Url::fromUri('base:entity/' . static::$entityTypeId . '/' . $this->entity->id());
// Note that the 'canonical' link relation type must be specified explicitly
// in the call to ::toUrl(). 'canonical' is the default for
// \Drupal\Core\Entity\Entity::toUrl(), but ConfigEntityBase overrides this.
return $has_canonical_url ? $this->entity->toUrl('canonical') : Url::fromUri('base:entity/' . static::$entityTypeId . '/' . $this->entity->id());
}
/**
@@ -1135,8 +1287,8 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
* The URL to POST to.
*/
protected function getEntityResourcePostUrl() {
$has_canonical_url = $this->entity->hasLinkTemplate('https://www.drupal.org/link-relations/create');
return $has_canonical_url ? $this->entity->toUrl() : Url::fromUri('base:entity/' . static::$entityTypeId);
$has_create_url = $this->entity->hasLinkTemplate('create');
return $has_create_url ? Url::fromUri('internal:' . $this->entity->getEntityType()->getLinkTemplate('create')) : Url::fromUri('base:entity/' . static::$entityTypeId);
}
/**
@@ -3,11 +3,14 @@
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -92,9 +95,7 @@ abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
]
],
'created' => [
[
'value' => (int) $this->entity->get('created')->value,
]
$this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value)
],
'user_id' => [
[
@@ -115,7 +116,11 @@ abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
*/
protected function getNormalizedPostEntity() {
return [
'type' => 'entity_test',
'type' => [
[
'value' => 'entity_test',
],
],
'name' => [
[
'value' => 'Dramallama',
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class EntityTestBundleJsonAnonTest extends EntityTestBundleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class EntityTestBundleJsonBasicAuthTest extends EntityTestBundleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class EntityTestBundleJsonCookieTest extends EntityTestBundleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestBundle;
use Drupal\entity_test\Entity\EntityTestBundle;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class EntityTestBundleResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'entity_test_bundle';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [];
/**
* @var \Drupal\entity_test\Entity\EntityTestBundle
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer entity_test_bundle content']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$entity_test_bundle = EntityTestBundle::create([
'id' => 'camelids',
'label' => 'Camelids',
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
]);
$entity_test_bundle->save();
return $entity_test_bundle;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'id' => 'camelids',
'label' => 'Camelids',
'langcode' => 'en',
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -3,11 +3,14 @@
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTestLabel;
use Drupal\entity_test\Entity\EntityTestLabel;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -94,9 +97,7 @@ abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
],
],
'created' => [
[
'value' => (int) $this->entity->get('created')->value,
],
$this->formatExpectedTimestampItemValues((int) $this->entity->get('created')->value),
],
'user_id' => [
[
@@ -116,7 +117,11 @@ abstract class EntityTestLabelResourceTestBase extends EntityResourceTestBase {
*/
protected function getNormalizedPostEntity() {
return [
'type' => 'entity_test_label',
'type' => [
[
'value' => 'entity_test_label',
],
],
'name' => [
[
'value' => 'label_llama',
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class EntityViewModeJsonAnonTest extends EntityViewModeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class EntityViewModeJsonBasicAuthTest extends EntityViewModeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class EntityViewModeJsonCookieTest extends EntityViewModeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityViewMode;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\Core\Entity\Entity\EntityViewMode;
abstract class EntityViewModeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*
* @todo: Remove 'field_ui' when https://www.drupal.org/node/2867266.
*/
public static $modules = ['user', 'field_ui'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'entity_view_mode';
/**
* @var \Drupal\Core\Entity\EntityViewModeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer display modes']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$entity_view_mode = EntityViewMode::create([
'id' => 'user.test',
'label' => 'Test',
'targetEntityType' => 'user',
]);
$entity_view_mode->save();
return $entity_view_mode;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'cache' => TRUE,
'dependencies' => [
'module' => [
'user',
],
],
'id' => 'user.test',
'label' => 'Test',
'langcode' => 'en',
'status' => TRUE,
'targetEntityType' => 'user',
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -2,11 +2,14 @@
namespace Drupal\Tests\rest\Functional\EntityResource\Feed;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityTest\EntityTestResourceTestBase;
use Drupal\aggregator\Entity\Feed;
abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -92,14 +95,10 @@ abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
]
],
'checked' => [
[
'value' => 123456789
]
$this->formatExpectedTimestampItemValues(123456789),
],
'queued' => [
[
'value' => 123456789
]
$this->formatExpectedTimestampItemValues(123456789),
],
'link' => [
[
@@ -127,9 +126,7 @@ abstract class FeedResourceTestBase extends EntityTestResourceTestBase {
]
],
'modified' => [
[
'value' => 123456789
]
$this->formatExpectedTimestampItemValues(123456789),
],
];
}
@@ -4,6 +4,7 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Item;
use Drupal\aggregator\Entity\Feed;
use Drupal\aggregator\Entity\Item;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
/**
@@ -11,6 +12,8 @@ use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
*/
abstract class ItemResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -113,9 +116,7 @@ abstract class ItemResourceTestBase extends EntityResourceTestBase {
'author' => [],
'description' => [],
'timestamp' => [
[
'value' => 123456789,
],
$this->formatExpectedTimestampItemValues(123456789),
],
'guid' => [],
];
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class MediaJsonAnonTest extends MediaResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class MediaJsonBasicAuthTest extends MediaResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class MediaJsonCookieTest extends MediaResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,264 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Media;
use Drupal\file\Entity\File;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class MediaResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'media';
/**
* @var \Drupal\media\MediaInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed',
];
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['view media']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['create media']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['update any media']);
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['delete any media']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
if (!MediaType::load('camelids')) {
// Create a "Camelids" media type.
$media_type = MediaType::create([
'name' => 'Camelids',
'id' => 'camelids',
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'source' => 'file',
]);
$media_type->save();
// Create the source field.
$source_field = $media_type->getSource()->createSourceField($media_type);
$source_field->getFieldStorageDefinition()->save();
$source_field->save();
$media_type
->set('source_configuration', [
'source_field' => $source_field->getName(),
])
->save();
}
// Create a file to upload.
$file = File::create([
'uri' => 'public://llama.txt',
]);
$file->setPermanent();
$file->save();
// Create a "Llama" media item.
$media = Media::create([
'bundle' => 'camelids',
'field_media_file_1' => [
'target_id' => $file->id(),
],
]);
$media
->setName('Llama')
->setPublished(TRUE)
->setCreatedTime(123456789)
->setOwnerId(static::$auth ? $this->account->id() : 0)
->setRevisionUserId(static::$auth ? $this->account->id() : 0)
->save();
return $media;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$file = File::load(1);
$thumbnail = File::load(2);
$author = User::load($this->entity->getOwnerId());
return [
'mid' => [
[
'value' => 1,
],
],
'uuid' => [
[
'value' => $this->entity->uuid(),
],
],
'vid' => [
[
'value' => 1,
],
],
'langcode' => [
[
'value' => 'en',
],
],
'bundle' => [
[
'target_id' => 'camelids',
'target_type' => 'media_type',
'target_uuid' => MediaType::load('camelids')->uuid(),
],
],
'name' => [
[
'value' => 'Llama',
],
],
'field_media_file_1' => [
[
'description' => NULL,
'display' => NULL,
'target_id' => (int) $file->id(),
'target_type' => 'file',
'target_uuid' => $file->uuid(),
'url' => $file->url(),
],
],
'thumbnail' => [
[
'alt' => 'Thumbnail',
'width' => 180,
'height' => 180,
'target_id' => (int) $thumbnail->id(),
'target_type' => 'file',
'target_uuid' => $thumbnail->uuid(),
'title' => 'Llama',
'url' => $thumbnail->url(),
],
],
'status' => [
[
'value' => TRUE,
],
],
'created' => [
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'revision_created' => [
$this->formatExpectedTimestampItemValues((int) $this->entity->getRevisionCreationTime()),
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'uid' => [
[
'target_id' => (int) $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_user' => [
[
'target_id' => (int) $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_log_message' => [],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'bundle' => [
[
'target_id' => 'camelids',
],
],
'name' => [
[
'value' => 'Dramallama',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
switch ($method) {
case 'GET';
return "The 'view media' permission is required and the media item must be published.";
case 'PATCH':
return 'You are not authorized to update this media entity of bundle camelids.';
case 'DELETE':
return 'You are not authorized to delete this media entity of bundle camelids.';
default:
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
/**
* {@inheritdoc}
*/
public function testPost() {
$this->markTestSkipped('POSTing File Media items is not supported until https://www.drupal.org/node/1927648 is solved.');
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonAnonTest extends MediaTypeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonBasicAuthTest extends MediaTypeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class MediaTypeJsonCookieTest extends MediaTypeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,78 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\MediaType;
use Drupal\media\Entity\MediaType;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class MediaTypeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['media'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'media_type';
/**
* @var \Drupal\media\MediaTypeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer media types']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" media type.
$camelids = MediaType::create([
'name' => 'Camelids',
'id' => 'camelids',
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'source' => 'file',
]);
$camelids->save();
return $camelids;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'description' => 'Camelids are large, strictly herbivorous animals with slender necks and long legs.',
'field_map' => [],
'id' => 'camelids',
'label' => NULL,
'langcode' => 'en',
'new_revision' => FALSE,
'queue_thumbnail_downloads' => FALSE,
'source' => 'file',
'source_configuration' => [
'source_field' => '',
],
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class MenuJsonAnonTest extends MenuResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class MenuJsonBasicAuthTest extends MenuResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class MenuJsonCookieTest extends MenuResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,78 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Menu;
use Drupal\system\Entity\Menu;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class MenuResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'menu';
/**
* @var \Drupal\system\MenuInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer menu']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$menu = Menu::create([
'id' => 'menu',
'label' => 'Menu',
'description' => 'Menu',
]);
$menu->save();
return $menu;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'description' => 'Menu',
'id' => 'menu',
'label' => 'Menu',
'langcode' => 'en',
'locked' => FALSE,
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return [
'user.permissions',
];
}
}
@@ -3,6 +3,7 @@
namespace Drupal\Tests\rest\Functional\EntityResource\MenuLinkContent;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
/**
@@ -10,6 +11,8 @@ use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
*/
abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -161,9 +164,7 @@ abstract class MenuLinkContentResourceTestBase extends EntityResourceTestBase {
],
],
'changed' => [
[
'value' => $this->entity->getChangedTime(),
],
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'default_langcode' => [
[
@@ -4,15 +4,19 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Node;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class NodeResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node'];
public static $modules = ['node', 'path'];
/**
* {@inheritdoc}
@@ -23,13 +27,13 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'uid',
'revision_timestamp',
'revision_uid',
'created',
'changed',
'promote',
'sticky',
'revision_timestamp',
'revision_uid',
'path',
];
/**
@@ -49,6 +53,10 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
$this->grantPermissionsToTestedRole(['access content', 'create camelids content']);
break;
case 'PATCH':
// Do not grant the 'create url aliases' permission to test the case
// when the path field is protected/not accessible, see
// \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase
// for a positive test.
$this->grantPermissionsToTestedRole(['access content', 'edit any camelids content']);
break;
case 'DELETE':
@@ -77,6 +85,7 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
->setCreatedTime(123456789)
->setChangedTime(123456789)
->setRevisionCreationTime(123456789)
->set('path', '/llama')
->save();
return $node;
@@ -120,14 +129,10 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
],
],
'created' => [
[
'value' => 123456789,
],
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
[
'value' => $this->entity->getChangedTime(),
],
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'promote' => [
[
@@ -140,9 +145,7 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
],
],
'revision_timestamp' => [
[
'value' => 123456789,
],
$this->formatExpectedTimestampItemValues(123456789),
],
'revision_translation_affected' => [
[
@@ -171,6 +174,13 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
],
],
'revision_log' => [],
'path' => [
[
'alias' => '/llama',
'pid' => 1,
'langcode' => 'en',
],
],
];
}
@@ -206,4 +216,60 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
/**
* Tests PATCHing a node's path with and without 'create url aliases'.
*
* For a positive test, see the similar test coverage for Term.
*
* @see \Drupal\Tests\rest\Functional\EntityResource\Term\TermResourceTestBase::testPatchPath()
*/
public function testPatchPath() {
$this->initAuthentication();
$this->provisionEntityResource();
$this->setUpAuthorization('GET');
$this->setUpAuthorization('PATCH');
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
// GET node's current normalization.
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
// @todo In https://www.drupal.org/node/2824851, we will be able to stop
// unsetting these fields from the normalization, because
// EntityResource::patch() will ignore any fields that are sent that
// match the current value (and obviously we're sending the current
// value).
$normalization = $this->removeFieldsFromNormalization($normalization, [
'revision_timestamp',
'revision_uid',
'created',
'changed',
'promote',
'sticky',
]);
// Change node's path alias.
$normalization['path'][0]['alias'] .= 's-rule-the-world';
// Create node PATCH request.
$request_options = [];
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// PATCH request: 403 when creating URL aliases unauthorized.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceErrorResponse(403, "Access denied on updating field 'path'.", $response);
// Grant permission to create URL aliases.
$this->grantPermissionsToTestedRole(['create url aliases']);
// Repeat PATCH request: 200.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
$this->assertSame($normalization['path'], $updated_normalization['path']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonAnonTest extends ResponsiveImageStyleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonBasicAuthTest extends ResponsiveImageStyleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonCookieTest extends ResponsiveImageStyleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,133 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ResponsiveImageStyle;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
/**
* ResourceTestBase for ResponsiveImageStyle entity.
*/
abstract class ResponsiveImageStyleResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['responsive_image'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'responsive_image_style';
/**
* The ResponsiveImageStyle entity.
*
* @var \Drupal\responsive_image\ResponsiveImageStyleInterface
*/
protected $entity;
/**
* The effect UUID.
*
* @var string
*/
protected $effectUuid;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer responsive images']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" responsive image style.
$camelids = ResponsiveImageStyle::create([
'id' => 'camelids',
'label' => 'Camelids',
]);
$camelids->setBreakpointGroup('test_group');
$camelids->setFallbackImageStyle('fallback');
$camelids->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'small',
]);
$camelids->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'medium' => 'medium',
'large' => 'large',
],
],
]);
$camelids->save();
return $camelids;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'breakpoint_group' => 'test_group',
'dependencies' => [
'config' => [
'image.style.large',
'image.style.medium',
],
],
'fallback_image_style' => 'fallback',
'id' => 'camelids',
'image_style_mappings' => [
0 => [
'breakpoint_id' => 'test_breakpoint',
'image_mapping' => 'small',
'image_mapping_type' => 'image_style',
'multiplier' => '1x',
],
1 => [
'breakpoint_id' => 'test_breakpoint',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
'medium' => 'medium',
],
],
'image_mapping_type' => 'sizes',
'multiplier' => '2x',
],
],
'label' => 'Camelids',
'langcode' => 'en',
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
return "The 'administer responsive images' permission is required.";
}
}
@@ -132,7 +132,11 @@ abstract class ShortcutResourceTestBase extends EntityResourceTestBase {
'uri' => 'internal:/',
],
],
'shortcut_set' => 'default',
'shortcut_set' => [
[
'target_id' => 'default',
],
],
];
}
@@ -4,14 +4,18 @@ namespace Drupal\Tests\rest\Functional\EntityResource\Term;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use GuzzleHttp\RequestOptions;
abstract class TermResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['taxonomy'];
public static $modules = ['taxonomy', 'path'];
/**
* {@inheritdoc}
@@ -41,8 +45,12 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
case 'POST':
case 'PATCH':
case 'DELETE':
// Grant the 'create url aliases' permission to test the case when
// the path field is accessible, see
// \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase
// for a negative test.
// @todo Update once https://www.drupal.org/node/2824408 lands.
$this->grantPermissionsToTestedRole(['administer taxonomy']);
$this->grantPermissionsToTestedRole(['administer taxonomy', 'create url aliases']);
break;
}
}
@@ -64,7 +72,8 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
// Create a "Llama" taxonomy term.
$term = Term::create(['vid' => $vocabulary->id()])
->setName('Llama')
->setChangedTime(123456789);
->setChangedTime(123456789)
->set('path', '/llama');
$term->save();
return $term;
@@ -107,15 +116,20 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
],
],
'changed' => [
[
'value' => $this->entity->getChangedTime(),
],
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'path' => [
[
'alias' => '/llama',
'pid' => 1,
'langcode' => 'en',
],
],
];
}
@@ -134,6 +148,12 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
'value' => 'Dramallama',
],
],
'description' => [
[
'value' => 'Dramallamas are the coolest camelids.',
'format' => NULL,
],
],
];
}
@@ -159,4 +179,48 @@ abstract class TermResourceTestBase extends EntityResourceTestBase {
}
}
/**
* Tests PATCHing a term's path.
*
* For a negative test, see the similar test coverage for Node.
*
* @see \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase::testPatchPath()
*/
public function testPatchPath() {
$this->initAuthentication();
$this->provisionEntityResource();
$this->setUpAuthorization('GET');
$this->setUpAuthorization('PATCH');
$url = $this->getEntityResourceUrl()->setOption('query', ['_format' => static::$format]);
// GET term's current normalization.
$response = $this->request('GET', $url, $this->getAuthenticationRequestOptions('GET'));
$normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
// @todo In https://www.drupal.org/node/2824851, we will be able to stop
// unsetting these fields from the normalization, because
// EntityResource::patch() will ignore any fields that are sent that
// match the current value (and obviously we're sending the current
// value).
$normalization = $this->removeFieldsFromNormalization($normalization, [
'changed',
]);
// Change term's path alias.
$normalization['path'][0]['alias'] .= 's-rule-the-world';
// Create term PATCH request.
$request_options = [];
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// PATCH request: 200.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
$updated_normalization = $this->serializer->decode((string) $response->getBody(), static::$format);
$this->assertSame($normalization['path'], $updated_normalization['path']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class TourJsonAnonTest extends TourResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class TourJsonBasicAuthTest extends TourResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class TourJsonCookieTest extends TourResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,123 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Tour;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\tour\Entity\Tour;
abstract class TourResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['tour'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'tour';
/**
* @var \Drupal\tour\TourInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['access tour']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$tour = Tour::create([
'id' => 'tour-llama',
'label' => 'Llama tour',
'langcode' => 'en',
'module' => 'tour',
'routes' => [
[
'route_name' => '<front>',
],
],
'tips' => [
'tour-llama-1' => [
'id' => 'tour-llama-1',
'plugin' => 'text',
'label' => 'Llama',
'body' => 'Who handle the awesomeness of llamas?',
'weight' => 100,
'attributes' => [
'data-id' => 'tour-llama-1',
],
],
],
]);
$tour->save();
return $tour;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'dependencies' => [],
'id' => 'tour-llama',
'label' => 'Llama tour',
'langcode' => 'en',
'module' => 'tour',
'routes' => [
[
'route_name' => '<front>',
],
],
'status' => TRUE,
'tips' => [
'tour-llama-1' => [
'id' => 'tour-llama-1',
'plugin' => 'text',
'label' => 'Llama',
'body' => 'Who handle the awesomeness of llamas?',
'weight' => 100,
'attributes' => [
'data-id' => 'tour-llama-1',
],
],
],
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return [
'user.permissions',
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
return "The following permissions are required: 'access tour' OR 'administer site configuration'.";
}
}
@@ -3,12 +3,15 @@
namespace Drupal\Tests\rest\Functional\EntityResource\User;
use Drupal\Core\Url;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class UserResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
@@ -98,14 +101,10 @@ abstract class UserResourceTestBase extends EntityResourceTestBase {
],
],
'created' => [
[
'value' => 123456789,
],
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
[
'value' => $this->entity->getChangedTime(),
],
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'default_langcode' => [
[
@@ -122,7 +121,7 @@ abstract class UserResourceTestBase extends EntityResourceTestBase {
return [
'name' => [
[
'value' => 'Dramallama ' . $this->randomMachineName(),
'value' => 'Dramallama',
],
],
];
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\View;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class ViewJsonAnonTest extends ViewResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}
@@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\View;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class ViewJsonBasicAuthTest extends ViewResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}
@@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\View;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class ViewJsonCookieTest extends ViewResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}
@@ -0,0 +1,99 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\View;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\views\Entity\View;
abstract class ViewResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['views'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'view';
/**
* @var \Drupal\views\ViewEntityInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer views']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$view = View::create([
'id' => 'test_rest',
'label' => 'Test REST',
]);
$view->save();
return $view;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'base_field' => 'nid',
'base_table' => 'node',
'core' => '8.x',
'dependencies' => [],
'description' => '',
'display' => [
'default' => [
'display_plugin' => 'default',
'id' => 'default',
'display_title' => 'Master',
'position' => 0,
'display_options' => [
'display_extenders' => [],
],
'cache_metadata' => [
'max-age' => -1,
'contexts' => [
'languages:language_interface',
'url.query_args',
],
'tags' => [],
],
],
],
'id' => 'test_rest',
'label' => 'Test REST',
'langcode' => 'en',
'module' => 'views',
'status' => TRUE,
'tag' => '',
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return [
'user.permissions',
];
}
}
@@ -0,0 +1,160 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Drupal\Core\Session\AccountInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\rest\Entity\RestResourceConfig;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use GuzzleHttp\RequestOptions;
/**
* Tests the structure of a REST resource.
*
* @group rest
*/
class ResourceTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['hal', 'rest', 'entity_test', 'rest_test'];
/**
* The entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create an entity programmatic.
$this->entity = EntityTest::create([
'name' => $this->randomMachineName(),
'user_id' => 1,
'field_test_text' => [
0 => [
'value' => $this->randomString(),
'format' => 'plain_text',
],
],
]);
$this->entity->save();
Role::load(AccountInterface::ANONYMOUS_ROLE)
->grantPermission('view test entity')
->save();
}
/**
* Tests that a resource without formats cannot be enabled.
*/
public function testFormats() {
RestResourceConfig::create([
'id' => 'entity.entity_test',
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
'configuration' => [
'GET' => [
'supported_auth' => [
'basic_auth',
],
],
],
])->save();
// Verify that accessing the resource returns 406.
$this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
// \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
// non-REST route a match, but a lower quality one: no format restrictions
// means there's always a match and hence when there is no matching REST
// route, the non-REST route is used, but can't render into
// application/hal+json, so it returns a 406.
$this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.');
}
/**
* Tests that a resource without authentication cannot be enabled.
*/
public function testAuthentication() {
RestResourceConfig::create([
'id' => 'entity.entity_test',
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
'configuration' => [
'GET' => [
'supported_formats' => [
'hal_json',
],
],
],
])->save();
// Verify that accessing the resource returns 401.
$this->drupalGet($this->entity->urlInfo()->setRouteParameter('_format', 'hal_json'));
// \Drupal\Core\Routing\RequestFormatRouteFilter considers the canonical,
// non-REST route a match, but a lower quality one: no format restrictions
// means there's always a match and hence when there is no matching REST
// route, the non-REST route is used, but can't render into
// application/hal+json, so it returns a 406.
$this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.');
}
/**
* Tests that serialization_class is optional.
*/
public function testSerializationClassIsOptional() {
RestResourceConfig::create([
'id' => 'serialization_test',
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
'configuration' => [
'POST' => [
'supported_formats' => [
'json',
],
'supported_auth' => [
'cookie',
]
],
],
])->save();
\Drupal::service('router.builder')->rebuildIfNeeded();
Role::load(RoleInterface::ANONYMOUS_ID)
->grantPermission('restful post serialization_test')
->save();
$serialized = $this->container->get('serializer')->serialize(['foo', 'bar'], 'json');
$request_options = [
RequestOptions::HEADERS => ['Content-Type' => 'application/json'],
RequestOptions::BODY => $serialized,
];
/** @var \GuzzleHttp\ClientInterface $client */
$client = $this->getSession()->getDriver()->getClient()->getClient();
$response = $client->request('POST', $this->buildUrl('serialization_test', ['query' => ['_format' => 'json']]), $request_options);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('["foo","bar"]', (string) $response->getBody());
}
/**
* Tests that resource URI paths are formatted properly.
*/
public function testUriPaths() {
/** @var \Drupal\rest\Plugin\Type\ResourcePluginManager $manager */
$manager = \Drupal::service('plugin.manager.rest');
foreach ($manager->getDefinitions() as $resource => $definition) {
foreach ($definition['uri_paths'] as $key => $uri_path) {
$this->assertFalse(strpos($uri_path, '//'), 'The resource URI path does not have duplicate slashes.');
}
}
}
}
@@ -62,6 +62,8 @@ abstract class ResourceTestBase extends BrowserTestBase {
* The REST Resource plugin ID can be calculated from this.
*
* @var string
*
* @see \Drupal\rest\Entity\RestResourceConfig::__construct()
*/
protected static $resourceConfigId = NULL;
@@ -99,6 +101,8 @@ abstract class ResourceTestBase extends BrowserTestBase {
public function setUp() {
parent::setUp();
$this->serializer = $this->container->get('serializer');
// Ensure the anonymous user role has no permissions at all.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
foreach ($user_role->getPermissions() as $permission) {
@@ -172,6 +176,21 @@ abstract class ResourceTestBase extends BrowserTestBase {
\Drupal::service('router.builder')->rebuildIfNeeded();
}
/**
* Return the expected error message.
*
* @param string $method
* The HTTP method (GET, POST, PATCH, DELETE).
*
* @return string
* The error string.
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
$resource_plugin_id = str_replace('.', ':', static::$resourceConfigId);
$permission = 'restful ' . strtolower($method) . ' ' . $resource_plugin_id;
return "The '$permission' permission is required.";
}
/**
* Sets up the necessary authorization.
*
@@ -230,29 +249,6 @@ abstract class ResourceTestBase extends BrowserTestBase {
*/
abstract protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options);
/**
* Return the expected error message.
*
* @param string $method
* The HTTP method (GET, POST, PATCH, DELETE).
*
* @return string
* The error string.
*/
abstract protected function getExpectedUnauthorizedAccessMessage($method);
/**
* Return the default expected error message if the
* bc_entity_resource_permissions is true.
*
* @param string $method
* The HTTP method (GET, POST, PATCH, DELETE).
*
* @return string
* The error string.
*/
abstract protected function getExpectedBcUnauthorizedAccessMessage($method);
/**
* Initializes authentication.
*
@@ -321,6 +317,9 @@ abstract class ResourceTestBase extends BrowserTestBase {
* 'http_errors = FALSE' request option, nor do we want them to have to
* convert Drupal Url objects to strings.
*
* We also don't want to follow redirects automatically, to ensure these tests
* are able to detect when redirects are added or removed.
*
* @see \GuzzleHttp\ClientInterface::request()
*
* @param string $method
@@ -334,6 +333,7 @@ abstract class ResourceTestBase extends BrowserTestBase {
*/
protected function request($method, Url $url, array $request_options) {
$request_options[RequestOptions::HTTP_ERRORS] = FALSE;
$request_options[RequestOptions::ALLOW_REDIRECTS] = FALSE;
$request_options = $this->decorateWithXdebugCookie($request_options);
$client = $this->getSession()->getDriver()->getClient()->getClient();
return $client->request($method, $url->setAbsolute(TRUE)->toString(), $request_options);
@@ -351,12 +351,7 @@ abstract class ResourceTestBase extends BrowserTestBase {
*/
protected function assertResourceResponse($expected_status_code, $expected_body, ResponseInterface $response) {
$this->assertSame($expected_status_code, $response->getStatusCode());
if ($expected_status_code < 400) {
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
}
else {
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
}
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
if ($expected_body !== FALSE) {
$this->assertSame($expected_body, (string) $response->getBody());
}
@@ -0,0 +1,56 @@
<?php
namespace Drupal\Tests\rest\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests that existing sites continue to use permissions for EntityResource.
*
* @see https://www.drupal.org/node/2664780
*
* @group rest
*/
class EntityResourcePermissionsUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['rest', 'serialization'];
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_update_8203.php',
];
}
/**
* Tests rest_update_8203().
*/
public function testBcEntityResourcePermissionSettingAdded() {
$permission_handler = $this->container->get('user.permissions');
$is_rest_resource_permission = function ($permission) {
return $permission['provider'] === 'rest' && (string) $permission['title'] !== 'Administer REST resource configuration';
};
// Make sure we have the expected values before the update.
$rest_settings = $this->config('rest.settings');
$this->assertFalse(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
$this->assertEqual([], array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
$this->runUpdates();
// Make sure we have the expected values after the update.
$rest_settings = $this->config('rest.settings');
$this->assertTrue(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
$this->assertTrue($rest_settings->get('bc_entity_resource_permissions'));
$rest_permissions = array_keys(array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
$this->assertEqual(['restful delete entity:node', 'restful get entity:node', 'restful patch entity:node', 'restful post entity:node'], $rest_permissions);
}
}
@@ -0,0 +1,71 @@
<?php
namespace Drupal\Tests\rest\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests method-granularity REST config is simplified to resource-granularity.
*
* @see https://www.drupal.org/node/2721595
* @see rest_post_update_resource_granularity()
*
* @group rest
*/
class ResourceGranularityUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['rest', 'serialization'];
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_post_update_resource_granularity.php',
];
}
/**
* Tests rest_post_update_simplify_resource_granularity().
*/
public function testMethodGranularityConvertedToResourceGranularity() {
/** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
$resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
// Make sure we have the expected values before the update.
$resource_config_entities = $resource_config_storage->loadMultiple();
$this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities));
$this->assertIdentical('method', $resource_config_entities['entity.node']->get('granularity'));
$this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity'));
$this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity'));
// Read the existing 'entity:comment' and 'entity:user' resource
// configuration so we can verify it after the update.
$comment_resource_configuration = $resource_config_entities['entity.comment']->get('configuration');
$user_resource_configuration = $resource_config_entities['entity.user']->get('configuration');
$this->runUpdates();
// Make sure we have the expected values after the update.
$resource_config_entities = $resource_config_storage->loadMultiple();
$this->assertIdentical(['entity.comment', 'entity.node', 'entity.user'], array_keys($resource_config_entities));
// 'entity:node' should be updated.
$this->assertIdentical('resource', $resource_config_entities['entity.node']->get('granularity'));
$this->assertidentical($resource_config_entities['entity.node']->get('configuration'), [
'methods' => ['GET', 'POST', 'PATCH', 'DELETE'],
'formats' => ['hal_json'],
'authentication' => ['basic_auth'],
]);
// 'entity:comment' should be unchanged.
$this->assertIdentical('method', $resource_config_entities['entity.comment']->get('granularity'));
$this->assertIdentical($comment_resource_configuration, $resource_config_entities['entity.comment']->get('configuration'));
// 'entity:user' should be unchanged.
$this->assertIdentical('method', $resource_config_entities['entity.user']->get('granularity'));
$this->assertIdentical($user_resource_configuration, $resource_config_entities['entity.user']->get('configuration'));
}
}
@@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\rest\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\rest\RestResourceConfigInterface;
/**
* Tests that rest.settings is converted to rest_resource_config entities.
*
* @see https://www.drupal.org/node/2308745
* @see rest_update_8201()
* @see rest_post_update_create_rest_resource_config_entities()
*
* @group rest
*/
class RestConfigurationEntitiesUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['rest', 'serialization'];
/**
* {@inheritdoc}
*/
public function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.rest-rest_update_8201.php',
];
}
/**
* Tests rest_update_8201().
*/
public function testResourcesConvertedToConfigEntities() {
/** @var \Drupal\Core\Entity\EntityStorageInterface $resource_config_storage */
$resource_config_storage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
// Make sure we have the expected values before the update.
$rest_settings = $this->config('rest.settings');
$this->assertTrue(array_key_exists('resources', $rest_settings->getRawData()));
$this->assertTrue(array_key_exists('entity:node', $rest_settings->getRawData()['resources']));
$resource_config_entities = $resource_config_storage->loadMultiple();
$this->assertIdentical([], array_keys($resource_config_entities));
$this->runUpdates();
// Make sure we have the expected values after the update.
$rest_settings = $this->config('rest.settings');
$this->assertFalse(array_key_exists('resources', $rest_settings->getRawData()));
$resource_config_entities = $resource_config_storage->loadMultiple();
$this->assertIdentical(['entity.node'], array_keys($resource_config_entities));
$node_resource_config_entity = $resource_config_entities['entity.node'];
$this->assertIdentical(RestResourceConfigInterface::RESOURCE_GRANULARITY, $node_resource_config_entity->get('granularity'));
$this->assertIdentical([
'methods' => ['GET'],
'formats' => ['json'],
'authentication' => ['basic_auth'],
], $node_resource_config_entity->get('configuration'));
$this->assertIdentical(['module' => ['basic_auth', 'node', 'serialization']], $node_resource_config_entity->getDependencies());
}
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\rest\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Ensures that update hook is run properly for REST Export config.
*
* @group Update
*/
class RestExportAuthUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../../tests/fixtures/update/rest-export-with-authentication.php',
];
}
/**
* Ensures that update hook is run for rest module.
*/
public function testUpdate() {
$this->runUpdates();
// Get particular view.
$view = \Drupal::entityTypeManager()->getStorage('view')->load('rest_export_with_authorization');
$displays = $view->get('display');
$this->assertIdentical($displays['rest_export_1']['display_options']['auth']['basic_auth'], 'basic_auth', 'Basic authentication is set as authentication method.');
}
}
@@ -0,0 +1,87 @@
<?php
namespace Drupal\Tests\rest\Functional\Views;
use Drupal\node\Entity\Node;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Views;
/**
* Tests the display of an excluded field that is used as a token.
*
* @group rest
* @see \Drupal\rest\Plugin\views\display\RestExport
* @see \Drupal\rest\Plugin\views\row\DataFieldRow
*/
class ExcludedFieldTokenTest extends ViewTestBase {
/**
* @var \Drupal\views\ViewExecutable
*/
protected $view;
/**
* The views that are used by this test.
*
* @var array
*/
public static $testViews = ['test_excluded_field_token_display'];
/**
* The modules that need to be installed for this test.
*
* @var array
*/
public static $modules = [
'entity_test',
'rest_test_views',
'node',
'field',
];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['rest_test_views']);
// Create some test content.
for ($i = 1; $i <= 10; $i++) {
Node::create([
'type' => 'article',
'title' => 'Article test ' . $i,
])->save();
}
$this->enableViewsTestModule();
$this->view = Views::getView('test_excluded_field_token_display');
$this->view->setDisplay('rest_export_1');
}
/**
* Tests the display of an excluded title field when used as a token.
*/
public function testExcludedTitleTokenDisplay() {
$actual_json = $this->drupalGet($this->view->getPath(), ['query' => ['_format' => 'json']]);
$this->assertResponse(200);
$expected = [
['nothing' => 'Article test 10'],
['nothing' => 'Article test 9'],
['nothing' => 'Article test 8'],
['nothing' => 'Article test 7'],
['nothing' => 'Article test 6'],
['nothing' => 'Article test 5'],
['nothing' => 'Article test 4'],
['nothing' => 'Article test 3'],
['nothing' => 'Article test 2'],
['nothing' => 'Article test 1'],
];
$this->assertIdentical($actual_json, json_encode($expected));
}
}
@@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\rest\Functional\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Entity\View;
/**
* Tests authentication for REST display.
*
* @group rest
*/
class RestExportAuthTest extends ViewTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['rest', 'views_ui', 'basic_auth'];
/**
* {@inheritdoc}
*/
public function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->drupalLogin($this->drupalCreateUser(['administer views']));
}
/**
* Checks that correct authentication providers are available for choosing.
*
* @link https://www.drupal.org/node/2825204
*/
public function testAuthProvidersOptions() {
$view_id = 'test_view_rest_export';
$view_label = 'Test view (REST export)';
$view_display = 'rest_export_1';
$view_rest_path = 'test-view/rest-export';
// Create new view.
$this->drupalPostForm('admin/structure/views/add', [
'id' => $view_id,
'label' => $view_label,
'show[wizard_key]' => 'users',
'rest_export[path]' => $view_rest_path,
'rest_export[create]' => TRUE,
], t('Save and edit'));
$this->drupalGet("admin/structure/views/nojs/display/$view_id/$view_display/auth");
// The "basic_auth" will always be available since module,
// providing it, has the same name.
$this->assertField('edit-auth-basic-auth', 'Basic auth is available for choosing.');
// The "cookie" authentication provider defined by "user" module.
$this->assertField('edit-auth-cookie', 'Cookie-based auth can be chosen.');
// Wrong behavior in "getAuthOptions()" method makes this option available
// instead of "cookie".
// @see \Drupal\rest\Plugin\views\display\RestExport::getAuthOptions()
$this->assertNoField('edit-auth-user', 'Wrong authentication option is unavailable.');
$this->drupalPostForm(NULL, ['auth[basic_auth]' => 1, 'auth[cookie]' => 1], 'Apply');
$this->drupalPostForm(NULL, [], 'Save');
$view = View::load($view_id);
$this->assertEquals(['basic_auth', 'cookie'], $view->getDisplay('rest_export_1')['display_options']['auth']);
}
}
@@ -0,0 +1,832 @@
<?php
namespace Drupal\Tests\rest\Functional\Views;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Cache\Cache;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Entity\View;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestData;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the serializer style plugin.
*
* @group rest
* @see \Drupal\rest\Plugin\views\display\RestExport
* @see \Drupal\rest\Plugin\views\style\Serializer
* @see \Drupal\rest\Plugin\views\row\DataEntityRow
* @see \Drupal\rest\Plugin\views\row\DataFieldRow
*/
class StyleSerializerTest extends ViewTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field', 'language', 'basic_auth'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_serializer_display_field', 'test_serializer_display_entity', 'test_serializer_display_entity_translated', 'test_serializer_node_display_field', 'test_serializer_node_exposed_filter'];
/**
* A user with administrative privileges to look at test entity and configure views.
*/
protected $adminUser;
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['rest_test_views']);
$this->adminUser = $this->drupalCreateUser(['administer views', 'administer entity_test content', 'access user profiles', 'view test entity']);
// Save some entity_test entities.
for ($i = 1; $i <= 10; $i++) {
EntityTest::create(['name' => 'test_' . $i, 'user_id' => $this->adminUser->id()])->save();
}
$this->enableViewsTestModule();
}
/**
* Checks that the auth options restricts access to a REST views display.
*/
public function testRestViewsAuthentication() {
// Assume the view is hidden behind a permission.
$this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
$this->assertResponse(401);
// Not even logging in would make it possible to see the view, because then
// we are denied based on authentication method (cookie).
$this->drupalLogin($this->adminUser);
$this->drupalGet('test/serialize/auth_with_perm', ['query' => ['_format' => 'json']]);
$this->assertResponse(403);
$this->drupalLogout();
// But if we use the basic auth authentication strategy, we should be able
// to see the page.
$url = $this->buildUrl('test/serialize/auth_with_perm');
$response = \Drupal::httpClient()->get($url, [
'auth' => [$this->adminUser->getUsername(), $this->adminUser->pass_raw],
]);
// Ensure that any changes to variables in the other thread are picked up.
$this->refreshVariables();
$this->assertResponse(200);
}
/**
* Checks the behavior of the Serializer callback paths and row plugins.
*/
public function testSerializerResponses() {
// Test the serialize callback.
$view = Views::getView('test_serializer_display_field');
$view->initDisplay();
$this->executeView($view);
$actual_json = $this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
$this->assertResponse(200);
$this->assertCacheTags($view->getCacheTags());
$this->assertCacheContexts(['languages:language_interface', 'theme', 'request_format']);
// @todo Due to https://www.drupal.org/node/2352009 we can't yet test the
// propagation of cache max-age.
// Test the http Content-type.
$headers = $this->drupalGetHeaders();
$this->assertSame(['application/json'], $headers['Content-Type']);
$expected = [];
foreach ($view->result as $row) {
$expected_row = [];
foreach ($view->field as $id => $field) {
$expected_row[$id] = $field->render($row);
}
$expected[] = $expected_row;
}
$this->assertIdentical($actual_json, json_encode($expected), 'The expected JSON output was found.');
// Test that the rendered output and the preview output are the same.
$view->destroy();
$view->setDisplay('rest_export_1');
// Mock the request content type by setting it on the display handler.
$view->display_handler->setContentType('json');
$output = $view->preview();
$this->assertIdentical($actual_json, (string) drupal_render_root($output), 'The expected JSON preview output was found.');
// Test a 403 callback.
$this->drupalGet('test/serialize/denied');
$this->assertResponse(403);
// Test the entity rows.
$view = Views::getView('test_serializer_display_entity');
$view->initDisplay();
$this->executeView($view);
// Get the serializer service.
$serializer = $this->container->get('serializer');
$entities = [];
foreach ($view->result as $row) {
$entities[] = $row->_entity;
}
$expected = $serializer->serialize($entities, 'json');
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
$this->assertResponse(200);
$this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
$expected_cache_tags = $view->getCacheTags();
$expected_cache_tags[] = 'entity_test_list';
/** @var \Drupal\Core\Entity\EntityInterface $entity */
foreach ($entities as $entity) {
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, $entity->getCacheTags());
}
$this->assertCacheTags($expected_cache_tags);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
$expected = $serializer->serialize($entities, 'hal_json');
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'hal_json']]);
$this->assertIdentical($actual_json, $expected, 'The expected HAL output was found.');
$this->assertCacheTags($expected_cache_tags);
// Change the default format to xml.
$view->setDisplay('rest_export_1');
$view->getDisplay()->setOption('style', [
'type' => 'serializer',
'options' => [
'uses_fields' => FALSE,
'formats' => [
'xml' => 'xml',
],
],
]);
$view->save();
$expected = $serializer->serialize($entities, 'xml');
$actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
$this->assertSame(trim($expected), $actual_xml);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
// Allow multiple formats.
$view->setDisplay('rest_export_1');
$view->getDisplay()->setOption('style', [
'type' => 'serializer',
'options' => [
'uses_fields' => FALSE,
'formats' => [
'xml' => 'xml',
'json' => 'json',
],
],
]);
$view->save();
$expected = $serializer->serialize($entities, 'json');
$actual_json = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
$this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
$expected = $serializer->serialize($entities, 'xml');
$actual_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
$this->assertSame(trim($expected), $actual_xml);
}
/**
* Verifies site maintenance mode functionality.
*/
public function testSiteMaintenance() {
$view = Views::getView('test_serializer_display_field');
$view->initDisplay();
$this->executeView($view);
// Set the site to maintenance mode.
$this->container->get('state')->set('system.maintenance_mode', TRUE);
$this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]);
// Verify that the endpoint is unavailable for anonymous users.
$this->assertResponse(503);
}
/**
* Sets up a request on the request stack with a specified format.
*
* @param string $format
* The new request format.
*/
protected function addRequestWithFormat($format) {
$request = \Drupal::request();
$request = clone $request;
$request->setRequestFormat($format);
\Drupal::requestStack()->push($request);
}
/**
* Tests REST export with views render caching enabled.
*/
public function testRestRenderCaching() {
$this->drupalLogin($this->adminUser);
/** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */
$render_cache = \Drupal::service('render_cache');
// Enable render caching for the views.
/** @var \Drupal\views\ViewEntityInterface $storage */
$storage = View::load('test_serializer_display_entity');
$options = &$storage->getDisplay('default');
$options['display_options']['cache'] = [
'type' => 'tag',
];
$storage->save();
$original = DisplayPluginBase::buildBasicRenderable('test_serializer_display_entity', 'rest_export_1');
// Ensure that there is no corresponding render cache item yet.
$original['#cache'] += ['contexts' => []];
$original['#cache']['contexts'] = Cache::mergeContexts($original['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
$cache_tags = [
'config:views.view.test_serializer_display_entity',
'entity_test:1',
'entity_test:10',
'entity_test:2',
'entity_test:3',
'entity_test:4',
'entity_test:5',
'entity_test:6',
'entity_test:7',
'entity_test:8',
'entity_test:9',
'entity_test_list'
];
$cache_contexts = [
'entity_test_view_grants',
'languages:language_interface',
'theme',
'request_format',
];
$this->assertFalse($render_cache->get($original));
// Request the page, once in XML and once in JSON to ensure that the caching
// varies by it.
$result1 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
$this->addRequestWithFormat('json');
$this->assertHeader('content-type', 'application/json');
$this->assertCacheContexts($cache_contexts);
$this->assertCacheTags($cache_tags);
$this->assertTrue($render_cache->get($original));
$result_xml = $this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'xml']]);
$this->addRequestWithFormat('xml');
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
$this->assertCacheContexts($cache_contexts);
$this->assertCacheTags($cache_tags);
$this->assertTrue($render_cache->get($original));
// Ensure that the XML output is different from the JSON one.
$this->assertNotEqual($result1, $result_xml);
// Ensure that the cached page works.
$result2 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
$this->addRequestWithFormat('json');
$this->assertHeader('content-type', 'application/json');
$this->assertEqual($result2, $result1);
$this->assertCacheContexts($cache_contexts);
$this->assertCacheTags($cache_tags);
$this->assertTrue($render_cache->get($original));
// Create a new entity and ensure that the cache tags are taken over.
EntityTest::create(['name' => 'test_11', 'user_id' => $this->adminUser->id()])->save();
$result3 = Json::decode($this->drupalGet('test/serialize/entity', ['query' => ['_format' => 'json']]));
$this->addRequestWithFormat('json');
$this->assertHeader('content-type', 'application/json');
$this->assertNotEqual($result3, $result2);
// Add the new entity cache tag and remove the first one, because we just
// show 10 items in total.
$cache_tags[] = 'entity_test:11';
unset($cache_tags[array_search('entity_test:1', $cache_tags)]);
$this->assertCacheContexts($cache_contexts);
$this->assertCacheTags($cache_tags);
$this->assertTrue($render_cache->get($original));
}
/**
* Tests the response format configuration.
*/
public function testResponseFormatConfiguration() {
$this->drupalLogin($this->adminUser);
$style_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/style_options';
// Select only 'xml' as an accepted format.
$this->drupalPostForm($style_options, ['style_options[formats][xml]' => 'xml'], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
// Should return a 406.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(406, 'A 406 response was returned when JSON was requested.');
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
$this->assertResponse(200, 'A 200 response was returned when XML was requested.');
// Add 'json' as an accepted format, so we have multiple.
$this->drupalPostForm($style_options, ['style_options[formats][json]' => 'json'], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
// Should return a 200.
// @todo This should be fixed when we have better content negotiation.
$this->drupalGet('test/serialize/field');
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when any format was requested.');
// Should return a 200. Emulates a sample Firefox header.
$this->drupalGet('test/serialize/field', [], ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']);
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when a browser accept header was requested.');
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
$headers = $this->drupalGetHeaders();
$this->assertEqual($headers['Content-Type'], ['application/json'], 'The header Content-type is correct.');
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
$this->assertResponse(200, 'A 200 response was returned when XML was requested');
$headers = $this->drupalGetHeaders();
$this->assertSame(['text/xml; charset=UTF-8'], $headers['Content-Type']);
// Should return a 406.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
// We want to show the first format by default, see
// \Drupal\rest\Plugin\views\style\Serializer::render.
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
// Now configure now format, so all of them should be allowed.
$this->drupalPostForm($style_options, ['style_options[formats][json]' => '0', 'style_options[formats][xml]' => '0'], t('Apply'));
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']]);
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'xml']]);
$this->assertHeader('content-type', 'text/xml; charset=UTF-8');
$this->assertResponse(200, 'A 200 response was returned when XML was requested');
// Should return a 200.
$this->drupalGet('test/serialize/field', ['query' => ['_format' => 'html']]);
// We want to show the first format by default, see
// \Drupal\rest\Plugin\views\style\Serializer::render.
$this->assertHeader('content-type', 'application/json');
$this->assertResponse(200, 'A 200 response was returned when HTML was requested.');
}
/**
* Test the field ID alias functionality of the DataFieldRow plugin.
*/
public function testUIFieldAlias() {
$this->drupalLogin($this->adminUser);
// Test the UI settings for adding field ID aliases.
$this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
$row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
$this->assertLinkByHref($row_options);
// Test an empty string for an alias, this should not be used. This also
// tests that the form can be submitted with no aliases.
$this->drupalPostForm($row_options, ['row_options[field_options][name][alias]' => ''], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$view = Views::getView('test_serializer_display_field');
$view->setDisplay('rest_export_1');
$this->executeView($view);
$expected = [];
foreach ($view->result as $row) {
$expected_row = [];
foreach ($view->field as $id => $field) {
$expected_row[$id] = $field->render($row);
}
$expected[] = $expected_row;
}
$this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
// Test a random aliases for fields, they should be replaced.
$alias_map = [
'name' => $this->randomMachineName(),
// Use # to produce an invalid character for the validation.
'nothing' => '#' . $this->randomMachineName(),
'created' => 'created',
];
$edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
$this->drupalPostForm($row_options, $edit, t('Apply'));
$this->assertText(t('The machine-readable name must contain only letters, numbers, dashes and underscores.'));
// Change the map alias value to a valid one.
$alias_map['nothing'] = $this->randomMachineName();
$edit = ['row_options[field_options][name][alias]' => $alias_map['name'], 'row_options[field_options][nothing][alias]' => $alias_map['nothing']];
$this->drupalPostForm($row_options, $edit, t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$view = Views::getView('test_serializer_display_field');
$view->setDisplay('rest_export_1');
$this->executeView($view);
$expected = [];
foreach ($view->result as $row) {
$expected_row = [];
foreach ($view->field as $id => $field) {
$expected_row[$alias_map[$id]] = $field->render($row);
}
$expected[] = $expected_row;
}
$this->assertIdentical(Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])), $this->castSafeStrings($expected));
}
/**
* Tests the raw output options for row field rendering.
*/
public function testFieldRawOutput() {
$this->drupalLogin($this->adminUser);
// Test the UI settings for adding field ID aliases.
$this->drupalGet('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1');
$row_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options';
$this->assertLinkByHref($row_options);
// Test an empty string for an alias, this should not be used. This also
// tests that the form can be submitted with no aliases.
$values = [
'row_options[field_options][created][raw_output]' => '1',
'row_options[field_options][name][raw_output]' => '1',
];
$this->drupalPostForm($row_options, $values, t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$view = Views::getView('test_serializer_display_field');
$view->setDisplay('rest_export_1');
$this->executeView($view);
$storage = $this->container->get('entity_type.manager')->getStorage('entity_test');
// Update the name for each to include a script tag.
foreach ($storage->loadMultiple() as $entity_test) {
$name = $entity_test->name->value;
$entity_test->set('name', "<script>$name</script>");
$entity_test->save();
}
// Just test the raw 'created' value against each row.
foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
$this->assertIdentical($values['created'], $view->result[$index]->views_test_data_created, 'Expected raw created value found.');
$this->assertIdentical($values['name'], $view->result[$index]->views_test_data_name, 'Expected raw name value found.');
}
// Test result with an excluded field.
$view->setDisplay('rest_export_1');
$view->displayHandlers->get('rest_export_1')->overrideOption('fields', [
'name' => [
'id' => 'name',
'table' => 'views_test_data',
'field' => 'name',
'relationship' => 'none',
],
'created' => [
'id' => 'created',
'exclude' => TRUE,
'table' => 'views_test_data',
'field' => 'created',
'relationship' => 'none',
],
]);
$view->save();
$this->executeView($view);
foreach (Json::decode($this->drupalGet('test/serialize/field', ['query' => ['_format' => 'json']])) as $index => $values) {
$this->assertTrue(!isset($values['created']), 'Excluded value not found.');
}
// Test that the excluded field is not shown in the row options.
$this->drupalGet('admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/row_options');
$this->assertNoText('created');
}
/**
* Tests the live preview output for json output.
*/
public function testLivePreview() {
// We set up a request so it looks like an request in the live preview.
$request = new Request();
$request->query->add([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']);
/** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
$request_stack = \Drupal::service('request_stack');
$request_stack->push($request);
$view = Views::getView('test_serializer_display_entity');
$view->setDisplay('rest_export_1');
$this->executeView($view);
// Get the serializer service.
$serializer = $this->container->get('serializer');
$entities = [];
foreach ($view->result as $row) {
$entities[] = $row->_entity;
}
$expected = $serializer->serialize($entities, 'json');
$view->live_preview = TRUE;
$build = $view->preview();
$rendered_json = $build['#plain_text'];
$this->assertTrue(!isset($build['#markup']) && $rendered_json == $expected, 'Ensure the previewed json is escaped.');
$view->destroy();
$expected = $serializer->serialize($entities, 'xml');
// Change the request format to xml.
$view->setDisplay('rest_export_1');
$view->getDisplay()->setOption('style', [
'type' => 'serializer',
'options' => [
'uses_fields' => FALSE,
'formats' => [
'xml' => 'xml',
],
],
]);
$this->executeView($view);
$build = $view->preview();
$rendered_xml = $build['#plain_text'];
$this->assertEqual($rendered_xml, $expected, 'Ensure we preview xml when we change the request format.');
}
/**
* Tests the views interface for REST export displays.
*/
public function testSerializerViewsUI() {
$this->drupalLogin($this->adminUser);
// Click the "Update preview button".
$this->drupalPostForm('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1', $edit = [], t('Update preview'));
$this->assertResponse(200);
// Check if we receive the expected result.
$result = $this->xpath('//div[@id="views-live-preview"]/pre');
$json_preview = $result[0]->getText();
$this->assertSame($json_preview, $this->drupalGet('test/serialize/field'), 'The expected JSON preview output was found.');
}
/**
* Tests the field row style using fieldapi fields.
*/
public function testFieldapiField() {
$this->drupalCreateContentType(['type' => 'page']);
$node = $this->drupalCreateNode();
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
$this->assertEqual($result[0]['nid'], $node->id());
$this->assertEqual($result[0]['body'], $node->body->processed);
// Make sure that serialized fields are not exposed to XSS.
$node = $this->drupalCreateNode();
$node->body = [
'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
'format' => filter_default_format(),
];
$node->save();
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
$this->assertEqual($result[1]['nid'], $node->id());
$this->assertTrue(strpos($this->getRawContent(), "<script") === FALSE, "No script tag is present in the raw page contents.");
$this->drupalLogin($this->adminUser);
// Add an alias and make the output raw.
$row_options = 'admin/structure/views/nojs/display/test_serializer_node_display_field/rest_export_1/row_options';
// Test an empty string for an alias, this should not be used. This also
// tests that the form can be submitted with no aliases.
$this->drupalPostForm($row_options, ['row_options[field_options][title][raw_output]' => '1'], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$view = Views::getView('test_serializer_node_display_field');
$view->setDisplay('rest_export_1');
$this->executeView($view);
// Test the raw 'created' value against each row.
foreach (Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']])) as $index => $values) {
$this->assertIdentical($values['title'], $view->result[$index]->_entity->title->value, 'Expected raw title value found.');
}
// Test that multiple raw body fields are shown.
// Make the body field unlimited cardinatlity.
$storage_definition = $node->getFieldDefinition('body')->getFieldStorageDefinition();
$storage_definition->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$storage_definition->save();
$this->drupalPostForm($row_options, ['row_options[field_options][body][raw_output]' => '1'], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$node = $this->drupalCreateNode();
$body = [
'value' => '<script type="text/javascript">alert("node-body");</script>' . $this->randomMachineName(32),
'format' => filter_default_format(),
];
// Add two body items.
$node->body = [$body, $body];
$node->save();
$view = Views::getView('test_serializer_node_display_field');
$view->setDisplay('rest_export_1');
$this->executeView($view);
$result = Json::decode($this->drupalGet('test/serialize/node-field', ['query' => ['_format' => 'json']]));
$this->assertEqual(count($result[2]['body']), $node->body->count(), 'Expected count of values');
$this->assertEqual($result[2]['body'], array_map(function ($item) {
return $item['value'];
}, $node->body->getValue()), 'Expected raw body values found.');
}
/**
* Tests the "Grouped rows" functionality.
*/
public function testGroupRows() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
$this->drupalCreateContentType(['type' => 'page']);
// Create a text field with cardinality set to unlimited.
$field_name = 'field_group_rows';
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => 'string',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
]);
$field_storage->save();
// Create an instance of the text field on the content type.
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => 'page',
]);
$field->save();
$grouped_field_values = ['a', 'b', 'c'];
$edit = [
'title' => $this->randomMachineName(),
$field_name => $grouped_field_values,
];
$this->drupalCreateNode($edit);
$view = Views::getView('test_serializer_node_display_field');
$view->setDisplay('rest_export_1');
// Override the view's fields to include the field_group_rows field, set the
// group_rows setting to true.
$fields = [
$field_name => [
'id' => $field_name,
'table' => 'node__' . $field_name,
'field' => $field_name,
'type' => 'string',
'group_rows' => TRUE,
],
];
$view->displayHandlers->get('default')->overrideOption('fields', $fields);
$build = $view->preview();
// Get the serializer service.
$serializer = $this->container->get('serializer');
// Check if the field_group_rows field is grouped.
$expected = [];
$expected[] = [$field_name => implode(', ', $grouped_field_values)];
$this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
// Set the group rows setting to false.
$view = Views::getView('test_serializer_node_display_field');
$view->setDisplay('rest_export_1');
$fields[$field_name]['group_rows'] = FALSE;
$view->displayHandlers->get('default')->overrideOption('fields', $fields);
$build = $view->preview();
// Check if the field_group_rows field is ungrouped and displayed per row.
$expected = [];
foreach ($grouped_field_values as $grouped_field_value) {
$expected[] = [$field_name => $grouped_field_value];
}
$this->assertEqual($serializer->serialize($expected, 'json'), (string) $renderer->renderRoot($build));
}
/**
* Tests the exposed filter works.
*
* There is an exposed filter on the title field which takes a title query
* parameter. This is set to filter nodes by those whose title starts with
* the value provided.
*/
public function testRestViewExposedFilter() {
$this->drupalCreateContentType(['type' => 'page']);
$node0 = $this->drupalCreateNode(['title' => 'Node 1']);
$node1 = $this->drupalCreateNode(['title' => 'Node 11']);
$node2 = $this->drupalCreateNode(['title' => 'Node 111']);
// Test that no filter brings back all three nodes.
$result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json']]));
$expected = [
0 => [
'nid' => $node0->id(),
'body' => $node0->body->processed,
],
1 => [
'nid' => $node1->id(),
'body' => $node1->body->processed,
],
2 => [
'nid' => $node2->id(),
'body' => $node2->body->processed,
],
];
$this->assertEqual($result, $expected, 'Querying a view with no exposed filter returns all nodes.');
// Test that title starts with 'Node 11' query finds 2 of the 3 nodes.
$result = Json::decode($this->drupalGet('test/serialize/node-exposed-filter', ['query' => ['_format' => 'json', 'title' => 'Node 11']]));
$expected = [
0 => [
'nid' => $node1->id(),
'body' => $node1->body->processed,
],
1 => [
'nid' => $node2->id(),
'body' => $node2->body->processed,
],
];
$cache_contexts = [
'languages:language_content',
'languages:language_interface',
'theme',
'request_format',
'user.node_grants:view',
'url',
];
$this->assertEqual($result, $expected, 'Querying a view with a starts with exposed filter on the title returns nodes whose title starts with value provided.');
$this->assertCacheContexts($cache_contexts);
}
/**
* Test multilingual entity rows.
*/
public function testMulEntityRows() {
// Create some languages.
ConfigurableLanguage::createFromLangcode('l1')->save();
ConfigurableLanguage::createFromLangcode('l2')->save();
// Create an entity with no translations.
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_mul');
$storage->create(['langcode' => 'l1', 'name' => 'mul-none'])->save();
// Create some entities with translations.
$entity = $storage->create(['langcode' => 'l1', 'name' => 'mul-l1-orig']);
$entity->save();
$entity->addTranslation('l2', ['name' => 'mul-l1-l2'])->save();
$entity = $storage->create(['langcode' => 'l2', 'name' => 'mul-l2-orig']);
$entity->save();
$entity->addTranslation('l1', ['name' => 'mul-l2-l1'])->save();
// Get the names of the output.
$json = $this->drupalGet('test/serialize/translated_entity', ['query' => ['_format' => 'json']]);
$decoded = $this->container->get('serializer')->decode($json, 'hal_json');
$names = [];
foreach ($decoded as $item) {
$names[] = $item['name'][0]['value'];
}
sort($names);
// Check that the names are correct.
$expected = ['mul-l1-l2', 'mul-l1-orig', 'mul-l2-l1', 'mul-l2-orig', 'mul-none'];
$this->assertIdentical($names, $expected, 'The translated content was found in the JSON.');
}
}
@@ -30,9 +30,9 @@ class ConfigDependenciesTest extends KernelTestBase {
$rest_config = RestResourceConfig::create($configuration);
$result = $config_dependencies->calculateDependencies($rest_config);
$this->assertEquals(['module' => [
'basic_auth', 'serialization', 'hal',
]], $result);
$this->assertEquals([
'module' => ['basic_auth', 'serialization', 'hal'],
], $result);
}
/**
@@ -49,7 +49,7 @@ class RequestHandlerTest extends KernelTestBase {
*/
public function testHandle() {
$request = new Request();
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_format' => 'json']));
$route_match = new RouteMatch('test', (new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_format' => 'json']))->setMethods(['GET']));
$resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
$resource->get(NULL, $request)
@@ -76,7 +76,7 @@ class RequestHandlerTest extends KernelTestBase {
$this->assertEquals($response, $handler_response);
// We will call the patch method this time.
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_content_type_format' => 'json']));
$route_match = new RouteMatch('test', (new Route('/rest/test', ['_rest_resource_config' => 'restplugin'], ['_content_type_format' => 'json']))->setMethods(['PATCH']));
$request->setMethod('PATCH');
$response = new ResourceResponse([]);
$resource->patch(NULL, $request)
@@ -0,0 +1,59 @@
<?php
namespace Drupal\Tests\rest\Kernel\Views;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
/**
* Tests the auth option of rest exports.
*
* @coversDefaultClass \Drupal\rest\Plugin\views\display\RestExport
*
* @group rest
*/
class RestExportAuthTest extends ViewsKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'rest',
'views_ui',
'basic_auth',
'serialization',
'rest',
'user',
];
/**
* {@inheritdoc}
*/
public static $testViews = ['rest_export_with_authorization_correction'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installConfig(['user']);
}
/**
* Ensures that rest export auth settings are automatically corrected.
*
* @see rest_update_8401()
* @see rest_views_presave()
* @see \Drupal\rest\Tests\Update\RestExportAuthCorrectionUpdateTest
*/
public function testAuthCorrection() {
// Get particular view.
$view = \Drupal::entityTypeManager()
->getStorage('view')
->load('rest_export_with_authorization_correction');
$displays = $view->get('display');
$this->assertSame($displays['rest_export_1']['display_options']['auth'], ['cookie'], 'Cookie is used for authentication');
}
}
@@ -141,8 +141,8 @@ class CollectRoutesTest extends UnitTestCase {
$requirements_1 = $this->routes->get('test_1')->getRequirements();
$requirements_2 = $this->routes->get('view.test_view.page_1')->getRequirements();
$this->assertEquals(count($requirements_1), 0, 'First route has no requirement.');
$this->assertEquals(count($requirements_2), 2, 'Views route with rest export had the format and method requirements added.');
$this->assertEquals(0, count($requirements_1), 'First route has no requirement.');
$this->assertEquals(1, count($requirements_2), 'Views route with rest export had the format requirement added.');
// Check auth options.
$auth = $this->routes->get('view.test_view.page_1')->getOption('_auth');
@@ -79,11 +79,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
* @dataProvider providerTestResponseFormat
*/
public function testResponseFormat($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
$parameters = [];
if ($request_format !== FALSE) {
$parameters['_format'] = $request_format;
}
foreach ($request_headers as $key => $value) {
unset($request_headers[$key]);
$key = strtoupper(str_replace('-', '_', $key));
@@ -91,8 +86,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
}
foreach ($methods as $method) {
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
// of this so we'll hard code it here.
if ($request_format) {
$request->setRequestFormat($request_format);
}
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $this->randomMachineName()], [$route_requirement_key_format => implode('|', $supported_formats)]));
$resource_response_subscriber = new ResourceResponseSubscriber(
@@ -116,11 +116,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
public function testOnResponseWithCacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
$rest_config_name = $this->randomMachineName();
$parameters = [];
if ($request_format !== FALSE) {
$parameters['_format'] = $request_format;
}
foreach ($request_headers as $key => $value) {
unset($request_headers[$key]);
$key = strtoupper(str_replace('-', '_', $key));
@@ -128,8 +123,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
}
foreach ($methods as $method) {
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
// of this so we'll hard code it here.
if ($request_format) {
$request->setRequestFormat($request_format);
}
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)]));
// The RequestHandler must return a ResourceResponseInterface object.
@@ -166,11 +166,6 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
public function testOnResponseWithUncacheableResponse($methods, array $supported_formats, $request_format, array $request_headers, $request_body, $expected_response_format, $expected_response_content_type, $expected_response_content) {
$rest_config_name = $this->randomMachineName();
$parameters = [];
if ($request_format !== FALSE) {
$parameters['_format'] = $request_format;
}
foreach ($request_headers as $key => $value) {
unset($request_headers[$key]);
$key = strtoupper(str_replace('-', '_', $key));
@@ -178,8 +173,13 @@ class ResourceResponseSubscriberTest extends UnitTestCase {
}
foreach ($methods as $method) {
$request = Request::create('/rest/test', $method, $parameters, [], [], $request_headers, $request_body);
$route_requirement_key_format = $request->isMethodSafe() ? '_format' : '_content_type_format';
$request = Request::create('/rest/test', $method, [], [], [], $request_headers, $request_body);
// \Drupal\Core\StackMiddleware\NegotiationMiddleware normally takes care
// of this so we'll hard code it here.
if ($request_format) {
$request->setRequestFormat($request_format);
}
$route_requirement_key_format = $request->isMethodCacheable() ? '_format' : '_content_type_format';
$route_match = new RouteMatch('test', new Route('/rest/test', ['_rest_resource_config' => $rest_config_name], [$route_requirement_key_format => implode('|', $supported_formats)]));
// The RequestHandler must return a ResourceResponseInterface object.