Skip to content

Commit 65296ea

Browse files
authored
feat(openapi): allow optional request body content (#6374)
1 parent e867d07 commit 65296ea

File tree

3 files changed

+58
-15
lines changed

3 files changed

+58
-15
lines changed

src/OpenApi/Factory/OpenApiFactory.php

+15-7
Original file line numberDiff line numberDiff line change
@@ -394,17 +394,25 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
394394
);
395395
$openapiOperation = $openapiOperation->withRequestBody(new RequestBody($contextRequestBody['description'] ?? '', new \ArrayObject($contextRequestBody['content']), $contextRequestBody['required'] ?? false));
396396
} elseif (
397-
null === $openapiOperation->getRequestBody() && \in_array($method, ['PATCH', 'PUT', 'POST'], true)
397+
\in_array($method, ['PATCH', 'PUT', 'POST'], true)
398398
&& !(false === ($input = $operation->getInput()) || (\is_array($input) && null === $input['class']))
399399
) {
400-
$operationInputSchemas = [];
401-
foreach ($requestMimeTypes as $operationFormat) {
402-
$operationInputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_INPUT, $operation, $schema, null, $forceSchemaCollection);
403-
$operationInputSchemas[$operationFormat] = $operationInputSchema;
404-
$this->appendSchemaDefinitions($schemas, $operationInputSchema->getDefinitions());
400+
$content = $openapiOperation->getRequestBody()?->getContent();
401+
if (null === $content) {
402+
$operationInputSchemas = [];
403+
foreach ($requestMimeTypes as $operationFormat) {
404+
$operationInputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_INPUT, $operation, $schema, null, $forceSchemaCollection);
405+
$operationInputSchemas[$operationFormat] = $operationInputSchema;
406+
$this->appendSchemaDefinitions($schemas, $operationInputSchema->getDefinitions());
407+
}
408+
$content = $this->buildContent($requestMimeTypes, $operationInputSchemas);
405409
}
406410

407-
$openapiOperation = $openapiOperation->withRequestBody(new RequestBody(sprintf('The %s %s resource', 'POST' === $method ? 'new' : 'updated', $resourceShortName), $this->buildContent($requestMimeTypes, $operationInputSchemas), true));
411+
$openapiOperation = $openapiOperation->withRequestBody(new RequestBody(
412+
description: $openapiOperation->getRequestBody()?->getDescription() ?? sprintf('The %s %s resource', 'POST' === $method ? 'new' : 'updated', $resourceShortName),
413+
content: $content,
414+
required: $openapiOperation->getRequestBody()?->getRequired() ?? true,
415+
));
408416
}
409417

410418
// TODO Remove in 4.0

src/OpenApi/Model/RequestBody.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ final class RequestBody
1717
{
1818
use ExtensionTrait;
1919

20-
public function __construct(private string $description = '', private ?\ArrayObject $content = null, private bool $required = false)
20+
public function __construct(private ?string $description = null, private ?\ArrayObject $content = null, private bool $required = false)
2121
{
2222
}
2323

24-
public function getDescription(): string
24+
public function getDescription(): ?string
2525
{
2626
return $this->description;
2727
}
2828

29-
public function getContent(): \ArrayObject
29+
public function getContent(): ?\ArrayObject
3030
{
3131
return $this->content;
3232
}

src/OpenApi/Tests/Factory/OpenApiFactoryTest.php

+40-5
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,11 @@ public function testInvoke(): void
175175
'filteredDummyCollection' => (new GetCollection())->withUriTemplate('/filtered')->withFilters(['f1', 'f2', 'f3', 'f4', 'f5'])->withOperation($baseOperation),
176176
// Paginated
177177
'paginatedDummyCollection' => (new GetCollection())->withUriTemplate('/paginated')
178-
->withPaginationClientEnabled(true)
179-
->withPaginationClientItemsPerPage(true)
180-
->withPaginationItemsPerPage(20)
181-
->withPaginationMaximumItemsPerPage(80)
182-
->withOperation($baseOperation),
178+
->withPaginationClientEnabled(true)
179+
->withPaginationClientItemsPerPage(true)
180+
->withPaginationItemsPerPage(20)
181+
->withPaginationMaximumItemsPerPage(80)
182+
->withOperation($baseOperation),
183183
'postDummyCollectionWithRequestBody' => (new Post())->withUriTemplate('/dummiesRequestBody')->withOperation($baseOperation)->withOpenapi(new OpenApiOperation(
184184
requestBody: new RequestBody(
185185
description: 'List of Ids',
@@ -202,6 +202,11 @@ public function testInvoke(): void
202202
]),
203203
),
204204
)),
205+
'postDummyCollectionWithRequestBodyWithoutContent' => (new Post())->withUriTemplate('/dummiesRequestBodyWithoutContent')->withOperation($baseOperation)->withOpenapi(new OpenApiOperation(
206+
requestBody: new RequestBody(
207+
description: 'Extended description for the new Dummy resource',
208+
),
209+
)),
205210
'putDummyItemWithResponse' => (new Put())->withUriTemplate('/dummyitems/{id}')->withOperation($baseOperation)->withOpenapi(new OpenApiOperation(
206211
responses: [
207212
'200' => new OpenApiResponse(
@@ -872,6 +877,36 @@ public function testInvoke(): void
872877
deprecated: false,
873878
), $requestBodyPath->getPost());
874879

880+
$requestBodyPath = $paths->getPath('/dummiesRequestBodyWithoutContent');
881+
$this->assertEquals(new Operation(
882+
'postDummyCollectionWithRequestBodyWithoutContent',
883+
['Dummy'],
884+
[
885+
'201' => new Response(
886+
'Dummy resource created',
887+
new \ArrayObject([
888+
'application/ld+json' => new MediaType(new \ArrayObject(new \ArrayObject(['$ref' => '#/components/schemas/Dummy.OutputDto']))),
889+
]),
890+
null,
891+
new \ArrayObject(['getDummyItem' => new Model\Link('getDummyItem', new \ArrayObject(['id' => '$response.body#/id']), null, 'This is a dummy')])
892+
),
893+
'400' => new Response('Invalid input'),
894+
'422' => new Response('Unprocessable entity'),
895+
],
896+
'Creates a Dummy resource.',
897+
'Creates a Dummy resource.',
898+
null,
899+
[],
900+
new RequestBody(
901+
'Extended description for the new Dummy resource',
902+
new \ArrayObject([
903+
'application/ld+json' => new MediaType(new \ArrayObject(new \ArrayObject(['$ref' => '#/components/schemas/Dummy']))),
904+
]),
905+
false
906+
),
907+
deprecated: false,
908+
), $requestBodyPath->getPost());
909+
875910
$dummyItemPath = $paths->getPath('/dummyitems/{id}');
876911
$this->assertEquals(new Operation(
877912
'putDummyItemWithResponse',

0 commit comments

Comments
 (0)