From 294c4da621326ec87bb5c6e20717d95a845b468b Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 15 Jan 2025 16:33:05 +0100 Subject: [PATCH 1/9] Specify checkout gateway mapping as name --- src/ApiResource/Gateway/CheckoutApiResource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ApiResource/Gateway/CheckoutApiResource.php b/src/ApiResource/Gateway/CheckoutApiResource.php index a1bc838..2ee2cfe 100644 --- a/src/ApiResource/Gateway/CheckoutApiResource.php +++ b/src/ApiResource/Gateway/CheckoutApiResource.php @@ -12,6 +12,7 @@ use App\Gateway\Tracking; use App\State\ApiResourceStateProvider; use App\State\Gateway\CheckoutStateProcessor; use AutoMapper\Attribute\MapFrom; +use AutoMapper\Attribute\MapTo; use Symfony\Component\Validator\Constraints as Assert; /** @@ -35,6 +36,7 @@ class CheckoutApiResource * The desired Gateway to checkout with. */ #[Assert\NotBlank()] + #[MapTo(property: 'gatewayName', transformer: 'source.gateway.name')] public GatewayApiResource $gateway; /** From 44be1e05fb0be1a2ccfcc37c4c9b9cc2ca832ca9 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 15 Jan 2025 16:33:26 +0100 Subject: [PATCH 2/9] Remove unnecessary accounting resource owner setting --- src/State/Accounting/AccountingStateProvider.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/State/Accounting/AccountingStateProvider.php b/src/State/Accounting/AccountingStateProvider.php index 3ef5d91..4c4e25a 100644 --- a/src/State/Accounting/AccountingStateProvider.php +++ b/src/State/Accounting/AccountingStateProvider.php @@ -65,21 +65,6 @@ class AccountingStateProvider implements ProviderInterface $resource = $this->autoMapper->map($accounting, AccountingApiResource::class); $resource->balance = $this->accountingService->calcBalance($accounting); - $owner = $accounting->getOwner(); - - switch ($owner::class) { - case User::class: - $resourceClass = UserApiResource::class; - break; - case Project::class: - $resourceClass = ProjectApiResource::class; - break; - case Tipjar::class: - $resourceClass = Tipjar::class; - } - - $resource->owner = $this->autoMapper->map($owner, $resourceClass); - return $resource; } } From 59883a7a42c6fb704868ad44c887af1c50ab7cb7 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Wed, 15 Jan 2025 16:34:37 +0100 Subject: [PATCH 3/9] Fix cascade persistence on charge and checkout --- src/Entity/Gateway/Charge.php | 2 +- src/Entity/Gateway/Checkout.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entity/Gateway/Charge.php b/src/Entity/Gateway/Charge.php index c7d6165..f7c348e 100644 --- a/src/Entity/Gateway/Charge.php +++ b/src/Entity/Gateway/Charge.php @@ -51,7 +51,7 @@ class Charge * The Accounting receiving the consequent Transaction for this GatewayCharge. */ #[Assert\NotBlank()] - #[ORM\ManyToOne] + #[ORM\ManyToOne(cascade: ['persist'])] #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; diff --git a/src/Entity/Gateway/Checkout.php b/src/Entity/Gateway/Checkout.php index 8394c43..bebb762 100644 --- a/src/Entity/Gateway/Checkout.php +++ b/src/Entity/Gateway/Checkout.php @@ -41,7 +41,7 @@ class Checkout * The Accounting that will issue the Transactions of the GatewayCharges after a successful checkout. */ #[Assert\NotBlank()] - #[ORM\ManyToOne] + #[ORM\ManyToOne(cascade: ['persist'])] #[ORM\JoinColumn(nullable: false)] private ?Accounting $origin = null; From 2b6b5c5cbcd4b43d52c18a8794cb7dfbca2482b4 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 10:59:22 +0100 Subject: [PATCH 4/9] Throw exception when non user checking out with stripe --- src/Gateway/Stripe/StripeGateway.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Gateway/Stripe/StripeGateway.php b/src/Gateway/Stripe/StripeGateway.php index dc69356..fe86b00 100644 --- a/src/Gateway/Stripe/StripeGateway.php +++ b/src/Gateway/Stripe/StripeGateway.php @@ -4,7 +4,7 @@ namespace App\Gateway\Stripe; use ApiPlatform\Api\IriConverterInterface; use App\Entity\Gateway\Checkout; -use App\Entity\User; +use App\Entity\User\User; use App\Gateway\ChargeType; use App\Gateway\CheckoutStatus; use App\Gateway\GatewayInterface; @@ -136,7 +136,10 @@ class StripeGateway implements GatewayInterface { $owner = $checkout->getOrigin()->getOwner(); if (!$owner instanceof User) { - return ''; + throw new \Exception(\sprintf( + "Checkout with Stripe must be performed by an User, instance of '%s' supplied", + $owner::class + )); } return $owner->getEmail(); From d6f932e48bd043e44c1bb3aed30989d3e74be384 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 11:10:01 +0100 Subject: [PATCH 5/9] Inject property transformers on decorated automapper --- config/services.yaml | 3 +++ src/Mapping/AutoMapper.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/config/services.yaml b/config/services.yaml index 8a6bc90..1ef66fb 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -20,6 +20,8 @@ services: tags: ['app.lib.economy.currency.exchange'] AutoMapper\Provider\ProviderInterface: tags: ['app.mapping.map_provider'] + AutoMapper\Transformer\PropertyTransformer\PropertyTransformerInterface: + tags: ['app.mapping.map_transformer'] # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name @@ -71,6 +73,7 @@ services: arguments: - '%kernel.cache_dir%' - !tagged 'app.mapping.map_provider' + - !tagged 'app.mapping.map_transformer' # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones diff --git a/src/Mapping/AutoMapper.php b/src/Mapping/AutoMapper.php index c9c10fc..1e20c33 100644 --- a/src/Mapping/AutoMapper.php +++ b/src/Mapping/AutoMapper.php @@ -18,10 +18,12 @@ class AutoMapper implements AutoMapperInterface public function __construct( ?string $cacheDirectory = null, iterable $mapProviders = [], + iterable $mapTransformers = [], ) { $this->innerMapper = InnerMapper::create( cacheDirectory: \sprintf('%s%s%s', $cacheDirectory, \DIRECTORY_SEPARATOR, self::CACHE_DIR), providers: $mapProviders, + propertyTransformers: $mapTransformers, ); } From 880221e11a3e14ee270cfa5efe1fde0e25ed5821 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 11:10:23 +0100 Subject: [PATCH 6/9] Use transformer to map entity gateway names to resource gateways --- .../Gateway/CheckoutApiResource.php | 2 ++ .../Transformer/GatewayNameMapTransformer.php | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/Mapping/Transformer/GatewayNameMapTransformer.php diff --git a/src/ApiResource/Gateway/CheckoutApiResource.php b/src/ApiResource/Gateway/CheckoutApiResource.php index 2ee2cfe..1ad6b23 100644 --- a/src/ApiResource/Gateway/CheckoutApiResource.php +++ b/src/ApiResource/Gateway/CheckoutApiResource.php @@ -9,6 +9,7 @@ use App\Entity\Gateway\Checkout; use App\Gateway\CheckoutStatus; use App\Gateway\Link; use App\Gateway\Tracking; +use App\Mapping\Transformer\GatewayNameMapTransformer; use App\State\ApiResourceStateProvider; use App\State\Gateway\CheckoutStateProcessor; use AutoMapper\Attribute\MapFrom; @@ -36,6 +37,7 @@ class CheckoutApiResource * The desired Gateway to checkout with. */ #[Assert\NotBlank()] + #[MapFrom(property: 'gatewayName', transformer: GatewayNameMapTransformer::class)] #[MapTo(property: 'gatewayName', transformer: 'source.gateway.name')] public GatewayApiResource $gateway; diff --git a/src/Mapping/Transformer/GatewayNameMapTransformer.php b/src/Mapping/Transformer/GatewayNameMapTransformer.php new file mode 100644 index 0000000..3393916 --- /dev/null +++ b/src/Mapping/Transformer/GatewayNameMapTransformer.php @@ -0,0 +1,24 @@ +gatewayLocator->get($value); + + $resource = new GatewayApiResource(); + $resource->name = $gateway::getName(); + $resource->supports = $gateway::getSupportedChargeTypes(); + + return $resource; + } +} From bfbaa4aa13e0a99f7364a866b1b43a077e88f7c1 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 12:08:46 +0100 Subject: [PATCH 7/9] Use mapping transformer for accounting resource balance --- .../Accounting/AccountingApiResource.php | 29 ++++++++++--------- .../AccountingBalanceMapTransformer.php | 22 ++++++++++++++ .../Accounting/AccountingStateProvider.php | 19 ++---------- 3 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 src/Mapping/Transformer/AccountingBalanceMapTransformer.php diff --git a/src/ApiResource/Accounting/AccountingApiResource.php b/src/ApiResource/Accounting/AccountingApiResource.php index b46ab69..b206dcf 100644 --- a/src/ApiResource/Accounting/AccountingApiResource.php +++ b/src/ApiResource/Accounting/AccountingApiResource.php @@ -11,8 +11,10 @@ use App\Entity\Money; use App\Entity\Project\Project; use App\Entity\Tipjar; use App\Entity\User\User; +use App\Mapping\Transformer\AccountingBalanceMapTransformer; use App\State\Accounting\AccountingStateProcessor; use App\State\Accounting\AccountingStateProvider; +use AutoMapper\Attribute\MapFrom; /** * v4 features an advanced economy model under the hood. @@ -41,18 +43,15 @@ class AccountingApiResource */ public string $currency; + /** + * The money currently held by the Accounting. + */ + #[MapFrom(Accounting::class, transformer: AccountingBalanceMapTransformer::class)] + public Money $balance; + #[API\ApiProperty(readable: false, writable: false)] public string $ownerClass; - #[API\ApiProperty(readable: false, writable: false)] - public ?UserApiResource $user = null; - - #[API\ApiProperty(readable: false, writable: false)] - public ?ProjectApiResource $project = null; - - #[API\ApiProperty(readable: false, writable: false)] - public ?Tipjar $tipjar = null; - /** * The resource owning this Accounting. * @@ -70,8 +69,12 @@ class AccountingApiResource } } - /** - * The money currently held by the Accounting. - */ - public Money $balance; + #[API\ApiProperty(readable: false, writable: false)] + public ?UserApiResource $user = null; + + #[API\ApiProperty(readable: false, writable: false)] + public ?ProjectApiResource $project = null; + + #[API\ApiProperty(readable: false, writable: false)] + public ?Tipjar $tipjar = null; } diff --git a/src/Mapping/Transformer/AccountingBalanceMapTransformer.php b/src/Mapping/Transformer/AccountingBalanceMapTransformer.php new file mode 100644 index 0000000..c829b35 --- /dev/null +++ b/src/Mapping/Transformer/AccountingBalanceMapTransformer.php @@ -0,0 +1,22 @@ +accountingService->calcBalance($source); + } +} diff --git a/src/State/Accounting/AccountingStateProvider.php b/src/State/Accounting/AccountingStateProvider.php index 4c4e25a..fb4eb11 100644 --- a/src/State/Accounting/AccountingStateProvider.php +++ b/src/State/Accounting/AccountingStateProvider.php @@ -10,12 +10,6 @@ use ApiPlatform\Metadata\Operation; use ApiPlatform\State\Pagination\TraversablePaginator; use ApiPlatform\State\ProviderInterface; use App\ApiResource\Accounting\AccountingApiResource; -use App\ApiResource\Project\ProjectApiResource; -use App\ApiResource\User\UserApiResource; -use App\Entity\Accounting\Accounting; -use App\Entity\Project\Project; -use App\Entity\Tipjar; -use App\Entity\User\User; use App\Mapping\AutoMapper; use App\Service\AccountingService; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -39,7 +33,7 @@ class AccountingStateProvider implements ProviderInterface $resources = []; foreach ($accountings as $accounting) { - $resources[] = $this->toResource($accounting); + $resources[] = $this->autoMapper->map($accounting, AccountingApiResource::class); } return new TraversablePaginator( @@ -56,15 +50,6 @@ class AccountingStateProvider implements ProviderInterface return null; } - return $this->toResource($accounting); - } - - private function toResource(Accounting $accounting): AccountingApiResource - { - /** @var AccountingApiResource */ - $resource = $this->autoMapper->map($accounting, AccountingApiResource::class); - $resource->balance = $this->accountingService->calcBalance($accounting); - - return $resource; + return $this->autoMapper->map($accounting, AccountingApiResource::class); } } From b9f9f0e71d5a4099259293e283cd0e55875d033d Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 12:09:15 +0100 Subject: [PATCH 8/9] Line break after function --- src/Mapping/Transformer/GatewayNameMapTransformer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mapping/Transformer/GatewayNameMapTransformer.php b/src/Mapping/Transformer/GatewayNameMapTransformer.php index 3393916..8f5b132 100644 --- a/src/Mapping/Transformer/GatewayNameMapTransformer.php +++ b/src/Mapping/Transformer/GatewayNameMapTransformer.php @@ -12,7 +12,8 @@ class GatewayNameMapTransformer implements PropertyTransformerInterface private GatewayLocator $gatewayLocator, ) {} - public function transform(mixed $value, object|array $source, array $context): mixed { + public function transform(mixed $value, object|array $source, array $context): mixed + { $gateway = $this->gatewayLocator->get($value); $resource = new GatewayApiResource(); From 84bfa13d2d6b82e870c386085a4f802e6d94f895 Mon Sep 17 00:00:00 2001 From: Daniel Subiabre Date: Thu, 16 Jan 2025 15:10:42 +0100 Subject: [PATCH 9/9] Move STRIPE_API_KEY from steps to phpunit job --- .github/workflows/phpunit.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index 82b84fe..13bdff3 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -10,6 +10,9 @@ jobs: phpunit: runs-on: ubuntu-latest + env: + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + services: mariadb: image: mariadb:10.11.2 @@ -25,8 +28,6 @@ jobs: - uses: actions/checkout@v4 - uses: php-actions/composer@v6 - env: - STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} with: php_version: ${{ env.PHP_VERSION }} php_extensions: ${{ env.PHP_EXTENSIONS }}