Merge branch 'develop' into fix/gv4-048-paypal-webhook-signature-verification
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\ApiResource\Accounting;
|
||||
use ApiPlatform\Doctrine\Orm\State\Options;
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\ApiResource\Project\ProjectApiResource;
|
||||
use App\ApiResource\TipjarApiResource;
|
||||
use App\ApiResource\User\UserApiResource;
|
||||
use App\Entity\Accounting\Accounting;
|
||||
use App\Entity\Money;
|
||||
@@ -56,7 +57,7 @@ class AccountingApiResource
|
||||
/**
|
||||
* The resource owning this Accounting.
|
||||
*
|
||||
* @return UserApiResource|ProjectApiResource|Tipjar
|
||||
* @return UserApiResource|ProjectApiResource|TipjarApiResource
|
||||
*/
|
||||
public function getOwner(): ?object
|
||||
{
|
||||
@@ -79,5 +80,5 @@ class AccountingApiResource
|
||||
public ?ProjectApiResource $project = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?Tipjar $tipjar = null;
|
||||
public ?TipjarApiResource $tipjar = null;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
provider: ApiResourceStateProvider::class
|
||||
)]
|
||||
#[API\Get()]
|
||||
#[API\GetCollection(security: "is_granted('IS_AUTHENTICATED_FULLY')")]
|
||||
#[API\GetCollection()]
|
||||
#[API\Patch(
|
||||
input: ChargeUpdationDto::class,
|
||||
processor: ChargeStateProcessor::class,
|
||||
@@ -37,7 +37,7 @@ class ChargeApiResource
|
||||
/**
|
||||
* The Checkout to which this Charge item belongs to.
|
||||
*/
|
||||
#[API\ApiProperty(writable: false)]
|
||||
#[API\ApiProperty(writable: false, security: 'is_granted("IS_AUTHENTICATED_FULLY")')]
|
||||
public CheckoutApiResource $checkout;
|
||||
|
||||
/**
|
||||
@@ -85,5 +85,8 @@ class ChargeApiResource
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
#[API\ApiFilter(Filter\SearchFilter::class, strategy: 'exact')]
|
||||
public ChargeStatus $status = ChargeStatus::Pending;
|
||||
public ChargeStatus $status = ChargeStatus::InPending;
|
||||
|
||||
public \DateTimeInterface $dateCreated;
|
||||
public \DateTimeInterface $dateUpdated;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class CheckoutApiResource
|
||||
* The status of this Checkout, as confirmed by the Gateway.
|
||||
*/
|
||||
#[API\ApiProperty(writable: false)]
|
||||
public CheckoutStatus $status = CheckoutStatus::Pending;
|
||||
public CheckoutStatus $status = CheckoutStatus::InPending;
|
||||
|
||||
/**
|
||||
* A list of related hyperlinks, as provided by the Gateway.
|
||||
@@ -110,6 +110,9 @@ class CheckoutApiResource
|
||||
#[MapFrom(Checkout::class, transformer: 'parseTrackings')]
|
||||
public array $trackings = [];
|
||||
|
||||
public \DateTimeInterface $dateCreated;
|
||||
public \DateTimeInterface $dateUpdated;
|
||||
|
||||
public static function parseLinks(array $values)
|
||||
{
|
||||
return \array_map(fn($value) => Link::tryFrom($value), $values);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\State\Options;
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\ApiResource\Accounting\AccountingApiResource;
|
||||
use App\Entity\Tipjar;
|
||||
use App\State\ApiResourceStateProcessor;
|
||||
use App\State\ApiResourceStateProvider;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* Unlike other Money recipients a Tipjar receives money with no further goal.\
|
||||
* \
|
||||
* Tips to the platform owners and other no-purpose money can target a Tipjar.
|
||||
*/
|
||||
#[API\ApiResource(
|
||||
shortName: 'Tipjar',
|
||||
stateOptions: new Options(entityClass: Tipjar::class),
|
||||
provider: ApiResourceStateProvider::class,
|
||||
processor: ApiResourceStateProcessor::class
|
||||
)]
|
||||
#[API\GetCollection()]
|
||||
#[API\Post()]
|
||||
#[API\Get()]
|
||||
#[API\Delete()]
|
||||
#[API\Patch()]
|
||||
class TipjarApiResource
|
||||
{
|
||||
#[API\ApiProperty(identifier: true, writable: false)]
|
||||
public int $id;
|
||||
|
||||
#[API\ApiProperty(writable: false)]
|
||||
public AccountingApiResource $accounting;
|
||||
|
||||
/**
|
||||
* Human readable, non white space, unique string.
|
||||
*/
|
||||
#[Assert\NotBlank()]
|
||||
public string $name;
|
||||
}
|
||||
+10
-11
@@ -5,7 +5,6 @@ namespace App\ApiResource;
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\Filter\ResourceVersionResourceFilter;
|
||||
use App\Filter\ResourceVersionResourceIdFilter;
|
||||
use App\Service\ApiService;
|
||||
use App\State\ResourceVersionStateProvider;
|
||||
use Gedmo\Loggable\Entity\LogEntry;
|
||||
|
||||
@@ -23,7 +22,7 @@ class Version
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LogEntry $log,
|
||||
private readonly object $entity,
|
||||
private readonly string $shortName,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -34,14 +33,6 @@ class Version
|
||||
return $this->log->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the version for this specific resource.
|
||||
*/
|
||||
public function getVersion(): ?int
|
||||
{
|
||||
return $this->log->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of action that performed the recorded changes.
|
||||
*/
|
||||
@@ -55,7 +46,7 @@ class Version
|
||||
*/
|
||||
public function getResource(): string
|
||||
{
|
||||
return ApiService::toResource($this->log->getObjectClass());
|
||||
return $this->shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +57,14 @@ class Version
|
||||
return $this->log->getObjectId();
|
||||
}
|
||||
|
||||
/**
|
||||
* The version number for this specific resource.
|
||||
*/
|
||||
public function getResourceVersion(): ?int
|
||||
{
|
||||
return $this->log->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* The changed resource data, i.e the new values of the changed properties.
|
||||
*/
|
||||
|
||||
@@ -182,7 +182,7 @@ class CheckoutsPump implements PumpInterface
|
||||
private function getCheckoutStatus(array $record): CheckoutStatus
|
||||
{
|
||||
if ($record['status'] < 1) {
|
||||
return CheckoutStatus::Pending;
|
||||
return CheckoutStatus::InPending;
|
||||
}
|
||||
|
||||
return CheckoutStatus::Charged;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\DependencyInjection\Compiler;
|
||||
|
||||
use App\Service\ApiService;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use App\Service\VersionedResourceService;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Gedmo\Mapping\Annotation\Loggable;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
@@ -13,27 +13,48 @@ class VersionedResourcePass implements CompilerPassInterface
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$allClasses = \get_declared_classes();
|
||||
$entityClasses = \array_filter($allClasses, function (string $className) {
|
||||
return \str_starts_with($className, 'App\\Entity\\');
|
||||
$resourceClasses = \array_filter($allClasses, function (string $className) {
|
||||
return \str_starts_with($className, 'App\\ApiResource\\');
|
||||
});
|
||||
|
||||
$versionedResourceNames = [];
|
||||
foreach ($entityClasses as $entityClass) {
|
||||
$reflectionClass = new \ReflectionClass($entityClass);
|
||||
$attributes = $reflectionClass->getAttributes(Gedmo\Loggable::class);
|
||||
$versionedResources = [];
|
||||
foreach ($resourceClasses as $resourceClass) {
|
||||
$resourceReflection = new \ReflectionClass($resourceClass);
|
||||
$resourceAttributes = $resourceReflection->getAttributes(ApiResource::class);
|
||||
|
||||
if (empty($attributes)) {
|
||||
if (empty($resourceAttributes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$versionedResourceNames = [
|
||||
...$versionedResourceNames,
|
||||
ApiService::toResource($entityClass),
|
||||
$resourceAttribute = $resourceAttributes[0];
|
||||
$resourceAttributeArgs = $resourceAttribute->getArguments();
|
||||
|
||||
if (!\array_key_exists('stateOptions', $resourceAttributeArgs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\array_key_exists('shortName', $resourceAttributeArgs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$resourceEntityClass = $resourceAttributeArgs['stateOptions']->getEntityClass();
|
||||
|
||||
$entityReflection = new \ReflectionClass($resourceEntityClass);
|
||||
$entityLoggable = $entityReflection->getAttributes(Loggable::class);
|
||||
|
||||
if (empty($entityLoggable)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$versionedResources = [
|
||||
...$versionedResources,
|
||||
$resourceAttributeArgs['shortName'] => $resourceEntityClass,
|
||||
];
|
||||
}
|
||||
|
||||
/** @var VersionedResourceService */
|
||||
$versionedResourceService = $container->get(VersionedResourceService::class);
|
||||
$versionedResourceService->compileNames($versionedResourceNames);
|
||||
$versionedResourceService->compileNames($versionedResources);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Doctrine;
|
||||
|
||||
use App\Entity\Gateway\Charge;
|
||||
use App\Security\Trait\UserTrait;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
final class GatewayChargeExtension extends AbstractQueryResourceExtensionInterface
|
||||
{
|
||||
use UserTrait;
|
||||
|
||||
protected function supports(string $resourceClass): bool
|
||||
{
|
||||
return $resourceClass === Charge::class;
|
||||
}
|
||||
|
||||
protected function applyFilters(QueryBuilder $queryBuilder, string $rootAlias): void
|
||||
{
|
||||
$user = $this->getAuthenticatedUser();
|
||||
|
||||
if ($this->isAdmin($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$queryBuilder
|
||||
->leftJoin("$rootAlias.checkout", 'c')
|
||||
->leftJoin('c.origin', 'co')
|
||||
->leftJoin('co.user', 'u')
|
||||
->andWhere('u.id = :userId')
|
||||
->setParameter('userId', $user->getId());
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use App\Entity\Accounting\Accounting;
|
||||
use App\Entity\Accounting\Transaction;
|
||||
use App\Entity\Money;
|
||||
use App\Entity\Project\Support;
|
||||
use App\Entity\Trait\TimestampedCreationEntity;
|
||||
use App\Entity\Trait\TimestampedUpdationEntity;
|
||||
use App\Gateway\ChargeStatus;
|
||||
use App\Gateway\ChargeType;
|
||||
use App\Mapping\Provider\EntityMapProvider;
|
||||
@@ -27,6 +29,9 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
#[ORM\Entity(repositoryClass: ChargeRepository::class)]
|
||||
class Charge
|
||||
{
|
||||
use TimestampedCreationEntity;
|
||||
use TimestampedUpdationEntity;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
@@ -90,7 +95,7 @@ class Charge
|
||||
public function __construct()
|
||||
{
|
||||
$this->transactions = new ArrayCollection();
|
||||
$this->status = ChargeStatus::Pending;
|
||||
$this->status = ChargeStatus::InPending;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
|
||||
@@ -109,7 +109,7 @@ class Checkout
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->status = CheckoutStatus::Pending;
|
||||
$this->status = CheckoutStatus::InPending;
|
||||
$this->charges = new ArrayCollection();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ enum ProjectStatus: string
|
||||
/**
|
||||
* Owner finished editing and Project is ready for review.
|
||||
*/
|
||||
case InPending = 'in_pending';
|
||||
case ToReview = 'to_review';
|
||||
|
||||
/**
|
||||
* An admin is reviewing the Project.
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\Entity\Accounting\Accounting;
|
||||
use App\Entity\Interface\AccountingOwnerInterface;
|
||||
use App\Mapping\Provider\EntityMapProvider;
|
||||
use App\Repository\TipjarRepository;
|
||||
use AutoMapper\Attribute\MapProvider;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
|
||||
@@ -14,12 +15,8 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
* \
|
||||
* Tips to the platform owners and other no-purpose money can target a Tipjar.
|
||||
*/
|
||||
#[MapProvider(EntityMapProvider::class)]
|
||||
#[UniqueEntity(fields: ['name'], message: 'A Tipjar with that name already exists.')]
|
||||
#[API\GetCollection()]
|
||||
#[API\Post()]
|
||||
#[API\Get()]
|
||||
#[API\Delete()]
|
||||
#[API\Patch()]
|
||||
#[ORM\Entity(repositoryClass: TipjarRepository::class)]
|
||||
class Tipjar implements AccountingOwnerInterface
|
||||
{
|
||||
@@ -34,7 +31,6 @@ class Tipjar implements AccountingOwnerInterface
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $name = null;
|
||||
|
||||
#[API\ApiProperty(writable: false)]
|
||||
#[ORM\OneToOne(inversedBy: 'tipjar', cascade: ['persist'])]
|
||||
private ?Accounting $accounting = null;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use App\Entity\Gateway\Charge;
|
||||
use App\Gateway\ChargeStatus;
|
||||
use App\Gateway\GatewayLocator;
|
||||
use App\Gateway\RefundStrategy;
|
||||
use App\Service\Gateway\ChargeService;
|
||||
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
|
||||
use Doctrine\ORM\Event\PreUpdateEventArgs;
|
||||
use Doctrine\ORM\Events;
|
||||
@@ -22,7 +21,6 @@ class ChargeRefundListener
|
||||
|
||||
public function __construct(
|
||||
private GatewayLocator $gatewayLocator,
|
||||
private ChargeService $transactionService,
|
||||
) {}
|
||||
|
||||
private function processGatewayRefund(Charge $charge): void
|
||||
@@ -38,8 +36,8 @@ class ChargeRefundListener
|
||||
}
|
||||
|
||||
if (
|
||||
$args->getOldValue(self::FIELD_STATUS) === ChargeStatus::Charged
|
||||
&& $args->getNewValue(self::FIELD_STATUS) === ChargeStatus::ToRefund
|
||||
$args->getOldValue(self::FIELD_STATUS) === ChargeStatus::Charged->value
|
||||
&& $args->getNewValue(self::FIELD_STATUS) === ChargeStatus::ToRefund->value
|
||||
) {
|
||||
$refundStrategy = $charge->getCheckout()->getRefundStrategy();
|
||||
match ($refundStrategy) {
|
||||
@@ -49,8 +47,6 @@ class ChargeRefundListener
|
||||
$refundStrategy->name
|
||||
)),
|
||||
};
|
||||
|
||||
$this->transactionService->addRefundTransaction($charge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ final class ResourceVersionResourceFilter extends AbstractFilter
|
||||
'description' => 'The name of the resource.',
|
||||
'schema' => [
|
||||
'type' => Type::BUILTIN_TYPE_STRING,
|
||||
'enum' => $this->versionedResourceService->getNames(),
|
||||
'enum' => \array_keys($this->versionedResourceService->getNames()),
|
||||
],
|
||||
'openapi' => [
|
||||
'allowEmptyValue' => false,
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Gateway;
|
||||
use App\Entity\Gateway\Charge;
|
||||
use App\Entity\Gateway\Checkout;
|
||||
use App\Repository\Gateway\CheckoutRepository;
|
||||
use App\Service\Gateway\ChargeService;
|
||||
use App\Service\Gateway\CheckoutService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -19,6 +20,7 @@ abstract class AbstractGateway implements GatewayInterface
|
||||
|
||||
protected CheckoutService $checkoutService;
|
||||
protected CheckoutRepository $checkoutRepository;
|
||||
protected ChargeService $chargeService;
|
||||
protected EntityManagerInterface $entityManager;
|
||||
|
||||
#[Required]
|
||||
@@ -33,6 +35,12 @@ abstract class AbstractGateway implements GatewayInterface
|
||||
$this->checkoutRepository = $checkoutRepository;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setChargeService(ChargeService $chargeService)
|
||||
{
|
||||
$this->chargeService = $chargeService;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setEntityManager(EntityManagerInterface $entityManager)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Gateway;
|
||||
|
||||
enum ChargeStatus: string
|
||||
{
|
||||
case Pending = 'pending';
|
||||
case InPending = 'in_pending';
|
||||
|
||||
case Charged = 'charged';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Gateway;
|
||||
|
||||
enum CheckoutStatus: string
|
||||
{
|
||||
case Pending = 'pending';
|
||||
case InPending = 'in_pending';
|
||||
|
||||
case Charged = 'charged';
|
||||
}
|
||||
|
||||
@@ -259,4 +259,29 @@ class PaypalGateway extends AbstractGateway
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function processRefund(Charge $charge): void
|
||||
{
|
||||
$trackings = $charge->getCheckout()->getTrackings();
|
||||
$captureTracking = current(array_filter(
|
||||
$trackings,
|
||||
fn(Tracking $tracking) => $tracking->title === self::TRACKING_TITLE_TRANSACTION && $tracking->value
|
||||
));
|
||||
|
||||
if (!$captureTracking) {
|
||||
throw new \Exception('PayPal capture ID not found in tracking.');
|
||||
}
|
||||
|
||||
$captureId = $captureTracking->value;
|
||||
|
||||
$response = $this->paypal->refundCapture($captureId, [
|
||||
'amount' => $this->getPaypalMoney($charge),
|
||||
]);
|
||||
|
||||
if (!in_array($response['status'], ['COMPLETED', 'PENDING'], true)) {
|
||||
throw new \Exception(sprintf('Refund failed. PayPal status: %s', $response['status']));
|
||||
}
|
||||
|
||||
$this->chargeService->addRefundTransaction($charge);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,4 +191,26 @@ class PaypalService
|
||||
|
||||
return json_decode($rawBody, true);
|
||||
}
|
||||
|
||||
public function refundCapture(string $captureId, array $payload): array
|
||||
{
|
||||
$response = $this->httpClient->request(
|
||||
'POST',
|
||||
"/v2/payments/captures/{$captureId}/refund",
|
||||
[
|
||||
'auth_bearer' => $this->getAuthToken()['access_token'],
|
||||
'json' => $payload,
|
||||
]
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() !== Response::HTTP_CREATED) {
|
||||
throw new \Exception(sprintf(
|
||||
"Refund for capture '%s' failed with status %d.",
|
||||
$captureId,
|
||||
$response->getStatusCode()
|
||||
));
|
||||
}
|
||||
|
||||
return $response->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,5 +234,7 @@ class StripeGateway extends AbstractGateway
|
||||
'payment_intent' => $paymentIntent->id,
|
||||
'amount' => $charge->getMoney()->amount,
|
||||
]);
|
||||
|
||||
$this->chargeService->addRefundTransaction($charge);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
class ApiService
|
||||
{
|
||||
/**
|
||||
* Converts an Entity class name into an API resource name.
|
||||
*/
|
||||
public static function toResource(string $className): string
|
||||
{
|
||||
$classPieces = explode('\\', $className);
|
||||
|
||||
return lcfirst(end($classPieces));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to an Entity class name from an API resource name.
|
||||
*/
|
||||
public static function toEntity(string $resourceName): string
|
||||
{
|
||||
return sprintf('App\\Entity\\%s', ucfirst($resourceName));
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class VersionedResourceService
|
||||
/**
|
||||
* Stores the given names in lock file.
|
||||
*
|
||||
* @param string[] $names
|
||||
* @param array<string, string> $names
|
||||
*/
|
||||
public function compileNames(array $names)
|
||||
{
|
||||
@@ -51,17 +51,45 @@ class VersionedResourceService
|
||||
|
||||
\file_put_contents(
|
||||
self::getNamesLockFile(),
|
||||
implode(PHP_EOL, $names)
|
||||
json_encode($names)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string> List of the versioned resource names
|
||||
* @return array<string, string> List of the versioned resource names
|
||||
*
|
||||
* @see compileNames()
|
||||
*/
|
||||
public static function getNames(): array
|
||||
{
|
||||
return explode(PHP_EOL, \file_get_contents(self::getNamesLockFile()));
|
||||
return json_decode(\file_get_contents(self::getNamesLockFile()), true);
|
||||
}
|
||||
|
||||
public static function getEntityFromResource(string $resourceShortName): string
|
||||
{
|
||||
$names = self::getNames();
|
||||
|
||||
if (\array_key_exists($resourceShortName, $names)) {
|
||||
return $names[$resourceShortName];
|
||||
}
|
||||
|
||||
throw new \Exception(\sprintf(
|
||||
"The resource shortName '%s' does not match to a known versioned resource",
|
||||
$resourceShortName
|
||||
));
|
||||
}
|
||||
|
||||
public static function getResourceFromEntity(string $entityClass): string
|
||||
{
|
||||
foreach (self::getNames() as $shortName => $entityFqcl) {
|
||||
if ($entityFqcl === $entityClass) {
|
||||
return $shortName;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception(\sprintf(
|
||||
"The entity class name '%s' does not match to a known versioned resource",
|
||||
$entityClass
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use ApiPlatform\Metadata as API;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\Version;
|
||||
use App\Service\ApiResourceNormalizer;
|
||||
use App\Service\VersionedResourceService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use Gedmo\Loggable\Entity\LogEntry;
|
||||
@@ -33,6 +33,8 @@ class ResourceVersionStateProvider implements ProviderInterface
|
||||
$context['request']->query->get('resource'),
|
||||
$context['request']->query->get('resourceId')
|
||||
);
|
||||
default:
|
||||
throw new \Exception('Operation not supported for Version resource', 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +50,9 @@ class ResourceVersionStateProvider implements ProviderInterface
|
||||
}
|
||||
|
||||
$entity = $this->entityManager->find($log->getObjectClass(), $log->getObjectId());
|
||||
$resourceName = VersionedResourceService::getResourceFromEntity($entity::class);
|
||||
|
||||
return new Version($log, $entity);
|
||||
return new Version($log, $resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,10 +60,10 @@ class ResourceVersionStateProvider implements ProviderInterface
|
||||
*/
|
||||
private function getVersions(string $resourceName, int $resourceId): array
|
||||
{
|
||||
$resourceClass = ApiResourceNormalizer::toEntity($resourceName);
|
||||
$entityClass = VersionedResourceService::getEntityFromResource($resourceName);
|
||||
|
||||
try {
|
||||
$entity = $this->entityManager->find($resourceClass, $resourceId);
|
||||
$entity = $this->entityManager->find($entityClass, $resourceId);
|
||||
} catch (MappingException $e) {
|
||||
throw new NotFoundHttpException(sprintf("Resource '%s' does not exist", $resourceName));
|
||||
}
|
||||
@@ -73,7 +76,7 @@ class ResourceVersionStateProvider implements ProviderInterface
|
||||
|
||||
$versions = [];
|
||||
foreach ($logs as $log) {
|
||||
$versions[] = new Version($log, $entity);
|
||||
$versions[] = new Version($log, $resourceName);
|
||||
}
|
||||
|
||||
return $versions;
|
||||
|
||||
Reference in New Issue
Block a user