diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php index ae5ca76..e62584d 100644 --- a/core/modules/file/src/Entity/File.php +++ b/core/modules/file/src/Entity/File.php @@ -235,6 +235,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['uid'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('User ID')) ->setDescription(t('The user ID of the file.')) + ->setDefaultValueCallback('Drupal\file\Entity\File::getCurrentUserId') ->setSetting('target_type', 'user'); $fields['filename'] = BaseFieldDefinition::create('string') @@ -275,4 +276,16 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { return $fields; } + /** + * Default value callback for 'uid' base field definition. + * + * @see ::baseFieldDefinitions() + * + * @return array + * An array of default values. + */ + public static function getCurrentUserId() { + return array(\Drupal::currentUser()->id()); + } + } diff --git a/core/modules/file/src/FileAccessControlHandler.php b/core/modules/file/src/FileAccessControlHandler.php index f70879d..6745969 100644 --- a/core/modules/file/src/FileAccessControlHandler.php +++ b/core/modules/file/src/FileAccessControlHandler.php @@ -11,6 +11,8 @@ use Drupal\Core\Entity\EntityAccessControlHandler; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Session\AccountInterface; /** @@ -68,4 +70,23 @@ protected function getFileReferences(FileInterface $file) { return file_get_file_references($file, NULL, EntityStorageInterface::FIELD_LOAD_REVISION, NULL); } + /** + * {@inheritdoc} + */ + protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { + return AccessResult::allowed(); + } + + /** + * {@inheritdoc} + */ + protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) { + // No user can edit the status of a file. Prevents saving a new file as + // persistent before even validating it. + if ($field_definition->getName() == 'status' && $operation == 'edit') { + return AccessResult::forbidden(); + } + return AccessResult::allowed(); + } + } diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml index e817fbf..58cc6cd 100644 --- a/core/modules/hal/hal.services.yml +++ b/core/modules/hal/hal.services.yml @@ -16,7 +16,7 @@ services: class: Drupal\hal\Normalizer\FileEntityNormalizer tags: - { name: normalizer, priority: 20 } - arguments: ['@entity.manager', '@http_client', '@rest.link_manager', '@module_handler'] + arguments: ['@rest.link_manager', '@entity.manager', '@module_handler', '@file_system'] serializer.normalizer.entity.hal: class: Drupal\hal\Normalizer\ContentEntityNormalizer arguments: ['@rest.link_manager', '@entity.manager', '@module_handler'] diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index 9c71da4..2c66c29 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -132,8 +132,9 @@ public function denormalize($data, $class, $format = NULL, array $context = arra throw new UnexpectedValueException('The type link relation must be specified.'); } - // Create the entity. + // Identify entity type and bundle. $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context); + // Get definition of entity type and bundle (if any). $entity_type = $this->entityManager->getDefinition($typed_data_ids['entity_type']); $langcode_key = $entity_type->getKey('langcode'); $values = array(); @@ -148,10 +149,9 @@ public function denormalize($data, $class, $format = NULL, array $context = arra if ($entity_type->hasKey('bundle')) { $bundle_key = $entity_type->getKey('bundle'); $values[$bundle_key] = $typed_data_ids['bundle']; - // Unset the bundle key from data, if it's there. - unset($data[$bundle_key]); } + // Create the entity. $entity = $this->entityManager->getStorage($typed_data_ids['entity_type'])->create($values); // Remove links from data array. diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 7c1943a..51f5f89 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -8,6 +8,7 @@ namespace Drupal\hal\Normalizer; use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\Core\Field\FieldItemInterface; use Drupal\rest\LinkManager\LinkManagerInterface; use Drupal\serialization\EntityResolver\EntityResolverInterface; use Drupal\serialization\EntityResolver\UuidReferenceInterface; @@ -58,12 +59,19 @@ public function normalize($field_item, $format = NULL, array $context = array()) /** @var $field_item \Drupal\Core\Field\FieldItemInterface */ $target_entity = $field_item->get('entity')->getValue(); + // Parent implementation simply copies the properties. + $properties = parent::normalize($field_item, $format, $context); + // If this is not a content entity, let the parent implementation handle it, // only content entities are supported as embedded resources. if (!($target_entity instanceof FieldableEntityInterface)) { - return parent::normalize($field_item, $format, $context); + return $properties; } + // Discard the target_id property. + $field_name = $field_item->getParent()->getName(); + unset($properties[$field_name][0]['target_id']); + // If the parent entity passed in a langcode, unset it before normalizing // the target entity. Otherwise, untranslatable fields of the target entity // will include the langcode. @@ -83,15 +91,13 @@ public function normalize($field_item, $format = NULL, array $context = array()) // The returned structure will be recursively merged into the normalized // entity so that the items are properly added to the _links and _embedded // objects. - $field_name = $field_item->getParent()->getName(); - $entity = $field_item->getEntity(); - $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name, $context); + $field_uri = $this->getFieldRelationUri($field_item); return array( '_links' => array( $field_uri => array($link), ), '_embedded' => array( - $field_uri => array($embedded), + $field_uri => array($embedded + $properties[$field_name][0]), ), ); } @@ -100,12 +106,19 @@ public function normalize($field_item, $format = NULL, array $context = array()) * {@inheritdoc} */ protected function constructValue($data, $context) { + /** @var FieldItemInterface $field_item */ $field_item = $context['target_instance']; $field_definition = $field_item->getFieldDefinition(); $target_type = $field_definition->getSetting('target_type'); $id = $this->entityResolver->resolve($this, $data, $target_type); if (isset($id)) { - return array('target_id' => $id); + $constructed = array('target_id' => $id); + foreach ($field_item->getProperties() as $property => $value) { + if ($property != 'target_id' && array_key_exists($property, $data)) { + $constructed[$property] = $data[$property]; + } + } + return $constructed; } return NULL; } @@ -124,4 +137,21 @@ public function getUuid($data) { } } + /** + * Gets the relation URI of the field containing an item. + * + * The relation URI is used as a property key when building the HAL structure. + * + * @param FieldItemInterface $field_item + * The field item that is being normalized. + * + * @return string + * The relation URI of the field. + */ + protected function getFieldRelationUri(FieldItemInterface $field_item) { + $field_name = $field_item->getParent()->getName(); + $entity = $field_item->getEntity(); + return $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name); + } + } diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php index 52ee04b..e3f5df1 100644 --- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php @@ -10,7 +10,8 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\rest\LinkManager\LinkManagerInterface; -use GuzzleHttp\ClientInterface; +use Drupal\Core\File\FileSystemInterface; +use Symfony\Component\Serializer\Exception\RuntimeException; /** * Converts the Drupal entity object structure to a HAL array structure. @@ -25,28 +26,19 @@ class FileEntityNormalizer extends ContentEntityNormalizer { protected $supportedInterfaceOrClass = 'Drupal\file\FileInterface'; /** - * The HTTP client. - * - * @var \GuzzleHttp\ClientInterface + * @var \Drupal\Core\File\FileSystemInterface */ - protected $httpClient; + protected $fileSystem; /** - * Constructs a FileEntityNormalizer object. + * Constructs an FileEntityNormalizer object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \GuzzleHttp\ClientInterface $http_client - * The HTTP Client. - * @param \Drupal\rest\LinkManager\LinkManagerInterface $link_manager - * The hypermedia link manager. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler. + * @param \Drupal\Core\File\FileSystemInterface $file_system + * The file system. */ - public function __construct(EntityManagerInterface $entity_manager, ClientInterface $http_client, LinkManagerInterface $link_manager, ModuleHandlerInterface $module_handler) { + public function __construct(LinkManagerInterface $link_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, FileSystemInterface $file_system) { + $this->fileSystem = $file_system; parent::__construct($link_manager, $entity_manager, $module_handler); - - $this->httpClient = $http_client; } /** @@ -54,8 +46,6 @@ public function __construct(EntityManagerInterface $entity_manager, ClientInterf */ public function normalize($entity, $format = NULL, array $context = array()) { $data = parent::normalize($entity, $format, $context); - // Replace the file url with a full url for the file. - $data['uri'][0]['value'] = $this->getEntityUri($entity); return $data; } @@ -64,12 +54,28 @@ public function normalize($entity, $format = NULL, array $context = array()) { * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = array()) { - $file_data = (string) $this->httpClient->get($data['uri'][0]['value'])->getBody(); + // File content can be passed base64 encoded in a special "data" property. + // That property is not a field, so we remove it before denormalizing the + // rest of the file entity. + $file_data = $data['data'][0]['value']; + unset($data['data']); + + $entity = parent::denormalize($data, $class, $format, $context); - $path = 'temporary://' . drupal_basename($data['uri'][0]['value']); - $data['uri'] = file_unmanaged_save_data($file_data, $path); + // Decode and save to file if it's a new file. + if (!isset($context['request_method']) || $context['request_method'] != 'patch') { + $file_contents = base64_decode($file_data); + $dirname = $this->fileSystem->dirname($entity->getFileUri()); + file_prepare_directory($dirname, FILE_CREATE_DIRECTORY); + if ($uri = file_unmanaged_save_data($file_contents, $entity->getFileUri())) { + $entity->setFileUri($uri); + } + else { + throw new RuntimeException('failed to write ' . $entity->getFilename()); + } + } - return $this->entityManager->getStorage('file')->create($data); + return $entity; } } diff --git a/core/modules/hal/src/Tests/EntityTest.php b/core/modules/hal/src/Tests/EntityTest.php index 6301fb0..a088b1c 100644 --- a/core/modules/hal/src/Tests/EntityTest.php +++ b/core/modules/hal/src/Tests/EntityTest.php @@ -8,6 +8,8 @@ namespace Drupal\hal\Tests; use Drupal\comment\Tests\CommentTestTrait; +use Drupal\file\Entity\File; +use Drupal\user\Entity\User; /** * Tests that nodes and terms are correctly normalized and denormalized. @@ -23,7 +25,7 @@ class EntityTest extends NormalizerTestBase { * * @var array */ - public static $modules = array('node', 'taxonomy', 'comment'); + public static $modules = array('node', 'taxonomy', 'comment', 'file'); /** * {@inheritdoc} @@ -36,6 +38,7 @@ protected function setUp() { $this->installSchema('comment', array('comment_entity_statistics')); $this->installEntitySchema('taxonomy_term'); $this->installConfig(['node', 'comment']); + $this->installEntitySchema('file'); } /** @@ -222,4 +225,41 @@ public function testComment() { } } + /** + * Tests the normalization of files. + */ + public function testFile() { + $user = User::create([ + 'name' => 'fileTestingUser', + ]); + $user->save(); + + $file_uri = 'public://normalization_test_file'; + $file_contents = 'hello world'; + $data = base64_encode($file_content); + file_put_contents($file_uri, $file_content); + $file = File::create([ + 'uid' => $user->id(), + 'uri' => $file_uri, + 'status' => FILE_STATUS_PERMANENT, + ]); + $file->save(); + + $original_values = $file->toArray(); + unset($original_values['fid']); + + $normalized = $this->serializer->normalize($file, $this->format); + // Adding data to the entity. + $normalized['data'][0]['value'] = $data; + // Use PATCH to avoid trying to create new file on denormalize. + $denormalized_file = $this->serializer->denormalize($normalized, File::class, $this->format, ['request_method' => 'patch']); + // Verify that the ID was skipped by the normalizer. + $this->assertEqual(NULL, $denormalized_file->id()); + + // Loop over the remaining fields and verify that they are identical. + foreach ($original_values as $field_name => $field_values) { + $this->assertEqual($field_values, $denormalized_file->get($field_name)->getValue()); + } + } + } diff --git a/core/modules/hal/src/Tests/FileDenormalizeTest.php b/core/modules/hal/src/Tests/FileDenormalizeTest.php index 853b06d..2482788 100644 --- a/core/modules/hal/src/Tests/FileDenormalizeTest.php +++ b/core/modules/hal/src/Tests/FileDenormalizeTest.php @@ -39,14 +39,18 @@ public function testFileDenormalize() { $file = entity_create('file', $file_params); file_put_contents($file->getFileUri(), 'hello world'); $file->save(); + $data = file_get_contents($file_params['uri']); + $data = base64_encode($data); $serializer = \Drupal::service('serializer'); $normalized_data = $serializer->normalize($file, 'hal_json'); - $denormalized = $serializer->denormalize($normalized_data, 'Drupal\file\Entity\File', 'hal_json'); - + // Adding data to the entity. + $normalized_data['data'][0]['value'] = $data; + // Use 'patch' to avoid trying to recreate the file. + $denormalized = $serializer->denormalize($normalized_data, File::class, 'hal_json', array('request_method' => 'patch')); $this->assertTrue($denormalized instanceof File, 'A File instance was created.'); - $this->assertIdentical('temporary://' . $file->getFilename(), $denormalized->getFileUri(), 'The expected file URI was found.'); + $this->assertIdentical('public://' . $file->getFilename(), $denormalized->getFileUri()); $this->assertTrue(file_exists($denormalized->getFileUri()), 'The temporary file was found.'); $this->assertIdentical($file->uuid(), $denormalized->uuid(), 'The expected UUID was found'); @@ -54,27 +58,6 @@ public function testFileDenormalize() { $this->assertIdentical($file->getFilename(), $denormalized->getFilename(), 'The expected filename was found.'); $this->assertTrue($denormalized->isPermanent(), 'The file has a permanent status.'); - // Try to denormalize with the file uri only. - $file_name = 'test_2.txt'; - $file_path = 'public://' . $file_name; - - file_put_contents($file_path, 'hello world'); - $file_uri = file_create_url($file_path); - - $data = array( - 'uri' => array( - array('value' => $file_uri), - ), - ); - - $denormalized = $serializer->denormalize($data, 'Drupal\file\Entity\File', 'hal_json'); - - $this->assertIdentical('temporary://' . $file_name, $denormalized->getFileUri(), 'The expected file URI was found.'); - $this->assertTrue(file_exists($denormalized->getFileUri()), 'The temporary file was found.'); - - $this->assertIdentical('text/plain', $denormalized->getMimeType(), 'The expected mime type was found.'); - $this->assertIdentical($file_name, $denormalized->getFilename(), 'The expected filename was found.'); - $this->assertFalse($denormalized->isPermanent(), 'The file has a permanent status.'); } } diff --git a/core/modules/hal/src/Tests/FileFieldNormalizeTest.php b/core/modules/hal/src/Tests/FileFieldNormalizeTest.php new file mode 100644 index 0000000..d03b088 --- /dev/null +++ b/core/modules/hal/src/Tests/FileFieldNormalizeTest.php @@ -0,0 +1,117 @@ +installEntitySchema('file'); + $this->installSchema('file', ['file_usage']); + } + + /** + * Tests that file field is identical before and after de/serialization. + */ + public function testFileFieldNormalize() { + // Create a file. + $file_name = 'test_file_field_normalize.txt'; + file_put_contents("public://$file_name", 'hello world'); + $file = File::create([ + 'uri' => "public://$file_name", + )); + $file->save(); + + // Attach a file field to the bundle. + FieldStorageConfig::create([ + 'type' => 'file', + 'entity_type' => 'entity_test', + 'field_name' => 'field_file', + ])->save(); + FieldConfig::create([ + 'field_name' => 'field_file', + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ])->save(); + + // Create an entity referencing the file. + $entity = EntityTest::create([ + 'field_file' => [ + 'target_id' => $file->id(), + 'display' => 0, + 'description' => 'An attached file', + ], + ]); + + $serialized = $this->container->get('serializer')->serialize($entity, $this->format); + $deserialized = $this->container->get('serializer')->deserialize($serialized, EntityTest::class, $this->format); + $this->assertEqual($entity->toArray()['field_file'], $deserialized->toArray()['field_file'], 'File field is preserved.'); + } + + /** + * Tests that image field is identical before and after de/serialization. + */ + public function testImageFieldNormalize() { + // Create a file. + $file_name = $this->randomMachineName() . '.png'; + file_put_contents("public://$file_name", $this->randomString()); + $file = File::create([ + 'uri' => "public://$file_name", + ]); + $file->save(); + + // Attach an image field to the bundle. + FieldStorageConfig::create([ + 'type' => 'image', + 'entity_type' => 'entity_test', + 'field_name' => 'field_image', + ])->save(); + FieldConfig::create([ + 'field_name' => 'field_image', + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ])->save(); + + // Create an entity referencing the file. + $entity = EntityTest::create([ + 'field_image' => [ + 'target_id' => $file->id(), + 'title' => $this->randomString(), + 'alt' => $this->randomString(), + 'width' => 400, + 'height' => 300, + ], + ]); + + $serialized = $this->container->get('serializer')->serialize($entity, $this->format); + $deserialized = $this->container->get('serializer')->deserialize($serialized, EntityTest::class, $this->format); + $this->assertEqual($entity->toArray()['field_image'], $deserialized->toArray()['field_image'], 'Image field is preserved.'); + } +} diff --git a/core/modules/hal/src/Tests/FileNormalizeTest.php b/core/modules/hal/src/Tests/FileNormalizeTest.php index 64ffd07..82c3200 100644 --- a/core/modules/hal/src/Tests/FileNormalizeTest.php +++ b/core/modules/hal/src/Tests/FileNormalizeTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Cache\MemoryBackend; use Drupal\hal\Encoder\JsonEncoder; use Drupal\hal\Normalizer\FieldItemNormalizer; +use Drupal\hal\Normalizer\FieldNormalizer; use Drupal\hal\Normalizer\FileEntityNormalizer; use Drupal\rest\LinkManager\LinkManager; use Drupal\rest\LinkManager\RelationLinkManager; @@ -43,8 +44,9 @@ protected function setUp() { // Set up the mock serializer. $normalizers = array( + new FieldNormalizer(), new FieldItemNormalizer(), - new FileEntityNormalizer($entity_manager, \Drupal::httpClient(), $link_manager, \Drupal::moduleHandler()), + new FileEntityNormalizer($link_manager, $entity_manager, \Drupal::moduleHandler(), $this->container->get('file_system')), ); $encoders = array( @@ -72,13 +74,17 @@ public function testNormalize() { $expected_array = array( 'uri' => array( array( - 'value' => file_create_url($file->getFileUri())), + 'value' => $file->getFileUri()), + ), + 'data' => array( + array( + 'value' => base64_encode('hello world'), + ), ), ); $normalized = $this->serializer->normalize($file, $this->format); - $this->assertEqual($normalized['uri'], $expected_array['uri'], 'URI is normalized.'); - + $this->assertEqual($normalized['uri'], $expected_array['uri']); } } diff --git a/core/modules/hal/src/Tests/NormalizerTestBase.php b/core/modules/hal/src/Tests/NormalizerTestBase.php index 104a5e2..675a251 100644 --- a/core/modules/hal/src/Tests/NormalizerTestBase.php +++ b/core/modules/hal/src/Tests/NormalizerTestBase.php @@ -13,6 +13,7 @@ use Drupal\hal\Normalizer\EntityReferenceItemNormalizer; use Drupal\hal\Normalizer\FieldItemNormalizer; use Drupal\hal\Normalizer\FieldNormalizer; +use Drupal\hal\Normalizer\FileEntityNormalizer; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\rest\LinkManager\LinkManager; use Drupal\rest\LinkManager\RelationLinkManager; @@ -134,14 +135,15 @@ protected function setUp() { 'translatable' => TRUE, ))->save(); - $entity_manager = \Drupal::entityManager(); - $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); + //$module_handler = \Drupal::moduleHandler(); + $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), $this->container->get('module_handler'), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $this->container->get('entity.manager'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); - $chain_resolver = new ChainEntityResolver(array(new UuidResolver($entity_manager), new TargetIdResolver())); + $chain_resolver = new ChainEntityResolver(array(new UuidResolver($this->container->get('entity.manager')), new TargetIdResolver())); // Set up the mock serializer. $normalizers = array( - new ContentEntityNormalizer($link_manager, $entity_manager, \Drupal::moduleHandler()), + new FileEntityNormalizer($link_manager, $this->container->get('entity.manager'), $this->container->get('module_handler'), $this->container->get('file_system')), + new ContentEntityNormalizer($link_manager, $this->container->get('entity.manager'), $this->container->get('module_handler')), new EntityReferenceItemNormalizer($link_manager, $chain_resolver), new FieldItemNormalizer(), new FieldNormalizer(), diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index 4b4d027..7b99baf 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -106,11 +106,16 @@ public function post(EntityInterface $entity = NULL) { try { $entity->save(); $this->logger->notice('Created entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id())); - - // 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); + // 201 Created responses have an empty body. Verify if canonical path + // exists. + if ($entity->hasLinkTemplate('canonical')) { + $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE); + $response = new ResourceResponse(NULL, 201, ['Location' => $url->getGeneratedUrl()]); + $response->addCacheableDependency($url); + } + else { + $response = new ResourceResponse(NULL, 201); + } return $response; } catch (EntityStorageException $e) {