diff --git a/src/Entities/Account.php b/src/Entities/Account.php index 56ce327..f6ff245 100644 --- a/src/Entities/Account.php +++ b/src/Entities/Account.php @@ -19,11 +19,14 @@ namespace D3\KlicktippPhpClient\Entities; use D3\KlicktippPhpClient\Exceptions\CommunicationException; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Account as AccountEndpoint; use Doctrine\Common\Collections\ArrayCollection; class Account extends Entity { + use EndpointTrait; + private ?AccountEndpoint $endpoint; public function __construct(array $elements = [], ?AccountEndpoint $endpoint = null) @@ -193,9 +196,10 @@ class Account extends Entity /** * @return null|bool * @throws CommunicationException + * @throws MissingEndpointException */ public function persist(): ?bool { - return $this->endpoint?->update(); + return $this->getEndpoint()->update(); } } diff --git a/src/Entities/EndpointTrait.php b/src/Entities/EndpointTrait.php new file mode 100644 index 0000000..8db7f80 --- /dev/null +++ b/src/Entities/EndpointTrait.php @@ -0,0 +1,36 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\KlicktippPhpClient\Entities; + +use Assert\Assert; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; +use D3\KlicktippPhpClient\Resources\Model; + +trait EndpointTrait +{ + private function getEndpoint(): Model + { + Assert::lazy() + ->setExceptionClass(MissingEndpointException::class) + ->that($this->endpoint) + ->isInstanceOf(Model::class) + ->verifyNow(); + + return $this->endpoint; + } +} diff --git a/src/Entities/Field.php b/src/Entities/Field.php index 9608ab7..3d8bf11 100644 --- a/src/Entities/Field.php +++ b/src/Entities/Field.php @@ -18,10 +18,13 @@ declare(strict_types=1); namespace D3\KlicktippPhpClient\Entities; use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Field as FieldEndpoint; class Field extends Entity { + use EndpointTrait; + private ?FieldEndpoint $endpoint; public function __construct(array $elements = [], ?FieldEndpoint $endpoint = null) @@ -50,11 +53,12 @@ class Field extends Entity /** * @return null|bool * @throws CommunicationException + * @throws MissingEndpointException */ public function persist(): ?bool { return !is_null($this->getId()) ? - $this->endpoint?->update( + $this->getEndpoint()->update( $this->getId(), $this->getName() ?? '' ) : diff --git a/src/Entities/Subscriber.php b/src/Entities/Subscriber.php index 49f361a..badc0df 100644 --- a/src/Entities/Subscriber.php +++ b/src/Entities/Subscriber.php @@ -18,12 +18,15 @@ declare(strict_types=1); namespace D3\KlicktippPhpClient\Entities; use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Subscriber as SubscriberEndpoint; use DateTime; use Doctrine\Common\Collections\ArrayCollection; class Subscriber extends Entity { + use EndpointTrait; + public const STATUS_SUBSCRIBED = 'subscribed'; public const BOUNCE_NOTBOUNCED = 'Not Bounced'; @@ -314,11 +317,12 @@ class Subscriber extends Entity /** * @return null|bool * @throws CommunicationException + * @throws MissingEndpointException */ public function persist(): ?bool { if (!is_null($this->getId())) { - $return = $this->endpoint?->update( + $return = $this->getEndpoint()->update( $this->getId(), $this->getFields()->toArray(), $this->getEmailAddress() ?? '', @@ -335,14 +339,11 @@ class Subscriber extends Entity /** * @throws CommunicationException + * @throws MissingEndpointException */ protected function persistTags(): void { - if (!$this->endpoint instanceof SubscriberEndpoint) { - return; - } - - $currentTags = $this->endpoint->getEntity($this->getId() ?? '')->getTags(); + $currentTags = $this->getEndpoint()->getEntity($this->getId() ?? '')->getTags(); $removeTags = array_diff( $currentTags?->toArray() ?? [], @@ -352,7 +353,7 @@ class Subscriber extends Entity if (count($removeTags)) { foreach ($removeTags as $removeTag) { if (!is_null($this->getEmailAddress())) { - $this->endpoint->untag($this->getEmailAddress(), $removeTag); + $this->getEndpoint()->untag($this->getEmailAddress(), $removeTag); } } } @@ -363,7 +364,7 @@ class Subscriber extends Entity ); if (count($addTags)) { if (!is_null($this->getEmailAddress())) { - $this->endpoint->tag($this->getEmailAddress(), $addTags); + $this->getEndpoint()->tag($this->getEmailAddress(), $addTags); } } } @@ -384,7 +385,7 @@ class Subscriber extends Entity ?string $smsNumber = null ): void { if (!$this->isSubscribed()) { - $this->endpoint->subscribe( + $this->getEndpoint()->subscribe( $this->getEmailAddress(), $listId, $tagId, diff --git a/src/Entities/Subscription.php b/src/Entities/Subscription.php index 9f1e3f1..17e1586 100644 --- a/src/Entities/Subscription.php +++ b/src/Entities/Subscription.php @@ -18,10 +18,13 @@ declare(strict_types=1); namespace D3\KlicktippPhpClient\Entities; use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\SubscriptionProcess; class Subscription extends Entity { + use EndpointTrait; + private ?SubscriptionProcess $endpoint; public function __construct(array $elements = [], ?SubscriptionProcess $endpoint = null) @@ -78,11 +81,12 @@ class Subscription extends Entity /** * @return null|bool * @throws CommunicationException + * @throws MissingEndpointException */ public function persist(): ?bool { return !is_null($this->getListId()) ? - $this->endpoint?->update( + $this->getEndpoint()->update( $this->getListId(), $this->getName() ?? '' ) : diff --git a/src/Entities/Tag.php b/src/Entities/Tag.php index f0ee888..6c5ab32 100644 --- a/src/Entities/Tag.php +++ b/src/Entities/Tag.php @@ -18,10 +18,13 @@ declare(strict_types=1); namespace D3\KlicktippPhpClient\Entities; use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Tag as TagEndpoint; class Tag extends Entity { + use EndpointTrait; + private ?TagEndpoint $endpoint; public function __construct(array $elements = [], ?TagEndpoint $endpoint = null) @@ -62,11 +65,12 @@ class Tag extends Entity /** * @return null|bool * @throws CommunicationException + * @throws MissingEndpointException */ public function persist(): ?bool { return !is_null($this->getId()) ? - $this->endpoint?->update( + $this->getEndpoint()->update( $this->getId(), $this->getName() ?? '', $this->getText() ?? '' diff --git a/src/Exceptions/MissingEndpointException.php b/src/Exceptions/MissingEndpointException.php new file mode 100644 index 0000000..53b446b --- /dev/null +++ b/src/Exceptions/MissingEndpointException.php @@ -0,0 +1,29 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\KlicktippPhpClient\Exceptions; + +use Assert\LazyAssertionException; + +class MissingEndpointException extends LazyAssertionException implements KlicktippExceptionInterface +{ + public function __construct($message, array $errors) + { + unset($message); + parent::__construct('required endpoint for this action missing', $errors); + } +} diff --git a/tests/unit/Entities/AccountTest.php b/tests/unit/Entities/AccountTest.php index f57cea0..026317c 100644 --- a/tests/unit/Entities/AccountTest.php +++ b/tests/unit/Entities/AccountTest.php @@ -19,6 +19,7 @@ namespace D3\KlicktippPhpClient\tests\unit\Entities; use D3\KlicktippPhpClient\Entities\Account; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Account as AccountEndpoint; use D3\KlicktippPhpClient\tests\TestCase; use Doctrine\Common\Collections\ArrayCollection; @@ -526,6 +527,10 @@ class AccountTest extends TestCase $sut = new Account([AccountEndpoint::UID => 'foo'], $endpointSet ? $endpointMock : null); + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->assertSame( $expectedReturn, $this->callMethod( diff --git a/tests/unit/Entities/FieldTest.php b/tests/unit/Entities/FieldTest.php index 6d44a96..da35395 100644 --- a/tests/unit/Entities/FieldTest.php +++ b/tests/unit/Entities/FieldTest.php @@ -19,6 +19,7 @@ namespace D3\KlicktippPhpClient\tests\unit\Entities; use D3\KlicktippPhpClient\Entities\Field; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Field as FieldEndpoint; use D3\KlicktippPhpClient\tests\TestCase; use Generator; @@ -184,6 +185,10 @@ class FieldTest extends TestCase $endpointSet ? $endpointMock : null ); + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->assertSame( $expectedReturn, $this->callMethod( diff --git a/tests/unit/Entities/SubscriberTest.php b/tests/unit/Entities/SubscriberTest.php index d7c063c..f0773b7 100644 --- a/tests/unit/Entities/SubscriberTest.php +++ b/tests/unit/Entities/SubscriberTest.php @@ -19,6 +19,7 @@ namespace D3\KlicktippPhpClient\tests\unit\Entities; use D3\KlicktippPhpClient\Entities\Subscriber; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Subscriber as SubscriberEndpoint; use D3\KlicktippPhpClient\tests\TestCase; use DateTime; @@ -844,6 +845,10 @@ class SubscriberTest extends TestCase ->getMock(); $sut->expects($persistTagsInvocation)->method('persistTags'); + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->assertSame( $expectedReturn, $this->callMethod( @@ -856,7 +861,7 @@ class SubscriberTest extends TestCase public static function persistDataProvider(): Generator { yield 'has endpoint' => [true, 'fixture', self::once(), self::once(), true]; - yield 'has no endpoint' => [false, 'fixture', self::never(), self::once(), null]; + yield 'has no endpoint' => [false, 'fixture', self::never(), self::never(), null]; yield 'has endpoint, no id' => [true, null, self::never(), self::never(), null]; } @@ -895,6 +900,10 @@ class SubscriberTest extends TestCase $sut->set('tags', $newTagList); } + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->callMethod( $sut, 'persistTags' @@ -983,14 +992,14 @@ class SubscriberTest extends TestCase * @test * @throws ReflectionException * @covers \D3\KlicktippPhpClient\Entities\Subscriber::resubscribe + * @covers \D3\KlicktippPhpClient\Entities\Subscriber::getEndpoint * @dataProvider resubscribeDataProvider */ public function testReSubscribe( bool $endpointSet, InvokedCount $subscribeExpections, bool $isSubscribed - ): void - { + ): void { $endpointMock = $this->getMockBuilder(SubscriberEndpoint::class) ->disableOriginalConstructor() ->onlyMethods(['subscribe']) @@ -1004,6 +1013,10 @@ class SubscriberTest extends TestCase $sut->method('isSubscribed')->willReturn($isSubscribed); $sut->method('getEmailAddress')->willReturn('mail@mydomain.tld'); + if (!$endpointSet && !$isSubscribed) { + $this->expectException(MissingEndpointException::class); + } + $this->callMethod( $sut, 'resubscribe', diff --git a/tests/unit/Entities/SubscriptionTest.php b/tests/unit/Entities/SubscriptionTest.php index f27a489..aa8cf4e 100644 --- a/tests/unit/Entities/SubscriptionTest.php +++ b/tests/unit/Entities/SubscriptionTest.php @@ -19,6 +19,7 @@ namespace D3\KlicktippPhpClient\tests\unit\Entities; use D3\KlicktippPhpClient\Entities\Subscription; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\SubscriptionProcess as SubscriptionEndpoint; use D3\KlicktippPhpClient\tests\TestCase; use Generator; @@ -223,6 +224,10 @@ class SubscriptionTest extends TestCase $endpointSet ? $endpointMock : null ); + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->assertSame( $expectedReturn, $this->callMethod( diff --git a/tests/unit/Entities/TagTest.php b/tests/unit/Entities/TagTest.php index 828b18d..a8fa4a5 100644 --- a/tests/unit/Entities/TagTest.php +++ b/tests/unit/Entities/TagTest.php @@ -19,6 +19,7 @@ namespace D3\KlicktippPhpClient\tests\unit\Entities; use D3\KlicktippPhpClient\Entities\Tag; use D3\KlicktippPhpClient\Exceptions\InvalidCredentialTypeException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; use D3\KlicktippPhpClient\Resources\Tag as TagEndpoint; use D3\KlicktippPhpClient\tests\TestCase; use Generator; @@ -198,6 +199,10 @@ class TagTest extends TestCase $endpointSet ? $endpointMock : null ); + if (!$endpointSet) { + $this->expectException(MissingEndpointException::class); + } + $this->assertSame( $expectedReturn, $this->callMethod( diff --git a/tests/unit/Exceptions/MissingEndpointExceptionTest.php b/tests/unit/Exceptions/MissingEndpointExceptionTest.php new file mode 100644 index 0000000..2d6c009 --- /dev/null +++ b/tests/unit/Exceptions/MissingEndpointExceptionTest.php @@ -0,0 +1,54 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\KlicktippPhpClient\tests\unit\Exceptions; + +use D3\KlicktippPhpClient\Exceptions\CommunicationException; +use D3\KlicktippPhpClient\Exceptions\MissingEndpointException; +use D3\KlicktippPhpClient\tests\TestCase; +use ReflectionException; + +/** + * @covers \D3\KlicktippPhpClient\Exceptions\MissingEndpointException + */ +class MissingEndpointExceptionTest extends TestCase +{ + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\KlicktippPhpClient\Exceptions\MissingEndpointException::__construct + */ + public function testConstructor(): void + { + /** @var MissingEndpointException $sutMock */ + $sutMock = $this->getMockBuilder(MissingEndpointException::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->callMethod( + $sutMock, + '__construct', + ['myMessage', []] + ); + + $this->assertMatchesRegularExpression( + '/.*?endpoint.*?missing.*/i', + $sutMock->getMessage() + ); + } +}