diff --git a/core/lib/Drupal/Core/Entity/Controller/EntityController.php b/core/lib/Drupal/Core/Entity/Controller/EntityController.php index 0af9638..61498a5 100644 --- a/core/lib/Drupal/Core/Entity/Controller/EntityController.php +++ b/core/lib/Drupal/Core/Entity/Controller/EntityController.php @@ -74,6 +74,10 @@ public function title(RouteMatchInterface $route_match, EntityInterface $_entity } } + public function configController() { + return []; + } + /** * Provides a generic edit title callback. * diff --git a/core/lib/Drupal/Core/Entity/Routing/EmptyRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/EmptyRouteProvider.php index c722ae4..f80c775 100644 --- a/core/lib/Drupal/Core/Entity/Routing/EmptyRouteProvider.php +++ b/core/lib/Drupal/Core/Entity/Routing/EmptyRouteProvider.php @@ -30,6 +30,7 @@ public function getRoutes(EntityTypeInterface $entity_type) { ]); $routes->add("entity.{$entity_type_id}.canonical", $route); } + return $routes; } } diff --git a/core/modules/config/tests/config_test/src/Entity/ConfigTest.php b/core/modules/config/tests/config_test/src/Entity/ConfigTest.php index 8ad8262..3fc58b4 100644 --- a/core/modules/config/tests/config_test/src/Entity/ConfigTest.php +++ b/core/modules/config/tests/config_test/src/Entity/ConfigTest.php @@ -25,7 +25,10 @@ * "default" = "Drupal\config_test\ConfigTestForm", * "delete" = "Drupal\Core\Entity\EntityDeleteForm" * }, - * "access" = "Drupal\config_test\ConfigTestAccessControlHandler" + * "access" = "Drupal\config_test\ConfigTestAccessControlHandler", + * "route_provider" = { + * "default" = "\Drupal\Core\Entity\Routing\EmptyRouteProvider", + * }, * }, * config_prefix = "dynamic", * entity_keys = { @@ -34,6 +37,7 @@ * "status" = "status" * }, * links = { + * "canonical" = "/config_test/{config_test}", * "edit-form" = "/admin/structure/config_test/manage/{config_test}", * "delete-form" = "/admin/structure/config_test/manage/{config_test}/delete", * "enable" = "/admin/structure/config_test/manage/{config_test}/enable", diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index 468f5b3..906f422 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageException; +use Drupal\Core\Url; use Drupal\rest\Plugin\ResourceBase; use Drupal\rest\ResourceResponse; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; @@ -54,13 +55,16 @@ public function get(EntityInterface $entity) { $response = new ResourceResponse($entity, 200); $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); + if ($entity instanceof FieldableEntityInterface) { + 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); + } } } @@ -117,7 +121,13 @@ public function post(EntityInterface $entity = NULL) { // 201 Created responses return the newly created entity in the response // body. - $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE); + if ($entity->getEntityType()->hasLinkTemplate('canonical')) { + $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE); + } + else { + $url = Url::fromRoute('')->setAbsolute()->toString(TRUE); + } + $response = new ResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]); // Responses after creating an entity are not cacheable, so we add no // cacheability metadata here. diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php index 090dda8..454e8ba 100644 --- a/core/modules/rest/src/Tests/CreateTest.php +++ b/core/modules/rest/src/Tests/CreateTest.php @@ -10,6 +10,9 @@ use Drupal\comment\Tests\CommentTestTrait; use Drupal\Component\Serialization\Json; use Drupal\config_test\Entity\ConfigTest; +use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Config\Entity\ConfigEntityType; +use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\entity_test\Entity\EntityTest; use Drupal\node\Entity\Node; @@ -386,22 +389,29 @@ public function createAccountPerEntity($entity_type) { /** * Creates the entity over the REST API. * - * @param string $entity_type + * @param string $entity_type_id * The type of the entity that should be created. * @param string $serialized * The body for the POST request. */ - public function assertCreateEntityOverRestApi($entity_type, $serialized = NULL) { + public function assertCreateEntityOverRestApi($entity_type_id, $serialized = NULL) { + $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id); // Note: this will fail with PHP 5.6 when always_populate_raw_post_data is // set to something other than -1. See https://www.drupal.org/node/2456025. - $response = $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); + $response = $this->httpRequest('entity/' . $entity_type_id, 'POST', $serialized, $this->defaultMimeType); $this->assertResponse(201); // Make sure that the response includes an entity in the body and check the // UUID as an example. $request = Json::decode($serialized); $response = Json::decode($response); - $this->assertEqual($request['uuid'][0]['value'], $response['uuid'][0]['value'], 'Got new entity created as response after successful POST over Rest API'); + + if ($entity_type instanceof ConfigEntityTypeInterface) { + $this->assertEqual($request['uuid'], $response['uuid'], 'Got new entity created as response after successful POST over Rest API'); + } + else { + $this->assertEqual($request['uuid'][0]['value'], $response['uuid'][0]['value'], 'Got new entity created as response after successful POST over Rest API'); + } } /** @@ -427,9 +437,16 @@ public function assertReadEntityIdFromHeaderAndDb($entity_type, EntityInterface $this->assertEqual($entity->uuid(), $loaded_entity->uuid(), 'UUID of created entity is correct.'); // Verify that the field values sent and received from DB are the same. + foreach ($entity_values as $property => $value) { - $actual_value = $loaded_entity->get($property)->value; - $send_value = $entity->get($property)->value; + if ($loaded_entity instanceof ConfigEntityInterface) { + $actual_value = $loaded_entity->get($property); + $send_value = $entity->get($property); + } + else { + $actual_value = $loaded_entity->get($property)->value; + $send_value = $entity->get($property)->value; + } $this->assertEqual($send_value, $actual_value, 'Created property ' . $property . ' expected: ' . $send_value . ', actual: ' . $actual_value); } diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php index 2ee6f82..500c1f7 100644 --- a/core/modules/rest/src/Tests/RESTTestBase.php +++ b/core/modules/rest/src/Tests/RESTTestBase.php @@ -92,6 +92,7 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) { } $url = $this->buildUrl($url); + debug($url); switch ($method) { case 'GET': diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php index 1cf1eac..b7f8bf9 100644 --- a/core/modules/rest/src/Tests/ReadTest.php +++ b/core/modules/rest/src/Tests/ReadTest.php @@ -8,6 +8,7 @@ namespace Drupal\rest\Tests; use Drupal\Component\Serialization\Json; +use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Url; /** @@ -22,7 +23,7 @@ class ReadTest extends RESTTestBase { * * @var array */ - public static $modules = array('hal', 'rest', 'entity_test'); + public static $modules = array('hal', 'rest', 'entity_test', 'config_test'); /** * Tests several valid and invalid read requests on all entity types. @@ -30,7 +31,8 @@ class ReadTest extends RESTTestBase { public function testRead() { // @todo Expand this at least to users. // Define the entity types we want to test. - $entity_types = array('entity_test', 'node'); + // $entity_types = array('entity_test', 'node', 'config_test'); + $entity_types = array('config_test'); foreach ($entity_types as $entity_type) { $this->enableService('entity:' . $entity_type, 'GET'); // Create a user account that has the required permissions to read @@ -44,23 +46,41 @@ public function testRead() { $entity = $this->entityCreate($entity_type); $entity->save(); // Read it over the REST API. - $response = $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); + $response = $this->httpRequest($entity->urlInfo('canonical')->setRouteParameter('_format', $this->defaultFormat), 'GET'); $this->assertResponse('200', 'HTTP response code is correct.'); $this->assertHeader('content-type', $this->defaultMimeType); $data = Json::decode($response); // Only assert one example property here, other properties should be // checked in serialization tests. - $this->assertEqual($data['uuid'][0]['value'], $entity->uuid(), 'Entity UUID is correct'); + + if ($entity instanceof ConfigEntityInterface) { + $this->assertEqual($data['uuid'], $entity->uuid(), 'Entity UUID is correct'); + } + else { + $this->assertEqual($data['uuid'][0]['value'], $entity->uuid(), 'Entity UUID is correct'); + } // Try to read the entity with an unsupported mime format. - $response = $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', 'wrongformat'), 'GET'); + $response = $this->httpRequest($entity->urlInfo('canonical')->setRouteParameter('_format', 'wrongformat'), 'GET'); $this->assertResponse(406); $this->assertHeader('Content-type', 'application/json'); // Try to read an entity that does not exist. $response = $this->httpRequest(Url::fromUri('base://' . $entity_type . '/9999', ['query' => ['_format' => $this->defaultFormat]]), 'GET'); $this->assertResponse(404); - $path = $entity_type == 'node' ? '/node/{node}' : '/entity_test/{entity_test}'; + switch ($entity_type) { + case 'node': + $path = '/node/{node}'; + break; + case 'entity_test': + $path = '/entity_test/{entity_test}'; + break; + case 'config_test': + $path = '/config_test/{config_test}'; + break; + default: + throw new \RuntimeException('This entity type is not supported'); + } $expected_message = Json::encode(['message' => 'The "' . $entity_type . '" parameter was not converted for the path "' . $path . '" (route name: "rest.entity.' . $entity_type . '.GET.hal_json")']); $this->assertIdentical($expected_message, $response, 'Response message is correct.'); @@ -70,7 +90,7 @@ public function testRead() { if ($entity_type == 'entity_test') { $entity->field_test_text->value = 'no access value'; $entity->save(); - $response = $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); + $response = $this->httpRequest($entity->urlInfo('canonical')->setRouteParameter('_format', $this->defaultFormat), 'GET'); $this->assertResponse(200); $this->assertHeader('content-type', $this->defaultMimeType); $data = Json::decode($response); @@ -79,14 +99,14 @@ public function testRead() { // Try to read an entity without proper permissions. $this->drupalLogout(); - $response = $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); + $response = $this->httpRequest($entity->urlInfo('canonical')->setRouteParameter('_format', $this->defaultFormat), 'GET'); $this->assertResponse(403); $this->assertIdentical('{"message":""}', $response); } // Try to read a resource which is not REST API enabled. $account = $this->drupalCreateUser(); $this->drupalLogin($account); - $response = $this->httpRequest($account->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET'); + $response = $this->httpRequest($account->urlInfo('canonical')->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