Skip to content

Commit

Permalink
Merge pull request #4 from yangyao/webhook
Browse files Browse the repository at this point in the history
Init webhook for feed plugin
  • Loading branch information
yangyao authored Jul 13, 2023
2 parents b40a862 + 3a23ddc commit e578e0e
Show file tree
Hide file tree
Showing 19 changed files with 974 additions and 3 deletions.
58 changes: 58 additions & 0 deletions Api/WebhookEntityInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
namespace AfterShip\Feed\Api;
interface WebhookEntityInterface
{
const DATA_ID = 'id';
const DATA_TOPIC = 'topic';
const DATA_APP_KEY = 'app_key';
const DATA_ADDRESS = 'address';
const DATA_INTEGRATION_ID = 'integration_id';

/**
* @return string
*/
public function getId();
/**
* @return string
*/
public function getTopic();
/**
* @return string
*/
public function getAppKey();
/**
* @return string
*/
public function getAddress();
/**
* @return string
*/
public function getIntegrationId();


/**
* @param string $id
* @return string
*/
public function setId($id);
/**
* @param string $topic
* @return $this
*/
public function setTopic($topic);
/**
* @param string $app_key
* @return $this
*/
public function setAppKey($app_key);
/**
* @param string $address
* @return $this
*/
public function setAddress($address);
/**
* @param string $integrationId
* @return $this
*/
public function setIntegrationId($integrationId);
}
36 changes: 36 additions & 0 deletions Api/WebhookManagementInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace AfterShip\Feed\Api;
use AfterShip\Feed\Api\WebhookEntityInterface;

interface WebhookManagementInterface
{
/**
* Register webhook.
*
* @param WebhookEntityInterface $webhook
* @return WebhookEntityInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function registerWebhook(WebhookEntityInterface $webhook);
/**
* List webhook.
*
* @return WebhookEntityInterface[]
*/
public function listWebhooks();
/**
* Delete webhook by id.
*
* @param string $webhookId
* @return WebhookEntityInterface|null
*/
public function deleteWebhook($webhookId);
/**
* Get webhook by id.
*
* @param string $webhookId
* @return WebhookEntityInterface|null
*/
public function getWebhook($webhookId);
}
11 changes: 11 additions & 0 deletions Constants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
namespace AfterShip\Feed;

final class Constants {
CONST AFTERSHIP_FEED_VERSION = '1.0.5';
CONST WEBHOOK_TOPIC_ORDERS_UPDATE = 'orders/update';
CONST WEBHOOK_TOPIC_PRODUCTS_UPDATE = 'products/update';
CONST WEBHOOK_TOPIC_VARIANTS_UPDATE = 'variants/update';
CONST WEBHOOK_TOPIC_PRODUCTS_DELETE = 'products/delete';
CONST WEBHOOK_TOPIC_VARIANTS_DELETE = 'variants/delete';
}
96 changes: 96 additions & 0 deletions Helper/WebhookHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace AfterShip\Feed\Helper;

use Exception;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Integration\Api\IntegrationServiceInterface;
use Magento\Integration\Api\OauthServiceInterface;
use Psr\Log\LoggerInterface;

class WebhookHelper
{

private $webhookConfigKey = 'webhooks';
/** @var array */
private $webhooks = [];
/** @var ScopeConfigInterface */
protected $scopeConfig;
/** @var OauthServiceInterface */
private $oauthService;
/** @var IntegrationServiceInterface */
private $integrationService;
/** @var LoggerInterface */
private $logger;

public function __construct(
ScopeConfigInterface $scopeConfig,
IntegrationServiceInterface $integrationService,
OauthServiceInterface $oauthService,
LoggerInterface $logger

) {
$this->scopeConfig = $scopeConfig;
$this->oauthService = $oauthService;
$this->logger = $logger;
$this->integrationService = $integrationService;
$webhooksJson = $this->scopeConfig->getValue(
'aftership/feed/' . $this->webhookConfigKey,
'default'
);
$this->webhooks = $webhooksJson ? json_decode($webhooksJson) : [];
}

/**
* @param string $topic
* @param array $data
* @return void
*/
public function makeWebhookRequest($topic, $data)
{
foreach ($this->webhooks as $webhook) {
if ($webhook->topic !== $topic) continue;
$this->sendWebhook($webhook, $data);
}
}

/**
* @param $webhook
* @param $data
* @return bool|string
*/
public function sendWebhook($webhook, $data) {
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $webhook->address,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'X-Magento-Hmac-Sha256: ' . $this->createWebhookSecurity($webhook->integration_id, $data),
'Content-Length: ' . strlen(json_encode($data)),
'X-Webhook-Topic: ' . $webhook->topic,
'X-App-Key: ' . $webhook->app_key
),
]);
$response = curl_exec($curl);
$err = curl_errno($curl);
if ($err) {
$this->logger->error(sprintf('[AfterShip Feed] Unable to send webhook to %s with data: %s, response: %s', $webhook->address, json_encode($data), $response));
}
curl_close($curl);
return $response;
}
/**
* @param string $integrationId
* @param array $data
* @return string
*/
private function createWebhookSecurity($integrationId, $data)
{
$integration = $this->integrationService->get($integrationId);
$consumer = $this->oauthService->loadConsumer($integration->getConsumerId());
return hash_hmac('sha256', json_encode($data), $consumer->getKey());
}
}
149 changes: 149 additions & 0 deletions Model/Api/WebhookManagement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<?php

