Skip to content

Commit

Permalink
Merge pull request #604 from City-of-Helsinki/UHF-10941
Browse files Browse the repository at this point in the history
UHF-10941: Hakuvahti Sentry error
  • Loading branch information
hyrsky authored Nov 19, 2024
2 parents 7a13d07 + e354780 commit 5aa591e
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 32 deletions.
10 changes: 4 additions & 6 deletions conf/cmi/search_api.index.job_listings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,16 @@ dependencies:
- scheduler
- search_api
- helfi_react_search
- helfi_rekry_content
id: job_listings
name: 'Job listings'
description: ''
read_only: false
field_settings:
_language:
label: 'Legacy Language'
datasource_id: 'entity:node'
property_path: langcode
label: Language
property_path: _language
type: string
dependencies:
module:
- node
employment_id:
label: 'Employment » Luokittelutermi » Termin ID'
datasource_id: 'entity:node'
Expand Down Expand Up @@ -343,6 +340,7 @@ processor_settings:
- field_recruitment_id
- field_search_id
- langcode
language_field: { }
language_with_fallback: { }
project_execution_schedule: { }
project_image_absolute_url: { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ declare(strict_types=1);
/**
* Implements hook_theme().
*/
function helfi_hakuvahti_theme() {
function helfi_hakuvahti_theme(): array {
return [
'hakuvahti_form' => [
'variables' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\Core\Utility\Token;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand Down Expand Up @@ -78,9 +78,28 @@ public function confirm(): array {
* A render array for the confirmation result.
*/
private function handleConfirmFormSubmission(string $hash, string $subscription): array {
return $this->sendConfirmationRequest($hash, $subscription)
? $this->buildConfirmationSuccess()
: $this->buildConfirmationFailure();
try {
$response = $this->sendConfirmationRequest($hash, $subscription);

if ($response->getBody()->getContents() !== '') {
return $this->buildConfirmationSuccess();
}
}
catch (GuzzleException $exception) {
// 404 error is returned if:
// * Submission has been deleted after it expired.
// * Submission has already been confirmed.
// * Submission does not exist.
if ($exception->getCode() === 404) {
$this->logger->info('Hakuvahti confirmation request failed: ' . $exception->getMessage());

return $this->buildConfirmationFailure();
}

$this->logger->error('Hakuvahti confirmation request failed: ' . $exception->getMessage());
}

return $this->buildConfirmationFailure();
}

/**
Expand Down Expand Up @@ -138,27 +157,20 @@ private function buildConfirmationFailure(): array {
* @param string $subscriptionId
* The subscription ID.
*
* @return bool
* TRUE if the confirmation request was successful, FALSE otherwise.
* @return \Psr\Http\Message\ResponseInterface
* API response object.
*
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function sendConfirmationRequest(string $subscriptionHash, string $subscriptionId): bool {
protected function sendConfirmationRequest(string $subscriptionHash, string $subscriptionId): ResponseInterface {
$csrfTokenService = $this->container->get('csrf_token');
$httpClient = new Client([

return $this->httpClient->request('GET', getenv('HAKUVAHTI_URL') . "/subscription/confirm/{$subscriptionId}/{$subscriptionHash}", [
'headers' => [
'Content-Type' => 'application/json',
'token' => $csrfTokenService->get('session'),
],
]);

try {
$response = $httpClient->get(getenv('HAKUVAHTI_URL') . "/subscription/confirm/{$subscriptionId}/{$subscriptionHash}");
return $response->getBody()->getContents() !== '';
}
catch (GuzzleException $exception) {
$this->logger
->error('Hakuvahti confirmation request failed: ' . $exception->getMessage());
return FALSE;
}
}

/**
Expand Down Expand Up @@ -256,15 +268,14 @@ private function buildUnsubscribeFailedSubmission(): array {
*/
protected function sendUnsubscribeRequest(string $hash, string $subscription): bool {
$csrfTokenService = $this->container->get('csrf_token');
$httpClient = new Client([
'headers' => [
'Content-Type' => 'application/json',
'token' => $csrfTokenService->get('session'),
],
]);

try {
$response = $httpClient->delete(getenv('HAKUVAHTI_URL') . "/subscription/delete/{$subscription}/{$hash}");
$response = $this->httpClient->request('DELETE', getenv('HAKUVAHTI_URL') . "/subscription/delete/{$subscription}/{$hash}", [
'headers' => [
'Content-Type' => 'application/json',
'token' => $csrfTokenService->get('session'),
],
]);
return $response->getStatusCode() >= 200 && $response->getStatusCode() < 300;
}
catch (GuzzleException $exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{ title }}
{{ message }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{ title }}
{{ message }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

declare(strict_types=1);

namespace Drupal\Tests\helfi_hakuvahti\Kernel;

use Drupal\Core\Url;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\helfi_api_base\Traits\ApiTestTrait;
use Drupal\Tests\user\Traits\UserCreationTrait;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Loader\Configurator\Traits\PropertyTrait;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

/**
* Tests for hakuvahti controller.
*/
class HakuvahtiControllerTest extends KernelTestBase {

use ApiTestTrait;
use UserCreationTrait;
use PropertyTrait;

/**
* {@inheritdoc}
*/
protected static $modules = [
'user',
'system',
'helfi_hakuvahti',
];

/**
* {@inheritDoc}
*/
public function setUp(): void {
parent::setUp();

$this->setUpCurrentUser(permissions: ['access content']);
}

/**
* Tests handleConfirmFormSubmission.
*/
public function testHandleConfirmFormSubmission(): void {
$this->container->set(ClientInterface::class, $this->createMockHttpClient([
new Response(200, body: 'success'),
new Response(404, body: 'not found'),
new Response(500, body: 'fail'),
new RequestException("womp womp", new Request('POST', 'test')),
]));

$logger = $this->prophesize(LoggerInterface::class);
$this->container->set('logger.channel.helfi_hakuvahti', $logger->reveal());

// Get request.
$response = $this->makeRequest('GET', 'helfi_hakuvahti.confirm', ['hash' => 'a', 'subscription' => 'b']);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Confirm saved search', $response->getContent() ?? '');

// Success.
$response = $this->makeRequest('POST', 'helfi_hakuvahti.confirm', ['hash' => 'a', 'subscription' => 'b']);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Search saved successfully', $response->getContent() ?? '');

// Not found.
$response = $this->makeRequest('POST', 'helfi_hakuvahti.confirm', ['hash' => 'a', 'subscription' => 'b']);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Confirmation failed', $response->getContent() ?? '');

// Server error.
$response = $this->makeRequest('POST', 'helfi_hakuvahti.confirm', ['hash' => 'a', 'subscription' => 'b']);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Confirmation failed', $response->getContent() ?? '');

// Guzzle exception.
$response = $this->makeRequest('POST', 'helfi_hakuvahti.confirm', ['hash' => 'a', 'subscription' => 'b']);
$this->assertEquals(200, $response->getStatusCode());
$this->assertStringContainsString('Confirmation failed', $response->getContent() ?? '');
}

/**
* Process a request.
*
* @param string $method
* HTTP method.
* @param string $route
* Drupal route.
* @param array $query
* Query parameters.
*
* @return \Symfony\Component\HttpFoundation\Response
* Controller response.
*/
private function makeRequest(string $method, string $route, array $query = []): SymfonyResponse {
$url = Url::fromRoute($route, options: [
'query' => $query,
]);

$request = $this->getMockedRequest($url->toString(), $method);

return $this->processRequest($request);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Drupal\helfi_rekry_content\Plugin\search_api\processor;

use Drupal\Core\Entity\EntityInterface;
use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;

/**
* Adds `_language` to the index.
*
* This processor adds _language field to the search api index. The field was
* renamed to `search_api_language` in elasticsearch_connector v8 update. We
* cannot modify rekry search api index without breaking existing hakuvahti
* queries.
*
* @SearchApiProcessor(
* id = "language_field",
* label = @Translation("Language field"),
* description = @Translation("Add _language field to the index."),
* stages = {
* "add_properties" = 20,
* },
* locked = true,
* hidden = true,
* )
*/
class LanguageFieldProcessor extends ProcessorPluginBase {

/**
* {@inheritdoc}
*/
public function getPropertyDefinitions(DatasourceInterface $datasource = NULL): array {
$properties = [];

if (!$datasource) {
$definition = [
'label' => $this->t('Language'),
'description' => $this->t('The legacy _language field.'),
'type' => 'string',
'processor_id' => $this->getPluginId(),
];
$properties['_language'] = new ProcessorProperty($definition);
}

return $properties;
}

/**
* {@inheritdoc}
*/
public function addFieldValues(ItemInterface $item): void {
$object = $item->getOriginalObject()->getValue();

if ($object instanceof EntityInterface) {
$indexableValue = $object->language()->getId();

$itemFields = $item->getFields();
$itemFields = $this->getFieldsHelper()
->filterForPropertyPath($itemFields, NULL, '_language');

foreach ($itemFields as $itemField) {
$itemField->addValue($indexableValue);
}

}
}

}

0 comments on commit 5aa591e

Please sign in to comment.