Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds product/save to message queue #144

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
8 changes: 8 additions & 0 deletions Api/ReclaimInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ public function reclaim();
*/
public function getWebhookSecret();

/**
* Returns the registered webhooks
*
* @return mixed[]
* @api
*/
public function getWebhooks();

/**
* Returns the Klaviyo log file
*
Expand Down
96 changes: 96 additions & 0 deletions Cron/ProductsTopic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace Klaviyo\Reclaim\Cron;

use Klaviyo\Reclaim\Helper\Logger;
use Klaviyo\Reclaim\Model\SyncsFactory;
use Klaviyo\Reclaim\Model\ResourceModel\Products;
use Klaviyo\Reclaim\Model\ResourceModel\Products\CollectionFactory;

class ProductsTopic
{
/**
* Klaviyo Logger
* @var Logger
*/
protected $_klaviyoLogger;

/**
* Klaviyo Products Resource Model
* @var Products
*/
protected $_klProduct;

/**
* Klaviyo Products Collection
* @var CollectionFactory
*/
protected $_klProductCollectionFactory;

/**
* Klaviyo Syncs Model
* @var SyncsFactory
*/
protected $_klSyncFactory;

/**
* @param Logger $klaviyoLogger
* @param Products $klProduct
* @param SyncsFactory $klSyncFactory
* @param CollectionFactory $klProductCollectionFactory
*/
public function __construct(
Logger $klaviyoLogger,
Products $klProduct,
SyncsFactory $klSyncFactory,
CollectionFactory $klProductCollectionFactory
)
{
$this->_klaviyoLogger = $klaviyoLogger;
$this->_klProduct = $klProduct;
$this->_klSyncFactory = $klSyncFactory;
$this->_klProductCollectionFactory = $klProductCollectionFactory;
}

public function queueKlProductsForSync()
{
$klProductsCollection = $this->_klProductCollectionFactory->create();
$klProductsToSync = $klProductsCollection->getRowsForSync('NEW')
->addFieldToSelect(['id','payload','status','topic', 'klaviyo_id'])
->getData();

if (empty($klProductsToSync))
{
return;
}

$idsToUpdate = [];

foreach ($klProductsToSync as $klProductToSync)
{
$klSync = $this->_klSyncFactory->create();
$klSync->setData([
'payload'=> $klProductToSync['payload'],
'topic'=> $klProductToSync['topic'],
'klaviyo_id'=>$klProductToSync['klaviyo_id'],
'status'=> 'NEW'
]);
try {
$klSync->save();
array_push($idsToUpdate, $klProductToSync['id']);
} catch (\Exception $e) {
$this->_klaviyoLogger->log(sprintf('Unable to move row: %s', $e));
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved
}
}

$klProductsCollection->updateRowStatus($idsToUpdate, 'MOVED');
}

public function clean()
{
$klProductsCollection = $this->_klProductCollectionFactory->create();
$idsToDelete = $klProductsCollection->getIdsToDelete('MOVED');

$klProductsCollection->deleteRows($idsToDelete);
}
}
24 changes: 21 additions & 3 deletions Helper/ScopeSetting.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class ScopeSetting extends \Magento\Framework\App\Helper\AbstractHelper

const WEBHOOK_SECRET = 'klaviyo_reclaim_webhook/klaviyo_webhooks/webhook_secret';
const PRODUCT_DELETE_BEFORE = 'klaviyo_reclaim_webhook/klaviyo_webhooks/using_product_delete_before_webhook';
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

const PRODUCT_SAVE_AFTER = 'klaviyo_reclaim_webhook/klaviyo_webhooks/using_product_save_after_webhook';
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

const KLAVIYO_OAUTH_NAME = 'klaviyo_reclaim_oauth/klaviyo_oauth/integration_name';

protected $_scopeConfig;
Expand Down Expand Up @@ -143,6 +144,19 @@ public function getWebhookSecret($storeId = null)
return $this->getScopeSetting(self::WEBHOOK_SECRET, $storeId);
}

public function getWebhooks()
{
$registeredWebhooks = [];

$product_delete = $this->getProductDeleteBeforeSetting();
$product_save = $this->getProductSaveAfterSetting();

array_push($registeredWebhooks, ['product/delete', $product_delete)];
array_push($registeredWebhooks, ['product/save', $product_save]);

return $registeredWebhooks;
}

