From 130acc0fce4f01fa9e3c8b050db9743e0d4efc7c Mon Sep 17 00:00:00 2001 From: aaa2000 Date: Wed, 15 Oct 2025 21:40:10 +0200 Subject: [PATCH] fix(openapi): fix request body when multipart/form-data input format --- src/OpenApi/Factory/OpenApiFactory.php | 2 +- .../TestBundle/Entity/Issue7436/Issue7436.php | 41 +++++++++++++++++++ tests/Functional/OpenApiTest.php | 25 +++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/Fixtures/TestBundle/Entity/Issue7436/Issue7436.php diff --git a/src/OpenApi/Factory/OpenApiFactory.php b/src/OpenApi/Factory/OpenApiFactory.php index 2854faf2d9..27731c6c96 100644 --- a/src/OpenApi/Factory/OpenApiFactory.php +++ b/src/OpenApi/Factory/OpenApiFactory.php @@ -458,7 +458,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection $operationInputSchemas = []; foreach ($requestMimeTypes as $operationFormat) { $operationInputSchema = null; - if (str_starts_with($operationFormat, 'json')) { + if (str_starts_with($operationFormat, 'json') || 'multipart' === $operationFormat) { $operationInputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_INPUT, $operation, $schema, null, $forceSchemaCollection); $this->appendSchemaDefinitions($schemas, $operationInputSchema->getDefinitions()); } diff --git a/tests/Fixtures/TestBundle/Entity/Issue7436/Issue7436.php b/tests/Fixtures/TestBundle/Entity/Issue7436/Issue7436.php new file mode 100644 index 0000000000..bc8f48de8e --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue7436/Issue7436.php @@ -0,0 +1,41 @@ + + * + * 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\Entity\Issue7436; + +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Post; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\HttpFoundation\File\UploadedFile; + +#[ApiResource( + operations: [ + new Post( + inputFormats: ['multipart' => ['multipart/form-data']], + ), + ] +)] +#[ORM\Entity] +class Issue7436 +{ + #[ORM\Column(type: 'integer')] + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'AUTO')] + private $id; + public ?UploadedFile $file = null; + + public function getId() + { + return $this->id; + } +} diff --git a/tests/Functional/OpenApiTest.php b/tests/Functional/OpenApiTest.php index 6912d7154d..6f64f42224 100644 --- a/tests/Functional/OpenApiTest.php +++ b/tests/Functional/OpenApiTest.php @@ -33,6 +33,7 @@ use ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyFriend; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyTableInheritance; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceChild; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue7436\Issue7436; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\JsonSchemaResource; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\JsonSchemaResourceRelated; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\NoCollectionDummy; @@ -100,6 +101,7 @@ public static function getResources(): array JsonSchemaResource::class, JsonSchemaResourceRelated::class, WrappedResponseEntity::class, + Issue7436::class, ]; } @@ -610,4 +612,27 @@ public function testRetrieveTheEntrypointWithUrlFormat(): void $this->assertResponseHeaderSame('content-type', 'application/vnd.openapi+json; charset=utf-8'); $this->assertJson($response->getContent()); } + + public function testMultipartOperationHasCorrectOpenApiSchema(): void + { + $response = self::createClient()->request('GET', '/docs', [ + 'headers' => ['Accept' => 'application/vnd.openapi+json'], + ]); + $this->assertResponseIsSuccessful(); + $json = $response->toArray(); + + $this->assertSame( + [ + 'type' => ['string', 'null'], + 'format' => 'binary', + ], + $json['components']['schemas']['Issue7436']['properties']['file'] + ); + $this->assertSame( + [ + 'multipart/form-data' => ['schema' => ['$ref' => '#/components/schemas/Issue7436']], + ], + $json['paths']['/issue7436s']['post']['requestBody']['content'] + ); + } }