From fddfbbcf1dd37d572be8291c2a93942dcdc27712 Mon Sep 17 00:00:00 2001 From: reznikartem Date: Mon, 27 Apr 2020 17:10:00 +0200 Subject: [PATCH 1/5] Fix problem with clients, that put charset in content type header. With this fix header "Content-Type: application/json; charset=utf-8" working same as "Content-Type: application/json" for parse input data --- .../main/resources/php-symfony/api_controller.mustache | 2 +- .../SymfonyBundle-php/Controller/PetController.php | 4 ++-- .../SymfonyBundle-php/Controller/StoreController.php | 2 +- .../SymfonyBundle-php/Controller/UserController.php | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache index 0a89e839938c..9183833e4b11 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache @@ -60,7 +60,7 @@ class {{controllerName}} extends Controller {{#bodyParams}} // Make sure that the client is providing something that we can consume $consumes = [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php index 8a1c9521a6b4..62bfcb447eb8 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php @@ -62,7 +62,7 @@ public function addPetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); @@ -491,7 +491,7 @@ public function updatePetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php index a7c17cae0b4b..59db05e78163 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php @@ -284,7 +284,7 @@ public function placeOrderAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php index b67fc2809823..25e08a1534ae 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php @@ -61,7 +61,7 @@ public function createUserAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); @@ -138,7 +138,7 @@ public function createUsersWithArrayInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); @@ -217,7 +217,7 @@ public function createUsersWithListInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); @@ -592,7 +592,7 @@ public function updateUserAction(Request $request, $username) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?$request->headers->get('Content-Type'):$consumes[0]; + $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; if (!in_array($inputFormat, $consumes)) { // We can't consume the content that the client is sending us return new Response('', 415); From 58c4d251f2b228d62cc0ab919ec68bbdfd54b09d Mon Sep 17 00:00:00 2001 From: reznikartem Date: Tue, 28 Apr 2020 10:59:03 +0200 Subject: [PATCH 2/5] Fix code style, add $consumes length check. --- .../php-symfony/api_controller.mustache | 14 +++-- .../Controller/PetController.php | 28 +++++++--- .../Controller/StoreController.php | 14 +++-- .../Controller/UserController.php | 56 +++++++++++++------ 4 files changed, 80 insertions(+), 32 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache index 9183833e4b11..7b7a9db2c3f9 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache @@ -60,10 +60,16 @@ class {{controllerName}} extends Controller {{#bodyParams}} // Make sure that the client is providing something that we can consume $consumes = [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } {{/bodyParams}} diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php index 62bfcb447eb8..677d03066f8c 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php @@ -62,10 +62,16 @@ public function addPetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication @@ -491,10 +497,16 @@ public function updatePetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php index 59db05e78163..43a4c130e5b3 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php @@ -284,10 +284,16 @@ public function placeOrderAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Figure out what data format to return to the client diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php index 25e08a1534ae..a73033c6979d 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php @@ -61,10 +61,16 @@ public function createUserAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication @@ -138,10 +144,16 @@ public function createUsersWithArrayInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication @@ -217,10 +229,16 @@ public function createUsersWithListInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication @@ -592,10 +610,16 @@ public function updateUserAction(Request $request, $username) { // Make sure that the client is providing something that we can consume $consumes = []; - $inputFormat = $request->headers->has('Content-Type')?explode(";",$request->headers->get('Content-Type'))[0]:$consumes[0]; - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); + if (sizeof($consumes) > 0) { + if ($request->headers->has('Content-Type')) { + $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; + } else { + $inputFormat = $consumes[0]; + } + if (!in_array($inputFormat, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); + } } // Handle authentication From fa582b3aae799307259d429fd939336bd97f1c3d Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Wed, 29 Apr 2020 18:21:40 +0300 Subject: [PATCH 3/5] Add isContentTypeAllowed static method and tests --- .../languages/PhpSymfonyServerCodegen.java | 6 + .../resources/php-symfony/Controller.mustache | 37 ++++++ .../php-symfony/api_controller.mustache | 14 +-- .../testing/ControllerTest.mustache | 113 ++++++++++++++++++ .../php-symfony/testing/phpunit.xml.mustache | 2 + 5 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/php-symfony/testing/ControllerTest.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSymfonyServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSymfonyServerCodegen.java index d0756dacc2a1..ef09fa0caa13 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSymfonyServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpSymfonyServerCodegen.java @@ -55,6 +55,7 @@ public class PhpSymfonyServerCodegen extends AbstractPhpCodegen implements Codeg protected String controllerDirName = "Controller"; protected String serviceDirName = "Service"; protected String controllerPackage; + protected String controllerTestsPackage; protected String servicePackage; protected Boolean phpLegacySupport = Boolean.TRUE; @@ -301,6 +302,7 @@ public void processOpts() { additionalProperties.put("servicePackage", servicePackage); additionalProperties.put("apiTestsPackage", apiTestsPackage); additionalProperties.put("modelTestsPackage", modelTestsPackage); + additionalProperties.put("controllerTestsPackage", controllerTestsPackage); // make Symonfy-specific properties available additionalProperties.put("bundleName", bundleName); @@ -311,11 +313,13 @@ public void processOpts() { // make api and model src path available in mustache template additionalProperties.put("apiSrcPath", "." + "/" + toSrcPath(apiPackage, srcBasePath)); additionalProperties.put("modelSrcPath", "." + "/" + toSrcPath(modelPackage, srcBasePath)); + additionalProperties.put("controllerSrcPath", "." + "/" + toSrcPath(controllerPackage, srcBasePath)); additionalProperties.put("testsSrcPath", "." + "/" + toSrcPath(testsPackage, srcBasePath)); additionalProperties.put("apiTestsSrcPath", "." + "/" + toSrcPath(apiTestsPackage, srcBasePath)); additionalProperties.put("modelTestsSrcPath", "." + "/" + toSrcPath(modelTestsPackage, srcBasePath)); additionalProperties.put("apiTestPath", "." + "/" + testsDirName + "/" + apiDirName); additionalProperties.put("modelTestPath", "." + "/" + testsDirName + "/" + modelDirName); + additionalProperties.put("controllerTestPath", "." + "/" + testsDirName + "/" + controllerDirName); // make api and model doc path available in mustache template additionalProperties.put("apiDocPath", apiDocPath); @@ -346,6 +350,7 @@ public void processOpts() { supportingFiles.add(new SupportingFile("testing/phpunit.xml.mustache", "", "phpunit.xml.dist")); supportingFiles.add(new SupportingFile("testing/pom.xml", "", "pom.xml")); supportingFiles.add(new SupportingFile("testing/AppKernel.php", toSrcPath(testsPackage, srcBasePath), "AppKernel.php")); + supportingFiles.add(new SupportingFile("testing/ControllerTest.mustache", toSrcPath(controllerTestsPackage, srcBasePath), "ControllerTest.php")); supportingFiles.add(new SupportingFile("testing/test_config.yml", toSrcPath(testsPackage, srcBasePath), "test_config.yml")); supportingFiles.add(new SupportingFile("routing.mustache", configDir, "routing.yml")); @@ -540,6 +545,7 @@ public void setInvokerPackage(String invokerPackage) { apiTestsPackage = testsPackage + "\\" + apiDirName; modelTestsPackage = testsPackage + "\\" + modelDirName; controllerPackage = invokerPackage + "\\" + controllerDirName; + controllerTestsPackage = testsPackage + "\\" + controllerDirName; servicePackage = invokerPackage + "\\" + serviceDirName; } diff --git a/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache b/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache index 5922196c1861..d7b5d5feec44 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/Controller.mustache @@ -20,6 +20,7 @@ namespace {{controllerPackage}}; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; use {{servicePackage}}\SerializerInterface; @@ -176,4 +177,40 @@ class Controller extends AbstractController // If we reach this point, we don't have a common ground between server and client return null; } + + /** + * Checks whether Content-Type request header presented in supported formats. + * + * @param Request $request Request instance. + * @param array $consumes Array of supported content types. + * + * @return bool Returns true if Content-Type supported otherwise false. + */ + public static function isContentTypeAllowed(Request $request, array $consumes = []) + { + if (!empty($consumes) && $consumes[0] !== '*/*') { + $currentFormat = $request->getContentType(); + foreach ($consumes as $mimeType) { + // canonize mime type + if (is_string($mimeType) && false !== $pos = strpos($mimeType, ';')) { + $mimeType = trim(substr($mimeType, 0, $pos)); + } + + if (!$format = $request->getFormat($mimeType)) { + // add custom format to request + $format = $mimeType; + $request->setFormat($format, $format); + $currentFormat = $request->getContentType(); + } + + if ($format === $currentFormat) { + return true; + } + } + + return false; + } + + return true; + } } diff --git a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache index 7b7a9db2c3f9..c0948e7c2a47 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/api_controller.mustache @@ -60,16 +60,9 @@ class {{controllerName}} extends Controller {{#bodyParams}} // Make sure that the client is providing something that we can consume $consumes = [{{#consumes}}'{{{mediaType}}}'{{#hasMore}}, {{/hasMore}}{{/consumes}}]; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } {{/bodyParams}} @@ -152,6 +145,7 @@ class {{controllerName}} extends Controller {{#allParams}} {{^isFile}} {{#isBodyParam}} + $inputFormat = $request->getMimeType($request->getContentType()); ${{paramName}} = $this->deserialize(${{paramName}}, '{{#isContainer}}{{#items}}array<{{dataType}}>{{/items}}{{/isContainer}}{{^isContainer}}{{dataType}}{{/isContainer}}', $inputFormat); {{/isBodyParam}} {{^isBodyParam}} diff --git a/modules/openapi-generator/src/main/resources/php-symfony/testing/ControllerTest.mustache b/modules/openapi-generator/src/main/resources/php-symfony/testing/ControllerTest.mustache new file mode 100644 index 000000000000..382b66c9154a --- /dev/null +++ b/modules/openapi-generator/src/main/resources/php-symfony/testing/ControllerTest.mustache @@ -0,0 +1,113 @@ +partial_header}} +/** + * NOTE: This class is auto generated by the openapi generator program. + * https://github.com/openapitools/openapi-generator + * Please update the test case below to test the endpoint. + */ + +namespace {{controllerTestsPackage}}; + +use {{controllerPackage}}\Controller; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; + +/** + * ControllerTest Class Doc Comment + * + * @category Class + * @package {{controllerTestsPackage}} + * @author openapi-generator contributors + * @link https://github.com/openapitools/openapi-generator + * @coversDefaultClass \{{controllerPackage}}\Controller + */ +class ControllerTest extends TestCase +{ + + /** + * Tests isContentTypeAllowed static method. + * + * @param string $contentType + * @param array $consumes + * @param bool $expectedReturn + * + * @covers ::isContentTypeAllowed + * @dataProvider provideArgumentsForIsContentTypeAllowed + */ + public function testIsContentTypeAllowed($contentType, array $consumes, $expectedReturn) + { + $request = new Request(); + $request->headers->set('CONTENT_TYPE', $contentType, true);// last one argument overrides header + $this->assertSame( + $expectedReturn, + Controller::isContentTypeAllowed($request, $consumes), + sprintf( + 'Failed assertion that "Content-Type: %s" %s by [%s] consumes array.', + $contentType, + ($expectedReturn) ? 'is allowed' : 'is forbidden', + implode(', ', $consumes) + ) + ); + } + + public function provideArgumentsForIsContentTypeAllowed() + { + return [ + 'usual JSON content type' => [ + 'application/json', + ['application/json'], + true, + ], + 'extended content type from PR #6078' => [ + 'application/json; charset=utf-8', + ['application/json'], + true, + ], + 'more than one content types' => [ + 'application/json', + ['application/xml', 'application/json; charset=utf-8'], + true, + ], + 'empty consumes array' => [ + 'application/json', + [], + true, + ], + 'empty consumes and content type' => [ + null, + [], + true, + ], + 'consumes everything' => [ + 'application/json', + ['*/*'], + true, + ], + 'fancy custom content type' => [ + 'foobar/foobaz', + ['application/xml', 'foobar/foobaz; charset=utf-8'], + true, + ], + 'empty content type' => [ + null, + ['application/xml', 'application/json; charset=utf-8'], + false, + ], + 'content type out of consumes' => [ + 'text/html', + ['application/xml', 'application/json; charset=utf-8'], + false, + ], + ]; + } +} diff --git a/modules/openapi-generator/src/main/resources/php-symfony/testing/phpunit.xml.mustache b/modules/openapi-generator/src/main/resources/php-symfony/testing/phpunit.xml.mustache index 8a6ff770c44e..35e39a7db34d 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/testing/phpunit.xml.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/testing/phpunit.xml.mustache @@ -9,12 +9,14 @@ {{apiTestPath}} {{modelTestPath}} + {{controllerTestPath}} {{apiSrcPath}} {{modelSrcPath}} + {{controllerSrcPath}} From 11a192c075057a3d895e9d570766862bc796df9c Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Wed, 29 Apr 2020 18:23:43 +0300 Subject: [PATCH 4/5] Fix old tests Right now serializer doesn't support anything beside json and xml. Call tests with application/json instead of form data. --- .../src/main/resources/php-symfony/testing/api_test.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/php-symfony/testing/api_test.mustache b/modules/openapi-generator/src/main/resources/php-symfony/testing/api_test.mustache index a279d6e61ea8..d7f3c1f76034 100644 --- a/modules/openapi-generator/src/main/resources/php-symfony/testing/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/php-symfony/testing/api_test.mustache @@ -99,7 +99,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; $path = str_replace($pattern, $data, $path); {{/pathParams}} - $crawler = $client->request('{{httpMethod}}', $path); + $crawler = $client->request('{{httpMethod}}', $path{{#hasBodyParam}}, [], [], ['CONTENT_TYPE' => 'application/json']{{/hasBodyParam}}); } {{/operation}} From f17162101ff5626e2ea373247f1f3e27c7535186 Mon Sep 17 00:00:00 2001 From: Yuriy Belenko Date: Wed, 29 Apr 2020 18:24:43 +0300 Subject: [PATCH 5/5] Refresh samples --- .../Controller/Controller.php | 37 ++++++ .../Controller/PetController.php | 28 ++-- .../Controller/StoreController.php | 14 +- .../Controller/UserController.php | 56 +++----- .../Tests/Api/PetApiInterfaceTest.php | 4 +- .../Tests/Api/StoreApiInterfaceTest.php | 2 +- .../Tests/Api/UserApiInterfaceTest.php | 8 +- .../Tests/Controller/ControllerTest.php | 123 ++++++++++++++++++ .../SymfonyBundle-php/phpunit.xml.dist | 2 + 9 files changed, 197 insertions(+), 77 deletions(-) create mode 100644 samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Controller/ControllerTest.php diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php index 2412b9bd264e..8f6030e3bd09 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/Controller.php @@ -30,6 +30,7 @@ namespace OpenAPI\Server\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; use OpenAPI\Server\Service\SerializerInterface; @@ -186,4 +187,40 @@ protected function getOutputFormat($accept, array $produced) // If we reach this point, we don't have a common ground between server and client return null; } + + /** + * Checks whether Content-Type request header presented in supported formats. + * + * @param Request $request Request instance. + * @param array $consumes Array of supported content types. + * + * @return bool Returns true if Content-Type supported otherwise false. + */ + public static function isContentTypeAllowed(Request $request, array $consumes = []) + { + if (!empty($consumes) && $consumes[0] !== '*/*') { + $currentFormat = $request->getContentType(); + foreach ($consumes as $mimeType) { + // canonize mime type + if (is_string($mimeType) && false !== $pos = strpos($mimeType, ';')) { + $mimeType = trim(substr($mimeType, 0, $pos)); + } + + if (!$format = $request->getFormat($mimeType)) { + // add custom format to request + $format = $mimeType; + $request->setFormat($format, $format); + $currentFormat = $request->getContentType(); + } + + if ($format === $currentFormat) { + return true; + } + } + + return false; + } + + return true; + } } diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php index 677d03066f8c..5cd8e8c4899f 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/PetController.php @@ -62,16 +62,9 @@ public function addPetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -86,6 +79,7 @@ public function addPetAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'OpenAPI\Server\Model\Pet', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); @@ -497,16 +491,9 @@ public function updatePetAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = ['application/json', 'application/xml']; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -521,6 +508,7 @@ public function updatePetAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'OpenAPI\Server\Model\Pet', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php index 43a4c130e5b3..433213776178 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/StoreController.php @@ -284,16 +284,9 @@ public function placeOrderAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Figure out what data format to return to the client @@ -314,6 +307,7 @@ public function placeOrderAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'OpenAPI\Server\Model\Order', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php index a73033c6979d..fcb17c698915 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Controller/UserController.php @@ -61,16 +61,9 @@ public function createUserAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -82,6 +75,7 @@ public function createUserAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'OpenAPI\Server\Model\User', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); @@ -144,16 +138,9 @@ public function createUsersWithArrayInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -165,6 +152,7 @@ public function createUsersWithArrayInputAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'array', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); @@ -229,16 +217,9 @@ public function createUsersWithListInputAction(Request $request) { // Make sure that the client is providing something that we can consume $consumes = []; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -250,6 +231,7 @@ public function createUsersWithListInputAction(Request $request) // Deserialize the input values that needs it try { + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'array', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); @@ -610,16 +592,9 @@ public function updateUserAction(Request $request, $username) { // Make sure that the client is providing something that we can consume $consumes = []; - if (sizeof($consumes) > 0) { - if ($request->headers->has('Content-Type')) { - $inputFormat = explode(";", $request->headers->get('Content-Type'))[0]; - } else { - $inputFormat = $consumes[0]; - } - if (!in_array($inputFormat, $consumes)) { - // We can't consume the content that the client is sending us - return new Response('', 415); - } + if (!static::isContentTypeAllowed($request, $consumes)) { + // We can't consume the content that the client is sending us + return new Response('', 415); } // Handle authentication @@ -632,6 +607,7 @@ public function updateUserAction(Request $request, $username) // Deserialize the input values that needs it try { $username = $this->deserialize($username, 'string', 'string'); + $inputFormat = $request->getMimeType($request->getContentType()); $body = $this->deserialize($body, 'OpenAPI\Server\Model\User', $inputFormat); } catch (SerializerRuntimeException $exception) { return $this->createBadRequestResponse($exception->getMessage()); diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/PetApiInterfaceTest.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/PetApiInterfaceTest.php index 91b849f2b273..de5c77400fe2 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/PetApiInterfaceTest.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/PetApiInterfaceTest.php @@ -85,7 +85,7 @@ public function testAddPet() $path = '/pet'; - $crawler = $client->request('POST', $path); + $crawler = $client->request('POST', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } /** @@ -166,7 +166,7 @@ public function testUpdatePet() $path = '/pet'; - $crawler = $client->request('PUT', $path); + $crawler = $client->request('PUT', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } /** diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/StoreApiInterfaceTest.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/StoreApiInterfaceTest.php index 919d30cf9a1e..9d10d568921b 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/StoreApiInterfaceTest.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/StoreApiInterfaceTest.php @@ -136,7 +136,7 @@ public function testPlaceOrder() $path = '/store/order'; - $crawler = $client->request('POST', $path); + $crawler = $client->request('POST', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } protected function genTestData($regexp) diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/UserApiInterfaceTest.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/UserApiInterfaceTest.php index 6f2e3ba0b6ce..775ec6704221 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/UserApiInterfaceTest.php +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Api/UserApiInterfaceTest.php @@ -85,7 +85,7 @@ public function testCreateUser() $path = '/user'; - $crawler = $client->request('POST', $path); + $crawler = $client->request('POST', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } /** @@ -100,7 +100,7 @@ public function testCreateUsersWithArrayInput() $path = '/user/createWithArray'; - $crawler = $client->request('POST', $path); + $crawler = $client->request('POST', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } /** @@ -115,7 +115,7 @@ public function testCreateUsersWithListInput() $path = '/user/createWithList'; - $crawler = $client->request('POST', $path); + $crawler = $client->request('POST', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } /** @@ -199,7 +199,7 @@ public function testUpdateUser() $data = $this->genTestData('[a-z0-9]+'); $path = str_replace($pattern, $data, $path); - $crawler = $client->request('PUT', $path); + $crawler = $client->request('PUT', $path, [], [], ['CONTENT_TYPE' => 'application/json']); } protected function genTestData($regexp) diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Controller/ControllerTest.php b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Controller/ControllerTest.php new file mode 100644 index 000000000000..e23c4afc7ca8 --- /dev/null +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/Tests/Controller/ControllerTest.php @@ -0,0 +1,123 @@ +headers->set('CONTENT_TYPE', $contentType, true);// last one argument overrides header + $this->assertSame( + $expectedReturn, + Controller::isContentTypeAllowed($request, $consumes), + sprintf( + 'Failed assertion that "Content-Type: %s" %s by [%s] consumes array.', + $contentType, + ($expectedReturn) ? 'is allowed' : 'is forbidden', + implode(', ', $consumes) + ) + ); + } + + public function provideArgumentsForIsContentTypeAllowed() + { + return [ + 'usual JSON content type' => [ + 'application/json', + ['application/json'], + true, + ], + 'extended content type from PR #6078' => [ + 'application/json; charset=utf-8', + ['application/json'], + true, + ], + 'more than one content types' => [ + 'application/json', + ['application/xml', 'application/json; charset=utf-8'], + true, + ], + 'empty consumes array' => [ + 'application/json', + [], + true, + ], + 'empty consumes and content type' => [ + null, + [], + true, + ], + 'consumes everything' => [ + 'application/json', + ['*/*'], + true, + ], + 'fancy custom content type' => [ + 'foobar/foobaz', + ['application/xml', 'foobar/foobaz; charset=utf-8'], + true, + ], + 'empty content type' => [ + null, + ['application/xml', 'application/json; charset=utf-8'], + false, + ], + 'content type out of consumes' => [ + 'text/html', + ['application/xml', 'application/json; charset=utf-8'], + false, + ], + ]; + } +} diff --git a/samples/server/petstore/php-symfony/SymfonyBundle-php/phpunit.xml.dist b/samples/server/petstore/php-symfony/SymfonyBundle-php/phpunit.xml.dist index 6f8b5ca0f932..38a8370fec6a 100644 --- a/samples/server/petstore/php-symfony/SymfonyBundle-php/phpunit.xml.dist +++ b/samples/server/petstore/php-symfony/SymfonyBundle-php/phpunit.xml.dist @@ -9,12 +9,14 @@ ./Tests/Api ./Tests/Model + ./Tests/Controller ././Api ././Model + ././Controller