public function isEnabled($storeId = null)
{
return $this->getScopeSetting(self::ENABLE, $storeId);
Expand Down Expand Up @@ -221,7 +235,7 @@ public function getConsentAtCheckoutSMSListId($storeId = null)
{
return $this->getScopeSetting(self::CONSENT_AT_CHECKOUT_SMS_LIST_ID, $storeId);
}

public function getConsentAtCheckoutSMSConsentText($storeId = null)
{
return $this->getScopeSetting(self::CONSENT_AT_CHECKOUT_SMS_CONSENT_TEXT, $storeId);
Expand Down Expand Up @@ -264,5 +278,9 @@ public function getProductDeleteBeforeSetting($storeId = null)
return $this->getScopeSetting(self::PRODUCT_DELETE_BEFORE, $storeId);
}

}
public function getProductSaveAfterSetting($storeId = null)
{
return $this->getScopeSetting(self::PRODUCT_SAVE_AFTER, $storeId);
}
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

}
18 changes: 8 additions & 10 deletions Helper/Webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ public function __construct(

/**
* @param string $webhookType
* @param array $data
* @param string $data
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved
* @param string $klaviyoId
* @return string
* @throws Exception
*/
public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
{

if (!$klaviyoId) {
$klaviyoId = $this->_klaviyoScopeSetting->getPublicApiKey();
}
Expand All @@ -51,12 +50,12 @@ public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_POSTFIELDS => $data,
CURLOPT_USERAGENT => self::USER_AGENT,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Magento-two-signature: ' . $this->createWebhookSecurity($data),
'Content-Length: '. strlen(json_encode($data)),
'Content-Length: '. strlen($data),
'Topic: ' . $webhookType
),
]);
Expand All @@ -66,24 +65,23 @@ public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
$err = curl_errno($curl);

if ($err) {
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, json_encode($data)));
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, $data));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe we could use double-quotes for string interpolation

Suggested change
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, $data));
$this->_klaviyoLogger->log("Unable to send webhook to $url with data: $data");

}

// Close cURL session handle
curl_close($curl);

return $response;
}

/**
* @param array data
* @param string data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a description to this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work?

* @param string $data json payload used to create hmac signature

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param string data
* Returns an HMAC signature for webhooks
* @param string data

* @return string
* @throws Exception
*/
private function createWebhookSecurity(array $data)
private function createWebhookSecurity(string $data)
{
$webhookSecret = $this->_klaviyoScopeSetting->getWebhookSecret();
return hash_hmac('sha256', json_encode($data), $webhookSecret);

return hash_hmac('sha256', $data, $webhookSecret);
}
}

5 changes: 5 additions & 0 deletions Model/Reclaim.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function getWebhookSecret()
return $this->_klaviyoScopeSetting->getWebhookSecret();
}

public function getWebhooks()
{
return $this->_klaviyoScopeSetting->getWebhooks();
}

/**
* Returns the Klaviyo log file
*
Expand Down
150 changes: 150 additions & 0 deletions Observer/ProductSaveAfter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

namespace Klaviyo\Reclaim\Observer;

use Exception;
use Klaviyo\Reclaim\Helper\ScopeSetting;
use Klaviyo\Reclaim\Helper\Webhook;
use Klaviyo\Reclaim\Helper\Logger;
use Klaviyo\Reclaim\Model\ProductsFactory;

use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\Product;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;


class ProductSaveAfter implements ObserverInterface
{
/**
* Klaviyo scope setting helper
* @var ScopeSetting $klaviyoScopeSetting
*/
protected $_klaviyoScopeSetting;

/**
* Klaviyo logger helper
* @var \Klaviyo\Reclaim\Helper\Logger $klaviyoLogger
*/
protected $_klaviyoLogger;

/**
* @var Webhook $webhookHelper
*/
protected $_webhookHelper;
protected $_categoryFactory;
protected $_klProductFactory;
protected $_stockRegistry;
protected $product_category_names = [];
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

