diff --git a/composer.json b/composer.json index 9d4f33f..013f927 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "jms/serializer": "^3.13", "symfony/validator": "^5.3", "symfony/cache": "^5.3", - "rinsvent/attribute-extractor": "^0.0.1" + "symfony/string": "^5.3", + "rinsvent/attribute-extractor": "^0.0.2" }, "require-dev": { "codeception/codeception": "^4.1", diff --git a/composer.lock b/composer.lock index 2c366ab..0da6926 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8bebb837cbbc196ec3ac49c24e16dc37", + "content-hash": "6f29c92495f5e5045ad725f6a287b947", "packages": [ { "name": "doctrine/annotations", @@ -582,16 +582,16 @@ }, { "name": "rinsvent/attribute-extractor", - "version": "v0.0.1", + "version": "v0.0.2", "source": { "type": "git", "url": "https://github.com/Rinsvent/attribute-extractor.git", - "reference": "f63bcb263ff17ed03bcaa87ab4b5c50e246d842e" + "reference": "081b9f18c2fa4305e8acbe63fff873ae59928c9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Rinsvent/attribute-extractor/zipball/f63bcb263ff17ed03bcaa87ab4b5c50e246d842e", - "reference": "f63bcb263ff17ed03bcaa87ab4b5c50e246d842e", + "url": "https://api.github.com/repos/Rinsvent/attribute-extractor/zipball/081b9f18c2fa4305e8acbe63fff873ae59928c9d", + "reference": "081b9f18c2fa4305e8acbe63fff873ae59928c9d", "shasum": "" }, "require": { @@ -615,9 +615,9 @@ "description": "PHP 8 attribute extractor", "support": { "issues": "https://github.com/Rinsvent/attribute-extractor/issues", - "source": "https://github.com/Rinsvent/attribute-extractor/tree/v0.0.1" + "source": "https://github.com/Rinsvent/attribute-extractor/tree/v0.0.2" }, - "time": "2021-07-27T16:37:22+00:00" + "time": "2021-07-28T13:37:17+00:00" }, { "name": "symfony/cache", @@ -940,6 +940,171 @@ ], "time": "2021-02-19T12:13:01+00:00" }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab", + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T09:17:38+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.23.0", @@ -1261,6 +1426,89 @@ ], "time": "2021-04-01T10:43:52+00:00" }, + { + "name": "symfony/string", + "version": "v5.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-06-27T11:44:38+00:00" + }, { "name": "symfony/translation-contracts", "version": "v2.4.0", @@ -5131,171 +5379,6 @@ ], "time": "2021-06-30T08:27:49+00:00" }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab", - "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-05-27T09:17:38+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-02-19T12:13:01+00:00" - }, { "name": "symfony/routing", "version": "v5.3.0", @@ -5386,89 +5469,6 @@ ], "time": "2021-05-26T17:43:10+00:00" }, - { - "name": "symfony/string", - "version": "v5.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "reference": "bd53358e3eccec6a670b5f33ab680d8dbe1d4ae1", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" - }, - "require-dev": { - "symfony/error-handler": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "files": [ - "Resources/functions.php" - ], - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v5.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-06-27T11:44:38+00:00" - }, { "name": "symfony/var-dumper", "version": "v5.3.3", diff --git a/src/Annotation/HeaderKey.php b/src/Annotation/PropertyPath.php similarity index 69% rename from src/Annotation/HeaderKey.php rename to src/Annotation/PropertyPath.php index 45eda63..288cbb3 100644 --- a/src/Annotation/HeaderKey.php +++ b/src/Annotation/PropertyPath.php @@ -3,9 +3,9 @@ namespace Rinsvent\RequestBundle\Annotation; #[\Attribute] -class HeaderKey +class PropertyPath { public function __construct( - public string $key + public string $path ) {} } \ No newline at end of file diff --git a/src/EventListener/RequestListener.php b/src/EventListener/RequestListener.php index f1c516c..3b86b85 100644 --- a/src/EventListener/RequestListener.php +++ b/src/EventListener/RequestListener.php @@ -3,12 +3,13 @@ namespace Rinsvent\RequestBundle\EventListener; use JMS\Serializer\SerializerBuilder; -use ReflectionMethod; use ReflectionObject; +use Rinsvent\AttributeExtractor\PropertyExtractor; +use Rinsvent\RequestBundle\Annotation\PropertyPath; use Rinsvent\RequestBundle\Annotation\RequestDTO; -use Rinsvent\RequestBundle\Annotation\HeaderKey; use Rinsvent\RequestBundle\DTO\Error; use Rinsvent\RequestBundle\DTO\ErrorCollection; +use Rinsvent\AttributeExtractor\MethodExtractor; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -25,20 +26,16 @@ class RequestListener $controller = $request->get('_controller'); if (is_string($controller)) { $controller = explode('::', $controller); - $method = new ReflectionMethod($controller[0], $controller[1]); } if (is_callable($controller)) { - $method = new ReflectionMethod($controller[0], $controller[1]); - } - if (!isset($method)) { - return; + if (is_object($controller[0])) { + $controller[0] = get_class($controller[0]); + } + $methodExtractor = new MethodExtractor($controller[0], $controller[1]); } - $attributes = $method->getAttributes(RequestDTO::class); - $attribute = $attributes[0] ?? null; - if ($attribute) { - /** @var RequestDTO $requestDTO */ - $requestDTO = $attribute->newInstance(); + /** @var RequestDTO $requestDTO */ + if ($requestDTO = $methodExtractor->fetch(RequestDTO::class)) { $requestDTOInstance = $this->grabRequestDTO($requestDTO->className, $request->getContent(), $request->query->all(), $request->request->all(), $request->headers->all()); $errorCollection = $this->validate($requestDTOInstance); @@ -92,12 +89,10 @@ class RequestListener $reflectionObject = new ReflectionObject($object); $properties = $reflectionObject->getProperties(); foreach ($properties as $property) { - $attributes = $property->getAttributes(HeaderKey::class); - $attribute = $attributes[0] ?? null; - if ($attribute) { - /** @var HeaderKey $headerKey */ - $headerKey = $attribute->newInstance(); - $value = $data[strtolower($headerKey->key)][0] ?? null; + $propertyExtractor = new PropertyExtractor($object::class, $property->getName()); + /** @var PropertyPath $propertyPath */ + if ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) { + $value = $data[strtolower($propertyPath->path)][0] ?? null; } else { $value = $data[$property->getName()] ?? null; } diff --git a/tests/_support/Helper/Unit.php b/tests/_support/Helper/Unit.php index 9b77d42..74dce91 100644 --- a/tests/_support/Helper/Unit.php +++ b/tests/_support/Helper/Unit.php @@ -4,21 +4,43 @@ namespace Rinsvent\RequestBundle\Tests\Helper; // here you can define custom actions // all public methods declared in helper class will be available in $I -use Rinsvent\ApiSDKGenerator\DTO\Writer\Config; -use Rinsvent\ApiSDKGenerator\Service\Writer; +use Rinsvent\RequestBundle\EventListener\RequestListener; +use Rinsvent\RequestBundle\Tests\unit\Listener\fixtures\FillTest\Controller; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ControllerResolver; +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; class Unit extends \Codeception\Module { - public function getWriter(string $lang = 'php'): Writer + public function send(Request $request): Response { - return new Writer( - new Config( - dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'templates', - dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'var/tests/cache', - $lang, - dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'var/tests/result', - 'Rinsvent\\AuthSDK' - ) - ); + $routes = new RouteCollection(); + $controller = new Controller(); + $routes->add('hello', new Route('/hello/{name}', [ + '_controller' => [$controller, 'hello'] + ] + )); + + $matcher = new UrlMatcher($routes, new RequestContext()); + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack())); + $listener = new RequestListener(); + $dispatcher->addListener('kernel.request', [$listener, 'onKernelRequest']); + + $controllerResolver = new ControllerResolver(); + $argumentResolver = new ArgumentResolver(); + $kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver); + $response = $kernel->handle($request); + $response->send(); + return $response; } } diff --git a/tests/unit/Listener/FillTest.php b/tests/unit/Listener/FillTest.php index b0c7592..6d8fa63 100644 --- a/tests/unit/Listener/FillTest.php +++ b/tests/unit/Listener/FillTest.php @@ -2,19 +2,7 @@ namespace Rinsvent\RequestBundle\Tests\Listener; -use Rinsvent\RequestBundle\Tests\unit\Listener\fixtures\FillTest\Controller; -use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Controller\ArgumentResolver; -use Symfony\Component\HttpKernel\Controller\ControllerResolver; -use Symfony\Component\HttpKernel\EventListener\RouterListener; -use Symfony\Component\HttpKernel\HttpKernel; -use Symfony\Component\Routing\Matcher\UrlMatcher; -use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\RouteCollection; use Rinsvent\RequestBundle\EventListener\RequestListener; @@ -39,31 +27,22 @@ class FillTest extends \Codeception\Test\Unit $request = Request::create('/hello/igor', 'GET', [ 'surname' => 'Surname' ]); - $response = $this->send($request); + $response = $this->tester->send($request); + $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('Surname', $request->get(RequestListener::REQUEST_DATA)->surname); + $this->assertEquals('Hello igor', $response->getContent()); + } - private function send(Request $request): Response + public function testFailRequestData() { - $routes = new RouteCollection(); - $controller = new Controller(); - $routes->add('hello', new Route('/hello/{name}', [ - '_controller' => [$controller, 'hello'] - ] - )); - - $matcher = new UrlMatcher($routes, new RequestContext()); - $dispatcher = new EventDispatcher(); - $dispatcher->addSubscriber(new RouterListener($matcher, new RequestStack())); - $listener = new RequestListener(); - $dispatcher->addListener('kernel.request', [$listener, 'onKernelRequest']); + $request = Request::create('/hello/igor', 'GET', [ + 'surname' => '' + ]); + $response = $this->tester->send($request); - $controllerResolver = new ControllerResolver(); - $argumentResolver = new ArgumentResolver(); - $kernel = new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver); - $response = $kernel->handle($request); - $response->send(); - return $response; + $this->assertEquals(400, $response->getStatusCode()); + $this->assertEquals('{"errors":[{"message":"This value should not be blank.","path":"surname"}]}', $response->getContent()); } }