PropertySchemaCollectionRestriction passes incorrect property metadata when merging constraint restrictions
API Platform version(s) affected: main
Description
When PropertySchemaCollectionRestriction attempts to generate the restrictions for the sub-graph, it passes incorrect property metadata.
How to reproduce
Given a validation rule like this:
<property name="someArray">
<constraint name="Collection">
<option name="fields">
<value key="someField">
<constraint name="Optional">
<constraint name="Length">
<option name="min">5</option>
</constraint>
</constraint>
</value>
</option>
</constraint>
</property>
This would be the expected OpenAPI schema:
"Schema": {
"type": "object",
"properties": {
"someArray": {
"type": "object",
"properties": {
"someField": {
"minLength": 5
},
},
"additionalProperties": false,
"items": {
"type": "string"
}
}
}
}
Instead this is the schema generated:
"Schema": {
"type": "object",
"properties": {
"someArray": {
"type": "object",
"properties": {
"someField": [],
},
"additionalProperties": false,
"items": {
"type": "string"
}
}
}
}
This is a) invalid because of the empty array but that one is filed with https://github.com/api-platform/core/issues/5413 but b) it misses the length restriction because of this check in PropertySchemaLengthRestriction::supports():
public function supports(Constraint $constraint, ApiProperty $propertyMetadata): bool
{
return $constraint instanceof Length && null !== ($type = $propertyMetadata->getBuiltinTypes()[0] ?? null) && Type::BUILTIN_TYPE_STRING === $type->getBuiltinType();
}
… in combination with PropertySchemaCollectionRestriction::mergeConstraintRestrictions():
private function mergeConstraintRestrictions(Required|Optional $constraint, ApiProperty $propertyMetadata): array
{
$propertyRestrictions = [];
$nestedConstraints = $constraint->constraints;
foreach ($nestedConstraints as $nestedConstraint) {
foreach ($this->restrictionsMetadata as $restrictionMetadata) {
if ($restrictionMetadata->supports($nestedConstraint, $propertyMetadata) && !empty($nestedConstraintRestriction = $restrictionMetadata->create($nestedConstraint, $propertyMetadata))) {
$propertyRestrictions[] = $nestedConstraintRestriction;
}
}
}
return array_merge([], ...$propertyRestrictions);
}
In mergeConstraintRestrictions $propertyMetadata is the property metadata of the container element (the array) and not the sub-element.
Since it’s an array, the sub elements are fundamentally unknowable. My hunch is, that the restriction implementations should not check at all for matching types and just assume that the validation rules are operating on the right type.
:+1: good catch, feel free to propose a patch