Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Revert "Revert "UHF-8526: Use PubSub to invalidate caches"" #44

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ Only main-navigation has syncing option. Other navigations are created in Etusiv
- run `drush upwd helfi-admin 123` to update admin password. it is used as api key in other instances.
- Setup any other instance with helfi_navigation module enabled.
- Add following line to local.settings.php. Otherwise syncing global navigation won't work
- `$config['helfi_navigation.api']['key'] = base64_encode('helfi-admin:123');`
```php
$config['helfi_api_base.api_accounts']['vault'][] = [
'id' => 'helfi_navigation',
'plugin' => 'authorization_token',
'data' => base64_encode('helfi-admin:123'),
];
```

### Steps after both instances are up and running.
1. Edit and save menu on any instance with helfi_navigation module enabled.
Expand Down
42 changes: 14 additions & 28 deletions helfi_navigation.module
Original file line number Diff line number Diff line change
Expand Up @@ -93,62 +93,48 @@ function helfi_navigation_preprocess_menu__external_menu__fallback(&$variables)
* Implements hook_ENTITY_TYPE_update().
*/
function helfi_navigation_menu_update(MenuInterface $entity) : void {
if ($entity->id() === 'main') {
_helfi_navigation_queue_item();
}
_helfi_navigation_queue_item($entity->id(), $entity->language()->getId(), 'update');
}

/**
* Implements hook_ENTITY_TYPE_update().
*/
function helfi_navigation_menu_link_content_update(MenuLinkContentInterface $entity) : void {
if ($entity->getMenuName() === 'main') {
_helfi_navigation_queue_item();
}
_helfi_navigation_queue_item($entity->getMenuName(), $entity->language()->getId(), 'update');
}

/**
* Implements hook_ENTITY_TYPE_insert().
*/
function helfi_navigation_menu_link_content_insert(MenuLinkContentInterface $entity) : void {
if ($entity->getMenuName() === 'main') {
_helfi_navigation_queue_item();
}
_helfi_navigation_queue_item($entity->getMenuName(), $entity->language()->getId(), 'insert');
}

/**
* Implements hook_ENTITY_TYPE_delete().
*/
function helfi_navigation_menu_link_content_delete(MenuLinkContentInterface $entity) : void {
if ($entity->getMenuName() === 'main') {
_helfi_navigation_queue_item();
}
_helfi_navigation_queue_item($entity->getMenuName(), $entity->language()->getId(), 'delete');
}

