Problem/Motivation

The api_json format is intended to only be usable by the JSON API module's routes. But … it's in fact possible to also use it on REST resources. Which then leads to confusion for end users, like for example #2835070: Views is returning empty entities.. If we want to avoid massive numbers of support requests along these lines (as well as related bugs), then we need to figure out a solution for this.

Steps to reproduce

  1. Install the JSON API module. The JSON API module has its own routes, but it also provides a serialization format.
  2. Install the REST module anyway.
  3. Create a "REST Export" view, and use the api_json format.

(Note that even Preston So got this wrong in his article at https://dev.acquia.com/blog/decoupled-drupal-with-ember-introducing-embe....)

I think this is a real problem: the REST module makes every installed serialization format available for use on REST resources, even when it's a format that does not really make sense in the REST module.

Proposed resolution

TBD

Remaining tasks

TBD

User interface changes

TBD

API changes

TBD

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

This is also a problem that modules like https://www.drupal.org/project/acquia_contenthub face.

Wim Leers’s picture

Priority: Critical » Major

Discussed with e0ipso, we agreed that this is major, because this is a pre-existing bug in core (see #2575761: Discuss a better system for discovering and selecting normalizers + #2822201: Allow contrib modules to deploy REST resources by creating routes, removing the need for config entities in that special case) due to an architectural oversight, that should only require minimal changes in the JSON API module code.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Issue tags: +DX (Developer Experience), +API-First Initiative

Been working on this today.

Wim Leers’s picture

Status: Active » Needs review
FileSize
3.08 KB

First: a failing test. To prove that currently, api_json is indeed one of the formats supported by the REST module.

Wim Leers’s picture

Ugh, I rolled it wrong NID and CID. Ah well. It's the right patch.

Status: Needs review » Needs work

The last submitted patch, 5: 2822201-8-test-only-FAIL.patch, failed testing.

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
6.83 KB
9.85 KB

Solution.

Wim Leers’s picture

FileSize
9.97 KB
4.1 KB

Clean-up.

Wim Leers’s picture

FileSize
9.97 KB
1.45 KB

Fix phpcs violations.

Wim Leers’s picture

Title: JSON API format should only be used on JSON API routes, but can also be used for REST resources » JSON API format should only be used on JSON API routes, but can currently also be used for REST resources
Assigned: Wim Leers » Unassigned
Wim Leers’s picture

+++ b/jsonapi.services.yml
@@ -100,4 +96,22 @@ services:
+  # We want the 'api_json' format to not be supported in the REST module, so we
+  # cannot register its encoder with the Serialization module. Hence we have an
+  # encoder without the 'encoder' tag and 'format' property. But that also
+  # means we cannot reuse the @serializer service. So we define our own
+  # serializer service. But we still do want to reuse the generic normalizers
+  # from the Serialization module. So we inject all normalizers just like the
+  # default @serializer service does. Only appropriate normalizers are called
+  # thanks to the supportsNormalization() API method.
+  # @see \Drupal\jsonapi\DependencyInjection\Compiler\RegisterNormalizerClassesCompilerPass
+  # @see \Drupal\jsonapi\JsonapiServiceProvider::register()
+  # @see \Drupal\Tests\jsonapi\Functional\RestJsonApiUnsupported
+  # @see \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization

+++ b/src/DependencyInjection/Compiler/RegisterNormalizerClassesCompilerPass.php
@@ -0,0 +1,37 @@
+  public function process(ContainerBuilder $container) {
+    $definition = $container->getDefinition('jsonapi.serializer');
+
+    // Retrieve registered Normalizers from the container.
+    foreach ($container->findTaggedServiceIds('normalizer') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $normalizers[$priority][] = new Reference($id);
+    }
+
+    // Add the registered Normalizers to the JSON API Serializer.
+    if (!empty($normalizers)) {
+      $definition->replaceArgument(0, $this->sort($normalizers));
+    }
+  }

The reason we have to do this messy thing:

15:12:21 <WimLeers> e0ipso: the problem is that we haven't really separated the 'normalizer' and 'encoder' concepts sufficiently
15:12:29 <WimLeers> e0ipso: well, when I say "we", I mean "Symfony"
15:12:41 <WimLeers> Symfony's serialization component is not designed for flexible, extensible systems like Drupal
15:12:49 <WimLeers> it's only designed for hand-written code maintained by a single owner

We have #2575761: Discuss a better system for discovering and selecting normalizers to improve that in D9.

e0ipso’s picture

I took a slightly different approach.

Wim Leers’s picture

HAHAHAHAHAHA.

^^-> that's me laughing at myself for being a moron. This is simpler. I like it :) Although it is also a bit more misleading/less explicit. That's okay though, at least for the foreseeable future :)


Review time!

#13 has no interdiff. I manually diffed, and the test coverage is identical to the one that I created. Great!

+++ b/src/DependencyInjection/Compiler/RemoveJsonapiFormatCompilerPass.php
@@ -0,0 +1,34 @@
+ * Adds services tagged 'normalizer' to the JSON API Serializer.
...
+   * Adds services to the Serializer.

These comments need to be updated. This class should contain a long comment similar to the one I added in jsonapi.services.yml.

e0ipso’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. Merging.

  • e0ipso committed 74a3440 on 8.x-1.x authored by Wim Leers
    fix(Serialization) Use api_json format only on JSON API routes not REST...
e0ipso’s picture

Status: Reviewed & tested by the community » Fixed

This is a great DX fix!!

Wim Leers’s picture

Yes it is :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.