From 5699dcb45e1bebb156948959c9d8cf9da652e9bb Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Thu, 22 Aug 2019 12:24:11 +1000
Subject: [PATCH] Issue #3043168 by Wim Leers, Niklan, gabesullice, joelstein,
 plach, mglaman: PATCH 405 for untranslatable content entities with different
 default language than English

---
 .../ParamConverter/EntityUuidConverter.php    |  5 +-
 .../JsonApiFunctionalMultilingualTest.php     | 20 +++---
 .../src/Functional/JsonApiRegressionTest.php  | 64 +++++++++++++++++++
 3 files changed, 78 insertions(+), 11 deletions(-)

diff --git a/core/modules/jsonapi/src/ParamConverter/EntityUuidConverter.php b/core/modules/jsonapi/src/ParamConverter/EntityUuidConverter.php
index aeec4c5d44b5..75f7181796ff 100644
--- a/core/modules/jsonapi/src/ParamConverter/EntityUuidConverter.php
+++ b/core/modules/jsonapi/src/ParamConverter/EntityUuidConverter.php
@@ -2,11 +2,10 @@
 
 namespace Drupal\jsonapi\ParamConverter;
 
-use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\TranslatableInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\ParamConverter\EntityConverter;
-use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\jsonapi\Routing\Routes;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
@@ -62,7 +61,7 @@ public function convert($value, $definition, $name, array $defaults) {
       $entity = reset($entities);
       // If the entity type is translatable, ensure we return the proper
       // translation object for the current context.
-      if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) {
+      if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
         // @see https://www.drupal.org/project/drupal/issues/2624770
         $entity_repository = isset($this->entityRepository) ? $this->entityRepository : $this->entityManager;
         $entity = $entity_repository->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']);
diff --git a/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php
index 01e01db7dcea..65cdf3c8baff 100644
--- a/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php
+++ b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php
@@ -25,6 +25,7 @@ class JsonApiFunctionalMultilingualTest extends JsonApiFunctionalTestBase {
    */
   public static $modules = [
     'language',
+    'content_translation',
   ];
 
   /**
@@ -45,6 +46,13 @@ protected function setUp() {
       ->set('url.prefixes.ca-fr', 'ca-fr')
       ->save();
 
+    ContentLanguageSettings::create([
+      'target_entity_type_id' => 'node',
+      'target_bundle' => 'article',
+    ])
+      ->setThirdPartySetting('content_translation', 'enabled', TRUE)
+      ->save();
+
     $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE);
   }
 
@@ -138,10 +146,8 @@ public function testPatchTranslation() {
 
     // Specifying a langcode is allowed once configured to be alterable. But
     // modifying the language of a non-default translation is still not allowed.
-    ContentLanguageSettings::create([
-      'target_entity_type_id' => 'node',
-      'target_bundle' => 'article',
-    ])->setLanguageAlterable(TRUE)
+    ContentLanguageSettings::loadByEntityTypeBundle('node', 'article')
+      ->setLanguageAlterable(TRUE)
       ->save();
     $response = $this->request('PATCH', Url::fromUri('base:/ca/jsonapi/node/article/' . $this->nodes[0]->uuid()), $request_options);
     $this->assertSame(500, $response->getStatusCode());
@@ -266,10 +272,8 @@ public function testPostTranslation() {
 
     // Specifying a langcode is allowed once configured to be alterable. Now an
     // entity can be created with the specified langcode.
-    ContentLanguageSettings::create([
-      'target_entity_type_id' => 'node',
-      'target_bundle' => 'article',
-    ])->setLanguageAlterable(TRUE)
+    ContentLanguageSettings::loadByEntityTypeBundle('node', 'article')
+      ->setLanguageAlterable(TRUE)
       ->save();
     $request_document['data']['attributes']['langcode'] = 'ca';
     $request_options[RequestOptions::BODY] = Json::encode($request_document);
diff --git a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
index bd05177b8c95..636e8d141f33 100644
--- a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
+++ b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
@@ -6,9 +6,12 @@
 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
 use Drupal\comment\Tests\CommentTestTrait;
 use Drupal\Component\Serialization\Json;
+use Drupal\Core\Entity\TranslatableInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
+use Drupal\entity_test\Entity\EntityTest;
 use Drupal\entity_test\Entity\EntityTestMapField;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
@@ -989,4 +992,65 @@ public function testLeakedCacheMetadataViaRdfFromIssue3053827() {
     $this->assertSame(200, $response->getStatusCode());
   }
 
+  /**
+   * Ensure non-translatable entities can be PATCHed with an alternate language.
+   *
+   * @see https://www.drupal.org/project/drupal/issues/3043168
+   */
+  public function testNonTranslatableEntityUpdatesFromIssue3043168() {
+    // Enable write-mode.
+    $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
+    // Set the site language to Russian.
+    $this->config('system.site')->set('langcode', 'ru')->set('default_langcode', 'ru')->save(TRUE);
+    // Install a "custom" entity type that is not translatable.
+    $this->assertTrue($this->container->get('module_installer')->install(['entity_test'], TRUE), 'Installed modules.');
+    // Clear and rebuild caches and routes.
+    $this->rebuildAll();
+    // Create a test entity.
+    // @see \Drupal\language\DefaultLanguageItem
+    $entity = EntityTest::create([
+      'name' => 'Alexander',
+      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+    ]);
+    $entity->save();
+    // Ensure it is an instance of TranslatableInterface and that it is *not*
+    // translatable.
+    $this->assertInstanceOf(TranslatableInterface::class, $entity);
+    $this->assertFalse($entity->isTranslatable());
+    // Set up a test user with permission to view and update the test entity.
+    $user = $this->drupalCreateUser(['view test entity', 'administer entity_test content']);
+    $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
+    $request_options[RequestOptions::AUTH] = [
+      $user->getAccountName(),
+      $user->pass_raw,
+    ];
+    // GET the test entity via JSON:API.
+    $entity_url = Url::fromUri('internal:/jsonapi/entity_test/entity_test/' . $entity->uuid());
+    $response = $this->request('GET', $entity_url, $request_options);
+    $this->assertSame(200, $response->getStatusCode());
+    $response_document = Json::decode($response->getBody());
+    // Ensure that the entity's langcode attribute is 'und'.
+    $this->assertSame(LanguageInterface::LANGCODE_NOT_SPECIFIED, $response_document['data']['attributes']['langcode']);
+    // Prepare to PATCH the entity via JSON:API.
+    $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json';
+    $request_options[RequestOptions::JSON] = [
+      'data' => [
+        'type' => 'entity_test--entity_test',
+        'id' => $entity->uuid(),
+        'attributes' => [
+          'name' => 'Constantine',
+        ],
+      ],
+    ];
+    // Issue the PATCH request and verify that the test entity was successfully
+    // updated.
+    $response = $this->request('PATCH', $entity_url, $request_options);
+    $this->assertSame(200, $response->getStatusCode(), (string) $response->getBody());
+    $response_document = Json::decode($response->getBody());
+    // Ensure that the entity's langcode attribute is still 'und' and the name
+    // was successfully updated.
+    $this->assertSame(LanguageInterface::LANGCODE_NOT_SPECIFIED, $response_document['data']['attributes']['langcode']);
+    $this->assertSame('Constantine', $response_document['data']['attributes']['name']);
+  }
+
 }
-- 
GitLab