-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from aligent/feature/add-webhooks-and-oro-5.0-c…
…ompatibility Added Oro webhooks to the bundle and Oro 5.0 campatibility
- Loading branch information
Showing
38 changed files
with
1,816 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace Aligent\AsyncEventsBundle\Async; | ||
|
||
use Aligent\AsyncEventsBundle\Provider\WebhookConfigProvider; | ||
|
||
/** | ||
* Class Topics | ||
* | ||
* @category Aligent | ||
* @package Aligent\WebhookBundle\Async | ||
* @author Adam Hall <[email protected]> | ||
* @copyright 2020 Aligent Consulting. | ||
* @link http://www.aligent.com.au/ | ||
*/ | ||
class Topics | ||
{ | ||
const WEBHOOK_ENTITY_CREATE = 'aligent.webhook.entity.create'; | ||
const WEBHOOK_ENTITY_UPDATE = 'aligent.webhook.entity.update'; | ||
const WEBHOOK_ENTITY_DELETE = 'aligent.webhook.entity.delete'; | ||
|
||
const EVENT_MAP = [ | ||
WebhookConfigProvider::UPDATE => self::WEBHOOK_ENTITY_UPDATE, | ||
WebhookConfigProvider::DELETE => self::WEBHOOK_ENTITY_DELETE, | ||
WebhookConfigProvider::CREATE => self::WEBHOOK_ENTITY_CREATE, | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
<?php | ||
|
||
namespace Aligent\AsyncEventsBundle\Async; | ||
|
||
use Aligent\AsyncEventsBundle\Exception\RetryableException; | ||
use Aligent\AsyncEventsBundle\Entity\WebhookTransport as WebhookTransportEntity; | ||
use Aligent\AsyncEventsBundle\Integration\WebhookTransport; | ||
use Aligent\AsyncEventsBundle\Provider\WebhookConfigProvider; | ||
use GuzzleHttp\Exception\GuzzleException; | ||
use Oro\Bundle\ImportExportBundle\Serializer\SerializerInterface; | ||
use Oro\Bundle\IntegrationBundle\Entity\Channel; | ||
use Oro\Component\MessageQueue\Client\Config; | ||
use Oro\Component\MessageQueue\Client\TopicSubscriberInterface; | ||
use Oro\Component\MessageQueue\Transport\MessageInterface; | ||
use Oro\Component\MessageQueue\Util\JSON; | ||
|
||
/** | ||
* Class WebhookEntityHandler | ||
* | ||
* @category Aligent | ||
* @package Aligent\WebhookBundle\Async | ||
* @author Adam Hall <[email protected]> | ||
* @copyright 2020 Aligent Consulting. | ||
* @link http://www.aligent.com.au/ | ||
*/ | ||
class WebhookEntityProcessor extends AbstractRetryableProcessor implements TopicSubscriberInterface | ||
{ | ||
const EVENT_MAP = [ | ||
Topics::WEBHOOK_ENTITY_UPDATE => WebhookConfigProvider::UPDATE, | ||
Topics::WEBHOOK_ENTITY_DELETE => WebhookConfigProvider::DELETE, | ||
Topics::WEBHOOK_ENTITY_CREATE => WebhookConfigProvider::CREATE, | ||
]; | ||
|
||
/** | ||
* @var WebhookConfigProvider | ||
*/ | ||
protected $configProvider; | ||
|
||
/** | ||
* @var WebhookTransport | ||
*/ | ||
protected $transport; | ||
|
||
/** | ||
* @var SerializerInterface | ||
*/ | ||
protected $serializer; | ||
|
||
/** | ||
* @param SerializerInterface $serializer | ||
* @return WebhookEntityProcessor | ||
*/ | ||
public function setSerializer(SerializerInterface $serializer): WebhookEntityProcessor | ||
{ | ||
$this->serializer = $serializer; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param WebhookConfigProvider $configProvider | ||
* @return WebhookEntityProcessor | ||
*/ | ||
public function setConfigProvider(WebhookConfigProvider $configProvider): WebhookEntityProcessor | ||
{ | ||
$this->configProvider = $configProvider; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @param WebhookTransport $transport | ||
* @return WebhookEntityProcessor | ||
*/ | ||
public function setTransport(WebhookTransport $transport): WebhookEntityProcessor | ||
{ | ||
$this->transport = $transport; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function execute(MessageInterface $message) | ||
{ | ||
$data = JSON::decode($message->getBody()); | ||
$topic = $message->getProperty(Config::PARAMETER_TOPIC_NAME); | ||
$channelRepo = $this->registry->getRepository(Channel::class); | ||
$channel = $channelRepo->find($data['channelId']); | ||
|
||
if (!$channel) { | ||
$this->logger->critical("Channel {$channel->getName()} no longer exists. Skipping webhook event."); | ||
return self::REJECT; | ||
} | ||
|
||
try { | ||
/** @var WebhookTransportEntity $transport */ | ||
$transport = $channel->getTransport(); | ||
$this->transport->init($transport); | ||
$eventType = self::EVENT_MAP[$topic]; | ||
|
||
$response = $this->transport->sendWebhookEvent( | ||
$transport->getMethod(), | ||
$this->buildPayload($eventType, $data) | ||
); | ||
$this->logger->info( | ||
'Webhook sent', | ||
[ | ||
'eventType' => $eventType, | ||
'message' => $data, | ||
'response' => $response | ||
] | ||
); | ||
} catch (\Exception $exception) { | ||
throw new RetryableException($exception->getMessage(), $exception->getCode(), $exception); | ||
} catch (GuzzleException $e) { | ||
$message = "Server responded with non-200 status code"; | ||
$this->logger->error( | ||
$message, | ||
[ | ||
'channelId' => $channel->getId(), | ||
'channel' => $channel->getName(), | ||
'topic' => $topic, | ||
'exception' => $e | ||
] | ||
); | ||
throw new RetryableException($message, 0, $e); | ||
} | ||
|
||
return self::ACK; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public static function getSubscribedTopics() | ||
{ | ||
return [ | ||
Topics::WEBHOOK_ENTITY_CREATE, | ||
Topics::WEBHOOK_ENTITY_DELETE, | ||
Topics::WEBHOOK_ENTITY_UPDATE, | ||
]; | ||
} | ||
|
||
/** | ||
* @param string $event | ||
* @param array $data | ||
* @return array | ||
* @throws \Symfony\Component\Serializer\Exception\ExceptionInterface | ||
*/ | ||
protected function buildPayload(string $event, array $data) | ||
{ | ||
$entity = $this->registry->getRepository($data['class'])->find($data['id']); | ||
|
||
if ($event === WebhookConfigProvider::CREATE) { | ||
$changeSet = []; | ||
} else { | ||
// extract all of the before values from the change set | ||
$changeSet = []; | ||
foreach ($data['changeSet'] as $field => $changes) { | ||
$changeSet[$field] = $changes[0]; | ||
} | ||
} | ||
|
||
$reflClass = new \ReflectionClass($data['class']); | ||
|
||
return [ | ||
'type' => $reflClass->getShortName(), | ||
'id' => count($data['id']) > 1 ? $data['id'] : reset($data['id']), | ||
'operation' => $event, | ||
'attributes' => $this->serializer->normalize($entity, null, ['webhook']), | ||
'before' => $changeSet | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.