From 684f1197d75be4d9d73cb67201028185c9ad2c7e Mon Sep 17 00:00:00 2001 From: Bram Leeda Date: Sun, 11 Oct 2026 17:57:09 +0200 Subject: [PATCH 1/3] fix(metadata): incorrect property schema format for ip version=all --- .../Property/Restriction/PropertySchemaFormatTest.php | 1 + .../Restriction/PropertySchemaOneOfRestrictionTest.php | 6 ++++++ .../Metadata/Property/Restriction/PropertySchemaFormat.php | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaFormatTest.php b/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaFormatTest.php index 7e3d513bce9..83d9a3b97b6 100644 --- a/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaFormatTest.php +++ b/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaFormatTest.php @@ -86,6 +86,7 @@ public static function createProvider(): \Generator } yield 'ipv4' => [new Ip(version: '4'), new ApiProperty(), ['format' => 'ipv4']]; yield 'ipv6' => [new Ip(version: '6'), new ApiProperty(), ['format' => 'ipv6']]; + yield 'ipv4 or ipv6' => [new Ip(version: 'all'), new ApiProperty(), ['oneOf' => [['format' => 'ipv4'], ['format' => 'ipv6']]]]; yield 'not supported' => [new Positive(), new ApiProperty(), []]; } } diff --git a/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaOneOfRestrictionTest.php b/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaOneOfRestrictionTest.php index 41cda4771c1..a59194e6f08 100644 --- a/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaOneOfRestrictionTest.php +++ b/src/Symfony/Tests/Validator/Metadata/Property/Restriction/PropertySchemaOneOfRestrictionTest.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Symfony\Tests\Validator\Metadata\Property\Restriction; use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaFormat; use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLengthRestriction; use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaOneOfRestriction; use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRegexRestriction; @@ -24,6 +25,7 @@ use Symfony\Component\TypeInfo\Type; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\AtLeastOneOf; +use Symfony\Component\Validator\Constraints\Ip; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Positive; @@ -42,6 +44,7 @@ protected function setUp(): void $this->propertySchemaOneOfRestriction = new PropertySchemaOneOfRestriction([ new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction(), + new PropertySchemaFormat(), ]); } @@ -91,5 +94,8 @@ public static function createProviderWithNativeType(): \Generator yield 'native type: one supported constraint' => [new AtLeastOneOf([new Positive(), new Length(min: 3)]), (new ApiProperty())->withNativeType(Type::string()), [ 'oneOf' => [['minLength' => 3]], ]]; + yield 'native type: ip version all' => [new AtLeastOneOf([new Length(min: 3), new Ip(version: 'all')]), (new ApiProperty())->withNativeType(Type::string()), [ + 'oneOf' => [['minLength' => 3], ['oneOf' => [['format' => 'ipv4'], ['format' => 'ipv6']]]], + ]]; } } diff --git a/src/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaFormat.php b/src/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaFormat.php index 8080c0a853f..d3fb1b202c6 100644 --- a/src/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaFormat.php +++ b/src/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaFormat.php @@ -58,8 +58,11 @@ public function create(Constraint $constraint, ApiProperty $propertyMetadata): a if ($constraint->version === $constraint::V4) { return ['format' => 'ipv4']; } + if ($constraint->version === $constraint::V6) { + return ['format' => 'ipv6']; + } - return ['format' => 'ipv6']; + return ['oneOf' => [['format' => 'ipv4'], ['format' => 'ipv6']]]; } return []; From b45fa910ebea5299de288e8631496725929fb70f Mon Sep 17 00:00:00 2001 From: Bram Leeda Date: Mon, 12 Oct 2026 21:46:45 +0200 Subject: [PATCH 2/3] added functional test covering all property schema format options --- .../PropertySchemaFormatResource.php | 56 +++++++++++++++++++ .../PropertySchemaFormatFunctionalTest.php | 54 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tests/Fixtures/TestBundle/ApiResource/PropertySchemaFormatResource.php create mode 100644 tests/Functional/PropertySchemaFormatFunctionalTest.php diff --git a/tests/Fixtures/TestBundle/ApiResource/PropertySchemaFormatResource.php b/tests/Fixtures/TestBundle/ApiResource/PropertySchemaFormatResource.php new file mode 100644 index 00000000000..f0ea773d770 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/PropertySchemaFormatResource.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use Symfony\Component\Validator\Constraints as Assert; + +#[ApiResource( + operations: [ + new Get(uriTemplate: '/property_schema_format_resources/{id}'), + new GetCollection(uriTemplate: '/property_schema_format_resources'), + ] +)] +final class PropertySchemaFormatResource +{ + #[ApiProperty(identifier: true)] + public ?int $id = null; + + #[Assert\Email] + public ?string $email = null; + + #[Assert\Url] + public ?string $url = null; + + #[Assert\Hostname] + public ?string $hostname = null; + + #[Assert\Uuid] + public ?string $uuid = null; + + #[Assert\Ulid] + public ?string $ulid = null; + + #[Assert\Ip(version: Assert\Ip::V4)] + public ?string $ipv4 = null; + + #[Assert\Ip(version: Assert\Ip::V6)] + public ?string $ipv6 = null; + + #[Assert\Ip(version: Assert\Ip::ALL_NO_RES)] + public ?string $ip = null; +} diff --git a/tests/Functional/PropertySchemaFormatFunctionalTest.php b/tests/Functional/PropertySchemaFormatFunctionalTest.php new file mode 100644 index 00000000000..d4735e74950 --- /dev/null +++ b/tests/Functional/PropertySchemaFormatFunctionalTest.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Functional; + +use ApiPlatform\JsonSchema\SchemaFactoryInterface; +use ApiPlatform\Test\ApiTestCase; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\PropertySchemaFormatResource; +use ApiPlatform\Tests\SetupClassResourcesTrait; + +final class PropertySchemaFormatFunctionalTest extends ApiTestCase +{ + use SetupClassResourcesTrait; + + protected static ?bool $alwaysBootKernel = false; + + /** + * @return class-string[] + */ + public static function getResources(): array + { + return [PropertySchemaFormatResource::class]; + } + + public function testJsonSchemaFormats(): void + { + /** @var SchemaFactoryInterface $schemaFactory */ + $schemaFactory = self::getContainer()->get('api_platform.json_schema.schema_factory'); + $schema = $schemaFactory->buildSchema(PropertySchemaFormatResource::class); + + $properties = $schema['definitions']['PropertySchemaFormatResource']['properties'] ?? null; + $this->assertIsArray($properties); + + $this->assertSame('email', $properties['email']['format'] ?? null); + $this->assertSame('uri', $properties['url']['format'] ?? null); + $this->assertSame('hostname', $properties['hostname']['format'] ?? null); + $this->assertSame('uuid', $properties['uuid']['format'] ?? null); + $this->assertSame('ulid', $properties['ulid']['format'] ?? null); + $this->assertSame('ipv4', $properties['ipv4']['format'] ?? null); + $this->assertSame('ipv6', $properties['ipv6']['format'] ?? null); + + $this->assertSame([['format' => 'ipv4'], ['format' => 'ipv6'],], $properties['ip']['oneOf'] ?? null); + } +} From 95155f9484f19550c280efdd2a50a4d19686d43a Mon Sep 17 00:00:00 2001 From: Bram Leeda Date: Mon, 12 Oct 2026 21:50:50 +0200 Subject: [PATCH 3/3] fix php-cs notice, removed trailing comma --- tests/Functional/PropertySchemaFormatFunctionalTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Functional/PropertySchemaFormatFunctionalTest.php b/tests/Functional/PropertySchemaFormatFunctionalTest.php index d4735e74950..fc806132afd 100644 --- a/tests/Functional/PropertySchemaFormatFunctionalTest.php +++ b/tests/Functional/PropertySchemaFormatFunctionalTest.php @@ -49,6 +49,6 @@ public function testJsonSchemaFormats(): void $this->assertSame('ipv4', $properties['ipv4']['format'] ?? null); $this->assertSame('ipv6', $properties['ipv6']['format'] ?? null); - $this->assertSame([['format' => 'ipv4'], ['format' => 'ipv6'],], $properties['ip']['oneOf'] ?? null); + $this->assertSame([['format' => 'ipv4'], ['format' => 'ipv6']], $properties['ip']['oneOf'] ?? null); } }