We have recently been seeing an issue related to field aliases, but we can't track down the root cause of it.

In a previous deployment we created a new date field (field_event_date_timezone) and then migrated dates from the existing field (field_event_date) to the new field. Using the json api alias config, we disabled field_event_date, and then set an alias for field_event_date_timezone to the old field name of "field_event_date". Maybe a picture will help explain:

json api config

We recently started seeing an issue when sorting on the field alias. When we hit the API and try to sort by doing the following:

sort=-field_event_date.value

An exception is thrown:

Drupal\Core\Http\Exception\CacheableBadRequestHttpException: Invalid nested filtering. The field `field_event_date`, given in the path `field_event_date.value`, does not exist. in Drupal\jsonapi\Context\FieldResolver->resolveInternalEntityQueryPath() (line 296 of /.../docroot/core/modules/jsonapi/src/Context/FieldResolver.php)

However, the big issue for us is that it isn't consistent. The issue crept up this morning and I consistently saw it when hitting the API directly from my browser. I decided to clear the Drupal cache, and now I no longer see it in my browser, but I occasionally see it in the logs. The inconsistency is what is making this hard to track down. I am not able to reproduce in any of our lower environments either.

Any thoughts on what could be happening?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

neuquen created an issue. See original summary.

neuquen’s picture

Issue summary: View changes
neuquen’s picture

I still can't reproduce the issue locally, but I decided to mimic the failure by sorting by a non-existent field.

My guess, after looking through the code, is that somehow the ResourceType/ConfigurableResourceType object is missing the field.

The stack trace shows that a jsonapi_extras ConfigurableResourceType is passed into the first argument of the resolveInternalEntityQueryPath() function.

Drupal\jsonapi\Context\FieldResolver->resolveInternalEntityQueryPath(Object(Drupal\jsonapi_extras\ResourceType\ConfigurableResourceType), 'field_event_dat...')

Drupal\jsonapi\Context\FieldResolver->ResolveInternalEntityQueryPath() calls isMemberFilterable() which calls RsourceType::isFieldEnabled() which calls Resourcetype::hasField() which checks the ResourceType object to see if the key exists within the list of fields from the ConfigurableResourceType object:

  public function hasField($field_name) {
    return array_key_exists($field_name, $this->fields);
  }

Clearing the Drupal cache consistently fixes the problem, so caching the incorrect ResourceType/ConfigurableResourceType could be the issue. I've noticed specific $cacheability values being used, so it's obvious why it is being cached, but I'm not sure why it is being cached without that particular field.

I'm also not quite sure how jsonapi_extras actually sends the object to resolveInternalIntityQueryPath(). I noticed that
resolveInternalEntityQueryPath() receives the ResourceType object from Drupal\jsonapi\Controller\EntityResource->getCollectionQuery() which receives it from Drupal\jsonapi\Controller\EntityResource->getCollection(). But I'm not sure how jsonapi_extras passes the ResourceType to getCollection().

I poked around Drupal\jsonapi_extras\Entity\JsonapiResourceconfig.php and stepped through getFieldMapping(), which seemed to work fine.

Drupal\jsonapi_extras\ResourceType\ConfigurableResourceType->overridFieldMapping() also successfully replaced the overridden field mappings.

It looks like Drupal\jsonapi_extras\ResourceType\ConfigurableResourceTypeRepository->createResourceType() actually creates the ConfigurableResourceType object to mimic the ResourceType object from the jsonapi module, and that includes $this->overrideFieldMapping(), so that looks like it should work as well.

At this point, I'm not sure if I'm even close, or if I've gone down the wrong path in the rabbit hole. Any help/guidance would be greatly appreciated.

neuquen’s picture

Title: Invalid nested filtering » "Invalid nested filtering ... field does not exist" on aliased field
neuquen’s picture

I think I'm stuck at this point.

I've been looking more closely at ConfigurableResourceType::overrideFieldMapping():

  protected function overrideFieldMapping(JsonapiResourceConfig $resource_config) {
    // This is not ideal, but we cannot load the resource type to get the entity
    // type object. That is because this is used during the creation of the
    // ResourceType.
    list($entity_type_id, $bundle) = explode('--', $resource_config->getOriginalId());
    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
    $field_names = $this->getAllFieldNames($entity_type, $bundle);
    // Use the base class to fetch the non-configurable field mappings.
    $mappings = $resource_config->getFieldMapping();
    // Ignore all the fields that don't have aliases.
    $mappings = array_filter($mappings, function ($field_info) {
      return $field_info !== TRUE;
    });
    // Make sure to respect the overrides coming from JSON:API if there is no
    // input in JSON:API Extras.
    return array_merge(
      static::getFieldMapping($field_names, $entity_type, $bundle),
      $mappings
    );
  }

I'm noticing that it uses the Entity Type Manager to get the entity type definition, and then calls ResourceTypeRepository::getAllFieldNames() which uses the Entity Type Manager to get the field definitions, and return the array keys (field names) back to the overrideFieldMapping() function. I'm assuming that the Entity Type Manager isn't broken, so I would expect overrideFieldMapping() to always return the correct fields. JsonapiResourceConfig::getFieldMapping() also behaved fine when I tested it locally.

My biggest hang up at this point is that I can only mimic the invalid nested filtering error by intentionally passing in a non-existent field. I haven't been able to reproduce the error where an existing field is throwing the non-existent error message. With caching in the mix, I'm at a loss for how to replicate it.

neuquen’s picture

Issue summary: View changes
e0ipso’s picture

I have had the opportunity to scan the issue you posted. It is very well described and reasoned. I wish all the bug reports were as thorough as that one!

I have never used the field aliasing as a way to deprecate a field and have other step in. That is clever. However I can see how that could lead to issues, if the presence of a field definition shortcuts the check for aliases.

Sadly I am not dedicating much of my spare time to maintenance these days. But if you find a fix for this and submit a patch with a test I'll be sure to merge it. In parallel I am also trying to find the best person to be the co-maintainer of this module.

bbrala’s picture

Hmm, when i read this issue i think this might be fixed with the changes made for 9.x compatibilty. The reference to the fields will probably change to the internal name and use the ResourceTypeField class to change public name. This would mean there is no unfortunate random overwriting of the field at the wrong time since the index in the array should be the internal name at all times.

https://www.drupal.org/project/jsonapi_extras/issues/3069220#comment-137...

bbrala’s picture

bbrala’s picture

Unfortunately i reproduced this issue.

bbrala’s picture

I tried to prove this problem in a test, but when sorting on a nested value i get the expected results.

bbrala’s picture

FileSize
5.03 KB

Removed a local change i added to speed things up.

  • bbrala committed 4cd2be0 on 8.x-3.x
    Issue #3143378 by bbrala, neuquen, e0ipso: "Invalid nested filtering...
bbrala’s picture

Status: Active » Fixed
bbrala’s picture

bbrala’s picture

Status: Fixed » Closed (fixed)