From d110cd4a0995f3032b9dbbfeb9b83733a1a6fc4a Mon Sep 17 00:00:00 2001 From: Ondrej Machulda Date: Mon, 20 Nov 2017 15:03:45 +0100 Subject: [PATCH 1/2] Builder for item properties setup request --- .../InvalidDomainModelArgumentException.php | 2 +- src/Exception/LogicException.php | 10 +++ src/RequestBuilder/AbstractRequestBuilder.php | 54 ++++++++++++++++ .../ItemPropertiesSetupRequestBuilder.php | 62 ++++++++++++++++++ src/RequestBuilder/RequestBuilderFactory.php | 41 ++++++++++++ .../ItemPropertiesSetupRequestBuilderTest.php | 63 +++++++++++++++++++ .../RequestBuilderFactoryTest.php | 59 +++++++++++++++++ 7 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/Exception/LogicException.php create mode 100644 src/RequestBuilder/AbstractRequestBuilder.php create mode 100644 src/RequestBuilder/ItemPropertiesSetupRequestBuilder.php create mode 100644 src/RequestBuilder/RequestBuilderFactory.php create mode 100644 tests/RequestBuilder/ItemPropertiesSetupRequestBuilderTest.php create mode 100644 tests/RequestBuilder/RequestBuilderFactoryTest.php diff --git a/src/Exception/InvalidDomainModelArgumentException.php b/src/Exception/InvalidDomainModelArgumentException.php index fa9d8b8..728440b 100644 --- a/src/Exception/InvalidDomainModelArgumentException.php +++ b/src/Exception/InvalidDomainModelArgumentException.php @@ -5,7 +5,7 @@ /** * Exception thrown when invalid argument is passed while creating domain model. */ -class InvalidDomainModelArgumentException extends AbstractMatejException +class InvalidDomainModelArgumentException extends LogicException { public static function forInconsistentNumberOfCommands( int $numberOfCommands, diff --git a/src/Exception/LogicException.php b/src/Exception/LogicException.php new file mode 100644 index 0000000..37e86e9 --- /dev/null +++ b/src/Exception/LogicException.php @@ -0,0 +1,10 @@ +requestManager = $requestManager; + + return $this; + } + + public function send(): Response + { + $this->assertRequestManagerIsAvailable(); + + return $this->requestManager->sendRequest($this->build()); + } + + private function assertRequestManagerIsAvailable(): void + { + if ($this->requestManager === null) { + throw new LogicException( + 'Instance of RequestManager must be set to request builder via setRequestManager() before' + . ' calling send()' + ); + } + } +} diff --git a/src/RequestBuilder/ItemPropertiesSetupRequestBuilder.php b/src/RequestBuilder/ItemPropertiesSetupRequestBuilder.php new file mode 100644 index 0000000..e40f048 --- /dev/null +++ b/src/RequestBuilder/ItemPropertiesSetupRequestBuilder.php @@ -0,0 +1,62 @@ +commands[] = $itemPropertySetup; + + return $this; + } + + /** + * @param ItemPropertySetup[] $itemPropertiesSetup + * @return self + */ + public function addProperties(array $itemPropertiesSetup): self + { + foreach ($itemPropertiesSetup as $itemPropertySetup) { + $this->addProperty($itemPropertySetup); + } + + return $this; + } + + /** + * Mark the request as "deleting" - item properties added to this request will be IRREVERSIBLY removed from + * all items in the database and the item property will from now be rejected by Matej. + */ + public function requestWillDelete(): self + { + $this->willDelete = true; + + return $this; + } + + public function build(): Request + { + if (empty($this->commands)) { + throw new LogicException( + 'At least one ItemPropertySetup command must be added to the builder before sending the request' + ); + } + + $method = $this->willDelete ? RequestMethodInterface::METHOD_DELETE : RequestMethodInterface::METHOD_PUT; + + return new Request(self::ENDPOINT_PATH, $method, $this->commands); + } +} diff --git a/src/RequestBuilder/RequestBuilderFactory.php b/src/RequestBuilder/RequestBuilderFactory.php new file mode 100644 index 0000000..4954fa8 --- /dev/null +++ b/src/RequestBuilder/RequestBuilderFactory.php @@ -0,0 +1,41 @@ +requestManager = $requestManager; + } + + public function itemPropertiesSetup(): ItemPropertiesSetupRequestBuilder + { + return $this->createConfiguredBuilder(ItemPropertiesSetupRequestBuilder::class); + } + + // TODO: builders for other endpoints + + /** + * @param string $builderClass + * @param array ...$args + * @return mixed + */ + private function createConfiguredBuilder(string $builderClass, ...$args) + { + /** @var AbstractRequestBuilder $requestBuilder */ + $requestBuilder = new $builderClass(...$args); + + $requestBuilder->setRequestManager($this->requestManager); + + return $requestBuilder; + } +} diff --git a/tests/RequestBuilder/ItemPropertiesSetupRequestBuilderTest.php b/tests/RequestBuilder/ItemPropertiesSetupRequestBuilderTest.php new file mode 100644 index 0000000..3efd4f5 --- /dev/null +++ b/tests/RequestBuilder/ItemPropertiesSetupRequestBuilderTest.php @@ -0,0 +1,63 @@ +expectException(LogicException::class); + $this->expectExceptionMessage('At least one ItemPropertySetup command must be added to the builder'); + $builder->build(); + } + + /** + * @test + * @dataProvider provideBuilderVariants + */ + public function shouldBuildRequestWithCommands(bool $shouldDelete, string $expectedMethod): void + { + $builder = new ItemPropertiesSetupRequestBuilder(); + + $command1 = ItemPropertySetup::timestamp('property1'); + $command2 = ItemPropertySetup::string('property2'); + $command3 = ItemPropertySetup::string('property3'); + + $builder->addProperty($command1); + $builder->addProperties([$command2, $command3]); + + if ($shouldDelete) { + $builder->requestWillDelete(); + } + + $request = $builder->build(); + + $this->assertInstanceOf(Request::class, $request); + $this->assertSame($expectedMethod, $request->getMethod()); + $this->assertSame('/item-properties', $request->getPath()); + $this->assertContainsOnlyInstancesOf(ItemPropertySetup::class, $request->getData()); + $this->assertSame($command1, $request->getData()[0]); + $this->assertSame($command2, $request->getData()[1]); + $this->assertSame($command3, $request->getData()[2]); + } + + /** + * @return array[] + */ + public function provideBuilderVariants(): array + { + return [ + 'builder to create item properties' => [false, RequestMethodInterface::METHOD_PUT], + 'builder to delete item properties' => [true, RequestMethodInterface::METHOD_DELETE], + ]; + } +} diff --git a/tests/RequestBuilder/RequestBuilderFactoryTest.php b/tests/RequestBuilder/RequestBuilderFactoryTest.php new file mode 100644 index 0000000..e74fcf2 --- /dev/null +++ b/tests/RequestBuilder/RequestBuilderFactoryTest.php @@ -0,0 +1,59 @@ +createMock(RequestManager::class); + $requestManagerMock->expects($this->once()) + ->method('sendRequest') + ->with($this->isInstanceOf(Request::class)) + ->willReturn(new Response(0, 0, 0, 0)); + + $factory = new RequestBuilderFactory($requestManagerMock); + + /** @var AbstractRequestBuilder $builder */ + $builder = $factory->$factoryMethod(); + + // Builders may require some minimal setup to be able to execute the build() method + $minimalBuilderInit($builder); + + $this->assertInstanceOf($expectedBuilderClass, $builder); + $this->assertInstanceOf(Request::class, $builder->build()); + + // Make sure the builder has been properly configured and it can execute send() via RequestManager mock: + $this->assertInstanceOf(Response::class, $builder->send()); + } + + /** + * @return array[] + */ + public function provideBuilderMethods(): array + { + $itemPropertiesSetupInit = function (ItemPropertiesSetupRequestBuilder $builder): void { + $builder->addProperty(ItemPropertySetup::timestamp('valid_from')); + }; + + return [ + ['itemPropertiesSetup', ItemPropertiesSetupRequestBuilder::class, $itemPropertiesSetupInit], + ]; + } +} From c8bbf2ed605ea70af322d8061a7de4a928448250 Mon Sep 17 00:00:00 2001 From: Ondrej Machulda Date: Mon, 20 Nov 2017 15:56:20 +0100 Subject: [PATCH 2/2] Temporarily setup PHPStan to ignore PHPUnit errors from Mock types This is only until PHPStan supports intersections in PHPDoc in 0.9. --- composer.json | 2 +- phpstan.neon | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 phpstan.neon diff --git a/composer.json b/composer.json index 2fae5ba..a9becb0 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ ], "analyze": [ "vendor/bin/php-cs-fixer fix -vvv --diff --dry-run --ansi", - "vendor/bin/phpstan.phar analyze ./src ./tests --level 7 --ansi" + "vendor/bin/phpstan.phar analyze ./src ./tests -c phpstan.neon --level 7 --ansi" ] } } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..ed08583 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,3 @@ +parameters: + ignoreErrors: + - '#expects .*, PHPUnit_Framework_MockObject_MockObject given#'