Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
553
web/core/modules/rest/src/Tests/RESTTestBase.php
Normal file
553
web/core/modules/rest/src/Tests/RESTTestBase.php
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityType;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Test helper class that provides a REST client method to send HTTP requests.
|
||||
*
|
||||
* @deprecated in Drupal 8.3.x-dev and will be removed before Drupal 9.0.0. Use \Drupal\Tests\rest\Functional\ResourceTestBase and \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase instead. Only retained for contributed module tests that may be using this base class.
|
||||
*/
|
||||
abstract class RESTTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The REST resource config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $resourceConfigStorage;
|
||||
|
||||
/**
|
||||
* The default serialization format to use for testing REST operations.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultFormat;
|
||||
|
||||
/**
|
||||
* The default MIME type to use for testing REST operations.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultMimeType;
|
||||
|
||||
/**
|
||||
* The entity type to use for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testEntityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The default authentication provider to use for testing REST operations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultAuth;
|
||||
|
||||
|
||||
/**
|
||||
* The raw response body from http request operations.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $responseBody;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('rest', 'entity_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->defaultFormat = 'hal_json';
|
||||
$this->defaultMimeType = 'application/hal+json';
|
||||
$this->defaultAuth = array('cookie');
|
||||
$this->resourceConfigStorage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
|
||||
// Create a test content type for node testing.
|
||||
if (in_array('node', static::$modules)) {
|
||||
$this->drupalCreateContentType(array('name' => 'resttest', 'type' => 'resttest'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to issue a HTTP request with simpletest's cURL.
|
||||
*
|
||||
* @param string|\Drupal\Core\Url $url
|
||||
* A Url object or system path.
|
||||
* @param string $method
|
||||
* HTTP method, one of GET, POST, PUT or DELETE.
|
||||
* @param string $body
|
||||
* The body for POST and PUT.
|
||||
* @param string $mime_type
|
||||
* The MIME type of the transmitted content.
|
||||
* @param bool $csrf_token
|
||||
* If NULL, a CSRF token will be retrieved and used. If FALSE, omit the
|
||||
* X-CSRF-Token request header (to simulate developer error). Otherwise, the
|
||||
* passed in value will be used as the value for the X-CSRF-Token request
|
||||
* header (to simulate developer error, by sending an invalid CSRF token).
|
||||
*
|
||||
* @return string
|
||||
* The content returned from the request.
|
||||
*/
|
||||
protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL, $csrf_token = NULL) {
|
||||
if (!isset($mime_type)) {
|
||||
$mime_type = $this->defaultMimeType;
|
||||
}
|
||||
if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE'))) {
|
||||
// GET the CSRF token first for writing requests.
|
||||
$requested_token = $this->drupalGet('session/token');
|
||||
}
|
||||
|
||||
$url = $this->buildUrl($url);
|
||||
|
||||
$curl_options = array();
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
// Set query if there are additional GET parameters.
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => TRUE,
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => FALSE,
|
||||
CURLOPT_HTTPHEADER => array('Accept: ' . $mime_type),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'HEAD':
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => FALSE,
|
||||
CURLOPT_CUSTOMREQUEST => 'HEAD',
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => TRUE,
|
||||
CURLOPT_HTTPHEADER => array('Accept: ' . $mime_type),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => FALSE,
|
||||
CURLOPT_POST => TRUE,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => FALSE,
|
||||
CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
|
||||
) : array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => FALSE,
|
||||
CURLOPT_CUSTOMREQUEST => 'PUT',
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => FALSE,
|
||||
CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
|
||||
) : array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PATCH':
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => FALSE,
|
||||
CURLOPT_CUSTOMREQUEST => 'PATCH',
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => FALSE,
|
||||
CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
|
||||
) : array(
|
||||
'Content-Type: ' . $mime_type,
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
$curl_options = array(
|
||||
CURLOPT_HTTPGET => FALSE,
|
||||
CURLOPT_CUSTOMREQUEST => 'DELETE',
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_NOBODY => FALSE,
|
||||
CURLOPT_HTTPHEADER => $csrf_token !== FALSE ? array(
|
||||
'X-CSRF-Token: ' . ($csrf_token === NULL ? $requested_token : $csrf_token),
|
||||
) : array(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($mime_type === 'none') {
|
||||
unset($curl_options[CURLOPT_HTTPHEADER]['Content-Type']);
|
||||
}
|
||||
|
||||
$this->responseBody = $this->curlExec($curl_options);
|
||||
|
||||
// Ensure that any changes to variables in the other thread are picked up.
|
||||
$this->refreshVariables();
|
||||
|
||||
$headers = $this->drupalGetHeaders();
|
||||
|
||||
$this->verbose($method . ' request to: ' . $url .
|
||||
'<hr />Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) .
|
||||
(isset($curl_options[CURLOPT_HTTPHEADER]) ? '<hr />Request headers: ' . nl2br(print_r($curl_options[CURLOPT_HTTPHEADER], TRUE)) : '' ) .
|
||||
(isset($curl_options[CURLOPT_POSTFIELDS]) ? '<hr />Request body: ' . nl2br(print_r($curl_options[CURLOPT_POSTFIELDS], TRUE)) : '' ) .
|
||||
'<hr />Response headers: ' . nl2br(print_r($headers, TRUE)) .
|
||||
'<hr />Response body: ' . $this->responseBody);
|
||||
|
||||
return $this->responseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates entity objects based on their types.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The type of the entity that should be created.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The new entity object.
|
||||
*/
|
||||
protected function entityCreate($entity_type) {
|
||||
return $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create($this->entityValues($entity_type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an array of suitable property values for an entity type.
|
||||
*
|
||||
* Required properties differ from entity type to entity type, so we keep a
|
||||
* minimum mapping here.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The ID of the type of entity that should be created.
|
||||
*
|
||||
* @return array
|
||||
* An array of values keyed by property name.
|
||||
*/
|
||||
protected function entityValues($entity_type_id) {
|
||||
switch ($entity_type_id) {
|
||||
case 'entity_test':
|
||||
return array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'user_id' => 1,
|
||||
'field_test_text' => array(0 => array(
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'plain_text',
|
||||
)),
|
||||
);
|
||||
case 'config_test':
|
||||
return [
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => 'Test label',
|
||||
];
|
||||
case 'node':
|
||||
return array('title' => $this->randomString(), 'type' => 'resttest');
|
||||
case 'node_type':
|
||||
return array(
|
||||
'type' => 'article',
|
||||
'name' => $this->randomMachineName(),
|
||||
);
|
||||
case 'user':
|
||||
return array('name' => $this->randomMachineName());
|
||||
|
||||
case 'comment':
|
||||
return [
|
||||
'subject' => $this->randomMachineName(),
|
||||
'entity_type' => 'node',
|
||||
'comment_type' => 'comment',
|
||||
'comment_body' => $this->randomString(),
|
||||
'entity_id' => 'invalid',
|
||||
'field_name' => 'comment',
|
||||
];
|
||||
case 'taxonomy_vocabulary':
|
||||
return [
|
||||
'vid' => 'tags',
|
||||
'name' => $this->randomMachineName(),
|
||||
];
|
||||
default:
|
||||
if ($this->isConfigEntity($entity_type_id)) {
|
||||
return $this->configEntityValues($entity_type_id);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the REST service interface for a specific entity type.
|
||||
*
|
||||
* @param string|false $resource_type
|
||||
* The resource type that should get REST API enabled or FALSE to disable all
|
||||
* resource types.
|
||||
* @param string $method
|
||||
* The HTTP method to enable, e.g. GET, POST etc.
|
||||
* @param string|array $format
|
||||
* (Optional) The serialization format, e.g. hal_json, or a list of formats.
|
||||
* @param array $auth
|
||||
* (Optional) The list of valid authentication methods.
|
||||
*/
|
||||
protected function enableService($resource_type, $method = 'GET', $format = NULL, array $auth = []) {
|
||||
if ($resource_type) {
|
||||
// Enable REST API for this entity type.
|
||||
$resource_config_id = str_replace(':', '.', $resource_type);
|
||||
// get entity by id
|
||||
/** @var \Drupal\rest\RestResourceConfigInterface $resource_config */
|
||||
$resource_config = $this->resourceConfigStorage->load($resource_config_id);
|
||||
if (!$resource_config) {
|
||||
$resource_config = $this->resourceConfigStorage->create([
|
||||
'id' => $resource_config_id,
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => []
|
||||
]);
|
||||
}
|
||||
$configuration = $resource_config->get('configuration');
|
||||
|
||||
if (is_array($format)) {
|
||||
for ($i = 0; $i < count($format); $i++) {
|
||||
$configuration[$method]['supported_formats'][] = $format[$i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ($format == NULL) {
|
||||
$format = $this->defaultFormat;
|
||||
}
|
||||
$configuration[$method]['supported_formats'][] = $format;
|
||||
}
|
||||
|
||||
if (!is_array($auth) || empty($auth)) {
|
||||
$auth = $this->defaultAuth;
|
||||
}
|
||||
foreach ($auth as $auth_provider) {
|
||||
$configuration[$method]['supported_auth'][] = $auth_provider;
|
||||
}
|
||||
|
||||
$resource_config->set('configuration', $configuration);
|
||||
$resource_config->save();
|
||||
}
|
||||
else {
|
||||
foreach ($this->resourceConfigStorage->loadMultiple() as $resource_config) {
|
||||
$resource_config->delete();
|
||||
}
|
||||
}
|
||||
$this->rebuildCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds routing caches.
|
||||
*/
|
||||
protected function rebuildCache() {
|
||||
// Rebuild routing cache, so that the REST API paths are available.
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This method is overridden to deal with a cURL quirk: the usage of
|
||||
* CURLOPT_CUSTOMREQUEST cannot be unset on the cURL handle, so we need to
|
||||
* override it every time it is omitted.
|
||||
*/
|
||||
protected function curlExec($curl_options, $redirect = FALSE) {
|
||||
if (!isset($curl_options[CURLOPT_CUSTOMREQUEST])) {
|
||||
if (!empty($curl_options[CURLOPT_HTTPGET])) {
|
||||
$curl_options[CURLOPT_CUSTOMREQUEST] = 'GET';
|
||||
}
|
||||
if (!empty($curl_options[CURLOPT_POST])) {
|
||||
$curl_options[CURLOPT_CUSTOMREQUEST] = 'POST';
|
||||
}
|
||||
}
|
||||
return parent::curlExec($curl_options, $redirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the necessary user permissions for entity operations.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type.
|
||||
* @param string $operation
|
||||
* The operation, one of 'view', 'create', 'update' or 'delete'.
|
||||
*
|
||||
* @return array
|
||||
* The set of user permission strings.
|
||||
*/
|
||||
protected function entityPermissions($entity_type_id, $operation) {
|
||||
switch ($entity_type_id) {
|
||||
case 'entity_test':
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return array('view test entity');
|
||||
case 'create':
|
||||
case 'update':
|
||||
case 'delete':
|
||||
return array('administer entity_test content');
|
||||
}
|
||||
case 'node':
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return array('access content');
|
||||
case 'create':
|
||||
return array('create resttest content');
|
||||
case 'update':
|
||||
return array('edit any resttest content');
|
||||
case 'delete':
|
||||
return array('delete any resttest content');
|
||||
}
|
||||
|
||||
case 'comment':
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return ['access comments'];
|
||||
|
||||
case 'create':
|
||||
return ['post comments', 'skip comment approval'];
|
||||
|
||||
case 'update':
|
||||
return ['edit own comments'];
|
||||
|
||||
case 'delete':
|
||||
return ['administer comments'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
switch ($operation) {
|
||||
case 'view':
|
||||
return ['access user profiles'];
|
||||
|
||||
default:
|
||||
return ['administer users'];
|
||||
}
|
||||
|
||||
default:
|
||||
if ($this->isConfigEntity($entity_type_id)) {
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
|
||||
if ($admin_permission = $entity_type->getAdminPermission()) {
|
||||
return [$admin_permission];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an entity based on the location URL returned in the location header.
|
||||
*
|
||||
* @param string $location_url
|
||||
* The URL returned in the Location header.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\Entity|false
|
||||
* The entity or FALSE if there is no matching entity.
|
||||
*/
|
||||
protected function loadEntityFromLocationHeader($location_url) {
|
||||
$url_parts = explode('/', $location_url);
|
||||
$id = end($url_parts);
|
||||
return $this->container->get('entity_type.manager')
|
||||
->getStorage($this->testEntityType)->load($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove node fields that can only be written by an admin user.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node to remove fields where non-administrative users cannot write.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The node with removed fields.
|
||||
*/
|
||||
protected function removeNodeFieldsForNonAdminUsers(NodeInterface $node) {
|
||||
$node->set('status', NULL);
|
||||
$node->set('created', NULL);
|
||||
$node->set('changed', NULL);
|
||||
$node->set('promote', NULL);
|
||||
$node->set('sticky', NULL);
|
||||
$node->set('revision_timestamp', NULL);
|
||||
$node->set('revision_log', NULL);
|
||||
$node->set('uid', NULL);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the HTTP request response body is identical to the expected
|
||||
* value.
|
||||
*
|
||||
* @param $expected
|
||||
* The first value to check.
|
||||
* @param $message
|
||||
* (optional) A message to display with the assertion. Do not translate
|
||||
* messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
|
||||
* variables in the message text, not t(). If left blank, a default message
|
||||
* will be displayed.
|
||||
* @param $group
|
||||
* (optional) The group this message is in, which is displayed in a column
|
||||
* in test output. Use 'Debug' to indicate this is debugging output. Do not
|
||||
* translate this string. Defaults to 'Other'; most tests do not override
|
||||
* this default.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertResponseBody($expected, $message = '', $group = 'REST Response') {
|
||||
return $this->assertIdentical($expected, $this->responseBody, $message ? $message : strtr('Response body @expected (expected) is equal to @response (actual).', array('@expected' => var_export($expected, TRUE), '@response' => var_export($this->responseBody, TRUE))), $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an entity type id is for a Config Entity.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID to check.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity is a Config Entity, FALSE otherwise.
|
||||
*/
|
||||
protected function isConfigEntity($entity_type_id) {
|
||||
return \Drupal::entityTypeManager()->getDefinition($entity_type_id) instanceof ConfigEntityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an array of suitable property values for a config entity type.
|
||||
*
|
||||
* Config entities have some common keys that need to be created. Required
|
||||
* properties differ among config entity types, so we keep a minimum mapping
|
||||
* here.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The ID of the type of entity that should be created.
|
||||
*
|
||||
* @return array
|
||||
* An array of values keyed by property name.
|
||||
*/
|
||||
protected function configEntityValues($entity_type_id) {
|
||||
$entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
|
||||
$keys = $entity_type->getKeys();
|
||||
$values = [];
|
||||
// Fill out known key values that are shared across entity types.
|
||||
foreach ($keys as $key) {
|
||||
if ($key === 'id' || $key === 'label') {
|
||||
$values[$key] = $this->randomMachineName();
|
||||
}
|
||||
}
|
||||
// Add extra values for particular entity types.
|
||||
switch ($entity_type_id) {
|
||||
case 'block':
|
||||
$values['plugin'] = 'system_powered_by_block';
|
||||
break;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
||||
130
web/core/modules/rest/src/Tests/ResourceTest.php
Normal file
130
web/core/modules/rest/src/Tests/ResourceTest.php
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the structure of a REST resource.
|
||||
*
|
||||
* @group rest
|
||||
*/
|
||||
class ResourceTest extends RESTTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('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 = $this->entityCreate('entity_test');
|
||||
$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() {
|
||||
$this->resourceConfigStorage->create([
|
||||
'id' => 'entity.entity_test',
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => [
|
||||
'GET' => [
|
||||
'supported_auth' => [
|
||||
'basic_auth',
|
||||
],
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Verify that accessing the resource returns 406.
|
||||
$response = $this->httpRequest($this->entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
|
||||
// \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.');
|
||||
$this->curlClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a resource without authentication cannot be enabled.
|
||||
*/
|
||||
public function testAuthentication() {
|
||||
$this->resourceConfigStorage->create([
|
||||
'id' => 'entity.entity_test',
|
||||
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
|
||||
'configuration' => [
|
||||
'GET' => [
|
||||
'supported_formats' => [
|
||||
'hal_json',
|
||||
],
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Verify that accessing the resource returns 401.
|
||||
$response = $this->httpRequest($this->entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
|
||||
// \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.');
|
||||
$this->curlClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that serialization_class is optional.
|
||||
*/
|
||||
public function testSerializationClassIsOptional() {
|
||||
$this->enableService('serialization_test', 'POST', 'json');
|
||||
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->grantPermission('restful post serialization_test')
|
||||
->save();
|
||||
|
||||
$serialized = $this->container->get('serializer')->serialize(['foo', 'bar'], 'json');
|
||||
$this->httpRequest('serialization_test', 'POST', $serialized, 'application/json');
|
||||
$this->assertResponse(200);
|
||||
$this->assertResponseBody('["foo","bar"]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that resource URI paths are formatted properly.
|
||||
*/
|
||||
public function testUriPaths() {
|
||||
$this->enableService('entity:entity_test');
|
||||
/** @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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\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__ . '/../../../../rest/tests/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\rest\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\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__ . '/../../../../rest/tests/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\rest\Tests\Update;
|
||||
|
||||
use Drupal\rest\RestResourceConfigInterface;
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* 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__ . '/../../../../rest/tests/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\rest\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\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,88 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests\Views;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\views\Tests\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() {
|
||||
parent::setUp();
|
||||
|
||||
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->drupalGetWithFormat($this->view->getPath(), '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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
838
web/core/modules/rest/src/Tests/Views/StyleSerializerTest.php
Normal file
838
web/core/modules/rest/src/Tests/Views/StyleSerializerTest.php
Normal file
|
|
@ -0,0 +1,838 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\rest\Tests\Views;
|
||||
|
||||
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\views\Entity\View;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\Plugin\PluginTestBase;
|
||||
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 PluginTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field', 'language', 'basic_auth');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('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() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('rest_test_views'));
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(array('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(array('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->drupalGetWithFormat('test/serialize/auth_with_perm', '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->drupalGetWithFormat('test/serialize/auth_with_perm', '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();
|
||||
|
||||
$headers = $response->getHeaders();
|
||||
$this->verbose('GET request to: ' . $url .
|
||||
'<hr />Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) .
|
||||
'<hr />Response headers: ' . nl2br(print_r($headers, TRUE)) .
|
||||
'<hr />Response body: ' . (string) $response->getBody());
|
||||
$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->drupalGetWithFormat('test/serialize/field', '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->assertEqual($headers['content-type'], 'application/json', 'The header Content-type is correct.');
|
||||
|
||||
$expected = array();
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = array();
|
||||
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 = array();
|
||||
foreach ($view->result as $row) {
|
||||
$entities[] = $row->_entity;
|
||||
}
|
||||
|
||||
$expected = $serializer->serialize($entities, 'json');
|
||||
|
||||
$actual_json = $this->drupalGetWithFormat('test/serialize/entity', '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->drupalGetWithFormat('test/serialize/entity', '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', array(
|
||||
'type' => 'serializer',
|
||||
'options' => array(
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => array(
|
||||
'xml' => 'xml',
|
||||
),
|
||||
),
|
||||
));
|
||||
$view->save();
|
||||
$expected = $serializer->serialize($entities, 'xml');
|
||||
$actual_xml = $this->drupalGet('test/serialize/entity');
|
||||
$this->assertIdentical($actual_xml, $expected, 'The expected XML output was found.');
|
||||
$this->assertCacheContexts(['languages:language_interface', 'theme', 'entity_test_view_grants', 'request_format']);
|
||||
|
||||
// Allow multiple formats.
|
||||
$view->setDisplay('rest_export_1');
|
||||
$view->getDisplay()->setOption('style', array(
|
||||
'type' => 'serializer',
|
||||
'options' => array(
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => array(
|
||||
'xml' => 'xml',
|
||||
'json' => 'json',
|
||||
),
|
||||
),
|
||||
));
|
||||
$view->save();
|
||||
$expected = $serializer->serialize($entities, 'json');
|
||||
$actual_json = $this->drupalGetWithFormat('test/serialize/entity', 'json');
|
||||
$this->assertIdentical($actual_json, $expected, 'The expected JSON output was found.');
|
||||
$expected = $serializer->serialize($entities, 'xml');
|
||||
$actual_xml = $this->drupalGetWithFormat('test/serialize/entity', 'xml');
|
||||
$this->assertIdentical($actual_xml, $expected, 'The expected XML output was found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies site maintenance mode functionality.
|
||||
*/
|
||||
protected 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->drupalGetWithFormat('test/serialize/entity', '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 = $this->drupalGetJSON('test/serialize/entity');
|
||||
$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->drupalGetWithFormat('test/serialize/entity', '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 = $this->drupalGetJSON('test/serialize/entity');
|
||||
$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 = $this->drupalGetJSON('test/serialize/entity');
|
||||
$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, array('style_options[formats][xml]' => 'xml'), t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Should return a 406.
|
||||
$this->drupalGetWithFormat('test/serialize/field', 'json');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(406, 'A 406 response was returned when JSON was requested.');
|
||||
// Should return a 200.
|
||||
$this->drupalGetWithFormat('test/serialize/field', '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, array('style_options[formats][json]' => 'json'), t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), 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', array(), array('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->drupalGetWithFormat('test/serialize/field', '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->drupalGetWithFormat('test/serialize/field', '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->assertTrue(strpos($headers['content-type'], 'text/xml') !== FALSE, 'The header Content-type is correct.');
|
||||
// Should return a 406.
|
||||
$this->drupalGetWithFormat('test/serialize/field', '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, array('style_options[formats][json]' => '0', 'style_options[formats][xml]' => '0'), t('Apply'));
|
||||
|
||||
// Should return a 200.
|
||||
$this->drupalGetWithFormat('test/serialize/field', 'json');
|
||||
$this->assertHeader('content-type', 'application/json');
|
||||
$this->assertResponse(200, 'A 200 response was returned when JSON was requested.');
|
||||
// Should return a 200.
|
||||
$this->drupalGetWithFormat('test/serialize/field', '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->drupalGetWithFormat('test/serialize/field', '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, array('row_options[field_options][name][alias]' => ''), t('Apply'));
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$expected = array();
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = array();
|
||||
foreach ($view->field as $id => $field) {
|
||||
$expected_row[$id] = $field->render($row);
|
||||
}
|
||||
$expected[] = $expected_row;
|
||||
}
|
||||
|
||||
$this->assertIdentical($this->drupalGetJSON('test/serialize/field'), $this->castSafeStrings($expected));
|
||||
|
||||
// Test a random aliases for fields, they should be replaced.
|
||||
$alias_map = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
// Use # to produce an invalid character for the validation.
|
||||
'nothing' => '#' . $this->randomMachineName(),
|
||||
'created' => 'created',
|
||||
);
|
||||
|
||||
$edit = array('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 = array('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, array(), t('Save'));
|
||||
|
||||
$view = Views::getView('test_serializer_display_field');
|
||||
$view->setDisplay('rest_export_1');
|
||||
$this->executeView($view);
|
||||
|
||||
$expected = array();
|
||||
foreach ($view->result as $row) {
|
||||
$expected_row = array();
|
||||
foreach ($view->field as $id => $field) {
|
||||
$expected_row[$alias_map[$id]] = $field->render($row);
|
||||
}
|
||||
$expected[] = $expected_row;
|
||||
}
|
||||
|
||||
$this->assertIdentical($this->drupalGetJSON('test/serialize/field'), $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 = array(
|
||||
'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, array(), 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 ($this->drupalGetJSON('test/serialize/field') 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 ($this->drupalGetJSON('test/serialize/field') 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 = array();
|
||||
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', array(
|
||||
'type' => 'serializer',
|
||||
'options' => array(
|
||||
'uses_fields' => FALSE,
|
||||
'formats' => array(
|
||||
'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 = array(), t('Update preview'));
|
||||
$this->assertResponse(200);
|
||||
// Check if we receive the expected result.
|
||||
$result = $this->xpath('//div[@id="views-live-preview"]/pre');
|
||||
$this->assertIdentical($this->drupalGet('test/serialize/field'), (string) $result[0], 'The expected JSON preview output was found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the field row style using fieldapi fields.
|
||||
*/
|
||||
public function testFieldapiField() {
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
$result = $this->drupalGetJSON('test/serialize/node-field');
|
||||
$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 = $this->drupalGetJSON('test/serialize/node-field');
|
||||
$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 ($this->drupalGetJSON('test/serialize/node-field') 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 = $this->drupalGetJSON('test/serialize/node-field');
|
||||
$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(array('type' => 'page'));
|
||||
$node0 = $this->drupalCreateNode(array('title' => 'Node 1'));
|
||||
$node1 = $this->drupalCreateNode(array('title' => 'Node 11'));
|
||||
$node2 = $this->drupalCreateNode(array('title' => 'Node 111'));
|
||||
|
||||
// Test that no filter brings back all three nodes.
|
||||
$result = $this->drupalGetJSON('test/serialize/node-exposed-filter');
|
||||
|
||||
$expected = array(
|
||||
0 => array(
|
||||
'nid' => $node0->id(),
|
||||
'body' => $node0->body->processed,
|
||||
),
|
||||
1 => array(
|
||||
'nid' => $node1->id(),
|
||||
'body' => $node1->body->processed,
|
||||
),
|
||||
2 => array(
|
||||
'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 = $this->drupalGetJSON('test/serialize/node-exposed-filter', ['query' => ['title' => 'Node 11']]);
|
||||
|
||||
$expected = array(
|
||||
0 => array(
|
||||
'nid' => $node1->id(),
|
||||
'body' => $node1->body->processed,
|
||||
),
|
||||
1 => array(
|
||||
'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->drupalGetWithFormat('test/serialize/translated_entity', '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.');
|
||||
}
|
||||
|
||||
}
|
||||
Reference in a new issue