Update to Drupal 8.0.5. For more information, see https://www.drupal.org/node/2679347

This commit is contained in:
Pantheon Automation 2016-03-02 12:40:24 -08:00 committed by Greg Anderson
parent 2a9f1f148d
commit fd3b12cf27
251 changed files with 5439 additions and 957 deletions

View file

@ -45,19 +45,24 @@ class EntityResource extends ResourceBase {
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function get(EntityInterface $entity) {
if (!$entity->access('view')) {
$entity_access = $entity->access('view', NULL, TRUE);
if (!$entity_access->isAllowed()) {
throw new AccessDeniedHttpException();
}
foreach ($entity as $field_name => $field) {
if (!$field->access('view')) {
unset($entity->{$field_name});
}
}
$response = new ResourceResponse($entity, 200);
// Make the response use the entity's cacheability metadata.
// @todo include access cacheability metadata, for the access checks above.
$response->addCacheableDependency($entity);
$response->addCacheableDependency($entity_access);
foreach ($entity as $field_name => $field) {
/** @var \Drupal\Core\Field\FieldItemListInterface $field */
$field_access = $field->access('view', NULL, TRUE);
$response->addCacheableDependency($field_access);
if (!$field_access->isAllowed()) {
$entity->set($field_name, NULL);
}
}
return $response;
}
@ -110,7 +115,8 @@ class EntityResource extends ResourceBase {
// 201 Created responses have an empty body.
$url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
$response = new ResourceResponse(NULL, 201, ['Location' => $url->getGeneratedUrl()]);
$response->addCacheableDependency($url);
// Responses after creating an entity are not cacheable, so we add no
// cacheability metadata here.
return $response;
}
catch (EntityStorageException $e) {

View file

@ -135,7 +135,7 @@ class Serializer extends StylePluginBase implements CacheableDependencyInterface
else {
$content_type = !empty($this->options['formats']) ? reset($this->options['formats']) : 'json';
}
return $this->serializer->serialize($rows, $content_type);
return $this->serializer->serialize($rows, $content_type, ['views_style_plugin' => $this]);
}
/**

View file

@ -102,8 +102,8 @@ class RequestHandler implements ContainerAwareInterface {
return new Response($content, $e->getStatusCode(), $headers);
}
// Serialize the outgoing data for the response, if available.
if ($response instanceof ResourceResponse && $data = $response->getResponseData()) {
if ($response instanceof ResourceResponse) {
$data = $response->getResponseData();
// Serialization can invoke rendering (e.g., generating URLs), but the
// serialization API does not provide a mechanism to collect the
// bubbleable metadata associated with that (e.g., language and other

View file

@ -64,7 +64,17 @@ class ResourceRoutes extends RouteSubscriberBase {
*/
protected function alterRoutes(RouteCollection $collection) {
$routes = array();
$enabled_resources = $this->config->get('rest.settings')->get('resources') ?: array();
// Silently ignore resources that are in the settings but are not defined on
// the plugin manager currently. That avoids exceptions when REST module is
// enabled before another module that provides the resource plugin specified
// in the settings.
// @todo Remove in https://www.drupal.org/node/2308745
$resources = $this->config->get('rest.settings')->get('resources') ?: array();
$enabled_resources = array_intersect_key($resources, $this->manager->getDefinitions());
if (count($resources) != count($enabled_resources)) {
trigger_error('rest.settings lists resources relying on the following missing plugins: ' . implode(', ', array_keys(array_diff_key($resources, $enabled_resources))));
}
// Iterate over all enabled resource plugins.
foreach ($enabled_resources as $id => $enabled_methods) {

View file

@ -74,7 +74,7 @@ class AuthTest extends RESTTestBase {
* set curl settings for basic authentication.
*
* @param \Drupal\Core\Url $url
* An Url object.
* A Url object.
* @param string $username
* The user name to authenticate with.
* @param string $password

View file

@ -199,8 +199,8 @@ class CreateTest extends RESTTestBase {
}
else {
// Changed and revision_timestamp fields can never be added.
unset($entity->changed);
unset($entity->revision_timestamp);
$entity->set('changed', NULL);
$entity->set('revision_timestamp', NULL);
}
$serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]);
@ -299,7 +299,7 @@ class CreateTest extends RESTTestBase {
}
// Changed field can never be added.
unset($entity->changed);
$entity->set('changed', NULL);
$serialized = $this->serializer->serialize($entity, $this->defaultFormat, ['account' => $account]);

View file

@ -33,6 +33,7 @@ class PageCacheTest extends RESTTestBase {
// Create an entity programmatically.
$entity = $this->entityCreate('entity_test');
$entity->set('field_test_text', 'custom cache tag value');
$entity->save();
// Read it over the REST API.
$this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET', NULL, $this->defaultMimeType);
@ -40,6 +41,7 @@ class PageCacheTest extends RESTTestBase {
$this->assertHeader('x-drupal-cache', 'MISS');
$this->assertCacheTag('config:rest.settings');
$this->assertCacheTag('entity_test:1');
$this->assertCacheTag('entity_test_access:field_test_text');
// Read it again, should be page-cached now.
$this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET', NULL, $this->defaultMimeType);
@ -47,6 +49,7 @@ class PageCacheTest extends RESTTestBase {
$this->assertHeader('x-drupal-cache', 'HIT');
$this->assertCacheTag('config:rest.settings');
$this->assertCacheTag('entity_test:1');
$this->assertCacheTag('entity_test_access:field_test_text');
// Trigger a config save which should clear the page cache, so we should get
// a cache miss now for the same request.
@ -56,6 +59,7 @@ class PageCacheTest extends RESTTestBase {
$this->assertHeader('x-drupal-cache', 'MISS');
$this->assertCacheTag('config:rest.settings');
$this->assertCacheTag('entity_test:1');
$this->assertCacheTag('entity_test_access:field_test_text');
}
}

View file

@ -6,6 +6,8 @@
*/
namespace Drupal\rest\Tests;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\Role;
@ -121,4 +123,30 @@ class ResourceTest extends RESTTestBase {
}
}
/**
* Tests that a resource with a missing plugin does not cause an exception.
*/
public function testMissingPlugin() {
$settings = array(
'entity:nonexisting' => array(
'GET' => array(
'supported_formats' => array(
'hal_json',
),
),
),
);
try {
// Attempt to enable the resource.
$this->config->set('resources', $settings);
$this->config->save();
$this->rebuildCache();
$this->pass('rest.settings referencing a missing REST resource plugin does not cause an exception.');
}
catch (PluginNotFoundException $e) {
$this->fail('rest.settings referencing a missing REST resource plugin caused an exception.');
}
}
}

View file

@ -52,7 +52,7 @@ class UpdateTest extends RESTTestBase {
));
$patch_entity = entity_create($entity_type, $patch_values);
// We don't want to overwrite the UUID.
unset($patch_entity->uuid);
$patch_entity->set('uuid', NULL);
$serialized = $serializer->serialize($patch_entity, $this->defaultFormat, $context);
// Update the entity over the REST API.

View file

@ -0,0 +1,135 @@
<?php
/**
* @file
* Contains \Drupal\Tests\rest\Kernel\RequestHandlerTest.
*/
namespace Drupal\Tests\rest\Kernel;
use Drupal\Core\Routing\RouteMatch;
use Drupal\KernelTests\KernelTestBase;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\Plugin\Type\ResourcePluginManager;
use Drupal\rest\RequestHandler;
use Drupal\rest\ResourceResponse;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Test REST RequestHandler controller logic.
*
* @group rest
* @coversDefaultClass \Drupal\rest\RequestHandler
*/
class RequestHandlerTest extends KernelTestBase {
/**
* @var \Drupal\rest\RequestHandler
*/
protected $requestHandler;
public static $modules = ['serialization', 'rest'];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->requestHandler = new RequestHandler();
$this->requestHandler->setContainer($this->container);
}
/**
* Assert some basic handler method logic.
*
* @covers ::handle
*/
public function testBaseHandler() {
$request = new Request();
$route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
$resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
$resource->get(NULL, $request)
->shouldBeCalled();
// Setup stub plugin manager that will return our plugin.
$stub = $this->prophesize(ResourcePluginManager::class);
$stub->getInstance(['id' => 'restplugin'])
->willReturn($resource->reveal());
$this->container->set('plugin.manager.rest', $stub->reveal());
// Response returns NULL this time because response from plugin is not
// a ResourceResponse so it is passed through directly.
$response = $this->requestHandler->handle($route_match, $request);
$this->assertEquals(NULL, $response);
// Response will return a ResourceResponse this time.
$response = new ResourceResponse([]);
$resource->get(NULL, $request)
->willReturn($response);
$handler_response = $this->requestHandler->handle($route_match, $request);
$this->assertEquals($response, $handler_response);
// We will call the patch method this time.
$request->setMethod('PATCH');
$response = new ResourceResponse([]);
$resource->patch(NULL, $request)
->shouldBeCalledTimes(1)
->willReturn($response);
$handler_response = $this->requestHandler->handle($route_match, $request);
$this->assertEquals($response, $handler_response);
}
/**
* Test that given structured data, the request handler will serialize it.
*
* @dataProvider providerTestSerialization
* @covers ::handle
*/
public function testSerialization($data) {
$request = new Request();
$route_match = new RouteMatch('test', new Route('/rest/test', ['_plugin' => 'restplugin', '_format' => 'json']));
$resource = $this->prophesize(StubRequestHandlerResourcePlugin::class);
// Setup stub plugin manager that will return our plugin.
$stub = $this->prophesize(ResourcePluginManager::class);
$stub->getInstance(['id' => 'restplugin'])
->willReturn($resource->reveal());
$this->container->set('plugin.manager.rest', $stub->reveal());
$response = new ResourceResponse($data);
$resource->get(NULL, $request)
->willReturn($response);
$handler_response = $this->requestHandler->handle($route_match, $request);
// Content is a serialized version of the data we provided.
$this->assertEquals(json_encode($data), $handler_response->getContent());
}
public function providerTestSerialization() {
return [
[NULL],
[''],
['string'],
['Complex \ string $%^&@ with unicode ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'],
[[]],
[['test']],
[['test' => 'foobar']],
[TRUE],
[FALSE],
// @todo Not supported. https://www.drupal.org/node/2427811
// [new \stdClass()],
// [(object) ['test' => 'foobar']],
];
}
}
class StubRequestHandlerResourcePlugin extends ResourceBase {
/** stub methods so they can be prophesied. */
function get() {}
function patch() {}
}

View file

@ -0,0 +1,83 @@
<?php
/**
* @file
* Contains \Drupal\Tests\rest\Unit\Plugin\views\style\SerializerTest.
*/
namespace Drupal\Tests\rest\Unit\Plugin\views\style;
use Drupal\rest\Plugin\views\display\RestExport;
use Drupal\rest\Plugin\views\style\Serializer;
use Drupal\Tests\UnitTestCase;
use Drupal\views\ViewExecutable;
use Prophecy\Argument;
use Symfony\Component\Serializer\SerializerInterface;
/**
* @coversDefaultClass \Drupal\rest\Plugin\views\style\Serializer
* @group rest
*/
class SerializerTest extends UnitTestCase {
/**
* The View instance.
*
* @var \Drupal\views\ViewExecutable|\PHPUnit_Framework_MockObject_MockObject
*/
protected $view;
/**
* The RestExport display handler.
*
* @var \Drupal\rest\Plugin\views\display\RestExport|\PHPUnit_Framework_MockObject_MockObject
*/
protected $displayHandler;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->view = $this->getMockBuilder(ViewExecutable::class)
->disableOriginalConstructor()
->getMock();
// Make the view result empty so we don't have to mock the row plugin render
// call.
$this->view->result = [];
$this->displayHandler = $this->getMockBuilder(RestExport::class)
->disableOriginalConstructor()
->getMock();
$this->displayHandler->expects($this->any())
->method('getContentType')
->willReturn('json');
}
/**
* Tests that the symfony serializer receives style plugin from the render() method.
*
* @covers ::render
*/
public function testSerializerReceivesOptions() {
$mock_serializer = $this->prophesize(SerializerInterface::class);
// This is the main expectation of the test. We want to make sure the
// serializer options are passed to the SerializerInterface object.
$mock_serializer->serialize([], 'json', Argument::that(function ($argument) {
return isset($argument['views_style_plugin']) && $argument['views_style_plugin'] instanceof Serializer;
}))
->willReturn()
->shouldBeCalled();
$view_serializer_style = new Serializer([], 'dummy_serializer', [], $mock_serializer->reveal(), ['json', 'xml']);
$view_serializer_style->options = ['formats' => ['xml', 'json']];
$view_serializer_style->view = $this->view;
$view_serializer_style->displayHandler = $this->displayHandler;
$view_serializer_style->render();
}
}