Skip to content
Snippets Groups Projects
Commit 18a8349f authored by Anna Demianik's avatar Anna Demianik
Browse files

Issue #3492218 by anna d: Support Bing batch and multiple URLs per request

parent 4de08fc9
No related branches found
No related tags found
1 merge request!5#3492218: Allowed to reindex multiple Urls per API request
Pipeline #361300 passed
......@@ -8,6 +8,12 @@ include:
- '/includes/include.drupalci.variables.yml'
- '/includes/include.drupalci.workflows.yml'
variables:
SKIP_ESLINT: '1'
OPT_IN_TEST_NEXT_MINOR: '1'
OPT_IN_TEST_NEXT_MAJOR: '1'
_CSPELL_WORDS: 'alexanderdross, Alves, Ingelheim, Boehringer, Demianik, Shibin, dman, nataliaalves'
phpcs:
allow_failure: false
......
name: Bing Indexing Api
name: Bing Indexing API
type: module
description: Provides Bing Indexing API
description: Provides the Bing Indexing API tool to reindex web content in the Bing Search Engine.
package: SEO
core_version_requirement: ^9.3 || ^10
core_version_requirement: ^9.3 || ^10 || ^11
......@@ -3,7 +3,7 @@ bing_indexing_api.settings_form:
path: '/admin/config/services/bing-index-api'
defaults:
_form: '\Drupal\bing_indexing_api\Form\BingSettingsForm'
_title: 'Bing Index API'
_title: 'Bing Indexing API'
requirements:
_permission: 'administer bing index api'
......@@ -11,7 +11,7 @@ bing_indexing_api.bing_index_api_bulk_update_form:
path: '/admin/config/services/bing-index-api/bulk-update'
defaults:
_form: '\Drupal\bing_indexing_api\Form\BingBulkUpdateForm'
_title: 'Bing Index API - Bulk Update'
_title: 'Bing Indexing API - Bulk Update'
requirements:
_permission: 'administer bing index api'
<?php
namespace Drupal\bing_indexing_api\Batch;
/**
* Class for calling Batch.
*
* @package Drupal\bing_indexing_api
*/
class BingIndexApiBatch {
/**
* Common batch processing callback for all operations.
*
* @param string $url
* The url string we are checking.
* @param array &$context
* The batch context object.
*/
public static function batchProcess(string $url, array &$context): void {
$context['message'] = t('Preprocessing %url', ['%url' => $url]);
\Drupal::service('bing_indexing_api.client')->reindexUrl($url);
$context['results']['final'][] = $url;
}
/**
* Finish callback for our batch processing.
*
* @param bool $success
* Whether the batch completed successfully.
* @param array $results
* The results array.
* @param array $operations
* The operations array.
*/
public static function batchFinished(bool $success, array $results, array $operations): void {
if ($success) {
$message = \Drupal::translation()
->formatPlural(count($results), 'One URL updated.', '@count urls updated.');
\Drupal::messenger()->addStatus($message);
}
else {
$error_operation = reset($operations);
\Drupal::messenger()
->addError(t('An error occurred while processing @operation with arguments : @args', [
'@operation' => $error_operation[0],
'@args' => print_r($error_operation[0], TRUE),
]));
}
}
}
<?php
declare(strict_types=1);
namespace Drupal\bing_indexing_api\Form;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\bing_indexing_api\Service\BingIndexingApiInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -15,7 +18,7 @@ class BingBulkUpdateForm extends FormBase {
/**
* Constructor.
*/
public function __construct(protected ModuleExtensionList $extensionListModule) {
public function __construct(protected BingIndexingApiInterface $bingIndexingApi) {
}
/**
......@@ -23,7 +26,7 @@ class BingBulkUpdateForm extends FormBase {
*/
public static function create(ContainerInterface $container): self {
return new self(
$container->get('extension.list.module')
$container->get('bing_indexing_api.client')
);
}
......@@ -58,62 +61,26 @@ class BingBulkUpdateForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state): void {
// Process the URLS.
$urls = $this->processUrls($form_state);
foreach ($urls as $url) {
$operations[] = [
'Drupal\bing_indexing_api\Batch\BingIndexApiBatch::batchProcess',
[$url],
];
}
$url_input = $form_state->getValue('urls');
// Fail safe in case the batch fails in building the operations.
if (isset($operations)) {
// Set the batch to win the stuff.
$batch = [
'title' => $this->t('Checking...'),
'operations' => $operations,
'init_message' => $this->t('Importing URLs to process.'),
'finished' => 'Drupal\bing_indexing_api\Batch\BingIndexApiBatch::batchFinished',
'file' => $this->extensionListModule->getPath('bing_indexing_api') . '/src/Batch/BingIndexApiBatch.php',
];
// Engage.
batch_set($batch);
}
else {
$this->messenger()->addMessage($this->t('No valid URLs to process.'));
}
}
// Preprocess user input. Validate URLs.
if ($url_input && is_string($url_input)) {
$urls = array_filter(array_map('trim', explode("\n", $url_input)));
/**
* Parses the URLs into an array.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form State object.
*
* @return array
* The parsed urls.
*/
protected function processUrls(FormStateInterface $form_state): array {
$urls = $form_state->getValue('urls');
if (!$urls || !is_string($urls)) {
return [];
foreach ($urls as $key => $url) {
if (!filter_var($url, FILTER_VALIDATE_URL)) {
unset($urls[$key]);
}
}
}
$urls = preg_split('/\r\n|\n|\r/', $urls);
if (!$urls) {
return [];
if (!empty($urls)) {
$result = $this->bingIndexingApi->reindexUrl($urls);
$this->messenger()->addMessage($result['message'], $result['success'] ? MessengerInterface::TYPE_STATUS : MessengerInterface::TYPE_ERROR);
}
// Validate urls and make an array.
foreach ($urls as $url) {
$url = preg_replace('/\s+/', ' ', $url) ?: '';
$url = trim($url);
if (filter_var($url, FILTER_VALIDATE_URL)) {
$parsed_urls[] = parse_url($url, PHP_URL_PATH);
}
else {
$this->messenger()->addMessage($this->t('No valid URLs to reindex.'));
}
return $parsed_urls ?? [];
}
}
<?php
declare(strict_types=1);
namespace Drupal\bing_indexing_api\Form;
use Drupal\Core\Form\FormBase;
......
<?php
declare(strict_types=1);
namespace Drupal\bing_indexing_api\Service;
use Drupal\Component\Serialization\Json;
......@@ -7,21 +9,20 @@ use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
/**
* Service class for calling the Bing Indexing API.
*
* @ingroup bing_index_api
* Provides the reindexing URL request to Bing Webmaster Tools.
*/
class BingIndexingApi {
class BingIndexingApi implements BingIndexingApiInterface {
use StringTranslationTrait;
/**
* The Bing Index API End Point.
* The Bing Indexing API endpoint.
*/
const END_POINT = 'https://www.bing.com/webmaster/api.svc/json/SubmitUrlbatch?apikey=';
const ENDPOINT = 'https://www.bing.com/webmaster/api.svc/json/SubmitUrlbatch?apikey=';
/**
* Constructs BingIndexingApi.
......@@ -37,61 +38,72 @@ class BingIndexingApi {
}
/**
* Reindex url in the Bing Index API.
*
* @param string $url
* The URL to be reindexed.
* {@inheritdoc}
*/
public function reindexUrl(string $url): void {
$this->callApi($url);
public function reindexUrl(string|array $urls): array {
$urls = is_string($urls) ? [$urls] : $urls;
if ($response = $this->callApi($urls)) {
$status_code = $response->getStatusCode();
if ($success = $status_code === 200) {
$message = $this->t('Bing Indexing API: successfully triggered URL reindexing for: @url.', ['@url' => implode(', ', $urls)]);
$this->logger->notice($message);
}
else {
$body = Json::decode($response->getBody()->getContents());
$body = is_array($body) ? $body : [];
$this->logger->error($this->t('The Bing Index API returned a status code of @status_code for @url. Response message: @message', [
'@status_code' => $status_code,
'@url' => implode(', ', $urls),
'@message' => $body['error']['message'] ?? '',
]));
}
}
return [
'success' => $success ?? FALSE,
'message' => $message ?? $this->t('A problem occurred during URL reindexing: @url', ['@url' => implode(', ', $urls)]),
];
}
/**
* Requests URL reindexing in the Bing API.
* Requests URLs reindexing in Bing Webmaster Tools.
*
* @param array $urls
* The URLs to be reindexed in Bing Webmaster Tools.
*
* @param string $url
* The url to reindex in the Bing.
* @return \Psr\Http\Message\ResponseInterface|null
* API response if successful or NULL
*/
private function callApi(string $url): void {
public function callApi(array $urls): ?ResponseInterface {
if (empty($urls)) {
return NULL;
}
$base_domain = $this->state->get('bing_index_api_base_domain');
$api_key = $this->state->get('bing_index_api_key');
if (!$base_domain || !$api_key) {
$this->logger->error($this->t('Check the Bing API settings: the domain or API key is missing.'));
return;
return NULL;
}
try {
$response = $this->client->request('POST', self::END_POINT . $api_key, [
return $this->client->request('POST', self::ENDPOINT . $api_key, [
'json' => [
'siteUrl' => $base_domain,
'urlList' => [$base_domain . $url],
'urlList' => $urls,
],
'headers' => [
'Content-type' => 'application/json; charset=utf-8',
],
]);
$status_code = $response->getStatusCode();
switch ($status_code) {
case '200':
$this->logger->notice('The Bing Index API: successfully updated @url', ['@url' => $url]);
break;
default:
$body = Json::decode($response->getBody()->getContents());
$body = is_array($body) ? $body : [];
$this->logger->error($this->t('The Bing Index API returned a status code of @status_code for @url. Response message: @message', [
'@status_code' => $status_code,
'@url' => $url,
'@message' => $body['error']['message'] ?? '',
]));
}
}
catch (\Exception | GuzzleException $exception) {
$this->logger->error($exception->getMessage());
}
return NULL;
}
}
<?php
declare(strict_types=1);
namespace Drupal\bing_indexing_api\Service;
/**
* Provides an interface for BingIndexingApi service.
*/
interface BingIndexingApiInterface {
/**
* Requests URLs reindexing in Bing Webmaster Tools.
*
* @param string|array $urls
* The URLs to be reindexed.
*/
public function reindexUrl(string|array $urls): array;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment