Problem/Motivation

I am using the Serial module (https://drupal.org/project/serial) which provides an auto-increment field type. When making a jsonapi request for bundles with serial fields, the field is present in the response, but the value is missing. An issue was filed on that project, but is stuck. See https://www.drupal.org/project/serial/issues/2921349.

The heart of the issue is how modules that provide FieldType plugins should interact with jsonapi. Currently, the advice has been to:

1) decorate a normalizer service
2) add a @DataType normalizer
3) write an enhancer using jsonapi_extras

I'm finding very little in the way of useful documentation or examples for how to do these things, and I haven't come up with any solution other than to hack the jsonapi module itself.

There are a lot of modules that provide @FieldType plugins, and not @DataType plugins. In the case of the Serial module, the field type is a computed field. In \Drupal\jsonapi\Normalizer\FieldItemNormalizer::normalize(), this line

$field_properties = TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item);

does not return an array of properties for the Serial field because it's a computed field. I feel like there may be a number of other modules that won't work with jsonapi for similar reasons.

Proposed resolution

My hack checks specifically for SerialItem fields to get the value:

diff --git a/src/Normalizer/FieldItemNormalizer.php b/src/Normalizer/FieldItemNormalizer.php
index 618d97e..fd7f50a 100644
--- a/src/Normalizer/FieldItemNormalizer.php
+++ b/src/Normalizer/FieldItemNormalizer.php
@@ -66,6 +66,9 @@ class FieldItemNormalizer extends NormalizerBase implements DenormalizerInterfac
     if (!empty($field_item->getProperties(TRUE))) {
       // We normalize each individual value, so each can do their own casting,
       // if needed.
+      if ($field_item instanceof \Drupal\serial\Plugin\Field\FieldType\SerialItem) {
+        $values = $field_item->get('value')->getValue();
+      }
       $field_properties = TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item);
       foreach ($field_properties as $property_name => $property) {
         $values[$property_name] = $this->serializer->normalize($property, $format, $context);

This patch shows what I'm trying to accomplish, and what I need is a way to do this without hacking up the module. I need some clear guidance regarding how to solve this problem - something that can be documented for other module maintainers who encounter similar issues.

CommentFileSizeAuthor
#4 serial_item_override.patch1.97 KBgabesullice

Comments

ebeyrent created an issue. See original summary.

ebeyrent’s picture

Issue summary: View changes
ebeyrent’s picture

After speaking with @pwolanin, we decided to look into why $field_properties = TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item); wasn't returning what we were expecting. The reason why is that in Drupal\Core\TypedData\DataDefinition::isInternal(), the code looks to see if the field definition has the 'internal' property set, and if it doesn't, it returns the value of isComputed(). The TypedDataInternalPropertiesHelpter::getNonInternalProperties() method returns the opposite of that.

In the case of the Serial field, it is marked as a computed field, but doesn't specify whether or not it's internal. In that module's FieldItem plugin, if I setInternal(FALSE) within the propertyDefinitions() method, that causes the value of the field to be included in the jsonapi response.

I think what this means is that jsonapi, in the FIeldItemNormalizer::normalize() method, is assuming along with core that all FieldType plugins are explicitly setting whether or not they are internal.

gabesullice’s picture

Title: Serial fields do not appear in jsonapi responses » Computed field properties do not appear in JSON:API responses unless their definition explicitly calls setInternal(FALSE)
Status: Active » Fixed
StatusFileSize
new1.97 KB

Hi @ebeyrent! First, let me sincerely say "thank you!" Your issue is very, very clear, reasonable and helpful. I really appreciate that.

I've updated the issue title to describe what I think the problem is as accurately as possible.

I think we're in a very difficult spot if we look at this problem as needing a solution in JSON:API. The internal value for field properties was explicitly designed to be the mechanism for hiding data from REST responses (JSON:API included). If we try to add some mechanism in JSON:API to override it, we'd be providing an API to override our own API. That just seems wrong.

I feel your pain though. I just spent about 45 minutes trying to find a simple solution that would attack the root of the problem for you without much luck. Unfortunately, the FieldType class is solely responsible for deciding which of its properties are internal or not.

I think we could consider turning this issue into a feature request for something like hook_field_property_info_alter(&$property_definitions, $field_type) which would let you surgically do what you want pretty dynamically.

OTOH, I think that is kinda risky and would let developers shoot themselves in the foot pretty easily. Not to mention, property definitions need to be pretty closely defined with the field schema. So, when you want to override them, I think you need to take responsibility for the whole field type, not just its definitions.


In typing that last paragraph out, I realized that exactly what I thought needed to be done is actually possible without too much code! I've attached a patch that creates a module that should solve your issue. In my testing locally with JSON:API, it worked and made the serial field appear :)

You can add this module to your site by running patch -p1 < serial_item_override.patch in your custom modules directory. That should place the new module in there for you (if it doesn't work, it's easy enough to copy and paste ;))

wim leers’s picture

#4++

Also: let's fix #2921349: Serial field value not in HTTP API responses (not in JSON:API, not in REST) instead so that this is solved forever, and not just for JSON:API, not just for your project, but also for REST and GraphQL!

Status: Fixed » Closed (fixed)

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