diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php index 7e02cb7..18a4dd9 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php @@ -73,6 +73,36 @@ public function post($id, EntityInterface $entity) { } /** + * Responds to entity PUT requests. + * + * @param mixed $id + * The entity ID. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + * + * @return \Drupal\rest\ResourceResponse + * The HTTP response object. + * + * @throws \Symfony\Component\HttpKernel\Exception\HttpException + */ + public function put($id, EntityInterface $entity) { + if (empty($id)) { + throw new NotFoundHttpException(); + } + $info = $entity->entityInfo(); + // Make sure that the entity ID is the one provided in the URL. + $entity->{$info['entity_keys']['id']} = $id; + try { + $entity->save(); + // Update responses have an empty body. + return new ResourceResponse(NULL, 200); + } + catch (EntityStorageException $e) { + throw new HttpException(500, 'Internal Server Error', $e); + } + } + + /** * Responds to entity DELETE requests. * * @param mixed $id diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php index 85b0c44..3640e48 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php @@ -101,25 +101,38 @@ protected function httpRequest($url, $method, $body = NULL, $format = 'applicati /** * Creates entity objects based on their types. * - * Required properties differ from entity type to entity type, so we keep a - * minimum mapping here. - * * @param string $entity_type - * The type of the entity that should be created.. + * The type of the entity that should be created. * * @return \Drupal\Core\Entity\EntityInterface * The new entity object. */ protected function entityCreate($entity_type) { + return entity_create($entity_type, $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 + * The type of the entity that should be created. + * + * @return array + * An array of values keyed by property name. + */ + protected function entityValues($entity_type) { switch ($entity_type) { case 'entity_test': - return entity_create('entity_test', array('name' => $this->randomName(), 'user_id' => 1)); + return array('name' => $this->randomName(), 'user_id' => 1); case 'node': - return entity_create('node', array('title' => $this->randomString())); + return array('title' => $this->randomString()); case 'user': - return entity_create('user', array('name' => $this->randomName())); + return array('name' => $this->randomName()); default: - return entity_create($entity_type, array()); + return array(); } } diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php new file mode 100644 index 0000000..f438043 --- /dev/null +++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php @@ -0,0 +1,84 @@ + 'Update resource', + 'description' => 'Tests the update of resources.', + 'group' => 'REST', + ); + } + + /** + * Tests several valid and invalid update requests on test entities. + */ + public function testCreate() { + $serializer = drupal_container()->get('serializer'); + // @todo once EntityNG is implemented for other entity types test all other + // entity types here as well. + $entity_type = 'entity_test'; + + $this->enableService('entity:' . $entity_type); + // Create a user account that has the required permissions to create + // resources via the web API. + $account = $this->drupalCreateUser(array('restful put entity:' . $entity_type)); + $this->drupalLogin($account); + + // Create an entity and save it to the database. + $entity_values = $this->entityValues($entity_type); + $entity = entity_create($entity_type, $entity_values); + $entity->save(); + // Create a second entity that will overwrite the original. + $update_values = $this->entityValues($entity_type); + // @todo Remove the next line once deserialization is implemented. For now + // we set the update value to the same that is used in the RequestHandler. + $update_values['name'] = 'test'; + $update_entity = entity_create($entity_type, $entity_values); + + $serialized = $serializer->serialize($update_entity, 'drupal_jsonld'); + // Update the entity over the web API. + $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json'); + $this->assertResponse('200', 'HTTP response code is correct.'); + + // Re-load updated entity from the database. + $entity = entity_load($entity_type, $entity->id(), TRUE); + foreach ($update_values as $property => $value) { + $actual_value = $entity->get($property); + $this->assertEqual($value, $actual_value->value, 'Updated property ' . $property . ' expected: ' . $value . ', actual: ' . $actual_value->value); + } + + // Try to update an entity without proper permissions. + $this->drupalLogout(); + $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json'); + $this->assertResponse(403); + + // Try to update a resource which is not web API enabled. + $this->enableService(FALSE); + // Reset cURL here because it is confused from our previously used cURL + // options. + unset($this->curlHandle); + $this->drupalLogin($account); + $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json'); + $this->assertResponse(404); + } +}