namespace AfterShip\Feed\Model\Api;

use AfterShip\Feed\Api\WebhookManagementInterface;
use AfterShip\Feed\Api\WebhookEntityInterface;
use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Exception\LocalizedException;

class WebhookManagement implements WebhookManagementInterface
{

private $integrationId;
/** @var ScopeConfigInterface */
private $scopeConfig;
/** @var WriterInterface */
private $configWriter;
/** @var TypeListInterface */
private $cacheTypeList;
private $webhookConfigKey = 'webhooks';

/** @var array */
private $webhooks = [];

public function __construct(
UserContextInterface $userContext,
ScopeConfigInterface $scopeConfig,
WriterInterface $configWriter,
TypeListInterface $cacheTypeList
)
{
$this->integrationId = $userContext->getUserId();
$this->cacheTypeList = $cacheTypeList;
$this->scopeConfig = $scopeConfig;
$this->configWriter = $configWriter;
$webhooksJson = $this->scopeConfig->getValue(
'aftership/feed/' . $this->webhookConfigKey,
'default'
);
$this->webhooks = $webhooksJson ? json_decode($webhooksJson) : [];
}

/**
* @param WebhookEntityInterface $request
* @return mixed|null
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function registerWebhook($request = null)
{
if (!$request || !$request->getTopic() || !$request->getAddress() || !$request->getAppKey()) {
throw new LocalizedException(
new Phrase('The necessary parameters for creating a webhook are missing.'),
null,
400
);
}
$webhookId = $request->getId();
$request->setIntegrationId($this->integrationId);
$webhook = [
"id" => $webhookId,
"topic" => $request->getTopic(),
"app_key" => $request->getAppKey(),
"address" => $request->getAddress(),
"integration_id" => $this->integrationId,
];
$results = array_filter($this->webhooks, function ($item) use ($webhookId) {
return $item->id === $webhookId;
});
$done = !count($results) && array_push($this->webhooks, $webhook) && $this->configWriter->save(
'aftership/feed/' . $this->webhookConfigKey,
json_encode($this->webhooks),
'default'
);
$this->cacheTypeList->cleanType('config');
return $request;
}

/**
* @return WebhookEntityInterface[]|array
*/
public function listWebhooks()
{
$webhooks = [];
foreach ($this->webhooks as $webhook) {
$entity = new WebhookRequest();
$entity->setId($webhook->id);
$entity->setTopic($webhook->topic);
$entity->setAddress($webhook->address);
$entity->setAppKey($webhook->app_key);
$entity->setIntegrationId($webhook->integration_id);
array_push($webhooks, $entity);
}
return $webhooks;
}

/**
* @param $webhookId
* @return WebhookRequest|null
*/
public function getWebhook($webhookId)
{
$entity = null;
foreach ($this->webhooks as $webhook) {
if ($webhook->id === $webhookId) {
$entity = new WebhookRequest();
$entity->setId($webhook->id);
$entity->setTopic($webhook->topic);
$entity->setAddress($webhook->address);
$entity->setAppKey($webhook->app_key);
$entity->setIntegrationId($webhook->integration_id);
}
}
return $entity;
}

/**
* @param $webhookId
* @return WebhookRequest|null
*/
public function deleteWebhook($webhookId)
{
$entity = null;
$filteredWebhooks = [];
foreach ($this->webhooks as $webhook) {
if ($webhook->id === $webhookId) {
$entity = new WebhookRequest();
$entity->setId($webhook->id);
$entity->setTopic($webhook->topic);
$entity->setAddress($webhook->address);
$entity->setAppKey($webhook->app_key);
$entity->setIntegrationId($webhook->integration_id);
}else {
array_push($filteredWebhooks, $webhook);
}
}
$this->webhooks = $filteredWebhooks;
$this->configWriter->save(
'aftership/feed/' . $this->webhookConfigKey,
json_encode($filteredWebhooks),
'default'
);
$this->cacheTypeList->cleanType('config');
return $entity;
}
}
Loading

0 comments on commit e578e0e

Please sign in to comment.