From f8bfd4fa71482c075ff06030f2ba22fd2d1fef5e Mon Sep 17 00:00:00 2001 From: Dominik Zogg Date: Sun, 28 Jan 2024 22:00:47 +0100 Subject: [PATCH] chubbyphp-parsing --- README.md | 115 ++++--- composer.json | 7 +- config/prod.php | 73 ++-- src/Collection/AbstractCollection.php | 20 ++ src/Collection/CollectionInterface.php | 2 +- .../Collection/CollectionRequestInterface.php | 12 + src/Dto/Collection/PetCollectionFilters.php | 10 + src/Dto/Collection/PetCollectionRequest.php | 30 ++ src/Dto/Collection/PetCollectionResponse.php | 36 ++ src/Dto/Collection/PetCollectionSort.php | 10 + src/Dto/Model/ModelRequestInterface.php | 14 + src/Dto/Model/PetRequest.php | 60 ++++ src/Dto/Model/PetResponse.php | 30 ++ src/Dto/Model/VaccinationRequest.php | 10 + src/Dto/Model/VaccinationResponse.php | 12 + .../Collection/PetCollectionFactory.php | 16 - src/Factory/CollectionFactoryInterface.php | 12 - src/Factory/Model/PetFactory.php | 21 -- src/Factory/ModelFactoryInterface.php | 14 - .../Deserialization/PetCollectionMapping.php | 43 --- src/Mapping/Deserialization/PetMapping.php | 49 --- .../Deserialization/VaccinationMapping.php | 40 --- .../AbstractCollectionMapping.php | 84 ----- .../Serialization/AbstractModelMapping.php | 69 ---- .../Serialization/PetCollectionMapping.php | 30 -- src/Mapping/Serialization/PetMapping.php | 50 --- .../Serialization/VaccinationMapping.php | 50 --- .../Validation/PetCollectionMapping.php | 56 --- src/Mapping/Validation/PetMapping.php | 49 --- src/Mapping/Validation/VaccinationMapping.php | 41 --- src/Middleware/ApiExceptionMiddleware.php | 108 ++++++ src/Model/ModelInterface.php | 2 +- src/Model/Pet.php | 17 + src/Model/Vaccination.php | 9 +- src/{Mapping => }/Orm/PetMapping.php | 2 +- src/{Mapping => }/Orm/VaccinationMapping.php | 2 +- src/Parsing/ParsingInterface.php | 19 + src/Parsing/PetParsing.php | 161 +++++++++ .../Api/Crud/CreateRequestHandler.php | 57 ++- .../Api/Crud/DeleteRequestHandler.php | 6 +- .../Api/Crud/ListRequestHandler.php | 43 ++- .../Api/Crud/ReadRequestHandler.php | 23 +- .../Api/Crud/UpdateRequestHandler.php | 61 ++-- .../DenormalizationObjectMappingsFactory.php | 32 -- .../PetCollectionFactoryFactory.php | 15 - .../Factory/Model/PetFactoryFactory.php | 15 - .../Framework/RoutesByNameFactory.php | 14 +- .../ApiExceptionMiddlewareFactory.php | 29 ++ ...ptNegotiatorSupportedMediaTypesFactory.php | 9 +- ...peNegotiatorSupportedMediaTypesFactory.php | 9 +- src/ServiceFactory/Parsing/ParserFactory.php | 16 + .../Parsing/PetParsingFactory.php | 21 ++ .../Crud/PetCreateRequestHandlerFactory.php | 16 +- .../Crud/PetDeleteRequestHandlerFactory.php | 4 +- .../Api/Crud/PetListRequestHandlerFactory.php | 14 +- .../Api/Crud/PetReadRequestHandlerFactory.php | 8 +- .../Crud/PetUpdateRequestHandlerFactory.php | 14 +- .../NormalizationObjectMappingsFactory.php | 30 -- .../ValidationMappingProviderFactory.php | 25 -- .../Integration/PetCrudRequestHandlerTest.php | 152 +++----- tests/Unit/Collection/CollectionTest.php | 31 +- .../Collection/PetCollectionFactoryTest.php | 24 -- tests/Unit/Factory/Model/PetFactoryTest.php | 31 -- .../PetCollectionMappingTest.php | 80 ----- .../Deserialization/PetMappingTest.php | 72 ---- .../VaccinationMappingTest.php | 62 ---- .../Serialization/CollectionMappingTest.php | 207 ----------- .../Serialization/ModelMappingTest.php | 212 ------------ .../PetCollectionMappingTest.php | 48 --- .../Mapping/Serialization/PetMappingTest.php | 77 ----- .../Serialization/VaccinationMappingTest.php | 61 ---- .../Validation/PetCollectionMappingTest.php | 65 ---- .../Mapping/Validation/PetMappingTest.php | 58 ---- .../Validation/VaccinationMappingTest.php | 50 --- .../Middleware/ApiExceptionMiddlewareTest.php | 324 ++++++++++++++++++ tests/Unit/Model/PetTest.php | 16 + tests/Unit/Model/VaccinationTest.php | 2 + .../Unit/{Mapping => }/Orm/PetMappingTest.php | 6 +- .../Orm/VaccinationMappingTest.php | 6 +- tests/Unit/Parsing/PetParsingTest.php | 295 ++++++++++++++++ .../Api/Crud/CreateRequestHandlerTest.php | 189 +++++----- .../Api/Crud/DeleteRequestHandlerTest.php | 36 +- .../Api/Crud/ListRequestHandlerTest.php | 153 +++++---- .../Api/Crud/ReadRequestHandlerTest.php | 104 ++++-- .../Api/Crud/UpdateRequestHandlerTest.php | 246 ++++++------- ...normalizationObjectMappingsFactoryTest.php | 37 -- .../PetCollectionFactoryFactoryTest.php | 31 -- .../Factory/Model/PetFactoryFactoryTest.php | 31 -- .../Framework/RoutesByNameFactoryTest.php | 20 +- .../ApiExceptionMiddlewareFactoryTest.php | 49 +++ ...gotiatorSupportedMediaTypesFactoryTest.php | 21 -- ...gotiatorSupportedMediaTypesFactoryTest.php | 21 -- .../Parsing/ParserFactoryTest.php | 24 ++ .../Parsing/PetParsingFactoryTest.php | 43 +++ .../PetCreateRequestHandlerFactoryTest.php | 42 +-- .../PetDeleteRequestHandlerFactoryTest.php | 16 +- .../Crud/PetListRequestHandlerFactoryTest.php | 39 +-- .../Crud/PetReadRequestHandlerFactoryTest.php | 27 +- .../PetUpdateRequestHandlerFactoryTest.php | 38 +- ...NormalizationObjectMappingsFactoryTest.php | 46 --- .../ValidationMappingProviderFactoryTest.php | 35 -- 101 files changed, 2182 insertions(+), 2791 deletions(-) create mode 100644 src/Dto/Collection/CollectionRequestInterface.php create mode 100644 src/Dto/Collection/PetCollectionFilters.php create mode 100644 src/Dto/Collection/PetCollectionRequest.php create mode 100644 src/Dto/Collection/PetCollectionResponse.php create mode 100644 src/Dto/Collection/PetCollectionSort.php create mode 100644 src/Dto/Model/ModelRequestInterface.php create mode 100644 src/Dto/Model/PetRequest.php create mode 100644 src/Dto/Model/PetResponse.php create mode 100644 src/Dto/Model/VaccinationRequest.php create mode 100644 src/Dto/Model/VaccinationResponse.php delete mode 100644 src/Factory/Collection/PetCollectionFactory.php delete mode 100644 src/Factory/CollectionFactoryInterface.php delete mode 100644 src/Factory/Model/PetFactory.php delete mode 100644 src/Factory/ModelFactoryInterface.php delete mode 100644 src/Mapping/Deserialization/PetCollectionMapping.php delete mode 100644 src/Mapping/Deserialization/PetMapping.php delete mode 100644 src/Mapping/Deserialization/VaccinationMapping.php delete mode 100644 src/Mapping/Serialization/AbstractCollectionMapping.php delete mode 100644 src/Mapping/Serialization/AbstractModelMapping.php delete mode 100644 src/Mapping/Serialization/PetCollectionMapping.php delete mode 100644 src/Mapping/Serialization/PetMapping.php delete mode 100644 src/Mapping/Serialization/VaccinationMapping.php delete mode 100644 src/Mapping/Validation/PetCollectionMapping.php delete mode 100644 src/Mapping/Validation/PetMapping.php delete mode 100644 src/Mapping/Validation/VaccinationMapping.php create mode 100644 src/Middleware/ApiExceptionMiddleware.php rename src/{Mapping => }/Orm/PetMapping.php (97%) rename src/{Mapping => }/Orm/VaccinationMapping.php (97%) create mode 100644 src/Parsing/ParsingInterface.php create mode 100644 src/Parsing/PetParsing.php delete mode 100644 src/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactory.php delete mode 100644 src/ServiceFactory/Factory/Collection/PetCollectionFactoryFactory.php delete mode 100644 src/ServiceFactory/Factory/Model/PetFactoryFactory.php create mode 100644 src/ServiceFactory/Middleware/ApiExceptionMiddlewareFactory.php create mode 100644 src/ServiceFactory/Parsing/ParserFactory.php create mode 100644 src/ServiceFactory/Parsing/PetParsingFactory.php delete mode 100644 src/ServiceFactory/Serialization/NormalizationObjectMappingsFactory.php delete mode 100644 src/ServiceFactory/Validation/ValidationMappingProviderFactory.php delete mode 100644 tests/Unit/Factory/Collection/PetCollectionFactoryTest.php delete mode 100644 tests/Unit/Factory/Model/PetFactoryTest.php delete mode 100644 tests/Unit/Mapping/Deserialization/PetCollectionMappingTest.php delete mode 100644 tests/Unit/Mapping/Deserialization/PetMappingTest.php delete mode 100644 tests/Unit/Mapping/Deserialization/VaccinationMappingTest.php delete mode 100644 tests/Unit/Mapping/Serialization/CollectionMappingTest.php delete mode 100644 tests/Unit/Mapping/Serialization/ModelMappingTest.php delete mode 100644 tests/Unit/Mapping/Serialization/PetCollectionMappingTest.php delete mode 100644 tests/Unit/Mapping/Serialization/PetMappingTest.php delete mode 100644 tests/Unit/Mapping/Serialization/VaccinationMappingTest.php delete mode 100644 tests/Unit/Mapping/Validation/PetCollectionMappingTest.php delete mode 100644 tests/Unit/Mapping/Validation/PetMappingTest.php delete mode 100644 tests/Unit/Mapping/Validation/VaccinationMappingTest.php create mode 100644 tests/Unit/Middleware/ApiExceptionMiddlewareTest.php rename tests/Unit/{Mapping => }/Orm/PetMappingTest.php (94%) rename tests/Unit/{Mapping => }/Orm/VaccinationMappingTest.php (92%) create mode 100644 tests/Unit/Parsing/PetParsingTest.php delete mode 100644 tests/Unit/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactoryTest.php delete mode 100644 tests/Unit/ServiceFactory/Factory/Collection/PetCollectionFactoryFactoryTest.php delete mode 100644 tests/Unit/ServiceFactory/Factory/Model/PetFactoryFactoryTest.php create mode 100644 tests/Unit/ServiceFactory/Middleware/ApiExceptionMiddlewareFactoryTest.php create mode 100644 tests/Unit/ServiceFactory/Parsing/ParserFactoryTest.php create mode 100644 tests/Unit/ServiceFactory/Parsing/PetParsingFactoryTest.php delete mode 100644 tests/Unit/ServiceFactory/Serialization/NormalizationObjectMappingsFactoryTest.php delete mode 100644 tests/Unit/ServiceFactory/Validation/ValidationMappingProviderFactoryTest.php diff --git a/README.md b/README.md index 4fd212ab..596e1cda 100644 --- a/README.md +++ b/README.md @@ -11,25 +11,22 @@ A simple skeleton to build api's based on the [chubbyphp-framework][1]. ## Requirements * php: ^8.1 - * [chubbyphp/chubbyphp-api-http][2]: ^6.0 - * [chubbyphp/chubbyphp-clean-directories][3]: ^1.3.1 - * [chubbyphp/chubbyphp-cors][4]: ^1.5 - * [chubbyphp/chubbyphp-decode-encode][5]: ^1.1 - * [chubbyphp/chubbyphp-deserialization][6]: ^4.1 - * [chubbyphp/chubbyphp-framework][7]: ^5.1.1 - * [chubbyphp/chubbyphp-framework-router-fastroute][8]: ^2.1 - * [chubbyphp/chubbyphp-http-exception][9]: ^1.1 - * [chubbyphp/chubbyphp-laminas-config][10]: ^1.4 - * [chubbyphp/chubbyphp-laminas-config-doctrine][11]: ^2.2 - * [chubbyphp/chubbyphp-laminas-config-factory][12]: ^1.3 - * [chubbyphp/chubbyphp-negotiation][13]: ^2.0 - * [chubbyphp/chubbyphp-serialization][14]: ^4.0 - * [chubbyphp/chubbyphp-validation][15]: ^4.0 - * [doctrine/orm][16]: ^2.17.2 - * [monolog/monolog][17]: ^3.5 - * [ramsey/uuid][18]: ^4.7.5 - * [slim/psr7][19]: ^1.6.1 - * [symfony/console][20]: ^6.4.2 + * [chubbyphp/chubbyphp-clean-directories][2]: ^1.3.1 + * [chubbyphp/chubbyphp-cors][3]: ^1.5 + * [chubbyphp/chubbyphp-decode-encode][4]: ^1.1 + * [chubbyphp/chubbyphp-framework][5]: ^5.1.1 + * [chubbyphp/chubbyphp-framework-router-fastroute][6]: ^2.1 + * [chubbyphp/chubbyphp-http-exception][7]: ^1.1 + * [chubbyphp/chubbyphp-laminas-config][8]: ^1.4 + * [chubbyphp/chubbyphp-laminas-config-doctrine][9]: ^2.2 + * [chubbyphp/chubbyphp-laminas-config-factory][10]: ^1.3 + * [chubbyphp/chubbyphp-negotiation][11]: ^2.0 + * [chubbyphp/chubbyphp-parsing][12]: ^1.0 + * [doctrine/orm][13]: ^2.17.2 + * [monolog/monolog][14]: ^3.5 + * [ramsey/uuid][15]: ^4.7.5 + * [slim/psr7][16]: ^1.6.1 + * [symfony/console][17]: ^6.4.2 ## Environment @@ -86,17 +83,17 @@ Collections are sortable, filterable paginated lists of models. * [App\Collection][60] -### Factory +### Dto -Factories to create collections, model or whatever you need to be created. +Data transer objects, for example coverting data from request to model and viseversa. - * [App\Factory][70] + * [App\Dto][70] -### Mapping +### Middleware -Mappings are used for deserialization, orm, serialization and validation defintions. They are all done in PHP. +Middlewares which can be added generic or to specific routes. - * [App\Mapping][80] + * [App\Middleware][80] ### Model @@ -104,11 +101,23 @@ Models, entities, documents what ever fits your purpose the best. * [App\Model][90] +### ORM + +ORM Mapping definitions. + + * [App\ORM][100] + +### Parsing + +Parsing data to DTO's, for example in request/response cycle. + + * [App\Parsing][110] + ### Repository Repositories get data from storages like databases, elasticsearch, redis or whereever your models are stored or cached. - * [App\Repository][100] + * [App\Repository][120] ### RequestHandler @@ -116,52 +125,52 @@ RequestHandler alias Controller, or Controller actions to be more precise. There is a directory with generic crud controllers. If you like the idea adapt them for your generic use case, if not drop them. I highly recommend to not extend them. - * [App\RequestHandler][110] + * [App\RequestHandler][130] ### ServiceFactory Service factories are the glue code of the dependeny injection container. - * [App\ServiceFactory][120] + * [App\ServiceFactory][140] ## Copyright 2024 Dominik Zogg [1]: https://github.com/chubbyphp/chubbyphp-framework - -[2]: https://packagist.org/packages/chubbyphp/chubbyphp-api-http -[3]: https://packagist.org/packages/chubbyphp/chubbyphp-clean-directories -[4]: https://packagist.org/packages/chubbyphp/chubbyphp-cors -[5]: https://packagist.org/packages/chubbyphp/chubbyphp-decode-encode -[6]: https://packagist.org/packages/chubbyphp/chubbyphp-deserialization -[7]: https://packagist.org/packages/chubbyphp/chubbyphp-framework -[8]: https://packagist.org/packages/chubbyphp/chubbyphp-framework-router-fastroute -[9]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception -[10]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config -[11]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config-doctrine -[12]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config-factory -[13]: https://packagist.org/packages/chubbyphp/chubbyphp-negotiation -[14]: https://packagist.org/packages/chubbyphp/chubbyphp-serialization -[15]: https://packagist.org/packages/chubbyphp/chubbyphp-validation -[16]: https://packagist.org/packages/doctrine/orm -[17]: https://packagist.org/packages/monolog/monolog -[18]: https://packagist.org/packages/ramsey/uuid -[19]: https://packagist.org/packages/slim/psr7 -[20]: https://packagist.org/packages/symfony/console +[2]: https://packagist.org/packages/chubbyphp/chubbyphp-clean-directories +[3]: https://packagist.org/packages/chubbyphp/chubbyphp-cors +[4]: https://packagist.org/packages/chubbyphp/chubbyphp-decode-encode +[5]: https://packagist.org/packages/chubbyphp/chubbyphp-framework +[6]: https://packagist.org/packages/chubbyphp/chubbyphp-framework-router-fastroute +[7]: https://packagist.org/packages/chubbyphp/chubbyphp-http-exception +[8]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config +[9]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config-doctrine +[10]: https://packagist.org/packages/chubbyphp/chubbyphp-laminas-config-factory +[11]: https://packagist.org/packages/chubbyphp/chubbyphp-negotiation +[12]: https://packagist.org/packages/chubbyphp/chubbyphp-parsing +[13]: https://packagist.org/packages/doctrine/orm +[14]: https://packagist.org/packages/monolog/monolog +[15]: https://packagist.org/packages/ramsey/uuid +[16]: https://packagist.org/packages/slim/psr7 +[17]: https://packagist.org/packages/symfony/console [40]: https://packagist.org/packages/chubbyphp/petstore [60]: src/Collection -[70]: src/Factory +[70]: src/Dto -[80]: src/Mapping +[80]: src/Middleware [90]: src/Model -[100]: src/Repository +[100]: src/Orm + +[110]: src/Parsing + +[120]: src/Repository -[110]: src/RequestHandler +[130]: src/RequestHandler -[120]: src/ServiceFactory +[140]: src/ServiceFactory diff --git a/composer.json b/composer.json index 4b03f7f8..ded77f2b 100644 --- a/composer.json +++ b/composer.json @@ -15,20 +15,17 @@ ], "require": { "php": "^8.1", - "chubbyphp/chubbyphp-api-http": "^6.0", "chubbyphp/chubbyphp-clean-directories": "^1.3.1", "chubbyphp/chubbyphp-cors": "^1.5", "chubbyphp/chubbyphp-decode-encode": "^1.1", - "chubbyphp/chubbyphp-deserialization": "^4.1", "chubbyphp/chubbyphp-framework": "^5.1.1", "chubbyphp/chubbyphp-framework-router-fastroute": "^2.1", "chubbyphp/chubbyphp-http-exception": "^1.1", "chubbyphp/chubbyphp-laminas-config": "^1.4", "chubbyphp/chubbyphp-laminas-config-doctrine": "^2.1", "chubbyphp/chubbyphp-laminas-config-factory": "^1.3", - "chubbyphp/chubbyphp-negotiation": "^2.0", - "chubbyphp/chubbyphp-serialization": "^4.0", - "chubbyphp/chubbyphp-validation": "^4.0", + "chubbyphp/chubbyphp-negotiation": "^2.1", + "chubbyphp/chubbyphp-parsing": "^1.0@dev", "doctrine/orm": "^2.17.2", "monolog/monolog": "^3.5", "ramsey/uuid": "^4.7.5", diff --git a/config/prod.php b/config/prod.php index 54cca0f7..9813f451 100644 --- a/config/prod.php +++ b/config/prod.php @@ -2,27 +2,24 @@ declare(strict_types=1); -use App\Factory\Collection\PetCollectionFactory; -use App\Factory\Model\PetFactory; -use App\Mapping\Orm\PetMapping; -use App\Mapping\Orm\VaccinationMapping; +use App\Middleware\ApiExceptionMiddleware; use App\Model\Pet; use App\Model\Vaccination; +use App\Orm\PetMapping; +use App\Orm\VaccinationMapping; +use App\Parsing\PetCollectionParsing; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\CreateRequestHandler; use App\RequestHandler\Api\Crud\DeleteRequestHandler; use App\RequestHandler\Api\Crud\ListRequestHandler; use App\RequestHandler\Api\Crud\ReadRequestHandler; use App\RequestHandler\Api\Crud\UpdateRequestHandler; -use App\RequestHandler\PingRequestHandler; -use Doctrine\DBAL\Connection; use App\RequestHandler\OpenapiRequestHandler; +use App\RequestHandler\PingRequestHandler; use App\ServiceFactory\Command\CommandsFactory; use App\ServiceFactory\DecodeEncode\TypeDecodersFactory; use App\ServiceFactory\DecodeEncode\TypeEncodersFactory; -use App\ServiceFactory\Deserialization\DenormalizationObjectMappingsFactory; -use App\ServiceFactory\Factory\Collection\PetCollectionFactoryFactory; -use App\ServiceFactory\Factory\Model\PetFactoryFactory; use App\ServiceFactory\Framework\ExceptionMiddlewareFactory; use App\ServiceFactory\Framework\MiddlewaresFactory; use App\ServiceFactory\Framework\RouteMatcherFactory; @@ -32,33 +29,28 @@ use App\ServiceFactory\Http\ResponseFactoryFactory; use App\ServiceFactory\Http\StreamFactoryFactory; use App\ServiceFactory\Logger\LoggerFactory; +use App\ServiceFactory\Middleware\ApiExceptionMiddlewareFactory; use App\ServiceFactory\Negotiation\AcceptNegotiatorSupportedMediaTypesFactory; use App\ServiceFactory\Negotiation\ContentTypeNegotiatorSupportedMediaTypesFactory; +use App\ServiceFactory\Parsing\ParserFactory; +use App\ServiceFactory\Parsing\PetCollectionParsingFactory; +use App\ServiceFactory\Parsing\PetParsingFactory; use App\ServiceFactory\Repository\PetRepositoryFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetCreateRequestHandlerFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetDeleteRequestHandlerFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetListRequestHandlerFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetReadRequestHandlerFactory; use App\ServiceFactory\RequestHandler\Api\Crud\PetUpdateRequestHandlerFactory; -use App\ServiceFactory\RequestHandler\PingRequestHandlerFactory; use App\ServiceFactory\RequestHandler\OpenapiRequestHandlerFactory; -use App\ServiceFactory\Serialization\NormalizationObjectMappingsFactory; -use App\ServiceFactory\Validation\ValidationMappingProviderFactory; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\ApiHttp\Middleware\AcceptAndContentTypeMiddleware; -use Chubbyphp\ApiHttp\Middleware\ApiExceptionMiddleware; -use Chubbyphp\ApiHttp\ServiceFactory\AcceptAndContentTypeMiddlewareFactory; -use Chubbyphp\ApiHttp\ServiceFactory\ApiExceptionMiddlewareFactory; -use Chubbyphp\ApiHttp\ServiceFactory\RequestManagerFactory; -use Chubbyphp\ApiHttp\ServiceFactory\ResponseManagerFactory; +use App\ServiceFactory\RequestHandler\PingRequestHandlerFactory; use Chubbyphp\Cors\CorsMiddleware; use Chubbyphp\Cors\ServiceFactory\CorsMiddlewareFactory; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; use Chubbyphp\DecodeEncode\Decoder\TypeDecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\DecodeEncode\Encoder\TypeEncoderInterface; -use Chubbyphp\Deserialization\DeserializerInterface; -use Chubbyphp\Deserialization\Mapping\DenormalizationObjectMappingInterface; -use Chubbyphp\Deserialization\ServiceFactory\DeserializerFactory; +use Chubbyphp\DecodeEncode\ServiceFactory\DecoderFactory; +use Chubbyphp\DecodeEncode\ServiceFactory\EncoderFactory; use Chubbyphp\Framework\Middleware\ExceptionMiddleware; use Chubbyphp\Framework\Middleware\RouteMatcherMiddleware; use Chubbyphp\Framework\Router\RouteMatcherInterface; @@ -72,28 +64,26 @@ use Chubbyphp\Laminas\Config\Doctrine\ServiceFactory\Persistence\Mapping\Driver\ClassMapDriverFactory; use Chubbyphp\Negotiation\AcceptNegotiatorInterface; use Chubbyphp\Negotiation\ContentTypeNegotiatorInterface; +use Chubbyphp\Negotiation\Middleware\AcceptMiddleware; +use Chubbyphp\Negotiation\Middleware\ContentTypeMiddleware; +use Chubbyphp\Negotiation\ServiceFactory\AcceptMiddlewareFactory; use Chubbyphp\Negotiation\ServiceFactory\AcceptNegotiatorFactory; +use Chubbyphp\Negotiation\ServiceFactory\ContentTypeMiddlewareFactory; use Chubbyphp\Negotiation\ServiceFactory\ContentTypeNegotiatorFactory; -use Chubbyphp\Serialization\Mapping\NormalizationObjectMappingInterface; -use Chubbyphp\Serialization\SerializerInterface; -use Chubbyphp\Serialization\ServiceFactory\SerializerFactory; -use Chubbyphp\Validation\Mapping\ValidationMappingProviderInterface; -use Chubbyphp\Validation\Mapping\ValidationMappingProviderRegistryInterface; -use Chubbyphp\Validation\ServiceFactory\ValidationMappingProviderRegistryFactory; -use Chubbyphp\Validation\ServiceFactory\ValidatorFactory; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserInterface; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Tools\Console\ConnectionProvider; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\Console\EntityManagerProvider; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Monolog\Level; +use Psr\Cache\CacheItemPoolInterface; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; -use Psr\Cache\CacheItemPoolInterface; $rootDir = \realpath(__DIR__ . '/..'); $cacheDir = $rootDir . '/var/cache/' . $env; @@ -116,7 +106,7 @@ EntityManager::class => EntityManagerInterface::class, ], 'factories' => [ - AcceptAndContentTypeMiddleware::class => AcceptAndContentTypeMiddlewareFactory::class, + AcceptMiddleware::class => AcceptMiddlewareFactory::class, AcceptNegotiatorInterface::class . 'supportedMediaTypes[]' => AcceptNegotiatorSupportedMediaTypesFactory::class, AcceptNegotiatorInterface::class => AcceptNegotiatorFactory::class, ApiExceptionMiddleware::class => ApiExceptionMiddlewareFactory::class, @@ -124,42 +114,37 @@ Command::class . '[]' => CommandsFactory::class, Connection::class => ConnectionFactory::class, ConnectionProvider::class => ContainerConnectionProviderFactory::class, + ContentTypeMiddleware::class => ContentTypeMiddlewareFactory::class, ContentTypeNegotiatorInterface::class . 'supportedMediaTypes[]' => ContentTypeNegotiatorSupportedMediaTypesFactory::class, ContentTypeNegotiatorInterface::class => ContentTypeNegotiatorFactory::class, CorsMiddleware::class => CorsMiddlewareFactory::class, - DenormalizationObjectMappingInterface::class . '[]' => DenormalizationObjectMappingsFactory::class, - DeserializerInterface::class => DeserializerFactory::class, + DecoderInterface::class => DecoderFactory::class, + EncoderInterface::class => EncoderFactory::class, EntityManagerInterface::class => EntityManagerFactory::class, EntityManagerProvider::class => ContainerEntityManagerProviderFactory::class, ExceptionMiddleware::class => ExceptionMiddlewareFactory::class, LoggerInterface::class => LoggerFactory::class, MappingDriver::class => ClassMapDriverFactory::class, MiddlewareInterface::class . '[]' => MiddlewaresFactory::class, - NormalizationObjectMappingInterface::class . '[]' => NormalizationObjectMappingsFactory::class, OpenapiRequestHandler::class => OpenapiRequestHandlerFactory::class, + ParserInterface::class => ParserFactory::class, Pet::class . CreateRequestHandler::class => PetCreateRequestHandlerFactory::class, Pet::class . DeleteRequestHandler::class => PetDeleteRequestHandlerFactory::class, Pet::class . ListRequestHandler::class => PetListRequestHandlerFactory::class, Pet::class . ReadRequestHandler::class => PetReadRequestHandlerFactory::class, Pet::class . UpdateRequestHandler::class => PetUpdateRequestHandlerFactory::class, - PetCollectionFactory::class => PetCollectionFactoryFactory::class, - PetFactory::class => PetFactoryFactory::class, + PetCollectionParsing::class => PetCollectionParsingFactory::class, + PetParsing::class => PetParsingFactory::class, PetRepository::class => PetRepositoryFactory::class, PingRequestHandler::class => PingRequestHandlerFactory::class, - RequestManagerInterface::class => RequestManagerFactory::class, ResponseFactoryInterface::class => ResponseFactoryFactory::class, - ResponseManagerInterface::class => ResponseManagerFactory::class, RouteMatcherInterface::class => RouteMatcherFactory::class, RouteMatcherMiddleware::class => RouteMatcherMiddlewareFactory::class, RoutesByNameInterface::class => RoutesByNameFactory::class, - SerializerInterface::class => SerializerFactory::class, StreamFactoryInterface::class => StreamFactoryFactory::class, TypeDecoderInterface::class . '[]' => TypeDecodersFactory::class, TypeEncoderInterface::class . '[]' => TypeEncodersFactory::class, UrlGeneratorInterface::class => UrlGeneratorFactory::class, - ValidationMappingProviderInterface::class . '[]' => ValidationMappingProviderFactory::class, - ValidationMappingProviderRegistryInterface::class => ValidationMappingProviderRegistryFactory::class, - ValidatorInterface::class => ValidatorFactory::class, ], ], 'directories' => [ diff --git a/src/Collection/AbstractCollection.php b/src/Collection/AbstractCollection.php index 83962e22..e3c2a12b 100644 --- a/src/Collection/AbstractCollection.php +++ b/src/Collection/AbstractCollection.php @@ -106,4 +106,24 @@ final public function getItems(): array { return $this->items; } + + /** + * @return array{offset: int, limit: int, filters: array, sort: array, items: array, count: int} + */ + public function jsonSerialize(): array + { + $items = []; + foreach ($this->items as $item) { + $items[] = $item->jsonSerialize(); + } + + return [ + 'offset' => $this->offset, + 'limit' => $this->limit, + 'filters' => $this->filters, + 'sort' => $this->sort, + 'items' => $items, + 'count' => $this->count, + ]; + } } diff --git a/src/Collection/CollectionInterface.php b/src/Collection/CollectionInterface.php index 3d209e90..632b81b0 100644 --- a/src/Collection/CollectionInterface.php +++ b/src/Collection/CollectionInterface.php @@ -6,7 +6,7 @@ use App\Model\ModelInterface; -interface CollectionInterface +interface CollectionInterface extends \JsonSerializable { public const LIMIT = 20; diff --git a/src/Dto/Collection/CollectionRequestInterface.php b/src/Dto/Collection/CollectionRequestInterface.php new file mode 100644 index 00000000..7b4c09c1 --- /dev/null +++ b/src/Dto/Collection/CollectionRequestInterface.php @@ -0,0 +1,12 @@ +setOffset($this->offset); + $collection->setLimit($this->limit); + $collection->setFilters((array) $this->filters); + $collection->setSort((array) $this->sort); + + return $collection; + } +} diff --git a/src/Dto/Collection/PetCollectionResponse.php b/src/Dto/Collection/PetCollectionResponse.php new file mode 100644 index 00000000..6aebf7c2 --- /dev/null +++ b/src/Dto/Collection/PetCollectionResponse.php @@ -0,0 +1,36 @@ + + */ + public array $filters; + + /** + * @var array + */ + public array $sort; + + /** + * @var array + */ + public array $items; + + public int $count; + + public string $_type; + + /** + * @var array, attributes: Array}>> + */ + public array $_links; +} diff --git a/src/Dto/Collection/PetCollectionSort.php b/src/Dto/Collection/PetCollectionSort.php new file mode 100644 index 00000000..de7531e0 --- /dev/null +++ b/src/Dto/Collection/PetCollectionSort.php @@ -0,0 +1,10 @@ + + */ + public array $vaccinations; + + public function createModel(): ModelInterface + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccinationRequest) { + $vaccination = new Vaccination(); + $vaccination->setName($vaccinationRequest->name); + + $vaccinations[] = $vaccination; + } + + $model = new Pet(); + $model->setName($this->name); + $model->setTag($this->tag); + $model->setVaccinations($vaccinations); + + return $model; + } + + /** + * @param Pet $model + */ + public function updateModel(ModelInterface $model): ModelInterface + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccinationRequest) { + $vaccination = new Vaccination(); + $vaccination->setName($vaccinationRequest->name); + + $vaccinations[] = $vaccination; + } + + $model->setUpdatedAt(new \DateTimeImmutable()); + $model->setName($this->name); + $model->setTag($this->tag); + $model->setVaccinations($vaccinations); + + return $model; + } +} diff --git a/src/Dto/Model/PetResponse.php b/src/Dto/Model/PetResponse.php new file mode 100644 index 00000000..a681dd93 --- /dev/null +++ b/src/Dto/Model/PetResponse.php @@ -0,0 +1,30 @@ + + */ + public array $vaccinations; + + public string $_type; + + /** + * @var array, attributes: Array}>> + */ + public array $_links; +} diff --git a/src/Dto/Model/VaccinationRequest.php b/src/Dto/Model/VaccinationRequest.php new file mode 100644 index 00000000..5e298f5c --- /dev/null +++ b/src/Dto/Model/VaccinationRequest.php @@ -0,0 +1,10 @@ +getClass(); - - return new $class(); - }; - } - - /** - * @return array - */ - public function getDenormalizationFieldMappings(string $path, ?string $type = null): array - { - return [ - $this->denormalizationFieldMappingFactory->createConvertType('offset', ConvertTypeFieldDenormalizer::TYPE_INT), - $this->denormalizationFieldMappingFactory->createConvertType('limit', ConvertTypeFieldDenormalizer::TYPE_INT), - $this->denormalizationFieldMappingFactory->create('filters'), - $this->denormalizationFieldMappingFactory->create('sort'), - ]; - } -} diff --git a/src/Mapping/Deserialization/PetMapping.php b/src/Mapping/Deserialization/PetMapping.php deleted file mode 100644 index a243a109..00000000 --- a/src/Mapping/Deserialization/PetMapping.php +++ /dev/null @@ -1,49 +0,0 @@ -getClass(); - - return new $class(); - }; - } - - /** - * @return array - */ - public function getDenormalizationFieldMappings(string $path, ?string $type = null): array - { - return [ - $this->denormalizationFieldMappingFactory->createConvertType('name', ConvertTypeFieldDenormalizer::TYPE_STRING), - $this->denormalizationFieldMappingFactory->createConvertType('tag', ConvertTypeFieldDenormalizer::TYPE_STRING), - $this->denormalizationFieldMappingFactory->create( - 'vaccinations', - false, - new EmbedManyFieldDenormalizer(Vaccination::class, new MethodAccessor('vaccinations')) - ), - ]; - } -} diff --git a/src/Mapping/Deserialization/VaccinationMapping.php b/src/Mapping/Deserialization/VaccinationMapping.php deleted file mode 100644 index 3b87cd1b..00000000 --- a/src/Mapping/Deserialization/VaccinationMapping.php +++ /dev/null @@ -1,40 +0,0 @@ -getClass(); - - return new $class(); - }; - } - - /** - * @return array - */ - public function getDenormalizationFieldMappings(string $path, ?string $type = null): array - { - return [ - $this->denormalizationFieldMappingFactory->createConvertType('name', ConvertTypeFieldDenormalizer::TYPE_STRING), - ]; - } -} diff --git a/src/Mapping/Serialization/AbstractCollectionMapping.php b/src/Mapping/Serialization/AbstractCollectionMapping.php deleted file mode 100644 index 643ab5b8..00000000 --- a/src/Mapping/Serialization/AbstractCollectionMapping.php +++ /dev/null @@ -1,84 +0,0 @@ - - */ - final public function getNormalizationFieldMappings(string $path): array - { - return [ - NormalizationFieldMappingBuilder::create('offset')->getMapping(), - NormalizationFieldMappingBuilder::create('limit')->getMapping(), - NormalizationFieldMappingBuilder::create('count')->getMapping(), - NormalizationFieldMappingBuilder::create('filters')->getMapping(), - NormalizationFieldMappingBuilder::create('sort')->getMapping(), - ]; - } - - /** - * @return array - */ - final public function getNormalizationEmbeddedFieldMappings(string $path): array - { - return [ - NormalizationFieldMappingBuilder::createEmbedMany('items')->getMapping(), - ]; - } - - /** - * @return array - */ - final public function getNormalizationLinkMappings(string $path): array - { - return [ - NormalizationLinkMappingBuilder::createCallback('list', function ( - string $path, - CollectionInterface $collection, - NormalizerContextInterface $context - ) { - $queryParams = []; - - if (null !== $request = $context->getRequest()) { - $queryParams = $request->getQueryParams(); - } - - /** @var array $queryParams */ - $queryParams = array_merge($queryParams, [ - 'offset' => $collection->getOffset(), - 'limit' => $collection->getLimit(), - ]); - - return LinkBuilder::create( - $this->urlGenerator->generatePath($this->getListRouteName(), [], $queryParams) - ) - ->setAttributes(['method' => 'GET']) - ->getLink() - ; - })->getMapping(), - NormalizationLinkMappingBuilder::createCallback('create', fn () => LinkBuilder::create($this->urlGenerator->generatePath($this->getCreateRouteName())) - ->setAttributes(['method' => 'POST']) - ->getLink())->getMapping(), - ]; - } - - abstract protected function getListRouteName(): string; - - abstract protected function getCreateRouteName(): string; -} diff --git a/src/Mapping/Serialization/AbstractModelMapping.php b/src/Mapping/Serialization/AbstractModelMapping.php deleted file mode 100644 index ab21effb..00000000 --- a/src/Mapping/Serialization/AbstractModelMapping.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ - public function getNormalizationFieldMappings(string $path): array - { - return [ - NormalizationFieldMappingBuilder::create('id')->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('createdAt', \DateTime::ATOM)->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('updatedAt', \DateTime::ATOM)->getMapping(), - ]; - } - - /** - * @return array - */ - final public function getNormalizationEmbeddedFieldMappings(string $path): array - { - return []; - } - - /** - * @return array - */ - final public function getNormalizationLinkMappings(string $path): array - { - return [ - NormalizationLinkMappingBuilder::createCallback('read', fn (string $path, ModelInterface $model) => LinkBuilder::create( - $this->urlGenerator->generatePath($this->getReadRouteName(), ['id' => $model->getId()]) - ) - ->setAttributes(['method' => 'GET']) - ->getLink())->getMapping(), - NormalizationLinkMappingBuilder::createCallback('update', fn (string $path, ModelInterface $model) => LinkBuilder::create( - $this->urlGenerator->generatePath($this->getUpdateRouteName(), ['id' => $model->getId()]) - ) - ->setAttributes(['method' => 'PUT']) - ->getLink())->getMapping(), - NormalizationLinkMappingBuilder::createCallback('delete', fn (string $path, ModelInterface $model) => LinkBuilder::create( - $this->urlGenerator->generatePath($this->getDeleteRouteName(), ['id' => $model->getId()]) - ) - ->setAttributes(['method' => 'DELETE']) - ->getLink())->getMapping(), - ]; - } - - abstract protected function getReadRouteName(): string; - - abstract protected function getUpdateRouteName(): string; - - abstract protected function getDeleteRouteName(): string; -} diff --git a/src/Mapping/Serialization/PetCollectionMapping.php b/src/Mapping/Serialization/PetCollectionMapping.php deleted file mode 100644 index 8edf4f2c..00000000 --- a/src/Mapping/Serialization/PetCollectionMapping.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - public function getNormalizationFieldMappings(string $path): array - { - $normalizationFieldMappings = parent::getNormalizationFieldMappings($path); - $normalizationFieldMappings[] = NormalizationFieldMappingBuilder::create('name')->getMapping(); - $normalizationFieldMappings[] = NormalizationFieldMappingBuilder::create('tag')->getMapping(); - $normalizationFieldMappings[] = NormalizationFieldMappingBuilder::createEmbedMany('vaccinations')->getMapping(); - - return $normalizationFieldMappings; - } - - protected function getReadRouteName(): string - { - return 'pet_read'; - } - - protected function getUpdateRouteName(): string - { - return 'pet_update'; - } - - protected function getDeleteRouteName(): string - { - return 'pet_delete'; - } -} diff --git a/src/Mapping/Serialization/VaccinationMapping.php b/src/Mapping/Serialization/VaccinationMapping.php deleted file mode 100644 index 9b08f32f..00000000 --- a/src/Mapping/Serialization/VaccinationMapping.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ - public function getNormalizationFieldMappings(string $path): array - { - return [ - NormalizationFieldMappingBuilder::create('name')->getMapping(), - ]; - } - - /** - * @return array - */ - public function getNormalizationEmbeddedFieldMappings(string $path): array - { - return []; - } - - /** - * @return array - */ - public function getNormalizationLinkMappings(string $path): array - { - return []; - } -} diff --git a/src/Mapping/Validation/PetCollectionMapping.php b/src/Mapping/Validation/PetCollectionMapping.php deleted file mode 100644 index f5f82977..00000000 --- a/src/Mapping/Validation/PetCollectionMapping.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ - public function getValidationPropertyMappings(string $path, ?string $type = null): array - { - return [ - ValidationPropertyMappingBuilder::create('offset', [ - new NotBlankConstraint(), - new TypeConstraint('integer'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('limit', [ - new NotBlankConstraint(), - new TypeConstraint('integer'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('filters', [ - new MapConstraint([ - 'name' => [ - new NotBlankConstraint(), - new TypeConstraint('string'), - ], - ]), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('sort', [ - new SortConstraint(['name']), - ])->getMapping(), - ]; - } -} diff --git a/src/Mapping/Validation/PetMapping.php b/src/Mapping/Validation/PetMapping.php deleted file mode 100644 index 10c4aaa8..00000000 --- a/src/Mapping/Validation/PetMapping.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ - public function getValidationPropertyMappings(string $path, ?string $type = null): array - { - return [ - ValidationPropertyMappingBuilder::create('name', [ - new NotNullConstraint(), - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('tag', [ - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('vaccinations', [ - new ValidConstraint(), - ])->getMapping(), - ]; - } -} diff --git a/src/Mapping/Validation/VaccinationMapping.php b/src/Mapping/Validation/VaccinationMapping.php deleted file mode 100644 index d7c67c27..00000000 --- a/src/Mapping/Validation/VaccinationMapping.php +++ /dev/null @@ -1,41 +0,0 @@ - - */ - public function getValidationPropertyMappings(string $path, ?string $type = null): array - { - return [ - ValidationPropertyMappingBuilder::create('name', [ - new NotNullConstraint(), - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ]; - } -} diff --git a/src/Middleware/ApiExceptionMiddleware.php b/src/Middleware/ApiExceptionMiddleware.php new file mode 100644 index 00000000..7b9540eb --- /dev/null +++ b/src/Middleware/ApiExceptionMiddleware.php @@ -0,0 +1,108 @@ +logger = $logger ?? new NullLogger(); + } + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + try { + return $handler->handle($request); + } catch (\Throwable $exception) { + return $this->handleException($request, $exception); + } + } + + private function handleException(ServerRequestInterface $request, \Throwable $exception): ResponseInterface + { + $backtrace = $this->backtrace($exception); + + $httpException = $this->exceptionToHttpException($exception, $backtrace); + + $logLevel = $httpException->getStatus() >= 500 ? 'error' : 'info'; + + $this->logger->{$logLevel}('Http Exception', ['backtrace' => $backtrace]); + + if (null === $accept = $request->getAttribute('accept')) { + throw $exception; + } + + $status = $httpException->getStatus(); + + $response = $this->responseFactory->createResponse($status) + ->withHeader('Content-Type', str_replace('/', '/problem+', $accept)) + ; + + $data = $httpException->jsonSerialize(); + $data['_type'] = 'apiProblem'; + + $body = $this->encoder->encode($data, $accept); + + $response->getBody()->write($body); + + return $response; + } + + /** + * @return array> + */ + private function backtrace(\Throwable $exception): array + { + $exceptions = []; + do { + $exceptions[] = [ + 'class' => $exception::class, + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTraceAsString(), + ]; + } while ($exception = $exception->getPrevious()); + + return $exceptions; + } + + /** + * @param array> $backtrace + */ + private function exceptionToHttpException(\Throwable $exception, array $backtrace): HttpExceptionInterface + { + if ($exception instanceof HttpExceptionInterface) { + return $exception; + } + + if (!$this->debug) { + return HttpException::createInternalServerError(); + } + + return HttpException::createInternalServerError([ + 'detail' => $exception->getMessage(), + 'backtrace' => $backtrace, + ]); + } +} diff --git a/src/Model/ModelInterface.php b/src/Model/ModelInterface.php index 3742af10..4c4dcd29 100644 --- a/src/Model/ModelInterface.php +++ b/src/Model/ModelInterface.php @@ -4,7 +4,7 @@ namespace App\Model; -interface ModelInterface +interface ModelInterface extends \JsonSerializable { public function getId(): string; diff --git a/src/Model/Pet.php b/src/Model/Pet.php index c341ec8d..8ce07efc 100644 --- a/src/Model/Pet.php +++ b/src/Model/Pet.php @@ -106,4 +106,21 @@ public function getVaccinations(): array { return $this->vaccinations->getValues(); } + + public function jsonSerialize(): array + { + $vaccinations = []; + foreach ($this->vaccinations as $vaccination) { + $vaccinations[] = $vaccination->jsonSerialize(); + } + + return [ + 'id' => $this->id, + 'createdAt' => $this->createdAt, + 'updatedAt' => $this->updatedAt, + 'name' => $this->name, + 'tag' => $this->tag, + 'vaccinations' => $vaccinations, + ]; + } } diff --git a/src/Model/Vaccination.php b/src/Model/Vaccination.php index a5087ea3..70fe1fe0 100644 --- a/src/Model/Vaccination.php +++ b/src/Model/Vaccination.php @@ -6,7 +6,7 @@ use Ramsey\Uuid\Uuid; -final class Vaccination +final class Vaccination implements \JsonSerializable { private string $id; @@ -38,4 +38,11 @@ public function setPet(?Pet $pet): void { $this->pet = $pet; } + + public function jsonSerialize(): array + { + return [ + 'name' => $this->name, + ]; + } } diff --git a/src/Mapping/Orm/PetMapping.php b/src/Orm/PetMapping.php similarity index 97% rename from src/Mapping/Orm/PetMapping.php rename to src/Orm/PetMapping.php index 27f6fffa..982bf435 100644 --- a/src/Mapping/Orm/PetMapping.php +++ b/src/Orm/PetMapping.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace App\Mapping\Orm; +namespace App\Orm; use App\Model\Vaccination; use Chubbyphp\Laminas\Config\Doctrine\Persistence\Mapping\Driver\ClassMapMappingInterface; diff --git a/src/Mapping/Orm/VaccinationMapping.php b/src/Orm/VaccinationMapping.php similarity index 97% rename from src/Mapping/Orm/VaccinationMapping.php rename to src/Orm/VaccinationMapping.php index 93b5b9f8..bff19cc1 100644 --- a/src/Mapping/Orm/VaccinationMapping.php +++ b/src/Orm/VaccinationMapping.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace App\Mapping\Orm; +namespace App\Orm; use App\Model\Pet; use Chubbyphp\Laminas\Config\Doctrine\Persistence\Mapping\Driver\ClassMapMappingInterface; diff --git a/src/Parsing/ParsingInterface.php b/src/Parsing/ParsingInterface.php new file mode 100644 index 00000000..32c896e1 --- /dev/null +++ b/src/Parsing/ParsingInterface.php @@ -0,0 +1,19 @@ +collectionRequestSchema) { + $p = $this->parser; + + $this->collectionRequestSchema = $p->object([ + 'offset' => $p->union([$p->string()->default('0')->toInt(), $p->int()->default(0)]), + 'limit' => $p->union([ + $p->string()->default((string) CollectionInterface::LIMIT)->toInt(), + $p->int()->default(CollectionInterface::LIMIT), + ]), + 'filters' => $p->object([ + 'name' => $p->string()->nullable(), + ], PetCollectionFilters::class)->strict()->default([]), + 'sort' => $p->object([ + 'name' => $p->union([$p->literal('asc'), $p->literal('desc')]), + ], PetCollectionSort::class)->strict()->default([]), + ], PetCollectionRequest::class)->strict(); + } + + return $this->collectionRequestSchema; + } + + public function getCollectionResponseSchema(ServerRequestInterface $request): ObjectSchemaInterface + { + $p = $this->parser; + + return $p->object([ + 'offset' => $p->int(), + 'limit' => $p->int(), + 'filters' => $p->record($p->string()->nullable()), + 'sort' => $p->record($p->union([$p->literal('asc'), $p->literal('desc')])->nullable()), + 'items' => $p->array($this->getModelResponseSchema($request)), + 'count' => $p->int(), + '_type' => $p->literal('petCollection')->default('petCollection'), + ], PetCollectionResponse::class) + ->strict() + ->postParse(function (PetCollectionResponse $petCollectionResponse) use ($request) { + $queryParams = $request->getQueryParams(); + + /** @var array $queryParams */ + $queryParams = array_merge($queryParams, [ + 'offset' => $petCollectionResponse->offset, + 'limit' => $petCollectionResponse->limit, + ]); + + $petCollectionResponse->_links = [ + 'list' => [ + 'href' => $this->urlGenerator->generatePath('pet_list', [], $queryParams), + 'templated' => false, + 'rel' => [], + 'attributes' => ['method' => 'GET'], + ], + 'create' => [ + 'href' => $this->urlGenerator->generatePath('pet_create'), + 'templated' => false, + 'rel' => [], + 'attributes' => ['method' => 'POST'], + ], + ]; + + return $petCollectionResponse; + }) + ; + } + + public function getModelRequestSchema(ServerRequestInterface $request): ObjectSchemaInterface + { + if (null === $this->modelRequestSchema) { + $p = $this->parser; + + $this->modelRequestSchema = $p->object([ + 'name' => $p->string()->minLength(1), + 'tag' => $p->string()->minLength(1)->nullable(), + 'vaccinations' => $p->array($p->object([ + 'name' => $p->string(), + ], VaccinationRequest::class)->strict(['_type']))->default([]), + ], PetRequest::class)->strict(['id', 'createdAt', 'updatedAt', '_type', '_links']); + } + + return $this->modelRequestSchema; + } + + public function getModelResponseSchema(ServerRequestInterface $request): ObjectSchemaInterface + { + if (null === $this->modelResponseSchema) { + $p = $this->parser; + + $this->modelResponseSchema = $p->object([ + 'id' => $p->string(), + 'createdAt' => $p->dateTime()->toString(), + 'updatedAt' => $p->dateTime()->nullable()->toString(), + 'name' => $p->string(), + 'tag' => $p->string()->nullable(), + 'vaccinations' => $p->array($p->object([ + 'name' => $p->string(), + '_type' => $p->literal('vaccination')->default('vaccination'), + ], VaccinationResponse::class)->strict()), + '_type' => $p->literal('pet')->default('pet'), + ], PetResponse::class)->strict()->postParse(function (PetResponse $petResponse) { + $petResponse->_links = [ + 'read' => [ + 'href' => $this->urlGenerator->generatePath('pet_read', ['id' => $petResponse->id]), + 'templated' => false, + 'rel' => [], + 'attributes' => ['method' => 'GET'], + ], + 'update' => [ + 'href' => $this->urlGenerator->generatePath('pet_update', ['id' => $petResponse->id]), + 'templated' => false, + 'rel' => [], + 'attributes' => ['method' => 'PUT'], + ], + 'delete' => [ + 'href' => $this->urlGenerator->generatePath('pet_delete', ['id' => $petResponse->id]), + 'templated' => false, + 'rel' => [], + 'attributes' => ['method' => 'DELETE'], + ], + ]; + + return $petResponse; + }); + } + + return $this->modelResponseSchema; + } +} diff --git a/src/RequestHandler/Api/Crud/CreateRequestHandler.php b/src/RequestHandler/Api/Crud/CreateRequestHandler.php index 820d7453..7590069b 100644 --- a/src/RequestHandler/Api/Crud/CreateRequestHandler.php +++ b/src/RequestHandler/Api/Crud/CreateRequestHandler.php @@ -4,18 +4,14 @@ namespace App\RequestHandler\Api\Crud; -use App\Factory\ModelFactoryInterface; -use App\Model\ModelInterface; +use App\Dto\Model\ModelRequestInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextBuilder; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -23,11 +19,11 @@ final class CreateRequestHandler implements RequestHandlerInterface { public function __construct( - private ModelFactoryInterface $factory, + private DecoderInterface $decoder, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -35,31 +31,28 @@ public function handle(ServerRequestInterface $request): ResponseInterface $accept = $request->getAttribute('accept'); $contentType = $request->getAttribute('contentType'); - /** @var ModelInterface $model */ - $model = $this->requestManager->getDataFromRequestBody( - $request, - $this->factory->create(), - $contentType, - $this->getDenormalizerContext() - ); + $input = $this->decoder->decode((string) $request->getBody(), $contentType); - if ([] !== $errors = $this->validator->validate($model)) { - throw HttpException::createUnprocessableEntity(['invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages()]); + try { + /** @var ModelRequestInterface $modelRequest */ + $modelRequest = $this->parsing->getModelRequestSchema($request)->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createUnprocessableEntity(['invalidParameters' => $e->getApiProblemErrorMessages()]); } + $model = $modelRequest->createModel(); + $this->repository->persist($model); $this->repository->flush(); - return $this->responseManager->create($model, $accept, 201, $this->getNormalizerContext($request)); - } + $output = $this->encoder->encode( + (array) $this->parsing->getModelResponseSchema($request)->parse($model->jsonSerialize()), + $accept + ); - private function getDenormalizerContext(): DenormalizerContextInterface - { - return DenormalizerContextBuilder::create()->setClearMissing(true)->getContext(); - } + $response = $this->responseFactory->createResponse(201)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface - { - return NormalizerContextBuilder::create()->setRequest($request)->getContext(); + return $response; } } diff --git a/src/RequestHandler/Api/Crud/DeleteRequestHandler.php b/src/RequestHandler/Api/Crud/DeleteRequestHandler.php index d370e51d..0a54dd72 100644 --- a/src/RequestHandler/Api/Crud/DeleteRequestHandler.php +++ b/src/RequestHandler/Api/Crud/DeleteRequestHandler.php @@ -5,8 +5,8 @@ namespace App\RequestHandler\Api\Crud; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; use Chubbyphp\HttpException\HttpException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -16,7 +16,7 @@ final class DeleteRequestHandler implements RequestHandlerInterface { public function __construct( private RepositoryInterface $repository, - private ResponseManagerInterface $responseManager + private ResponseFactoryInterface $responseFactory ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -31,6 +31,6 @@ public function handle(ServerRequestInterface $request): ResponseInterface $this->repository->remove($model); $this->repository->flush(); - return $this->responseManager->createEmpty($accept); + return $this->responseFactory->createResponse(204)->withHeader('Content-Type', $accept); } } diff --git a/src/RequestHandler/Api/Crud/ListRequestHandler.php b/src/RequestHandler/Api/Crud/ListRequestHandler.php index 66ac1367..71eddc12 100644 --- a/src/RequestHandler/Api/Crud/ListRequestHandler.php +++ b/src/RequestHandler/Api/Crud/ListRequestHandler.php @@ -4,15 +4,13 @@ namespace App\RequestHandler\Api\Crud; -use App\Collection\CollectionInterface; -use App\Factory\CollectionFactoryInterface; +use App\Dto\Collection\CollectionRequestInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -20,30 +18,37 @@ final class ListRequestHandler implements RequestHandlerInterface { public function __construct( - private CollectionFactoryInterface $factory, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface { $accept = $request->getAttribute('accept'); - /** @var CollectionInterface $collection */ - $collection = $this->requestManager->getDataFromRequestQuery($request, $this->factory->create()); + $input = $request->getQueryParams(); - if ([] !== $errors = $this->validator->validate($collection)) { - throw HttpException::createBadRequest([ - 'invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages(), - ]); + try { + /** @var CollectionRequestInterface $collectionRequest */ + $collectionRequest = $this->parsing->getCollectionRequestSchema($request)->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createBadRequest(['invalidParameters' => $e->getApiProblemErrorMessages()]); } + $collection = $collectionRequest->createCollection(); + $this->repository->resolveCollection($collection); - $context = NormalizerContextBuilder::create()->setRequest($request)->getContext(); + $output = $this->encoder->encode( + (array) $this->parsing->getCollectionResponseSchema($request)->parse($collection->jsonSerialize()), + $accept + ); + + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - return $this->responseManager->create($collection, $accept, 200, $context); + return $response; } } diff --git a/src/RequestHandler/Api/Crud/ReadRequestHandler.php b/src/RequestHandler/Api/Crud/ReadRequestHandler.php index 3c4cc671..5d3e3bd2 100644 --- a/src/RequestHandler/Api/Crud/ReadRequestHandler.php +++ b/src/RequestHandler/Api/Crud/ReadRequestHandler.php @@ -4,11 +4,11 @@ namespace App\RequestHandler\Api\Crud; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -17,8 +17,10 @@ final class ReadRequestHandler implements RequestHandlerInterface { public function __construct( + private ParsingInterface $parsing, private RepositoryInterface $repository, - private ResponseManagerInterface $responseManager + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -30,11 +32,14 @@ public function handle(ServerRequestInterface $request): ResponseInterface throw HttpException::createNotFound(); } - return $this->responseManager->create($model, $accept, 200, $this->getNormalizerContext($request)); - } + $output = $this->encoder->encode( + (array) $this->parsing->getModelResponseSchema($request)->parse($model->jsonSerialize()), + $accept + ); - private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface - { - return NormalizerContextBuilder::create()->setRequest($request)->getContext(); + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); + + return $response; } } diff --git a/src/RequestHandler/Api/Crud/UpdateRequestHandler.php b/src/RequestHandler/Api/Crud/UpdateRequestHandler.php index 6690580f..d8580da7 100644 --- a/src/RequestHandler/Api/Crud/UpdateRequestHandler.php +++ b/src/RequestHandler/Api/Crud/UpdateRequestHandler.php @@ -4,17 +4,14 @@ namespace App\RequestHandler\Api\Crud; -use App\Model\ModelInterface; +use App\Dto\ModelRequestInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextBuilder; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpException; -use Chubbyphp\Serialization\Normalizer\NormalizerContextBuilder; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ApiProblemErrorMessages; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -23,10 +20,11 @@ final class UpdateRequestHandler implements RequestHandlerInterface { public function __construct( + private DecoderInterface $decoder, + private ParsingInterface $parsing, private RepositoryInterface $repository, - private RequestManagerInterface $requestManager, - private ResponseManagerInterface $responseManager, - private ValidatorInterface $validator + private EncoderInterface $encoder, + private ResponseFactoryInterface $responseFactory, ) {} public function handle(ServerRequestInterface $request): ResponseInterface @@ -39,39 +37,28 @@ public function handle(ServerRequestInterface $request): ResponseInterface throw HttpException::createNotFound(); } - /** @var ModelInterface $model */ - $model = $this->requestManager->getDataFromRequestBody( - $request, - $model, - $contentType, - $this->getDenormalizerContext() - ); + $input = $this->decoder->decode((string) $request->getBody(), $contentType); - if ([] !== $errors = $this->validator->validate($model)) { - throw HttpException::createUnprocessableEntity([ - 'invalidParameters' => (new ApiProblemErrorMessages($errors))->getMessages(), - ]); + try { + /** @var ModelRequestInterface $modelRequest */ + $modelRequest = $this->parsing->getModelRequestSchema($request)->parse($input); + } catch (ParserErrorException $e) { + throw HttpException::createUnprocessableEntity(['invalidParameters' => $e->getApiProblemErrorMessages()]); } - $model->setUpdatedAt(new \DateTimeImmutable()); + $model = $modelRequest->updateModel($model); $this->repository->persist($model); $this->repository->flush(); - return $this->responseManager->create($model, $accept, 200, $this->getNormalizerContext($request)); - } + $output = $this->encoder->encode( + (array) $this->parsing->getModelResponseSchema($request)->parse($model->jsonSerialize()), + $accept + ); - private function getDenormalizerContext(): DenormalizerContextInterface - { - return DenormalizerContextBuilder::create() - ->setAllowedAdditionalFields(['id', 'createdAt', 'updatedAt', '_links']) - ->setClearMissing(true) - ->getContext() - ; - } + $response = $this->responseFactory->createResponse(200)->withHeader('Content-Type', $accept); + $response->getBody()->write($output); - private function getNormalizerContext(ServerRequestInterface $request): NormalizerContextInterface - { - return NormalizerContextBuilder::create()->setRequest($request)->getContext(); + return $response; } } diff --git a/src/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactory.php b/src/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactory.php deleted file mode 100644 index 8952d99c..00000000 --- a/src/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactory.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public function __invoke(ContainerInterface $container): array - { - /** @var DenormalizationFieldMappingFactoryInterface $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->resolveDependency($container, DenormalizationFieldMappingFactoryInterface::class, DenormalizationFieldMappingFactoryFactory::class); - - return [ - new PetCollectionMapping($denormalizationFieldMappingFactory), - new PetMapping($denormalizationFieldMappingFactory), - new VaccinationMapping($denormalizationFieldMappingFactory), - ]; - } -} diff --git a/src/ServiceFactory/Factory/Collection/PetCollectionFactoryFactory.php b/src/ServiceFactory/Factory/Collection/PetCollectionFactoryFactory.php deleted file mode 100644 index f8872b82..00000000 --- a/src/ServiceFactory/Factory/Collection/PetCollectionFactoryFactory.php +++ /dev/null @@ -1,15 +0,0 @@ -getRoutes() ); } diff --git a/src/ServiceFactory/Middleware/ApiExceptionMiddlewareFactory.php b/src/ServiceFactory/Middleware/ApiExceptionMiddlewareFactory.php new file mode 100644 index 00000000..e529486d --- /dev/null +++ b/src/ServiceFactory/Middleware/ApiExceptionMiddlewareFactory.php @@ -0,0 +1,29 @@ + + */ + public function __invoke(ContainerInterface $container): ApiExceptionMiddleware + { + $debug = $container->get('config')['debug']; + + return new ApiExceptionMiddleware( + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), + $debug, + $container->get(LoggerInterface::class), + ); + } +} diff --git a/src/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactory.php b/src/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactory.php index 1be6f5fc..79930df7 100644 --- a/src/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactory.php +++ b/src/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactory.php @@ -5,20 +5,15 @@ namespace App\ServiceFactory\Negotiation; use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; -use Chubbyphp\DecodeEncode\ServiceFactory\EncoderFactory; -use Chubbyphp\Laminas\Config\Factory\AbstractFactory; use Psr\Container\ContainerInterface; -final class AcceptNegotiatorSupportedMediaTypesFactory extends AbstractFactory +final class AcceptNegotiatorSupportedMediaTypesFactory { /** * @return array */ public function __invoke(ContainerInterface $container): array { - /** @var EncoderInterface $encoder */ - $encoder = $this->resolveDependency($container, EncoderInterface::class, EncoderFactory::class); - - return $encoder->getContentTypes(); + return $container->get(EncoderInterface::class)->getContentTypes(); } } diff --git a/src/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactory.php b/src/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactory.php index 876ac6b1..f66ae1b0 100644 --- a/src/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactory.php +++ b/src/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactory.php @@ -5,20 +5,15 @@ namespace App\ServiceFactory\Negotiation; use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; -use Chubbyphp\DecodeEncode\ServiceFactory\DecoderFactory; -use Chubbyphp\Laminas\Config\Factory\AbstractFactory; use Psr\Container\ContainerInterface; -final class ContentTypeNegotiatorSupportedMediaTypesFactory extends AbstractFactory +final class ContentTypeNegotiatorSupportedMediaTypesFactory { /** * @return array */ public function __invoke(ContainerInterface $container): array { - /** @var DecoderInterface $decoder */ - $decoder = $this->resolveDependency($container, DecoderInterface::class, DecoderFactory::class); - - return $decoder->getContentTypes(); + return $container->get(DecoderInterface::class)->getContentTypes(); } } diff --git a/src/ServiceFactory/Parsing/ParserFactory.php b/src/ServiceFactory/Parsing/ParserFactory.php new file mode 100644 index 00000000..6c918d66 --- /dev/null +++ b/src/ServiceFactory/Parsing/ParserFactory.php @@ -0,0 +1,16 @@ +get(ParserInterface::class), + $container->get(UrlGeneratorInterface::class), + ); + } +} diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php index 1e83dc35..8e48b619 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactory.php @@ -4,24 +4,24 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Model\PetFactory; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\CreateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetCreateRequestHandlerFactory { public function __invoke(ContainerInterface $container): CreateRequestHandler { return new CreateRequestHandler( - $container->get(PetFactory::class), + $container->get(DecoderInterface::class), + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactory.php index 70322369..ee64861f 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactory.php @@ -6,8 +6,8 @@ use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\DeleteRequestHandler; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetDeleteRequestHandlerFactory { @@ -15,7 +15,7 @@ public function __invoke(ContainerInterface $container): DeleteRequestHandler { return new DeleteRequestHandler( $container->get(PetRepository::class), - $container->get(ResponseManagerInterface::class) + $container->get(ResponseFactoryInterface::class) ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php index ec68b929..b674c286 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactory.php @@ -4,24 +4,22 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Collection\PetCollectionFactory; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\ListRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetListRequestHandlerFactory { public function __invoke(ContainerInterface $container): ListRequestHandler { return new ListRequestHandler( - $container->get(PetCollectionFactory::class), + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php index 4ce4666c..215d07c6 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactory.php @@ -4,18 +4,22 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\ReadRequestHandler; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetReadRequestHandlerFactory { public function __invoke(ContainerInterface $container): ReadRequestHandler { return new ReadRequestHandler( + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(ResponseManagerInterface::class) + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php b/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php index b092ab8e..26e7db0b 100644 --- a/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php +++ b/src/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactory.php @@ -4,22 +4,24 @@ namespace App\ServiceFactory\RequestHandler\Api\Crud; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\RequestHandler\Api\Crud\UpdateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; final class PetUpdateRequestHandlerFactory { public function __invoke(ContainerInterface $container): UpdateRequestHandler { return new UpdateRequestHandler( + $container->get(DecoderInterface::class), + $container->get(PetParsing::class), $container->get(PetRepository::class), - $container->get(RequestManagerInterface::class), - $container->get(ResponseManagerInterface::class), - $container->get(ValidatorInterface::class) + $container->get(EncoderInterface::class), + $container->get(ResponseFactoryInterface::class), ); } } diff --git a/src/ServiceFactory/Serialization/NormalizationObjectMappingsFactory.php b/src/ServiceFactory/Serialization/NormalizationObjectMappingsFactory.php deleted file mode 100644 index 20084d9c..00000000 --- a/src/ServiceFactory/Serialization/NormalizationObjectMappingsFactory.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ - public function __invoke(ContainerInterface $container): array - { - /** @var UrlGeneratorInterface $urlGenerator */ - $urlGenerator = $container->get(UrlGeneratorInterface::class); - - return [ - new PetCollectionMapping($urlGenerator), - new PetMapping($urlGenerator), - new VaccinationMapping(), - ]; - } -} diff --git a/src/ServiceFactory/Validation/ValidationMappingProviderFactory.php b/src/ServiceFactory/Validation/ValidationMappingProviderFactory.php deleted file mode 100644 index 25d1e67d..00000000 --- a/src/ServiceFactory/Validation/ValidationMappingProviderFactory.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public function __invoke(): array - { - return [ - new PetCollectionMapping(), - new PetMapping(), - new VaccinationMapping(), - ]; - } -} diff --git a/tests/Integration/PetCrudRequestHandlerTest.php b/tests/Integration/PetCrudRequestHandlerTest.php index 3eba84d3..df247c02 100644 --- a/tests/Integration/PetCrudRequestHandlerTest.php +++ b/tests/Integration/PetCrudRequestHandlerTest.php @@ -23,25 +23,9 @@ public function testCreateWithUnsupportedAccept(): void self::assertSame(406, $response['status']['code'], $response['body'] ?? ''); - self::assertSame('application/problem+json', $response['headers']['content-type'][0]); - - $apiProblem = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR); + self::assertSame('text/html;charset=utf-8', $response['headers']['content-type'][0]); - self::assertEquals([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.7', - 'status' => 406, - 'title' => 'Not Acceptable', - 'detail' => 'Not supported accept, supportedValues: "application/json", application/jsonx+xml", application/x-www-form-urlencoded", application/x-yaml"', - 'instance' => null, - 'value' => 'text/html', - 'supportedValues' => [ - 'application/json', - 'application/jsonx+xml', - 'application/x-www-form-urlencoded', - 'application/x-yaml', - ], - '_type' => 'apiProblem', - ], $apiProblem); + self::assertMatchesRegularExpression('/Not supported accept/', $response['body']); } public function testCreateWithUnsupportedContentType(): void @@ -105,8 +89,12 @@ public function testCreateWithValidationError(): void 'invalidParameters' => [ [ 'name' => 'name', - 'reason' => 'constraint.notblank.blank', - 'details' => [], + 'reason' => 'Min length {{min}}, 0 given', + 'details' => [ + '_template' => 'Min length {{min}}, {{given}} given', + 'minLength' => 1, + 'given' => 0, + ], ], ], '_type' => 'apiProblem', @@ -136,25 +124,9 @@ public function testListWithUnsupportedAccept(): void self::assertSame(406, $response['status']['code'], $response['body'] ?? ''); - self::assertSame('application/problem+json', $response['headers']['content-type'][0]); + self::assertSame('text/html;charset=utf-8', $response['headers']['content-type'][0]); - $apiProblem = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR); - - self::assertEquals([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.7', - 'status' => 406, - 'title' => 'Not Acceptable', - 'detail' => 'Not supported accept, supportedValues: "application/json", application/jsonx+xml", application/x-www-form-urlencoded", application/x-yaml"', - 'instance' => null, - 'value' => 'text/html', - 'supportedValues' => [ - 'application/json', - 'application/jsonx+xml', - 'application/x-www-form-urlencoded', - 'application/x-yaml', - ], - '_type' => 'apiProblem', - ], $apiProblem); + self::assertMatchesRegularExpression('/Not supported accept/', $response['body']); } public function testListWithValidationError(): void @@ -181,20 +153,29 @@ public function testListWithValidationError(): void 'instance' => null, 'invalidParameters' => [ [ - 'name' => 'filters.name2', - 'reason' => 'constraint.map.field.notallowed', + 'name' => 'filters[name2]', + 'reason' => 'Unknown field "name2"', 'details' => [ - 'field' => 'name2', - 'allowedFields' => ['name'], + '_template' => 'Unknown field {{fieldName}}', + 'fieldName' => 'name2', ], ], [ - 'name' => 'sort.name', - 'reason' => 'constraint.sort.order.notallowed', + 'name' => 'sort[name]', + 'reason' => 'Input should be "asc", "test" given', 'details' => [ - 'field' => 'name', - 'order' => 'test', - 'allowedOrders' => ['asc', 'desc'], + '_template' => 'Input should be {{expected}}, {{given}} given', + 'expected' => 'asc', + 'given' => 'test', + ], + ], + [ + 'name' => 'sort[name]', + 'reason' => 'Input should be "desc", "test" given', + 'details' => [ + '_template' => 'Input should be {{expected}}, {{given}} given', + 'expected' => 'desc', + 'given' => 'test', ], ], ], @@ -225,8 +206,7 @@ public function testList(): void self::assertArrayHasKey('filters', $petCollection); self::assertArrayHasKey('sort', $petCollection); self::assertArrayHasKey('count', $petCollection); - self::assertArrayHasKey('_embedded', $petCollection); - self::assertArrayHasKey('items', $petCollection['_embedded']); + self::assertArrayHasKey('items', $petCollection); self::assertArrayHasKey('_links', $petCollection); self::assertArrayHasKey('list', $petCollection['_links']); self::assertArrayHasKey('create', $petCollection['_links']); @@ -236,12 +216,12 @@ public function testList(): void self::assertSame(20, $petCollection['limit']); self::assertSame(['name' => 'desc'], $petCollection['sort']); self::assertGreaterThanOrEqual(1, $petCollection['count']); - self::assertGreaterThanOrEqual(1, is_countable($petCollection['_embedded']['items']) ? \count($petCollection['_embedded']['items']) : 0); - self::assertSame($petCollection['count'], is_countable($petCollection['_embedded']['items']) ? \count($petCollection['_embedded']['items']) : 0); + self::assertGreaterThanOrEqual(1, is_countable($petCollection['items']) ? \count($petCollection['items']) : 0); + self::assertSame($petCollection['count'], is_countable($petCollection['items']) ? \count($petCollection['items']) : 0); $found = false; - foreach ($petCollection['_embedded']['items'] as $item) { + foreach ($petCollection['items'] as $item) { if ($item['id'] === $pet['id']) { $this::assertPet( $item, @@ -285,25 +265,9 @@ public function testReadWithUnsupportedAccept(): void self::assertSame(406, $response['status']['code'], $response['body'] ?? ''); - self::assertSame('application/problem+json', $response['headers']['content-type'][0]); + self::assertSame('text/html;charset=utf-8', $response['headers']['content-type'][0]); - $apiProblem = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR); - - self::assertEquals([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.7', - 'status' => 406, - 'title' => 'Not Acceptable', - 'detail' => 'Not supported accept, supportedValues: "application/json", application/jsonx+xml", application/x-www-form-urlencoded", application/x-yaml"', - 'instance' => null, - 'value' => 'text/html', - 'supportedValues' => [ - 'application/json', - 'application/jsonx+xml', - 'application/x-www-form-urlencoded', - 'application/x-yaml', - ], - '_type' => 'apiProblem', - ], $apiProblem); + self::assertMatchesRegularExpression('/Not supported accept/', $response['body']); } public function testReadWithNotFound(): void @@ -369,25 +333,9 @@ public function testUpdateWithUnsupportedAccept(): void self::assertSame(406, $response['status']['code'], $response['body'] ?? ''); - self::assertSame('application/problem+json', $response['headers']['content-type'][0]); - - $apiProblem = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR); + self::assertSame('text/html;charset=utf-8', $response['headers']['content-type'][0]); - self::assertEquals([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.7', - 'status' => 406, - 'title' => 'Not Acceptable', - 'detail' => 'Not supported accept, supportedValues: "application/json", application/jsonx+xml", application/x-www-form-urlencoded", application/x-yaml"', - 'instance' => null, - 'value' => 'text/html', - 'supportedValues' => [ - 'application/json', - 'application/jsonx+xml', - 'application/x-www-form-urlencoded', - 'application/x-yaml', - ], - '_type' => 'apiProblem', - ], $apiProblem); + self::assertMatchesRegularExpression('/Not supported accept/', $response['body']); } public function testUpdateWithUnsupportedContentType(): void @@ -480,8 +428,12 @@ public function testUpdateWithValidationError(): void 'invalidParameters' => [ [ 'name' => 'name', - 'reason' => 'constraint.notblank.blank', - 'details' => [], + 'reason' => 'Min length {{min}}, 0 given', + 'details' => [ + '_template' => 'Min length {{min}}, {{given}} given', + 'minLength' => 1, + 'given' => 0, + ], ], ], '_type' => 'apiProblem', @@ -552,25 +504,9 @@ public function testDeleteWithUnsupportedAccept(): void self::assertSame(406, $response['status']['code'], $response['body'] ?? ''); - self::assertSame('application/problem+json', $response['headers']['content-type'][0]); - - $apiProblem = json_decode($response['body'], true, 512, JSON_THROW_ON_ERROR); + self::assertSame('text/html;charset=utf-8', $response['headers']['content-type'][0]); - self::assertEquals([ - 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.7', - 'status' => 406, - 'title' => 'Not Acceptable', - 'detail' => 'Not supported accept, supportedValues: "application/json", application/jsonx+xml", application/x-www-form-urlencoded", application/x-yaml"', - 'instance' => null, - 'value' => 'text/html', - 'supportedValues' => [ - 'application/json', - 'application/jsonx+xml', - 'application/x-www-form-urlencoded', - 'application/x-yaml', - ], - '_type' => 'apiProblem', - ], $apiProblem); + self::assertMatchesRegularExpression('/Not supported accept/', $response['body']); } public function testDeleteWithNotFound(): void diff --git a/tests/Unit/Collection/CollectionTest.php b/tests/Unit/Collection/CollectionTest.php index 6a4137ab..a6f0ef47 100644 --- a/tests/Unit/Collection/CollectionTest.php +++ b/tests/Unit/Collection/CollectionTest.php @@ -6,6 +6,10 @@ use App\Collection\AbstractCollection; use App\Collection\CollectionInterface; +use App\Model\ModelInterface; +use Chubbyphp\Mock\Call; +use Chubbyphp\Mock\MockByCallsTrait; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** @@ -15,6 +19,8 @@ */ class CollectionTest extends TestCase { + use MockByCallsTrait; + public function testGetSet(): void { $collection = $this->getCollection(); @@ -26,21 +32,40 @@ public function testGetSet(): void self::assertSame(0, $collection->getCount()); self::assertSame([], $collection->getItems()); - $object = new \stdClass(); + /** @var MockObject|ModelInterface $model */ + $model = $this->getMockByCalls(ModelInterface::class, [ + Call::create('jsonSerialize')->with()->willReturn(['id' => '111d1691-8486-4447-997c-d10ce35d1fea']), + ]); $collection->setOffset(5); $collection->setLimit(15); $collection->setFilters(['name' => 'sample']); $collection->setSort(['name' => 'asc']); $collection->setCount(6); - $collection->setItems([$object]); + $collection->setItems([$model]); self::assertSame(5, $collection->getOffset()); self::assertSame(15, $collection->getLimit()); self::assertSame(['name' => 'sample'], $collection->getFilters()); self::assertSame(['name' => 'asc'], $collection->getSort()); self::assertSame(6, $collection->getCount()); - self::assertSame([$object], $collection->getItems()); + self::assertSame([$model], $collection->getItems()); + self::assertSame([ + 'offset' => 5, + 'limit' => 15, + 'filters' => [ + 'name' => 'sample', + ], + 'sort' => [ + 'name' => 'asc', + ], + 'items' => [ + 0 => [ + 'id' => '111d1691-8486-4447-997c-d10ce35d1fea', + ], + ], + 'count' => 6, + ], $collection->jsonSerialize()); } protected function getCollection(): CollectionInterface diff --git a/tests/Unit/Factory/Collection/PetCollectionFactoryTest.php b/tests/Unit/Factory/Collection/PetCollectionFactoryTest.php deleted file mode 100644 index b94e33a5..00000000 --- a/tests/Unit/Factory/Collection/PetCollectionFactoryTest.php +++ /dev/null @@ -1,24 +0,0 @@ -create()); - } -} diff --git a/tests/Unit/Factory/Model/PetFactoryTest.php b/tests/Unit/Factory/Model/PetFactoryTest.php deleted file mode 100644 index d98f4c0e..00000000 --- a/tests/Unit/Factory/Model/PetFactoryTest.php +++ /dev/null @@ -1,31 +0,0 @@ -create()); - } - - public function testGetClass(): void - { - $factory = new PetFactory(); - - self::assertSame(Pet::class, $factory->getClass()); - } -} diff --git a/tests/Unit/Mapping/Deserialization/PetCollectionMappingTest.php b/tests/Unit/Mapping/Deserialization/PetCollectionMappingTest.php deleted file mode 100644 index 776efc9a..00000000 --- a/tests/Unit/Mapping/Deserialization/PetCollectionMappingTest.php +++ /dev/null @@ -1,80 +0,0 @@ -getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new PetCollectionMapping($denormalizationFieldMappingFactory); - - self::assertSame(PetCollection::class, $mapping->getClass()); - } - - public function testGetDenormalizationFactory(): void - { - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new PetCollectionMapping($denormalizationFieldMappingFactory); - - $factory = $mapping->getDenormalizationFactory('/', 'petCollection'); - - self::assertInstanceOf(\Closure::class, $factory); - - self::assertInstanceOf(PetCollection::class, $factory()); - } - - public function testGetDenormalizationFieldMappings(): void - { - /** @var DenormalizationFieldMappingInterface|MockObject $offsetDenormalizationFieldMapping */ - $offsetDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingInterface|MockObject $limitDenormalizationFieldMapping */ - $limitDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingInterface|MockObject $filtersDenormalizationFieldMapping */ - $filtersDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingInterface|MockObject $sortDenormalizationFieldMapping */ - $sortDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class, [ - Call::create('createConvertType')->with('offset', ConvertTypeFieldDenormalizer::TYPE_INT, false, null)->willReturn($offsetDenormalizationFieldMapping), - Call::create('createConvertType')->with('limit', ConvertTypeFieldDenormalizer::TYPE_INT, false, null)->willReturn($limitDenormalizationFieldMapping), - Call::create('create')->with('filters', false, null, null)->willReturn($filtersDenormalizationFieldMapping), - Call::create('create')->with('sort', false, null, null)->willReturn($sortDenormalizationFieldMapping), - ]); - - $mapping = new PetCollectionMapping($denormalizationFieldMappingFactory); - - $fieldMappings = $mapping->getDenormalizationFieldMappings('/', 'petCollection'); - - self::assertSame($offsetDenormalizationFieldMapping, array_shift($fieldMappings)); - self::assertSame($limitDenormalizationFieldMapping, array_shift($fieldMappings)); - self::assertSame($filtersDenormalizationFieldMapping, array_shift($fieldMappings)); - self::assertSame($sortDenormalizationFieldMapping, array_shift($fieldMappings)); - } -} diff --git a/tests/Unit/Mapping/Deserialization/PetMappingTest.php b/tests/Unit/Mapping/Deserialization/PetMappingTest.php deleted file mode 100644 index f315c013..00000000 --- a/tests/Unit/Mapping/Deserialization/PetMappingTest.php +++ /dev/null @@ -1,72 +0,0 @@ -getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new PetMapping($denormalizationFieldMappingFactory); - - self::assertSame(Pet::class, $mapping->getClass()); - } - - public function testGetDenormalizationFactory(): void - { - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new PetMapping($denormalizationFieldMappingFactory); - - $factory = $mapping->getDenormalizationFactory('/', 'pet'); - - self::assertInstanceOf(\Closure::class, $factory); - - self::assertInstanceOf(Pet::class, $factory()); - } - - public function testGetDenormalizationFieldMappings(): void - { - /** @var DenormalizationFieldMappingInterface|MockObject $nameDenormalizationFieldMapping */ - $nameDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingInterface|MockObject $tagDenormalizationFieldMapping */ - $tagDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingInterface|MockObject $vaccinationsDenormalizationFieldMapping */ - $vaccinationsDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class, [ - Call::create('createConvertType')->with('name', ConvertTypeFieldDenormalizer::TYPE_STRING, false, null)->willReturn($nameDenormalizationFieldMapping), - Call::create('createConvertType')->with('tag', ConvertTypeFieldDenormalizer::TYPE_STRING, false, null)->willReturn($tagDenormalizationFieldMapping), - Call::create('create')->with('vaccinations', false, new ArgumentInstanceOf(EmbedManyFieldDenormalizer::class), null)->willReturn($vaccinationsDenormalizationFieldMapping), - ]); - - $mapping = new PetMapping($denormalizationFieldMappingFactory); - $mapping->getDenormalizationFieldMappings('/', 'pet'); - } -} diff --git a/tests/Unit/Mapping/Deserialization/VaccinationMappingTest.php b/tests/Unit/Mapping/Deserialization/VaccinationMappingTest.php deleted file mode 100644 index 0d90d15d..00000000 --- a/tests/Unit/Mapping/Deserialization/VaccinationMappingTest.php +++ /dev/null @@ -1,62 +0,0 @@ -getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new VaccinationMapping($denormalizationFieldMappingFactory); - - self::assertSame(Vaccination::class, $mapping->getClass()); - } - - public function testGetDenormalizationFactory(): void - { - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - $mapping = new VaccinationMapping($denormalizationFieldMappingFactory); - - $factory = $mapping->getDenormalizationFactory('/', 'vaccination'); - - self::assertInstanceOf(\Closure::class, $factory); - - self::assertInstanceOf(Vaccination::class, $factory()); - } - - public function testGetDenormalizationFieldMappings(): void - { - /** @var DenormalizationFieldMappingInterface|MockObject $nameDenormalizationFieldMapping */ - $nameDenormalizationFieldMapping = $this->getMockByCalls(DenormalizationFieldMappingInterface::class); - - /** @var DenormalizationFieldMappingFactoryInterface|MockObject $denormalizationFieldMappingFactory */ - $denormalizationFieldMappingFactory = $this->getMockByCalls(DenormalizationFieldMappingFactoryInterface::class, [ - Call::create('createConvertType')->with('name', ConvertTypeFieldDenormalizer::TYPE_STRING, false, null)->willReturn($nameDenormalizationFieldMapping), - ]); - - $mapping = new VaccinationMapping($denormalizationFieldMappingFactory); - $mapping->getDenormalizationFieldMappings('/', 'vaccination'); - } -} diff --git a/tests/Unit/Mapping/Serialization/CollectionMappingTest.php b/tests/Unit/Mapping/Serialization/CollectionMappingTest.php deleted file mode 100644 index 78b1904b..00000000 --- a/tests/Unit/Mapping/Serialization/CollectionMappingTest.php +++ /dev/null @@ -1,207 +0,0 @@ -getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getCollectionMapping($router); - - self::assertSame($this->getClass(), $mapping->getClass()); - } - - public function testGetNormalizationType(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getCollectionMapping($router); - - self::assertSame($this->getNormalizationType(), $mapping->getNormalizationType()); - } - - public function testGetNormalizationFieldMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getCollectionMapping($router); - - $fieldMappings = $mapping->getNormalizationFieldMappings('/'); - - self::assertEquals($this->getNormalizationFieldMappings('/'), $fieldMappings); - } - - public function testGetNormalizationEmbeddedFieldMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getCollectionMapping($router); - - $fieldMappings = $mapping->getNormalizationEmbeddedFieldMappings('/'); - - self::assertEquals([ - NormalizationFieldMappingBuilder::createEmbedMany('items')->getMapping(), - ], $fieldMappings); - } - - public function testGetNormalizationLinkMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class, [ - Call::create('generatePath') - ->with($this->getListRoute(), [], ['key' => 'value', 'offset' => 0, 'limit' => 20]) - ->willReturn(sprintf('%s?offset=0&limit=20', $this->getCollectionPath())), - Call::create('generatePath') - ->with($this->getCreateRoute(), [], []) - ->willReturn($this->getCollectionPath()), - ]); - - $mapping = $this->getCollectionMapping($router); - - $linkMappings = $mapping->getNormalizationLinkMappings('/'); - - self::assertCount(2, $linkMappings); - - self::assertInstanceOf(NormalizationLinkMappingInterface::class, $linkMappings[0]); - self::assertInstanceOf(NormalizationLinkMappingInterface::class, $linkMappings[1]); - - $object = new class() extends AbstractCollection {}; - - $object->setOffset(0); - $object->setLimit(20); - $object->setCount(25); - - /** @var MockObject|ServerRequestInterface $request */ - $request = $this->getMockByCalls(ServerRequestInterface::class, [ - Call::create('getQueryParams')->with()->willReturn(['key' => 'value']), - ]); - - /** @var MockObject|NormalizerContextInterface $context */ - $context = $this->getMockByCalls(NormalizerContextInterface::class, [ - Call::create('getRequest')->with()->willReturn($request), - ]); - - $list = $linkMappings[0]->getLinkNormalizer()->normalizeLink('/', $object, $context); - $create = $linkMappings[1]->getLinkNormalizer()->normalizeLink('/', $object, $context); - - self::assertSame([ - 'href' => sprintf('%s?offset=0&limit=20', $this->getCollectionPath()), - 'templated' => false, - 'rel' => [], - 'attributes' => [ - 'method' => 'GET', - ], - ], $list); - - self::assertSame([ - 'href' => $this->getCollectionPath(), - 'templated' => false, - 'rel' => [], - 'attributes' => [ - 'method' => 'POST', - ], - ], $create); - } - - /** - * @return NormalizationFieldMappingInterface[] - */ - protected function getNormalizationFieldMappings(string $path): array - { - return [ - NormalizationFieldMappingBuilder::create('offset')->getMapping(), - NormalizationFieldMappingBuilder::create('limit')->getMapping(), - NormalizationFieldMappingBuilder::create('count')->getMapping(), - NormalizationFieldMappingBuilder::create('filters')->getMapping(), - NormalizationFieldMappingBuilder::create('sort')->getMapping(), - ]; - } - - protected function getClass(): string - { - return CollectionInterface::class; - } - - protected function getNormalizationType(): string - { - return 'collection'; - } - - protected function getListRoute(): string - { - return 'collection_list'; - } - - protected function getCreateRoute(): string - { - return 'collection_create'; - } - - protected function getCollectionPath(): string - { - return '/api/collection'; - } - - protected function getCollectionMapping(UrlGeneratorInterface $router): AbstractCollectionMapping - { - return new class($router, $this->getClass(), $this->getNormalizationType(), $this->getListRoute(), $this->getCreateRoute()) extends AbstractCollectionMapping { - public function __construct( - UrlGeneratorInterface $router, - private string $class, - private string $normalizationType, - private string $listRouteName, - private string $createRouteName - ) { - parent::__construct($router); - } - - public function getClass(): string - { - return $this->class; - } - - public function getNormalizationType(): string - { - return $this->normalizationType; - } - - protected function getListRouteName(): string - { - return $this->listRouteName; - } - - protected function getCreateRouteName(): string - { - return $this->createRouteName; - } - }; - } -} diff --git a/tests/Unit/Mapping/Serialization/ModelMappingTest.php b/tests/Unit/Mapping/Serialization/ModelMappingTest.php deleted file mode 100644 index 12caa806..00000000 --- a/tests/Unit/Mapping/Serialization/ModelMappingTest.php +++ /dev/null @@ -1,212 +0,0 @@ -getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getModelMapping($router); - - self::assertSame($this->getClass(), $mapping->getClass()); - } - - public function testGetNormalizationType(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getModelMapping($router); - - self::assertSame($this->getNormalizationType(), $mapping->getNormalizationType()); - } - - public function testGetNormalizationFieldMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getModelMapping($router); - - $fieldMappings = $mapping->getNormalizationFieldMappings('/'); - - self::assertEquals([ - NormalizationFieldMappingBuilder::create('id')->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('createdAt', \DateTime::ATOM)->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('updatedAt', \DateTime::ATOM)->getMapping(), - ], $fieldMappings); - } - - public function testGetNormalizationEmbeddedFieldMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getModelMapping($router); - - $fieldMappings = $mapping->getNormalizationEmbeddedFieldMappings('/'); - - self::assertEquals([], $fieldMappings); - } - - public function testGetNormalizationLinkMappings(): void - { - /** @var MockObject|UrlGeneratorInterface $router */ - $router = $this->getMockByCalls(UrlGeneratorInterface::class, [ - Call::create('generatePath') - ->with($this->getReadRoute(), ['id' => 'f183c7ff-7683-451e-807c-b916d9b5cf86'], []) - ->willReturn(sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86')), - Call::create('generatePath') - ->with($this->getUpdateRoute(), ['id' => 'f183c7ff-7683-451e-807c-b916d9b5cf86'], []) - ->willReturn(sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86')), - Call::create('generatePath') - ->with($this->getDeleteRoute(), ['id' => 'f183c7ff-7683-451e-807c-b916d9b5cf86'], []) - ->willReturn(sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86')), - ]); - - $mapping = $this->getModelMapping($router); - - $linkMappings = $mapping->getNormalizationLinkMappings('/'); - - self::assertCount(3, $linkMappings); - - self::assertInstanceOf(NormalizationLinkMappingInterface::class, $linkMappings[0]); - self::assertInstanceOf(NormalizationLinkMappingInterface::class, $linkMappings[1]); - self::assertInstanceOf(NormalizationLinkMappingInterface::class, $linkMappings[2]); - - /** @var MockObject|ModelInterface $model */ - $model = $this->getMockByCalls(ModelInterface::class, [ - Call::create('getId')->with()->willReturn('f183c7ff-7683-451e-807c-b916d9b5cf86'), - Call::create('getId')->with()->willReturn('f183c7ff-7683-451e-807c-b916d9b5cf86'), - Call::create('getId')->with()->willReturn('f183c7ff-7683-451e-807c-b916d9b5cf86'), - ]); - - /** @var MockObject|NormalizerContextInterface $context */ - $context = $this->getMockByCalls(NormalizerContextInterface::class); - - $read = $linkMappings[0]->getLinkNormalizer()->normalizeLink('/', $model, $context); - $update = $linkMappings[1]->getLinkNormalizer()->normalizeLink('/', $model, $context); - $delete = $linkMappings[2]->getLinkNormalizer()->normalizeLink('/', $model, $context); - - self::assertSame([ - 'href' => sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86'), - 'templated' => false, - 'rel' => [], - 'attributes' => [ - 'method' => 'GET', - ], - ], $read); - - self::assertSame([ - 'href' => sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86'), - 'templated' => false, - 'rel' => [], - 'attributes' => [ - 'method' => 'PUT', - ], - ], $update); - - self::assertSame([ - 'href' => sprintf($this->getModelPath(), 'f183c7ff-7683-451e-807c-b916d9b5cf86'), - 'templated' => false, - 'rel' => [], - 'attributes' => [ - 'method' => 'DELETE', - ], - ], $delete); - } - - protected function getClass(): string - { - return ModelInterface::class; - } - - protected function getNormalizationType(): string - { - return 'model'; - } - - protected function getReadRoute(): string - { - return 'model_read'; - } - - protected function getUpdateRoute(): string - { - return 'model_update'; - } - - protected function getDeleteRoute(): string - { - return 'model_delete'; - } - - protected function getModelPath(): string - { - return '/api/collection/%s'; - } - - protected function getModelMapping(UrlGeneratorInterface $router): AbstractModelMapping - { - return new class($router, $this->getClass(), $this->getNormalizationType(), $this->getReadRoute(), $this->getUpdateRoute(), $this->getDeleteRoute()) extends AbstractModelMapping { - public function __construct( - UrlGeneratorInterface $router, - private string $class, - private string $normalizationType, - private string $readRouteName, - private string $updateRouteName, - private string $deleteRouteName - ) { - parent::__construct($router); - } - - public function getClass(): string - { - return $this->class; - } - - public function getNormalizationType(): string - { - return $this->normalizationType; - } - - protected function getReadRouteName(): string - { - return $this->readRouteName; - } - - protected function getUpdateRouteName(): string - { - return $this->updateRouteName; - } - - protected function getDeleteRouteName(): string - { - return $this->deleteRouteName; - } - }; - } -} diff --git a/tests/Unit/Mapping/Serialization/PetCollectionMappingTest.php b/tests/Unit/Mapping/Serialization/PetCollectionMappingTest.php deleted file mode 100644 index 7ccc8269..00000000 --- a/tests/Unit/Mapping/Serialization/PetCollectionMappingTest.php +++ /dev/null @@ -1,48 +0,0 @@ -getMockByCalls(UrlGeneratorInterface::class); - - $mapping = $this->getModelMapping($router); - - $fieldMappings = $mapping->getNormalizationFieldMappings('/'); - - self::assertEquals([ - NormalizationFieldMappingBuilder::create('id')->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('createdAt', \DateTime::ATOM)->getMapping(), - NormalizationFieldMappingBuilder::createDateTime('updatedAt', \DateTime::ATOM)->getMapping(), - NormalizationFieldMappingBuilder::create('name')->getMapping(), - NormalizationFieldMappingBuilder::create('tag')->getMapping(), - NormalizationFieldMappingBuilder::createEmbedMany('vaccinations')->getMapping(), - ], $fieldMappings); - } - - protected function getClass(): string - { - return Pet::class; - } - - protected function getNormalizationType(): string - { - return 'pet'; - } - - protected function getReadRoute(): string - { - return 'pet_read'; - } - - protected function getUpdateRoute(): string - { - return 'pet_update'; - } - - protected function getDeleteRoute(): string - { - return 'pet_delete'; - } - - protected function getModelPath(): string - { - return '/api/pets/%s'; - } - - protected function getModelMapping(UrlGeneratorInterface $router): AbstractModelMapping - { - return new PetMapping($router); - } -} diff --git a/tests/Unit/Mapping/Serialization/VaccinationMappingTest.php b/tests/Unit/Mapping/Serialization/VaccinationMappingTest.php deleted file mode 100644 index 4d1c3834..00000000 --- a/tests/Unit/Mapping/Serialization/VaccinationMappingTest.php +++ /dev/null @@ -1,61 +0,0 @@ -getClass()); - } - - public function testGetType(): void - { - $mapping = new VaccinationMapping(); - - self::assertSame('vaccination', $mapping->getNormalizationType()); - } - - public function testGetNormalizationFieldMappings(): void - { - $mapping = new VaccinationMapping(); - - $fieldMappings = $mapping->getNormalizationFieldMappings('/'); - - self::assertEquals([ - NormalizationFieldMappingBuilder::create('name')->getMapping(), - ], $fieldMappings); - } - - public function testGetNormalizationEmbeddedFieldMappings(): void - { - $mapping = new VaccinationMapping(); - - $embeddedFieldMappings = $mapping->getNormalizationEmbeddedFieldMappings('/'); - - self::assertEquals([], $embeddedFieldMappings); - } - - public function testGetNormalizationLinkMappings(): void - { - $mapping = new VaccinationMapping(); - - $linkMappings = $mapping->getNormalizationLinkMappings('/'); - - self::assertEquals([], $linkMappings); - } -} diff --git a/tests/Unit/Mapping/Validation/PetCollectionMappingTest.php b/tests/Unit/Mapping/Validation/PetCollectionMappingTest.php deleted file mode 100644 index 11a8788f..00000000 --- a/tests/Unit/Mapping/Validation/PetCollectionMappingTest.php +++ /dev/null @@ -1,65 +0,0 @@ -getClass()); - } - - public function testGetValidationClassMapping(): void - { - $mapping = new PetCollectionMapping(); - - self::assertNull($mapping->getValidationClassMapping('/path')); - } - - public function testGetValidationPropertyMappings(): void - { - $mapping = new PetCollectionMapping(); - - $propertyMappings = $mapping->getValidationPropertyMappings('/path'); - - self::assertEquals([ - ValidationPropertyMappingBuilder::create('offset', [ - new NotBlankConstraint(), - new TypeConstraint('integer'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('limit', [ - new NotBlankConstraint(), - new TypeConstraint('integer'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('filters', [ - new MapConstraint([ - 'name' => [ - new NotBlankConstraint(), - new TypeConstraint('string'), - ], - ]), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('sort', [ - new SortConstraint(['name']), - ])->getMapping(), - ], $propertyMappings); - } -} diff --git a/tests/Unit/Mapping/Validation/PetMappingTest.php b/tests/Unit/Mapping/Validation/PetMappingTest.php deleted file mode 100644 index b486e579..00000000 --- a/tests/Unit/Mapping/Validation/PetMappingTest.php +++ /dev/null @@ -1,58 +0,0 @@ -getClass()); - } - - public function testGetValidationClassMapping(): void - { - $mapping = new PetMapping(); - - self::assertNull($mapping->getValidationClassMapping('/path')); - } - - public function testGetValidationPropertyMappings(): void - { - $mapping = new PetMapping(); - - $propertyMappings = $mapping->getValidationPropertyMappings('/path'); - - self::assertEquals([ - ValidationPropertyMappingBuilder::create('name', [ - new NotNullConstraint(), - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('tag', [ - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ValidationPropertyMappingBuilder::create('vaccinations', [ - new ValidConstraint(), - ])->getMapping(), - ], $propertyMappings); - } -} diff --git a/tests/Unit/Mapping/Validation/VaccinationMappingTest.php b/tests/Unit/Mapping/Validation/VaccinationMappingTest.php deleted file mode 100644 index d262fa8e..00000000 --- a/tests/Unit/Mapping/Validation/VaccinationMappingTest.php +++ /dev/null @@ -1,50 +0,0 @@ -getClass()); - } - - public function testGetValidationClassMapping(): void - { - $mapping = new VaccinationMapping(); - - self::assertNull($mapping->getValidationClassMapping('/path')); - } - - public function testGetValidationPropertyMappings(): void - { - $mapping = new VaccinationMapping(); - - $propertyMappings = $mapping->getValidationPropertyMappings('/path'); - - self::assertEquals([ - ValidationPropertyMappingBuilder::create('name', [ - new NotNullConstraint(), - new NotBlankConstraint(), - new TypeConstraint('string'), - ])->getMapping(), - ], $propertyMappings); - } -} diff --git a/tests/Unit/Middleware/ApiExceptionMiddlewareTest.php b/tests/Unit/Middleware/ApiExceptionMiddlewareTest.php new file mode 100644 index 00000000..2712dd90 --- /dev/null +++ b/tests/Unit/Middleware/ApiExceptionMiddlewareTest.php @@ -0,0 +1,324 @@ +getMockByCalls(ServerRequestInterface::class); + + /** @var MockObject|ResponseInterface $response */ + $response = $this->getMockByCalls(ResponseInterface::class); + + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ + Call::create('handle')->with($request)->willReturn($response), + ]); + + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class); + + $apiExceptionMiddleware = new ApiExceptionMiddleware($encoder, $responseFactory, true, $logger); + + self::assertSame($response, $apiExceptionMiddleware->process($request, $handler)); + } + + public function testWithDebugAndLoggerWithExceptionAndWithoutAccept(): void + { + $previousException = new \RuntimeException('previous', 3); + $exception = new \LogicException('current', 5, $previousException); + + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class, [ + Call::create('getAttribute')->with('accept', null)->willReturn(null), + ]); + + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ + Call::create('handle')->with($request)->willThrowException($exception), + ]); + + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class, [ + Call::create('error')->with('Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('backtrace', $context); + + self::assertCount(2, $context['backtrace']); + + $trace1 = array_shift($context['backtrace']); + + self::assertSame(\LogicException::class, $trace1['class']); + self::assertSame('current', $trace1['message']); + self::assertSame(5, $trace1['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace1['file']); + + $trace2 = array_shift($context['backtrace']); + + self::assertSame(\RuntimeException::class, $trace2['class']); + self::assertSame('previous', $trace2['message']); + self::assertSame(3, $trace2['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace2['file']); + })), + ]); + + $apiExceptionMiddleware = new ApiExceptionMiddleware($encoder, $responseFactory, true, $logger); + + try { + $apiExceptionMiddleware->process($request, $handler); + + throw new \Exception('Expect exception'); + } catch (\Throwable $e) { + self::assertSame($exception->getMessage(), $e->getMessage()); + } + } + + public function testWithDebugAndLoggerWithExceptionAndWithAccept(): void + { + $previousException = new \RuntimeException('previous', 3); + $exception = new \LogicException('current', 5, $previousException); + + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class, [ + Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), + ]); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with('encoded'), + ]); + + /** @var MockObject|ResponseInterface $response */ + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/problem+json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); + + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ + Call::create('handle')->with($request)->willThrowException($exception), + ]); + + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with(new ArgumentCallback(static function (array $data): void { + self::assertSame('https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', $data['type']); + self::assertSame(500, $data['status']); + self::assertSame('Internal Server Error', $data['title']); + self::assertSame('current', $data['detail']); + self::assertNull($data['instance']); + self::assertCount(2, $data['backtrace']); + }), 'application/json')->willReturn('encoded'), + ]); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(500, '')->willReturn($response), + ]); + + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class, [ + Call::create('error')->with('Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('backtrace', $context); + + self::assertCount(2, $context['backtrace']); + + $trace1 = array_shift($context['backtrace']); + + self::assertSame(\LogicException::class, $trace1['class']); + self::assertSame('current', $trace1['message']); + self::assertSame(5, $trace1['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace1['file']); + + $trace2 = array_shift($context['backtrace']); + + self::assertSame(\RuntimeException::class, $trace2['class']); + self::assertSame('previous', $trace2['message']); + self::assertSame(3, $trace2['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace2['file']); + })), + ]); + + $apiExceptionMiddleware = new ApiExceptionMiddleware($encoder, $responseFactory, true, $logger); + + self::assertSame($response, $apiExceptionMiddleware->process($request, $handler)); + } + + public function testWithoutDebugAndLoggerWithExceptionAndWithAccept(): void + { + $previousException = new \RuntimeException('previous', 3); + $exception = new \LogicException('current', 5, $previousException); + + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class, [ + Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), + ]); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with('encoded'), + ]); + + /** @var MockObject|ResponseInterface $response */ + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/problem+json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); + + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ + Call::create('handle')->with($request)->willThrowException($exception), + ]); + + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with(new ArgumentCallback(static function (array $data): void { + self::assertSame('https://datatracker.ietf.org/doc/html/rfc2616#section-10.5.1', $data['type']); + self::assertSame(500, $data['status']); + self::assertSame('Internal Server Error', $data['title']); + self::assertNull($data['detail']); + self::assertNull($data['instance']); + self::assertArrayNotHasKey('backtrace', $data); + }), 'application/json')->willReturn('encoded'), + ]); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(500, '')->willReturn($response), + ]); + + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class, [ + Call::create('error')->with('Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('backtrace', $context); + + self::assertCount(2, $context['backtrace']); + + $trace1 = array_shift($context['backtrace']); + + self::assertSame(\LogicException::class, $trace1['class']); + self::assertSame('current', $trace1['message']); + self::assertSame(5, $trace1['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace1['file']); + + $trace2 = array_shift($context['backtrace']); + + self::assertSame(\RuntimeException::class, $trace2['class']); + self::assertSame('previous', $trace2['message']); + self::assertSame(3, $trace2['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace2['file']); + })), + ]); + + $apiExceptionMiddleware = new ApiExceptionMiddleware($encoder, $responseFactory, false, $logger); + + self::assertSame($response, $apiExceptionMiddleware->process($request, $handler)); + } + + public function testWithDebugAndLoggerWithHttpExceptionAndWithAccept(): void + { + $previousException = new \RuntimeException('previous', 3); + $httpException = HttpException::createBadRequest(['key' => 'value'], $previousException); + + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class, [ + Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), + ]); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with('encoded'), + ]); + + /** @var MockObject|ResponseInterface $response */ + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/problem+json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); + + /** @var MockObject|RequestHandlerInterface $handler */ + $handler = $this->getMockByCalls(RequestHandlerInterface::class, [ + Call::create('handle')->with($request)->willThrowException($httpException), + ]); + + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with(new ArgumentCallback(static function (array $data): void { + self::assertSame('https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.1', $data['type']); + self::assertSame(400, $data['status']); + self::assertSame('Bad Request', $data['title']); + self::assertNull($data['detail']); + self::assertNull($data['instance']); + }), 'application/json')->willReturn('encoded'), + ]); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(400, '')->willReturn($response), + ]); + + /** @var LoggerInterface|MockObject $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class, [ + Call::create('info')->with('Http Exception', new ArgumentCallback(static function (array $context): void { + self::assertArrayHasKey('backtrace', $context); + + self::assertCount(2, $context['backtrace']); + + $trace1 = array_shift($context['backtrace']); + + self::assertSame(HttpException::class, $trace1['class']); + self::assertSame('Bad Request', $trace1['message']); + self::assertSame(400, $trace1['code']); + self::assertMatchesRegularExpression('/HttpException\.php/', $trace1['file']); + + $trace2 = array_shift($context['backtrace']); + + self::assertSame(\RuntimeException::class, $trace2['class']); + self::assertSame('previous', $trace2['message']); + self::assertSame(3, $trace2['code']); + self::assertMatchesRegularExpression('/ApiExceptionMiddlewareTest\.php/', $trace2['file']); + })), + ]); + + $apiExceptionMiddleware = new ApiExceptionMiddleware($encoder, $responseFactory, true, $logger); + + self::assertSame($response, $apiExceptionMiddleware->process($request, $handler)); + } +} diff --git a/tests/Unit/Model/PetTest.php b/tests/Unit/Model/PetTest.php index 0fa3880a..7d2bb637 100644 --- a/tests/Unit/Model/PetTest.php +++ b/tests/Unit/Model/PetTest.php @@ -62,5 +62,21 @@ public function testGetSet(): void AssertHelper::readProperty('name', $vaccination2) ); self::assertSame($pet, AssertHelper::readProperty('pet', $vaccination2)); + + self::assertSame([ + 'id' => $pet->getId(), + 'createdAt' => $pet->getCreatedAt(), + 'updatedAt' => $pet->getUpdatedAt(), + 'name' => $pet->getName(), + 'tag' => $pet->getTag(), + 'vaccinations' => [ + [ + 'name' => $pet->getVaccinations()[0]->getName(), + ], + [ + 'name' => $pet->getVaccinations()[1]->getName(), + ], + ], + ], $pet->jsonSerialize()); } } diff --git a/tests/Unit/Model/VaccinationTest.php b/tests/Unit/Model/VaccinationTest.php index 8778c17b..4984b3aa 100644 --- a/tests/Unit/Model/VaccinationTest.php +++ b/tests/Unit/Model/VaccinationTest.php @@ -31,5 +31,7 @@ public function testGetSet(): void self::assertSame('Rabies', $vaccination->getName()); self::assertSame($pet, AssertHelper::readProperty('pet', $vaccination)); + + self::assertSame(['name' => $vaccination->getName()], $vaccination->jsonSerialize()); } } diff --git a/tests/Unit/Mapping/Orm/PetMappingTest.php b/tests/Unit/Orm/PetMappingTest.php similarity index 94% rename from tests/Unit/Mapping/Orm/PetMappingTest.php rename to tests/Unit/Orm/PetMappingTest.php index 3edcba03..2a9a988e 100644 --- a/tests/Unit/Mapping/Orm/PetMappingTest.php +++ b/tests/Unit/Orm/PetMappingTest.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace App\Tests\Unit\Mapping\Orm; +namespace App\Tests\Unit\Orm; -use App\Mapping\Orm\PetMapping; use App\Model\Vaccination; +use App\Orm\PetMapping; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use Doctrine\ORM\Mapping\ClassMetadata; use PHPUnit\Framework\TestCase; /** - * @covers \App\Mapping\Orm\PetMapping + * @covers \App\Orm\PetMapping * * @internal */ diff --git a/tests/Unit/Mapping/Orm/VaccinationMappingTest.php b/tests/Unit/Orm/VaccinationMappingTest.php similarity index 92% rename from tests/Unit/Mapping/Orm/VaccinationMappingTest.php rename to tests/Unit/Orm/VaccinationMappingTest.php index c3be514c..563ea8ec 100644 --- a/tests/Unit/Mapping/Orm/VaccinationMappingTest.php +++ b/tests/Unit/Orm/VaccinationMappingTest.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace App\Tests\Unit\Mapping\Orm; +namespace App\Tests\Unit\Orm; -use App\Mapping\Orm\VaccinationMapping; use App\Model\Pet; +use App\Orm\VaccinationMapping; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use Doctrine\ORM\Mapping\ClassMetadata; use PHPUnit\Framework\TestCase; /** - * @covers \App\Mapping\Orm\VaccinationMapping + * @covers \App\Orm\VaccinationMapping * * @internal */ diff --git a/tests/Unit/Parsing/PetParsingTest.php b/tests/Unit/Parsing/PetParsingTest.php new file mode 100644 index 00000000..a29b8881 --- /dev/null +++ b/tests/Unit/Parsing/PetParsingTest.php @@ -0,0 +1,295 @@ +getMockByCalls(ServerRequestInterface::class); + + $parser = new Parser(); + + /** @var MockObject|UrlGeneratorInterface $urlGenerator */ + $urlGenerator = $this->getMockByCalls(UrlGeneratorInterface::class); + + $petParsing = new PetParsing($parser, $urlGenerator); + + $petCollectionRequest = $petParsing->getCollectionRequestSchema($request)->parse([ + 'offset' => '10', + 'limit' => 10, + 'filters' => ['name' => 'test'], + 'sort' => ['name' => 'asc'], + ]); + + self::assertInstanceOf(PetCollectionRequest::class, $petCollectionRequest); + + self::assertSame([ + 'offset' => 10, + 'limit' => 10, + 'filters' => [ + 'name' => 'test', + ], + 'sort' => [ + 'name' => 'asc', + ], + ], json_decode(json_encode($petCollectionRequest), true)); + } + + public function testGetCollectionResponseSchema(): void + { + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class, [ + Call::create('getQueryParams')->with()->willReturn(['offset' => '10']), + ]); + + $parser = new Parser(); + + /** @var MockObject|UrlGeneratorInterface $urlGenerator */ + $urlGenerator = $this->getMockByCalls(UrlGeneratorInterface::class, [ + Call::create('generatePath') + ->with('pet_read', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + Call::create('generatePath') + ->with('pet_update', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + Call::create('generatePath') + ->with('pet_delete', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + Call::create('generatePath')->with('pet_list', [], ['offset' => 10, 'limit' => 10])->willReturn('/api/pets'), + Call::create('generatePath')->with('pet_create', [], [])->willReturn('/api/pets'), + ]); + + $petParsing = new PetParsing($parser, $urlGenerator); + + $petCollectionResponse = $petParsing->getCollectionResponseSchema($request)->parse([ + 'offset' => 10, + 'limit' => 10, + 'filters' => ['name' => 'test'], + 'sort' => ['name' => 'asc'], + 'items' => [ + [ + 'id' => 'f8b51629-d105-401e-8872-bebd9911709a', + 'createdAt' => new \DateTimeImmutable('2024-01-20T09:15:00+00:00'), + 'updatedAt' => new \DateTimeImmutable('2024-01-20T09:15:00+00:00'), + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [], + ], + ], + 'count' => 1, + ]); + + self::assertInstanceOf(PetCollectionResponse::class, $petCollectionResponse); + + self::assertSame([ + 'offset' => 10, + 'limit' => 10, + 'filters' => [ + 'name' => 'test', + ], + 'sort' => [ + 'name' => 'asc', + ], + 'items' => [ + 0 => [ + 'id' => 'f8b51629-d105-401e-8872-bebd9911709a', + 'createdAt' => '2024-01-20T09:15:00+00:00', + 'updatedAt' => '2024-01-20T09:15:00+00:00', + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [], + '_type' => 'pet', + '_links' => [ + 'read' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'GET', + ], + ], + 'update' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'PUT', + ], + ], + 'delete' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'DELETE', + ], + ], + ], + ], + ], + 'count' => 1, + '_type' => 'petCollection', + '_links' => [ + 'list' => [ + 'href' => '/api/pets', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'GET', + ], + ], + 'create' => [ + 'href' => '/api/pets', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'POST', + ], + ], + ], + ], json_decode(json_encode($petCollectionResponse), true)); + } + + public function testGetModelRequestSchema(): void + { + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class); + + $parser = new Parser(); + + /** @var MockObject|UrlGeneratorInterface $urlGenerator */ + $urlGenerator = $this->getMockByCalls(UrlGeneratorInterface::class); + + $petParsing = new PetParsing($parser, $urlGenerator); + + $petRequest = $petParsing->getModelRequestSchema($request)->parse([ + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [ + ['name' => 'rabid'], + ['name' => 'cat cold'], + ], + ]); + + self::assertInstanceOf(PetRequest::class, $petRequest); + + self::assertSame([ + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [ + 0 => [ + 'name' => 'rabid', + ], + 1 => [ + 'name' => 'cat cold', + ], + ], + ], json_decode(json_encode($petRequest), true)); + } + + public function testGetModelResponseSchema(): void + { + /** @var MockObject|ServerRequestInterface $request */ + $request = $this->getMockByCalls(ServerRequestInterface::class); + + $parser = new Parser(); + + /** @var MockObject|UrlGeneratorInterface $urlGenerator */ + $urlGenerator = $this->getMockByCalls(UrlGeneratorInterface::class, [ + Call::create('generatePath') + ->with('pet_read', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + Call::create('generatePath') + ->with('pet_update', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + Call::create('generatePath') + ->with('pet_delete', ['id' => 'f8b51629-d105-401e-8872-bebd9911709a'], []) + ->willReturn('/api/pets/f8b51629-d105-401e-8872-bebd9911709a'), + ]); + + $petParsing = new PetParsing($parser, $urlGenerator); + + $petResponse = $petParsing->getModelResponseSchema($request)->parse([ + 'id' => 'f8b51629-d105-401e-8872-bebd9911709a', + 'createdAt' => new \DateTimeImmutable('2024-01-20T09:15:00+00:00'), + 'updatedAt' => new \DateTimeImmutable('2024-01-20T09:15:00+00:00'), + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [ + ['name' => 'rabid'], + ['name' => 'cat cold'], + ], + ]); + + self::assertInstanceOf(PetResponse::class, $petResponse); + + self::assertSame([ + 'id' => 'f8b51629-d105-401e-8872-bebd9911709a', + 'createdAt' => '2024-01-20T09:15:00+00:00', + 'updatedAt' => '2024-01-20T09:15:00+00:00', + 'name' => 'test', + 'tag' => null, + 'vaccinations' => [ + 0 => [ + 'name' => 'rabid', + '_type' => 'vaccination', + ], + 1 => [ + 'name' => 'cat cold', + '_type' => 'vaccination', + ], + ], + '_type' => 'pet', + '_links' => [ + 'read' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'GET', + ], + ], + 'update' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'PUT', + ], + ], + 'delete' => [ + 'href' => '/api/pets/f8b51629-d105-401e-8872-bebd9911709a', + 'templated' => false, + 'rel' => [], + 'attributes' => [ + 'method' => 'DELETE', + ], + ], + ], + ], json_decode(json_encode($petResponse), true)); + } +} diff --git a/tests/Unit/RequestHandler/Api/Crud/CreateRequestHandlerTest.php b/tests/Unit/RequestHandler/Api/Crud/CreateRequestHandlerTest.php index b7a72376..a17123b8 100644 --- a/tests/Unit/RequestHandler/Api/Crud/CreateRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/Api/Crud/CreateRequestHandlerTest.php @@ -4,23 +4,23 @@ namespace App\Tests\Unit\RequestHandler\Api\Crud; -use App\Factory\ModelFactoryInterface; +use App\Dto\Model\ModelRequestInterface; use App\Model\ModelInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\CreateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpExceptionInterface; -use Chubbyphp\Mock\Argument\ArgumentCallback; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ErrorInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Chubbyphp\Parsing\Schema\ObjectSchemaInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; /** * @covers \App\RequestHandler\Api\Crud\CreateRequestHandler @@ -31,63 +31,57 @@ final class CreateRequestHandlerTest extends TestCase { use MockByCallsTrait; - public function testCreateWithValidationError(): void + public function testWithParsingError(): void { + $parserErrorException = new ParserErrorException(); + + $inputAsStdClass = new \stdClass(); + $inputAsStdClass->name = 'test'; + $inputAsArray = (array) $inputAsStdClass; + $inputAsJson = json_encode($inputAsArray); + + /** @var MockObject|StreamInterface $requestBody */ + $requestBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('__toString')->with()->willReturn($inputAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), + Call::create('getBody')->with()->willReturn($requestBody), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var ErrorInterface|MockObject $error */ - $error = $this->getMockByCalls(ErrorInterface::class, [ - Call::create('getPath')->with()->willReturn('name'), - Call::create('getKey')->with()->willReturn('notunique'), - Call::create('getArguments')->with()->willReturn([]), + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class, [ + Call::create('decode')->with($inputAsJson, 'application/json')->willReturn($inputAsArray), ]); - /** @var MockObject|ModelInterface $model */ - $model = $this->getMockByCalls(ModelInterface::class); + /** @var MockObject|ObjectSchemaInterface $modelRequestSchema */ + $modelRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willThrowException($parserErrorException), + ]); - /** @var MockObject|ModelFactoryInterface $factory */ - $factory = $this->getMockByCalls(ModelFactoryInterface::class, [ - Call::create('create')->with()->willReturn($model), + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getModelRequestSchema')->with($request)->willReturn($modelRequestSchema), ]); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestBody') - ->with( - $request, - $model, - 'application/json', - new ArgumentCallback(static function (DenormalizerContextInterface $context): void { - self::assertTrue($context->isClearMissing()); - }) - ) - ->willReturn($model), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($model, null, '')->willReturn([$error]), - ]); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); $requestHandler = new CreateRequestHandler( - $factory, + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); try { @@ -101,34 +95,70 @@ public function testCreateWithValidationError(): void 'title' => 'Unprocessable Entity', 'detail' => null, 'instance' => null, - 'invalidParameters' => [ - 0 => [ - 'name' => 'name', - 'reason' => 'notunique', - 'details' => [], - ], - ], + 'invalidParameters' => [], ], $e->jsonSerialize()); } } public function testSuccessful(): void { + $inputAsStdClass = new \stdClass(); + $inputAsStdClass->name = 'test'; + $inputAsArray = (array) $inputAsStdClass; + $inputAsJson = json_encode($inputAsArray); + + /** @var MockObject|StreamInterface $requestBody */ + $requestBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('__toString')->with()->willReturn($inputAsJson), + ]); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with($inputAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), + Call::create('getBody')->with()->willReturn($requestBody), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); /** @var MockObject|ModelInterface $model */ - $model = $this->getMockByCalls(ModelInterface::class); + $model = $this->getMockByCalls(ModelInterface::class, [ + Call::create('jsonSerialize')->with()->willReturn($inputAsArray), + ]); + + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class, [ + Call::create('decode')->with($inputAsJson, 'application/json')->willReturn($inputAsArray), + ]); + + /** @var MockObject|ModelRequestInterface $modelRequest */ + $modelRequest = $this->getMockByCalls(ModelRequestInterface::class, [ + Call::create('createModel')->with()->willReturn($model), + ]); - /** @var MockObject|ModelFactoryInterface $factory */ - $factory = $this->getMockByCalls(ModelFactoryInterface::class, [ - Call::create('create')->with()->willReturn($model), + /** @var MockObject|ObjectSchemaInterface $modelRequestSchema */ + $modelRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willReturn($modelRequest), + ]); + + /** @var MockObject|ObjectSchemaInterface $modelResponseSchema */ + $modelResponseSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willReturn($inputAsStdClass), + ]); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getModelRequestSchema')->with($request)->willReturn($modelRequestSchema), + Call::create('getModelResponseSchema')->with($request)->willReturn($modelResponseSchema), ]); /** @var MockObject|RepositoryInterface $repository */ @@ -137,45 +167,22 @@ public function testSuccessful(): void Call::create('flush')->with(), ]); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestBody') - ->with( - $request, - $model, - 'application/json', - new ArgumentCallback(static function (DenormalizerContextInterface $context): void { - self::assertTrue($context->isClearMissing()); - }) - ) - ->willReturn($model), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [ - Call::create('create') - ->with( - $model, - 'application/json', - 201, - new ArgumentCallback(static function (NormalizerContextInterface $context) use ($request): void { - self::assertSame($request, $context->getRequest()); - }) - ) - ->willReturn($response), + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with($inputAsArray, 'application/json')->willReturn($inputAsJson), ]); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($model, null, '')->willReturn([]), + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(201, '')->willReturn($response), ]); $requestHandler = new CreateRequestHandler( - $factory, + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); self::assertSame($response, $requestHandler->handle($request)); diff --git a/tests/Unit/RequestHandler/Api/Crud/DeleteRequestHandlerTest.php b/tests/Unit/RequestHandler/Api/Crud/DeleteRequestHandlerTest.php index fdd9b106..df218f40 100644 --- a/tests/Unit/RequestHandler/Api/Crud/DeleteRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/Api/Crud/DeleteRequestHandlerTest.php @@ -7,11 +7,11 @@ use App\Model\ModelInterface; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\DeleteRequestHandler; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; use Chubbyphp\HttpException\HttpExceptionInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -32,16 +32,13 @@ public function testCreateResourceNotFoundInvalidUuid(): void Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); - $requestHandler = new DeleteRequestHandler($repository, $responseManager); + $requestHandler = new DeleteRequestHandler($repository, $responseFactory); try { $requestHandler->handle($request); @@ -60,18 +57,15 @@ public function testCreateResourceNotFoundMissingModel(): void Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class, [ Call::create('findById')->with('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0')->willReturn(null), ]); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); - $requestHandler = new DeleteRequestHandler($repository, $responseManager); + $requestHandler = new DeleteRequestHandler($repository, $responseFactory); try { $requestHandler->handle($request); @@ -91,7 +85,11 @@ public function testSuccessful(): void ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader') + ->with('Content-Type', 'application/json') + ->willReturnSelf(), + ]); /** @var MockObject|ModelInterface $model */ $model = $this->getMockByCalls(ModelInterface::class); @@ -103,14 +101,14 @@ public function testSuccessful(): void Call::create('flush')->with(), ]); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [ - Call::create('createEmpty') - ->with('application/json', 204) + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse') + ->with(204, '') ->willReturn($response), ]); - $requestHandler = new DeleteRequestHandler($repository, $responseManager); + $requestHandler = new DeleteRequestHandler($repository, $responseFactory); self::assertSame($response, $requestHandler->handle($request)); } diff --git a/tests/Unit/RequestHandler/Api/Crud/ListRequestHandlerTest.php b/tests/Unit/RequestHandler/Api/Crud/ListRequestHandlerTest.php index 2c5131ee..a9e80356 100644 --- a/tests/Unit/RequestHandler/Api/Crud/ListRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/Api/Crud/ListRequestHandlerTest.php @@ -5,21 +5,21 @@ namespace App\Tests\Unit\RequestHandler\Api\Crud; use App\Collection\CollectionInterface; -use App\Factory\CollectionFactoryInterface; +use App\Dto\Collection\CollectionRequestInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\ListRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpExceptionInterface; -use Chubbyphp\Mock\Argument\ArgumentCallback; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ErrorInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Chubbyphp\Parsing\Schema\ObjectSchemaInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; /** * @covers \App\RequestHandler\Api\Crud\ListRequestHandler @@ -30,55 +30,44 @@ final class ListRequestHandlerTest extends TestCase { use MockByCallsTrait; - public function testCreateWithValidationError(): void + public function testWithParsingError(): void { + $parserErrorException = new ParserErrorException(); + + $queryAsStdClass = new \stdClass(); + $queryAsStdClass->name = 'test'; + $queryAsArray = (array) $queryAsStdClass; + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), + Call::create('getQueryParams')->with()->willReturn($queryAsArray), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); - - /** @var ErrorInterface|MockObject $error */ - $error = $this->getMockByCalls(ErrorInterface::class, [ - Call::create('getPath')->with()->willReturn('offset'), - Call::create('getKey')->with()->willReturn('notinteger'), - Call::create('getArguments')->with()->willReturn([]), + /** @var MockObject|ObjectSchemaInterface $collectionRequestSchema */ + $collectionRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($queryAsArray)->willThrowException($parserErrorException), ]); - /** @var CollectionInterface|MockObject $collection */ - $collection = $this->getMockByCalls(CollectionInterface::class); - - /** @var CollectionFactoryInterface|MockObject $factory */ - $factory = $this->getMockByCalls(CollectionFactoryInterface::class, [ - Call::create('create')->with()->willReturn($collection), + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getCollectionRequestSchema')->with($request)->willReturn($collectionRequestSchema), ]); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestQuery') - ->with($request, $collection, null) - ->willReturn($collection), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($collection, null, '')->willReturn([$error]), - ]); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); $requestHandler = new ListRequestHandler( - $factory, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); try { @@ -86,26 +75,65 @@ public function testCreateWithValidationError(): void self::fail('Expected Exception'); } catch (\Throwable $e) { self::assertInstanceOf(HttpExceptionInterface::class, $e); - self::assertSame(400, $e->getStatus()); + self::assertSame([ + 'type' => 'https://datatracker.ietf.org/doc/html/rfc2616#section-10.4.1', + 'status' => 400, + 'title' => 'Bad Request', + 'detail' => null, + 'instance' => null, + 'invalidParameters' => [], + ], $e->jsonSerialize()); } } public function testSuccessful(): void { + $queryAsStdClass = new \stdClass(); + $queryAsStdClass->name = 'test'; + $queryAsArray = (array) $queryAsStdClass; + $queryAsJson = json_encode($queryAsArray); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with($queryAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), + Call::create('getQueryParams')->with()->willReturn($queryAsArray), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); /** @var CollectionInterface|MockObject $collection */ - $collection = $this->getMockByCalls(CollectionInterface::class); + $collection = $this->getMockByCalls(CollectionInterface::class, [ + Call::create('jsonSerialize')->with()->willReturn($queryAsArray), + ]); + + /** @var CollectionRequestInterface|MockObject $collectionRequest */ + $collectionRequest = $this->getMockByCalls(CollectionRequestInterface::class, [ + Call::create('createCollection')->with()->willReturn($collection), + ]); + + /** @var MockObject|ObjectSchemaInterface $collectionRequestSchema */ + $collectionRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($queryAsArray)->willReturn($collectionRequest), + ]); + + /** @var MockObject|ObjectSchemaInterface $collectionResponseSchema */ + $collectionResponseSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($queryAsArray)->willReturn($queryAsStdClass), + ]); - /** @var CollectionFactoryInterface|MockObject $factory */ - $factory = $this->getMockByCalls(CollectionFactoryInterface::class, [ - Call::create('create')->with()->willReturn($collection), + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getCollectionRequestSchema')->with($request)->willReturn($collectionRequestSchema), + Call::create('getCollectionResponseSchema')->with($request)->willReturn($collectionResponseSchema), ]); /** @var MockObject|RepositoryInterface $repository */ @@ -113,38 +141,21 @@ public function testSuccessful(): void Call::create('resolveCollection')->with($collection), ]); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestQuery') - ->with($request, $collection, null) - ->willReturn($collection), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [ - Call::create('create') - ->with( - $collection, - 'application/json', - 200, - new ArgumentCallback(static function (NormalizerContextInterface $context) use ($request): void { - self::assertSame($request, $context->getRequest()); - }) - ) - ->willReturn($response), + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with($queryAsArray, 'application/json')->willReturn($queryAsJson), ]); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($collection, null, '')->willReturn([]), + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(200, '')->willReturn($response), ]); $requestHandler = new ListRequestHandler( - $factory, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); self::assertSame($response, $requestHandler->handle($request)); diff --git a/tests/Unit/RequestHandler/Api/Crud/ReadRequestHandlerTest.php b/tests/Unit/RequestHandler/Api/Crud/ReadRequestHandlerTest.php index 8fcdf5b8..5f43b528 100644 --- a/tests/Unit/RequestHandler/Api/Crud/ReadRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/Api/Crud/ReadRequestHandlerTest.php @@ -5,17 +5,19 @@ namespace App\Tests\Unit\RequestHandler\Api\Crud; use App\Model\ModelInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\ReadRequestHandler; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpExceptionInterface; -use Chubbyphp\Mock\Argument\ArgumentCallback; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; +use Chubbyphp\Parsing\Schema\ObjectSchemaInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; /** * @covers \App\RequestHandler\Api\Crud\ReadRequestHandler @@ -26,7 +28,7 @@ final class ReadRequestHandlerTest extends TestCase { use MockByCallsTrait; - public function testCreateResourceNotFoundInvalidUuid(): void + public function testResourceNotFoundInvalidUuid(): void { /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ @@ -34,16 +36,24 @@ public function testCreateResourceNotFoundInvalidUuid(): void Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); - $requestHandler = new ReadRequestHandler($repository, $responseManager); + $requestHandler = new ReadRequestHandler( + $parsing, + $repository, + $encoder, + $responseFactory + ); try { $requestHandler->handle($request); @@ -54,7 +64,7 @@ public function testCreateResourceNotFoundInvalidUuid(): void } } - public function testCreateResourceNotFoundMissingModel(): void + public function testResourceNotFoundMissingModel(): void { /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ @@ -62,18 +72,26 @@ public function testCreateResourceNotFoundMissingModel(): void Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class, [ Call::create('findById')->with('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0')->willReturn(null), ]); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); - $requestHandler = new ReadRequestHandler($repository, $responseManager); + $requestHandler = new ReadRequestHandler( + $parsing, + $repository, + $encoder, + $responseFactory + ); try { $requestHandler->handle($request); @@ -86,6 +104,16 @@ public function testCreateResourceNotFoundMissingModel(): void public function testSuccessful(): void { + $inputAsStdClass = new \stdClass(); + $inputAsStdClass->name = 'test'; + $inputAsArray = (array) $inputAsStdClass; + $inputAsJson = json_encode($inputAsArray); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with($inputAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('id', null)->willReturn('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0'), @@ -93,31 +121,47 @@ public function testSuccessful(): void ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); /** @var MockObject|ModelInterface $model */ - $model = $this->getMockByCalls(ModelInterface::class); + $model = $this->getMockByCalls(ModelInterface::class, [ + Call::create('jsonSerialize')->with()->willReturn($inputAsArray), + ]); + + /** @var MockObject|ObjectSchemaInterface $modelResponseSchema */ + $modelResponseSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willReturn($inputAsStdClass), + ]); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getModelResponseSchema')->with($request)->willReturn($modelResponseSchema), + ]); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class, [ Call::create('findById')->with('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0')->willReturn($model), ]); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [ - Call::create('create') - ->with( - $model, - 'application/json', - 200, - new ArgumentCallback(static function (NormalizerContextInterface $context) use ($request): void { - self::assertSame($request, $context->getRequest()); - }) - ) - ->willReturn($response), + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with($inputAsArray, 'application/json')->willReturn($inputAsJson), + ]); + + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(200, '')->willReturn($response), ]); - $requestHandler = new ReadRequestHandler($repository, $responseManager); + $requestHandler = new ReadRequestHandler( + $parsing, + $repository, + $encoder, + $responseFactory + ); self::assertSame($response, $requestHandler->handle($request)); } diff --git a/tests/Unit/RequestHandler/Api/Crud/UpdateRequestHandlerTest.php b/tests/Unit/RequestHandler/Api/Crud/UpdateRequestHandlerTest.php index 1b64590d..ab0e8ff2 100644 --- a/tests/Unit/RequestHandler/Api/Crud/UpdateRequestHandlerTest.php +++ b/tests/Unit/RequestHandler/Api/Crud/UpdateRequestHandlerTest.php @@ -4,23 +4,23 @@ namespace App\Tests\Unit\RequestHandler\Api\Crud; +use App\Dto\Model\ModelRequestInterface; use App\Model\ModelInterface; +use App\Parsing\ParsingInterface; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\UpdateRequestHandler; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; -use Chubbyphp\Deserialization\Denormalizer\DenormalizerContextInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\HttpException\HttpExceptionInterface; -use Chubbyphp\Mock\Argument\ArgumentCallback; -use Chubbyphp\Mock\Argument\ArgumentInstanceOf; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Serialization\Normalizer\NormalizerContextInterface; -use Chubbyphp\Validation\Error\ErrorInterface; -use Chubbyphp\Validation\ValidatorInterface; +use Chubbyphp\Parsing\ParserErrorException; +use Chubbyphp\Parsing\Schema\ObjectSchemaInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; /** * @covers \App\RequestHandler\Api\Crud\UpdateRequestHandler @@ -31,7 +31,7 @@ final class UpdateRequestHandlerTest extends TestCase { use MockByCallsTrait; - public function testCreateResourceNotFoundInvalidUuid(): void + public function testResourceNotFoundInvalidUuid(): void { /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ @@ -40,26 +40,27 @@ public function testCreateResourceNotFoundInvalidUuid(): void Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); $requestHandler = new UpdateRequestHandler( + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); try { @@ -71,7 +72,7 @@ public function testCreateResourceNotFoundInvalidUuid(): void } } - public function testCreateResourceNotFoundMissingModel(): void + public function testResourceNotFoundMissingModel(): void { /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ @@ -80,28 +81,29 @@ public function testCreateResourceNotFoundMissingModel(): void Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class, [ Call::create('findById')->with('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0')->willReturn(null), ]); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); - - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); $requestHandler = new UpdateRequestHandler( + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); try { @@ -113,64 +115,63 @@ public function testCreateResourceNotFoundMissingModel(): void } } - public function testCreateWithValidationError(): void + public function testWithParsingError(): void { + $parserErrorException = new ParserErrorException(); + + $inputAsStdClass = new \stdClass(); + $inputAsStdClass->name = 'test'; + $inputAsArray = (array) $inputAsStdClass; + $inputAsJson = json_encode($inputAsArray); + + /** @var MockObject|StreamInterface $requestBody */ + $requestBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('__toString')->with()->willReturn($inputAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('id', null)->willReturn('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0'), Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), + Call::create('getBody')->with()->willReturn($requestBody), ]); - /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + /** @var MockObject|ModelInterface $model */ + $model = $this->getMockByCalls(ModelInterface::class, []); - /** @var ErrorInterface|MockObject $error */ - $error = $this->getMockByCalls(ErrorInterface::class, [ - Call::create('getPath')->with()->willReturn('name'), - Call::create('getKey')->with()->willReturn('notunique'), - Call::create('getArguments')->with()->willReturn([]), + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class, [ + Call::create('decode')->with($inputAsJson, 'application/json')->willReturn($inputAsArray), ]); - /** @var MockObject|ModelInterface $model */ - $model = $this->getMockByCalls(ModelInterface::class); + /** @var MockObject|ObjectSchemaInterface $modelRequestSchema */ + $modelRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willThrowException($parserErrorException), + ]); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getModelRequestSchema')->with($request)->willReturn($modelRequestSchema), + ]); /** @var MockObject|RepositoryInterface $repository */ $repository = $this->getMockByCalls(RepositoryInterface::class, [ Call::create('findById')->with('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0')->willReturn($model), ]); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestBody') - ->with( - $request, - $model, - 'application/json', - new ArgumentCallback(static function (DenormalizerContextInterface $context): void { - self::assertTrue($context->isClearMissing()); - self::assertSame( - ['id', 'createdAt', 'updatedAt', '_links'], - $context->getAllowedAdditionalFields() - ); - }) - ) - ->willReturn($model), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($model, null, '')->willReturn([$error]), - ]); + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); $requestHandler = new UpdateRequestHandler( + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); try { @@ -184,32 +185,71 @@ public function testCreateWithValidationError(): void 'title' => 'Unprocessable Entity', 'detail' => null, 'instance' => null, - 'invalidParameters' => [ - 0 => [ - 'name' => 'name', - 'reason' => 'notunique', - 'details' => [], - ], - ], + 'invalidParameters' => [], ], $e->jsonSerialize()); } } public function testSuccessful(): void { + $inputAsStdClass = new \stdClass(); + $inputAsStdClass->name = 'test'; + $inputAsArray = (array) $inputAsStdClass; + $inputAsJson = json_encode($inputAsArray); + + /** @var MockObject|StreamInterface $requestBody */ + $requestBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('__toString')->with()->willReturn($inputAsJson), + ]); + + /** @var MockObject|StreamInterface $responseBody */ + $responseBody = $this->getMockByCalls(StreamInterface::class, [ + Call::create('write')->with($inputAsJson), + ]); + /** @var MockObject|ServerRequestInterface $request */ $request = $this->getMockByCalls(ServerRequestInterface::class, [ Call::create('getAttribute')->with('id', null)->willReturn('cbb6bd79-b6a9-4b07-9d8b-f6be0f19aaa0'), Call::create('getAttribute')->with('accept', null)->willReturn('application/json'), Call::create('getAttribute')->with('contentType', null)->willReturn('application/json'), + Call::create('getBody')->with()->willReturn($requestBody), ]); /** @var MockObject|ResponseInterface $response */ - $response = $this->getMockByCalls(ResponseInterface::class); + $response = $this->getMockByCalls(ResponseInterface::class, [ + Call::create('withHeader')->with('Content-Type', 'application/json')->willReturnSelf(), + Call::create('getBody')->with()->willReturn($responseBody), + ]); /** @var MockObject|ModelInterface $model */ $model = $this->getMockByCalls(ModelInterface::class, [ - Call::create('setUpdatedAt')->with(new ArgumentInstanceOf(\DateTimeImmutable::class)), + Call::create('jsonSerialize')->with()->willReturn($inputAsArray), + ]); + + /** @var DecoderInterface|MockObject $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class, [ + Call::create('decode')->with($inputAsJson, 'application/json')->willReturn($inputAsArray), + ]); + + /** @var MockObject|ModelRequestInterface $modelRequest */ + $modelRequest = $this->getMockByCalls(ModelRequestInterface::class, [ + Call::create('updateModel')->with($model)->willReturn($model), + ]); + + /** @var MockObject|ObjectSchemaInterface $modelRequestSchema */ + $modelRequestSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willReturn($modelRequest), + ]); + + /** @var MockObject|ObjectSchemaInterface $modelResponseSchema */ + $modelResponseSchema = $this->getMockByCalls(ObjectSchemaInterface::class, [ + Call::create('parse')->with($inputAsArray)->willReturn($inputAsStdClass), + ]); + + /** @var MockObject|ParsingInterface $parsing */ + $parsing = $this->getMockByCalls(ParsingInterface::class, [ + Call::create('getModelRequestSchema')->with($request)->willReturn($modelRequestSchema), + Call::create('getModelResponseSchema')->with($request)->willReturn($modelResponseSchema), ]); /** @var MockObject|RepositoryInterface $repository */ @@ -219,48 +259,22 @@ public function testSuccessful(): void Call::create('flush')->with(), ]); - /** @var MockObject|RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class, [ - Call::create('getDataFromRequestBody') - ->with( - $request, - $model, - 'application/json', - new ArgumentCallback(static function (DenormalizerContextInterface $context): void { - self::assertTrue($context->isClearMissing()); - self::assertSame( - ['id', 'createdAt', 'updatedAt', '_links'], - $context->getAllowedAdditionalFields() - ); - }) - ) - ->willReturn($model), - ]); - - /** @var MockObject|ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class, [ - Call::create('create') - ->with( - $model, - 'application/json', - 200, - new ArgumentCallback(static function (NormalizerContextInterface $context) use ($request): void { - self::assertSame($request, $context->getRequest()); - }) - ) - ->willReturn($response), + /** @var EncoderInterface|MockObject $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class, [ + Call::create('encode')->with($inputAsArray, 'application/json')->willReturn($inputAsJson), ]); - /** @var MockObject|ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class, [ - Call::create('validate')->with($model, null, '')->willReturn([]), + /** @var MockObject|ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class, [ + Call::create('createResponse')->with(200, '')->willReturn($response), ]); $requestHandler = new UpdateRequestHandler( + $decoder, + $parsing, $repository, - $requestManager, - $responseManager, - $validator + $encoder, + $responseFactory ); self::assertSame($response, $requestHandler->handle($request)); diff --git a/tests/Unit/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactoryTest.php b/tests/Unit/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactoryTest.php deleted file mode 100644 index 795df2d7..00000000 --- a/tests/Unit/ServiceFactory/Deserialization/DenormalizationObjectMappingsFactoryTest.php +++ /dev/null @@ -1,37 +0,0 @@ -getMockByCalls(DenormalizationFieldMappingFactoryInterface::class); - - /** @var ContainerInterface $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('has')->with(DenormalizationFieldMappingFactoryInterface::class)->willReturn(true), - Call::create('get')->with(DenormalizationFieldMappingFactoryInterface::class)->willReturn($denormalizationFieldMappingFactory), - ]); - - $factory = new DenormalizationObjectMappingsFactory(); - $factory($container); - } -} diff --git a/tests/Unit/ServiceFactory/Factory/Collection/PetCollectionFactoryFactoryTest.php b/tests/Unit/ServiceFactory/Factory/Collection/PetCollectionFactoryFactoryTest.php deleted file mode 100644 index 68804936..00000000 --- a/tests/Unit/ServiceFactory/Factory/Collection/PetCollectionFactoryFactoryTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getMockByCalls(ContainerInterface::class); - - $factory = new PetCollectionFactoryFactory(); - - self::assertInstanceOf(PetCollectionFactory::class, $factory()); - } -} diff --git a/tests/Unit/ServiceFactory/Factory/Model/PetFactoryFactoryTest.php b/tests/Unit/ServiceFactory/Factory/Model/PetFactoryFactoryTest.php deleted file mode 100644 index 9d12bac8..00000000 --- a/tests/Unit/ServiceFactory/Factory/Model/PetFactoryFactoryTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getMockByCalls(ContainerInterface::class); - - $factory = new PetFactoryFactory(); - - self::assertInstanceOf(PetFactory::class, $factory()); - } -} diff --git a/tests/Unit/ServiceFactory/Framework/RoutesByNameFactoryTest.php b/tests/Unit/ServiceFactory/Framework/RoutesByNameFactoryTest.php index c7171fdf..463224d7 100644 --- a/tests/Unit/ServiceFactory/Framework/RoutesByNameFactoryTest.php +++ b/tests/Unit/ServiceFactory/Framework/RoutesByNameFactoryTest.php @@ -4,6 +4,7 @@ namespace App\Tests\Unit\ServiceFactory\Framework; +use App\Middleware\ApiExceptionMiddleware as MiddlewareApiExceptionMiddleware; use App\Model\Pet; use App\RequestHandler\Api\Crud\CreateRequestHandler; use App\RequestHandler\Api\Crud\DeleteRequestHandler; @@ -13,12 +14,12 @@ use App\RequestHandler\OpenapiRequestHandler; use App\RequestHandler\PingRequestHandler; use App\ServiceFactory\Framework\RoutesByNameFactory; -use Chubbyphp\ApiHttp\Middleware\AcceptAndContentTypeMiddleware; -use Chubbyphp\ApiHttp\Middleware\ApiExceptionMiddleware; use Chubbyphp\Framework\Middleware\LazyMiddleware; use Chubbyphp\Framework\RequestHandler\LazyRequestHandler; use Chubbyphp\Framework\Router\Route; use Chubbyphp\Mock\MockByCallsTrait; +use Chubbyphp\Negotiation\Middleware\AcceptMiddleware; +use Chubbyphp\Negotiation\Middleware\ContentTypeMiddleware; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; @@ -39,8 +40,9 @@ public function testInvoke(): void $ping = new LazyRequestHandler($container, PingRequestHandler::class); $openApi = new LazyRequestHandler($container, OpenapiRequestHandler::class); - $acceptAndContentType = new LazyMiddleware($container, AcceptAndContentTypeMiddleware::class); - $apiExceptionMiddleware = new LazyMiddleware($container, ApiExceptionMiddleware::class); + $accept = new LazyMiddleware($container, AcceptMiddleware::class); + $contentType = new LazyMiddleware($container, ContentTypeMiddleware::class); + $apiExceptionMiddleware = new LazyMiddleware($container, MiddlewareApiExceptionMiddleware::class); $petList = new LazyRequestHandler($container, Pet::class.ListRequestHandler::class); $petCreate = new LazyRequestHandler($container, Pet::class.CreateRequestHandler::class); @@ -53,11 +55,11 @@ public function testInvoke(): void self::assertEquals([ 'ping' => Route::get('/ping', 'ping', $ping), 'openapi' => Route::get('/openapi', 'openapi', $openApi), - 'pet_list' => Route::get('/api/pets', 'pet_list', $petList, [$acceptAndContentType, $apiExceptionMiddleware]), - 'pet_create' => Route::post('/api/pets', 'pet_create', $petCreate, [$acceptAndContentType, $apiExceptionMiddleware]), - 'pet_read' => Route::get('/api/pets/{id}', 'pet_read', $petRead, [$acceptAndContentType, $apiExceptionMiddleware]), - 'pet_update' => Route::put('/api/pets/{id}', 'pet_update', $petUpdate, [$acceptAndContentType, $apiExceptionMiddleware]), - 'pet_delete' => Route::delete('/api/pets/{id}', 'pet_delete', $petDelete, [$acceptAndContentType, $apiExceptionMiddleware]), + 'pet_list' => Route::get('/api/pets', 'pet_list', $petList, [$accept, $apiExceptionMiddleware]), + 'pet_create' => Route::post('/api/pets', 'pet_create', $petCreate, [$accept, $apiExceptionMiddleware, $contentType]), + 'pet_read' => Route::get('/api/pets/{id}', 'pet_read', $petRead, [$accept, $apiExceptionMiddleware]), + 'pet_update' => Route::put('/api/pets/{id}', 'pet_update', $petUpdate, [$accept, $apiExceptionMiddleware, $contentType]), + 'pet_delete' => Route::delete('/api/pets/{id}', 'pet_delete', $petDelete, [$accept, $apiExceptionMiddleware]), ], $factory($container)->getRoutesByName()); } } diff --git a/tests/Unit/ServiceFactory/Middleware/ApiExceptionMiddlewareFactoryTest.php b/tests/Unit/ServiceFactory/Middleware/ApiExceptionMiddlewareFactoryTest.php new file mode 100644 index 00000000..31d78e2a --- /dev/null +++ b/tests/Unit/ServiceFactory/Middleware/ApiExceptionMiddlewareFactoryTest.php @@ -0,0 +1,49 @@ +getMockByCalls(EncoderInterface::class); + + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); + + /** @var LoggerInterface $logger */ + $logger = $this->getMockByCalls(LoggerInterface::class); + + /** @var ContainerInterface $container */ + $container = $this->getMockByCalls(ContainerInterface::class, [ + Call::create('get')->with('config')->willReturn(['debug' => true]), + Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), + Call::create('get')->with(LoggerInterface::class)->willReturn($logger), + ]); + + $factory = new ApiExceptionMiddlewareFactory(); + + self::assertInstanceOf(ApiExceptionMiddleware::class, $factory($container)); + } +} diff --git a/tests/Unit/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactoryTest.php b/tests/Unit/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactoryTest.php index f8760ae8..98c1aa05 100644 --- a/tests/Unit/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactoryTest.php +++ b/tests/Unit/ServiceFactory/Negotiation/AcceptNegotiatorSupportedMediaTypesFactoryTest.php @@ -29,7 +29,6 @@ public function testInvoke(): void /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('has')->with(EncoderInterface::class)->willReturn(true), Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), ]); @@ -39,24 +38,4 @@ public function testInvoke(): void self::assertSame(['application/json'], $service); } - - public function testCallStatic(): void - { - /** @var EncoderInterface $encoder */ - $encoder = $this->getMockByCalls(EncoderInterface::class, [ - Call::create('getContentTypes')->with()->willReturn(['application/json']), - ]); - - /** @var ContainerInterface $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('has')->with(EncoderInterface::class.'default')->willReturn(true), - Call::create('get')->with(EncoderInterface::class.'default')->willReturn($encoder), - ]); - - $factory = [AcceptNegotiatorSupportedMediaTypesFactory::class, 'default']; - - $service = $factory($container); - - self::assertSame(['application/json'], $service); - } } diff --git a/tests/Unit/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactoryTest.php b/tests/Unit/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactoryTest.php index fbad128a..4912019d 100644 --- a/tests/Unit/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactoryTest.php +++ b/tests/Unit/ServiceFactory/Negotiation/ContentTypeNegotiatorSupportedMediaTypesFactoryTest.php @@ -29,7 +29,6 @@ public function testInvoke(): void /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('has')->with(DecoderInterface::class)->willReturn(true), Call::create('get')->with(DecoderInterface::class)->willReturn($decoder), ]); @@ -39,24 +38,4 @@ public function testInvoke(): void self::assertSame(['application/json'], $service); } - - public function testCallStatic(): void - { - /** @var DecoderInterface $decoder */ - $decoder = $this->getMockByCalls(DecoderInterface::class, [ - Call::create('getContentTypes')->with()->willReturn(['application/json']), - ]); - - /** @var ContainerInterface $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('has')->with(DecoderInterface::class.'default')->willReturn(true), - Call::create('get')->with(DecoderInterface::class.'default')->willReturn($decoder), - ]); - - $factory = [ContentTypeNegotiatorSupportedMediaTypesFactory::class, 'default']; - - $service = $factory($container); - - self::assertSame(['application/json'], $service); - } } diff --git a/tests/Unit/ServiceFactory/Parsing/ParserFactoryTest.php b/tests/Unit/ServiceFactory/Parsing/ParserFactoryTest.php new file mode 100644 index 00000000..8d1d322c --- /dev/null +++ b/tests/Unit/ServiceFactory/Parsing/ParserFactoryTest.php @@ -0,0 +1,24 @@ +getMockByCalls(ParserInterface::class); + + /** @var ParserInterface $urlGenerator */ + $urlGenerator = $this->getMockByCalls(UrlGeneratorInterface::class); + + /** @var ContainerInterface $container */ + $container = $this->getMockByCalls(ContainerInterface::class, [ + Call::create('get')->with(ParserInterface::class)->willReturn($parser), + Call::create('get')->with(UrlGeneratorInterface::class)->willReturn($urlGenerator), + ]); + + $factory = new PetParsingFactory(); + + self::assertInstanceOf(PetParsing::class, $factory($container)); + } +} diff --git a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactoryTest.php b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactoryTest.php index 10ebb35b..74d73e32 100644 --- a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactoryTest.php +++ b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetCreateRequestHandlerFactoryTest.php @@ -2,21 +2,21 @@ declare(strict_types=1); -namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api; +namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Model\PetFactory; -use App\Factory\ModelFactoryInterface; +use App\Parsing\ParsingInterface; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\CreateRequestHandler; use App\ServiceFactory\RequestHandler\Api\Crud\PetCreateRequestHandlerFactory; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Validation\ValidatorInterface; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; /** * @covers \App\ServiceFactory\RequestHandler\Api\Crud\PetCreateRequestHandlerFactory @@ -29,28 +29,28 @@ final class PetCreateRequestHandlerFactoryTest extends TestCase public function testInvoke(): void { - /** @var ModelFactoryInterface $factory */ - $modelFactory = $this->getMockByCalls(ModelFactoryInterface::class); + /** @var DecoderInterface $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class); - /** @var RepositoryInterface $repository */ - $repository = $this->getMockByCalls(RepositoryInterface::class); + /** @var ParsingInterface $petParsing */ + $petParsing = $this->getMockByCalls(ParsingInterface::class); - /** @var RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class); + /** @var RepositoryInterface $petRepository */ + $petRepository = $this->getMockByCalls(RepositoryInterface::class); - /** @var ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var EncoderInterface $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(PetFactory::class)->willReturn($modelFactory), - Call::create('get')->with(PetRepository::class)->willReturn($repository), - Call::create('get')->with(RequestManagerInterface::class)->willReturn($requestManager), - Call::create('get')->with(ResponseManagerInterface::class)->willReturn($responseManager), - Call::create('get')->with(ValidatorInterface::class)->willReturn($validator), + Call::create('get')->with(DecoderInterface::class)->willReturn($decoder), + Call::create('get')->with(PetParsing::class)->willReturn($petParsing), + Call::create('get')->with(PetRepository::class)->willReturn($petRepository), + Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), ]); $factory = new PetCreateRequestHandlerFactory(); diff --git a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactoryTest.php b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactoryTest.php index e21e45f3..fe10f8eb 100644 --- a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactoryTest.php +++ b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetDeleteRequestHandlerFactoryTest.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api; +namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api\Crud; use App\Repository\PetRepository; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\DeleteRequestHandler; use App\ServiceFactory\RequestHandler\Api\Crud\PetDeleteRequestHandlerFactory; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; /** * @covers \App\ServiceFactory\RequestHandler\Api\Crud\PetDeleteRequestHandlerFactory @@ -25,16 +25,16 @@ final class PetDeleteRequestHandlerFactoryTest extends TestCase public function testInvoke(): void { - /** @var RepositoryInterface $repository */ - $repository = $this->getMockByCalls(RepositoryInterface::class); + /** @var RepositoryInterface $petRepository */ + $petRepository = $this->getMockByCalls(RepositoryInterface::class); - /** @var ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(PetRepository::class)->willReturn($repository), - Call::create('get')->with(ResponseManagerInterface::class)->willReturn($responseManager), + Call::create('get')->with(PetRepository::class)->willReturn($petRepository), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), ]); $factory = new PetDeleteRequestHandlerFactory(); diff --git a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactoryTest.php b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactoryTest.php index 67e1e5c7..cf180c14 100644 --- a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactoryTest.php +++ b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetListRequestHandlerFactoryTest.php @@ -2,21 +2,20 @@ declare(strict_types=1); -namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api; +namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api\Crud; -use App\Factory\Collection\PetCollectionFactory; -use App\Factory\CollectionFactoryInterface; +use App\Parsing\ParsingInterface; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\ListRequestHandler; use App\ServiceFactory\RequestHandler\Api\Crud\PetListRequestHandlerFactory; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Validation\ValidatorInterface; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; /** * @covers \App\ServiceFactory\RequestHandler\Api\Crud\PetListRequestHandlerFactory @@ -29,28 +28,24 @@ final class PetListRequestHandlerFactoryTest extends TestCase public function testInvoke(): void { - /** @var CollectionFactoryInterface $factory */ - $collectionfactory = $this->getMockByCalls(CollectionFactoryInterface::class); + /** @var ParsingInterface $petParsing */ + $petParsing = $this->getMockByCalls(ParsingInterface::class); - /** @var RepositoryInterface $repository */ - $repository = $this->getMockByCalls(RepositoryInterface::class); + /** @var RepositoryInterface $petRepository */ + $petRepository = $this->getMockByCalls(RepositoryInterface::class); - /** @var RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class); + /** @var EncoderInterface $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); - /** @var ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); - - /** @var ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class); + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(PetCollectionFactory::class)->willReturn($collectionfactory), - Call::create('get')->with(PetRepository::class)->willReturn($repository), - Call::create('get')->with(RequestManagerInterface::class)->willReturn($requestManager), - Call::create('get')->with(ResponseManagerInterface::class)->willReturn($responseManager), - Call::create('get')->with(ValidatorInterface::class)->willReturn($validator), + Call::create('get')->with(PetParsing::class)->willReturn($petParsing), + Call::create('get')->with(PetRepository::class)->willReturn($petRepository), + Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), ]); $factory = new PetListRequestHandlerFactory(); diff --git a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactoryTest.php b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactoryTest.php index b65029a9..9bf4dc77 100644 --- a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactoryTest.php +++ b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetReadRequestHandlerFactoryTest.php @@ -2,17 +2,20 @@ declare(strict_types=1); -namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api; +namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api\Crud; +use App\Parsing\ParsingInterface; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\ReadRequestHandler; use App\ServiceFactory\RequestHandler\Api\Crud\PetReadRequestHandlerFactory; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; /** * @covers \App\ServiceFactory\RequestHandler\Api\Crud\PetReadRequestHandlerFactory @@ -25,16 +28,24 @@ final class PetReadRequestHandlerFactoryTest extends TestCase public function testInvoke(): void { - /** @var RepositoryInterface $repository */ - $repository = $this->getMockByCalls(RepositoryInterface::class); + /** @var ParsingInterface $petParsing */ + $petParsing = $this->getMockByCalls(ParsingInterface::class); - /** @var ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var RepositoryInterface $petRepository */ + $petRepository = $this->getMockByCalls(RepositoryInterface::class); + + /** @var EncoderInterface $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(PetRepository::class)->willReturn($repository), - Call::create('get')->with(ResponseManagerInterface::class)->willReturn($responseManager), + Call::create('get')->with(PetParsing::class)->willReturn($petParsing), + Call::create('get')->with(PetRepository::class)->willReturn($petRepository), + Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), ]); $factory = new PetReadRequestHandlerFactory(); diff --git a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactoryTest.php b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactoryTest.php index e39a0fb2..cc6fa03c 100644 --- a/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactoryTest.php +++ b/tests/Unit/ServiceFactory/RequestHandler/Api/Crud/PetUpdateRequestHandlerFactoryTest.php @@ -2,19 +2,21 @@ declare(strict_types=1); -namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api; +namespace App\Tests\Unit\ServiceFactory\RequestHandler\Api\Crud; +use App\Parsing\ParsingInterface; +use App\Parsing\PetParsing; use App\Repository\PetRepository; use App\Repository\RepositoryInterface; use App\RequestHandler\Api\Crud\UpdateRequestHandler; use App\ServiceFactory\RequestHandler\Api\Crud\PetUpdateRequestHandlerFactory; -use Chubbyphp\ApiHttp\Manager\RequestManagerInterface; -use Chubbyphp\ApiHttp\Manager\ResponseManagerInterface; +use Chubbyphp\DecodeEncode\Decoder\DecoderInterface; +use Chubbyphp\DecodeEncode\Encoder\EncoderInterface; use Chubbyphp\Mock\Call; use Chubbyphp\Mock\MockByCallsTrait; -use Chubbyphp\Validation\ValidatorInterface; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseFactoryInterface; /** * @covers \App\ServiceFactory\RequestHandler\Api\Crud\PetUpdateRequestHandlerFactory @@ -27,24 +29,28 @@ final class PetUpdateRequestHandlerFactoryTest extends TestCase public function testInvoke(): void { - /** @var RepositoryInterface $repository */ - $repository = $this->getMockByCalls(RepositoryInterface::class); + /** @var DecoderInterface $decoder */ + $decoder = $this->getMockByCalls(DecoderInterface::class); - /** @var RequestManagerInterface $requestManager */ - $requestManager = $this->getMockByCalls(RequestManagerInterface::class); + /** @var ParsingInterface $petParsing */ + $petParsing = $this->getMockByCalls(ParsingInterface::class); - /** @var ResponseManagerInterface $responseManager */ - $responseManager = $this->getMockByCalls(ResponseManagerInterface::class); + /** @var RepositoryInterface $petRepository */ + $petRepository = $this->getMockByCalls(RepositoryInterface::class); - /** @var ValidatorInterface $validator */ - $validator = $this->getMockByCalls(ValidatorInterface::class); + /** @var EncoderInterface $encoder */ + $encoder = $this->getMockByCalls(EncoderInterface::class); + + /** @var ResponseFactoryInterface $responseFactory */ + $responseFactory = $this->getMockByCalls(ResponseFactoryInterface::class); /** @var ContainerInterface $container */ $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(PetRepository::class)->willReturn($repository), - Call::create('get')->with(RequestManagerInterface::class)->willReturn($requestManager), - Call::create('get')->with(ResponseManagerInterface::class)->willReturn($responseManager), - Call::create('get')->with(ValidatorInterface::class)->willReturn($validator), + Call::create('get')->with(DecoderInterface::class)->willReturn($decoder), + Call::create('get')->with(PetParsing::class)->willReturn($petParsing), + Call::create('get')->with(PetRepository::class)->willReturn($petRepository), + Call::create('get')->with(EncoderInterface::class)->willReturn($encoder), + Call::create('get')->with(ResponseFactoryInterface::class)->willReturn($responseFactory), ]); $factory = new PetUpdateRequestHandlerFactory(); diff --git a/tests/Unit/ServiceFactory/Serialization/NormalizationObjectMappingsFactoryTest.php b/tests/Unit/ServiceFactory/Serialization/NormalizationObjectMappingsFactoryTest.php deleted file mode 100644 index 1eda9483..00000000 --- a/tests/Unit/ServiceFactory/Serialization/NormalizationObjectMappingsFactoryTest.php +++ /dev/null @@ -1,46 +0,0 @@ -getMockByCalls(UrlGeneratorInterface::class); - - /** @var ContainerInterface $container */ - $container = $this->getMockByCalls(ContainerInterface::class, [ - Call::create('get')->with(UrlGeneratorInterface::class)->willReturn($urlGenerator), - ]); - - $factory = new NormalizationObjectMappingsFactory(); - - $service = $factory($container); - - self::assertEquals([ - new PetCollectionMapping($urlGenerator), - new PetMapping($urlGenerator), - new VaccinationMapping(), - ], $service); - } -} diff --git a/tests/Unit/ServiceFactory/Validation/ValidationMappingProviderFactoryTest.php b/tests/Unit/ServiceFactory/Validation/ValidationMappingProviderFactoryTest.php deleted file mode 100644 index 7b920dc1..00000000 --- a/tests/Unit/ServiceFactory/Validation/ValidationMappingProviderFactoryTest.php +++ /dev/null @@ -1,35 +0,0 @@ -