- Issue created by @m.stenta
- Merge request !9Issue #3397275: Use OptionsProviderInterface::getPossibleOptions() for allowed... β (Open) created by m.stenta
- Open on Drupal.org βCore: 10.1.4 + Environment: PHP 8.1 & MySQL 8last update
about 1 year ago Waiting for branch to pass - Status changed to Needs review
about 1 year ago 4:59pm 27 October 2023 - Open on Drupal.org βCore: 10.1.4 + Environment: PHP 8.1 & MySQL 8last update
about 1 year ago Waiting for branch to pass - πΊπΈUnited States m.stenta
I managed to get this working, but I'd like to get some more eyes on it to see if it makes sense. I opened an MR, and also attached a patch.
This required one additional change outside of
DataDefinitionNormalizer
. Here is why...Within the context of
DataDefinitionNormalizer::normalize()
, when it is normalizing a field that implementsOptionsProviderInterface
, we have access to the following method arguments:$entity
- An object of type\Drupal\Core\TypedData\DataDefinition
, which represents the low-level data value of the field. (This is not an "entity" in the Drupal sense, despite its name.)$format
- This is alwaysschema_json
to indicate that this is being normalized into JSON Schema.$context
- An array with two keyed item:name
(the data name, usually "value" in this context) andparent
(theFieldItemDataDefinition
representing the field itself).
With these as our available input, we can get the field storage definition from
$context['parent']
, and then callgetOptionsProvider()
, which gives us the instantiated field item class object itself (eg:ListStringItem
), which we can then callgetPossibleOptions()
on.The catch is:
getOptionsProvider()
requires a Drupal entity be passed into it. This makes sense, because in theory the allowed values may depend on the specific entity in question. However, in this context, we are building a general JSON Schema for all entities of a certain bundle. We don't have a specific entity.So, my patch creates a mock entity. I can't see any way around this, but would love to know if there's a better way!
Now... even creating a mock entity is a little bit tricky because in the relevant context we're working in (
DataDefinitionNormalizer::normalize()
), we can't actually figure out what bundle we're dealing with! I tried getting that information from the field definition, with$context['parent']->getFieldDefinition()->getTargetBundle()
but this always seems to returnNULL
, so it was a non-starter.This brings me to the "one additional change outside of
DataDefinitionNormalizer
" I mentioned above...In
JsonApiSchemaController::addFieldsSchema()
, currently the only thing being added to$context
is$context['name']
(further down the chain, inComplexDataDefinitionNormalizer
,$context['parent']
gets added too). Another argument that is available to us inJsonApiSchemaController::addFieldsSchema()
is\Drupal\jsonapi\ResourceType\ResourceType $resource_type
. If we add that to$context['resource_type']
it becomes available downstream inDataDefinitionNormalizer::normalize()
, and we can do$resource_type->getEntityTypeId()
and$resource_type->getBundle()
to get the entity type ID and bundle.With these two pieces of information, we can create a mock entity of the correct bundle, pass that into
getOptionsProvider()
, and then callgetPossibleOptions()
.After that, we use the existing code to plug those allowed values into
anyOf
/oneOf
enumerations in the normalized JSON schema. Voila! :-DCurious what others think! Or if there is a better way to do any of this. Setting to "Needs review"...
- πΊπΈUnited States m.stenta
Also worth noting, I came across this recent issue from @joachim, which feels very related: #3332360: Add possible values for data types β
The MR in that issue *also* uses
getPossibleValues()
, but it seems to be for a different case than this one. It does not handle fields withallowed_values
, etc. Nor does it conflict with this MR. I found that with both, it added even more enumerations that are currently missing, so we may actually want to merge both of them. I also wonder if there's a way we could combine their logic together, so they handle all cases, but that was a bit beyond my current means, so I didn't dig into it. Curious if @joachim has any thoughts on this one! I will comment on the other issue to bring attention to this one. - Open on Drupal.org βCore: 10.2.1 + Environment: PHP 8.1 & MySQL 8last update
10 months ago Waiting for branch to pass - Open on Drupal.org βCore: 10.2.1 + Environment: PHP 8.1 & MySQL 8last update
10 months ago Waiting for branch to pass - πΊπΈUnited States m.stenta
It is also used in core's
BooleanItem
A user of this patch reported a bug today... tl;dr: we should omit
BooleanItem
fields from this logic.On
BooleanItem
fields, theoneOf
values that are included in the schema are1
and0
. This makes sense because that's what Drupal / PHP expects for boolean values, but JSON expectstrue
andfalse
.json-schema.org explicitly states:
Note that values that evaluate to
true
orfalse
, such as 1 and 0, are not accepted by the schema.https://json-schema.org/understanding-json-schema/reference/boolean
This means that it's not possible to validate boolean fields with this patch, because it only allows values (1 and 0) that are unacceptable.
I think the easiest solution to this is to simply omit
BooleanItem
fields from being included in this logic, so that we do not add theoneOf
property to them.I've added a commit to the MR to this effect, and attached an updated patch.