From 6b833d178d0fc2b42d6bbd925699c7e678c6ec6f Mon Sep 17 00:00:00 2001 From: Mark Garratt Date: Wed, 15 Jan 2025 20:36:40 +0000 Subject: [PATCH] Begin refactoring WebhookRunner into WebhookService The plan is to enable multiple webhooks to be called for different events which will allow integration with other services without having to poll for changes. --- controllers/BaseController.php | 6 ++ controllers/BatteriesApiController.php | 11 +-- controllers/ChoresApiController.php | 11 +-- controllers/RecipesApiController.php | 11 +-- controllers/StockApiController.php | 20 ++--- helpers/WebhookRunner.php | 48 ----------- services/BaseService.php | 5 ++ services/StockService.php | 51 +++++------ services/WebhookService.php | 113 +++++++++++++++++++++++++ 9 files changed, 165 insertions(+), 111 deletions(-) delete mode 100644 helpers/WebhookRunner.php create mode 100644 services/WebhookService.php diff --git a/controllers/BaseController.php b/controllers/BaseController.php index d948df20..8193624f 100644 --- a/controllers/BaseController.php +++ b/controllers/BaseController.php @@ -18,6 +18,7 @@ use Grocy\Services\StockService; use Grocy\Services\TasksService; use Grocy\Services\UserfieldsService; use Grocy\Services\UsersService; +use Grocy\Services\WebhookService; use DI\Container; class BaseController @@ -116,6 +117,11 @@ class BaseController return UsersService::getInstance(); } + protected function getWebhookService() + { + return WebhookService::getInstance(); + } + protected function render($response, $viewName, $data = []) { $container = $this->AppContainer; diff --git a/controllers/BatteriesApiController.php b/controllers/BatteriesApiController.php index b5cd7430..6d71df29 100644 --- a/controllers/BatteriesApiController.php +++ b/controllers/BatteriesApiController.php @@ -3,8 +3,8 @@ namespace Grocy\Controllers; use Grocy\Controllers\Users\User; -use Grocy\Helpers\WebhookRunner; use Grocy\Helpers\Grocycode; +use Grocy\Services\WebhookService; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -71,15 +71,12 @@ class BatteriesApiController extends BaseApiController { $battery = $this->getDatabase()->batteries()->where('id', $args['batteryId'])->fetch(); - $webhookData = array_merge([ + $webhookData = [ 'battery' => $battery->name, 'grocycode' => (string)(new Grocycode(Grocycode::BATTERY, $args['batteryId'])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; - if (GROCY_LABEL_PRINTER_RUN_SERVER) - { - (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); - } + $this->getWebhookService()->run(WebhookService::EVENT_BATTERY_PRINT_LABEL, $webhookData); return $this->ApiResponse($response, $webhookData); } diff --git a/controllers/ChoresApiController.php b/controllers/ChoresApiController.php index e8a6d2f7..82623373 100644 --- a/controllers/ChoresApiController.php +++ b/controllers/ChoresApiController.php @@ -3,8 +3,8 @@ namespace Grocy\Controllers; use Grocy\Controllers\Users\User; -use Grocy\Helpers\WebhookRunner; use Grocy\Helpers\Grocycode; +use Grocy\Services\WebhookService; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -122,15 +122,12 @@ class ChoresApiController extends BaseApiController { $chore = $this->getDatabase()->chores()->where('id', $args['choreId'])->fetch(); - $webhookData = array_merge([ + $webhookData = [ 'chore' => $chore->name, 'grocycode' => (string)(new Grocycode(Grocycode::CHORE, $args['choreId'])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; - if (GROCY_LABEL_PRINTER_RUN_SERVER) - { - (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); - } + $this->getWebhookService()->run(WebhookService::EVENT_CHORE_PRINT_LABEL, $webhookData); return $this->ApiResponse($response, $webhookData); } diff --git a/controllers/RecipesApiController.php b/controllers/RecipesApiController.php index 36e76020..aff5fdf5 100644 --- a/controllers/RecipesApiController.php +++ b/controllers/RecipesApiController.php @@ -3,8 +3,8 @@ namespace Grocy\Controllers; use Grocy\Controllers\Users\User; -use Grocy\Helpers\WebhookRunner; use Grocy\Helpers\Grocycode; +use Grocy\Services\WebhookService; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -87,15 +87,12 @@ class RecipesApiController extends BaseApiController { $recipe = $this->getDatabase()->recipes()->where('id', $args['recipeId'])->fetch(); - $webhookData = array_merge([ + $webhookData = [ 'recipe' => $recipe->name, 'grocycode' => (string)(new Grocycode(Grocycode::RECIPE, $args['recipeId'])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; - if (GROCY_LABEL_PRINTER_RUN_SERVER) - { - (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); - } + $this->getWebhookService()->run(WebhookService::EVENT_RECIPE_PRINT_LABEL, $webhookData); return $this->ApiResponse($response, $webhookData); } diff --git a/controllers/StockApiController.php b/controllers/StockApiController.php index 27fd5b26..c52f6da2 100644 --- a/controllers/StockApiController.php +++ b/controllers/StockApiController.php @@ -4,7 +4,7 @@ namespace Grocy\Controllers; use Grocy\Controllers\Users\User; use Grocy\Services\StockService; -use Grocy\Helpers\WebhookRunner; +use Grocy\Services\WebhookService; use Grocy\Helpers\Grocycode; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -674,15 +674,12 @@ class StockApiController extends BaseApiController { $product = $this->getDatabase()->products()->where('id', $args['productId'])->fetch(); - $webhookData = array_merge([ + $webhookData = [ 'product' => $product->name, 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $product->id)), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; - if (GROCY_LABEL_PRINTER_RUN_SERVER) - { - (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); - } + $this->getWebhookService()->run(WebhookService::EVENT_PRODUCT_PRINT_LABEL, $webhookData); return $this->ApiResponse($response, $webhookData); } @@ -699,20 +696,17 @@ class StockApiController extends BaseApiController $stockEntry = $this->getDatabase()->stock()->where('id', $args['entryId'])->fetch(); $product = $this->getDatabase()->products()->where('id', $stockEntry->product_id)->fetch(); - $webhookData = array_merge([ + $webhookData = [ 'product' => $product->name, 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $stockEntry->product_id, [$stockEntry->stock_id])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) { $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $stockEntry->best_before_date; } - if (GROCY_LABEL_PRINTER_RUN_SERVER) - { - (new WebhookRunner())->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); - } + $this->getWebhookService()->run(WebhookService::EVENT_STOCK_ENTRY_PRINT_LABEL, $webhookData); return $this->ApiResponse($response, $webhookData); } diff --git a/helpers/WebhookRunner.php b/helpers/WebhookRunner.php deleted file mode 100644 index 150155a5..00000000 --- a/helpers/WebhookRunner.php +++ /dev/null @@ -1,48 +0,0 @@ -client = new Client(['timeout' => 2.0]); - } - - private $client; - - public function run($url, $args, $json = false) - { - $reqArgs = []; - if ($json) - { - $reqArgs = ['json' => $args]; - } - else - { - $reqArgs = ['form_params' => $args]; - } - try - { - file_put_contents('php://stderr', 'Running Webhook: ' . $url . "\n" . print_r($reqArgs, true)); - - $this->client->request('POST', $url, $reqArgs); - } - catch (RequestException $e) - { - file_put_contents('php://stderr', 'Webhook failed: ' . $url . "\n" . $e->getMessage()); - } - } - - public function runAll($urls, $args) - { - foreach ($urls as $url) - { - $this->run($url, $args); - } - } -} diff --git a/services/BaseService.php b/services/BaseService.php index 13825068..aafee1d8 100644 --- a/services/BaseService.php +++ b/services/BaseService.php @@ -76,4 +76,9 @@ class BaseService { return ApplicationService::getInstance(); } + + protected function getWebhookService() + { + return WebhookService::getInstance(); + } } diff --git a/services/StockService.php b/services/StockService.php index 3c60714b..28d31725 100644 --- a/services/StockService.php +++ b/services/StockService.php @@ -3,7 +3,7 @@ namespace Grocy\Services; use Grocy\Helpers\Grocycode; -use Grocy\Helpers\WebhookRunner; +use Grocy\Services\WebhookService; use GuzzleHttp\Client; class StockService extends BaseService @@ -220,21 +220,17 @@ class StockService extends BaseService ]); $stockRow->save(); - if (GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER) + $webhookData = [ + 'product' => $productDetails->product->name, + 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockId])), + ]; + + if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) { - $webhookData = array_merge([ - 'product' => $productDetails->product->name, - 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockId])), - ], GROCY_LABEL_PRINTER_PARAMS); - - if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) - { - $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate; - } - - $runner = new WebhookRunner(); - $runner->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); + $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate; } + + $this->getWebhookService()->run(WebhookService::EVENT_ADD_PRODUCT, $webhookData); } } else @@ -271,20 +267,19 @@ class StockService extends BaseService ]); $stockRow->save(); - if ($stockLabelType == 1 && GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER) + if ($stockLabelType == 1) { - $webhookData = array_merge([ + $webhookData = [ 'product' => $productDetails->product->name, 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockId])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) { $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $bestBeforeDate; } - $runner = new WebhookRunner(); - $runner->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); + $this->getWebhookService()->run(WebhookService::EVENT_ADD_PRODUCT, $webhookData); } } @@ -996,20 +991,19 @@ class StockService extends BaseService $newBestBeforeDate = $stockEntry->best_before_date; } - if (GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER && $productDetails->product->auto_reprint_stock_label == 1 && $newBestBeforeDate != $stockEntry->best_before_date) + if ($productDetails->product->auto_reprint_stock_label == 1 && $newBestBeforeDate != $stockEntry->best_before_date) { - $webhookData = array_merge([ + $webhookData = [ 'product' => $productDetails->product->name, 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockEntry->stock_id])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) { $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $newBestBeforeDate; } - $runner = new WebhookRunner(); - $runner->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); + $this->getWebhookService()->run(WebhookService::EVENT_OPEN_PRODUCT, $webhookData); } } @@ -1308,20 +1302,19 @@ class StockService extends BaseService $newBestBeforeDate = date('Y-m-d', strtotime('+' . $productDetails->product->default_best_before_days_after_thawing . ' days')); } - if (GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER && $productDetails->product->auto_reprint_stock_label == 1 && $stockEntry->best_before_date != $newBestBeforeDate) + if ($productDetails->product->auto_reprint_stock_label == 1 && $stockEntry->best_before_date != $newBestBeforeDate) { - $webhookData = array_merge([ + $webhookData = [ 'product' => $productDetails->product->name, 'grocycode' => (string)(new Grocycode(Grocycode::PRODUCT, $productId, [$stockEntry->stock_id])), - ], GROCY_LABEL_PRINTER_PARAMS); + ]; if (GROCY_FEATURE_FLAG_STOCK_BEST_BEFORE_DATE_TRACKING) { $webhookData['due_date'] = $this->getLocalizationService()->__t('DD') . ': ' . $newBestBeforeDate; } - $runner = new WebhookRunner(); - $runner->run(GROCY_LABEL_PRINTER_WEBHOOK, $webhookData, GROCY_LABEL_PRINTER_HOOK_JSON); + $this->getWebhookService()->run(WebhookService::EVENT_TRANSFER_PRODUCT, $webhookData); } } diff --git a/services/WebhookService.php b/services/WebhookService.php new file mode 100644 index 00000000..d0bc2520 --- /dev/null +++ b/services/WebhookService.php @@ -0,0 +1,113 @@ + GROCY_LABEL_PRINTER_WEBHOOK, + 'default_args' => GROCY_LABEL_PRINTER_PARAMS, + 'json' => GROCY_LABEL_PRINTER_HOOK_JSON, + 'include_events' => [ + WebhookService::EVENT_BATTERY_PRINT_LABEL, + WebhookService::EVENT_CHORE_PRINT_LABEL, + WebhookService::EVENT_RECIPE_PRINT_LABEL, + WebhookService::EVENT_PRODUCT_PRINT_LABEL, + WebhookService::EVENT_STOCK_ENTRY_PRINT_LABEL, + WebhookService::EVENT_ADD_PRODUCT, + WebhookService::EVENT_OPEN_PRODUCT, + WebhookService::EVENT_TRANSFER_PRODUCT, + ], + 'exclude_events' => [], + 'enabled' => GROCY_FEATURE_FLAG_LABEL_PRINTER && GROCY_LABEL_PRINTER_RUN_SERVER, + ], +]; + +class WebhookService extends BaseService +{ + const EVENT_BATTERY_PRINT_LABEL = 'battery_print_label'; + const EVENT_CHORE_PRINT_LABEL = 'chore_print_label'; + const EVENT_RECIPE_PRINT_LABEL = 'recipe_print_label'; + const EVENT_PRODUCT_PRINT_LABEL = 'product_print_label'; + const EVENT_STOCK_ENTRY_PRINT_LABEL = 'stock_entry_print_label'; + const EVENT_ADD_PRODUCT = 'add_product'; + const EVENT_OPEN_PRODUCT = 'open_product'; + const EVENT_TRANSFER_PRODUCT = 'transfer_product'; + + public function __construct() + { + $this->client = new Client(['timeout' => 2.0]); + } + + private $client; + + private function shouldFire($webhook, $event) + { + if (!$webhook['enabled']) + { + return false; + } + + $includeEvents = $webhook['include_events'] ?? []; + $excludeEvents = $webhook['exclude_events'] ?? []; + + // No restrictions + if (empty($includeEvents) && empty($excludeEvents)) + { + return true; + } + + // Only include events + if (!empty($includeEvents) && in_array($event, $includeEvents)) + { + return true; + } + + // Only exclude events + if (!empty($excludeEvents) && !in_array($event, $excludeEvents)) + { + return true; + } + + // No events match + return false; + } + + public function fire($event, $args) + { + foreach (WEBHOOKS as $webhook) + { + if ($this->shouldFire($webhook, $event)) + { + $webhookData = array_merge($webhook['default_args'], $args); + $this->run($webhook['url'], $webhookData, $webhook['json']); + } + } + } + + private function run($url, $args, $json = false) + { + $reqArgs = []; + if ($json) + { + $reqArgs = ['json' => $args]; + } + else + { + $reqArgs = ['form_params' => $args]; + } + try + { + file_put_contents('php://stderr', 'Running Webhook: ' . $url . "\n" . print_r($reqArgs, true)); + + $this->client->request('POST', $url, $reqArgs); + } + catch (RequestException $e) + { + file_put_contents('php://stderr', 'Webhook failed: ' . $url . "\n" . $e->getMessage()); + } + } +}