/**
* @param Webhook $webhookHelper
* @param ScopeSetting $klaviyoScopeSetting
* @param CategoryFactory $categoryFactory
* @param ProductsFactory $klProductFactory
* @param StockRegistryInterface $stockRegistry
* @param Logger $klaviyoLogger
*/
public function __construct(
Webhook $webhookHelper,
ScopeSetting $klaviyoScopeSetting,
CategoryFactory $categoryFactory,
ProductsFactory $klProductFactory,
StockRegistryInterface $stockRegistry,
Logger $klaviyoLogger
) {
$this->_webhookHelper = $webhookHelper;
$this->_klaviyoScopeSetting = $klaviyoScopeSetting;
$this->_categoryFactory = $categoryFactory;
$this->_klProductFactory = $klProductFactory;
$this->_klaviyoLogger = $klaviyoLogger;
$this->_stockRegistry = $stockRegistry;
}

/**
* customer register event handler
*
* @param Observer $observer
* @return void
* @throws Exception
*/
public function execute(Observer $observer)
{
$product = $observer->getEvent()->getProduct();
$storeIds = $product->getStoreIds();
$storeIdKlaviyoMap = $this->_klaviyoScopeSetting->getStoreIdKlaviyoAccountSetMap($storeIds);

foreach ($storeIdKlaviyoMap as $klaviyoId => $storeIds) {
if (empty($storeIds)) {
continue;
}

if ($this->_klaviyoScopeSetting->getWebhookSecret() && $this->_klaviyoScopeSetting->getProductSaveAfterSetting($storeIds[0])) {
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

$normalizedProduct = $this->normalizeProduct($product);
$data = [
"status"=>"NEW",
"topic"=>"product/save",
"klaviyo_id"=>$klaviyoId,
"payload"=>json_encode($normalizedProduct)
];
$klProduct = $this->_klProductFactory->create();
$klProduct->setData($data);
$klProduct->save();
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved

// $this->_klaviyoLogger->log( print_r("Created new row?", true));
jordanallain marked this conversation as resolved.
Show resolved Hide resolved

// $this->_webhookHelper->makeWebhookRequest('product/save', $normalizedProduct, $klaviyoId);
jordanallain marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

private function normalizeProduct($product=null)
{
if ($product == null) {
return;
}

$product_id = $product->getId();

// remove thumnbail image url?
jordanallain marked this conversation as resolved.
Show resolved Hide resolved

$product_info = array(
'product' => array(
'store_ids' => $product->getStoreIds(),
'ID' => $product_id,
'TypeID' => $product->getTypeId(),
'Name' => $product->getName(),
'qty' => $this->_stockRegistry->getStockItem($product_id)->getQty(),
'Visibility' => $product->getVisibility(),
'IsInStock' => $product->isInStock(),
'Status' => $product->getStatus(),
'CreatedAt' => $product->getCreatedAt(),
'UpdatedAt' => $product->getUpdatedAt(),
'FirstImageURL' => $product->getImage(),
'ThumbnailImageURL' => $product->getThumbnail(),
'metadata' => array(
'price' => $product->getPrice(),
'sku' => $product->getSku()
),
'categories' => []
)
);

if ($product->getSpecialPrice()) {
$product_info['metadata']['special_price'] = $product->getSpecialPrice();
$product_info['metadata']['special_from_date'] = $product->getSpecialFromDate();
$product_info['metadata']['special_to_date'] = $product->getSpecialToDate();
}

$product_category_ids = $product->getCategoryIds();
$category_factory = $this->_categoryFactory->create();
foreach ($product_category_ids as $category_id) {
$category = $category_factory->load($category_id);
$product_info['categories'][$category_id] = $category->getName();
}

return $product_info;
}
}
3 changes: 3 additions & 0 deletions etc/adminhtml/events.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<event name="catalog_product_delete_before">
<observer name="klaviyo_reclaim" instance="Klaviyo\Reclaim\Observer\ProductDeleteBefore" />
</event>
<event name="catalog_product_save_after">
<observer name="klaviyo_reclaim" instance="Klaviyo\Reclaim\Observer\ProductSaveAfter"/>
</event>
<event name="admin_system_config_changed_section_klaviyo_reclaim_oauth">
<observer name="custom_admin_system_config_changed_section_klaviyo_reclaim_oauth" instance="Klaviyo\Reclaim\Observer\KlaviyoOAuthObserver"/>
</event>
Expand Down
Loading