From 62922b5c30bf49960326cd45e695b9eabb35c04c Mon Sep 17 00:00:00 2001 From: Martin Boer Date: Thu, 6 Mar 2025 15:00:03 +0100 Subject: [PATCH 1/2] :recycle: transformers can now define their resource type using an enum --- src/Enums/NullResource.php | 10 ++++ src/Transformers/AbstractTransformer.php | 16 ++++-- tests/Stubs/TransformerStub.php | 10 ---- .../Transformers/AbstractTransformerTest.php | 52 ++++++++++++++++++- 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 src/Enums/NullResource.php diff --git a/src/Enums/NullResource.php b/src/Enums/NullResource.php new file mode 100644 index 0000000..18a2f7c --- /dev/null +++ b/src/Enums/NullResource.php @@ -0,0 +1,10 @@ +type)) { - throw new TransformerException('Error no transformer resource `type` set for model'); + $type = match (true) { + !empty($this->type) => $this->type, + $this->resourceType instanceof BackedEnum => $this->resourceType->value, + $this->resourceType instanceof UnitEnum => $this->resourceType->name, + }; + + if (empty($type)) { + throw new TransformerException('Error no `resourceType` or `type` set on transformer'); } - return $this->type; + return $type; } /** diff --git a/tests/Stubs/TransformerStub.php b/tests/Stubs/TransformerStub.php index 4e101df..3af9681 100644 --- a/tests/Stubs/TransformerStub.php +++ b/tests/Stubs/TransformerStub.php @@ -15,16 +15,6 @@ class TransformerStub extends AbstractTransformer protected string $type = 'test'; - /** - * Helper function to reset the type for the abstract exception test. - */ - public function clearType(): self - { - $this->type = ''; - - return $this; - } - public function getId($model): string { return 'mockId'; diff --git a/tests/Transformers/AbstractTransformerTest.php b/tests/Transformers/AbstractTransformerTest.php index 345f308..9ac414b 100644 --- a/tests/Transformers/AbstractTransformerTest.php +++ b/tests/Transformers/AbstractTransformerTest.php @@ -14,6 +14,17 @@ use MyParcelCom\JsonApi\Transformers\TransformerFactory; use PHPUnit\Framework\TestCase; use stdClass; +use UnitEnum; + +enum TestUnitEnum +{ + case test; +} + +enum TestBackedEnum: string +{ + case TYPE = 'test'; +} class AbstractTransformerTest extends TestCase { @@ -64,10 +75,49 @@ public function testTransform(): void $this->assertEmpty($this->transformer->getRelationLink($this->model)); } + public function testTypeUsingUnitEnum(): void + { + $transformer = new class extends TransformerStub { + protected string $type = ''; + protected UnitEnum $resourceType = TestUnitEnum::test; + + public function __construct() + { + parent::__construct(Mockery::mock(TransformerFactory::class)); + } + }; + + $this->assertSame('test', $transformer->getType()); + } + + public function testTypeUsingBackedEnum(): void + { + $transformer = new class extends TransformerStub { + protected string $type = ''; + protected UnitEnum $resourceType = TestBackedEnum::TYPE; + + public function __construct() + { + parent::__construct(Mockery::mock(TransformerFactory::class)); + } + }; + + $this->assertSame('test', $transformer->getType()); + } + public function testGetTypeException(): void { + $transformer = new class extends TransformerStub { + protected string $type = ''; + + public function __construct() + { + parent::__construct(Mockery::mock(TransformerFactory::class)); + } + }; + $this->expectException(TransformerException::class); - $this->transformer->clearType()->getType(); + $transformer->getType(); } public function testTransformRelationship(): void From 31f9641e71b364f45dd31c669af473b3a8471b87 Mon Sep 17 00:00:00 2001 From: Martin Boer Date: Thu, 6 Mar 2025 15:25:00 +0100 Subject: [PATCH 2/2] :recycle: make exceptions accept enum as resource type --- src/Exceptions/InvalidScopeException.php | 11 +++----- src/Exceptions/MissingScopeException.php | 11 +++----- .../RelationshipCannotBeModifiedException.php | 11 ++++++-- .../ResourceHandledBy3rdPartyException.php | 10 +++++--- src/Exceptions/ResourceNotFoundException.php | 10 +++++--- src/Traits/EnumTrait.php | 20 +++++++++++++++ src/Transformers/AbstractTransformer.php | 9 +++---- tests/Enums/TestBackedEnum.php | 10 ++++++++ tests/Enums/TestUnitEnum.php | 10 ++++++++ tests/Exceptions/ExceptionsTest.php | 25 +++++++++++++++++++ .../Transformers/AbstractTransformerTest.php | 14 +++-------- 11 files changed, 100 insertions(+), 41 deletions(-) create mode 100644 src/Traits/EnumTrait.php create mode 100644 tests/Enums/TestBackedEnum.php create mode 100644 tests/Enums/TestUnitEnum.php diff --git a/src/Exceptions/InvalidScopeException.php b/src/Exceptions/InvalidScopeException.php index 001881c..ed4f9e6 100644 --- a/src/Exceptions/InvalidScopeException.php +++ b/src/Exceptions/InvalidScopeException.php @@ -4,10 +4,9 @@ namespace MyParcelCom\JsonApi\Exceptions; -use BackedEnum; +use MyParcelCom\JsonApi\Traits\EnumTrait; use Symfony\Component\HttpFoundation\Response; use Throwable; -use UnitEnum; /** * This exception is thrown when a scope is either not available at all, not unavailable for the chosen grant type or @@ -15,14 +14,12 @@ */ class InvalidScopeException extends AbstractException { + use EnumTrait; + public function __construct(array $scopeSlugs, Throwable $previous = null) { $scopeStrings = collect($scopeSlugs) - ->map(fn ($scope) => match (true) { - $scope instanceof BackedEnum => $scope->value, - $scope instanceof UnitEnum => $scope->name, - default => $scope, - }) + ->map(fn (mixed $scopeSlug) => $this->getEnumValue($scopeSlug)) ->toArray(); parent::__construct( diff --git a/src/Exceptions/MissingScopeException.php b/src/Exceptions/MissingScopeException.php index b2e9421..bc6a4ac 100644 --- a/src/Exceptions/MissingScopeException.php +++ b/src/Exceptions/MissingScopeException.php @@ -4,21 +4,18 @@ namespace MyParcelCom\JsonApi\Exceptions; -use BackedEnum; +use MyParcelCom\JsonApi\Traits\EnumTrait; use Symfony\Component\HttpFoundation\Response; use Throwable; -use UnitEnum; class MissingScopeException extends AbstractException { + use EnumTrait; + public function __construct(array $scopeSlugs, Throwable $previous = null) { $scopeStrings = collect($scopeSlugs) - ->map(fn ($scope) => match (true) { - $scope instanceof BackedEnum => $scope->value, - $scope instanceof UnitEnum => $scope->name, - default => $scope, - }) + ->map(fn (mixed $scopeSlug) => $this->getEnumValue($scopeSlug)) ->toArray(); parent::__construct( diff --git a/src/Exceptions/RelationshipCannotBeModifiedException.php b/src/Exceptions/RelationshipCannotBeModifiedException.php index 2e32af9..3629a5a 100644 --- a/src/Exceptions/RelationshipCannotBeModifiedException.php +++ b/src/Exceptions/RelationshipCannotBeModifiedException.php @@ -4,15 +4,22 @@ namespace MyParcelCom\JsonApi\Exceptions; +use MyParcelCom\JsonApi\Traits\EnumTrait; use Symfony\Component\HttpFoundation\Response; use Throwable; +use UnitEnum; class RelationshipCannotBeModifiedException extends AbstractException { - public function __construct(string $relationshipType, Throwable $previous = null) + use EnumTrait; + + public function __construct(UnitEnum|string $relationshipType, Throwable $previous = null) { parent::__construct( - "The relationship of type '{$relationshipType}' cannot be modified on this resource.", + sprintf( + "The relationship of type '%s' cannot be modified on this resource.", + $this->getEnumValue($relationshipType), + ), self::RELATIONSHIP_CANNOT_BE_MODIFIED, Response::HTTP_FORBIDDEN, $previous, diff --git a/src/Exceptions/ResourceHandledBy3rdPartyException.php b/src/Exceptions/ResourceHandledBy3rdPartyException.php index 670ccea..0cf1061 100644 --- a/src/Exceptions/ResourceHandledBy3rdPartyException.php +++ b/src/Exceptions/ResourceHandledBy3rdPartyException.php @@ -4,17 +4,19 @@ namespace MyParcelCom\JsonApi\Exceptions; +use MyParcelCom\JsonApi\Traits\EnumTrait; use Symfony\Component\HttpFoundation\Response; use Throwable; +use UnitEnum; class ResourceHandledBy3rdPartyException extends AbstractException { - public function __construct(string $resourceType, string $platform, Throwable $previous = null) - { - $message = sprintf('One or more of the %s resource is handled by a 3rd party.', $resourceType); + use EnumTrait; + public function __construct(UnitEnum|string $resourceType, string $platform, Throwable $previous = null) + { parent::__construct( - $message, + sprintf('One or more of the %s resource is handled by a 3rd party.', $this->getEnumValue($resourceType)), self::RESOURCE_HANDLED_BY_3RD_PARTY, Response::HTTP_CONFLICT, $previous, diff --git a/src/Exceptions/ResourceNotFoundException.php b/src/Exceptions/ResourceNotFoundException.php index abcd851..3f983d3 100644 --- a/src/Exceptions/ResourceNotFoundException.php +++ b/src/Exceptions/ResourceNotFoundException.php @@ -4,17 +4,19 @@ namespace MyParcelCom\JsonApi\Exceptions; +use MyParcelCom\JsonApi\Traits\EnumTrait; use Symfony\Component\HttpFoundation\Response; use Throwable; +use UnitEnum; class ResourceNotFoundException extends AbstractException { - public function __construct(string $resourceType, Throwable $previous = null) - { - $message = sprintf('One or more of the %s resource could not be found.', $resourceType); + use EnumTrait; + public function __construct(UnitEnum|string $resourceType, Throwable $previous = null) + { parent::__construct( - $message, + sprintf('One or more of the %s resource could not be found.', $this->getEnumValue($resourceType)), self::RESOURCE_NOT_FOUND, Response::HTTP_NOT_FOUND, $previous, diff --git a/src/Traits/EnumTrait.php b/src/Traits/EnumTrait.php new file mode 100644 index 0000000..32d160d --- /dev/null +++ b/src/Traits/EnumTrait.php @@ -0,0 +1,20 @@ + $enum->value, + $enum instanceof UnitEnum => $enum->name, + default => $enum, + }; + } +} diff --git a/src/Transformers/AbstractTransformer.php b/src/Transformers/AbstractTransformer.php index 757c52a..78efc2e 100644 --- a/src/Transformers/AbstractTransformer.php +++ b/src/Transformers/AbstractTransformer.php @@ -4,18 +4,19 @@ namespace MyParcelCom\JsonApi\Transformers; -use BackedEnum; use DateTime; use Illuminate\Contracts\Routing\UrlGenerator; use MyParcelCom\JsonApi\Enums\NullResource; use MyParcelCom\JsonApi\Resources\ResourceIdentifier; use MyParcelCom\JsonApi\Traits\ArrayFilterTrait; +use MyParcelCom\JsonApi\Traits\EnumTrait; use UnitEnum; /** @template TModel */ abstract class AbstractTransformer implements TransformerInterface { use ArrayFilterTrait; + use EnumTrait; protected UrlGenerator $urlGenerator; @@ -136,11 +137,7 @@ public function transformIdentifier($model, bool $includeMeta = false): array */ public function getType(): string { - $type = match (true) { - !empty($this->type) => $this->type, - $this->resourceType instanceof BackedEnum => $this->resourceType->value, - $this->resourceType instanceof UnitEnum => $this->resourceType->name, - }; + $type = empty($this->type) ? $this->getEnumValue($this->resourceType) : $this->type; if (empty($type)) { throw new TransformerException('Error no `resourceType` or `type` set on transformer'); diff --git a/tests/Enums/TestBackedEnum.php b/tests/Enums/TestBackedEnum.php new file mode 100644 index 0000000..961347c --- /dev/null +++ b/tests/Enums/TestBackedEnum.php @@ -0,0 +1,10 @@ +assertEquals('One or more of the API resource could not be found.', $exception->getMessage()); } + public function testResourceNotFoundExceptionUsingEnum(): void + { + $exception = new ResourceNotFoundException(TestBackedEnum::TEST); + + $this->assertEquals('One or more of the test resource could not be found.', $exception->getMessage()); + } + public function testUnprocessableEntityException(): void { $exception = new UnprocessableEntityException('RAW', new Exception('G-Star')); @@ -279,6 +287,16 @@ public function testRelationshipCannotBeModifiedException(): void ); } + public function testRelationshipCannotBeModifiedExceptionUsingEnum(): void + { + $exception = new RelationshipCannotBeModifiedException(TestBackedEnum::TEST); + + $this->assertEquals( + "The relationship of type 'test' cannot be modified on this resource.", + $exception->getMessage(), + ); + } + public function testInvalidCredentialsException(): void { $errors = [ @@ -321,4 +339,11 @@ public function testResourceHandledBy3rdPartyException(): void self::assertEquals(409, $exception->getStatus()); self::assertEquals(['3rd_party' => 'Bol'], $exception->getMeta()); } + + public function testResourceHandledBy3rdPartyExceptionUsingEnum(): void + { + $exception = new ResourceHandledBy3rdPartyException(TestBackedEnum::TEST, 'Bol'); + + $this->assertEquals('One or more of the test resource is handled by a 3rd party.', $exception->getMessage()); + } } diff --git a/tests/Transformers/AbstractTransformerTest.php b/tests/Transformers/AbstractTransformerTest.php index 9ac414b..5d5a1e1 100644 --- a/tests/Transformers/AbstractTransformerTest.php +++ b/tests/Transformers/AbstractTransformerTest.php @@ -9,6 +9,8 @@ use Mockery; use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration; use MyParcelCom\JsonApi\Resources\ResourceIdentifier; +use MyParcelCom\JsonApi\Tests\Enums\TestBackedEnum; +use MyParcelCom\JsonApi\Tests\Enums\TestUnitEnum; use MyParcelCom\JsonApi\Tests\Stubs\TransformerStub; use MyParcelCom\JsonApi\Transformers\TransformerException; use MyParcelCom\JsonApi\Transformers\TransformerFactory; @@ -16,16 +18,6 @@ use stdClass; use UnitEnum; -enum TestUnitEnum -{ - case test; -} - -enum TestBackedEnum: string -{ - case TYPE = 'test'; -} - class AbstractTransformerTest extends TestCase { use MockeryPHPUnitIntegration; @@ -94,7 +86,7 @@ public function testTypeUsingBackedEnum(): void { $transformer = new class extends TransformerStub { protected string $type = ''; - protected UnitEnum $resourceType = TestBackedEnum::TYPE; + protected UnitEnum $resourceType = TestBackedEnum::TEST; public function __construct() {