/**
* Create menu update queue item.
*/
function _helfi_navigation_queue_item() : void {
if (!\Drupal::config('helfi_navigation.api')->get('key')) {
function _helfi_navigation_queue_item(string $menuName, string $langcode, string $action) : void {
if (!\Drupal::service('helfi_navigation.api_authorization')->getAuthorization()) {
return;
}
$queue = Drupal::queue('helfi_navigation_menu_queue');

// Queue items only when queue is empty.
if ($queue->numberOfItems() === 0) {
foreach (\Drupal::languageManager()->getLanguages() as $language) {
$queue->createItem($language->getId());
}
}
}
static $items = [];

/**
* Implements hook_cron().
*/
function helfi_navigation_cron() : void {
/** @var \Drupal\helfi_navigation\CacheWarmer $warmer */
$warmer = Drupal::service('helfi_navigation.cache_warmer');
$warmer->warm();
$key = sprintf('%s:%s:%s', $menuName, $langcode, $action);

// Queue item once per request.
if (!isset($items[$key])) {
$queue->createItem(['menu' => $menuName, 'language' => $langcode]);
$items[$key] = $key;
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions helfi_navigation.services.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
parameters:
helfi_navigation.request_timeout: 15
services:
logger.channel.helfi_navigation:
parent: logger.channel_base
Expand All @@ -16,6 +18,7 @@ services:
- '@helfi_api_base.environment_resolver'
- '@logger.channel.helfi_navigation'
- '@helfi_navigation.api_authorization'
- '%helfi_navigation.request_timeout%'
helfi_navigation.menu_manager:
class: Drupal\helfi_navigation\MainMenuManager
arguments:
Expand Down
39 changes: 20 additions & 19 deletions src/ApiManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use Psr\Log\LoggerInterface;

/**
* Service class for global navigation related functions.
* Service class for global navigation-related functions.
*/
class ApiManager {

Expand Down Expand Up @@ -58,14 +58,17 @@ class ApiManager {
* Logger channel.
* @param \Drupal\helfi_navigation\ApiAuthorization $apiAuthorization
* The API authorization service.
* @param int $requestTimeout
* The request timeout.
*/
public function __construct(
private readonly TimeInterface $time,
private readonly CacheBackendInterface $cache,
private readonly ClientInterface $httpClient,
private readonly EnvironmentResolverInterface $environmentResolver,
private readonly LoggerInterface $logger,
private readonly ApiAuthorization $apiAuthorization
private readonly ApiAuthorization $apiAuthorization,
private readonly int $requestTimeout,
) {
}

Expand Down Expand Up @@ -99,7 +102,7 @@ private function cache(string $key, callable $callback) : ? CacheValue {
$value = ($cache = $this->cache->get($key)) ? $cache->data : NULL;

// Attempt to re-fetch the data in case cache does not exist, cache has
// expired or bypass cache is set to true.
// expired, or bypass cache is set to true.
if (
($value instanceof CacheValue && $value->hasExpired($this->time->getRequestTime())) ||
$this->bypassCache ||
Expand Down Expand Up @@ -128,7 +131,7 @@ private function cache(string $key, callable $callback) : ? CacheValue {
}

/**
* Makes a request to fetch external menu from Etusivu instance.
* Makes a request to fetch an external menu from Etusivu instance.
*
* @param string $langcode
* The langcode.
Expand All @@ -138,7 +141,7 @@ private function cache(string $key, callable $callback) : ? CacheValue {
* The request options.
*
* @return \Drupal\helfi_navigation\ApiResponse
* The JSON object representing external menu.
* The JSON object representing the external menu.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
Expand All @@ -159,13 +162,13 @@ public function get(
new CacheValue(
$this->makeRequest('GET', $endpoint, $langcode, $options),
$this->time->getRequestTime(),
['external_menu:%s:%s', $menuId, $langcode],
[sprintf('external_menu:%s:%s', $menuId, $langcode)],
)
)->value;
}

/**
* Updates the main menu for currently active project.
* Updates the main menu for the currently active project.
*
* @param string $langcode
* The langcode.
Expand Down Expand Up @@ -194,25 +197,23 @@ public function update(string $langcode, array $data) : ApiResponse {
*
* @param string $environmentName
* Environment name.
* @param array $options
* The optional options.
*
* @return array
* The request options.
*/
private function getDefaultRequestOptions(string $environmentName) : array {
$options = ['timeout' => 15];
$options['curl'] = [CURLOPT_TCP_KEEPALIVE => TRUE];

if (drupal_valid_test_ua()) {
// Speed up mock tests by using very low request timeout value when
// running tests.
$options['timeout'] = 1;
}
private function getRequestOptions(string $environmentName, array $options = []) : array {
$default = [
'timeout' => $this->requestTimeout,
'curl' => [CURLOPT_TCP_KEEPALIVE => TRUE],
];

if ($environmentName === 'local') {
// Disable SSL verification in local environment.
$options['verify'] = FALSE;
$default['verify'] = FALSE;
}
return $options;
return array_merge_recursive($options, $default);
}

/**
Expand Down Expand Up @@ -296,7 +297,7 @@ private function makeRequest(

$url = $this->getUrl('api', $langcode, ['endpoint' => $endpoint]);

$options = array_merge_recursive($options, $this->getDefaultRequestOptions($activeEnvironmentName));
$options = $this->getRequestOptions($activeEnvironmentName, $options);

try {
if ($this->previousException instanceof \Exception) {
Expand Down
6 changes: 1 addition & 5 deletions src/ExternalMenuTreeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,6 @@ private function createLink(

// Parse the URL.
$item->url = !empty($item->url) ? UrlHelper::parse($item->url) : new Url('<nolink>');

if (!isset($item->parentId)) {
$item->parentId = NULL;
}
$item->external = $this->domainResolver->isExternal($item->url);

if (isset($item->weight)) {
Expand All @@ -162,7 +158,7 @@ private function createLink(
'attributes' => new Attribute($item->attributes ?? []),
'title' => $item->name,
'id' => $item->id,
'parent_id' => $item->parentId,
'parent_id' => $item->parentId ?? NULL,
'is_expanded' => $expand_all_items || !empty($item->expanded),
'in_active_trail' => $inActiveTrail,
'is_currentPage' => $inActiveTrail,
Expand Down
4 changes: 2 additions & 2 deletions src/MainMenuManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function __construct(
}

/**
* Sends main menu tree to frontpage instance.
* Sends the main menu tree to Etusivu instance.
*
* @param string $langcode
* The langcode.
Expand All @@ -49,7 +49,7 @@ public function __construct(
* @throws \InvalidArgumentException
*/
public function sync(string $langcode): bool {
// Sync menu as an anonymous user to make sure no sensitive
// Sync the menu as an anonymous user to make sure no sensitive
// links are synced.
$this->accountSwitcher->switchTo(new AnonymousUserSession());
$response = $this->apiManager->update(
Expand Down
4 changes: 2 additions & 2 deletions src/Menu/MenuTreeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class MenuTreeBuilder {
* @param \Drupal\helfi_api_base\Link\InternalDomainResolver $domainResolver
* The internal domain resolver.
* @param \Drupal\Core\Menu\MenuLinkTreeInterface $menuTree
* The menu link tree builder service.
* The 'menu link tree builder' service.
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager
* The menu link manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
Expand Down Expand Up @@ -157,7 +157,7 @@ private function transform(array $menuItems, string $langcode, string $rootId =

$urlObject = $link->getUrlObject();

// Make sure url object retains the language information.
// Make sure the url object retains the language information.
if (!$urlObject->getOption('language')) {
$urlObject->setOptions(['language' => $link->language()]);
}
Expand Down
47 changes: 38 additions & 9 deletions src/Plugin/QueueWorker/MenuQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\helfi_api_base\Cache\CacheTagInvalidator;
use Drupal\helfi_api_base\Environment\Project;
use Drupal\helfi_navigation\MainMenuManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

Expand All @@ -27,35 +29,62 @@ final class MenuQueue extends QueueWorkerBase implements ContainerFactoryPluginI
*/
private MainMenuManager $mainMenuManager;

/**
* The cache tag invalidator service.
*
* @var \Drupal\helfi_api_base\Cache\CacheTagInvalidator
*/
private CacheTagInvalidator $cacheTagInvalidator;

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) : self {
$instance = new self($configuration, $plugin_id, $plugin_definition);
$instance->mainMenuManager = $container->get('helfi_navigation.menu_manager');
$instance->cacheTagInvalidator = $container->get('helfi_api_base.cache_tag_invalidator');
return $instance;
}

/**
* Process queue item.
*
* @param string $data
* Data of the processable language code.
* @param array|mixed $data
* The queue data. Should contain 'menu' and 'language'.
*
* @throws \Exception
* Throws exception if language code is not set.
*/
public function processItem($data) : void {
// Data is a langcode, like 'fi' or 'en'.
if (!is_string($data)) {
if (!isset($data['menu'], $data['language'])) {
return;
}
try {
$this->mainMenuManager->sync($data);
}
catch (\Throwable) {
// The failed sync will be logged by ApiManager.

['menu' => $menuName, 'language' => $language] = $data;

if ($menuName === 'main') {
try {
$this->mainMenuManager->sync($language);
}
catch (\Throwable) {
// The failed sync will be logged by ApiManager.
}
}

$this->cacheTagInvalidator->invalidateTags([
// These are used by the menu block itself and local Global mobile menu
// REST API endpoint.
sprintf('config:system.menu.%s', $menuName),
sprintf('external_menu_block:%s', $menuName),
// This is used by ApiManager service to cache the API response
// locally.
sprintf('external_menu:%s:%s', $menuName, $language),
]);
$this->cacheTagInvalidator->invalidateTags([
// This is used by REST API collection endpoint on Etusivu.
'config:rest.resource.helfi_global_menu_collection',
], [Project::ETUSIVU]);

}

}
1 change: 1 addition & 0 deletions tests/src/Functional/MenuBlockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function setUp() : void {
$this->setActiveProject(Project::ASUMINEN, EnvironmentEnum::Local);

_helfi_navigation_generate_blocks('stark', 'content', TRUE);
$this->setContainerParameter('helfi_navigation.request_timeout', 1);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions tests/src/Kernel/KernelTestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ protected function setUp() : void {
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
$container->setParameter('helfi_navigation.request_timeout', 1);

parent::register($container);

// Core's KernelTestBase removes service_collector tags from
Expand Down
Loading
Loading