From 1f0a456102e4304502165805e675536016c4ba74 Mon Sep 17 00:00:00 2001 From: Luis Bizarro Date: Sun, 24 Sep 2023 09:04:35 +0100 Subject: [PATCH 1/5] add: Image and file validator support, request @mediaType tag support (#222) * add: Support for image and file format (binary). * add: Detection of @mediaType tag for requests. --- .../RequestBodyExtension.php | 10 +++++++-- .../RulesExtractor/RulesMapper.php | 18 +++++++++++++++ tests/ValidationRulesDocumentingTest.php | 22 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Support/OperationExtensions/RequestBodyExtension.php b/src/Support/OperationExtensions/RequestBodyExtension.php index 6bfcbe44..4c4934c1 100644 --- a/src/Support/OperationExtensions/RequestBodyExtension.php +++ b/src/Support/OperationExtensions/RequestBodyExtension.php @@ -12,6 +12,7 @@ use Dedoc\Scramble\Support\OperationExtensions\RulesExtractor\ValidateCallExtractor; use Dedoc\Scramble\Support\RouteInfo; use Illuminate\Routing\Route; +use Illuminate\Support\Arr; use Illuminate\Support\Str; use PhpParser\Node\Stmt\ClassMethod; use Throwable; @@ -24,11 +25,16 @@ public function handle(Operation $operation, RouteInfo $routeInfo) $description = Str::of($routeInfo->phpDoc()->getAttribute('description')); + $mediaType = 'application/json'; + if ($mediaTags = $routeInfo->phpDoc()->getTagsByName('@mediaType')) { + $mediaType = trim(Arr::first($mediaTags)?->value?->value); + } + try { if (count($bodyParams = $this->extractParamsFromRequestValidationRules($routeInfo->route, $routeInfo->methodNode()))) { if ($method !== 'get') { $operation->addRequestBodyObject( - RequestBodyObject::make()->setContent('application/json', Schema::createFromParameters($bodyParams)) + RequestBodyObject::make()->setContent($mediaType, Schema::createFromParameters($bodyParams)) ); } else { $operation->addParameters($bodyParams); @@ -38,7 +44,7 @@ public function handle(Operation $operation, RouteInfo $routeInfo) ->addRequestBodyObject( RequestBodyObject::make() ->setContent( - 'application/json', + $mediaType, Schema::fromType(new ObjectType) ) ); diff --git a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php index d749e324..9364e409 100644 --- a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php +++ b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php @@ -92,6 +92,24 @@ public function email(Type $type) return $type->format('email'); } + public function image(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('binary'); + } + + public function file(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('binary'); + } + public function uuid(Type $type) { if ($type instanceof UnknownType) { diff --git a/tests/ValidationRulesDocumentingTest.php b/tests/ValidationRulesDocumentingTest.php index fc28656a..c4faf24a 100644 --- a/tests/ValidationRulesDocumentingTest.php +++ b/tests/ValidationRulesDocumentingTest.php @@ -134,6 +134,28 @@ ->and($type->format)->toBe('email'); }); +it('supports image rule', function () { + $rules = [ + 'image' => 'required|image', + ]; + + $type = app()->make(RulesToParameters::class, ['rules' => $rules])->handle()[0]->schema->type; + + expect($type)->toBeInstanceOf(StringType::class) + ->and($type->format)->toBe('binary'); +}); + +it('supports file rule', function () { + $rules = [ + 'file' => 'required|file', + ]; + + $type = app()->make(RulesToParameters::class, ['rules' => $rules])->handle()[0]->schema->type; + + expect($type)->toBeInstanceOf(StringType::class) + ->and($type->format)->toBe('binary'); +}); + it('extracts rules from request->validate call', function () { RouteFacade::get('api/test', [ValidationRulesDocumenting_Test::class, 'index']); From c1973385ab419d40fe42a6158846f2e0e024379a Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 24 Sep 2023 11:09:25 +0300 Subject: [PATCH 2/5] added support for 'multipart/form-data' in the requestBody (#224) Co-authored-by: romalytvynenko --- .../RequestBodyExtension.php | 16 +++++- .../RulesExtractor/RulesMapper.php | 14 ++++++ .../RulesExtractor/RulesToParameter.php | 2 +- tests/ValidationRulesDocumentingTest.php | 49 +++++++++++++++++++ ...rule_from_Validatormake_facade_call__1.yml | 8 +++ ...tracts_image_rule_from_form_request__1.yml | 10 ++++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml create mode 100644 tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml diff --git a/src/Support/OperationExtensions/RequestBodyExtension.php b/src/Support/OperationExtensions/RequestBodyExtension.php index 4c4934c1..44076268 100644 --- a/src/Support/OperationExtensions/RequestBodyExtension.php +++ b/src/Support/OperationExtensions/RequestBodyExtension.php @@ -4,6 +4,7 @@ use Dedoc\Scramble\Extensions\OperationExtension; use Dedoc\Scramble\Support\Generator\Operation; +use Dedoc\Scramble\Support\Generator\Parameter; use Dedoc\Scramble\Support\Generator\RequestBodyObject; use Dedoc\Scramble\Support\Generator\Schema; use Dedoc\Scramble\Support\Generator\Types\ObjectType; @@ -33,8 +34,10 @@ public function handle(Operation $operation, RouteInfo $routeInfo) try { if (count($bodyParams = $this->extractParamsFromRequestValidationRules($routeInfo->route, $routeInfo->methodNode()))) { if ($method !== 'get') { + $contentType = $this->hasBinary($bodyParams) ? 'multipart/form-data' : 'application/json'; + $operation->addRequestBodyObject( - RequestBodyObject::make()->setContent($mediaType, Schema::createFromParameters($bodyParams)) + RequestBodyObject::make()->setContent($contentType, Schema::createFromParameters($bodyParams)) ); } else { $operation->addParameters($bodyParams); @@ -61,6 +64,17 @@ public function handle(Operation $operation, RouteInfo $routeInfo) ->description($description); } + private function hasBinary($bodyParams): bool + { + return collect($bodyParams)->contains(function (Parameter $parameter) { + if (property_exists($parameter?->schema?->type, 'format')) { + return $parameter->schema->type->format === 'binary'; + } + + return false; + }); + } + private function extractParamsFromRequestValidationRules(Route $route, ?ClassMethod $methodNode) { [$rules, $nodesResults] = $this->extractRouteRequestValidationRules($route, $methodNode); diff --git a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php index 9364e409..2fb75b05 100644 --- a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php +++ b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php @@ -175,4 +175,18 @@ public function enum(Type $_, Enum $rule) new ObjectType($enumName) ); } + + public function image(Type $type) + { + return $this->file($type); + } + + public function file(Type $type) + { + if ($type instanceof UnknownType) { + $type = $this->string($type); + } + + return $type->format('binary'); + } } diff --git a/src/Support/OperationExtensions/RulesExtractor/RulesToParameter.php b/src/Support/OperationExtensions/RulesExtractor/RulesToParameter.php index 238b24fa..156e244b 100644 --- a/src/Support/OperationExtensions/RulesExtractor/RulesToParameter.php +++ b/src/Support/OperationExtensions/RulesExtractor/RulesToParameter.php @@ -23,7 +23,7 @@ class RulesToParameter private ?PhpDocNode $docNode; const RULES_PRIORITY = [ - 'bool', 'boolean', 'numeric', 'int', 'integer', 'string', 'array', 'exists', + 'bool', 'boolean', 'numeric', 'int', 'integer', 'file', 'image', 'string', 'array', 'exists', ]; public function __construct(string $name, $rules, ?PhpDocNode $docNode, TypeTransformer $openApiTransformer) diff --git a/tests/ValidationRulesDocumentingTest.php b/tests/ValidationRulesDocumentingTest.php index c4faf24a..a8df9f15 100644 --- a/tests/ValidationRulesDocumentingTest.php +++ b/tests/ValidationRulesDocumentingTest.php @@ -343,3 +343,52 @@ public function index() { } } + +it('extracts file rule from Validator::make facade call', function () { + RouteFacade::post('api/test', [ValidationFacadeRulesFile_Test::class, 'index']); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/test'); + $openApiDocument = app()->make(\Dedoc\Scramble\Generator::class)(); + + assertMatchesSnapshot($openApiDocument); +}); + +it('extracts image rule from form request', function () { + RouteFacade::post('api/test', [ValidationRulesImageFromRequest_Test::class, 'index']); + + Scramble::routes(fn (Route $r) => $r->uri === 'api/test'); + $openApiDocument = app()->make(\Dedoc\Scramble\Generator::class)(); + + expect($openApiDocument['paths']['/test']['post']['requestBody']['content']) + ->toHaveKey('multipart/form-data'); + + assertMatchesSnapshot($openApiDocument); +}); + +class ValidationRulesImage_TestFormRequest extends \Illuminate\Foundation\Http\FormRequest +{ + public function rules() + { + return [ + 'file' => 'image', + ]; + } +} + +class ValidationRulesImageFromRequest_Test +{ + public function index(ValidationRulesImage_TestFormRequest $request) + { + // + } +} + +class ValidationFacadeRulesFile_Test +{ + public function index(Request $request) + { + Validator::make($request->all(), [ + 'file' => ['file'], + ]); + } +} diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml new file mode 100644 index 00000000..4106589c --- /dev/null +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml @@ -0,0 +1,8 @@ +openapi: 3.1.0 +info: + title: Laravel + version: 0.0.1 +servers: + - { url: 'http://localhost/api' } +paths: + /test: { post: { operationId: validationFacadeRulesFileTest.index, tags: [ValidationFacadeRulesFile_Test], requestBody: { content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } } }, responses: { 200: { description: '', content: { application/json: { schema: { type: string } } } } } } } diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml new file mode 100644 index 00000000..660a17b6 --- /dev/null +++ b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml @@ -0,0 +1,10 @@ +openapi: 3.1.0 +info: + title: Laravel + version: 0.0.1 +servers: + - { url: 'http://localhost/api' } +paths: + /test: { post: { operationId: validationRulesImageFromRequestTest.index, tags: [ValidationRulesImageFromRequest_Test], requestBody: { content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } } }, responses: { 200: { description: '', content: { application/json: { schema: { type: string } } } }, 422: { $ref: '#/components/responses/ValidationException' }, 403: { $ref: '#/components/responses/AuthorizationException' } } } } +components: + responses: { ValidationException: { description: 'Validation error', content: { application/json: { schema: { type: object, properties: { message: { type: string, description: 'Errors overview.' }, errors: { type: object, description: 'A detailed description of each field that failed validation.', additionalProperties: { type: array, items: { type: string } } } }, required: [message, errors] } } } }, AuthorizationException: { description: 'Authorization error', content: { application/json: { schema: { type: object, properties: { message: { type: string, description: 'Error overview.' } }, required: [message] } } } } } From 39b1e142cb511fd00201aaef5c693f4a3b91d84e Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko Date: Sun, 24 Sep 2023 11:18:34 +0300 Subject: [PATCH 3/5] fix: merge conflicts --- .../RequestBodyExtension.php | 43 ++++++++++------ .../RulesExtractor/RulesMapper.php | 18 ------- tests/ValidationRulesDocumentingTest.php | 49 ------------------- ...rule_from_Validatormake_facade_call__1.yml | 8 --- ...tracts_image_rule_from_form_request__1.yml | 10 ---- 5 files changed, 28 insertions(+), 100 deletions(-) delete mode 100644 tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml delete mode 100644 tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml diff --git a/src/Support/OperationExtensions/RequestBodyExtension.php b/src/Support/OperationExtensions/RequestBodyExtension.php index 44076268..64ddeda1 100644 --- a/src/Support/OperationExtensions/RequestBodyExtension.php +++ b/src/Support/OperationExtensions/RequestBodyExtension.php @@ -22,27 +22,22 @@ class RequestBodyExtension extends OperationExtension { public function handle(Operation $operation, RouteInfo $routeInfo) { - $method = $operation->method; - $description = Str::of($routeInfo->phpDoc()->getAttribute('description')); - $mediaType = 'application/json'; - if ($mediaTags = $routeInfo->phpDoc()->getTagsByName('@mediaType')) { - $mediaType = trim(Arr::first($mediaTags)?->value?->value); - } - try { - if (count($bodyParams = $this->extractParamsFromRequestValidationRules($routeInfo->route, $routeInfo->methodNode()))) { - if ($method !== 'get') { - $contentType = $this->hasBinary($bodyParams) ? 'multipart/form-data' : 'application/json'; + $bodyParams = $this->extractParamsFromRequestValidationRules($routeInfo->route, $routeInfo->methodNode()); + $mediaType = $this->getMediaType($operation, $routeInfo, $bodyParams); + + if (count($bodyParams)) { + if ($operation->method !== 'get') { $operation->addRequestBodyObject( - RequestBodyObject::make()->setContent($contentType, Schema::createFromParameters($bodyParams)) + RequestBodyObject::make()->setContent($mediaType, Schema::createFromParameters($bodyParams)) ); } else { $operation->addParameters($bodyParams); } - } elseif ($method !== 'get') { + } elseif ($operation->method !== 'get') { $operation ->addRequestBodyObject( RequestBodyObject::make() @@ -64,7 +59,25 @@ public function handle(Operation $operation, RouteInfo $routeInfo) ->description($description); } - private function hasBinary($bodyParams): bool + protected function getMediaType(Operation $operation, RouteInfo $routeInfo, array $bodyParams): string + { + $jsonMediaType = 'application/json'; + + if ( + ($mediaTags = $routeInfo->phpDoc()->getTagsByName('@requestMediaType')) + && ($mediaType = trim(Arr::first($mediaTags)?->value?->value)) + ) { + return $mediaType; + } + + if ($operation->method === 'get') { + return $jsonMediaType; + } + + return $this->hasBinary($bodyParams) ? 'multipart/form-data' : $jsonMediaType; + } + + protected function hasBinary($bodyParams): bool { return collect($bodyParams)->contains(function (Parameter $parameter) { if (property_exists($parameter?->schema?->type, 'format')) { @@ -75,14 +88,14 @@ private function hasBinary($bodyParams): bool }); } - private function extractParamsFromRequestValidationRules(Route $route, ?ClassMethod $methodNode) + protected function extractParamsFromRequestValidationRules(Route $route, ?ClassMethod $methodNode) { [$rules, $nodesResults] = $this->extractRouteRequestValidationRules($route, $methodNode); return (new RulesToParameters($rules, $nodesResults, $this->openApiTransformer))->handle(); } - private function extractRouteRequestValidationRules(Route $route, $methodNode) + protected function extractRouteRequestValidationRules(Route $route, $methodNode) { $rules = []; $nodesResults = []; diff --git a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php index 2fb75b05..f318ff6a 100644 --- a/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php +++ b/src/Support/OperationExtensions/RulesExtractor/RulesMapper.php @@ -92,24 +92,6 @@ public function email(Type $type) return $type->format('email'); } - public function image(Type $type) - { - if ($type instanceof UnknownType) { - $type = $this->string($type); - } - - return $type->format('binary'); - } - - public function file(Type $type) - { - if ($type instanceof UnknownType) { - $type = $this->string($type); - } - - return $type->format('binary'); - } - public function uuid(Type $type) { if ($type instanceof UnknownType) { diff --git a/tests/ValidationRulesDocumentingTest.php b/tests/ValidationRulesDocumentingTest.php index a8df9f15..c4faf24a 100644 --- a/tests/ValidationRulesDocumentingTest.php +++ b/tests/ValidationRulesDocumentingTest.php @@ -343,52 +343,3 @@ public function index() { } } - -it('extracts file rule from Validator::make facade call', function () { - RouteFacade::post('api/test', [ValidationFacadeRulesFile_Test::class, 'index']); - - Scramble::routes(fn (Route $r) => $r->uri === 'api/test'); - $openApiDocument = app()->make(\Dedoc\Scramble\Generator::class)(); - - assertMatchesSnapshot($openApiDocument); -}); - -it('extracts image rule from form request', function () { - RouteFacade::post('api/test', [ValidationRulesImageFromRequest_Test::class, 'index']); - - Scramble::routes(fn (Route $r) => $r->uri === 'api/test'); - $openApiDocument = app()->make(\Dedoc\Scramble\Generator::class)(); - - expect($openApiDocument['paths']['/test']['post']['requestBody']['content']) - ->toHaveKey('multipart/form-data'); - - assertMatchesSnapshot($openApiDocument); -}); - -class ValidationRulesImage_TestFormRequest extends \Illuminate\Foundation\Http\FormRequest -{ - public function rules() - { - return [ - 'file' => 'image', - ]; - } -} - -class ValidationRulesImageFromRequest_Test -{ - public function index(ValidationRulesImage_TestFormRequest $request) - { - // - } -} - -class ValidationFacadeRulesFile_Test -{ - public function index(Request $request) - { - Validator::make($request->all(), [ - 'file' => ['file'], - ]); - } -} diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml deleted file mode 100644 index 4106589c..00000000 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_file_rule_from_Validatormake_facade_call__1.yml +++ /dev/null @@ -1,8 +0,0 @@ -openapi: 3.1.0 -info: - title: Laravel - version: 0.0.1 -servers: - - { url: 'http://localhost/api' } -paths: - /test: { post: { operationId: validationFacadeRulesFileTest.index, tags: [ValidationFacadeRulesFile_Test], requestBody: { content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } } }, responses: { 200: { description: '', content: { application/json: { schema: { type: string } } } } } } } diff --git a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml b/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml deleted file mode 100644 index 660a17b6..00000000 --- a/tests/__snapshots__/ValidationRulesDocumentingTest__it_extracts_image_rule_from_form_request__1.yml +++ /dev/null @@ -1,10 +0,0 @@ -openapi: 3.1.0 -info: - title: Laravel - version: 0.0.1 -servers: - - { url: 'http://localhost/api' } -paths: - /test: { post: { operationId: validationRulesImageFromRequestTest.index, tags: [ValidationRulesImageFromRequest_Test], requestBody: { content: { multipart/form-data: { schema: { type: object, properties: { file: { type: string, format: binary } } } } } }, responses: { 200: { description: '', content: { application/json: { schema: { type: string } } } }, 422: { $ref: '#/components/responses/ValidationException' }, 403: { $ref: '#/components/responses/AuthorizationException' } } } } -components: - responses: { ValidationException: { description: 'Validation error', content: { application/json: { schema: { type: object, properties: { message: { type: string, description: 'Errors overview.' }, errors: { type: object, description: 'A detailed description of each field that failed validation.', additionalProperties: { type: array, items: { type: string } } } }, required: [message, errors] } } } }, AuthorizationException: { description: 'Authorization error', content: { application/json: { schema: { type: object, properties: { message: { type: string, description: 'Error overview.' } }, required: [message] } } } } } From 7fbe40fa6c23b89b01dde78f8cb98c046bcfa666 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko Date: Sun, 24 Sep 2023 11:31:32 +0300 Subject: [PATCH 4/5] tests for request media type --- .../RequestBodyExtension.php | 4 +- .../RequestBodyExtensionTest.php | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/Support/OperationExtensions/RequestBodyExtensionTest.php diff --git a/src/Support/OperationExtensions/RequestBodyExtension.php b/src/Support/OperationExtensions/RequestBodyExtension.php index 64ddeda1..424c6803 100644 --- a/src/Support/OperationExtensions/RequestBodyExtension.php +++ b/src/Support/OperationExtensions/RequestBodyExtension.php @@ -61,8 +61,6 @@ public function handle(Operation $operation, RouteInfo $routeInfo) protected function getMediaType(Operation $operation, RouteInfo $routeInfo, array $bodyParams): string { - $jsonMediaType = 'application/json'; - if ( ($mediaTags = $routeInfo->phpDoc()->getTagsByName('@requestMediaType')) && ($mediaType = trim(Arr::first($mediaTags)?->value?->value)) @@ -70,6 +68,8 @@ protected function getMediaType(Operation $operation, RouteInfo $routeInfo, arra return $mediaType; } + $jsonMediaType = 'application/json'; + if ($operation->method === 'get') { return $jsonMediaType; } diff --git a/tests/Support/OperationExtensions/RequestBodyExtensionTest.php b/tests/Support/OperationExtensions/RequestBodyExtensionTest.php new file mode 100644 index 00000000..a32a580a --- /dev/null +++ b/tests/Support/OperationExtensions/RequestBodyExtensionTest.php @@ -0,0 +1,57 @@ +toHaveKey('application/json') + ->toHaveLength(1); +}); +class RequestBodyExtensionTest__uses_application_json_as_default +{ + public function index(\Illuminate\Http\Request $request) + { + $request->validate(['foo' => 'string']); + } +} + +it('allows manually defining a request media type', function () { + $openApiDocument = generateForRoute(function () { + return RouteFacade::post('api/test', [RequestBodyExtensionTest__allows_manual_request_media_type::class, 'index']); + }); + + expect($openApiDocument['paths']['/test']['post']['requestBody']['content']) + ->toHaveKey('application/xml') + ->toHaveLength(1); +}); +class RequestBodyExtensionTest__allows_manual_request_media_type +{ + /** + * @requestMediaType application/xml + */ + public function index(\Illuminate\Http\Request $request) + { + $request->validate(['foo' => 'string']); + } +} + +it('automatically infers multipart/form-data as request media type when some of body params is binary', function () { + $openApiDocument = generateForRoute(function () { + return RouteFacade::post('api/test', [RequestBodyExtensionTest__automaticall_infers_form_data::class, 'index']); + }); + + expect($openApiDocument['paths']['/test']['post']['requestBody']['content']) + ->toHaveKey('multipart/form-data') + ->toHaveLength(1); +}); +class RequestBodyExtensionTest__automaticall_infers_form_data +{ + public function index(\Illuminate\Http\Request $request) + { + $request->validate(['foo' => 'file']); + } +} From 0bfd8fc0860e96b56ebcaf6e5e13376b627ffc89 Mon Sep 17 00:00:00 2001 From: romalytvynenko Date: Sun, 24 Sep 2023 08:32:07 +0000 Subject: [PATCH 5/5] Fix styling --- .../OperationExtensions/RequestBodyExtensionTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Support/OperationExtensions/RequestBodyExtensionTest.php b/tests/Support/OperationExtensions/RequestBodyExtensionTest.php index a32a580a..6917d2e0 100644 --- a/tests/Support/OperationExtensions/RequestBodyExtensionTest.php +++ b/tests/Support/OperationExtensions/RequestBodyExtensionTest.php @@ -13,7 +13,7 @@ }); class RequestBodyExtensionTest__uses_application_json_as_default { - public function index(\Illuminate\Http\Request $request) + public function index(Illuminate\Http\Request $request) { $request->validate(['foo' => 'string']); } @@ -33,7 +33,7 @@ class RequestBodyExtensionTest__allows_manual_request_media_type /** * @requestMediaType application/xml */ - public function index(\Illuminate\Http\Request $request) + public function index(Illuminate\Http\Request $request) { $request->validate(['foo' => 'string']); } @@ -50,7 +50,7 @@ public function index(\Illuminate\Http\Request $request) }); class RequestBodyExtensionTest__automaticall_infers_form_data { - public function index(\Illuminate\Http\Request $request) + public function index(Illuminate\Http\Request $request) { $request->validate(['foo' => 'file']); }