diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 10874680a6..610bfd5db9 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -6,6 +6,7 @@
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Config\Entity\ConfigEntityType;
+use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
@@ -180,31 +181,26 @@ public function post(EntityInterface $entity = NULL) {
// Validate the received data before saving.
$this->validate($entity);
- try {
- $entity->save();
- $this->logger->notice('Created entity %type with ID %id.', ['%type' => $entity->getEntityTypeId(), '%id' => $entity->id()]);
+ $entity->save();
+ $this->logger->notice('Created entity %type with ID %id.', ['%type' => $entity->getEntityTypeId(), '%id' => $entity->id()]);
- // 201 Created responses return the newly created entity in the response
- // body. These responses are not cacheable, so we add no cacheability
- // metadata here.
- $headers = [];
- if (in_array('canonical', $entity->uriRelationships(), TRUE)) {
- $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
- $headers['Location'] = $url->getGeneratedUrl();
- }
- return new ModifiedResourceResponse($entity, 201, $headers);
- }
- catch (EntityStorageException $e) {
- throw new HttpException(500, 'Internal Server Error', $e);
+ // 201 Created responses return the newly created entity in the response
+ // body. These responses are not cacheable, so we add no cacheability
+ // metadata here.
+ $headers = [];
+ if (in_array('canonical', $entity->uriRelationships(), TRUE)) {
+ $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
+ $headers['Location'] = $url->getGeneratedUrl();
}
+ return new ModifiedResourceResponse($entity, 201, $headers);
}
/**
* Responds to entity PATCH requests.
*
- * @param \Drupal\Core\Entity\EntityInterface $original_entity
+ * @param \Drupal\Core\Entity\ContentEntityInterface $original_entity
* The original entity object.
- * @param \Drupal\Core\Entity\EntityInterface $entity
+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity.
*
* @return \Drupal\rest\ModifiedResourceResponse
@@ -212,7 +208,7 @@ public function post(EntityInterface $entity = NULL) {
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
- public function patch(EntityInterface $original_entity, EntityInterface $entity = NULL) {
+ public function patch(ContentEntityInterface $original_entity, ContentEntityInterface $entity = NULL) {
if ($entity == NULL) {
throw new BadRequestHttpException('No entity content received.');
}
@@ -227,41 +223,41 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
// Overwrite the received fields.
foreach ($entity->_restSubmittedFields as $field_name) {
- $field = $entity->get($field_name);
- $original_field = $original_entity->get($field_name);
+ foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+ if (!$original_entity->hasTranslation($langcode)) {
+ $original_entity->addTranslation($langcode, $original_entity->toArray());
+ }
+ $field = $entity->getTranslation($langcode)->get($field_name);
+ $original_field = $original_entity->getTranslation($langcode)->get($field_name);
- // If the user has access to view the field, we need to check update
- // access regardless of the field value to avoid information disclosure.
- // (Otherwise the user may try PATCHing with value after value, until they
- // send the current value for the field, and then they won't get a 403
- // response anymore, which indicates that the value they sent in the PATCH
- // request body matches the current value.)
- if (!$original_field->access('view')) {
- if (!$original_field->access('edit')) {
+ // If the user has access to view the field, we need to check update
+ // access regardless of the field value to avoid information disclosure.
+ // (Otherwise the user may try PATCHing with value after value, until they
+ // send the current value for the field, and then they won't get a 403
+ // response anymore, which indicates that the value they sent in the PATCH
+ // request body matches the current value.)
+ if (!$original_field->access('view')) {
+ if (!$original_field->access('edit')) {
+ throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
+ }
+ }
+ // Check access for all received fields, but only if they are being
+ // changed. The bundle of an entity, for example, must be provided for
+ // denormalization to succeed, but it may not be changed.
+ elseif (!$original_field->equals($field) && !$original_field->access('edit')) {
throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
}
+ $original_entity->getTranslation($langcode)->set($field_name, $field->getValue());
}
- // Check access for all received fields, but only if they are being
- // changed. The bundle of an entity, for example, must be provided for
- // denormalization to succeed, but it may not be changed.
- elseif (!$original_field->equals($field) && !$original_field->access('edit')) {
- throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
- }
- $original_entity->set($field_name, $field->getValue());
}
// Validate the received data before saving.
$this->validate($original_entity);
- try {
- $original_entity->save();
- $this->logger->notice('Updated entity %type with ID %id.', ['%type' => $original_entity->getEntityTypeId(), '%id' => $original_entity->id()]);
+ $original_entity->save();
+ $this->logger->notice('Updated entity %type with ID %id.', ['%type' => $original_entity->getEntityTypeId(), '%id' => $original_entity->id()]);
- // Return the updated entity in the response body.
- return new ModifiedResourceResponse($original_entity, 200);
- }
- catch (EntityStorageException $e) {
- throw new HttpException(500, 'Internal Server Error', $e);
- }
+ // Return the updated entity in the response body.
+ return new ModifiedResourceResponse($original_entity, 200);
}
/**
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
index 09b4b64bae..a769b6b6e3 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
@@ -3,7 +3,7 @@
namespace Drupal\rest\Plugin\rest\resource;
use Drupal\Component\Render\PlainTextOutput;
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
@@ -16,31 +16,31 @@
/**
* Verifies that the whole entity does not violate any validation constraints.
*
- * @param \Drupal\Core\Entity\EntityInterface $entity
+ * @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity to validate.
*
* @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
* If validation errors are found.
*/
- protected function validate(EntityInterface $entity) {
+ protected function validate(ContentEntityInterface $entity) {
// @todo Remove when https://www.drupal.org/node/2164373 is committed.
if (!$entity instanceof FieldableEntityInterface) {
return;
}
- $violations = $entity->validate();
-
- // Remove violations of inaccessible fields as they cannot stem from our
- // changes.
- $violations->filterByFieldAccess();
-
- if ($violations->count() > 0) {
- $message = "Unprocessable Entity: validation failed.\n";
- foreach ($violations as $violation) {
- // We strip every HTML from the error message to have a nicer to read
- // message on REST responses.
- $message .= $violation->getPropertyPath() . ': ' . PlainTextOutput::renderFromHtml($violation->getMessage()) . "\n";
+ foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+ $violations = $entity->getTranslation($langcode)->validate();
+ // Remove violations of inaccessible fields as they cannot stem from our
+ // changes.
+ $violations->filterByFieldAccess();
+ if ($violations->count() > 0) {
+ $message = "Unprocessable Entity: validation failed.\n";
+ foreach ($violations as $violation) {
+ // We strip every HTML from the error message to have a nicer to read
+ // message on REST responses.
+ $message .= $violation->getPropertyPath() . ': ' . PlainTextOutput::renderFromHtml($violation->getMessage()) . "\n";
+ }
+ throw new UnprocessableEntityHttpException($message);
}
- throw new UnprocessableEntityHttpException($message);
}
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
index 5b5101445a..037bb3b606 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
@@ -277,8 +277,8 @@ public function testPostDxWithoutCriticalBaseFields() {
$response = $this->request('POST', $url, $request_options);
// @todo Uncomment, remove next 3 lines in https://www.drupal.org/node/2820364.
$this->assertSame(500, $response->getStatusCode());
- $this->assertSame(['text/plain; charset=UTF-8'], $response->getHeader('Content-Type'));
- $this->assertStringStartsWith('The website encountered an unexpected error. Please try again later.Symfony\Component\HttpKernel\Exception\HttpException: Internal Server Error in Drupal\rest\Plugin\rest\resource\EntityResource->post()', (string) $response->getBody());
+ $this->assertSame(['application/json'], $response->getHeader('Content-Type'));
+ $this->assertSame('{"message":"A fatal error occurred: The \u0022\u0022 entity type does not exist."}', (string) $response->getBody());
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// DX: 422 when missing 'entity_id' field.
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 72589bd3b8..5753c85942 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -473,12 +473,16 @@ public function testGet() {
if ($this->entity->getEntityType()->getLinkTemplates()) {
$this->assertArrayHasKey('Link', $response->getHeaders());
$link_relation_type_manager = $this->container->get('plugin.manager.link_relation_type');
- $expected_link_relation_headers = array_map(function ($relation_name) use ($link_relation_type_manager) {
+ $expected_link_relation_headers = array_filter(array_map(function ($relation_name) use ($link_relation_type_manager) {
+ // @todo What to do about content_translation link relationships?
+ if (strpos($relation_name, 'drupal:content-translation') !== FALSE) {
+ return FALSE;
+ }
$link_relation_type = $link_relation_type_manager->createInstance($relation_name);
return $link_relation_type->isRegistered()
? $link_relation_type->getRegisteredName()
: $link_relation_type->getExtensionUri();
- }, array_keys($this->entity->getEntityType()->getLinkTemplates()));
+ }, array_keys($this->entity->getEntityType()->getLinkTemplates())));
$parse_rel_from_link_header = function ($value) use ($link_relation_type_manager) {
$matches = [];
if (preg_match('/rel="([^"]+)"/', $value, $matches) === 1) {