Skip to content

Commit

Permalink
Merge 5bba875 into d625038
Browse files Browse the repository at this point in the history
  • Loading branch information
tuutti authored Oct 5, 2023
2 parents d625038 + 5bba875 commit 9a3ba48
Show file tree
Hide file tree
Showing 13 changed files with 116 additions and 83 deletions.
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

0 comments on commit 9a3ba48

Please sign in to comment.