Merge pull request #47 from GoteoFoundation/fix/state_stack

[fix] State stack and data mappings
This commit is contained in:
2025-01-16 15:14:08 +01:00
committed by GitHub
11 changed files with 84 additions and 51 deletions
+3 -2
View File
@@ -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 }}
+3
View File
@@ -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
@@ -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;
}
@@ -9,9 +9,11 @@ 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;
use AutoMapper\Attribute\MapTo;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -35,6 +37,8 @@ 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;
/**
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+5 -2
View File
@@ -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();
+2
View File
@@ -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,
);
}
@@ -0,0 +1,22 @@
<?php
namespace App\Mapping\Transformer;
use App\Entity\Accounting\Accounting;
use App\Service\AccountingService;
use AutoMapper\Transformer\PropertyTransformer\PropertyTransformerInterface;
class AccountingBalanceMapTransformer implements PropertyTransformerInterface
{
public function __construct(
private AccountingService $accountingService,
) {}
/**
* @param Accounting $source
*/
public function transform(mixed $value, object|array $source, array $context): mixed
{
return $this->accountingService->calcBalance($source);
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Mapping\Transformer;
use App\ApiResource\Gateway\GatewayApiResource;
use App\Gateway\GatewayLocator;
use AutoMapper\Transformer\PropertyTransformer\PropertyTransformerInterface;
class GatewayNameMapTransformer implements PropertyTransformerInterface
{
public function __construct(
private GatewayLocator $gatewayLocator,
) {}
public function transform(mixed $value, object|array $source, array $context): mixed
{
$gateway = $this->gatewayLocator->get($value);
$resource = new GatewayApiResource();
$resource->name = $gateway::getName();
$resource->supports = $gateway::getSupportedChargeTypes();
return $resource;
}
}
@@ -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,30 +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);
$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;
return $this->autoMapper->map($accounting, AccountingApiResource::class);
}
}