Skip to content

PropertySchemaCollectionRestriction passes incorrect property metadata when merging constraint restrictions #5414

Open
@lstrojny

Description

@lstrojny

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 #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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions