From 6460816ce0550de0cc9f999aa27ecc0e46dcab40 Mon Sep 17 00:00:00 2001 From: Maxime Leclercq Date: Thu, 27 Jul 2023 16:24:26 +0200 Subject: [PATCH 1/3] feat: fallback on sylius taxon renderer if ES is down --- src/Checker/ElasticsearchChecker.php | 60 +++++++++++++++++++++++++ src/Resources/config/routing/shop.yaml | 2 + src/Resources/config/services.yaml | 10 +++++ src/Twig/Extension/RenderSearchForm.php | 11 ++++- 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/Checker/ElasticsearchChecker.php diff --git a/src/Checker/ElasticsearchChecker.php b/src/Checker/ElasticsearchChecker.php new file mode 100644 index 00000000..6b4fec2b --- /dev/null +++ b/src/Checker/ElasticsearchChecker.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Checker; + +use MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory; +use Sylius\Component\Locale\Context\LocaleContextInterface; +use Sylius\Component\Registry\ServiceRegistryInterface; + +class ElasticsearchChecker +{ + private ClientFactory $clientFactory; + + private ServiceRegistryInterface $documentableRegistry; + + private LocaleContextInterface $localeContext; + + private ?bool $isAvailable = null; + + public function __construct( + ClientFactory $clientFactory, + ServiceRegistryInterface $documentableRegistry, + LocaleContextInterface $localeContext + ) { + $this->clientFactory = $clientFactory; + $this->documentableRegistry = $documentableRegistry; + $this->localeContext = $localeContext; + } + + public function check(): bool + { + if (null === $this->isAvailable) { + $documentables = $this->documentableRegistry->all(); + $documentable = reset($documentables); + $client = $this->clientFactory->getClient($documentable, $this->localeContext->getLocaleCode()); + + try { + $client->getStatus()->getResponse(); + } catch (\Exception $e) { + $this->isAvailable = false; + + return $this->isAvailable; + } + + $this->isAvailable = true; + } + + return $this->isAvailable; + } +} diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml index 89cc7123..d0c61f4b 100644 --- a/src/Resources/config/routing/shop.yaml +++ b/src/Resources/config/routing/shop.yaml @@ -5,6 +5,7 @@ monsieurbiz_search_search: _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController::searchAction requirements: query: .+ + condition: service('monsieurbiz.search.checker.elasticsearch_checker').check() monsieurbiz_search_post: path: /search @@ -27,3 +28,4 @@ monsieurbiz_sylius_search_taxon: taxon: "expr:notFoundOnNull(service('sylius.repository.taxon').findOneBySlug($slug, service('sylius.context.locale').getLocaleCode()))" requirements: slug: .+ + condition: service('monsieurbiz.search.checker.elasticsearch_checker').check() diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index e8dffe1e..96dfa6fa 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -252,3 +252,13 @@ services: - { name: kernel.event_listener, event: sylius.product_variant.post_update, method: dispatchProductVariantReindexMessage } - { name: kernel.event_listener, event: sylius.product_variant.pre_delete, method: saveProductIdToDispatchReindexMessage } - { name: kernel.event_listener, event: sylius.product_variant.post_delete, method: dispatchProductReindexMessage } + + monsieurbiz.search.checker.elasticsearch_checker: + class: MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchChecker + public: true + tags: + - { name: routing.condition_service } + + MonsieurBiz\SyliusSearchPlugin\Twig\Extension\RenderSearchForm: + arguments: + $elasticsearchChecker: '@monsieurbiz.search.checker.elasticsearch_checker' diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php index bf93806c..82fb703e 100644 --- a/src/Twig/Extension/RenderSearchForm.php +++ b/src/Twig/Extension/RenderSearchForm.php @@ -13,6 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; +use MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchChecker; use MonsieurBiz\SyliusSearchPlugin\Form\Type\SearchType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -29,14 +30,18 @@ class RenderSearchForm extends AbstractExtension private RequestStack $requestStack; + private ElasticsearchChecker $elasticsearchChecker; + public function __construct( FormFactoryInterface $formFactory, Environment $templatingEngine, - RequestStack $requestStack + RequestStack $requestStack, + ElasticsearchChecker $elasticsearchChecker ) { $this->formFactory = $formFactory; $this->templatingEngine = $templatingEngine; $this->requestStack = $requestStack; + $this->elasticsearchChecker = $elasticsearchChecker; } public function getFunctions() @@ -48,6 +53,10 @@ public function getFunctions() public function createForm(?string $template = null): Markup { + if (false === $this->elasticsearchChecker->check()) { + return new Markup('', 'UTF-8'); + } + $request = $this->requestStack->getCurrentRequest(); $template = $template ?? '@MonsieurBizSyliusSearchPlugin/Search/_form.html.twig'; $query = null !== $request ? $request->get('query', '') : ''; From 2aa66bd26f477f055594fc4e4c466aabdec3cd3c Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Thu, 27 Jul 2023 16:39:54 +0200 Subject: [PATCH 2/3] Search is available if we have at least one documentable registry --- src/Checker/ElasticsearchChecker.php | 27 ++------ src/Checker/ElasticsearchCheckerInterface.php | 19 ++++++ src/Resources/config/routing/shop.yaml | 4 +- src/Resources/config/services.yaml | 10 ++- src/Routing/RequestContext.php | 63 +++++++++++++++++++ src/Twig/Extension/RenderSearchForm.php | 6 +- 6 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 src/Checker/ElasticsearchCheckerInterface.php create mode 100644 src/Routing/RequestContext.php diff --git a/src/Checker/ElasticsearchChecker.php b/src/Checker/ElasticsearchChecker.php index 6b4fec2b..7e94b498 100644 --- a/src/Checker/ElasticsearchChecker.php +++ b/src/Checker/ElasticsearchChecker.php @@ -13,37 +13,18 @@ namespace MonsieurBiz\SyliusSearchPlugin\Checker; -use MonsieurBiz\SyliusSearchPlugin\Search\ClientFactory; -use Sylius\Component\Locale\Context\LocaleContextInterface; -use Sylius\Component\Registry\ServiceRegistryInterface; +use JoliCode\Elastically\Factory; -class ElasticsearchChecker +class ElasticsearchChecker implements ElasticsearchCheckerInterface { - private ClientFactory $clientFactory; - - private ServiceRegistryInterface $documentableRegistry; - - private LocaleContextInterface $localeContext; - private ?bool $isAvailable = null; - public function __construct( - ClientFactory $clientFactory, - ServiceRegistryInterface $documentableRegistry, - LocaleContextInterface $localeContext - ) { - $this->clientFactory = $clientFactory; - $this->documentableRegistry = $documentableRegistry; - $this->localeContext = $localeContext; - } - public function check(): bool { if (null === $this->isAvailable) { - $documentables = $this->documentableRegistry->all(); - $documentable = reset($documentables); - $client = $this->clientFactory->getClient($documentable, $this->localeContext->getLocaleCode()); + $client = (new Factory())->buildClient(); + // Check client response try { $client->getStatus()->getResponse(); } catch (\Exception $e) { diff --git a/src/Checker/ElasticsearchCheckerInterface.php b/src/Checker/ElasticsearchCheckerInterface.php new file mode 100644 index 00000000..b79e38b0 --- /dev/null +++ b/src/Checker/ElasticsearchCheckerInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Checker; + +interface ElasticsearchCheckerInterface +{ + public function check(): bool; +} diff --git a/src/Resources/config/routing/shop.yaml b/src/Resources/config/routing/shop.yaml index d0c61f4b..e0eaf4bb 100644 --- a/src/Resources/config/routing/shop.yaml +++ b/src/Resources/config/routing/shop.yaml @@ -5,7 +5,7 @@ monsieurbiz_search_search: _controller: MonsieurBiz\SyliusSearchPlugin\Controller\SearchController::searchAction requirements: query: .+ - condition: service('monsieurbiz.search.checker.elasticsearch_checker').check() + condition: "not(context.getPathInfo() matches '`^%sylius.security.new_api_route%`') and context.checkElasticsearch()" monsieurbiz_search_post: path: /search @@ -28,4 +28,4 @@ monsieurbiz_sylius_search_taxon: taxon: "expr:notFoundOnNull(service('sylius.repository.taxon').findOneBySlug($slug, service('sylius.context.locale').getLocaleCode()))" requirements: slug: .+ - condition: service('monsieurbiz.search.checker.elasticsearch_checker').check() + condition: "not(context.getPathInfo() matches '`^%sylius.security.new_api_route%`') and context.checkElasticsearch()" diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 96dfa6fa..5fa46664 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -255,10 +255,14 @@ services: monsieurbiz.search.checker.elasticsearch_checker: class: MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchChecker - public: true - tags: - - { name: routing.condition_service } MonsieurBiz\SyliusSearchPlugin\Twig\Extension\RenderSearchForm: arguments: $elasticsearchChecker: '@monsieurbiz.search.checker.elasticsearch_checker' + + # Routing Context + MonsieurBiz\SyliusSearchPlugin\Routing\RequestContext: + decorates: router.request_context + arguments: + - '@MonsieurBiz\SyliusSearchPlugin\Routing\RequestContext.inner' + - '@monsieurbiz.search.checker.elasticsearch_checker' diff --git a/src/Routing/RequestContext.php b/src/Routing/RequestContext.php new file mode 100644 index 00000000..506a1267 --- /dev/null +++ b/src/Routing/RequestContext.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Routing; + +use Exception; +use MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchCheckerInterface; +use Symfony\Component\Routing\RequestContext as BaseRequestContext; + +class RequestContext extends BaseRequestContext +{ + private BaseRequestContext $decorated; + + private ElasticsearchCheckerInterface $elasticsearchChecker; + + public function __construct( + BaseRequestContext $decorated, + ElasticsearchCheckerInterface $elasticsearchChecker + ) { + parent::__construct( + $decorated->getBaseUrl(), + $decorated->getMethod(), + $decorated->getHost(), + $decorated->getScheme(), + $decorated->getHttpPort(), + $decorated->getHttpsPort(), + $decorated->getPathInfo(), + $decorated->getQueryString() + ); + $this->decorated = $decorated; + $this->elasticsearchChecker = $elasticsearchChecker; + } + + public function checkElasticsearch(): bool + { + return $this->elasticsearchChecker->check(); + } + + /** + * @throws Exception + * + * @return mixed + */ + public function __call(string $name, array $arguments) + { + $callback = [$this->decorated, $name]; + if (\is_callable($callback)) { + return \call_user_func($callback, $arguments); + } + + throw new Exception(sprintf('Method %s not found for class "%s"', $name, \get_class($this->decorated))); + } +} diff --git a/src/Twig/Extension/RenderSearchForm.php b/src/Twig/Extension/RenderSearchForm.php index 82fb703e..9f2c8a3e 100644 --- a/src/Twig/Extension/RenderSearchForm.php +++ b/src/Twig/Extension/RenderSearchForm.php @@ -13,7 +13,7 @@ namespace MonsieurBiz\SyliusSearchPlugin\Twig\Extension; -use MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchChecker; +use MonsieurBiz\SyliusSearchPlugin\Checker\ElasticsearchCheckerInterface; use MonsieurBiz\SyliusSearchPlugin\Form\Type\SearchType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -30,13 +30,13 @@ class RenderSearchForm extends AbstractExtension private RequestStack $requestStack; - private ElasticsearchChecker $elasticsearchChecker; + private ElasticsearchCheckerInterface $elasticsearchChecker; public function __construct( FormFactoryInterface $formFactory, Environment $templatingEngine, RequestStack $requestStack, - ElasticsearchChecker $elasticsearchChecker + ElasticsearchCheckerInterface $elasticsearchChecker ) { $this->formFactory = $formFactory; $this->templatingEngine = $templatingEngine; From 7968ab99acb400040f10286ab897c390645cdc9f Mon Sep 17 00:00:00 2001 From: Maxime Huran Date: Thu, 27 Jul 2023 18:17:03 +0200 Subject: [PATCH 3/3] Add fake elasticsearch checker --- UPGRADE-2.0.md | 1 + docs/disable_elasticsearch_checker.md | 11 +++++++++++ docs/index.md | 1 + src/Checker/FakeElasticsearchChecker.php | 22 ++++++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 docs/disable_elasticsearch_checker.md create mode 100644 src/Checker/FakeElasticsearchChecker.php diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index a55e3154..52473d44 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -11,6 +11,7 @@ - New method `deleteByDocumentIds` in the `MonsieurBiz\SyliusSearchPlugin\Index\IndexerInterface` interface - Deprecated the method `deleteByDocuments` in the `MonsieurBiz\SyliusSearchPlugin\Index\IndexerInterface` interface. Use `deleteByDocumentIds` instead. - `ChannelFilter` and `EnabledFilter` in `MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter\Product` were moved to `MonsieurBiz\SyliusSearchPlugin\Search\Request\QueryFilter` +- A fallback on the Sylius' taxon display is now used to keep your pages even if you Elasticsearch instance is down. If you want to disable it, check the [FakeElasticsearchChecker](docs/disable_elasticsearch_checker.md) # UPGRADE FROM v1.X.X TO v2.0.x diff --git a/docs/disable_elasticsearch_checker.md b/docs/disable_elasticsearch_checker.md new file mode 100644 index 00000000..50cef848 --- /dev/null +++ b/docs/disable_elasticsearch_checker.md @@ -0,0 +1,11 @@ +# Disable the ElasticsearchChecker + +The plugin now checks if the Elasticsearch server is running before each search. + +If you want to disable this feature, you can do it by adding the following configuration: + +```yaml +services: + monsieurbiz.search.checker.elasticsearch_checker: + class: MonsieurBiz\SyliusSearchPlugin\Checker\FakeElasticsearchChecker +``` diff --git a/docs/index.md b/docs/index.md index 1e19a1e7..57205f04 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,6 +2,7 @@ ## Menu +- [Disable the ElasticsearchChecker](./disable_elasticsearch_checker.md) - [Add custom sorts](./add_custom_sorts.md) - [Add custom values for an entity](./add_custom_values.md) - [Add custom filters](./add_custom_filters.md) diff --git a/src/Checker/FakeElasticsearchChecker.php b/src/Checker/FakeElasticsearchChecker.php new file mode 100644 index 00000000..0febc406 --- /dev/null +++ b/src/Checker/FakeElasticsearchChecker.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace MonsieurBiz\SyliusSearchPlugin\Checker; + +class FakeElasticsearchChecker implements ElasticsearchCheckerInterface +{ + public function check(): bool + { + return true; + } +}