From a6226f946ae0be6e221b601b91a7dcd4952b9326 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Thu, 12 Feb 2026 13:31:36 +0200 Subject: [PATCH 1/7] UHF-12723: Replace annotations --- src/Entity/HakuvahtiConfig.php | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Entity/HakuvahtiConfig.php b/src/Entity/HakuvahtiConfig.php index 1674a35..fbf2809 100644 --- a/src/Entity/HakuvahtiConfig.php +++ b/src/Entity/HakuvahtiConfig.php @@ -5,25 +5,19 @@ namespace Drupal\helfi_hakuvahti\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Entity\Attribute\ConfigEntityType; +use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Defines the Hakuvahti configuration entity. - * - * @ConfigEntityType( - * id = "hakuvahti_config", - * label = @Translation("Hakuvahti Configuration"), - * config_prefix = "config", - * entity_keys = { - * "id" = "id", - * "label" = "label" - * }, - * config_export = { - * "id", - * "label", - * "site_id" - * } - * ) */ +#[ConfigEntityType( + id: 'hakuvahti_config', + label: new TranslatableMarkup('Hakuvahti Configuration'), + config_prefix: 'config', + entity_keys: ['id' => 'id', 'label' => 'label'], + config_export: ['id', 'label', 'site_id'], +)] class HakuvahtiConfig extends ConfigEntityBase { /** From cfc6ea7dc76678ad82863ed982d1ef6ee6e24894 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Thu, 12 Feb 2026 14:00:28 +0200 Subject: [PATCH 2/7] UHF-12723: Add form for hakuvahti settings This was originally implemented in rekry, but I moved this to helfi_hakuvahti when we started to install hakuvahti to other instances. --- config/install/helfi_hakuvahti.settings.yml | 5 ++ config/schema/helfi_hakuvahti.schema.yml | 18 ++++++ helfi_hakuvahti.config_translation.yml | 5 ++ helfi_hakuvahti.install | 23 +++++++ helfi_hakuvahti.links.menu.yml | 5 ++ helfi_hakuvahti.links.task.yml | 4 ++ helfi_hakuvahti.routing.yml | 10 +++ src/Form/SettingsForm.php | 71 +++++++++++++++++++++ src/Hook/ConfigIgnoreHook.php | 27 ++++++++ tests/src/Kernel/SettingsFormTest.php | 55 ++++++++++++++++ translations/fi.po | 3 + 11 files changed, 226 insertions(+) create mode 100644 config/install/helfi_hakuvahti.settings.yml create mode 100644 helfi_hakuvahti.config_translation.yml create mode 100644 helfi_hakuvahti.install create mode 100644 helfi_hakuvahti.links.menu.yml create mode 100644 helfi_hakuvahti.links.task.yml create mode 100644 src/Form/SettingsForm.php create mode 100644 src/Hook/ConfigIgnoreHook.php create mode 100644 tests/src/Kernel/SettingsFormTest.php diff --git a/config/install/helfi_hakuvahti.settings.yml b/config/install/helfi_hakuvahti.settings.yml new file mode 100644 index 0000000..3b5e5ed --- /dev/null +++ b/config/install/helfi_hakuvahti.settings.yml @@ -0,0 +1,5 @@ +langcode: en +hakuvahti_tos_checkbox_label: 'I consent to the processing of my personal data' +hakuvahti_tos_link_text: 'Read our privacy policy (pdf).' +hakuvahti_tos_link_url: '' +hakuvahti_instructions_link_url: '' diff --git a/config/schema/helfi_hakuvahti.schema.yml b/config/schema/helfi_hakuvahti.schema.yml index 67b7f51..88fe293 100644 --- a/config/schema/helfi_hakuvahti.schema.yml +++ b/config/schema/helfi_hakuvahti.schema.yml @@ -20,3 +20,21 @@ helfi_hakuvahti.settings: type: string base_url: type: string + + hakuvahti_tos_checkbox_label: + type: label + label: 'Hakuvahti terms of service checkbox label' + translatable: true + hakuvahti_tos_link_text: + type: label + label: 'Hakuvahti terms of service link text' + translatable: true + hakuvahti_tos_link_url: + type: label + label: 'Hakuvahti terms of service URL' + translatable: true + hakuvahti_instructions_link_url: + type: label + label: 'Hakuvahti detailed instructions link URL' + translatable: true + diff --git a/helfi_hakuvahti.config_translation.yml b/helfi_hakuvahti.config_translation.yml new file mode 100644 index 0000000..8090cbe --- /dev/null +++ b/helfi_hakuvahti.config_translation.yml @@ -0,0 +1,5 @@ +helfi_hakuvahti.settings: + title: 'Hakuvahti settings' + base_route_name: helfi_hakuvahti.settings + names: + - helfi_hakuvahti.settings diff --git a/helfi_hakuvahti.install b/helfi_hakuvahti.install new file mode 100644 index 0000000..f1a73dc --- /dev/null +++ b/helfi_hakuvahti.install @@ -0,0 +1,23 @@ +getEditable('helfi_hakuvahti.settings') + ->set('hakuvahti_tos_checkbox_label', 'I consent to the processing of my personal data') + ->set('hakuvahti_tos_link_text', 'Read our privacy policy (pdf).') + ->set('hakuvahti_tos_link_url', '') + ->set('hakuvahti_instructions_link_url', '') + ->set('langcode', 'en') + ->save(); +} diff --git a/helfi_hakuvahti.links.menu.yml b/helfi_hakuvahti.links.menu.yml new file mode 100644 index 0000000..5619035 --- /dev/null +++ b/helfi_hakuvahti.links.menu.yml @@ -0,0 +1,5 @@ +helfi_hakuvahti.settings: + title: 'Hakuvahti settings' + route_name: helfi_hakuvahti.settings + parent: hdbt_admin_tools.site_information + weight: 90 diff --git a/helfi_hakuvahti.links.task.yml b/helfi_hakuvahti.links.task.yml new file mode 100644 index 0000000..be1fa76 --- /dev/null +++ b/helfi_hakuvahti.links.task.yml @@ -0,0 +1,4 @@ +helfi_hakuvahti.settings: + route_name: helfi_hakuvahti.settings + title: 'Hakuvahti settings' + base_route: helfi_hakuvahti.settings diff --git a/helfi_hakuvahti.routing.yml b/helfi_hakuvahti.routing.yml index fc6d5e2..1b68870 100644 --- a/helfi_hakuvahti.routing.yml +++ b/helfi_hakuvahti.routing.yml @@ -1,3 +1,13 @@ +helfi_hakuvahti.settings: + path: '/admin/tools/hakuvahti' + defaults: + _form: '\Drupal\helfi_hakuvahti\Form\SettingsForm' + _title: 'Hakuvahti settings' + requirements: + _permission: 'administer site configuration' + options: + _admin_route: TRUE + helfi_hakuvahti.subscribe: path: '/hakuvahti/subscribe' defaults: diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php new file mode 100644 index 0000000..f5a1e12 --- /dev/null +++ b/src/Form/SettingsForm.php @@ -0,0 +1,71 @@ + 'textfield', + '#config_target' => 'helfi_hakuvahti.settings:hakuvahti_tos_checkbox_label', + '#title' => $this->t('Hakuvahti terms of service checkbox label'), + '#description' => $this->t('Label for the terms of service checkbox.'), + ]; + + $form['settings']['hakuvahti_tos_link_text'] = [ + '#type' => 'textfield', + '#config_target' => 'helfi_hakuvahti.settings:hakuvahti_tos_link_text', + '#title' => $this->t('Hakuvahti terms of service link text'), + ]; + + $form['settings']['hakuvahti_tos_link_url'] = [ + '#type' => 'textfield', + '#title' => $this->t('Hakuvahti terms of service URL'), + '#config_target' => 'helfi_hakuvahti.settings:hakuvahti_tos_link_url', + '#description' => $this->t('URL for the webpage or pdf to the Hakuvahti terms of service.'), + ]; + + $form['settings']['hakuvahti_instructions_link_url'] = [ + '#type' => 'textfield', + '#title' => $this->t('More detailed instructions on how to use saved searches'), + '#config_target' => 'helfi_hakuvahti.settings:hakuvahti_instructions_link_url', + ]; + + return parent::buildForm($form, $form_state); + } + +} diff --git a/src/Hook/ConfigIgnoreHook.php b/src/Hook/ConfigIgnoreHook.php new file mode 100644 index 0000000..49095e5 --- /dev/null +++ b/src/Hook/ConfigIgnoreHook.php @@ -0,0 +1,27 @@ +installConfig(['helfi_hakuvahti']); + } + + /** + * Tests that submitting the form saves settings to config. + */ + public function testSubmitSavesConfig(): void { + $form_state = new FormState(); + $form_state->setValues([ + 'hakuvahti_tos_checkbox_label' => 'I agree', + 'hakuvahti_tos_link_text' => 'Privacy policy', + 'hakuvahti_tos_link_url' => 'https://example.com/tos', + 'hakuvahti_instructions_link_url' => 'https://example.com/instructions', + ]); + + $this->container->get(FormBuilderInterface::class)->submitForm(SettingsForm::class, $form_state); + + $this->assertEmpty($form_state->getErrors()); + + $config = $this->config('helfi_hakuvahti.settings'); + $this->assertEquals('I agree', $config->get('hakuvahti_tos_checkbox_label')); + $this->assertEquals('Privacy policy', $config->get('hakuvahti_tos_link_text')); + $this->assertEquals('https://example.com/tos', $config->get('hakuvahti_tos_link_url')); + $this->assertEquals('https://example.com/instructions', $config->get('hakuvahti_instructions_link_url')); + } + +} diff --git a/translations/fi.po b/translations/fi.po index aade81e..3bc0db9 100644 --- a/translations/fi.po +++ b/translations/fi.po @@ -1,6 +1,9 @@ msgid "" msgstr "" +msgid "Hakuvahti settings" +msgstr "Hakuvahdin asetukset" + msgctxt "Hakuvahti" msgid "You can save more searches at any time." msgstr "Voit koska tahansa tilata uusia hakuvahteja." From 0fd55e2e980e035a7d86ead0642ef45cddeea76b Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Thu, 12 Feb 2026 14:04:07 +0200 Subject: [PATCH 3/7] UHF-12723: Add DrupalSettings class This was previously implemented in hdbt.theme --- helfi_hakuvahti.services.yml | 2 + src/DrupalSettings.php | 75 +++++++++++++++++++++++ src/Form/SettingsForm.php | 9 --- tests/src/Kernel/DrupalSettingsTest.php | 80 +++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 src/DrupalSettings.php create mode 100644 tests/src/Kernel/DrupalSettingsTest.php diff --git a/helfi_hakuvahti.services.yml b/helfi_hakuvahti.services.yml index 755beb6..103f754 100644 --- a/helfi_hakuvahti.services.yml +++ b/helfi_hakuvahti.services.yml @@ -9,3 +9,5 @@ services: Drupal\helfi_hakuvahti\HakuvahtiInterface: class: Drupal\helfi_hakuvahti\Hakuvahti + + Drupal\helfi_hakuvahti\DrupalSettings: ~ diff --git a/src/DrupalSettings.php b/src/DrupalSettings.php new file mode 100644 index 0000000..a74dd24 --- /dev/null +++ b/src/DrupalSettings.php @@ -0,0 +1,75 @@ +languageManager + ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT) + ->getId(); + + // Attempt to get the translated configuration. + // @todo Why is this needed? + $language = $this->languageManager->getLanguage($langcode); + $originalLanguage = $this->languageManager->getConfigOverrideLanguage(); + $this->languageManager->setConfigOverrideLanguage($language); + + try { + $cache = new CacheableMetadata(); + + $settings = $this->configFactory->get('helfi_hakuvahti.settings'); + + // Do not expose settings if base_url is not configured. + if (empty($settings->get('base_url'))) { + return; + } + + $drupalSettings = []; + foreach (self::EXPOSED_SETTINGS as $exposed_setting) { + $drupalSettings[$exposed_setting] = $settings->get($exposed_setting) ?: NULL; + } + + $drupalSettings['subscribe_url'] = Url::fromRoute('helfi_hakuvahti.subscribe')->toString(); + + $build['#attached']['drupalSettings']['hakuvahti'] = $drupalSettings; + + $cache->addCacheableDependency($settings); + $cache->addCacheContexts(['languages:language_content']); + $cache->applyTo($build); + } + finally { + // Set the config back to the original language. + $this->languageManager->setConfigOverrideLanguage($originalLanguage); + } + } + +} diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index f5a1e12..eeb511e 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -12,15 +12,6 @@ */ class SettingsForm extends ConfigFormBase { - use AutowireTrait; - - public function __construct( - ConfigFactoryInterface $configFactory, - TypedConfigManagerInterface $typedConfigManager, - ) { - parent::__construct($configFactory, $typedConfigManager); - } - /** * {@inheritdoc} */ diff --git a/tests/src/Kernel/DrupalSettingsTest.php b/tests/src/Kernel/DrupalSettingsTest.php new file mode 100644 index 0000000..d6d3821 --- /dev/null +++ b/tests/src/Kernel/DrupalSettingsTest.php @@ -0,0 +1,80 @@ +installConfig(['helfi_hakuvahti']); + } + + /** + * Tests that applyTo does nothing when base_url is empty. + */ + public function testApplyToWithoutBaseUrl(): void { + $build = []; + $this->container->get(DrupalSettings::class)->applyTo($build); + + $this->assertArrayNotHasKey('#attached', $build); + } + + /** + * Tests that applyTo exposes settings when base_url is configured. + */ + public function testApplyToExposesSettings(): void { + $this->config('helfi_hakuvahti.settings') + ->set('base_url', 'https://hakuvahti.example.com') + ->set('hakuvahti_tos_checkbox_label', 'I agree') + ->set('hakuvahti_tos_link_text', 'Privacy policy') + ->set('hakuvahti_tos_link_url', 'https://example.com/tos') + ->set('hakuvahti_instructions_link_url', 'https://example.com/instructions') + ->save(); + + $build = []; + $this->container->get(DrupalSettings::class)->applyTo($build); + + $settings = $build['#attached']['drupalSettings']['hakuvahti']; + $this->assertEquals('I agree', $settings['hakuvahti_tos_checkbox_label']); + $this->assertEquals('Privacy policy', $settings['hakuvahti_tos_link_text']); + $this->assertEquals('https://example.com/tos', $settings['hakuvahti_tos_link_url']); + $this->assertEquals('https://example.com/instructions', $settings['hakuvahti_instructions_link_url']); + } + + /** + * Tests that empty settings are exposed as NULL. + */ + public function testApplyToExposesEmptySettingsAsNull(): void { + $this->config('helfi_hakuvahti.settings') + ->set('base_url', 'https://hakuvahti.example.com') + ->set('hakuvahti_tos_link_url', '') + ->set('hakuvahti_instructions_link_url', '') + ->save(); + + $build = []; + $this->container->get(DrupalSettings::class)->applyTo($build); + + $settings = $build['#attached']['drupalSettings']['hakuvahti']; + $this->assertNull($settings['hakuvahti_tos_link_url']); + $this->assertNull($settings['hakuvahti_instructions_link_url']); + } + +} From 308e173189c34c84feb65551afe0a965d752c5ab Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Fri, 13 Feb 2026 09:25:59 +0200 Subject: [PATCH 4/7] UHF-12723: Add #[RunTestsInSeparateProcesses] to kernel tests Kernel test classes must specify the #[RunTestsInSeparateProcesses] attribute, not doing so is deprecated in drupal:11.3.0 and will throw an exception in drupal:12.0.0. See https://www.drupal.org/node/3548485 --- tests/src/Kernel/DrupalSettingsTest.php | 6 ++++-- tests/src/Kernel/HakuvahtiConfigEntityTest.php | 6 ++++-- tests/src/Kernel/HakuvahtiControllerTest.php | 4 ++++ tests/src/Kernel/HakuvahtiSubscribeControllerTest.php | 4 ++++ tests/src/Kernel/SettingsFormTest.php | 6 ++++-- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/src/Kernel/DrupalSettingsTest.php b/tests/src/Kernel/DrupalSettingsTest.php index d6d3821..0587406 100644 --- a/tests/src/Kernel/DrupalSettingsTest.php +++ b/tests/src/Kernel/DrupalSettingsTest.php @@ -6,12 +6,14 @@ use Drupal\helfi_hakuvahti\DrupalSettings; use Drupal\KernelTests\KernelTestBase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; /** * Tests for the DrupalSettings service. - * - * @group helfi_hakuvahti */ +#[Group('helfi_hakuvahti')] +#[RunTestsInSeparateProcesses] class DrupalSettingsTest extends KernelTestBase { /** diff --git a/tests/src/Kernel/HakuvahtiConfigEntityTest.php b/tests/src/Kernel/HakuvahtiConfigEntityTest.php index dab4bf6..0ba55cf 100644 --- a/tests/src/Kernel/HakuvahtiConfigEntityTest.php +++ b/tests/src/Kernel/HakuvahtiConfigEntityTest.php @@ -6,12 +6,14 @@ use Drupal\helfi_hakuvahti\Entity\HakuvahtiConfig; use Drupal\KernelTests\KernelTestBase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; /** * Tests for Hakuvahti configuration entity. - * - * @group helfi_hakuvahti */ +#[Group('helfi_hakuvahti')] +#[RunTestsInSeparateProcesses] class HakuvahtiConfigEntityTest extends KernelTestBase { /** diff --git a/tests/src/Kernel/HakuvahtiControllerTest.php b/tests/src/Kernel/HakuvahtiControllerTest.php index 7910d40..689833c 100644 --- a/tests/src/Kernel/HakuvahtiControllerTest.php +++ b/tests/src/Kernel/HakuvahtiControllerTest.php @@ -12,6 +12,8 @@ use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Loader\Configurator\Traits\PropertyTrait; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; @@ -19,6 +21,8 @@ /** * Tests for hakuvahti controller. */ +#[Group('helfi_hakuvahti')] +#[RunTestsInSeparateProcesses] class HakuvahtiControllerTest extends KernelTestBase { use ApiTestTrait; diff --git a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php index bb9403b..a1dc6db 100644 --- a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php +++ b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php @@ -14,12 +14,16 @@ use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use Symfony\Component\DependencyInjection\Loader\Configurator\Traits\PropertyTrait; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; /** * Tests for hakuvahti subscribe controller. */ +#[Group('helfi_hakuvahti')] +#[RunTestsInSeparateProcesses] class HakuvahtiSubscribeControllerTest extends KernelTestBase { use ApiTestTrait; diff --git a/tests/src/Kernel/SettingsFormTest.php b/tests/src/Kernel/SettingsFormTest.php index f1626cd..67ebbbc 100644 --- a/tests/src/Kernel/SettingsFormTest.php +++ b/tests/src/Kernel/SettingsFormTest.php @@ -8,12 +8,14 @@ use Drupal\Core\Form\FormState; use Drupal\helfi_hakuvahti\Form\SettingsForm; use Drupal\KernelTests\KernelTestBase; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; /** * Tests for the Hakuvahti settings form. - * - * @group helfi_hakuvahti */ +#[Group('helfi_hakuvahti')] +#[RunTestsInSeparateProcesses] class SettingsFormTest extends KernelTestBase { /** From 13708aba6918eca5cc212fc57047063867ec4921 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Fri, 13 Feb 2026 11:50:36 +0200 Subject: [PATCH 5/7] fixup! UHF-12723: Add DrupalSettings class --- src/DrupalSettings.php | 4 ++-- tests/src/Kernel/DrupalSettingsTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DrupalSettings.php b/src/DrupalSettings.php index a74dd24..e171243 100644 --- a/src/DrupalSettings.php +++ b/src/DrupalSettings.php @@ -55,10 +55,10 @@ public function applyTo(array &$build): void { $drupalSettings = []; foreach (self::EXPOSED_SETTINGS as $exposed_setting) { - $drupalSettings[$exposed_setting] = $settings->get($exposed_setting) ?: NULL; + $drupalSettings['texts'][$exposed_setting] = $settings->get($exposed_setting) ?: NULL; } - $drupalSettings['subscribe_url'] = Url::fromRoute('helfi_hakuvahti.subscribe')->toString(); + $drupalSettings['apiUrl'] = Url::fromRoute('helfi_hakuvahti.subscribe')->toString(); $build['#attached']['drupalSettings']['hakuvahti'] = $drupalSettings; diff --git a/tests/src/Kernel/DrupalSettingsTest.php b/tests/src/Kernel/DrupalSettingsTest.php index 0587406..78e8ee9 100644 --- a/tests/src/Kernel/DrupalSettingsTest.php +++ b/tests/src/Kernel/DrupalSettingsTest.php @@ -54,7 +54,7 @@ public function testApplyToExposesSettings(): void { $build = []; $this->container->get(DrupalSettings::class)->applyTo($build); - $settings = $build['#attached']['drupalSettings']['hakuvahti']; + $settings = $build['#attached']['drupalSettings']['hakuvahti']['texts']; $this->assertEquals('I agree', $settings['hakuvahti_tos_checkbox_label']); $this->assertEquals('Privacy policy', $settings['hakuvahti_tos_link_text']); $this->assertEquals('https://example.com/tos', $settings['hakuvahti_tos_link_url']); @@ -74,7 +74,7 @@ public function testApplyToExposesEmptySettingsAsNull(): void { $build = []; $this->container->get(DrupalSettings::class)->applyTo($build); - $settings = $build['#attached']['drupalSettings']['hakuvahti']; + $settings = $build['#attached']['drupalSettings']['hakuvahti']['texts']; $this->assertNull($settings['hakuvahti_tos_link_url']); $this->assertNull($settings['hakuvahti_instructions_link_url']); } From 95c4a9880e1386e2ca0447d3b3ec1f15509ab4da Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Fri, 13 Feb 2026 11:51:20 +0200 Subject: [PATCH 6/7] UHF-12723: Get language from request context This simpliefies frontend --- src/Controller/HakuvahtiSubscribeController.php | 6 ++++++ tests/src/Kernel/HakuvahtiSubscribeControllerTest.php | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Controller/HakuvahtiSubscribeController.php b/src/Controller/HakuvahtiSubscribeController.php index e3157b7..838a4ea 100644 --- a/src/Controller/HakuvahtiSubscribeController.php +++ b/src/Controller/HakuvahtiSubscribeController.php @@ -56,6 +56,12 @@ public function post(Request $request): JsonResponse { // Use site_id from configuration entity. $requestData['site_id'] = $config->getSiteId(); + // The frontend gets api url from the DrupalSettings service. The + // service sets language prefixed subscribe urls, so the frontend + // does not need to keep track of the current language, we know it + // from the request context. + $requestData['lang'] = $this->languageManager()->getCurrentLanguage()->getId(); + $requestObject = new HakuvahtiRequest($requestData); } catch (\InvalidArgumentException | \JsonException $e) { diff --git a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php index a1dc6db..0c85109 100644 --- a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php +++ b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php @@ -81,7 +81,6 @@ public function testHandleConfirmFormSubmission(): void { // Missing config. $response = $this->makeRequest([ 'email' => 'valid@email.fi', - 'lang' => 'fi', 'query' => '?query=123¶meters=4567', 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', 'search_description' => 'This, is the query filters string, separated, by comma', @@ -95,7 +94,6 @@ public function testHandleConfirmFormSubmission(): void { // Subscribe with api error. $response = $this->makeRequest([ 'email' => 'valid@email.fi', - 'lang' => 'fi', 'query' => '?query=123¶meters=4567', 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', 'search_description' => 'This, is the query filters string, separated, by comma', @@ -105,7 +103,6 @@ public function testHandleConfirmFormSubmission(): void { // Success. $response = $this->makeRequest([ 'email' => 'valid@email.fi', - 'lang' => 'fi', 'query' => '?query=123¶meters=4567', 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', 'search_description' => 'This, is the query filters string, separated, by comma', From 78bcf62b5667e81613b76a708d2c288559693ffc Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Fri, 13 Feb 2026 17:24:32 +0200 Subject: [PATCH 7/7] UHF-12723: Support new options --- .../HakuvahtiSubscribeController.php | 2 +- src/HakuvahtiRequest.php | 55 ++++++--- .../HakuvahtiSubscribeControllerTest.php | 12 +- tests/src/Unit/HakuvahtiRequestClassTest.php | 109 ++++++++++++------ 4 files changed, 116 insertions(+), 62 deletions(-) diff --git a/src/Controller/HakuvahtiSubscribeController.php b/src/Controller/HakuvahtiSubscribeController.php index 838a4ea..2ec4972 100644 --- a/src/Controller/HakuvahtiSubscribeController.php +++ b/src/Controller/HakuvahtiSubscribeController.php @@ -54,7 +54,7 @@ public function post(Request $request): JsonResponse { } // Use site_id from configuration entity. - $requestData['site_id'] = $config->getSiteId(); + $requestData['siteId'] = $config->getSiteId(); // The frontend gets api url from the DrupalSettings service. The // service sets language prefixed subscribe urls, so the frontend diff --git a/src/HakuvahtiRequest.php b/src/HakuvahtiRequest.php index e66d781..a1a6a32 100644 --- a/src/HakuvahtiRequest.php +++ b/src/HakuvahtiRequest.php @@ -11,12 +11,24 @@ */ final readonly class HakuvahtiRequest { - private const MAX_SEARCH_DESCRIPTION_LENGTH = 999; + private const int MAX_SEARCH_DESCRIPTION_LENGTH = 999; + private const array REQUIRED_FIELDS = [ + 'lang', + 'siteId', + 'query', + 'elasticQuery', + 'searchDescription', + ]; /** * The email address. */ - public string $email; + public ?string $email; + + /** + * User phone number. + */ + public ?string $sms; /** * Language id. @@ -29,7 +41,7 @@ public string $siteId; /** - * The request parameters from the request uli. + * The request parameters from the request uri. */ public string $query; @@ -40,6 +52,13 @@ */ public string $elasticQuery; + /** + * If true, the elastic query is stored in ATV. + * + * Use this if the query can contain user data. + */ + public bool $elasticQueryAtv; + /** * The search description. * @@ -50,43 +69,43 @@ public string $searchDescription; public function __construct(array $requestData) { - $requiredFields = ['email', 'lang', 'site_id', 'query', 'elastic_query', 'search_description']; - - foreach ($requiredFields as $fieldName) { + foreach (self::REQUIRED_FIELDS as $fieldName) { if (!isset($requestData[$fieldName])) { throw new \InvalidArgumentException("Request is missing field: $fieldName"); } } - if (!filter_var($requestData['email'], FILTER_VALIDATE_EMAIL)) { + $this->lang = $requestData['lang']; + $this->siteId = $requestData['siteId']; + $this->query = $requestData['query']; + $this->elasticQuery = $requestData['elasticQuery']; + $this->elasticQueryAtv = $requestData['elasticQueryAtv'] ?? FALSE; + $this->searchDescription = $requestData['searchDescription']; + $this->email = $requestData['email'] ?? NULL; + $this->sms = $requestData['sms'] ?? NULL; + + // User chooses which notification type they get. Either field can be NULL. + if ($this->email && !filter_var($this->email, FILTER_VALIDATE_EMAIL)) { throw new \InvalidArgumentException("Email must be a valid email address"); } - if (strlen($requestData['search_description']) > self::MAX_SEARCH_DESCRIPTION_LENGTH) { + if (strlen($this->searchDescription) > self::MAX_SEARCH_DESCRIPTION_LENGTH) { throw new \InvalidArgumentException("Search description is too long."); } - - $this->email = $requestData['email']; - $this->lang = $requestData['lang']; - $this->siteId = $requestData['site_id']; - $this->query = $requestData['query']; - $this->elasticQuery = $requestData['elastic_query']; - $this->searchDescription = $requestData['search_description']; } /** * Return the data to be sent for hakuvahti services subscription endpoint. - * - * @return array - * The data for hakuvahti subscription request. */ public function getServiceRequestData(): array { return [ 'email' => $this->email, + 'sms' => $this->sms, 'lang' => $this->lang, 'site_id' => $this->siteId, 'query' => $this->query, 'elastic_query' => $this->elasticQuery, + 'elastic_query_atv' => $this->elasticQueryAtv, 'search_description' => $this->searchDescription, ]; } diff --git a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php index 0c85109..7db1fa9 100644 --- a/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php +++ b/tests/src/Kernel/HakuvahtiSubscribeControllerTest.php @@ -82,8 +82,8 @@ public function testHandleConfirmFormSubmission(): void { $response = $this->makeRequest([ 'email' => 'valid@email.fi', 'query' => '?query=123¶meters=4567', - 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', - 'search_description' => 'This, is the query filters string, separated, by comma', + 'elasticQuery' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', + 'searchDescription' => 'This, is the query filters string, separated, by comma', ]); $this->assertEquals(500, $response->getStatusCode()); @@ -95,8 +95,8 @@ public function testHandleConfirmFormSubmission(): void { $response = $this->makeRequest([ 'email' => 'valid@email.fi', 'query' => '?query=123¶meters=4567', - 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', - 'search_description' => 'This, is the query filters string, separated, by comma', + 'elasticQuery' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', + 'searchDescription' => 'This, is the query filters string, separated, by comma', ]); $this->assertEquals(500, $response->getStatusCode()); @@ -104,8 +104,8 @@ public function testHandleConfirmFormSubmission(): void { $response = $this->makeRequest([ 'email' => 'valid@email.fi', 'query' => '?query=123¶meters=4567', - 'elastic_query' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', - 'search_description' => 'This, is the query filters string, separated, by comma', + 'elasticQuery' => 'eyJxdWVyeSI6eyJib29sIjp7ImZpbHRlciI6W3sidGVybSI6eyJlbnRpdHlfdHlwZSI6Im5vZGUifX1dfX19', + 'searchDescription' => 'This, is the query filters string, separated, by comma', ]); $this->assertEquals(200, $response->getStatusCode()); } diff --git a/tests/src/Unit/HakuvahtiRequestClassTest.php b/tests/src/Unit/HakuvahtiRequestClassTest.php index 078460a..df198b0 100644 --- a/tests/src/Unit/HakuvahtiRequestClassTest.php +++ b/tests/src/Unit/HakuvahtiRequestClassTest.php @@ -6,60 +6,95 @@ use Drupal\helfi_hakuvahti\HakuvahtiRequest; use Drupal\Tests\UnitTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; /** * Tests rekry specific hakuvahti features. - * - * @group helfi_rekry_content */ +#[Group('helfi_hakuvahti')] class HakuvahtiRequestClassTest extends UnitTestCase { - /** - * Test that an exception is thrown if request has missing parameters. - */ - public function testMissingRequestParameter(): void { - $this->expectException(\InvalidArgumentException::class); - new HakuvahtiRequest([]); - } - /** * Test the request class. */ - public function testRequestClass(): void { - $requiredFields = $this->getRequiredData(); - - try { - $requiredFields['email'] = 'invalid@email'; - new HakuvahtiRequest($requiredFields); - } - catch (\InvalidArgumentException $e) { - $this->assertIsObject($e, 'Validated email address format'); + #[DataProvider('data')] + public function testRequestClass(array $request, ?array $expected = NULL): void { + if (!$expected) { + $this->expectException(\InvalidArgumentException::class); } - $requiredFields = $this->getRequiredData(); - $request = new HakuvahtiRequest($requiredFields); + $hakuvahtiRequest = new HakuvahtiRequest($request); - $serviceRequestData = $request->getServiceRequestData(); - $this->assertEquals($serviceRequestData, $this->getRequiredData()); - $this->assertEquals($serviceRequestData['elastic_query'], $request->elasticQuery); - $this->assertEquals($serviceRequestData['search_description'], $request->searchDescription); - $this->assertEquals($serviceRequestData['query'], $request->query); + if ($expected) { + $serviceRequestData = $hakuvahtiRequest->getServiceRequestData(); + $this->assertEquals($serviceRequestData, $expected); + } } /** - * Get the initial request data. - * - * @return array - * The hakuvahti initial request data. + * Get tests data. */ - private function getRequiredData(): array { + public static function data(): array { return [ - 'email' => 'valid@email.fi', - 'lang' => 'fi', - 'site_id' => 'rekry', - 'query' => '?query=123¶meters=4567', - 'elastic_query' => 'this-is_the_base64_encoded_elasticsearch_query', - 'search_description' => 'This, is the query filters string, separated, by comma', + // Email only request. + [ + 'request' => [ + 'email' => 'valid@email.fi', + 'lang' => 'fi', + 'siteId' => 'rekry', + 'query' => '?query=123¶meters=4567', + 'elasticQuery' => 'this-is_the_base64_encoded_elasticsearch_query', + 'searchDescription' => 'This, is the query filters string, separated, by comma', + ], + 'expected' => [ + 'email' => 'valid@email.fi', + 'sms' => NULL, + 'lang' => 'fi', + 'site_id' => 'rekry', + 'query' => '?query=123¶meters=4567', + 'elastic_query' => 'this-is_the_base64_encoded_elasticsearch_query', + 'elastic_query_atv' => FALSE, + 'search_description' => 'This, is the query filters string, separated, by comma', + ], + ], + // Phone-number-only request. + [ + 'request' => [ + 'sms' => '044 123 4567', + 'lang' => 'fi', + 'siteId' => 'rekry', + 'query' => '?query=123¶meters=4567', + 'elasticQuery' => 'this-is_the_base64_encoded_elasticsearch_query', + 'elasticQueryAtv' => TRUE, + 'searchDescription' => 'This, is the query filters string, separated, by comma', + ], + 'expected' => [ + 'email' => NULL, + 'sms' => '044 123 4567', + 'lang' => 'fi', + 'site_id' => 'rekry', + 'query' => '?query=123¶meters=4567', + 'elastic_query' => 'this-is_the_base64_encoded_elasticsearch_query', + 'elastic_query_atv' => TRUE, + 'search_description' => 'This, is the query filters string, separated, by comma', + ], + ], + // Test that an exception is thrown if the request has missing parameters. + [ + 'request' => [], + ], + // Test that an exception is thrown if parameters fail validation. + [ + 'request' => [ + 'email' => 'invalid@email', + 'lang' => 'fi', + 'siteId' => 'rekry', + 'query' => '?query=123¶meters=4567', + 'elasticQuery' => 'this-is_the_base64_encoded_elasticsearch_query', + 'searchDescription' => 'This, is the query filters string, separated, by comma', + ], + ], ]; }