<?php
namespace App\V4\Controller\CustomAction;
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use App\V4\CustomAction\Builder\CustomActionBuilder;
use App\V4\Entity\CustomAction;
use App\V4\Logger\SentryLogger;
use App\V4\Model\Contact\Contact;
use App\V4\Model\CustomActionResponse\CustomActionResponse;
use App\V4\Model\CustomActionResponse\CustomActionResponseMessage;
use App\V4\Model\Prospect\Prospect;
use App\V4\Model\Quote\Quote;
use App\V4\Model\Task\Task;
use App\V4\Transformer\FQCNToShortNameTransformer;
use Psr\Log\LogLevel;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Throwable;
class CustomActionAction extends AbstractController
{
public const CUSTOM_ACTION_ACTIONS = 'actions';
public const CUSTOM_ACTION_TOKEN_PROSPECT_ID = '%prospectId%';
public const CUSTOM_ACTION_TOKEN_ID = '%id%';
public const CUSTOM_ACTION_TOKEN_NEW_ID = '%newId%';
public const CUSTOM_ACTION_TOKEN_CONTACT_ID = '%contactId%';
public const CUSTOM_ACTION_TOKEN_TASK_ID = '%taskId%';
public const CUSTOM_ACTION_TOKEN_QUOTE_ID = '%quoteId%';
public const CUSTOM_ACTION_TOKEN_CONTACT_IDS = '%contactIds%';
/**
* @var ItemDataProviderInterface
*/
private $itemDataProvider;
/**
* @var CustomActionBuilder
*/
private $customActionBuilder;
/**
* @var SentryLogger
*/
private $sentryLogger;
public function __construct(
ItemDataProviderInterface $itemDataProvider,
CustomActionBuilder $customActionBuilder,
SentryLogger $sentryLogger
) {
$this->itemDataProvider = $itemDataProvider;
$this->customActionBuilder = $customActionBuilder;
$this->sentryLogger = $sentryLogger;
}
/**
* @param Request $request
* @param CustomAction $customAction
*
* @return JsonResponse
*
* @throws ResourceClassNotSupportedException
* @throws ExceptionInterface
*/
public function __invoke(Request $request, CustomAction $customAction): JsonResponse
{
$class = (new FQCNToShortNameTransformer())->reverseTransform($customAction->getEntity());
$content = json_decode($request->getContent(), true);
$config = json_decode($customAction->getConfig(), true);
if (empty($content['id'])) {
return new JsonResponse(
['error' => 'error_custom_action_config'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$entity = $this->itemDataProvider->getItem($class, $content['id']);
if (null === $entity) {
return new JsonResponse(
['error' => 'error_custom_action_not_found'],
Response::HTTP_NOT_FOUND
);
}
$config[self::CUSTOM_ACTION_ACTIONS] = $config[self::CUSTOM_ACTION_ACTIONS] ?? [];
try {
foreach ($config[self::CUSTOM_ACTION_ACTIONS] as $action) {
$entity = $this->customActionBuilder->getBuilder($entity, $action)->apply($entity, $action);
}
} catch (Throwable $e) {
$this->sentryLogger->captureException(
SentryLogger::CHANNEL_CUSTOMER_ACTION,
$e,
[
'config' => $config,
'action' => $action,
]
);
return new JsonResponse(
['error' => $config['error_message'] ?? 'error_custom_action_config'],
Response::HTTP_INTERNAL_SERVER_ERROR
);
}
$response = new CustomActionResponse();
if (!empty($config['confirm_message'])) {
$response->addMessage(
(new CustomActionResponseMessage())
->setType('success')
->setMessage($config['confirm_message'])
);
}
if (isset($config[CustomActionResponse::CUSTOM_ACTION_NEED_REFRESH_LIST])
&& true === $config[CustomActionResponse::CUSTOM_ACTION_NEED_REFRESH_LIST]
) {
$response->addEffect([CustomActionResponse::CUSTOM_ACTION_NEED_REFRESH_LIST => true]);
}
if (!empty($config[CustomActionResponse::CUSTOM_ACTION_REDIRECT_TO])) {
$response->addEffect(
[
CustomActionResponse::CUSTOM_ACTION_REDIRECT_TO => $this->replaceTokens(
$config[CustomActionResponse::CUSTOM_ACTION_REDIRECT_TO],
$content['id'],
$entity
),
]
);
}
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers);
return new JsonResponse($serializer->normalize($response), Response::HTTP_OK);
}
/**
* @param string $url
* @param string $lineId
* @param mixed $entity
*
* @return string
*/
private function replaceTokens(string $url, string $lineId, $entity): string
{
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_PROSPECT_ID)) {
$prospectId = null;
if (method_exists($entity, 'getProspectId') && null !== $entity->getProspectId()) {
$prospectId = $entity->getProspectId();
}
if (method_exists($entity, 'getProspect') && $entity->getProspect() instanceof Prospect && null !== $entity->getProspect()->getId()) {
$prospectId = $entity->getProspect()->getId();
}
if (null === $prospectId) {
$this->sentryLogger->captureMessage(
SentryLogger::CHANNEL_CUSTOMER_ACTION,
self::CUSTOM_ACTION_TOKEN_PROSPECT_ID." token was present in URL, yet no prospect id could be determined. URL: $url",
[
'args' => [
'url' => $url,
'entity' => $entity,
'lineId' => $lineId,
],
],
LogLevel::ERROR
);
}
$url = str_replace(self::CUSTOM_ACTION_TOKEN_PROSPECT_ID, $prospectId, $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_ID)) {
$url = str_replace(self::CUSTOM_ACTION_TOKEN_ID, $lineId, $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_NEW_ID) && method_exists($entity, 'getId')) {
$url = str_replace(self::CUSTOM_ACTION_TOKEN_ID, $entity->getId(), $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_CONTACT_ID) && method_exists($entity, 'getContactId')) {
$url = str_replace(self::CUSTOM_ACTION_TOKEN_CONTACT_ID, $entity->getContactId(), $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_CONTACT_IDS) && method_exists($entity, 'getContacts')) {
$contactIds = [];
/** @var Contact $contact */
foreach ($entity->getContacts() as $contact) {
$contactIds[] = $contact->getId();
}
$url = str_replace(self::CUSTOM_ACTION_TOKEN_CONTACT_IDS, implode(',', $contactIds), $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_TASK_ID) && $entity instanceof Task) {
$url = str_replace(self::CUSTOM_ACTION_TOKEN_TASK_ID, $entity->getId(), $url);
}
if (str_contains($url, self::CUSTOM_ACTION_TOKEN_QUOTE_ID) && $entity instanceof Quote) {
$url = str_replace(self::CUSTOM_ACTION_TOKEN_QUOTE_ID, $entity->getId(), $url);
}
return $url;
}
}