From dd64a2deed069c49760268cafb9150cac8bb00a1 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 27 Aug 2024 11:28:10 +0200 Subject: [PATCH 01/56] Start webhook implementation --- Model/Carrier/UData.php | 11 ++- Observer/OrderCreateWebhook.php | 119 ++++++++++++++++++++++++++++++++ Observer/OrderUpdateWebhook.php | 76 ++++++++++++++++++++ etc/events.xml | 6 ++ etc/frontend/di.xml | 8 +++ 5 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 Observer/OrderCreateWebhook.php create mode 100644 Observer/OrderUpdateWebhook.php diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index 47b5d8b..a7e5d2e 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -12,12 +12,19 @@ class UData * * @var string */ - public const TRACKING = 'https://api.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; + public const TRACKING = 'https://api.dev.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; /** * Rates API Endpoint * * @var string */ - public const RATES_ENDPOINT = 'https://api.bobgo.co.za/rates-at-checkout/magento'; + public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento'; + + /** + * Order create/update webhook URL + * + * @var string + */ + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php new file mode 100644 index 0000000..f301cc8 --- /dev/null +++ b/Observer/OrderCreateWebhook.php @@ -0,0 +1,119 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use BobGroup\BobGo\Model\Carrier\UData; +use Psr\Log\LoggerInterface; +use Magento\Store\Model\StoreManagerInterface; + +class OrderCreateWebhook implements ObserverInterface +{ + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; // Initialize the Curl instance + $this->storeManager = $storeManager; + } + + public function execute(Observer $observer) + { + $this->logger->info('OrderCreateWebhook: execute method started'); + + $order = $observer->getEvent()->getOrder(); + if (!$order) { + $this->logger->error('OrderCreateWebhook: No order object found in observer'); + return; + } + + // Log order creation data + $this->logger->info('Order Created:', ['order_id' => $order->getId(), 'order_data' => $order->getData()]); + + // Extract order data and send to the webhook URL + $this->sendWebhook($order, 'order_created'); + + $this->logger->info('OrderCreateWebhook: execute method finished'); + } + + private function sendWebhook($order, $eventType) + { + $this->logger->info('OrderCreateWebhook: sendWebhook method started'); + + // Webhook URL + $url = $this->getWebhookUrl(); + $this->logger->info('OrderCreateWebhook: Webhook URL', ['url' => $url]); + + // Get Store UUID and add to query parameters + $storeUuid = $this->getStoreUuid(); + $this->logger->info('UUID: ', ['uuid' => $storeUuid]); + $url .= '?channel=' . urlencode($storeUuid); + $this->logger->info('Webhook URL', ['url' => $url]); + + // Prepare payload + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'order_data' => $order->getData() + ]; + + // Log webhook payload + $this->logger->info('Sending Webhook:', ['url' => $url, 'payload' => $data]); + + // Send the webhook + $this->makeHttpPostRequest($url, $data); + + $this->logger->info('OrderCreateWebhook: sendWebhook method finished'); + } + + + private function makeHttpPostRequest($url, $data) + { + $this->logger->info('URL:', ['url' => $url]); + $this->logger->info('Data:', ['data' => $data]); + + // Perform the API request + $payloadJson = json_encode($data); + if ($payloadJson === false) { + $this->logger->error('Payload Webhook failed: Unable to encode JSON.'); + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->logger->info('Payload Webhook:', ['response' => $payloadJson]); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + $statusCode = $this->curl->getStatus(); + $responseBody = $this->curl->getBody(); + + // Log status code and response body + $this->logger->info('Webhook Response Status:', ['status' => $statusCode]); + $this->logger->info('Webhook Response Body:', ['response' => $responseBody]); + + // Decode the response + $response = json_decode($responseBody, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $this->logger->error('Failed to decode webhook response:', ['error' => json_last_error_msg()]); + } else { + $this->logger->info('Response Webhook:', ['response' => $response]); + } + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreUuid(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + //return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } +} diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php new file mode 100644 index 0000000..ad2a4ad --- /dev/null +++ b/Observer/OrderUpdateWebhook.php @@ -0,0 +1,76 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Store\Model\StoreManagerInterface; +use BobGroup\BobGo\Model\Carrier\UData; +use Psr\Log\LoggerInterface; + +class OrderUpdateWebhook implements ObserverInterface +{ + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; + $this->storeManager = $storeManager; + } + + public function execute(Observer $observer) + { + $order = $observer->getEvent()->getOrder(); + if (!$order) { + return; + } + + $this->sendWebhook($order, 'order_updated'); + } + + private function sendWebhook($order, $eventType) + { + $url = $this->getWebhookUrl(); + + // Get Store UUID and add to query parameters + $storeUuid = $this->getStoreUuid(); + $url .= '?channel=' . urlencode($storeUuid); + + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'order_data' => $order->getData() + ]; + + $this->makeHttpPostRequest($url, $data); + } + + private function makeHttpPostRequest($url, $data) + { + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + $statusCode = $this->curl->getStatus(); + $responseBody = $this->curl->getBody(); + + $response = json_decode($responseBody, true); + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreUuid(): string + { + return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } +} diff --git a/etc/events.xml b/etc/events.xml index bfb5e96..3e71bc3 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -1,3 +1,9 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="sales_order_place_after"> + <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> + </event> + <event name="sales_order_save_after"> + <observer name="order_update_webhook" instance="BobGroup\BobGo\Observer\OrderUpdateWebhook"/> + </event> </config> diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index bc1aaeb..25a80a9 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -13,4 +13,12 @@ <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> </arguments> </type> + <type name="BobGroup\BobGo\Observer\OrderCreateWebhook"> + <arguments> + <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> + <argument name="curl" xsi:type="object">Magento\Framework\HTTP\Client\Curl</argument> + <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface</argument> + </arguments> + </type> + </config> -- GitLab From 6d003aacf9861dd4bed866e56577e56cce4a0e2f Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 28 Aug 2024 14:18:32 +0200 Subject: [PATCH 02/56] Add to webhooks --- Observer/OrderCreateWebhook.php | 78 ++++++++++++++++----------------- Observer/OrderUpdateWebhook.php | 52 +++++++++++++++++++--- 2 files changed, 82 insertions(+), 48 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index f301cc8..3f29ba7 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -18,91 +18,83 @@ class OrderCreateWebhook implements ObserverInterface public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) { $this->logger = $logger; - $this->curl = $curl; // Initialize the Curl instance + $this->curl = $curl; $this->storeManager = $storeManager; } public function execute(Observer $observer) { - $this->logger->info('OrderCreateWebhook: execute method started'); - $order = $observer->getEvent()->getOrder(); if (!$order) { - $this->logger->error('OrderCreateWebhook: No order object found in observer'); + //$this->logger->error('OrderCreateWebhook: No order object found in observer'); return; } - // Log order creation data - $this->logger->info('Order Created:', ['order_id' => $order->getId(), 'order_data' => $order->getData()]); - // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_created'); - - $this->logger->info('OrderCreateWebhook: execute method finished'); + $this->sendWebhook($order, 'order_create'); } private function sendWebhook($order, $eventType) { - $this->logger->info('OrderCreateWebhook: sendWebhook method started'); - // Webhook URL $url = $this->getWebhookUrl(); - $this->logger->info('OrderCreateWebhook: Webhook URL', ['url' => $url]); - // Get Store UUID and add to query parameters - $storeUuid = $this->getStoreUuid(); - $this->logger->info('UUID: ', ['uuid' => $storeUuid]); - $url .= '?channel=' . urlencode($storeUuid); - $this->logger->info('Webhook URL', ['url' => $url]); + // Extract order items + $itemsData = []; + foreach ($order->getAllItems() as $item) { + $itemsData[] = $item->getData(); + } + + // Extract shipping address + $shippingAddress = $order->getShippingAddress(); + $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; + + // Extract billing address + $billingAddress = $order->getBillingAddress(); + $billingAddressData = $billingAddress ? $billingAddress->getData() : []; // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), - 'order_data' => $order->getData() + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $this->getStoreId(), + 'order_data' => $order->getData(), + 'items' => $itemsData, + 'shipping_address' => $shippingAddressData, + 'billing_address' => $billingAddressData, ]; - // Log webhook payload - $this->logger->info('Sending Webhook:', ['url' => $url, 'payload' => $data]); - // Send the webhook $this->makeHttpPostRequest($url, $data); - - $this->logger->info('OrderCreateWebhook: sendWebhook method finished'); } - private function makeHttpPostRequest($url, $data) { - $this->logger->info('URL:', ['url' => $url]); - $this->logger->info('Data:', ['data' => $data]); + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'your_secret_key'; + $payloadJson = json_encode($data); + $signature = hash_hmac('sha256', $payloadJson, $secretKey); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here // Perform the API request $payloadJson = json_encode($data); if ($payloadJson === false) { - $this->logger->error('Payload Webhook failed: Unable to encode JSON.'); + //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); throw new \RuntimeException('Failed to encode payload to JSON.'); } - $this->logger->info('Payload Webhook:', ['response' => $payloadJson]); - // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); $statusCode = $this->curl->getStatus(); $responseBody = $this->curl->getBody(); - // Log status code and response body - $this->logger->info('Webhook Response Status:', ['status' => $statusCode]); - $this->logger->info('Webhook Response Body:', ['response' => $responseBody]); - // Decode the response $response = json_decode($responseBody, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $this->logger->error('Failed to decode webhook response:', ['error' => json_last_error_msg()]); - } else { - $this->logger->info('Response Webhook:', ['response' => $response]); - } } private function getWebhookUrl(): string @@ -110,10 +102,14 @@ class OrderCreateWebhook implements ObserverInterface return UData::WEBHOOK_URL; } - private function getStoreUuid(): string + private function getStoreId(): string { $storeId = $this->storeManager->getStore()->getId(); return $storeId; - //return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } + + private function getStoreUrl(): string + { + return $this->storeManager->getStore()->getBaseUrl(); } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index ad2a4ad..6d6b43a 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -5,9 +5,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; -use Magento\Store\Model\StoreManagerInterface; use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; +use Magento\Store\Model\StoreManagerInterface; class OrderUpdateWebhook implements ObserverInterface { @@ -29,38 +29,70 @@ class OrderUpdateWebhook implements ObserverInterface return; } + // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_updated'); } private function sendWebhook($order, $eventType) { + // Webhook URL $url = $this->getWebhookUrl(); - // Get Store UUID and add to query parameters - $storeUuid = $this->getStoreUuid(); - $url .= '?channel=' . urlencode($storeUuid); + // Extract order items + $itemsData = []; + foreach ($order->getAllItems() as $item) { + $itemsData[] = $item->getData(); + } + + // Extract shipping address + $shippingAddress = $order->getShippingAddress(); + $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; + + // Extract billing address + $billingAddress = $order->getBillingAddress(); + $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), - 'order_data' => $order->getData() + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $this->getStoreId(), + 'order_data' => $order->getData(), + 'items' => $itemsData, + 'shipping_address' => $shippingAddressData, + 'billing_address' => $billingAddressData, ]; + // Send the webhook $this->makeHttpPostRequest($url, $data); } private function makeHttpPostRequest($url, $data) { + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'your_secret_key'; + $payloadJson = json_encode($data); + $signature = hash_hmac('sha256', $payloadJson, $secretKey); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here + + // Perform the API request $payloadJson = json_encode($data); if ($payloadJson === false) { + //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); throw new \RuntimeException('Failed to encode payload to JSON.'); } + // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); $statusCode = $this->curl->getStatus(); $responseBody = $this->curl->getBody(); + // Decode the response $response = json_decode($responseBody, true); } @@ -69,8 +101,14 @@ class OrderUpdateWebhook implements ObserverInterface return UData::WEBHOOK_URL; } - private function getStoreUuid(): string + private function getStoreId(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + } + + private function getStoreUrl(): string { - return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + return $this->storeManager->getStore()->getBaseUrl(); } } -- GitLab From 4b9bbb9fa1cadabdd6ed70d30f4145748981bc04 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 08:14:04 +0200 Subject: [PATCH 03/56] Clean identifier url --- Observer/OrderCreateWebhook.php | 18 +++++++++++++++++- Observer/OrderUpdateWebhook.php | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 3f29ba7..010bec5 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -110,6 +110,22 @@ class OrderCreateWebhook implements ObserverInterface private function getStoreUrl(): string { - return $this->storeManager->getStore()->getBaseUrl(); + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 6d6b43a..01b7695 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -109,6 +109,22 @@ class OrderUpdateWebhook implements ObserverInterface private function getStoreUrl(): string { - return $this->storeManager->getStore()->getBaseUrl(); + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; } } -- GitLab From 10570227e1bf7bf6f77e469f93733229d53ebf50 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 10:55:10 +0200 Subject: [PATCH 04/56] webhook signature --- Observer/OrderCreateWebhook.php | 28 +++++++++++++++++----------- Observer/OrderUpdateWebhook.php | 25 +++++++++++++++---------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 010bec5..cf90443 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -31,7 +31,7 @@ class OrderCreateWebhook implements ObserverInterface } // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_create'); + $this->sendWebhook($order, 'order_created'); } private function sendWebhook($order, $eventType) @@ -53,28 +53,34 @@ class OrderCreateWebhook implements ObserverInterface $billingAddress = $order->getBillingAddress(); $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + $storeId = $this->getStoreId(); + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $this->getStoreId(), - 'order_data' => $order->getData(), - 'items' => $itemsData, - 'shipping_address' => $shippingAddressData, - 'billing_address' => $billingAddressData, + 'store_id' => $storeId, + //'order_data' => $order->getData(), + //'items' => $itemsData, + //'shipping_address' => $shippingAddressData, + //'billing_address' => $billingAddressData, ]; // Send the webhook - $this->makeHttpPostRequest($url, $data); + $this->makeHttpPostRequest($url, $data, $storeId); } - private function makeHttpPostRequest($url, $data) + private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'your_secret_key'; - $payloadJson = json_encode($data); - $signature = hash_hmac('sha256', $payloadJson, $secretKey); + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 01b7695..095c887 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -52,28 +52,33 @@ class OrderUpdateWebhook implements ObserverInterface $billingAddress = $order->getBillingAddress(); $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + $storeId = $this->getStoreId(); + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $this->getStoreId(), - 'order_data' => $order->getData(), - 'items' => $itemsData, - 'shipping_address' => $shippingAddressData, - 'billing_address' => $billingAddressData, + 'store_id' => $storeId, + //'order_data' => $order->getData(), + //'items' => $itemsData, + //'shipping_address' => $shippingAddressData, + //'billing_address' => $billingAddressData, ]; // Send the webhook - $this->makeHttpPostRequest($url, $data); + $this->makeHttpPostRequest($url, $data, $storeId); } - private function makeHttpPostRequest($url, $data) + private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'your_secret_key'; - $payloadJson = json_encode($data); - $signature = hash_hmac('sha256', $payloadJson, $secretKey); + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); -- GitLab From 716ecb04b0759c92c01b801f33af34182209e837 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 11:46:53 +0200 Subject: [PATCH 05/56] use sahred functions for webhooks --- Observer/OrderCreateWebhook.php | 104 +------------------------------- Observer/OrderUpdateWebhook.php | 102 +------------------------------ Observer/OrderWebhookBase.php | 85 ++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 204 deletions(-) create mode 100644 Observer/OrderWebhookBase.php diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index cf90443..1b93779 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -9,7 +9,7 @@ use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; use Magento\Store\Model\StoreManagerInterface; -class OrderCreateWebhook implements ObserverInterface +class OrderCreateWebhook extends OrderWebhookBase { protected Curl $curl; protected LoggerInterface $logger; @@ -26,112 +26,10 @@ class OrderCreateWebhook implements ObserverInterface { $order = $observer->getEvent()->getOrder(); if (!$order) { - //$this->logger->error('OrderCreateWebhook: No order object found in observer'); return; } // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_created'); } - - private function sendWebhook($order, $eventType) - { - // Webhook URL - $url = $this->getWebhookUrl(); - - // Extract order items - $itemsData = []; - foreach ($order->getAllItems() as $item) { - $itemsData[] = $item->getData(); - } - - // Extract shipping address - $shippingAddress = $order->getShippingAddress(); - $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; - - // Extract billing address - $billingAddress = $order->getBillingAddress(); - $billingAddressData = $billingAddress ? $billingAddress->getData() : []; - - $storeId = $this->getStoreId(); - - // Prepare payload - $data = [ - 'event' => $eventType, - 'order_id' => $order->getId(), - 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $storeId, - //'order_data' => $order->getData(), - //'items' => $itemsData, - //'shipping_address' => $shippingAddressData, - //'billing_address' => $billingAddressData, - ]; - - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); - $statusCode = $this->curl->getStatus(); - $responseBody = $this->curl->getBody(); - - // Decode the response - $response = json_decode($responseBody, true); - } - - private function getWebhookUrl(): string - { - return UData::WEBHOOK_URL; - } - - private function getStoreId(): string - { - $storeId = $this->storeManager->getStore()->getId(); - return $storeId; - } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 095c887..03b96cb 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -9,7 +9,7 @@ use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; use Magento\Store\Model\StoreManagerInterface; -class OrderUpdateWebhook implements ObserverInterface +class OrderUpdateWebhook extends OrderWebhookBase { protected Curl $curl; protected LoggerInterface $logger; @@ -32,104 +32,4 @@ class OrderUpdateWebhook implements ObserverInterface // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_updated'); } - - private function sendWebhook($order, $eventType) - { - // Webhook URL - $url = $this->getWebhookUrl(); - - // Extract order items - $itemsData = []; - foreach ($order->getAllItems() as $item) { - $itemsData[] = $item->getData(); - } - - // Extract shipping address - $shippingAddress = $order->getShippingAddress(); - $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; - - // Extract billing address - $billingAddress = $order->getBillingAddress(); - $billingAddressData = $billingAddress ? $billingAddress->getData() : []; - - $storeId = $this->getStoreId(); - - // Prepare payload - $data = [ - 'event' => $eventType, - 'order_id' => $order->getId(), - 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $storeId, - //'order_data' => $order->getData(), - //'items' => $itemsData, - //'shipping_address' => $shippingAddressData, - //'billing_address' => $billingAddressData, - ]; - - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); - $statusCode = $this->curl->getStatus(); - $responseBody = $this->curl->getBody(); - - // Decode the response - $response = json_decode($responseBody, true); - } - - private function getWebhookUrl(): string - { - return UData::WEBHOOK_URL; - } - - private function getStoreId(): string - { - $storeId = $this->storeManager->getStore()->getId(); - return $storeId; - } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php new file mode 100644 index 0000000..9f2e83f --- /dev/null +++ b/Observer/OrderWebhookBase.php @@ -0,0 +1,85 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\Event\ObserverInterface; + +abstract class OrderWebhookBase implements ObserverInterface +{ + protected function sendWebhook($order, $eventType) + { + // Webhook URL + $url = $this->getWebhookUrl(); + + $storeId = $this->getStoreId(); + + // Prepare payload + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $storeId, + ]; + + // Send the webhook + $this->makeHttpPostRequest($url, $data, $storeId); + } + + private function makeHttpPostRequest($url, $data, $storeId) + { + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); + + // Perform the API request + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreId(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + } + + private function getStoreUrl(): string + { + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } +} -- GitLab From 9d91f05abd105839cda7e36e85312fca08884a49 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 12:00:12 +0200 Subject: [PATCH 06/56] cleanup --- Observer/OrderCreateWebhook.php | 16 ---------------- Observer/OrderUpdateWebhook.php | 16 ---------------- Observer/OrderWebhookBase.php | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 1b93779..678f2de 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -3,25 +3,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\HTTP\Client\Curl; -use BobGroup\BobGo\Model\Carrier\UData; -use Psr\Log\LoggerInterface; -use Magento\Store\Model\StoreManagerInterface; class OrderCreateWebhook extends OrderWebhookBase { - protected Curl $curl; - protected LoggerInterface $logger; - protected StoreManagerInterface $storeManager; - - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) - { - $this->logger = $logger; - $this->curl = $curl; - $this->storeManager = $storeManager; - } - public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 03b96cb..3074bdd 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -3,25 +3,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\HTTP\Client\Curl; -use BobGroup\BobGo\Model\Carrier\UData; -use Psr\Log\LoggerInterface; -use Magento\Store\Model\StoreManagerInterface; class OrderUpdateWebhook extends OrderWebhookBase { - protected Curl $curl; - protected LoggerInterface $logger; - protected StoreManagerInterface $storeManager; - - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) - { - $this->logger = $logger; - $this->curl = $curl; - $this->storeManager = $storeManager; - } - public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 9f2e83f..b17375e 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -4,9 +4,23 @@ namespace BobGroup\BobGo\Observer; use BobGroup\BobGo\Model\Carrier\UData; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; abstract class OrderWebhookBase implements ObserverInterface { + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; + $this->storeManager = $storeManager; + } + protected function sendWebhook($order, $eventType) { // Webhook URL -- GitLab From 2c7e23cd114945e40000eb3c0ded52295faadfab Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 14:57:11 +0200 Subject: [PATCH 07/56] orderId for both created and updated --- Observer/OrderCreateWebhook.php | 2 +- Observer/OrderUpdateWebhook.php | 19 ------------------- Observer/OrderWebhookBase.php | 23 +++++++++++++++++++++-- etc/events.xml | 5 +---- 4 files changed, 23 insertions(+), 26 deletions(-) delete mode 100644 Observer/OrderUpdateWebhook.php diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 678f2de..74f1ce4 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -14,6 +14,6 @@ class OrderCreateWebhook extends OrderWebhookBase } // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_created'); + $this->sendWebhook($order); } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php deleted file mode 100644 index 3074bdd..0000000 --- a/Observer/OrderUpdateWebhook.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Observer; - -use Magento\Framework\Event\Observer; - -class OrderUpdateWebhook extends OrderWebhookBase -{ - public function execute(Observer $observer) - { - $order = $observer->getEvent()->getOrder(); - if (!$order) { - return; - } - - // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_updated'); - } -} diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index b17375e..f4ca74d 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -21,17 +21,36 @@ abstract class OrderWebhookBase implements ObserverInterface $this->storeManager = $storeManager; } - protected function sendWebhook($order, $eventType) + protected function sendWebhook($order) { // Webhook URL $url = $this->getWebhookUrl(); $storeId = $this->getStoreId(); + $orderId = $order->getId(); + + // Get the order creation time + $createdAt = strtotime($order->getCreatedAt()); + $currentTime = time(); + + // Define a time threshold to consider the order as newly created + $threshold = 5; // 5 seconds + + // Determine the event type based on the creation time + if (($currentTime - $createdAt) < $threshold) { + $eventType = 'order_created'; + } else { + $eventType = 'order_updated'; + } + + // Log the event type for debugging purposes + $this->logger->info('event: ' . $eventType); + // Prepare payload $data = [ 'event' => $eventType, - 'order_id' => $order->getId(), + 'order_id' => $orderId, 'channel_identifier' => $this->getStoreUrl(), 'store_id' => $storeId, ]; diff --git a/etc/events.xml b/etc/events.xml index 3e71bc3..d4d14f1 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -1,9 +1,6 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="sales_order_place_after"> - <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> - </event> <event name="sales_order_save_after"> - <observer name="order_update_webhook" instance="BobGroup\BobGo\Observer\OrderUpdateWebhook"/> + <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> </event> </config> -- GitLab From bb9b08a1e575ba35c1c73d2fe254969d6d6b1af6 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 14:58:47 +0200 Subject: [PATCH 08/56] cleanup --- Observer/OrderWebhookBase.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index f4ca74d..eeb106e 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -44,9 +44,6 @@ abstract class OrderWebhookBase implements ObserverInterface $eventType = 'order_updated'; } - // Log the event type for debugging purposes - $this->logger->info('event: ' . $eventType); - // Prepare payload $data = [ 'event' => $eventType, -- GitLab From 366c40ca7cb2693fa52b45e0195be818cb6385d1 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 15:08:50 +0200 Subject: [PATCH 09/56] change endpoint --- Model/Carrier/UData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index a7e5d2e..adf83a2 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; } -- GitLab From 0a4fe2d80feadc91b8296bc610ca400d07b75a51 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Fri, 30 Aug 2024 15:00:54 +0200 Subject: [PATCH 10/56] start implementation --- Block/TrackOrder.php | 18 ++++++++++ Controller/Test/Index.php | 20 +++++++++++ Controller/TrackOrder/Index.php | 33 +++++++++++++++++++ etc/adminhtml/system.xml | 6 ++++ etc/frontend/routes.xml | 7 ++++ .../layout/bobgo_trackorder_index.xml | 14 ++++++++ view/frontend/layout/customer_account.xml | 20 +++++++++++ .../frontend/templates/TrackOrder/index.phtml | 5 +++ 8 files changed, 123 insertions(+) create mode 100644 Block/TrackOrder.php create mode 100644 Controller/Test/Index.php create mode 100644 Controller/TrackOrder/Index.php create mode 100644 etc/frontend/routes.xml create mode 100644 view/frontend/layout/bobgo_trackorder_index.xml create mode 100644 view/frontend/layout/customer_account.xml create mode 100644 view/frontend/templates/TrackOrder/index.phtml diff --git a/Block/TrackOrder.php b/Block/TrackOrder.php new file mode 100644 index 0000000..49983f6 --- /dev/null +++ b/Block/TrackOrder.php @@ -0,0 +1,18 @@ +<?php +namespace BobGroup\BobGo\Block; +class TrackOrder extends \Magento\Framework\View\Element\Template +{ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + array $data = [] + ) + { + parent::__construct($context, $data); + } + + public function getHelloWorld() + { + return 'Hello World'; + } + +} diff --git a/Controller/Test/Index.php b/Controller/Test/Index.php new file mode 100644 index 0000000..115b950 --- /dev/null +++ b/Controller/Test/Index.php @@ -0,0 +1,20 @@ +<?php + +namespace BobGroup\BobGo\Controller\Test; + +use Magento\Framework\App\Action\Action; +use Magento\Framework\App\Action\Context; + +class Index extends Action +{ + public function __construct(Context $context) + { + parent::__construct($context); + } + + public function execute() + { + echo "Test controller works!"; + exit; + } +} diff --git a/Controller/TrackOrder/Index.php b/Controller/TrackOrder/Index.php new file mode 100644 index 0000000..0810484 --- /dev/null +++ b/Controller/TrackOrder/Index.php @@ -0,0 +1,33 @@ +<?php + +namespace BobGroup\BobGo\Controller\TrackOrder; + +class Index extends \Magento\Framework\App\Action\Action +{ + /** + * @var \Magento\Framework\View\Result\PageFactory + */ + protected $resultPageFactory; + + /** + * @param \Magento\Framework\App\Action\Context $context + * @param \Magento\Framework\View\Result\PageFactory resultPageFactory + */ + public function __construct( + \Magento\Framework\App\Action\Context $context, + \Magento\Framework\View\Result\PageFactory $resultPageFactory + ) + { + $this->resultPageFactory = $resultPageFactory; + parent::__construct($context); + } + /** + * Default customer account page + * + * @return void + */ + public function execute() + { + return $this->resultPageFactory->create(); + } +} diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 410c2c2..263bc8b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -28,7 +28,13 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Displays the delivery timeframe and additional service level description, as configured on Bob Go.</comment> </field> + <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Enable Track My Order</label> + <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> </section> + </system> </config> diff --git a/etc/frontend/routes.xml b/etc/frontend/routes.xml new file mode 100644 index 0000000..e0d84f8 --- /dev/null +++ b/etc/frontend/routes.xml @@ -0,0 +1,7 @@ +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> + <router id="standard"> + <route id="trackorder" frontName="trackorder"> + <module name="BobGroup_BobGo" /> + </route> + </router> +</config> diff --git a/view/frontend/layout/bobgo_trackorder_index.xml b/view/frontend/layout/bobgo_trackorder_index.xml new file mode 100644 index 0000000..91e4a80 --- /dev/null +++ b/view/frontend/layout/bobgo_trackorder_index.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> +<update handle="customer_account"/> +<head> + <title> + Your First Link + </title> +</head> +<body> + <referenceContainer name="content"> + <block class="BobGroup\BobGo\Block\TrackOrder" name="bobgo.trackorder.index" template="BobGroup_BobGo::trackorder/index.phtml" cacheable="false" /> + </referenceContainer> +</body> +</page> diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml new file mode 100644 index 0000000..50bb287 --- /dev/null +++ b/view/frontend/layout/customer_account.xml @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="customer_account_navigation"> + <!-- Add menu to the end of the sidebar --> + <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-first-link"> + <arguments> + <argument name="path" xsi:type="string">BobGo/TrackOrder/index</argument> + <argument name="label" xsi:type="string">Your First Link</argument> + </arguments> + </block> + <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-second-link"> + <arguments> + <argument name="path" xsi:type="string">BobGo/TrackOrder/index</argument> + <argument name="label" xsi:type="string">Your Second Link</argument> + </arguments> + </block> + </referenceBlock> + </body> +</page> diff --git a/view/frontend/templates/TrackOrder/index.phtml b/view/frontend/templates/TrackOrder/index.phtml new file mode 100644 index 0000000..8c81e6b --- /dev/null +++ b/view/frontend/templates/TrackOrder/index.phtml @@ -0,0 +1,5 @@ +<h2> + <?php echo $block->getHelloWorld(); ?> +</h2> + +echo 'My First Link Page'; -- GitLab From 61a9c3d101c0dbdefa85d959141d0d684dc69f34 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 2 Sep 2024 10:31:31 +0200 Subject: [PATCH 11/56] page link working --- Block/System/Config/Form/Field/Version.php | 66 ------------------- Block/{TrackOrder.php => YourBlock.php} | 2 +- Controller/Test/Index.php | 20 ------ .../{TrackOrder => YourFirstLink}/Index.php | 8 ++- Controller/YourSecondLink/Index.php | 38 +++++++++++ etc/frontend/routes.xml | 4 +- .../layout/bobgo_trackorder_index.xml | 14 ---- .../layout/bobgo_yourfirstlink_index.xml | 14 ++++ .../layout/bobgo_yoursecondlink_index.xml | 14 ++++ view/frontend/layout/customer_account.xml | 34 +++++----- .../frontend/templates/secondlink/index.phtml | 1 + .../{TrackOrder => yourfirstlink}/index.phtml | 0 12 files changed, 95 insertions(+), 120 deletions(-) delete mode 100644 Block/System/Config/Form/Field/Version.php rename Block/{TrackOrder.php => YourBlock.php} (82%) delete mode 100644 Controller/Test/Index.php rename Controller/{TrackOrder => YourFirstLink}/Index.php (76%) create mode 100644 Controller/YourSecondLink/Index.php delete mode 100644 view/frontend/layout/bobgo_trackorder_index.xml create mode 100644 view/frontend/layout/bobgo_yourfirstlink_index.xml create mode 100644 view/frontend/layout/bobgo_yoursecondlink_index.xml create mode 100644 view/frontend/templates/secondlink/index.phtml rename view/frontend/templates/{TrackOrder => yourfirstlink}/index.phtml (100%) diff --git a/Block/System/Config/Form/Field/Version.php b/Block/System/Config/Form/Field/Version.php deleted file mode 100644 index 2ebcbba..0000000 --- a/Block/System/Config/Form/Field/Version.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Block\System\Config\Form\Field; - -use Magento\Framework\Data\Form\Element\AbstractElement; -use BobGroup\BobGo\Helper\Data; - -/** - * Displays Version number in System Configuration - * - * This block is responsible for displaying the version number of the BobGo extension - * in the system configuration settings. - * - * @website https://www.bobgo.co.za - */ -class Version extends \Magento\Config\Block\System\Config\Form\Field -{ - /** - * @var string - */ - public const EXTENSION_URL = 'https://www.bobgo.co.za'; - - /** - * @var Data - */ - protected Data $_helper; - - /** - * Constructor - * - * @param \Magento\Backend\Block\Template\Context $context - * @param Data $helper - */ - public function __construct( - \Magento\Backend\Block\Template\Context $context, - Data $helper - ) { - $this->_helper = $helper; - parent::__construct($context); - } - - /** - * Get HTML for the element - * - * @param AbstractElement $element - * @return string - */ - protected function _getElementHtml(AbstractElement $element): string - { - $extensionVersion = $this->_helper->getExtensionVersion(); - $extensionTitle = 'BobGo'; - $versionLabel = sprintf( - '<a href="%s" title="%s" target="_blank">%s</a>', - self::EXTENSION_URL, - $extensionTitle, - $extensionVersion - ); - - // Set the value using setData - $element->setData('value', $versionLabel); - - // Ensure the return value is a string or provide a fallback - $value = $element->getData('value'); - return is_string($value) ? $value : ''; - } -} diff --git a/Block/TrackOrder.php b/Block/YourBlock.php similarity index 82% rename from Block/TrackOrder.php rename to Block/YourBlock.php index 49983f6..c3e7e01 100644 --- a/Block/TrackOrder.php +++ b/Block/YourBlock.php @@ -1,6 +1,6 @@ <?php namespace BobGroup\BobGo\Block; -class TrackOrder extends \Magento\Framework\View\Element\Template +class YourBlock extends \Magento\Framework\View\Element\Template { public function __construct( \Magento\Backend\Block\Template\Context $context, diff --git a/Controller/Test/Index.php b/Controller/Test/Index.php deleted file mode 100644 index 115b950..0000000 --- a/Controller/Test/Index.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Controller\Test; - -use Magento\Framework\App\Action\Action; -use Magento\Framework\App\Action\Context; - -class Index extends Action -{ - public function __construct(Context $context) - { - parent::__construct($context); - } - - public function execute() - { - echo "Test controller works!"; - exit; - } -} diff --git a/Controller/TrackOrder/Index.php b/Controller/YourFirstLink/Index.php similarity index 76% rename from Controller/TrackOrder/Index.php rename to Controller/YourFirstLink/Index.php index 0810484..51781bc 100644 --- a/Controller/TrackOrder/Index.php +++ b/Controller/YourFirstLink/Index.php @@ -1,6 +1,7 @@ <?php +namespace BobGroup\BobGo\Controller\YourFirstLink; -namespace BobGroup\BobGo\Controller\TrackOrder; +use Psr\Log\LoggerInterface; class Index extends \Magento\Framework\App\Action\Action { @@ -9,16 +10,20 @@ class Index extends \Magento\Framework\App\Action\Action */ protected $resultPageFactory; + protected $logger; + /** * @param \Magento\Framework\App\Action\Context $context * @param \Magento\Framework\View\Result\PageFactory resultPageFactory */ public function __construct( \Magento\Framework\App\Action\Context $context, + LoggerInterface $logger, \Magento\Framework\View\Result\PageFactory $resultPageFactory ) { $this->resultPageFactory = $resultPageFactory; + $this->logger = $logger; parent::__construct($context); } /** @@ -28,6 +33,7 @@ class Index extends \Magento\Framework\App\Action\Action */ public function execute() { + $this->logger->info('Page Controller is executed.'); return $this->resultPageFactory->create(); } } diff --git a/Controller/YourSecondLink/Index.php b/Controller/YourSecondLink/Index.php new file mode 100644 index 0000000..be38312 --- /dev/null +++ b/Controller/YourSecondLink/Index.php @@ -0,0 +1,38 @@ +<?php +namespace BobGroup\BobGo\Controller\YourSecondLink; + +use Psr\Log\LoggerInterface; +class Index extends \Magento\Framework\App\Action\Action +{ + /** + * @var \Magento\Framework\View\Result\PageFactory + */ + protected $resultPageFactory; + + protected $logger; + + /** + * @param \Magento\Framework\App\Action\Context $context + * @param \Magento\Framework\View\Result\PageFactory resultPageFactory + */ + public function __construct( + \Magento\Framework\App\Action\Context $context, + LoggerInterface $logger, + \Magento\Framework\View\Result\PageFactory $resultPageFactory + ) + { + $this->resultPageFactory = $resultPageFactory; + $this->logger = $logger; + parent::__construct($context); + } + /** + * Default customer account page + * + * @return void + */ + public function execute() + { + $this->logger->info('Page2 Controller is executed.'); + return $this->resultPageFactory->create(); + } +} diff --git a/etc/frontend/routes.xml b/etc/frontend/routes.xml index e0d84f8..d6d1046 100644 --- a/etc/frontend/routes.xml +++ b/etc/frontend/routes.xml @@ -1,6 +1,8 @@ +<?xml version="1.0"?> + <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> - <route id="trackorder" frontName="trackorder"> + <route id="bobgo" frontName="bobgo"> <module name="BobGroup_BobGo" /> </route> </router> diff --git a/view/frontend/layout/bobgo_trackorder_index.xml b/view/frontend/layout/bobgo_trackorder_index.xml deleted file mode 100644 index 91e4a80..0000000 --- a/view/frontend/layout/bobgo_trackorder_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> -<update handle="customer_account"/> -<head> - <title> - Your First Link - </title> -</head> -<body> - <referenceContainer name="content"> - <block class="BobGroup\BobGo\Block\TrackOrder" name="bobgo.trackorder.index" template="BobGroup_BobGo::trackorder/index.phtml" cacheable="false" /> - </referenceContainer> -</body> -</page> diff --git a/view/frontend/layout/bobgo_yourfirstlink_index.xml b/view/frontend/layout/bobgo_yourfirstlink_index.xml new file mode 100644 index 0000000..f5178ae --- /dev/null +++ b/view/frontend/layout/bobgo_yourfirstlink_index.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <update handle="customer_account"/> + <head> + <title> + Your First Link + </title> + </head> + <body> + <referenceContainer name="content"> + <block class="BobGroup\BobGo\Block\YourBlock" name="bobgo.firstlink.index" template="BobGroup_BobGo::yourfirstlink/index.phtml" cacheable="false" /> + </referenceContainer> + </body> +</page> diff --git a/view/frontend/layout/bobgo_yoursecondlink_index.xml b/view/frontend/layout/bobgo_yoursecondlink_index.xml new file mode 100644 index 0000000..d456381 --- /dev/null +++ b/view/frontend/layout/bobgo_yoursecondlink_index.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <update handle="customer_account"/> + <head> + <title> + Your Second Link + </title> + </head> + <body> + <referenceContainer name="content"> + <block class="Magento\Framework\View\Element\Template" name="bobgo.secondlink.index" template="BobGroup_BobGo::yoursecondlink/index.phtml" cacheable="false" /> + </referenceContainer> + </body> +</page> diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml index 50bb287..cdc2464 100644 --- a/view/frontend/layout/customer_account.xml +++ b/view/frontend/layout/customer_account.xml @@ -1,20 +1,20 @@ <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <body> - <referenceBlock name="customer_account_navigation"> - <!-- Add menu to the end of the sidebar --> - <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-first-link"> - <arguments> - <argument name="path" xsi:type="string">BobGo/TrackOrder/index</argument> - <argument name="label" xsi:type="string">Your First Link</argument> - </arguments> - </block> - <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-second-link"> - <arguments> - <argument name="path" xsi:type="string">BobGo/TrackOrder/index</argument> - <argument name="label" xsi:type="string">Your Second Link</argument> - </arguments> - </block> - </referenceBlock> - </body> +<body> + <referenceBlock name="customer_account_navigation"> + <!-- Add menu to the end of the sidebar --> + <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-first-link"> + <arguments> + <argument name="path" xsi:type="string">bobgo/yourfirstlink/index</argument> + <argument name="label" xsi:type="string">Your First Link</argument> + </arguments> + </block> + <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-second-link"> + <arguments> + <argument name="path" xsi:type="string">bobgo/yoursecondlink/index</argument> + <argument name="label" xsi:type="string">Your Second Link</argument> + </arguments> + </block> + </referenceBlock> +</body> </page> diff --git a/view/frontend/templates/secondlink/index.phtml b/view/frontend/templates/secondlink/index.phtml new file mode 100644 index 0000000..c19dc87 --- /dev/null +++ b/view/frontend/templates/secondlink/index.phtml @@ -0,0 +1 @@ +echo 'My Second Link Page'; diff --git a/view/frontend/templates/TrackOrder/index.phtml b/view/frontend/templates/yourfirstlink/index.phtml similarity index 100% rename from view/frontend/templates/TrackOrder/index.phtml rename to view/frontend/templates/yourfirstlink/index.phtml -- GitLab From 0b51db24a80dee63d1bc28eafa2be2775807cdef Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 2 Sep 2024 10:41:33 +0200 Subject: [PATCH 12/56] tracking page link working --- Block/{YourBlock.php => TrackingBlock.php} | 2 +- .../{YourFirstLink => Tracking}/Index.php | 2 +- Controller/YourSecondLink/Index.php | 38 ------------------- ...ink_index.xml => bobgo_tracking_index.xml} | 4 +- .../layout/bobgo_yoursecondlink_index.xml | 14 ------- view/frontend/layout/customer_account.xml | 12 ++---- .../frontend/templates/secondlink/index.phtml | 1 - .../{yourfirstlink => tracking}/index.phtml | 0 8 files changed, 7 insertions(+), 66 deletions(-) rename Block/{YourBlock.php => TrackingBlock.php} (81%) rename Controller/{YourFirstLink => Tracking}/Index.php (94%) delete mode 100644 Controller/YourSecondLink/Index.php rename view/frontend/layout/{bobgo_yourfirstlink_index.xml => bobgo_tracking_index.xml} (67%) delete mode 100644 view/frontend/layout/bobgo_yoursecondlink_index.xml delete mode 100644 view/frontend/templates/secondlink/index.phtml rename view/frontend/templates/{yourfirstlink => tracking}/index.phtml (100%) diff --git a/Block/YourBlock.php b/Block/TrackingBlock.php similarity index 81% rename from Block/YourBlock.php rename to Block/TrackingBlock.php index c3e7e01..f77dab0 100644 --- a/Block/YourBlock.php +++ b/Block/TrackingBlock.php @@ -1,6 +1,6 @@ <?php namespace BobGroup\BobGo\Block; -class YourBlock extends \Magento\Framework\View\Element\Template +class TrackingBlock extends \Magento\Framework\View\Element\Template { public function __construct( \Magento\Backend\Block\Template\Context $context, diff --git a/Controller/YourFirstLink/Index.php b/Controller/Tracking/Index.php similarity index 94% rename from Controller/YourFirstLink/Index.php rename to Controller/Tracking/Index.php index 51781bc..9955e8d 100644 --- a/Controller/YourFirstLink/Index.php +++ b/Controller/Tracking/Index.php @@ -1,5 +1,5 @@ <?php -namespace BobGroup\BobGo\Controller\YourFirstLink; +namespace BobGroup\BobGo\Controller\Tracking; use Psr\Log\LoggerInterface; diff --git a/Controller/YourSecondLink/Index.php b/Controller/YourSecondLink/Index.php deleted file mode 100644 index be38312..0000000 --- a/Controller/YourSecondLink/Index.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -namespace BobGroup\BobGo\Controller\YourSecondLink; - -use Psr\Log\LoggerInterface; -class Index extends \Magento\Framework\App\Action\Action -{ - /** - * @var \Magento\Framework\View\Result\PageFactory - */ - protected $resultPageFactory; - - protected $logger; - - /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\View\Result\PageFactory resultPageFactory - */ - public function __construct( - \Magento\Framework\App\Action\Context $context, - LoggerInterface $logger, - \Magento\Framework\View\Result\PageFactory $resultPageFactory - ) - { - $this->resultPageFactory = $resultPageFactory; - $this->logger = $logger; - parent::__construct($context); - } - /** - * Default customer account page - * - * @return void - */ - public function execute() - { - $this->logger->info('Page2 Controller is executed.'); - return $this->resultPageFactory->create(); - } -} diff --git a/view/frontend/layout/bobgo_yourfirstlink_index.xml b/view/frontend/layout/bobgo_tracking_index.xml similarity index 67% rename from view/frontend/layout/bobgo_yourfirstlink_index.xml rename to view/frontend/layout/bobgo_tracking_index.xml index f5178ae..6e842d7 100644 --- a/view/frontend/layout/bobgo_yourfirstlink_index.xml +++ b/view/frontend/layout/bobgo_tracking_index.xml @@ -3,12 +3,12 @@ <update handle="customer_account"/> <head> <title> - Your First Link + Tracking </title> </head> <body> <referenceContainer name="content"> - <block class="BobGroup\BobGo\Block\YourBlock" name="bobgo.firstlink.index" template="BobGroup_BobGo::yourfirstlink/index.phtml" cacheable="false" /> + <block class="BobGroup\BobGo\Block\TrackingBlock" name="bobgo.tracking.index" template="BobGroup_BobGo::tracking/index.phtml" cacheable="false" /> </referenceContainer> </body> </page> diff --git a/view/frontend/layout/bobgo_yoursecondlink_index.xml b/view/frontend/layout/bobgo_yoursecondlink_index.xml deleted file mode 100644 index d456381..0000000 --- a/view/frontend/layout/bobgo_yoursecondlink_index.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> - <update handle="customer_account"/> - <head> - <title> - Your Second Link - </title> - </head> - <body> - <referenceContainer name="content"> - <block class="Magento\Framework\View\Element\Template" name="bobgo.secondlink.index" template="BobGroup_BobGo::yoursecondlink/index.phtml" cacheable="false" /> - </referenceContainer> - </body> -</page> diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml index cdc2464..54c3c21 100644 --- a/view/frontend/layout/customer_account.xml +++ b/view/frontend/layout/customer_account.xml @@ -3,16 +3,10 @@ <body> <referenceBlock name="customer_account_navigation"> <!-- Add menu to the end of the sidebar --> - <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-first-link"> + <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-tracking"> <arguments> - <argument name="path" xsi:type="string">bobgo/yourfirstlink/index</argument> - <argument name="label" xsi:type="string">Your First Link</argument> - </arguments> - </block> - <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-your-second-link"> - <arguments> - <argument name="path" xsi:type="string">bobgo/yoursecondlink/index</argument> - <argument name="label" xsi:type="string">Your Second Link</argument> + <argument name="path" xsi:type="string">bobgo/tracking/index</argument> + <argument name="label" xsi:type="string">Tracking</argument> </arguments> </block> </referenceBlock> diff --git a/view/frontend/templates/secondlink/index.phtml b/view/frontend/templates/secondlink/index.phtml deleted file mode 100644 index c19dc87..0000000 --- a/view/frontend/templates/secondlink/index.phtml +++ /dev/null @@ -1 +0,0 @@ -echo 'My Second Link Page'; diff --git a/view/frontend/templates/yourfirstlink/index.phtml b/view/frontend/templates/tracking/index.phtml similarity index 100% rename from view/frontend/templates/yourfirstlink/index.phtml rename to view/frontend/templates/tracking/index.phtml -- GitLab From d5dc33c1f7ef981b99e6af4194d2aae146f4e671 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 2 Sep 2024 14:50:18 +0200 Subject: [PATCH 13/56] progress --- Block/System/Config/Form/Field/Version.php | 66 +++++++++ Block/TrackingBlock.php | 23 +++- Controller/Tracking/Index.php | 138 ++++++++++++++++--- Model/Carrier/UData.php | 4 +- etc/frontend/di.xml | 5 + view/frontend/layout/customer_account.xml | 2 +- view/frontend/templates/tracking/index.phtml | 79 ++++++++++- view/frontend/web/images/logo.png | Bin 0 -> 59227 bytes 8 files changed, 286 insertions(+), 31 deletions(-) create mode 100644 Block/System/Config/Form/Field/Version.php create mode 100644 view/frontend/web/images/logo.png diff --git a/Block/System/Config/Form/Field/Version.php b/Block/System/Config/Form/Field/Version.php new file mode 100644 index 0000000..2ebcbba --- /dev/null +++ b/Block/System/Config/Form/Field/Version.php @@ -0,0 +1,66 @@ +<?php + +namespace BobGroup\BobGo\Block\System\Config\Form\Field; + +use Magento\Framework\Data\Form\Element\AbstractElement; +use BobGroup\BobGo\Helper\Data; + +/** + * Displays Version number in System Configuration + * + * This block is responsible for displaying the version number of the BobGo extension + * in the system configuration settings. + * + * @website https://www.bobgo.co.za + */ +class Version extends \Magento\Config\Block\System\Config\Form\Field +{ + /** + * @var string + */ + public const EXTENSION_URL = 'https://www.bobgo.co.za'; + + /** + * @var Data + */ + protected Data $_helper; + + /** + * Constructor + * + * @param \Magento\Backend\Block\Template\Context $context + * @param Data $helper + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + Data $helper + ) { + $this->_helper = $helper; + parent::__construct($context); + } + + /** + * Get HTML for the element + * + * @param AbstractElement $element + * @return string + */ + protected function _getElementHtml(AbstractElement $element): string + { + $extensionVersion = $this->_helper->getExtensionVersion(); + $extensionTitle = 'BobGo'; + $versionLabel = sprintf( + '<a href="%s" title="%s" target="_blank">%s</a>', + self::EXTENSION_URL, + $extensionTitle, + $extensionVersion + ); + + // Set the value using setData + $element->setData('value', $versionLabel); + + // Ensure the return value is a string or provide a fallback + $value = $element->getData('value'); + return is_string($value) ? $value : ''; + } +} diff --git a/Block/TrackingBlock.php b/Block/TrackingBlock.php index f77dab0..69068a2 100644 --- a/Block/TrackingBlock.php +++ b/Block/TrackingBlock.php @@ -1,18 +1,27 @@ <?php + namespace BobGroup\BobGo\Block; + +use Magento\Framework\View\Element\Template; + class TrackingBlock extends \Magento\Framework\View\Element\Template { - public function __construct( - \Magento\Backend\Block\Template\Context $context, - array $data = [] - ) + protected $response; + + protected function _toHtml() { - parent::__construct($context, $data); + $this->_logger->info('Block HTML rendered: ' . $this->getNameInLayout()); + return parent::_toHtml(); } - public function getHelloWorld() + public function setResponse(array $response): self { - return 'Hello World'; + $this->_response = $response; + return $this; } + public function getResponse() + { + return $this->response; + } } diff --git a/Controller/Tracking/Index.php b/Controller/Tracking/Index.php index 9955e8d..7d073ba 100644 --- a/Controller/Tracking/Index.php +++ b/Controller/Tracking/Index.php @@ -2,38 +2,142 @@ namespace BobGroup\BobGo\Controller\Tracking; use Psr\Log\LoggerInterface; +use Magento\Framework\App\Action\Context; +use Magento\Framework\View\Result\PageFactory; +use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Store\Model\StoreManagerInterface; class Index extends \Magento\Framework\App\Action\Action { - /** - * @var \Magento\Framework\View\Result\PageFactory - */ protected $resultPageFactory; - + protected $jsonFactory; + protected $curl; protected $logger; + protected StoreManagerInterface $storeManager; - /** - * @param \Magento\Framework\App\Action\Context $context - * @param \Magento\Framework\View\Result\PageFactory resultPageFactory - */ public function __construct( - \Magento\Framework\App\Action\Context $context, + Context $context, + PageFactory $resultPageFactory, + JsonFactory $jsonFactory, LoggerInterface $logger, - \Magento\Framework\View\Result\PageFactory $resultPageFactory - ) - { + StoreManagerInterface $storeManager, + Curl $curl + ) { $this->resultPageFactory = $resultPageFactory; + $this->jsonFactory = $jsonFactory; $this->logger = $logger; + $this->storeManager = $storeManager; + $this->curl = $curl; parent::__construct($context); } - /** - * Default customer account page - * - * @return void - */ + public function execute() { $this->logger->info('Page Controller is executed.'); + $trackingReference = $this->getRequest()->getParam('order_reference'); + + $this->logger->info('Tracking reference:', [$trackingReference]); + + $channel = $this->getStoreUrl(); + + $this->logger->info('Channel:', [$channel]); + +// if ($trackingReference) { +// $trackingUrl = sprintf(UData::TRACKING, $channel, $trackingReference); +// +// try { +// // Make the API call +// $this->curl->get($trackingUrl); +// $response = $this->curl->getBody(); +// +// // Optionally, you can decode the response if it's JSON +// $response = json_decode($response, true); +// +// // Handle the response (e.g., display it on the page) +// $resultJson = $this->jsonFactory->create(); +// return $resultJson->setData($response); +// +// } catch (\Exception $e) { +// $this->messageManager->addErrorMessage(__('Unable to track the order.')); +// } +// } + + if ($trackingReference) { + $trackingUrl = sprintf(UData::TRACKING, $channel, $trackingReference); + + try { + // Make the API call + $this->curl->get($trackingUrl); + $response = $this->curl->getBody(); + + // Decode the response + $this->logger->info('Response:', [$response]); + + $decodedResponse = json_decode($response, true); + $this->logger->info('Decoded Response:', [$decodedResponse]); + + if (is_array($decodedResponse) && isset($decodedResponse[0])) { + $shipmentData = $decodedResponse[0]; + + // Set response in the block + // In your controller action or block: + $layout = $this->_view->getLayout(); + $this->logger->info('Layout Handles: ' . implode(', ', $layout->getUpdate()->getHandles())); + + + $blocks = $layout->getAllBlocks(); + + $blockNames = []; + foreach ($blocks as $block) { + $blockNames[] = $block->getNameInLayout(); + } + + $this->logger->info('Available Blocks:', $blockNames); + + $block = $layout->getBlock('bobgo.tracking.index'); + + $this->logger->info('BobGo block:', [$block]); + + if ($block) { + $block->setResponse($shipmentData); + $this->logger->info('Block found and data set.'); + } else { + $this->logger->info('Block not found.'); + } + } else { + $this->logger->info('Unexpected response format.'); + } + + } catch (\Exception $e) { + $this->logger->info('Track error: ' . $e->getMessage()); + $this->messageManager->addErrorMessage(__('Unable to track the order.')); + } + } + return $this->resultPageFactory->create(); } + + + private function getStoreUrl(): string + { + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } } diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index 47b5d8b..d173baa 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -12,12 +12,12 @@ class UData * * @var string */ - public const TRACKING = 'https://api.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; + public const TRACKING = 'https://api.dev.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; /** * Rates API Endpoint * * @var string */ - public const RATES_ENDPOINT = 'https://api.bobgo.co.za/rates-at-checkout/magento'; + public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento'; } diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index bc1aaeb..3814d81 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -13,4 +13,9 @@ <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> </arguments> </type> + <type name="Magento\Framework\View\Layout"> + <arguments> + <argument name="debug" xsi:type="boolean">true</argument> + </arguments> + </type> </config> diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml index 54c3c21..be9a72f 100644 --- a/view/frontend/layout/customer_account.xml +++ b/view/frontend/layout/customer_account.xml @@ -6,7 +6,7 @@ <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-tracking"> <arguments> <argument name="path" xsi:type="string">bobgo/tracking/index</argument> - <argument name="label" xsi:type="string">Tracking</argument> + <argument name="label" xsi:type="string">Track my order</argument> </arguments> </block> </referenceBlock> diff --git a/view/frontend/templates/tracking/index.phtml b/view/frontend/templates/tracking/index.phtml index 8c81e6b..f6b8ef8 100644 --- a/view/frontend/templates/tracking/index.phtml +++ b/view/frontend/templates/tracking/index.phtml @@ -1,5 +1,76 @@ -<h2> - <?php echo $block->getHelloWorld(); ?> -</h2> +<style> + .track-order-container { + max-width: 600px; + margin: auto; + padding: 20px; + border: 1px solid #ddd; + background-color: #f9f9f9; + } -echo 'My First Link Page'; + .tracking-details h2 { + margin-top: 20px; + } + + .tracking-details p { + margin: 5px 0; + } + + footer { + margin-top: 20px; + font-size: 0.9em; + text-align: center; + } + + footer img { + display: block; + margin: 10px auto; + } +</style> + +<div class="track-order-container"> + <form action="<?php echo $this->getUrl('bobgo/tracking/index'); ?>" method="post"> + <div class="field"> + <label for="order_reference"><?php echo __('Order Number/Tracking Reference:'); ?></label> + <input type="text" id="order_reference" name="order_reference" required> + </div> + <br> + <div class="actions-toolbar"> + <button type="submit" class="action submit primary"><?php echo __('Track Order'); ?></button> + </div> + </form> + +<!-- --><?php //if ($response = $block->getResponse()): ?> +<!-- <div class="tracking-response">--> +<!-- <!-- Handle and display the API response -->--> +<!-- <h2>--><?php //echo __('Tracking Information'); ?><!--</h2>--> +<!-- <p>--><?php //echo __('Status: ' . $response['status']); ?><!--</p>--> +<!-- <!-- Add more fields as needed from the response -->--> +<!-- </div>--> +<!-- --><?php //endif; ?> + + <?php + +//<!-- --><?php +// $shipmentData = $block->getResponse(); +// if ($shipmentData && is_array($shipmentData)) { +// foreach ($shipmentData as $shipment) { +// echo '<div class="tracking-details">'; +// echo '<h2>' . __('Shipping Details') . '</h2>'; +// echo '<p>' . __('Shipment: ') . $shipment['shipment_tracking_reference'] . '</p>'; +// echo '<p>' . __('Order: ') . ltrim($shipment['order_number'], '0') . '</p>'; +// echo '<p>' . __('Courier: ') . $shipment['courier_name'] . '</p>'; +// +// echo '<h2>' . __('Tracking Details') . '</h2>'; +// echo '<p>' . __('Current Status: ') . $shipment['status_friendly'] . '</p>'; +// +// echo '<footer>'; +// echo '<p>' . __('For additional information, please contact BobGo (support@bobgo.co.za).') . '</p>'; +// echo '<img src="' . $shipment['courier_logo'] . '" alt="Courier Logo" style="width: 100px;"/>'; +// echo '</footer>'; +// echo '</div>'; +// } +// } else { +// echo '<p>No tracking data available.</p>'; +// } +// ?> +</div> diff --git a/view/frontend/web/images/logo.png b/view/frontend/web/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a1214967803c946558511b30e91ac9600543a707 GIT binary patch literal 59227 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 I>zopr053k>ZvX%Q literal 0 HcmV?d00001 -- GitLab From abe3e0ebbb8624d4078d9fd9bdb2f9a6c165627a Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 3 Sep 2024 08:37:48 +0200 Subject: [PATCH 14/56] tracking payload data being displayed --- Block/TrackingBlock.php | 23 +++--- Controller/Tracking/Index.php | 59 +++------------- view/frontend/templates/tracking/index.phtml | 74 +++++++++++--------- 3 files changed, 61 insertions(+), 95 deletions(-) diff --git a/Block/TrackingBlock.php b/Block/TrackingBlock.php index 69068a2..647cb3d 100644 --- a/Block/TrackingBlock.php +++ b/Block/TrackingBlock.php @@ -3,25 +3,24 @@ namespace BobGroup\BobGo\Block; use Magento\Framework\View\Element\Template; +use Magento\Framework\Registry; class TrackingBlock extends \Magento\Framework\View\Element\Template { - protected $response; + protected $registry; - protected function _toHtml() - { - $this->_logger->info('Block HTML rendered: ' . $this->getNameInLayout()); - return parent::_toHtml(); - } - - public function setResponse(array $response): self - { - $this->_response = $response; - return $this; + public function __construct( + Template\Context $context, + Registry $registry, + array $data = [] + ) { + $this->registry = $registry; + parent::__construct($context, $data); } public function getResponse() { - return $this->response; + return $this->registry->registry('shipment_data'); } } + diff --git a/Controller/Tracking/Index.php b/Controller/Tracking/Index.php index 7d073ba..0fc445b 100644 --- a/Controller/Tracking/Index.php +++ b/Controller/Tracking/Index.php @@ -1,13 +1,14 @@ <?php namespace BobGroup\BobGo\Controller\Tracking; -use Psr\Log\LoggerInterface; use Magento\Framework\App\Action\Context; use Magento\Framework\View\Result\PageFactory; -use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\Registry; +use Psr\Log\LoggerInterface; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; +use BobGroup\BobGo\Model\Carrier\UData; class Index extends \Magento\Framework\App\Action\Action { @@ -15,6 +16,7 @@ class Index extends \Magento\Framework\App\Action\Action protected $jsonFactory; protected $curl; protected $logger; + protected $registry; protected StoreManagerInterface $storeManager; public function __construct( @@ -23,13 +25,15 @@ class Index extends \Magento\Framework\App\Action\Action JsonFactory $jsonFactory, LoggerInterface $logger, StoreManagerInterface $storeManager, - Curl $curl + Curl $curl, + Registry $registry // Add Registry ) { $this->resultPageFactory = $resultPageFactory; $this->jsonFactory = $jsonFactory; $this->logger = $logger; $this->storeManager = $storeManager; $this->curl = $curl; + $this->registry = $registry; // Assign Registry parent::__construct($context); } @@ -44,35 +48,13 @@ class Index extends \Magento\Framework\App\Action\Action $this->logger->info('Channel:', [$channel]); -// if ($trackingReference) { -// $trackingUrl = sprintf(UData::TRACKING, $channel, $trackingReference); -// -// try { -// // Make the API call -// $this->curl->get($trackingUrl); -// $response = $this->curl->getBody(); -// -// // Optionally, you can decode the response if it's JSON -// $response = json_decode($response, true); -// -// // Handle the response (e.g., display it on the page) -// $resultJson = $this->jsonFactory->create(); -// return $resultJson->setData($response); -// -// } catch (\Exception $e) { -// $this->messageManager->addErrorMessage(__('Unable to track the order.')); -// } -// } - if ($trackingReference) { $trackingUrl = sprintf(UData::TRACKING, $channel, $trackingReference); try { - // Make the API call $this->curl->get($trackingUrl); $response = $this->curl->getBody(); - // Decode the response $this->logger->info('Response:', [$response]); $decodedResponse = json_decode($response, true); @@ -81,31 +63,10 @@ class Index extends \Magento\Framework\App\Action\Action if (is_array($decodedResponse) && isset($decodedResponse[0])) { $shipmentData = $decodedResponse[0]; - // Set response in the block - // In your controller action or block: - $layout = $this->_view->getLayout(); - $this->logger->info('Layout Handles: ' . implode(', ', $layout->getUpdate()->getHandles())); - - - $blocks = $layout->getAllBlocks(); - - $blockNames = []; - foreach ($blocks as $block) { - $blockNames[] = $block->getNameInLayout(); - } - - $this->logger->info('Available Blocks:', $blockNames); - - $block = $layout->getBlock('bobgo.tracking.index'); - - $this->logger->info('BobGo block:', [$block]); + // Save data to the registry + $this->registry->register('shipment_data', $shipmentData); + $this->logger->info('Shipment data registered in the registry.'); - if ($block) { - $block->setResponse($shipmentData); - $this->logger->info('Block found and data set.'); - } else { - $this->logger->info('Block not found.'); - } } else { $this->logger->info('Unexpected response format.'); } diff --git a/view/frontend/templates/tracking/index.phtml b/view/frontend/templates/tracking/index.phtml index f6b8ef8..f6e2b23 100644 --- a/view/frontend/templates/tracking/index.phtml +++ b/view/frontend/templates/tracking/index.phtml @@ -39,38 +39,44 @@ </div> </form> -<!-- --><?php //if ($response = $block->getResponse()): ?> -<!-- <div class="tracking-response">--> -<!-- <!-- Handle and display the API response -->--> -<!-- <h2>--><?php //echo __('Tracking Information'); ?><!--</h2>--> -<!-- <p>--><?php //echo __('Status: ' . $response['status']); ?><!--</p>--> -<!-- <!-- Add more fields as needed from the response -->--> -<!-- </div>--> -<!-- --><?php //endif; ?> - - <?php - -//<!-- --><?php -// $shipmentData = $block->getResponse(); -// if ($shipmentData && is_array($shipmentData)) { -// foreach ($shipmentData as $shipment) { -// echo '<div class="tracking-details">'; -// echo '<h2>' . __('Shipping Details') . '</h2>'; -// echo '<p>' . __('Shipment: ') . $shipment['shipment_tracking_reference'] . '</p>'; -// echo '<p>' . __('Order: ') . ltrim($shipment['order_number'], '0') . '</p>'; -// echo '<p>' . __('Courier: ') . $shipment['courier_name'] . '</p>'; -// -// echo '<h2>' . __('Tracking Details') . '</h2>'; -// echo '<p>' . __('Current Status: ') . $shipment['status_friendly'] . '</p>'; -// -// echo '<footer>'; -// echo '<p>' . __('For additional information, please contact BobGo (support@bobgo.co.za).') . '</p>'; -// echo '<img src="' . $shipment['courier_logo'] . '" alt="Courier Logo" style="width: 100px;"/>'; -// echo '</footer>'; -// echo '</div>'; -// } -// } else { -// echo '<p>No tracking data available.</p>'; -// } -// ?> + <?php + $shipmentData = $this->getResponse(); + + if ($shipmentData && is_array($shipmentData)) { + echo '<div class="tracking-details">'; + echo '<h2>' . __('Shipping Details') . '</h2>'; + + // Ensure each accessed array key exists and contains an array where expected + if (isset($shipmentData['shipment_tracking_reference'])) { + echo '<p>' . __('Shipment: ') . $shipmentData['shipment_tracking_reference'] . '</p>'; + } + + if (isset($shipmentData['order_number'])) { + echo '<p>' . __('Order: ') . ltrim($shipmentData['order_number'], '0') . '</p>'; + } + + if (isset($shipmentData['courier_name'])) { + echo '<p>' . __('Courier: ') . $shipmentData['courier_name'] . '</p>'; + } + + echo '<h2>' . __('Tracking Details') . '</h2>'; + + if (isset($shipmentData['status_friendly'])) { + echo '<p>' . __('Current Status: ') . $shipmentData['status_friendly'] . '</p>'; + } + + if (isset($shipmentData['courier_logo'])) { + echo '<footer>'; + echo '<p>' . __('For additional information, please contact BobGo (support@bobgo.co.za).') . '</p>'; + echo '<img src="' . $shipmentData['courier_logo'] . '" alt="Courier Logo" style="width: 100px;"/>'; + echo '</footer>'; + } + + echo '</div>'; + } else { + echo '<p>No tracking data available.</p>'; + } + ?> + + </div> -- GitLab From 62c32bf0fcec9de4fa66e7cdc0dcbf287baf55dc Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 3 Sep 2024 10:41:00 +0200 Subject: [PATCH 15/56] link the setting to the tracking page --- Block/TrackOrderLink.php | 39 +++++ Controller/Tracking/Index.php | 19 +++ view/frontend/layout/bobgo_tracking_index.xml | 2 +- view/frontend/layout/customer_account.xml | 22 +-- view/frontend/templates/tracking/index.phtml | 143 ++++++++++++------ view/frontend/web/images/logo.png | Bin 59227 -> 0 bytes 6 files changed, 164 insertions(+), 61 deletions(-) create mode 100644 Block/TrackOrderLink.php delete mode 100644 view/frontend/web/images/logo.png diff --git a/Block/TrackOrderLink.php b/Block/TrackOrderLink.php new file mode 100644 index 0000000..54c3c6f --- /dev/null +++ b/Block/TrackOrderLink.php @@ -0,0 +1,39 @@ +<?php + +namespace BobGroup\BobGo\Block; + +use Magento\Framework\View\Element\Html\Link\Current; +use Magento\Framework\App\Config\ScopeConfigInterface; + +class TrackOrderLink extends Current +{ + protected $scopeConfig; + + public function __construct( + \Magento\Framework\View\Element\Template\Context $context, + ScopeConfigInterface $scopeConfig, + \Magento\Framework\App\DefaultPathInterface $defaultPath, // Add this dependency + array $data = [] + ) { + $this->scopeConfig = $scopeConfig; + parent::__construct($context, $defaultPath, $data); // Pass it to the parent constructor + } + + protected function _toHtml() + { + // Check if the Track My Order feature is enabled + $isEnabled = $this->scopeConfig->isSetFlag( + 'carriers/bobgo/enable_track_order', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if (!$isEnabled) { + return ''; // Return an empty string if the feature is disabled + } + + return parent::_toHtml(); // Use the parent class's rendering method + } +} + + + diff --git a/Controller/Tracking/Index.php b/Controller/Tracking/Index.php index 0fc445b..c3a01ee 100644 --- a/Controller/Tracking/Index.php +++ b/Controller/Tracking/Index.php @@ -5,6 +5,8 @@ use Magento\Framework\App\Action\Context; use Magento\Framework\View\Result\PageFactory; use Magento\Framework\Registry; use Psr\Log\LoggerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Controller\Result\RedirectFactory; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; @@ -16,6 +18,8 @@ class Index extends \Magento\Framework\App\Action\Action protected $jsonFactory; protected $curl; protected $logger; + protected $scopeConfig; + protected $redirectFactory; protected $registry; protected StoreManagerInterface $storeManager; @@ -24,6 +28,8 @@ class Index extends \Magento\Framework\App\Action\Action PageFactory $resultPageFactory, JsonFactory $jsonFactory, LoggerInterface $logger, + ScopeConfigInterface $scopeConfig, + RedirectFactory $redirectFactory, StoreManagerInterface $storeManager, Curl $curl, Registry $registry // Add Registry @@ -31,6 +37,8 @@ class Index extends \Magento\Framework\App\Action\Action $this->resultPageFactory = $resultPageFactory; $this->jsonFactory = $jsonFactory; $this->logger = $logger; + $this->scopeConfig = $scopeConfig; + $this->redirectFactory = $redirectFactory; $this->storeManager = $storeManager; $this->curl = $curl; $this->registry = $registry; // Assign Registry @@ -39,6 +47,17 @@ class Index extends \Magento\Framework\App\Action\Action public function execute() { + // Check if the "Track My Order" feature is enabled + $isEnabled = $this->scopeConfig->isSetFlag( + 'carriers/bobgo/enable_track_order', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + if (!$isEnabled) { + // If the feature is disabled, redirect to home page or show a 404 error + return $this->redirectFactory->create()->setPath('noroute'); + } + $this->logger->info('Page Controller is executed.'); $trackingReference = $this->getRequest()->getParam('order_reference'); diff --git a/view/frontend/layout/bobgo_tracking_index.xml b/view/frontend/layout/bobgo_tracking_index.xml index 6e842d7..e36faa5 100644 --- a/view/frontend/layout/bobgo_tracking_index.xml +++ b/view/frontend/layout/bobgo_tracking_index.xml @@ -3,7 +3,7 @@ <update handle="customer_account"/> <head> <title> - Tracking + Track my order </title> </head> <body> diff --git a/view/frontend/layout/customer_account.xml b/view/frontend/layout/customer_account.xml index be9a72f..701335d 100644 --- a/view/frontend/layout/customer_account.xml +++ b/view/frontend/layout/customer_account.xml @@ -1,14 +1,14 @@ <?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> -<body> - <referenceBlock name="customer_account_navigation"> - <!-- Add menu to the end of the sidebar --> - <block class="Magento\Framework\View\Element\Html\Link\Current" name="customer-account-navigation-tracking"> - <arguments> - <argument name="path" xsi:type="string">bobgo/tracking/index</argument> - <argument name="label" xsi:type="string">Track my order</argument> - </arguments> - </block> - </referenceBlock> -</body> + <body> + <referenceBlock name="customer_account_navigation"> + <!-- Add conditional menu to the end of the sidebar --> + <block class="BobGroup\BobGo\Block\TrackOrderLink" name="customer-account-navigation-tracking"> + <arguments> + <argument name="path" xsi:type="string">bobgo/tracking/index</argument> + <argument name="label" xsi:type="string">Track my order</argument> + </arguments> + </block> + </referenceBlock> + </body> </page> diff --git a/view/frontend/templates/tracking/index.phtml b/view/frontend/templates/tracking/index.phtml index f6e2b23..b9f8ba0 100644 --- a/view/frontend/templates/tracking/index.phtml +++ b/view/frontend/templates/tracking/index.phtml @@ -1,3 +1,7 @@ +<?php +$shipmentData = $this->getResponse(); +?> + <style> .track-order-container { max-width: 600px; @@ -28,55 +32,96 @@ </style> <div class="track-order-container"> - <form action="<?php echo $this->getUrl('bobgo/tracking/index'); ?>" method="post"> - <div class="field"> - <label for="order_reference"><?php echo __('Order Number/Tracking Reference:'); ?></label> - <input type="text" id="order_reference" name="order_reference" required> - </div> - <br> - <div class="actions-toolbar"> - <button type="submit" class="action submit primary"><?php echo __('Track Order'); ?></button> + <?php if (empty($shipmentData)) : ?> + <form action="<?php echo $this->getUrl('bobgo/tracking/index'); ?>" method="post" class="track-order-form"> + <div class="field"> + <label for="order_reference"><?php echo __('Order Number/Tracking Reference:'); ?></label> + <input type="text" id="order_reference" name="order_reference" required> + </div> + <br> + <div class="actions-toolbar"> + <button type="submit" class="action submit primary"><?php echo __('Track Order'); ?></button> + </div> + </form> + <?php endif; ?> + + <?php if ($shipmentData && is_array($shipmentData)) : ?> + <div class="tracking-details"> + <h2><?php echo __('Shipping Details'); ?></h2> + + <?php if (isset($shipmentData['shipment_tracking_reference'])) : ?> + <p><strong><?php echo __('Shipment:'); ?></strong> <?php echo $shipmentData['shipment_tracking_reference']; ?></p> + <?php endif; ?> + + <?php if (isset($shipmentData['order_number'])) : ?> + <p><strong><?php echo __('Order:'); ?></strong> <?php echo ltrim($shipmentData['order_number'], '0'); ?></p> + <?php endif; ?> + + <?php if (isset($shipmentData['courier_name'])) : ?> + <p><strong><?php echo __('Courier:'); ?></strong> <?php echo $shipmentData['courier_name']; ?></p> + <?php endif; ?> + + <br> + <h2><?php echo __('Tracking Details'); ?></h2> + + <?php if (isset($shipmentData['status']) && isset($shipmentData['status_friendly'])) : ?> + <?php if ($shipmentData['status'] == 'pending-collection') : ?> + <p><?php echo 'Your shipment will be collected soon. Please check back later for more information.' ?></p> + <p><strong><?php echo __('Current Status:'); ?></strong> <?php echo $shipmentData['status_friendly']; ?></p> + <?php elseif (in_array($shipmentData['status'], ['cancelled-by-courier', 'cancelled'])) : ?> + <p><?php echo 'The shipment has been cancelled.'; ?></p> + <p><strong><?php echo __('Current Status:'); ?></strong> <?php echo $shipmentData['status_friendly']; ?></p> + <?php else : ?> + <?php if (empty($shipmentData['checkpoints'])) : ?> + <p><?php echo 'Tracking information is not yet available. Please check back later for more information.'; ?></p> + <?php else : ?> + <p><?php echo 'Tracking information is available below:'; ?></p> + <table id="table_checkpoints"> + <thead> + <tr> + <th><?php echo __('Date and Time'); ?></th> + <th><?php echo __('Status'); ?></th> + <th><?php echo __('Message'); ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($shipmentData['checkpoints'] as $checkpoint) : ?> + <tr> + <td> + <?php + $timeDate = new DateTime($checkpoint['time']); + echo $timeDate->format('Y M d, H:i'); + ?> + </td> + <td><strong><?php echo $checkpoint['status_friendly']; ?></strong></td> + <td><?php echo $checkpoint['message']; ?></td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> </div> - </form> - - <?php - $shipmentData = $this->getResponse(); - - if ($shipmentData && is_array($shipmentData)) { - echo '<div class="tracking-details">'; - echo '<h2>' . __('Shipping Details') . '</h2>'; - - // Ensure each accessed array key exists and contains an array where expected - if (isset($shipmentData['shipment_tracking_reference'])) { - echo '<p>' . __('Shipment: ') . $shipmentData['shipment_tracking_reference'] . '</p>'; - } - - if (isset($shipmentData['order_number'])) { - echo '<p>' . __('Order: ') . ltrim($shipmentData['order_number'], '0') . '</p>'; - } - - if (isset($shipmentData['courier_name'])) { - echo '<p>' . __('Courier: ') . $shipmentData['courier_name'] . '</p>'; - } - - echo '<h2>' . __('Tracking Details') . '</h2>'; - - if (isset($shipmentData['status_friendly'])) { - echo '<p>' . __('Current Status: ') . $shipmentData['status_friendly'] . '</p>'; - } - - if (isset($shipmentData['courier_logo'])) { - echo '<footer>'; - echo '<p>' . __('For additional information, please contact BobGo (support@bobgo.co.za).') . '</p>'; - echo '<img src="' . $shipmentData['courier_logo'] . '" alt="Courier Logo" style="width: 100px;"/>'; - echo '</footer>'; - } - - echo '</div>'; - } else { - echo '<p>No tracking data available.</p>'; - } - ?> - + <br> + <footer> + <p> + <?php + echo sprintf( + __('For additional information, please contact %s (%s) and quote tracking reference %s.'), + $shipmentData['courier_name'], + $shipmentData['courier_phone'], + $shipmentData['id'] + ); + ?> + </p> + <img id="show_branding" class="image" + src="https://img.bob.co.za/bobgo/image/upload/bobgo-logos/bobgo_logo_smart_shipping_black.png?tr=w-400&ts=20231024" + alt="Bob Go logo" style="width: 200px;"> + </footer> + <?php else : ?> + <br> + <p><?php echo __('No tracking data available.'); ?></p> + <?php endif; ?> </div> diff --git a/view/frontend/web/images/logo.png b/view/frontend/web/images/logo.png deleted file mode 100644 index a1214967803c946558511b30e91ac9600543a707..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59227 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 I>zopr053k>ZvX%Q -- GitLab From 436e55f7ca00c39368da000e4f637d1796e349b2 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 3 Sep 2024 11:16:38 +0200 Subject: [PATCH 16/56] cleanup --- Block/TrackOrderLink.php | 10 ++++++---- Controller/Tracking/Index.php | 25 +++++-------------------- etc/frontend/di.xml | 5 ----- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/Block/TrackOrderLink.php b/Block/TrackOrderLink.php index 54c3c6f..ada8c22 100644 --- a/Block/TrackOrderLink.php +++ b/Block/TrackOrderLink.php @@ -12,11 +12,11 @@ class TrackOrderLink extends Current public function __construct( \Magento\Framework\View\Element\Template\Context $context, ScopeConfigInterface $scopeConfig, - \Magento\Framework\App\DefaultPathInterface $defaultPath, // Add this dependency + \Magento\Framework\App\DefaultPathInterface $defaultPath, array $data = [] ) { $this->scopeConfig = $scopeConfig; - parent::__construct($context, $defaultPath, $data); // Pass it to the parent constructor + parent::__construct($context, $defaultPath, $data); } protected function _toHtml() @@ -27,11 +27,13 @@ class TrackOrderLink extends Current \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); + // Return an empty string if the feature is disabled if (!$isEnabled) { - return ''; // Return an empty string if the feature is disabled + return ''; } - return parent::_toHtml(); // Use the parent class's rendering method + // Use the parent class's rendering method + return parent::_toHtml(); } } diff --git a/Controller/Tracking/Index.php b/Controller/Tracking/Index.php index c3a01ee..78e527f 100644 --- a/Controller/Tracking/Index.php +++ b/Controller/Tracking/Index.php @@ -32,7 +32,7 @@ class Index extends \Magento\Framework\App\Action\Action RedirectFactory $redirectFactory, StoreManagerInterface $storeManager, Curl $curl, - Registry $registry // Add Registry + Registry $registry ) { $this->resultPageFactory = $resultPageFactory; $this->jsonFactory = $jsonFactory; @@ -41,12 +41,13 @@ class Index extends \Magento\Framework\App\Action\Action $this->redirectFactory = $redirectFactory; $this->storeManager = $storeManager; $this->curl = $curl; - $this->registry = $registry; // Assign Registry + $this->registry = $registry; parent::__construct($context); } public function execute() { + // This is only an extra check after the TrackOrderLink block // Check if the "Track My Order" feature is enabled $isEnabled = $this->scopeConfig->isSetFlag( 'carriers/bobgo/enable_track_order', @@ -58,48 +59,32 @@ class Index extends \Magento\Framework\App\Action\Action return $this->redirectFactory->create()->setPath('noroute'); } - $this->logger->info('Page Controller is executed.'); $trackingReference = $this->getRequest()->getParam('order_reference'); - $this->logger->info('Tracking reference:', [$trackingReference]); - $channel = $this->getStoreUrl(); - $this->logger->info('Channel:', [$channel]); - if ($trackingReference) { $trackingUrl = sprintf(UData::TRACKING, $channel, $trackingReference); - - try { $this->curl->get($trackingUrl); $response = $this->curl->getBody(); - $this->logger->info('Response:', [$response]); - $decodedResponse = json_decode($response, true); - $this->logger->info('Decoded Response:', [$decodedResponse]); if (is_array($decodedResponse) && isset($decodedResponse[0])) { $shipmentData = $decodedResponse[0]; // Save data to the registry $this->registry->register('shipment_data', $shipmentData); - $this->logger->info('Shipment data registered in the registry.'); } else { - $this->logger->info('Unexpected response format.'); + // Return early the response is not valid + return $this->resultPageFactory->create(); } - - } catch (\Exception $e) { - $this->logger->info('Track error: ' . $e->getMessage()); - $this->messageManager->addErrorMessage(__('Unable to track the order.')); - } } return $this->resultPageFactory->create(); } - private function getStoreUrl(): string { $url = $this->storeManager->getStore()->getBaseUrl(); diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 3814d81..bc1aaeb 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -13,9 +13,4 @@ <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> </arguments> </type> - <type name="Magento\Framework\View\Layout"> - <arguments> - <argument name="debug" xsi:type="boolean">true</argument> - </arguments> - </type> </config> -- GitLab From 17cd8bd9f2b8197c1088d70feef2f3ab6e264b66 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 3 Sep 2024 12:57:26 +0200 Subject: [PATCH 17/56] remove old tracking classes and functions --- Model/Carrier/BobGo.php | 269 ------------------ .../DataProviders/Tracking/ChangeTitle.php | 31 -- Plugin/Block/Tracking/PopUpDeliveryDate.php | 53 ---- .../Tracking/ChangeTitleTest.php | 62 ---- .../Block/Tracking/PopUpDeliveryDateTest.php | 92 ------ 5 files changed, 507 deletions(-) delete mode 100644 Plugin/Block/DataProviders/Tracking/ChangeTitle.php delete mode 100644 Plugin/Block/Tracking/PopUpDeliveryDate.php delete mode 100644 Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php delete mode 100644 Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 386e4cb..2809b13 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -527,141 +527,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } - /** - * Get tracking - * - * @param string|string[] $trackings - * @return \Magento\Shipping\Model\Tracking\Result|null - */ - public function getTracking($trackings) - { - $this->setTrackingRequest(); // Ensure this method is correctly defined - - if (!is_array($trackings)) { - $trackings = [$trackings]; - } - - foreach ($trackings as $tracking) { - $this->_getXMLTracking([$tracking]); // Ensure _getXMLTracking processes tracking correctly - } - - return $this->_result; // Ensure _result is a \Magento\Shipping\Model\Tracking\Result - } - - /** - * Set tracking request - * - * @return void - */ - protected function setTrackingRequest() - { - $r = new \Magento\Framework\DataObject(); - - $account = $this->getConfigData('account'); - $r->setData('account', $account); // Using setData with the key 'account' - - $this->_rawTrackingRequest = $r; - } - - /** - * Get tracking request - * - * @return \Magento\Framework\DataObject|null - */ - protected function getTrackingRequest(): ?\Magento\Framework\DataObject - { - return $this->_rawTrackingRequest; - } - - /** - * Send request for tracking - * - * @param string[] $tracking - * @return void - */ - protected function _getXMLTracking($tracking) - { - $this->_parseTrackingResponse($tracking); - } - - /** - * Parse tracking response - * - * @param string|array<int,string> $trackingValue - * @return void - */ - protected function _parseTrackingResponse($trackingValue) - { - $result = $this->getResult(); - $carrierTitle = $this->getConfigData('title'); - $counter = 0; - - if (!is_array($trackingValue)) { - $trackingValue = [$trackingValue]; - } - - foreach ($trackingValue as $trackingReference) { - $tracking = $this->_trackStatusFactory->create(); - - $tracking->setCarrier(self::CODE); - $tracking->setCarrierTitle($carrierTitle); - - // Adjust as needed based on the environment - $tracking->setUrl(UData::TRACKING . $trackingReference); - $tracking->setTracking($trackingReference); - $tracking->addData($this->processTrackingDetails($trackingReference)); - - if ($result) { - $result->append($tracking); - $counter++; - } - } - - // Tracking Details Not Available - if ($counter === 0) { - $this->appendTrackingError( - $trackingValue[0] ?? '', - (string)__('For some reason we can\'t retrieve tracking info right now.') - ); - } - } - - /** - * Get tracking response - * - * @return string - */ - public function getResponse(): string - { - $statuses = ''; - - // If $_result is of type \Magento\Shipping\Model\Tracking\Result, handle it - if ($this->_result instanceof \Magento\Shipping\Model\Tracking\Result) { - if ($trackings = $this->_result->getAllTrackings()) { - foreach ($trackings as $tracking) { - if ($data = $tracking->getAllData()) { - if (!empty($data['status'])) { - $statuses .= __($data['status']) . "\n<br/>"; - } else { - $statuses .= __('Empty response') . "\n<br/>"; - } - } - } - } - } - -// // Handle \Magento\Shipping\Model\Rate\Result if needed -// if ($this->_result instanceof \Magento\Shipping\Model\Rate\Result) { -// // Implement the logic for Rate\Result if applicable -// } - - if (trim($statuses) === '') { - $statuses = (string)__('Empty response'); - } - - return $statuses; - } - /** * Get allowed shipping methods * @@ -773,55 +638,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $data; } - /** - * Parse track details response from Bob Go. - * - * @param string $trackInfo - * @return array<string, array<int, array<string, string>>> - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function processTrackingDetails(string $trackInfo): array - { - $result = [ - 'shippeddate' => [], // Initializing as an array of arrays - 'deliverydate' => [], // Initializing as an array of arrays - 'deliverytime' => [], // Initializing as an array of arrays - 'deliverylocation' => [], // Initializing as an array of arrays - 'weight' => [], // Initializing as an array of arrays - 'progressdetail' => [], // This will be populated with an array of arrays - ]; - - $result = $this->_requestTracking($trackInfo, $result); - - return $result; - } - - /** - * Append error message to rate result instance. - * - * @param string $trackingValue - * @param string $errorMessage - * @return void - */ - private function appendTrackingError(string $trackingValue, string $errorMessage): void - { - $error = $this->_trackErrorFactory->create(); - $error->setCarrier(self::CODE); - $error->setCarrierTitle($this->getConfigData('title')); - $error->setTracking($trackingValue); - $error->setErrorMessage($errorMessage); - - $result = $this->getResult(); - - if ($result !== null) { - $result->append($error); - } else { - // Handle the case where $result is null, such as logging an error - $this->_logger->error('Failed to append tracking error: Result object is null.'); - } - } - /** * Format a date to 'd M Y'. * @@ -883,26 +699,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } - /** - * Perform API Request for Shipment Tracking to Bob Go API and return response. - * - * @param string $trackInfo The tracking information or tracking ID. - * @param array<string,array<int,array<string,string>>> $result The result array to be - * populated with tracking details. - * @return array<string, array<int, array<string, string>>> The updated result array with tracking details. - */ - private function _requestTracking(string $trackInfo, array $result): array - { - $response = $this->trackBobGoShipment($trackInfo); - - // Validate that the response is an array and contains at least one element - if (is_array($response) && isset($response[0]) && is_array($response[0])) { - $result = $this->prepareActivity($response[0], $result); - } - - return $result; - } - /** * Format rates from Bob Go API response and append to rate result instance of carrier. * @@ -976,35 +772,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } } - /** - * Prepare received checkpoints and activity from Bob Go Shipment Tracking API. - * - * @param array<string,mixed> $response The API response containing tracking checkpoints. - * @param array<string,array<int,array<string,string>>> $result The result array to be - * populated with activity details. - * @return array<string, array<int, array<string, string>>> The updated result array with activity details. - */ - private function prepareActivity(array $response, array $result): array - { - if (isset($response['checkpoints']) && is_array($response['checkpoints'])) { - foreach ($response['checkpoints'] as $checkpoint) { - if (is_array($checkpoint) && - isset($checkpoint['status'], $checkpoint['time']) && - is_string($checkpoint['status']) && - is_string($checkpoint['time']) - ) { - $result['progressdetail'][] = [ - 'activity' => $checkpoint['status'], - 'deliverydate' => $this->formatDate($checkpoint['time']), - 'deliverytime' => $this->formatTime($checkpoint['time']), - ]; - } - } - } - - return $result; - } - /** * Get Working Days between time of checkout and delivery date (min and max). * @@ -1037,21 +804,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $no_days - $weekends; } - /** - * Curl request to Bob Go Shipment Tracking API. - * - * @param string $trackInfo The tracking information or tracking ID. - * @return mixed The decoded API response. - */ - private function trackBobGoShipment(string $trackInfo): mixed - { - $this->curl->get(UData::TRACKING . $trackInfo); - - $response = $this->curl->getBody(); - - return json_decode($response, true); - } - /** * Build the payload for Bob Go API request and return the response. * @@ -1199,27 +951,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return $itemsArray; } - /** - * Checks if the required data fields are present in the request. - * - * @param \Magento\Framework\DataObject $request The data object containing the request information. - * @return bool True if all required fields are present, otherwise false. - */ - public function hasRequiredData(\Magento\Framework\DataObject $request): bool - { - $requiredFields = [ - 'dest_country_id', - 'dest_region_id', - ]; - - foreach ($requiredFields as $field) { - if (!$request->getData($field)) { - return false; - } - } - return true; - } - /** * Trigger a test for rates. * diff --git a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/Plugin/Block/DataProviders/Tracking/ChangeTitle.php deleted file mode 100644 index a035d23..0000000 --- a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Plugin\Block\DataProviders\Tracking; - -use BobGroup\BobGo\Model\Carrier; -use Magento\Shipping\Model\Tracking\Result\Status; -use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; - -/** - * Plugin to change delivery date title with bobgo customized value - */ - -class ChangeTitle -{ - /** - * Title modification in case if bobgo used as carrier - * - * @param Subject $subject - * @param \Magento\Framework\Phrase|string $result - * @param Status $trackingStatus - * @return \Magento\Framework\Phrase|string - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterGetTitle(Subject $subject, $result, Status $trackingStatus) - { - if ($trackingStatus->getCarrier() === \BobGroup\BobGo\Model\Carrier\BobGo::CODE) { - $result = __('Expected delivery:'); - } - return $result; - } -} diff --git a/Plugin/Block/Tracking/PopUpDeliveryDate.php b/Plugin/Block/Tracking/PopUpDeliveryDate.php deleted file mode 100644 index bb37528..0000000 --- a/Plugin/Block/Tracking/PopUpDeliveryDate.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Plugin\Block\Tracking; - -use Magento\Shipping\Block\Tracking\Popup; -use Magento\Shipping\Model\Tracking\Result\Status; - -/* - * Plugin to update delivery date value in case if Bob Go is a carrier used - */ -class PopupDeliveryDate -{ - /** - * Bob Go carrier code - */ - private const BOB_GO_CARRIER_CODE = 'bobgo_carrier_code'; // Replace with your actual carrier code - - /** - * Show only date for expected delivery in case if Bob Go is a carrier - * - * @param Popup $subject - * @param string $result - * @param string $date - * @param string $time - * @return string - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time) - { - if ($this->getCarrier($subject) === self::BOB_GO_CARRIER_CODE) { - $result = $subject->formatDeliveryDate($date); - } - return $result; - } - - /** - * Retrieve carrier name from tracking info - * - * @param Popup $subject - * @return string - */ - private function getCarrier(Popup $subject): string - { - foreach ($subject->getTrackingInfo() as $trackingData) { - foreach ($trackingData as $trackingInfo) { - if ($trackingInfo instanceof Status) { - return $trackingInfo->getCarrier() ?? ''; - } - } - } - return ''; - } -} diff --git a/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php b/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php deleted file mode 100644 index 4d8bd9d..0000000 --- a/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Test\Unit\Plugin\Block\DataProviders\Tracking; - -use BobGroup\BobGo\Plugin\Block\DataProviders\Tracking\ChangeTitle; -use BobGroup\BobGo\Model\Carrier\BobGo; -use Magento\Shipping\Model\Tracking\Result\Status; -use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; -use PHPUnit\Framework\TestCase; - -class ChangeTitleTest extends TestCase -{ - /** - * @var ChangeTitle - */ - private $plugin; - - protected function setUp(): void - { - // Instantiate the ChangeTitle plugin - $this->plugin = new ChangeTitle(); - } - - public function testAfterGetTitleWithBobGoCarrier(): void - { - // Create a custom Status object with BobGo carrier - $status = $this->getMockBuilder(Status::class) - ->setMethods(['getCarrier']) - ->getMock(); - - $status->method('getCarrier')->willReturn(BobGo::CODE); - - // Mock the Subject class - $subjectMock = $this->createMock(Subject::class); - - // Call the plugin method afterGetTitle - $result = $this->plugin->afterGetTitle($subjectMock, 'Original Title', $status); - - // Assert that the title was changed for BobGo carrier - $this->assertEquals('Expected delivery:', $result); - } - - public function testAfterGetTitleWithOtherCarrier(): void - { - // Create a custom Status object with a different carrier - $status = $this->getMockBuilder(Status::class) - ->setMethods(['getCarrier']) - ->getMock(); - - $status->method('getCarrier')->willReturn('other_carrier_code'); - - // Mock the Subject class - $subjectMock = $this->createMock(Subject::class); - - // Call the plugin method afterGetTitle - $originalTitle = 'Original Title'; - $result = $this->plugin->afterGetTitle($subjectMock, $originalTitle, $status); - - // Assert that the title was not changed for other carriers - $this->assertEquals($originalTitle, $result); - } -} diff --git a/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php b/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php deleted file mode 100644 index 6c01770..0000000 --- a/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Test\Unit\Plugin\Block\Tracking; - -use BobGroup\BobGo\Plugin\Block\Tracking\PopupDeliveryDate; -use Magento\Shipping\Block\Tracking\Popup; -use Magento\Shipping\Model\Tracking\Result\Status; -use PHPUnit\Framework\TestCase; - -class PopupDeliveryDateTest extends TestCase -{ - /** - * @var PopupDeliveryDate - */ - private $plugin; - - protected function setUp(): void - { - // Instantiate the PopupDeliveryDate plugin - $this->plugin = new PopupDeliveryDate(); - } - - public function testAfterFormatDeliveryDateTimeWithBobGoCarrier(): void - { - // Create an instance of the Status class - $status = new Status(); - $status->setCarrier('bobgo_carrier_code'); - - // Mock the Popup class - $popupMock = $this->createMock(Popup::class); - $popupMock->method('getTrackingInfo')->willReturn([ - ['tracking_info' => $status], - ]); - - // Mock the formatDeliveryDate method - $popupMock->method('formatDeliveryDate') - ->with('2024-08-19') - ->willReturn('Aug 19, 2024'); - - // Call the plugin method afterFormatDeliveryDateTime - $result = $this->plugin->afterFormatDeliveryDateTime( - $popupMock, - 'Aug 19, 2024 10:00 AM', - '2024-08-19', - '10:00 AM' - ); - - // Assert that the time was stripped for BobGo carrier - $this->assertEquals('Aug 19, 2024', $result); - } - - public function testAfterFormatDeliveryDateTimeWithOtherCarrier(): void - { - // Create an instance of the Status class - $status = new Status(); - $status->setCarrier('other_carrier_code'); - - // Mock the Popup class - $popupMock = $this->createMock(Popup::class); - $popupMock->method('getTrackingInfo')->willReturn([ - ['tracking_info' => $status], - ]); - - // Call the plugin method afterFormatDeliveryDateTime - $result = $this->plugin->afterFormatDeliveryDateTime( - $popupMock, - 'Aug 19, 2024 10:00 AM', - '2024-08-19', - '10:00 AM' - ); - - // Assert that the time remains unchanged for other carriers - $this->assertEquals('Aug 19, 2024 10:00 AM', $result); - } - - public function testGetCarrierWithNoTrackingInfo(): void - { - // Mock the Popup class with no tracking info - $popupMock = $this->createMock(Popup::class); - $popupMock->method('getTrackingInfo')->willReturn([]); - - // Call the getCarrier method directly - $reflection = new \ReflectionClass($this->plugin); - $method = $reflection->getMethod('getCarrier'); - $method->setAccessible(true); - - $result = $method->invokeArgs($this->plugin, [$popupMock]); - - // Assert that an empty string is returned when no tracking info is available - $this->assertEquals('', $result); - } -} -- GitLab From 396e93a12670b55ea5174f2b598e03cb809cae4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 25 Sep 2024 13:07:50 +0200 Subject: [PATCH 18/56] Update the logo --- view/frontend/templates/tracking/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/frontend/templates/tracking/index.phtml b/view/frontend/templates/tracking/index.phtml index b9f8ba0..5895f01 100644 --- a/view/frontend/templates/tracking/index.phtml +++ b/view/frontend/templates/tracking/index.phtml @@ -117,7 +117,7 @@ $shipmentData = $this->getResponse(); ?> </p> <img id="show_branding" class="image" - src="https://img.bob.co.za/bobgo/image/upload/bobgo-logos/bobgo_logo_smart_shipping_black.png?tr=w-400&ts=20231024" + src="https://ik.imagekit.io/z1viz85yxs/prod-v3/bobgo/corporate-identity/bobgo_logo_smart_shipping.png?tr=w-400" alt="Bob Go logo" style="width: 200px;"> </footer> <?php else : ?> -- GitLab From dbcb4eb74924ac5493699a08003d149a6eb14722 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 2 Oct 2024 15:44:00 +0200 Subject: [PATCH 19/56] triggerWebhookTest --- Model/Carrier/BobGo.php | 87 +++++++++++++++++++++++++++++++ Model/Carrier/UData.php | 2 +- Observer/ConfigChangeObserver.php | 20 +++++++ Observer/OrderCreateWebhook.php | 1 + Observer/OrderWebhookBase.php | 24 +++++++-- etc/adminhtml/system.xml | 16 ++++++ 6 files changed, 145 insertions(+), 5 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 386e4cb..51cf36b 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -864,6 +864,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return UData::RATES_ENDPOINT; } + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + /** * Perform API Request to Bob Go API and return response. * @@ -1310,4 +1315,86 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } return false; } + + public function isWebhookEnabled(): bool + { + $enabled = $this->scopeConfig->getValue( + 'carriers/bobgo/enable_webhooks', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + // Cast the value to a boolean + return filter_var($enabled, FILTER_VALIDATE_BOOLEAN); + } + + + public function triggerWebhookTest(): bool + { + $webhookKey = $this->scopeConfig->getValue( + 'carriers/bobgo/webhook_key', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + $isEnabled = $this->scopeConfig->getValue( + 'carriers/bobgo/enable_webhooks', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + // Convert the string to a boolean value + $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); + +// if (!$webhookKey) { +// $this->_logger->error('Webhook key not configured.'); +// return false; +// } + + $storeId = strval($this->_storeManager->getStore()->getId()); + + $payload = [ + 'event' => 'webhook_validation', + 'channel_identifier' => $this->getBaseUrl(), + 'store_id' => $storeId, + 'webhooks_enabled' => $isEnabled, + ]; + + try { + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); + + $payloadJson = json_encode($payload); + $this->_logger->info('Webhooks payload: ' . $payloadJson); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($this->getWebhookUrl(), $payloadJson); + $statusCode = $this->curl->getStatus(); + $this->_logger->info('Webhooks statuscode: ' . $statusCode); + $responseBody = $this->curl->getBody(); + $this->_logger->info('Webhooks response: ' . $responseBody); + + $response = json_decode($responseBody, true); + + if ($statusCode == 200 && isset($response['success']) && $response['success'] === true) { + $this->_logger->info('Webhook validation successful.'); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return true; + } else { + $this->_logger->error('Webhook validation failed: ' . ($response['message'] ?? 'Unknown error')); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return false; + } + } catch (\Exception $e) { + $this->_logger->error('Webhook validation exception: ' . $e->getMessage()); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return false; + } + } + } diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index adf83a2..a7e5d2e 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; } diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 1b6c072..528dd82 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -52,6 +52,7 @@ class ConfigChangeObserver implements ObserverInterface { $changedPaths = $observer->getEvent()->getData('changed_paths'); + // Test for rates at checkout if (is_array($changedPaths) && in_array('carriers/bobgo/active', $changedPaths)) { if ($this->bobGo->isActive()) { $result = $this->bobGo->triggerRatesTest(); @@ -70,5 +71,24 @@ class ConfigChangeObserver implements ObserverInterface } } } + + // Test for webhooks + if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { +// if ($this->bobGo->isWebhookEnabled()) { + $this->logger->info('Webhooks test start: '); + $result = $this->bobGo->triggerWebhookTest(); + $this->logger->info('Webhooks test end: ' . $result); + + if ($result) { + $this->messageManager->addSuccessMessage( + __('Webhook validation successful.') + ); + } else { + $this->messageManager->addErrorMessage( + __('Webhook validation failed. Please check the webhook key and try again.') + ); + } +// } + } } } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 74f1ce4..f605801 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -15,5 +15,6 @@ class OrderCreateWebhook extends OrderWebhookBase // Extract order data and send to the webhook URL $this->sendWebhook($order); + $this->logger->info('Webhooks sent'); } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index eeb106e..db27d61 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -3,6 +3,7 @@ namespace BobGroup\BobGo\Observer; use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; @@ -13,18 +14,29 @@ abstract class OrderWebhookBase implements ObserverInterface protected Curl $curl; protected LoggerInterface $logger; protected StoreManagerInterface $storeManager; + protected ScopeConfigInterface $scopeConfig; - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig) { $this->logger = $logger; $this->curl = $curl; $this->storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; } protected function sendWebhook($order) { + $enabled = $this->scopeConfig->getValue('carriers/bobgo/enable_webhooks', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + + // Return early if webhooks is disabled + if (!$enabled) { + $this->logger->info('Webhooks are disabled. Exiting webhook process for order: ' . $order->getIncrementId()); + return; + } + // Webhook URL $url = $this->getWebhookUrl(); + $this->logger->info('Webhooks url: ' . $url); $storeId = $this->getStoreId(); @@ -54,14 +66,17 @@ abstract class OrderWebhookBase implements ObserverInterface // Send the webhook $this->makeHttpPostRequest($url, $data, $storeId); + $this->logger->info('Webhooks sent'); } private function makeHttpPostRequest($url, $data, $storeId) { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the signature using the webhook key saved in config + $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + $this->logger->info('Webhooks - key: ' . $webhookKey); +// $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); // Encode the binary data in Base64 $signature = base64_encode($rawSignature); @@ -79,6 +94,7 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); + $this->logger->info('Webhooks payload: ' . $payloadJson); } private function getWebhookUrl(): string diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 410c2c2..3f4b31f 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -28,6 +28,22 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Displays the delivery timeframe and additional service level description, as configured on Bob Go.</comment> </field> + + <!-- Enable Webhooks Checkbox --> + <field id="enable_webhooks" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Enable webhooks</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Enable or disable the webhook functionality for Bob Go.</comment> + </field> + + <!-- Webhook Key Input Field --> + <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Webhook key</label> + <comment>Enter the webhook key for authentication.</comment> + <depends> + <field id="enable_webhooks">1</field> + </depends> + </field> </group> </section> </system> -- GitLab From b77abc3818a1ced5d22db839ca743cf8bd32b7d1 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 10:10:05 +0200 Subject: [PATCH 20/56] webhooks test update and webhooks payload --- Model/Carrier/BobGo.php | 4 --- Observer/ConfigChangeObserver.php | 14 ++++---- Observer/OrderCreateWebhook.php | 1 - Observer/OrderWebhookBase.php | 57 +++++-------------------------- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 51cf36b..fdb7283 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1343,10 +1343,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car // Convert the string to a boolean value $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); -// if (!$webhookKey) { -// $this->_logger->error('Webhook key not configured.'); -// return false; -// } $storeId = strval($this->_storeManager->getStore()->getId()); diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 528dd82..9cbde5d 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -74,21 +74,23 @@ class ConfigChangeObserver implements ObserverInterface // Test for webhooks if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { -// if ($this->bobGo->isWebhookEnabled()) { - $this->logger->info('Webhooks test start: '); - $result = $this->bobGo->triggerWebhookTest(); - $this->logger->info('Webhooks test end: ' . $result); + $this->logger->info('Webhooks test start: '); + $result = $this->bobGo->triggerWebhookTest(); + $this->logger->info('Webhooks test end: ' . $result); + if ($this->bobGo->isWebhookEnabled()) { if ($result) { $this->messageManager->addSuccessMessage( __('Webhook validation successful.') ); } else { $this->messageManager->addErrorMessage( - __('Webhook validation failed. Please check the webhook key and try again.') + __('Webhook validation failed. Please check your internet connection + and get the webhook key for your channel on Bob Go sales channels page. + https://my.bobgo.co.za/sales-channels') ); } -// } + } } } } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index f605801..74f1ce4 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -15,6 +15,5 @@ class OrderCreateWebhook extends OrderWebhookBase // Extract order data and send to the webhook URL $this->sendWebhook($order); - $this->logger->info('Webhooks sent'); } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index db27d61..6a5163f 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -8,6 +8,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; +use BobGroup\BobGo\Model\Carrier\BobGo; abstract class OrderWebhookBase implements ObserverInterface { @@ -15,66 +16,48 @@ abstract class OrderWebhookBase implements ObserverInterface protected LoggerInterface $logger; protected StoreManagerInterface $storeManager; protected ScopeConfigInterface $scopeConfig; + protected $bobGo; - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig) + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig, BobGo $bobGo) { $this->logger = $logger; $this->curl = $curl; $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig; + $this->bobGo = $bobGo; } protected function sendWebhook($order) { - $enabled = $this->scopeConfig->getValue('carriers/bobgo/enable_webhooks', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - - // Return early if webhooks is disabled - if (!$enabled) { - $this->logger->info('Webhooks are disabled. Exiting webhook process for order: ' . $order->getIncrementId()); + // Return early if not enabled + if (!$this->bobGo->isWebhookEnabled()) { return; } // Webhook URL $url = $this->getWebhookUrl(); - $this->logger->info('Webhooks url: ' . $url); $storeId = $this->getStoreId(); $orderId = $order->getId(); - // Get the order creation time - $createdAt = strtotime($order->getCreatedAt()); - $currentTime = time(); - - // Define a time threshold to consider the order as newly created - $threshold = 5; // 5 seconds - - // Determine the event type based on the creation time - if (($currentTime - $createdAt) < $threshold) { - $eventType = 'order_created'; - } else { - $eventType = 'order_updated'; - } - // Prepare payload $data = [ - 'event' => $eventType, + 'event' => 'order_updated', 'order_id' => $orderId, - 'channel_identifier' => $this->getStoreUrl(), + 'channel_identifier' => $this->bobGo->getBaseUrl(), 'store_id' => $storeId, + 'webhooks_enabled' => true, // If we get to this point webhooks are enabled ]; // Send the webhook $this->makeHttpPostRequest($url, $data, $storeId); - $this->logger->info('Webhooks sent'); } private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using the webhook key saved in config $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - $this->logger->info('Webhooks - key: ' . $webhookKey); -// $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; // Generate the HMAC-SHA256 hash as raw binary data $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); @@ -94,7 +77,6 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); - $this->logger->info('Webhooks payload: ' . $payloadJson); } private function getWebhookUrl(): string @@ -107,25 +89,4 @@ abstract class OrderWebhookBase implements ObserverInterface $storeId = $this->storeManager->getStore()->getId(); return $storeId; } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } -- GitLab From d40f977ba30ea60d486f824ad10de0f895b807ad Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 11:47:31 +0200 Subject: [PATCH 21/56] change shipping description --- Observer/ModifyShippingDescription.php | 70 ++++++++++++++++++++++++++ etc/events.xml | 5 ++ 2 files changed, 75 insertions(+) create mode 100644 Observer/ModifyShippingDescription.php diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php new file mode 100644 index 0000000..3c19823 --- /dev/null +++ b/Observer/ModifyShippingDescription.php @@ -0,0 +1,70 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Psr\Log\LoggerInterface; + +class ModifyShippingDescription implements ObserverInterface +{ + public const CODE = 'bobgo'; + + protected $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function execute(Observer $observer) + { + // Get the order object from the event + $order = $observer->getEvent()->getOrder(); + + // Get the current shipping description + $shippingDescription = $order->getShippingDescription(); + + // Log the original shipping description + $this->logger->info('Original Shipping Description: ' . $shippingDescription); + + // Get the shipping method used in the order (e.g., bobgo_10303_39_1) + $shippingMethod = $order->getShippingMethod(); + + // Get the method title from the shipping description (which might already include the title) + $methodTitle = $this->extractMethodTitle($shippingDescription); + + // Log the method title for debugging + $this->logger->info('Extracted Method Title: ' . $methodTitle); + + // Set the new dynamic shipping description based only on MethodTitle + $newDescription = $methodTitle; + + // Update the shipping description in the order + $order->setShippingDescription($newDescription); + + // Optionally log the new shipping description + $this->logger->info('Updated Shipping Description: ' . $newDescription); + } + + /** + * Helper function to extract the method title from the original shipping description + * + * @param string $shippingDescription + * @return string + */ + private function extractMethodTitle($shippingDescription) + { + // Find the position of the last dash in the string + $lastDashPosition = strrpos($shippingDescription, ' - '); + + // If a dash is found, extract the part after the last dash + if ($lastDashPosition !== false) { + return trim(substr($shippingDescription, $lastDashPosition + 3)); // +3 to skip the ' - ' part + } + + // If no dash is found, return the full description (fallback) + return $shippingDescription; + } + +} diff --git a/etc/events.xml b/etc/events.xml index d4d14f1..a6fab5c 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -3,4 +3,9 @@ <event name="sales_order_save_after"> <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> </event> + <!-- app/code/Vendor/Module/etc/events.xml --> + <event name="sales_order_place_before"> + <observer name="modify_shipping_description" instance="BobGroup\BobGo\Observer\ModifyShippingDescription"/> + </event> + </config> -- GitLab From 57d26adc7c75cc5c482206f2e4fa71f282e6ab78 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 13:38:30 +0200 Subject: [PATCH 22/56] change shipping description --- Model/Carrier/BobGo.php | 15 ++++----------- Model/Carrier/UData.php | 2 +- Observer/ConfigChangeObserver.php | 4 +--- Observer/ModifyShippingDescription.php | 12 ------------ Observer/OrderWebhookBase.php | 2 +- 5 files changed, 7 insertions(+), 28 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index fdb7283..332ab93 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1360,7 +1360,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $signature = base64_encode($rawSignature); // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); + $this->curl->addHeader('x-m-webhook-signature', $signature); $payloadJson = json_encode($payload); $this->_logger->info('Webhooks payload: ' . $payloadJson); @@ -1377,20 +1377,13 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $response = json_decode($responseBody, true); - if ($statusCode == 200 && isset($response['success']) && $response['success'] === true) { - $this->_logger->info('Webhook validation successful.'); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); - return true; - } else { - $this->_logger->error('Webhook validation failed: ' . ($response['message'] ?? 'Unknown error')); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); - return false; + if ($statusCode != 200) { + throw new LocalizedException(__('Status code from BobGo: %1', $statusCode)); } } catch (\Exception $e) { - $this->_logger->error('Webhook validation exception: ' . $e->getMessage()); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); return false; } + return true; } } diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index a7e5d2e..adf83a2 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; } diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 9cbde5d..8362feb 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -73,10 +73,8 @@ class ConfigChangeObserver implements ObserverInterface } // Test for webhooks - if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { - $this->logger->info('Webhooks test start: '); + if ((is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) || (is_array($changedPaths) && in_array('carriers/bobgo/webhook_key', $changedPaths))) { $result = $this->bobGo->triggerWebhookTest(); - $this->logger->info('Webhooks test end: ' . $result); if ($this->bobGo->isWebhookEnabled()) { if ($result) { diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php index 3c19823..2684798 100644 --- a/Observer/ModifyShippingDescription.php +++ b/Observer/ModifyShippingDescription.php @@ -25,26 +25,14 @@ class ModifyShippingDescription implements ObserverInterface // Get the current shipping description $shippingDescription = $order->getShippingDescription(); - // Log the original shipping description - $this->logger->info('Original Shipping Description: ' . $shippingDescription); - - // Get the shipping method used in the order (e.g., bobgo_10303_39_1) - $shippingMethod = $order->getShippingMethod(); - // Get the method title from the shipping description (which might already include the title) $methodTitle = $this->extractMethodTitle($shippingDescription); - // Log the method title for debugging - $this->logger->info('Extracted Method Title: ' . $methodTitle); - // Set the new dynamic shipping description based only on MethodTitle $newDescription = $methodTitle; // Update the shipping description in the order $order->setShippingDescription($newDescription); - - // Optionally log the new shipping description - $this->logger->info('Updated Shipping Description: ' . $newDescription); } /** diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 6a5163f..75767cc 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -66,7 +66,7 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); + $this->curl->addHeader('x-m-webhook-signature', $signature); // Perform the API request $payloadJson = json_encode($data); -- GitLab From b0265f41023e1074c06cc45144ccd178ddb27129 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 14:09:45 +0200 Subject: [PATCH 23/56] cleanup --- Model/Carrier/BobGo.php | 46 +++++++++++--------------- Observer/ModifyShippingDescription.php | 4 +-- Observer/OrderWebhookBase.php | 27 ++------------- 3 files changed, 23 insertions(+), 54 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 332ab93..67c7f0f 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1335,14 +1335,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - $isEnabled = $this->scopeConfig->getValue( - 'carriers/bobgo/enable_webhooks', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - // Convert the string to a boolean value - $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); - + $isEnabled = $this->isWebhookEnabled(); $storeId = strval($this->_storeManager->getStore()->getId()); @@ -1354,28 +1348,9 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car ]; try { - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('x-m-webhook-signature', $signature); - - $payloadJson = json_encode($payload); - $this->_logger->info('Webhooks payload: ' . $payloadJson); - if ($payloadJson === false) { - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($this->getWebhookUrl(), $payloadJson); + $this->encodeWebhookAndPostRequest($this->getWebhookUrl(), $payload, $storeId, $webhookKey); $statusCode = $this->curl->getStatus(); - $this->_logger->info('Webhooks statuscode: ' . $statusCode); $responseBody = $this->curl->getBody(); - $this->_logger->info('Webhooks response: ' . $responseBody); - - $response = json_decode($responseBody, true); if ($statusCode != 200) { throw new LocalizedException(__('Status code from BobGo: %1', $statusCode)); @@ -1386,4 +1361,21 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return true; } + public function encodeWebhookAndPostRequest($url, $data, $storeId, $webhookKey) { + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('x-m-webhook-signature', $signature); + + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + } } diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php index 2684798..2e443af 100644 --- a/Observer/ModifyShippingDescription.php +++ b/Observer/ModifyShippingDescription.php @@ -25,10 +25,10 @@ class ModifyShippingDescription implements ObserverInterface // Get the current shipping description $shippingDescription = $order->getShippingDescription(); - // Get the method title from the shipping description (which might already include the title) + // Get the method title from the shipping description $methodTitle = $this->extractMethodTitle($shippingDescription); - // Set the new dynamic shipping description based only on MethodTitle + // Set the new shipping description based only on MethodTitle $newDescription = $methodTitle; // Update the shipping description in the order diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 75767cc..62988c4 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -50,33 +50,10 @@ abstract class OrderWebhookBase implements ObserverInterface 'webhooks_enabled' => true, // If we get to this point webhooks are enabled ]; - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { // Generate the signature using the webhook key saved in config $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('x-m-webhook-signature', $signature); - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); + // Send the webhook + $this->bobGo->encodeWebhookAndPostRequest($url, $data, $storeId, $webhookKey); } private function getWebhookUrl(): string -- GitLab From e841e5b6ce80fabe6d185377031c6072e8dd2c7d Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 14 Oct 2024 14:34:28 +0200 Subject: [PATCH 24/56] consumer secret key as webhook key --- Observer/ConfigChangeObserver.php | 3 +-- etc/adminhtml/system.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 8362feb..efc0179 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -84,8 +84,7 @@ class ConfigChangeObserver implements ObserverInterface } else { $this->messageManager->addErrorMessage( __('Webhook validation failed. Please check your internet connection - and get the webhook key for your channel on Bob Go sales channels page. - https://my.bobgo.co.za/sales-channels') + and use your Bob Go integration consumer secret key for webhook validation.') ); } } diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 3f4b31f..7aae293 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -39,7 +39,7 @@ <!-- Webhook Key Input Field --> <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook key</label> - <comment>Enter the webhook key for authentication.</comment> + <comment>Enter Bob Go integration consumer secret key for webhook authentication.</comment> <depends> <field id="enable_webhooks">1</field> </depends> -- GitLab From 500707307c24ff55d83719d1fbd929a51f59be90 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 27 Aug 2024 11:28:10 +0200 Subject: [PATCH 25/56] Start webhook implementation --- Model/Carrier/UData.php | 7 ++ Observer/OrderCreateWebhook.php | 119 ++++++++++++++++++++++++++++++++ Observer/OrderUpdateWebhook.php | 76 ++++++++++++++++++++ etc/events.xml | 6 ++ etc/frontend/di.xml | 8 +++ 5 files changed, 216 insertions(+) create mode 100644 Observer/OrderCreateWebhook.php create mode 100644 Observer/OrderUpdateWebhook.php diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index d173baa..a7e5d2e 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -20,4 +20,11 @@ class UData * @var string */ public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento'; + + /** + * Order create/update webhook URL + * + * @var string + */ + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php new file mode 100644 index 0000000..f301cc8 --- /dev/null +++ b/Observer/OrderCreateWebhook.php @@ -0,0 +1,119 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use BobGroup\BobGo\Model\Carrier\UData; +use Psr\Log\LoggerInterface; +use Magento\Store\Model\StoreManagerInterface; + +class OrderCreateWebhook implements ObserverInterface +{ + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; // Initialize the Curl instance + $this->storeManager = $storeManager; + } + + public function execute(Observer $observer) + { + $this->logger->info('OrderCreateWebhook: execute method started'); + + $order = $observer->getEvent()->getOrder(); + if (!$order) { + $this->logger->error('OrderCreateWebhook: No order object found in observer'); + return; + } + + // Log order creation data + $this->logger->info('Order Created:', ['order_id' => $order->getId(), 'order_data' => $order->getData()]); + + // Extract order data and send to the webhook URL + $this->sendWebhook($order, 'order_created'); + + $this->logger->info('OrderCreateWebhook: execute method finished'); + } + + private function sendWebhook($order, $eventType) + { + $this->logger->info('OrderCreateWebhook: sendWebhook method started'); + + // Webhook URL + $url = $this->getWebhookUrl(); + $this->logger->info('OrderCreateWebhook: Webhook URL', ['url' => $url]); + + // Get Store UUID and add to query parameters + $storeUuid = $this->getStoreUuid(); + $this->logger->info('UUID: ', ['uuid' => $storeUuid]); + $url .= '?channel=' . urlencode($storeUuid); + $this->logger->info('Webhook URL', ['url' => $url]); + + // Prepare payload + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'order_data' => $order->getData() + ]; + + // Log webhook payload + $this->logger->info('Sending Webhook:', ['url' => $url, 'payload' => $data]); + + // Send the webhook + $this->makeHttpPostRequest($url, $data); + + $this->logger->info('OrderCreateWebhook: sendWebhook method finished'); + } + + + private function makeHttpPostRequest($url, $data) + { + $this->logger->info('URL:', ['url' => $url]); + $this->logger->info('Data:', ['data' => $data]); + + // Perform the API request + $payloadJson = json_encode($data); + if ($payloadJson === false) { + $this->logger->error('Payload Webhook failed: Unable to encode JSON.'); + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->logger->info('Payload Webhook:', ['response' => $payloadJson]); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + $statusCode = $this->curl->getStatus(); + $responseBody = $this->curl->getBody(); + + // Log status code and response body + $this->logger->info('Webhook Response Status:', ['status' => $statusCode]); + $this->logger->info('Webhook Response Body:', ['response' => $responseBody]); + + // Decode the response + $response = json_decode($responseBody, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $this->logger->error('Failed to decode webhook response:', ['error' => json_last_error_msg()]); + } else { + $this->logger->info('Response Webhook:', ['response' => $response]); + } + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreUuid(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + //return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } +} diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php new file mode 100644 index 0000000..ad2a4ad --- /dev/null +++ b/Observer/OrderUpdateWebhook.php @@ -0,0 +1,76 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Store\Model\StoreManagerInterface; +use BobGroup\BobGo\Model\Carrier\UData; +use Psr\Log\LoggerInterface; + +class OrderUpdateWebhook implements ObserverInterface +{ + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; + $this->storeManager = $storeManager; + } + + public function execute(Observer $observer) + { + $order = $observer->getEvent()->getOrder(); + if (!$order) { + return; + } + + $this->sendWebhook($order, 'order_updated'); + } + + private function sendWebhook($order, $eventType) + { + $url = $this->getWebhookUrl(); + + // Get Store UUID and add to query parameters + $storeUuid = $this->getStoreUuid(); + $url .= '?channel=' . urlencode($storeUuid); + + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'order_data' => $order->getData() + ]; + + $this->makeHttpPostRequest($url, $data); + } + + private function makeHttpPostRequest($url, $data) + { + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + $statusCode = $this->curl->getStatus(); + $responseBody = $this->curl->getBody(); + + $response = json_decode($responseBody, true); + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreUuid(): string + { + return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } +} diff --git a/etc/events.xml b/etc/events.xml index bfb5e96..3e71bc3 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -1,3 +1,9 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="sales_order_place_after"> + <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> + </event> + <event name="sales_order_save_after"> + <observer name="order_update_webhook" instance="BobGroup\BobGo\Observer\OrderUpdateWebhook"/> + </event> </config> diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index bc1aaeb..25a80a9 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -13,4 +13,12 @@ <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> </arguments> </type> + <type name="BobGroup\BobGo\Observer\OrderCreateWebhook"> + <arguments> + <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument> + <argument name="curl" xsi:type="object">Magento\Framework\HTTP\Client\Curl</argument> + <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface</argument> + </arguments> + </type> + </config> -- GitLab From a0918ae512892d1857097913831e7865bd005ad7 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 28 Aug 2024 14:18:32 +0200 Subject: [PATCH 26/56] Add to webhooks --- Observer/OrderCreateWebhook.php | 78 ++++++++++++++++----------------- Observer/OrderUpdateWebhook.php | 52 +++++++++++++++++++--- 2 files changed, 82 insertions(+), 48 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index f301cc8..3f29ba7 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -18,91 +18,83 @@ class OrderCreateWebhook implements ObserverInterface public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) { $this->logger = $logger; - $this->curl = $curl; // Initialize the Curl instance + $this->curl = $curl; $this->storeManager = $storeManager; } public function execute(Observer $observer) { - $this->logger->info('OrderCreateWebhook: execute method started'); - $order = $observer->getEvent()->getOrder(); if (!$order) { - $this->logger->error('OrderCreateWebhook: No order object found in observer'); + //$this->logger->error('OrderCreateWebhook: No order object found in observer'); return; } - // Log order creation data - $this->logger->info('Order Created:', ['order_id' => $order->getId(), 'order_data' => $order->getData()]); - // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_created'); - - $this->logger->info('OrderCreateWebhook: execute method finished'); + $this->sendWebhook($order, 'order_create'); } private function sendWebhook($order, $eventType) { - $this->logger->info('OrderCreateWebhook: sendWebhook method started'); - // Webhook URL $url = $this->getWebhookUrl(); - $this->logger->info('OrderCreateWebhook: Webhook URL', ['url' => $url]); - // Get Store UUID and add to query parameters - $storeUuid = $this->getStoreUuid(); - $this->logger->info('UUID: ', ['uuid' => $storeUuid]); - $url .= '?channel=' . urlencode($storeUuid); - $this->logger->info('Webhook URL', ['url' => $url]); + // Extract order items + $itemsData = []; + foreach ($order->getAllItems() as $item) { + $itemsData[] = $item->getData(); + } + + // Extract shipping address + $shippingAddress = $order->getShippingAddress(); + $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; + + // Extract billing address + $billingAddress = $order->getBillingAddress(); + $billingAddressData = $billingAddress ? $billingAddress->getData() : []; // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), - 'order_data' => $order->getData() + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $this->getStoreId(), + 'order_data' => $order->getData(), + 'items' => $itemsData, + 'shipping_address' => $shippingAddressData, + 'billing_address' => $billingAddressData, ]; - // Log webhook payload - $this->logger->info('Sending Webhook:', ['url' => $url, 'payload' => $data]); - // Send the webhook $this->makeHttpPostRequest($url, $data); - - $this->logger->info('OrderCreateWebhook: sendWebhook method finished'); } - private function makeHttpPostRequest($url, $data) { - $this->logger->info('URL:', ['url' => $url]); - $this->logger->info('Data:', ['data' => $data]); + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'your_secret_key'; + $payloadJson = json_encode($data); + $signature = hash_hmac('sha256', $payloadJson, $secretKey); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here // Perform the API request $payloadJson = json_encode($data); if ($payloadJson === false) { - $this->logger->error('Payload Webhook failed: Unable to encode JSON.'); + //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); throw new \RuntimeException('Failed to encode payload to JSON.'); } - $this->logger->info('Payload Webhook:', ['response' => $payloadJson]); - // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); $statusCode = $this->curl->getStatus(); $responseBody = $this->curl->getBody(); - // Log status code and response body - $this->logger->info('Webhook Response Status:', ['status' => $statusCode]); - $this->logger->info('Webhook Response Body:', ['response' => $responseBody]); - // Decode the response $response = json_decode($responseBody, true); - if (json_last_error() !== JSON_ERROR_NONE) { - $this->logger->error('Failed to decode webhook response:', ['error' => json_last_error_msg()]); - } else { - $this->logger->info('Response Webhook:', ['response' => $response]); - } } private function getWebhookUrl(): string @@ -110,10 +102,14 @@ class OrderCreateWebhook implements ObserverInterface return UData::WEBHOOK_URL; } - private function getStoreUuid(): string + private function getStoreId(): string { $storeId = $this->storeManager->getStore()->getId(); return $storeId; - //return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + } + + private function getStoreUrl(): string + { + return $this->storeManager->getStore()->getBaseUrl(); } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index ad2a4ad..6d6b43a 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -5,9 +5,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; -use Magento\Store\Model\StoreManagerInterface; use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; +use Magento\Store\Model\StoreManagerInterface; class OrderUpdateWebhook implements ObserverInterface { @@ -29,38 +29,70 @@ class OrderUpdateWebhook implements ObserverInterface return; } + // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_updated'); } private function sendWebhook($order, $eventType) { + // Webhook URL $url = $this->getWebhookUrl(); - // Get Store UUID and add to query parameters - $storeUuid = $this->getStoreUuid(); - $url .= '?channel=' . urlencode($storeUuid); + // Extract order items + $itemsData = []; + foreach ($order->getAllItems() as $item) { + $itemsData[] = $item->getData(); + } + + // Extract shipping address + $shippingAddress = $order->getShippingAddress(); + $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; + + // Extract billing address + $billingAddress = $order->getBillingAddress(); + $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), - 'order_data' => $order->getData() + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $this->getStoreId(), + 'order_data' => $order->getData(), + 'items' => $itemsData, + 'shipping_address' => $shippingAddressData, + 'billing_address' => $billingAddressData, ]; + // Send the webhook $this->makeHttpPostRequest($url, $data); } private function makeHttpPostRequest($url, $data) { + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'your_secret_key'; + $payloadJson = json_encode($data); + $signature = hash_hmac('sha256', $payloadJson, $secretKey); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here + + // Perform the API request $payloadJson = json_encode($data); if ($payloadJson === false) { + //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); throw new \RuntimeException('Failed to encode payload to JSON.'); } + // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); $statusCode = $this->curl->getStatus(); $responseBody = $this->curl->getBody(); + // Decode the response $response = json_decode($responseBody, true); } @@ -69,8 +101,14 @@ class OrderUpdateWebhook implements ObserverInterface return UData::WEBHOOK_URL; } - private function getStoreUuid(): string + private function getStoreId(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + } + + private function getStoreUrl(): string { - return $this->storeManager->getStore()->getConfig('general/store_information/store_id'); + return $this->storeManager->getStore()->getBaseUrl(); } } -- GitLab From 524ce233f070724f1c97542e4baae10f786a19ad Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 08:14:04 +0200 Subject: [PATCH 27/56] Clean identifier url --- Observer/OrderCreateWebhook.php | 18 +++++++++++++++++- Observer/OrderUpdateWebhook.php | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 3f29ba7..010bec5 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -110,6 +110,22 @@ class OrderCreateWebhook implements ObserverInterface private function getStoreUrl(): string { - return $this->storeManager->getStore()->getBaseUrl(); + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 6d6b43a..01b7695 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -109,6 +109,22 @@ class OrderUpdateWebhook implements ObserverInterface private function getStoreUrl(): string { - return $this->storeManager->getStore()->getBaseUrl(); + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; } } -- GitLab From b0af6b717d878ae72d13e1d691a679fd02472a6a Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 10:55:10 +0200 Subject: [PATCH 28/56] webhook signature --- Observer/OrderCreateWebhook.php | 28 +++++++++++++++++----------- Observer/OrderUpdateWebhook.php | 25 +++++++++++++++---------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 010bec5..cf90443 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -31,7 +31,7 @@ class OrderCreateWebhook implements ObserverInterface } // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_create'); + $this->sendWebhook($order, 'order_created'); } private function sendWebhook($order, $eventType) @@ -53,28 +53,34 @@ class OrderCreateWebhook implements ObserverInterface $billingAddress = $order->getBillingAddress(); $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + $storeId = $this->getStoreId(); + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $this->getStoreId(), - 'order_data' => $order->getData(), - 'items' => $itemsData, - 'shipping_address' => $shippingAddressData, - 'billing_address' => $billingAddressData, + 'store_id' => $storeId, + //'order_data' => $order->getData(), + //'items' => $itemsData, + //'shipping_address' => $shippingAddressData, + //'billing_address' => $billingAddressData, ]; // Send the webhook - $this->makeHttpPostRequest($url, $data); + $this->makeHttpPostRequest($url, $data, $storeId); } - private function makeHttpPostRequest($url, $data) + private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'your_secret_key'; - $payloadJson = json_encode($data); - $signature = hash_hmac('sha256', $payloadJson, $secretKey); + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 01b7695..095c887 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -52,28 +52,33 @@ class OrderUpdateWebhook implements ObserverInterface $billingAddress = $order->getBillingAddress(); $billingAddressData = $billingAddress ? $billingAddress->getData() : []; + $storeId = $this->getStoreId(); + // Prepare payload $data = [ 'event' => $eventType, 'order_id' => $order->getId(), 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $this->getStoreId(), - 'order_data' => $order->getData(), - 'items' => $itemsData, - 'shipping_address' => $shippingAddressData, - 'billing_address' => $billingAddressData, + 'store_id' => $storeId, + //'order_data' => $order->getData(), + //'items' => $itemsData, + //'shipping_address' => $shippingAddressData, + //'billing_address' => $billingAddressData, ]; // Send the webhook - $this->makeHttpPostRequest($url, $data); + $this->makeHttpPostRequest($url, $data, $storeId); } - private function makeHttpPostRequest($url, $data) + private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'your_secret_key'; - $payloadJson = json_encode($data); - $signature = hash_hmac('sha256', $payloadJson, $secretKey); + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); -- GitLab From 3e0d8153cfe473fe1d374cdfa22833adeadf17e4 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 11:46:53 +0200 Subject: [PATCH 29/56] use sahred functions for webhooks --- Observer/OrderCreateWebhook.php | 104 +------------------------------- Observer/OrderUpdateWebhook.php | 102 +------------------------------ Observer/OrderWebhookBase.php | 85 ++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 204 deletions(-) create mode 100644 Observer/OrderWebhookBase.php diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index cf90443..1b93779 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -9,7 +9,7 @@ use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; use Magento\Store\Model\StoreManagerInterface; -class OrderCreateWebhook implements ObserverInterface +class OrderCreateWebhook extends OrderWebhookBase { protected Curl $curl; protected LoggerInterface $logger; @@ -26,112 +26,10 @@ class OrderCreateWebhook implements ObserverInterface { $order = $observer->getEvent()->getOrder(); if (!$order) { - //$this->logger->error('OrderCreateWebhook: No order object found in observer'); return; } // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_created'); } - - private function sendWebhook($order, $eventType) - { - // Webhook URL - $url = $this->getWebhookUrl(); - - // Extract order items - $itemsData = []; - foreach ($order->getAllItems() as $item) { - $itemsData[] = $item->getData(); - } - - // Extract shipping address - $shippingAddress = $order->getShippingAddress(); - $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; - - // Extract billing address - $billingAddress = $order->getBillingAddress(); - $billingAddressData = $billingAddress ? $billingAddress->getData() : []; - - $storeId = $this->getStoreId(); - - // Prepare payload - $data = [ - 'event' => $eventType, - 'order_id' => $order->getId(), - 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $storeId, - //'order_data' => $order->getData(), - //'items' => $itemsData, - //'shipping_address' => $shippingAddressData, - //'billing_address' => $billingAddressData, - ]; - - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); - $statusCode = $this->curl->getStatus(); - $responseBody = $this->curl->getBody(); - - // Decode the response - $response = json_decode($responseBody, true); - } - - private function getWebhookUrl(): string - { - return UData::WEBHOOK_URL; - } - - private function getStoreId(): string - { - $storeId = $this->storeManager->getStore()->getId(); - return $storeId; - } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 095c887..03b96cb 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -9,7 +9,7 @@ use BobGroup\BobGo\Model\Carrier\UData; use Psr\Log\LoggerInterface; use Magento\Store\Model\StoreManagerInterface; -class OrderUpdateWebhook implements ObserverInterface +class OrderUpdateWebhook extends OrderWebhookBase { protected Curl $curl; protected LoggerInterface $logger; @@ -32,104 +32,4 @@ class OrderUpdateWebhook implements ObserverInterface // Extract order data and send to the webhook URL $this->sendWebhook($order, 'order_updated'); } - - private function sendWebhook($order, $eventType) - { - // Webhook URL - $url = $this->getWebhookUrl(); - - // Extract order items - $itemsData = []; - foreach ($order->getAllItems() as $item) { - $itemsData[] = $item->getData(); - } - - // Extract shipping address - $shippingAddress = $order->getShippingAddress(); - $shippingAddressData = $shippingAddress ? $shippingAddress->getData() : []; - - // Extract billing address - $billingAddress = $order->getBillingAddress(); - $billingAddressData = $billingAddress ? $billingAddress->getData() : []; - - $storeId = $this->getStoreId(); - - // Prepare payload - $data = [ - 'event' => $eventType, - 'order_id' => $order->getId(), - 'channel_identifier' => $this->getStoreUrl(), - 'store_id' => $storeId, - //'order_data' => $order->getData(), - //'items' => $itemsData, - //'shipping_address' => $shippingAddressData, - //'billing_address' => $billingAddressData, - ]; - - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); // Add your custom header here - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - //$this->logger->error('Payload Webhook failed: Unable to encode JSON.'); - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); - $statusCode = $this->curl->getStatus(); - $responseBody = $this->curl->getBody(); - - // Decode the response - $response = json_decode($responseBody, true); - } - - private function getWebhookUrl(): string - { - return UData::WEBHOOK_URL; - } - - private function getStoreId(): string - { - $storeId = $this->storeManager->getStore()->getId(); - return $storeId; - } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php new file mode 100644 index 0000000..9f2e83f --- /dev/null +++ b/Observer/OrderWebhookBase.php @@ -0,0 +1,85 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\Event\ObserverInterface; + +abstract class OrderWebhookBase implements ObserverInterface +{ + protected function sendWebhook($order, $eventType) + { + // Webhook URL + $url = $this->getWebhookUrl(); + + $storeId = $this->getStoreId(); + + // Prepare payload + $data = [ + 'event' => $eventType, + 'order_id' => $order->getId(), + 'channel_identifier' => $this->getStoreUrl(), + 'store_id' => $storeId, + ]; + + // Send the webhook + $this->makeHttpPostRequest($url, $data, $storeId); + } + + private function makeHttpPostRequest($url, $data, $storeId) + { + // Generate the signature using a secret key and the payload (example using HMAC SHA256) + $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); + + // Perform the API request + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + } + + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + + private function getStoreId(): string + { + $storeId = $this->storeManager->getStore()->getId(); + return $storeId; + } + + private function getStoreUrl(): string + { + $url = $this->storeManager->getStore()->getBaseUrl(); + + // Remove protocol (http:// or https://) + $host = preg_replace('#^https?://#', '', $url); + + // Ensure $host is a string before using it in explode + $host = $host ?? ''; + + // Remove everything after the host (e.g., paths, query strings) + $host = explode('/', $host)[0]; + + // If the host starts with 'www.', remove it + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } +} -- GitLab From d061262a822de9a89f7de139665186c2e69b6304 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 12:00:12 +0200 Subject: [PATCH 30/56] cleanup --- Observer/OrderCreateWebhook.php | 16 ---------------- Observer/OrderUpdateWebhook.php | 16 ---------------- Observer/OrderWebhookBase.php | 14 ++++++++++++++ 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 1b93779..678f2de 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -3,25 +3,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\HTTP\Client\Curl; -use BobGroup\BobGo\Model\Carrier\UData; -use Psr\Log\LoggerInterface; -use Magento\Store\Model\StoreManagerInterface; class OrderCreateWebhook extends OrderWebhookBase { - protected Curl $curl; - protected LoggerInterface $logger; - protected StoreManagerInterface $storeManager; - - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) - { - $this->logger = $logger; - $this->curl = $curl; - $this->storeManager = $storeManager; - } - public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php index 03b96cb..3074bdd 100644 --- a/Observer/OrderUpdateWebhook.php +++ b/Observer/OrderUpdateWebhook.php @@ -3,25 +3,9 @@ namespace BobGroup\BobGo\Observer; use Magento\Framework\Event\Observer; -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\HTTP\Client\Curl; -use BobGroup\BobGo\Model\Carrier\UData; -use Psr\Log\LoggerInterface; -use Magento\Store\Model\StoreManagerInterface; class OrderUpdateWebhook extends OrderWebhookBase { - protected Curl $curl; - protected LoggerInterface $logger; - protected StoreManagerInterface $storeManager; - - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) - { - $this->logger = $logger; - $this->curl = $curl; - $this->storeManager = $storeManager; - } - public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 9f2e83f..b17375e 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -4,9 +4,23 @@ namespace BobGroup\BobGo\Observer; use BobGroup\BobGo\Model\Carrier\UData; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\HTTP\Client\Curl; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; abstract class OrderWebhookBase implements ObserverInterface { + protected Curl $curl; + protected LoggerInterface $logger; + protected StoreManagerInterface $storeManager; + + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + { + $this->logger = $logger; + $this->curl = $curl; + $this->storeManager = $storeManager; + } + protected function sendWebhook($order, $eventType) { // Webhook URL -- GitLab From 8d5a03f71fb188401a264b5519f6862dabb5d47a Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 14:57:11 +0200 Subject: [PATCH 31/56] orderId for both created and updated --- Observer/OrderCreateWebhook.php | 2 +- Observer/OrderUpdateWebhook.php | 19 ------------------- Observer/OrderWebhookBase.php | 23 +++++++++++++++++++++-- etc/events.xml | 5 +---- 4 files changed, 23 insertions(+), 26 deletions(-) delete mode 100644 Observer/OrderUpdateWebhook.php diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 678f2de..74f1ce4 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -14,6 +14,6 @@ class OrderCreateWebhook extends OrderWebhookBase } // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_created'); + $this->sendWebhook($order); } } diff --git a/Observer/OrderUpdateWebhook.php b/Observer/OrderUpdateWebhook.php deleted file mode 100644 index 3074bdd..0000000 --- a/Observer/OrderUpdateWebhook.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -namespace BobGroup\BobGo\Observer; - -use Magento\Framework\Event\Observer; - -class OrderUpdateWebhook extends OrderWebhookBase -{ - public function execute(Observer $observer) - { - $order = $observer->getEvent()->getOrder(); - if (!$order) { - return; - } - - // Extract order data and send to the webhook URL - $this->sendWebhook($order, 'order_updated'); - } -} diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index b17375e..f4ca74d 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -21,17 +21,36 @@ abstract class OrderWebhookBase implements ObserverInterface $this->storeManager = $storeManager; } - protected function sendWebhook($order, $eventType) + protected function sendWebhook($order) { // Webhook URL $url = $this->getWebhookUrl(); $storeId = $this->getStoreId(); + $orderId = $order->getId(); + + // Get the order creation time + $createdAt = strtotime($order->getCreatedAt()); + $currentTime = time(); + + // Define a time threshold to consider the order as newly created + $threshold = 5; // 5 seconds + + // Determine the event type based on the creation time + if (($currentTime - $createdAt) < $threshold) { + $eventType = 'order_created'; + } else { + $eventType = 'order_updated'; + } + + // Log the event type for debugging purposes + $this->logger->info('event: ' . $eventType); + // Prepare payload $data = [ 'event' => $eventType, - 'order_id' => $order->getId(), + 'order_id' => $orderId, 'channel_identifier' => $this->getStoreUrl(), 'store_id' => $storeId, ]; diff --git a/etc/events.xml b/etc/events.xml index 3e71bc3..d4d14f1 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -1,9 +1,6 @@ <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="sales_order_place_after"> - <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> - </event> <event name="sales_order_save_after"> - <observer name="order_update_webhook" instance="BobGroup\BobGo\Observer\OrderUpdateWebhook"/> + <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> </event> </config> -- GitLab From 06c7ec846891a49d6328d541cc176b6e03ca9834 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 14:58:47 +0200 Subject: [PATCH 32/56] cleanup --- Observer/OrderWebhookBase.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index f4ca74d..eeb106e 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -44,9 +44,6 @@ abstract class OrderWebhookBase implements ObserverInterface $eventType = 'order_updated'; } - // Log the event type for debugging purposes - $this->logger->info('event: ' . $eventType); - // Prepare payload $data = [ 'event' => $eventType, -- GitLab From b07a8cd51af5973048864010869adfd94130ba68 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 29 Aug 2024 15:08:50 +0200 Subject: [PATCH 33/56] change endpoint --- Model/Carrier/UData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index a7e5d2e..adf83a2 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; } -- GitLab From eda30797c33a55153a5db5c0f4605b7d1bd0417e Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 2 Oct 2024 15:44:00 +0200 Subject: [PATCH 34/56] triggerWebhookTest --- Model/Carrier/BobGo.php | 87 +++++++++++++++++++++++++++++++ Model/Carrier/UData.php | 2 +- Observer/ConfigChangeObserver.php | 20 +++++++ Observer/OrderCreateWebhook.php | 1 + Observer/OrderWebhookBase.php | 24 +++++++-- etc/adminhtml/system.xml | 17 +++++- 6 files changed, 145 insertions(+), 6 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 2809b13..77b8e88 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -680,6 +680,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return UData::RATES_ENDPOINT; } + private function getWebhookUrl(): string + { + return UData::WEBHOOK_URL; + } + /** * Perform API Request to Bob Go API and return response. * @@ -1041,4 +1046,86 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car } return false; } + + public function isWebhookEnabled(): bool + { + $enabled = $this->scopeConfig->getValue( + 'carriers/bobgo/enable_webhooks', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + // Cast the value to a boolean + return filter_var($enabled, FILTER_VALIDATE_BOOLEAN); + } + + + public function triggerWebhookTest(): bool + { + $webhookKey = $this->scopeConfig->getValue( + 'carriers/bobgo/webhook_key', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + $isEnabled = $this->scopeConfig->getValue( + 'carriers/bobgo/enable_webhooks', + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + + // Convert the string to a boolean value + $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); + +// if (!$webhookKey) { +// $this->_logger->error('Webhook key not configured.'); +// return false; +// } + + $storeId = strval($this->_storeManager->getStore()->getId()); + + $payload = [ + 'event' => 'webhook_validation', + 'channel_identifier' => $this->getBaseUrl(), + 'store_id' => $storeId, + 'webhooks_enabled' => $isEnabled, + ]; + + try { + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('X-M-Webhook-Signature', $signature); + + $payloadJson = json_encode($payload); + $this->_logger->info('Webhooks payload: ' . $payloadJson); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($this->getWebhookUrl(), $payloadJson); + $statusCode = $this->curl->getStatus(); + $this->_logger->info('Webhooks statuscode: ' . $statusCode); + $responseBody = $this->curl->getBody(); + $this->_logger->info('Webhooks response: ' . $responseBody); + + $response = json_decode($responseBody, true); + + if ($statusCode == 200 && isset($response['success']) && $response['success'] === true) { + $this->_logger->info('Webhook validation successful.'); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return true; + } else { + $this->_logger->error('Webhook validation failed: ' . ($response['message'] ?? 'Unknown error')); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return false; + } + } catch (\Exception $e) { + $this->_logger->error('Webhook validation exception: ' . $e->getMessage()); +// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); + return false; + } + } + } diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index adf83a2..a7e5d2e 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; } diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 1b6c072..528dd82 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -52,6 +52,7 @@ class ConfigChangeObserver implements ObserverInterface { $changedPaths = $observer->getEvent()->getData('changed_paths'); + // Test for rates at checkout if (is_array($changedPaths) && in_array('carriers/bobgo/active', $changedPaths)) { if ($this->bobGo->isActive()) { $result = $this->bobGo->triggerRatesTest(); @@ -70,5 +71,24 @@ class ConfigChangeObserver implements ObserverInterface } } } + + // Test for webhooks + if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { +// if ($this->bobGo->isWebhookEnabled()) { + $this->logger->info('Webhooks test start: '); + $result = $this->bobGo->triggerWebhookTest(); + $this->logger->info('Webhooks test end: ' . $result); + + if ($result) { + $this->messageManager->addSuccessMessage( + __('Webhook validation successful.') + ); + } else { + $this->messageManager->addErrorMessage( + __('Webhook validation failed. Please check the webhook key and try again.') + ); + } +// } + } } } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index 74f1ce4..f605801 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -15,5 +15,6 @@ class OrderCreateWebhook extends OrderWebhookBase // Extract order data and send to the webhook URL $this->sendWebhook($order); + $this->logger->info('Webhooks sent'); } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index eeb106e..db27d61 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -3,6 +3,7 @@ namespace BobGroup\BobGo\Observer; use BobGroup\BobGo\Model\Carrier\UData; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; @@ -13,18 +14,29 @@ abstract class OrderWebhookBase implements ObserverInterface protected Curl $curl; protected LoggerInterface $logger; protected StoreManagerInterface $storeManager; + protected ScopeConfigInterface $scopeConfig; - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager) + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig) { $this->logger = $logger; $this->curl = $curl; $this->storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; } protected function sendWebhook($order) { + $enabled = $this->scopeConfig->getValue('carriers/bobgo/enable_webhooks', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + + // Return early if webhooks is disabled + if (!$enabled) { + $this->logger->info('Webhooks are disabled. Exiting webhook process for order: ' . $order->getIncrementId()); + return; + } + // Webhook URL $url = $this->getWebhookUrl(); + $this->logger->info('Webhooks url: ' . $url); $storeId = $this->getStoreId(); @@ -54,14 +66,17 @@ abstract class OrderWebhookBase implements ObserverInterface // Send the webhook $this->makeHttpPostRequest($url, $data, $storeId); + $this->logger->info('Webhooks sent'); } private function makeHttpPostRequest($url, $data, $storeId) { - // Generate the signature using a secret key and the payload (example using HMAC SHA256) - $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; + // Generate the signature using the webhook key saved in config + $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + $this->logger->info('Webhooks - key: ' . $webhookKey); +// $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true); + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); // Encode the binary data in Base64 $signature = base64_encode($rawSignature); @@ -79,6 +94,7 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); + $this->logger->info('Webhooks payload: ' . $payloadJson); } private function getWebhookUrl(): string diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 263bc8b..0483f32 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -28,6 +28,22 @@ <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>Displays the delivery timeframe and additional service level description, as configured on Bob Go.</comment> </field> + + <!-- Enable Webhooks Checkbox --> + <field id="enable_webhooks" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Enable webhooks</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Enable or disable the webhook functionality for Bob Go.</comment> + </field> + + <!-- Webhook Key Input Field --> + <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> + <label>Webhook key</label> + <comment>Enter the webhook key for authentication.</comment> + <depends> + <field id="enable_webhooks">1</field> + </depends> + </field> <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Track My Order</label> <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> @@ -35,6 +51,5 @@ </field> </group> </section> - </system> </config> -- GitLab From 5c320b2df5db473aa9233772406a303a518213db Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 10:10:05 +0200 Subject: [PATCH 35/56] webhooks test update and webhooks payload --- Model/Carrier/BobGo.php | 4 --- Observer/ConfigChangeObserver.php | 14 ++++---- Observer/OrderCreateWebhook.php | 1 - Observer/OrderWebhookBase.php | 57 +++++-------------------------- 4 files changed, 17 insertions(+), 59 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 77b8e88..0e750fd 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1074,10 +1074,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car // Convert the string to a boolean value $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); -// if (!$webhookKey) { -// $this->_logger->error('Webhook key not configured.'); -// return false; -// } $storeId = strval($this->_storeManager->getStore()->getId()); diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 528dd82..9cbde5d 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -74,21 +74,23 @@ class ConfigChangeObserver implements ObserverInterface // Test for webhooks if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { -// if ($this->bobGo->isWebhookEnabled()) { - $this->logger->info('Webhooks test start: '); - $result = $this->bobGo->triggerWebhookTest(); - $this->logger->info('Webhooks test end: ' . $result); + $this->logger->info('Webhooks test start: '); + $result = $this->bobGo->triggerWebhookTest(); + $this->logger->info('Webhooks test end: ' . $result); + if ($this->bobGo->isWebhookEnabled()) { if ($result) { $this->messageManager->addSuccessMessage( __('Webhook validation successful.') ); } else { $this->messageManager->addErrorMessage( - __('Webhook validation failed. Please check the webhook key and try again.') + __('Webhook validation failed. Please check your internet connection + and get the webhook key for your channel on Bob Go sales channels page. + https://my.bobgo.co.za/sales-channels') ); } -// } + } } } } diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php index f605801..74f1ce4 100644 --- a/Observer/OrderCreateWebhook.php +++ b/Observer/OrderCreateWebhook.php @@ -15,6 +15,5 @@ class OrderCreateWebhook extends OrderWebhookBase // Extract order data and send to the webhook URL $this->sendWebhook($order); - $this->logger->info('Webhooks sent'); } } diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index db27d61..6a5163f 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -8,6 +8,7 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\HTTP\Client\Curl; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; +use BobGroup\BobGo\Model\Carrier\BobGo; abstract class OrderWebhookBase implements ObserverInterface { @@ -15,66 +16,48 @@ abstract class OrderWebhookBase implements ObserverInterface protected LoggerInterface $logger; protected StoreManagerInterface $storeManager; protected ScopeConfigInterface $scopeConfig; + protected $bobGo; - public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig) + public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig, BobGo $bobGo) { $this->logger = $logger; $this->curl = $curl; $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig; + $this->bobGo = $bobGo; } protected function sendWebhook($order) { - $enabled = $this->scopeConfig->getValue('carriers/bobgo/enable_webhooks', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - - // Return early if webhooks is disabled - if (!$enabled) { - $this->logger->info('Webhooks are disabled. Exiting webhook process for order: ' . $order->getIncrementId()); + // Return early if not enabled + if (!$this->bobGo->isWebhookEnabled()) { return; } // Webhook URL $url = $this->getWebhookUrl(); - $this->logger->info('Webhooks url: ' . $url); $storeId = $this->getStoreId(); $orderId = $order->getId(); - // Get the order creation time - $createdAt = strtotime($order->getCreatedAt()); - $currentTime = time(); - - // Define a time threshold to consider the order as newly created - $threshold = 5; // 5 seconds - - // Determine the event type based on the creation time - if (($currentTime - $createdAt) < $threshold) { - $eventType = 'order_created'; - } else { - $eventType = 'order_updated'; - } - // Prepare payload $data = [ - 'event' => $eventType, + 'event' => 'order_updated', 'order_id' => $orderId, - 'channel_identifier' => $this->getStoreUrl(), + 'channel_identifier' => $this->bobGo->getBaseUrl(), 'store_id' => $storeId, + 'webhooks_enabled' => true, // If we get to this point webhooks are enabled ]; // Send the webhook $this->makeHttpPostRequest($url, $data, $storeId); - $this->logger->info('Webhooks sent'); } private function makeHttpPostRequest($url, $data, $storeId) { // Generate the signature using the webhook key saved in config $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - $this->logger->info('Webhooks - key: ' . $webhookKey); -// $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A'; // Generate the HMAC-SHA256 hash as raw binary data $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); @@ -94,7 +77,6 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); $this->curl->post($url, $payloadJson); - $this->logger->info('Webhooks payload: ' . $payloadJson); } private function getWebhookUrl(): string @@ -107,25 +89,4 @@ abstract class OrderWebhookBase implements ObserverInterface $storeId = $this->storeManager->getStore()->getId(); return $storeId; } - - private function getStoreUrl(): string - { - $url = $this->storeManager->getStore()->getBaseUrl(); - - // Remove protocol (http:// or https://) - $host = preg_replace('#^https?://#', '', $url); - - // Ensure $host is a string before using it in explode - $host = $host ?? ''; - - // Remove everything after the host (e.g., paths, query strings) - $host = explode('/', $host)[0]; - - // If the host starts with 'www.', remove it - if (strpos($host, 'www.') === 0) { - $host = substr($host, 4); - } - - return $host; - } } -- GitLab From 06e38aa331ba767505d0532026fa55491fc32ba3 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 11:47:31 +0200 Subject: [PATCH 36/56] change shipping description --- Observer/ModifyShippingDescription.php | 70 ++++++++++++++++++++++++++ etc/events.xml | 5 ++ 2 files changed, 75 insertions(+) create mode 100644 Observer/ModifyShippingDescription.php diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php new file mode 100644 index 0000000..3c19823 --- /dev/null +++ b/Observer/ModifyShippingDescription.php @@ -0,0 +1,70 @@ +<?php + +namespace BobGroup\BobGo\Observer; + +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Psr\Log\LoggerInterface; + +class ModifyShippingDescription implements ObserverInterface +{ + public const CODE = 'bobgo'; + + protected $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function execute(Observer $observer) + { + // Get the order object from the event + $order = $observer->getEvent()->getOrder(); + + // Get the current shipping description + $shippingDescription = $order->getShippingDescription(); + + // Log the original shipping description + $this->logger->info('Original Shipping Description: ' . $shippingDescription); + + // Get the shipping method used in the order (e.g., bobgo_10303_39_1) + $shippingMethod = $order->getShippingMethod(); + + // Get the method title from the shipping description (which might already include the title) + $methodTitle = $this->extractMethodTitle($shippingDescription); + + // Log the method title for debugging + $this->logger->info('Extracted Method Title: ' . $methodTitle); + + // Set the new dynamic shipping description based only on MethodTitle + $newDescription = $methodTitle; + + // Update the shipping description in the order + $order->setShippingDescription($newDescription); + + // Optionally log the new shipping description + $this->logger->info('Updated Shipping Description: ' . $newDescription); + } + + /** + * Helper function to extract the method title from the original shipping description + * + * @param string $shippingDescription + * @return string + */ + private function extractMethodTitle($shippingDescription) + { + // Find the position of the last dash in the string + $lastDashPosition = strrpos($shippingDescription, ' - '); + + // If a dash is found, extract the part after the last dash + if ($lastDashPosition !== false) { + return trim(substr($shippingDescription, $lastDashPosition + 3)); // +3 to skip the ' - ' part + } + + // If no dash is found, return the full description (fallback) + return $shippingDescription; + } + +} diff --git a/etc/events.xml b/etc/events.xml index d4d14f1..a6fab5c 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -3,4 +3,9 @@ <event name="sales_order_save_after"> <observer name="order_create_webhook" instance="BobGroup\BobGo\Observer\OrderCreateWebhook"/> </event> + <!-- app/code/Vendor/Module/etc/events.xml --> + <event name="sales_order_place_before"> + <observer name="modify_shipping_description" instance="BobGroup\BobGo\Observer\ModifyShippingDescription"/> + </event> + </config> -- GitLab From be182ac9d34ebf29e4c4a58108525398176c512d Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 13:38:30 +0200 Subject: [PATCH 37/56] change shipping description --- Model/Carrier/BobGo.php | 15 ++++----------- Model/Carrier/UData.php | 2 +- Observer/ConfigChangeObserver.php | 4 +--- Observer/ModifyShippingDescription.php | 12 ------------ Observer/OrderWebhookBase.php | 2 +- 5 files changed, 7 insertions(+), 28 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 0e750fd..21144ea 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1091,7 +1091,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $signature = base64_encode($rawSignature); // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); + $this->curl->addHeader('x-m-webhook-signature', $signature); $payloadJson = json_encode($payload); $this->_logger->info('Webhooks payload: ' . $payloadJson); @@ -1108,20 +1108,13 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car $response = json_decode($responseBody, true); - if ($statusCode == 200 && isset($response['success']) && $response['success'] === true) { - $this->_logger->info('Webhook validation successful.'); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); - return true; - } else { - $this->_logger->error('Webhook validation failed: ' . ($response['message'] ?? 'Unknown error')); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); - return false; + if ($statusCode != 200) { + throw new LocalizedException(__('Status code from BobGo: %1', $statusCode)); } } catch (\Exception $e) { - $this->_logger->error('Webhook validation exception: ' . $e->getMessage()); -// throw new LocalizedException(__('Rates received but id field is empty or invalid.')); return false; } + return true; } } diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index a7e5d2e..adf83a2 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -26,5 +26,5 @@ class UData * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel'; + public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; } diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 9cbde5d..8362feb 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -73,10 +73,8 @@ class ConfigChangeObserver implements ObserverInterface } // Test for webhooks - if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) { - $this->logger->info('Webhooks test start: '); + if ((is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) || (is_array($changedPaths) && in_array('carriers/bobgo/webhook_key', $changedPaths))) { $result = $this->bobGo->triggerWebhookTest(); - $this->logger->info('Webhooks test end: ' . $result); if ($this->bobGo->isWebhookEnabled()) { if ($result) { diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php index 3c19823..2684798 100644 --- a/Observer/ModifyShippingDescription.php +++ b/Observer/ModifyShippingDescription.php @@ -25,26 +25,14 @@ class ModifyShippingDescription implements ObserverInterface // Get the current shipping description $shippingDescription = $order->getShippingDescription(); - // Log the original shipping description - $this->logger->info('Original Shipping Description: ' . $shippingDescription); - - // Get the shipping method used in the order (e.g., bobgo_10303_39_1) - $shippingMethod = $order->getShippingMethod(); - // Get the method title from the shipping description (which might already include the title) $methodTitle = $this->extractMethodTitle($shippingDescription); - // Log the method title for debugging - $this->logger->info('Extracted Method Title: ' . $methodTitle); - // Set the new dynamic shipping description based only on MethodTitle $newDescription = $methodTitle; // Update the shipping description in the order $order->setShippingDescription($newDescription); - - // Optionally log the new shipping description - $this->logger->info('Updated Shipping Description: ' . $newDescription); } /** diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 6a5163f..75767cc 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -66,7 +66,7 @@ abstract class OrderWebhookBase implements ObserverInterface // Set headers and post the data $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('X-M-Webhook-Signature', $signature); + $this->curl->addHeader('x-m-webhook-signature', $signature); // Perform the API request $payloadJson = json_encode($data); -- GitLab From c86e1b1026ffe7ae2dcaa56c94cc306afe505ebf Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Thu, 3 Oct 2024 14:09:45 +0200 Subject: [PATCH 38/56] cleanup --- Model/Carrier/BobGo.php | 46 +++++++++++--------------- Observer/ModifyShippingDescription.php | 4 +-- Observer/OrderWebhookBase.php | 27 ++------------- 3 files changed, 23 insertions(+), 54 deletions(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 21144ea..1260bbf 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1066,14 +1066,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - $isEnabled = $this->scopeConfig->getValue( - 'carriers/bobgo/enable_webhooks', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - // Convert the string to a boolean value - $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN); - + $isEnabled = $this->isWebhookEnabled(); $storeId = strval($this->_storeManager->getStore()->getId()); @@ -1085,28 +1079,9 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car ]; try { - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('x-m-webhook-signature', $signature); - - $payloadJson = json_encode($payload); - $this->_logger->info('Webhooks payload: ' . $payloadJson); - if ($payloadJson === false) { - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($this->getWebhookUrl(), $payloadJson); + $this->encodeWebhookAndPostRequest($this->getWebhookUrl(), $payload, $storeId, $webhookKey); $statusCode = $this->curl->getStatus(); - $this->_logger->info('Webhooks statuscode: ' . $statusCode); $responseBody = $this->curl->getBody(); - $this->_logger->info('Webhooks response: ' . $responseBody); - - $response = json_decode($responseBody, true); if ($statusCode != 200) { throw new LocalizedException(__('Status code from BobGo: %1', $statusCode)); @@ -1117,4 +1092,21 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car return true; } + public function encodeWebhookAndPostRequest($url, $data, $storeId, $webhookKey) { + // Generate the HMAC-SHA256 hash as raw binary data + $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); + // Encode the binary data in Base64 + $signature = base64_encode($rawSignature); + // Set headers and post the data + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->addHeader('x-m-webhook-signature', $signature); + + $payloadJson = json_encode($data); + if ($payloadJson === false) { + throw new \RuntimeException('Failed to encode payload to JSON.'); + } + + $this->curl->addHeader('Content-Type', 'application/json'); + $this->curl->post($url, $payloadJson); + } } diff --git a/Observer/ModifyShippingDescription.php b/Observer/ModifyShippingDescription.php index 2684798..2e443af 100644 --- a/Observer/ModifyShippingDescription.php +++ b/Observer/ModifyShippingDescription.php @@ -25,10 +25,10 @@ class ModifyShippingDescription implements ObserverInterface // Get the current shipping description $shippingDescription = $order->getShippingDescription(); - // Get the method title from the shipping description (which might already include the title) + // Get the method title from the shipping description $methodTitle = $this->extractMethodTitle($shippingDescription); - // Set the new dynamic shipping description based only on MethodTitle + // Set the new shipping description based only on MethodTitle $newDescription = $methodTitle; // Update the shipping description in the order diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 75767cc..62988c4 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -50,33 +50,10 @@ abstract class OrderWebhookBase implements ObserverInterface 'webhooks_enabled' => true, // If we get to this point webhooks are enabled ]; - // Send the webhook - $this->makeHttpPostRequest($url, $data, $storeId); - } - - private function makeHttpPostRequest($url, $data, $storeId) - { // Generate the signature using the webhook key saved in config $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - // Generate the HMAC-SHA256 hash as raw binary data - $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true); - - // Encode the binary data in Base64 - $signature = base64_encode($rawSignature); - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->addHeader('x-m-webhook-signature', $signature); - - // Perform the API request - $payloadJson = json_encode($data); - if ($payloadJson === false) { - throw new \RuntimeException('Failed to encode payload to JSON.'); - } - - // Set headers and post the data - $this->curl->addHeader('Content-Type', 'application/json'); - $this->curl->post($url, $payloadJson); + // Send the webhook + $this->bobGo->encodeWebhookAndPostRequest($url, $data, $storeId, $webhookKey); } private function getWebhookUrl(): string -- GitLab From d32f2f6d791338936ff199403a9bc9bbc63f3d71 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 14 Oct 2024 14:34:28 +0200 Subject: [PATCH 39/56] consumer secret key as webhook key --- Observer/ConfigChangeObserver.php | 3 +-- etc/adminhtml/system.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php index 8362feb..efc0179 100644 --- a/Observer/ConfigChangeObserver.php +++ b/Observer/ConfigChangeObserver.php @@ -84,8 +84,7 @@ class ConfigChangeObserver implements ObserverInterface } else { $this->messageManager->addErrorMessage( __('Webhook validation failed. Please check your internet connection - and get the webhook key for your channel on Bob Go sales channels page. - https://my.bobgo.co.za/sales-channels') + and use your Bob Go integration consumer secret key for webhook validation.') ); } } diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 0483f32..4d6ee89 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -39,7 +39,7 @@ <!-- Webhook Key Input Field --> <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook key</label> - <comment>Enter the webhook key for authentication.</comment> + <comment>Enter Bob Go integration consumer secret key for webhook authentication.</comment> <depends> <field id="enable_webhooks">1</field> </depends> -- GitLab From 622419426b4ffdf84d76b0e754a81c9bb7b28fcf Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 16 Oct 2024 08:28:31 +0200 Subject: [PATCH 40/56] merge with dev --- etc/adminhtml/system.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 4d6ee89..16f3d83 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -44,11 +44,11 @@ <field id="enable_webhooks">1</field> </depends> </field> - <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Enable Track My Order</label> - <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> + <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> + <label>Enable Track My Order</label> + <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> </section> </system> -- GitLab From 1e1dae675e81ada59e7856e6f486cdbd83ed85bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Thu, 24 Oct 2024 11:25:24 +0200 Subject: [PATCH 41/56] Update the readme --- ...ob Go shipping extension for Magento 2.pdf | Bin 0 -> 120911 bytes Readme.md | 98 +----------------- 2 files changed, 2 insertions(+), 96 deletions(-) create mode 100644 Installation guide - Bob Go shipping extension for Magento 2.pdf diff --git a/Installation guide - Bob Go shipping extension for Magento 2.pdf b/Installation guide - Bob Go shipping extension for Magento 2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2b410bed7e7f035526b31166ae6838292cd7d39b GIT binary patch literal 120911 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 literal 0 HcmV?d00001 diff --git a/Readme.md b/Readme.md index fcb6fb8..e85f78f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,97 +1,3 @@ -# Magento 2 Bob Go Shipping Extension +# Bob Go shipping extension for Magento 2 -## Introduction - -A complete guide to install Magento Bob Go Shipping extension in Magento 2. - -## Features ->This extension allows you to get real-time shipping rates from Bob Go shipping services and display them to your customers during checkout. - ->This extension also allows you to track shipments and get delivery status updates from Bob Go shipping services. - -## How to install Magento 2 Bob Go Shipping Extension - -### Option 1 (recommended): Install via composer - -Run the following command in Magento 2 root folder:</br> - ->_Note: You must have composer installed on your server for this option_ - -#### 1. Execute the following command to install the module: - -``` -composer require bobgo/bobgo-magento-extension -``` -#### 2. Enter following commands to enable the module: - -``` -bin/magento module:enable BobGroup_BobGo -bin/magento cache:clean -bin/magento cache:flush -bin/magento setup:upgrade -bin/magento setup:di:compile -bin/magento setup:static-content:deploy -``` - -### Option 2: Install via zip file - -1. Download the extension zip file from the link below: </br> - - <a href="https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/prod/bobgo-magento-extension-prod.zip"> Download Magento 2 Bob Go Shipping Extension </a> - -2. Unzip the file and copy contents - -3. Create `BobGroup/BobGo` <em>Directory</em> - -**It should look like this:** </br> ->{Magento root}/app/code/BobGroup/BobGo/ - - ->**{Magento root}**`/app/code/BobGroup/BobGo/`**{Paste here}** - - -4. Go to Magento root folder and run all commands below to install `BobGroup_BobGo`: </br> -``` -bin/magento cache:clean -bin/magento cache:flush -bin/magento setup:upgrade -bin/magento setup:di:compile -bin/magento setup:static-content:deploy -``` -_____________________________________________________________________________________________________________________ -# After installation - - -## How to configure Magento 2 Bob Go Shipping Extension - -### ✓ Step 1: Create an account on Bob Go - -You need to create an account on Bob Go to get your store identified by the API. - -Please visit [Bob Go](https://bobgo.co.za) to create an account. - -### ✓ Step 2: Integrate Magento and Bob Go - -1. In the Magento admin portal click on `System > Integrations` and click on `Add New Integration` -2. Under `Basic Settings > Integration Info` fill in the name of the integration eg. Bob Go -3. Under `Basic Settings > API` select `All` for Resource access -4. Click `Save` and enter your Magento admin portal password -5. The `Integration Details` will be displayed on the page -6. On [Bob Go](https://bobgo.co.za) go to `Sales channels > Add channel > Magento` and enter the Magento integration details from step 5 and click `Grant access` -7. On Bob Go go to `Rates at checkout > Settings > Installed channels` and enable your channel for rates at checkout -8. Make sure you have service levels configured and enabled on Bob Go `Rates at checkout > Service levels` - -### ✓ Step 3: Login to Magento Admin - -1. In the Magento admin portal click on `Stores > Configuration > Sales > Delivery Methods` -2. Go to the Bob Go delivery method and enable Bob Go rates at checkout -3. Click `Save Config` -4. A message at the top of the screen will confirm that rates at checkout is enabled correctly: `Bob Go rates at checkout connected.` - -#### Admin Configurations: Update Store Information* - -When the extension is **installed** and **enabled**, a new field will be created in `Store Information` : `Suburb`. Enter the necessary details: - -1. `Stores`> `Configuration`> `General`>`General` -2. `Store Information`>`Suburb` -3. Update the store information and click `Save config` +See the installation guide for information: [Installation Guide](./Installation guide - Bob Go shipping extension for Magento 2.pdf) \ No newline at end of file -- GitLab From 41203e3dbd2035ae1dd8ec709612e4519dcf6991 Mon Sep 17 00:00:00 2001 From: Arno Rossouw <arno@bob.co.za> Date: Thu, 31 Oct 2024 13:43:55 +0200 Subject: [PATCH 42/56] 5-INFRASTRUCTURE :: husky for auto versioning and build on dev when not tag --- .distignore | 11 ++++ .gitignore | 2 + .gitlab-ci.yml | 32 +++++----- .husky/pre-commit | 1 + composer.json | 2 +- etc/module.xml | 2 +- make-zip.sh | 153 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 ++++++ update-version.js | 36 +++++++++++ 9 files changed, 238 insertions(+), 20 deletions(-) create mode 100644 .distignore create mode 100644 .gitignore create mode 100755 .husky/pre-commit create mode 100755 make-zip.sh create mode 100644 package.json create mode 100644 update-version.js diff --git a/.distignore b/.distignore new file mode 100644 index 0000000..e5dc85b --- /dev/null +++ b/.distignore @@ -0,0 +1,11 @@ +*.pdf +node_modules +.git +package.json +.husky +package-lock.json +.distignore +make-zip.sh +update-version.js +.gitlab-ci.yml +.gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..918863b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.zip diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de5cdd5..bc49012 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,24 +4,20 @@ variables: GIT_SUBMODULE_STRATEGY: recursive stages: - - tagged_release + - deploy -tagged_deploy: - stage: tagged_release - only: - - tags +deploy: + stage: deploy before_script: - # - AWS_ACCESS_KEY_ID_KEY=$(echo "$AWS_ACCESS_KEY_ID") - # - AWS_SECRET_ACCESS_KEY_KEY=$(echo "$AWS_SECRET_ACCESS_KEY") - # - AWS_ACCESS_KEY_ID=$(eval echo -e "\$$AWS_ACCESS_KEY_ID_KEY") - # - AWS_SECRET_ACCESS_KEY=$(eval echo -e "\$$AWS_SECRET_ACCESS_KEY_KEY") - # - export AWS_ACCESS_KEY_ID - # - export AWS_SECRET_ACCESS_KEY + - AWS_ACCESS_KEY_ID_KEY=$(echo "$CI_COMMIT_BRANCH"_"AWS_ACCESS_KEY_ID") + - AWS_ACCESS_KEY_ID=$(eval echo -e "\$$AWS_ACCESS_KEY_ID_KEY") + - AWS_SECRET_ACCESS_KEY_KEY=$(echo "$CI_COMMIT_BRANCH"_"AWS_SECRET_ACCESS_KEY") + - AWS_SECRET_ACCESS_KEY=$(eval echo -e "\$$AWS_SECRET_ACCESS_KEY_KEY") + - export AWS_ACCESS_KEY_ID + - export AWS_SECRET_ACCESS_KEY script: - # - echo $AWS_ACCESS_KEY_ID - # - echo $AWS_SECRET_ACCESS_KEY - - wget https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/"$CI_COMMIT_TAG"/bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz - - ls -al - - aws s3 cp bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz s3://bobgo-s3-magento-plugin/ --region=af-south-1 - allow_failure: false - when: on_success \ No newline at end of file + - ./make-zip.sh + - aws s3 cp bobgo-magento-plugin.zip s3://bobgo-s3-magento-plugin/ --region=af-south-1 + rules: + - if: '$CI_COMMIT_BRANCH == "dev" && $CI_COMMIT_TAG == null' + when: always diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..0e8cbb0 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm config set git-tag-version false && npm version patch && npm run update-version-files && git add package.json etc/module.xml composer.json diff --git a/composer.json b/composer.json index 637ad87..07aad65 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.33", + "version": "1.0.34", "authors": [ { "name": "Bob Go", diff --git a/etc/module.xml b/etc/module.xml index fe2b36d..d94e8d2 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.0"> + <module name="BobGroup_BobGo" setup_version="1.0.34"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/make-zip.sh b/make-zip.sh new file mode 100755 index 0000000..6385066 --- /dev/null +++ b/make-zip.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status +# +set -e + +# Function to log messages +log() { + echo "** $1" +} + +# Function to install jq if not installed +install_jq() { + if command -v jq >/dev/null 2>&1; then + log "jq is already installed." + else + log "jq is not installed. Attempting to install..." + + if [[ "$OSTYPE" == "darwin"* ]]; then + if command -v brew >/dev/null 2>&1; then + log "Installing jq using Homebrew..." + brew install jq + else + log "Homebrew is not installed. Please install Homebrew first:" + log "https://brew.sh/" + exit 1 + fi + elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + if command -v apt-get >/dev/null 2>&1; then + log "Installing jq using apt-get..." + sudo apt-get update + sudo apt-get install -y jq + else + log "apt-get not found. Please install jq manually." + exit 1 + fi + else + log "Unsupported OS. Please install jq manually." + exit 1 + fi + + if command -v jq >/dev/null 2>&1; then + log "jq installed successfully." + else + log "Failed to install jq. Please install it manually." + exit 1 + fi + fi +} + +# Function to ensure Perl is installed +install_perl() { + if command -v perl >/dev/null 2>&1; then + log "Perl is already installed." + else + log "Perl is not installed. Please install Perl to proceed." + exit 1 + fi +} + +# Function to extract version using jq +get_version() { + if command -v jq >/dev/null 2>&1; then + jq -r '.version' package.json + else + # Fallback to grep and awk if jq is not available + grep '"version":' package.json | head -1 | awk -F'"' '{print $4}' + fi +} + +# Function to update the 'Version:' header in bobpay-plugin.php using Perl +update_version_header() { + local VERSION=$1 + local FILE="composer.json" + + log "Updating 'version:' header in ${FILE} using Perl..." + + # Use Perl to update the 'Version:' line + perl -pi -e "s/(\"version\":\s*\")([0-9.]+)(\"\,)/\${1}${VERSION}\"\,/g" "$FILE" + log "'version:' header updated to ${VERSION}." +} + +update_define_constant() { + local VERSION=$1 + local FILE="etc/module.xml" + + log "Updating 'version' constant in ${FILE} using Perl..." + + # Use Perl to replace the version constant + perl -pi -e "s/(setup_version=\")([^\"]*)/\${1}${VERSION}/g" "$FILE" + log "'setup_version' constant updated to ${VERSION}." +} + +# Function to build the plugin +build_plugin() { + log "Building plugin..." + npm install + log "Plugin built successfully." +} + +# Function to create the zip package +create_zip() { + local VERSION=$1 + local ZIPNAME="bobgo-magento-plugin.zip" + local PACKAGE_DIR="package" + + log "Creating zip file: ${ZIPNAME}..." + + # Remove any existing package directory and zip file + rm -rf "${PACKAGE_DIR}" + rm -f "${ZIPNAME}" + + # Create the package directory + mkdir -p "${PACKAGE_DIR}" + + # Use rsync to copy files, excluding those in .distignore + log "Copying files to package directory..." + rsync -av --exclude-from='.distignore' ./ "${PACKAGE_DIR}/" + + # Create the zip file from the package directory + log "Creating zip file..." + cd "${PACKAGE_DIR}" + zip -r "../${ZIPNAME}" . + cd .. + + # Clean up the package directory + log "Cleaning up..." + rm -rf "${PACKAGE_DIR}" + + log "Zip file '${ZIPNAME}' created successfully." +} + +# Main script execution +main() { + log "Starting build process..." + + install_jq + install_perl + + VERSION=$(get_version) + log "Extracted version: ${VERSION}" + + build_plugin + update_version_header "${VERSION}" + update_define_constant "${VERSION}" + create_zip "${VERSION}" + + log "Build process completed successfully." +} + +# Execute the main function +main + diff --git a/package.json b/package.json new file mode 100644 index 0000000..b5b8706 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "bobgo-magento-plugin", + "description": "Bob Go magento plugin", + "version": "1.0.34", + "license": "GPL-2.0-or-later", + "scripts": { + "prepare": "husky install", + "update-version-files": "node update-version.js" + }, + "husky": { + "hooks": { + "pre-commit": "npm config set git-tag-version false && npm version patch && npm run update-version-files && git add package.json etc/module.xml composer.json && pretty-quick --staged" + } + }, + "devDependencies": { + "husky": "^8.0.1", + "npm-run-all": "^4.1.5" + } +} diff --git a/update-version.js b/update-version.js new file mode 100644 index 0000000..f5a9606 --- /dev/null +++ b/update-version.js @@ -0,0 +1,36 @@ +const fs = require('fs'); + +// Read version from package.json +const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); +const version = packageJson.version; + +// Files to update +const filesToUpdate = ['composer.json', 'etc/module.xml']; + +// Function to update version in files +function updateVersionInFile(filePath) { + let content = fs.readFileSync(filePath, 'utf8'); + + if (filePath === 'composer.json') { + // Update the Version header in the plugin file + // Update the version in composer.json + content = content.replace( + /(\"version\":\s*\")([0-9.]+)(\"\,)/g, + `$1${version}$3` + ); + } + + if (filePath === 'etc/module.xml') { + // Update the version tag in etc/module.xml + content = content.replace( + /(setup_version=")([^"]*)/g, + `$1${version}` + ); + + } + + fs.writeFileSync(filePath, content); +} + +// Update each file +filesToUpdate.forEach(updateVersionInFile); -- GitLab From 7e4c1824dd6fe0e3b0088c9debed73795cf361b7 Mon Sep 17 00:00:00 2001 From: Arno Rossouw <arno@bob.co.za> Date: Mon, 4 Nov 2024 09:47:22 +0200 Subject: [PATCH 43/56] 7-INFRASTRUCTURE :: exclude package dir from make-zip.sh --- .distignore | 1 + composer.json | 2 +- etc/module.xml | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.distignore b/.distignore index e5dc85b..e06a047 100644 --- a/.distignore +++ b/.distignore @@ -9,3 +9,4 @@ make-zip.sh update-version.js .gitlab-ci.yml .gitignore +package diff --git a/composer.json b/composer.json index 07aad65..7962a85 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.34", + "version": "1.0.36", "authors": [ { "name": "Bob Go", diff --git a/etc/module.xml b/etc/module.xml index d94e8d2..6a1ae04 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.34"> + <module name="BobGroup_BobGo" setup_version="1.0.36"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package.json b/package.json index b5b8706..91a85bc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.34", + "version": "1.0.36", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab From b023bc7ee3e5b2c619cc7f72d14a181128d5e44e Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 11 Nov 2024 14:00:48 +0200 Subject: [PATCH 44/56] hide tracking page --- etc/adminhtml/system.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 16f3d83..1d01da5 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -44,7 +44,9 @@ <field id="enable_webhooks">1</field> </depends> </field> - <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> +<!-- Hiding the `Enable Track My Order` setting for now--> +<!-- To show the setting again `showInDefault="1" showInWebsite="1" showInStore="1"` --> + <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0"> <label>Enable Track My Order</label> <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> -- GitLab From 0869b518d710ff483655d977e205949bad1ab43f Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 11 Nov 2024 14:11:24 +0200 Subject: [PATCH 45/56] hide tracking page --- etc/adminhtml/system.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 1d01da5..c08fe4d 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -45,12 +45,11 @@ </depends> </field> <!-- Hiding the `Enable Track My Order` setting for now--> -<!-- To show the setting again `showInDefault="1" showInWebsite="1" showInStore="1"` --> - <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0"> - <label>Enable Track My Order</label> - <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> +<!-- <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0">--> +<!-- <label>Enable Track My Order</label>--> +<!-- <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment>--> +<!-- <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>--> +<!-- </field>--> </group> </section> </system> -- GitLab From ce4c61daf52a1652ad2ba3d6dff84c1153d1a683 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Mon, 11 Nov 2024 15:43:45 +0200 Subject: [PATCH 46/56] hide tracking page --- etc/adminhtml/system.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index c08fe4d..81e9649 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -45,11 +45,11 @@ </depends> </field> <!-- Hiding the `Enable Track My Order` setting for now--> -<!-- <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0">--> -<!-- <label>Enable Track My Order</label>--> -<!-- <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment>--> -<!-- <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>--> -<!-- </field>--> + <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0"> + <label>Enable Track My Order</label> + <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> </group> </section> </system> -- GitLab From a092e7cc9bfb79d94f27535a560a9be37991ab59 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Tue, 12 Nov 2024 12:28:03 +0200 Subject: [PATCH 47/56] add check for empty webhook key --- Model/Carrier/BobGo.php | 5 +++++ Observer/OrderWebhookBase.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 1260bbf..d269be1 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -1066,6 +1066,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); + // Check if the webhook key is empty and return false + if (empty($webhookKey)) { + return false; + } + // Convert the string to a boolean value $isEnabled = $this->isWebhookEnabled(); diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php index 62988c4..311daa7 100644 --- a/Observer/OrderWebhookBase.php +++ b/Observer/OrderWebhookBase.php @@ -52,6 +52,11 @@ abstract class OrderWebhookBase implements ObserverInterface // Generate the signature using the webhook key saved in config $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + // Check if the webhook key is empty and return false + if (empty($webhookKey)) { + return; + } + // Send the webhook $this->bobGo->encodeWebhookAndPostRequest($url, $data, $storeId, $webhookKey); } -- GitLab From e08d0c00f4e1a1c063a5876f856902bb47de154c Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Wed, 2 Apr 2025 11:58:31 +0200 Subject: [PATCH 48/56] #19 - prevent null values for allowed methods --- Model/Carrier/BobGo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index d269be1..938e847 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -535,7 +535,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car public function getAllowedMethods(): array { $allowedMethods = $this->getConfigData('allowed_methods'); - if ($allowedMethods === false) { + if ($allowedMethods === false || $allowedMethods === null || trim($allowedMethods) === '') { return []; // Return an empty array if no allowed methods are configured } -- GitLab From 9a2c1137a6dde6ed5d3c2369017a375a1b3ad156 Mon Sep 17 00:00:00 2001 From: "@ChristelLoftus" <christel@bob.co.za> Date: Fri, 4 Apr 2025 07:52:45 +0200 Subject: [PATCH 49/56] #19 - use if empty instead to cater for all 3 checks --- Model/Carrier/BobGo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php index 938e847..12e46e6 100644 --- a/Model/Carrier/BobGo.php +++ b/Model/Carrier/BobGo.php @@ -535,7 +535,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car public function getAllowedMethods(): array { $allowedMethods = $this->getConfigData('allowed_methods'); - if ($allowedMethods === false || $allowedMethods === null || trim($allowedMethods) === '') { + if (empty($allowedMethods)) { return []; // Return an empty array if no allowed methods are configured } -- GitLab From 88aeeb20fcb64b6085bd6117bc94ebdbb64450ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 09:48:19 +0200 Subject: [PATCH 50/56] ADHOC :: Wording changes --- etc/adminhtml/system.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 81e9649..5850116 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -33,13 +33,13 @@ <field id="enable_webhooks" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enable webhooks</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>Enable or disable the webhook functionality for Bob Go.</comment> + <comment>When enabled, webhooks will be sent to Bob Go to notify about order creation and updates, ensuring real-time synchronization between your store and Bob Go.</comment> </field> <!-- Webhook Key Input Field --> <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Webhook key</label> - <comment>Enter Bob Go integration consumer secret key for webhook authentication.</comment> + <label>Webhook authentication key</label> + <comment>Enter your Bob Go integration API consumer secret key for webhook authentication.</comment> <depends> <field id="enable_webhooks">1</field> </depends> -- GitLab From 97ef02f79c41dec35036e414bc7ba766d45c001b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:08:13 +0200 Subject: [PATCH 51/56] ADHOC :: Remove check to only show webhook key if webhooks are enabled --- .gitignore | 2 ++ etc/adminhtml/system.xml | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 918863b..6450684 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules *.zip +.idea +/.idea/ diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5850116..1b718c6 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -40,9 +40,6 @@ <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook authentication key</label> <comment>Enter your Bob Go integration API consumer secret key for webhook authentication.</comment> - <depends> - <field id="enable_webhooks">1</field> - </depends> </field> <!-- Hiding the `Enable Track My Order` setting for now--> <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0"> -- GitLab From 66b4162c5e284c2714b80489892e4cef74879403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:11:50 +0200 Subject: [PATCH 52/56] ADHOC :: Wording change --- composer.json | 2 +- etc/adminhtml/system.xml | 4 +- etc/module.xml | 2 +- package-lock.json | 1901 ++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 5 files changed, 1906 insertions(+), 5 deletions(-) create mode 100644 package-lock.json diff --git a/composer.json b/composer.json index 7962a85..af2ae11 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.36", + "version": "1.0.37", "authors": [ { "name": "Bob Go", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 1b718c6..a834ac2 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -20,7 +20,7 @@ </field> <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enable Bob Go rates at checkout</label> - <comment>When this setting is enabled, your customers will be presented with shipping rates at checkout, as configured on the Bob Go platform under Rates at checkout.</comment> + <comment>When enabled, your customers will be presented with shipping rates at checkout, as configured on the Bob Go platform under Rates at checkout.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="additional_info" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1"> @@ -43,7 +43,7 @@ </field> <!-- Hiding the `Enable Track My Order` setting for now--> <field id="enable_track_order" translate="label" type="select" sortOrder="10" showInDefault="0" showInWebsite="0" showInStore="0"> - <label>Enable Track My Order</label> + <label>Enable Track my order</label> <comment>When this setting is enabled, your customers will be presented with a page to track orders.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/etc/module.xml b/etc/module.xml index 6a1ae04..d49e765 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.36"> + <module name="BobGroup_BobGo" setup_version="1.0.37"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a908a08 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1901 @@ +{ + "name": "bobgo-magento-plugin", + "version": "1.0.37", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bobgo-magento-plugin", + "version": "1.0.37", + "license": "GPL-2.0-or-later", + "devDependencies": { + "husky": "^8.0.1", + "npm-run-all": "^4.1.5" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + } + } +} diff --git a/package.json b/package.json index 91a85bc..bb53460 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.36", + "version": "1.0.37", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab From 127b8912786df897b96bdd5770b0961eb1792db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:12:17 +0200 Subject: [PATCH 53/56] ADHOC :: Wording change --- composer.json | 2 +- etc/adminhtml/system.xml | 2 +- etc/module.xml | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index af2ae11..59cf095 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.37", + "version": "1.0.38", "authors": [ { "name": "Bob Go", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index a834ac2..03d7787 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -33,7 +33,7 @@ <field id="enable_webhooks" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Enable webhooks</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>When enabled, webhooks will be sent to Bob Go to notify about order creation and updates, ensuring real-time synchronization between your store and Bob Go.</comment> + <comment>When this setting is enabled, webhooks will be sent to Bob Go to notify about order creation and updates, ensuring real-time synchronization between your store and Bob Go.</comment> </field> <!-- Webhook Key Input Field --> diff --git a/etc/module.xml b/etc/module.xml index d49e765..b6cd413 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.37"> + <module name="BobGroup_BobGo" setup_version="1.0.38"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package.json b/package.json index bb53460..0b6984b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.37", + "version": "1.0.38", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab From 7747491f52596730b8ca8e58c53c916bec00e517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:12:25 +0200 Subject: [PATCH 54/56] ADHOC :: Wording change --- composer.json | 2 +- etc/module.xml | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 59cf095..01abbbf 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.38", + "version": "1.0.39", "authors": [ { "name": "Bob Go", diff --git a/etc/module.xml b/etc/module.xml index b6cd413..81828e8 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.38"> + <module name="BobGroup_BobGo" setup_version="1.0.39"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package-lock.json b/package-lock.json index a908a08..f622b48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bobgo-magento-plugin", - "version": "1.0.37", + "version": "1.0.38", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bobgo-magento-plugin", - "version": "1.0.37", + "version": "1.0.38", "license": "GPL-2.0-or-later", "devDependencies": { "husky": "^8.0.1", diff --git a/package.json b/package.json index 0b6984b..50a44a9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.38", + "version": "1.0.39", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab From 6eb8f023e15ce9f2cd09b3c36cbf5e95db56cb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:13:29 +0200 Subject: [PATCH 55/56] ADHOC :: Remove package lock --- composer.json | 2 +- etc/module.xml | 2 +- package-lock.json | 1901 --------------------------------------------- package.json | 2 +- 4 files changed, 3 insertions(+), 1904 deletions(-) delete mode 100644 package-lock.json diff --git a/composer.json b/composer.json index 01abbbf..9b2166f 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.39", + "version": "1.0.40", "authors": [ { "name": "Bob Go", diff --git a/etc/module.xml b/etc/module.xml index 81828e8..962488e 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.39"> + <module name="BobGroup_BobGo" setup_version="1.0.40"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f622b48..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1901 +0,0 @@ -{ - "name": "bobgo-magento-plugin", - "version": "1.0.38", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "bobgo-magento-plugin", - "version": "1.0.38", - "license": "GPL-2.0-or-later", - "devDependencies": { - "husky": "^8.0.1", - "npm-run-all": "^4.1.5" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true - }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - } - } -} diff --git a/package.json b/package.json index 50a44a9..0bb7580 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.39", + "version": "1.0.40", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab From edfd9d93db2232c95b0ebab7348ade3479bf3d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franc=C3=A9=20Wilke?= <francewilke@gmail.com> Date: Wed, 23 Apr 2025 10:48:36 +0200 Subject: [PATCH 56/56] ADHOC :: Update the endpoints --- Model/Carrier/UData.php | 6 +++--- composer.json | 2 +- etc/module.xml | 2 +- package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php index adf83a2..bbb17dd 100644 --- a/Model/Carrier/UData.php +++ b/Model/Carrier/UData.php @@ -12,19 +12,19 @@ class UData * * @var string */ - public const TRACKING = 'https://api.dev.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; + public const TRACKING = 'https://api.bobgo.co.za/tracking?channel=%s&tracking_reference=%s'; /** * Rates API Endpoint * * @var string */ - public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento'; + public const RATES_ENDPOINT = 'https://api.bobgo.co.za/rates-at-checkout/magento'; /** * Order create/update webhook URL * * @var string */ - public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento'; + public const WEBHOOK_URL = 'https://api.bobgo.co.za/webhook/channel/magento'; } diff --git a/composer.json b/composer.json index 9b2166f..83317ac 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bobgo/bobgo-magento-extension", "description": "Smart shipping and order management solution in South Africa", "type": "magento2-module", - "version": "1.0.40", + "version": "1.0.41", "authors": [ { "name": "Bob Go", diff --git a/etc/module.xml b/etc/module.xml index 962488e..a79063f 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -7,7 +7,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="BobGroup_BobGo" setup_version="1.0.40"> + <module name="BobGroup_BobGo" setup_version="1.0.41"> <sequence> <module name="Magento_Webapi"/> <module name="Magento_Catalog"/> diff --git a/package.json b/package.json index 0bb7580..e44822d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bobgo-magento-plugin", "description": "Bob Go magento plugin", - "version": "1.0.40", + "version": "1.0.41", "license": "GPL-2.0-or-later", "scripts": { "prepare": "husky install", -- GitLab