Merge pull request #99 from goteo/feat/project-reward-claim-validation
[feat] Project RewardClaim validation
This commit is contained in:
@@ -4,10 +4,13 @@ namespace App\ApiResource\Project;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\State\Options;
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\ApiResource\Gateway\ChargeApiResource;
|
||||
use App\ApiResource\User\UserApiResource;
|
||||
use App\Entity\Project\RewardClaim;
|
||||
use App\State\ApiResourceStateProvider;
|
||||
use App\State\Project\RewardClaimStateProcessor;
|
||||
use App\Validator\AvailableRewardUnits;
|
||||
use App\Validator\EnoughRewardCharge;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
@@ -19,20 +22,28 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
provider: ApiResourceStateProvider::class,
|
||||
processor: RewardClaimStateProcessor::class
|
||||
)]
|
||||
#[EnoughRewardCharge()]
|
||||
class RewardClaimApiResource
|
||||
{
|
||||
#[API\ApiProperty(identifier: true, writable: false)]
|
||||
public int $id;
|
||||
|
||||
/**
|
||||
* The ProjectReward being claimed.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
public RewardApiResource $reward;
|
||||
|
||||
/**
|
||||
* The User claiming the ProjectReward.
|
||||
*/
|
||||
#[API\ApiProperty(writable: false)]
|
||||
public UserApiResource $owner;
|
||||
|
||||
/**
|
||||
* The GatewayCharge granting access to the ProjectReward.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
public ChargeApiResource $charge;
|
||||
|
||||
/**
|
||||
* The ProjectReward being claimed.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
#[AvailableRewardUnits()]
|
||||
public RewardApiResource $reward;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Entity\Project;
|
||||
|
||||
use App\Entity\Gateway\Charge;
|
||||
use App\Entity\Interface\UserOwnedInterface;
|
||||
use App\Entity\Trait\UserOwnedTrait;
|
||||
use App\Entity\User\User;
|
||||
@@ -22,6 +23,10 @@ class RewardClaim implements UserOwnedInterface
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\OneToOne(cascade: ['persist', 'remove'])]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Charge $charge = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'claims')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Reward $reward = null;
|
||||
@@ -35,6 +40,18 @@ class RewardClaim implements UserOwnedInterface
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getCharge(): ?Charge
|
||||
{
|
||||
return $this->charge;
|
||||
}
|
||||
|
||||
public function setCharge(Charge $charge): static
|
||||
{
|
||||
$this->charge = $charge;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReward(): ?Reward
|
||||
{
|
||||
return $this->reward;
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Project;
|
||||
|
||||
use App\Entity\Project\Reward;
|
||||
use App\Entity\Project\RewardClaim;
|
||||
|
||||
class RewardService
|
||||
{
|
||||
/**
|
||||
* Processes a RewardClaim for the set Reward.
|
||||
*
|
||||
* @return RewardClaim The processed RewardClaim with updated Reward
|
||||
*/
|
||||
public function processClaim(RewardClaim $claim): RewardClaim
|
||||
{
|
||||
$reward = $claim->getReward();
|
||||
|
||||
if (!$reward->hasUnits()) {
|
||||
return $claim;
|
||||
}
|
||||
|
||||
$available = $reward->getUnitsAvailable();
|
||||
|
||||
if ($available < 1) {
|
||||
throw new \Exception('The claimed Reward has no units available');
|
||||
}
|
||||
|
||||
$reward->addClaim($claim);
|
||||
$reward->setUnitsAvailable($available - 1);
|
||||
|
||||
return $claim->setReward($reward);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ use App\ApiResource\Project\RewardClaimApiResource;
|
||||
use App\Entity\Project\RewardClaim;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\Auth\AuthService;
|
||||
use App\Service\Project\RewardService;
|
||||
use App\State\EntityStateProcessor;
|
||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||
|
||||
@@ -18,7 +17,6 @@ class RewardClaimStateProcessor implements ProcessorInterface
|
||||
private EntityStateProcessor $entityStateProcessor,
|
||||
private AutoMapper $autoMapper,
|
||||
private AuthService $authService,
|
||||
private RewardService $rewardService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -41,7 +39,6 @@ class RewardClaimStateProcessor implements ProcessorInterface
|
||||
$claim->setOwner($owner);
|
||||
}
|
||||
|
||||
$claim = $this->rewardService->processClaim($claim);
|
||||
$claim = $this->entityStateProcessor->process($claim, $operation, $uriVariables, $context);
|
||||
|
||||
return $this->autoMapper->map($claim, $data);
|
||||
|
||||
@@ -21,9 +21,13 @@ class SupportStateProcessor implements ProcessorInterface
|
||||
private EntityManagerInterface $entityManager,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param SupportApiResource $data
|
||||
*
|
||||
* @return SupportApiResource
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
|
||||
{
|
||||
// Verify if the user is authenticated
|
||||
$user = $this->authService->getUser();
|
||||
if (!$user) {
|
||||
throw new AuthenticationException();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
final class AvailableRewardUnits extends Constraint
|
||||
{
|
||||
public string $message = 'The ProjectReward "{{ id }}" has no units available.';
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validator;
|
||||
|
||||
use App\ApiResource\Project\RewardApiResource;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
final class AvailableRewardUnitsValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @param RewardApiResource $value
|
||||
*/
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!$value->hasUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value->hasUnits && $value->unitsAvailable > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ id }}', $value->id)
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class EnoughRewardCharge extends Constraint
|
||||
{
|
||||
public string $message = 'The charged money is not enough to claim the ProjectReward.';
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Validator;
|
||||
|
||||
use App\ApiResource\Project\RewardClaimApiResource;
|
||||
use App\Library\Economy\MoneyService;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
final class EnoughRewardChargeValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private MoneyService $money,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param RewardClaimApiResource $value
|
||||
* @param EnoughRewardCharge $constraint
|
||||
*/
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
$charge = $value->charge->money;
|
||||
$reward = $value->reward->money;
|
||||
|
||||
if ($this->money->isMoreOrSame($charge, $reward)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user