external_entity-1.0.x-dev/src/Plugin/ExternalEntity/ConnectionType/ExternalEntityServer.php

src/Plugin/ExternalEntity/ConnectionType/ExternalEntityServer.php
<?php

declare(strict_types=1);

namespace Drupal\external_entity\Plugin\ExternalEntity\ConnectionType;

use GuzzleHttp\TransferStats;
use Drupal\Core\Utility\Error;
use Drupal\Core\Http\ClientFactory;
use Drupal\Core\Form\FormStateInterface;
use GuzzleHttp\Exception\GuzzleException;
use Drupal\external_entity\AjaxFormTrait;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Component\Utility\DeprecationHelper;
use Drupal\external_entity\ExternalEntityResource;
use Drupal\external_entity\ExternalEntityDefinition;
use Drupal\external_entity\Entity\Query\SearchQuery;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\external_entity\Annotation\ExternalEntityConnectionType;
use Drupal\external_entity\Definition\ExternalEntitySearchDefinition;
use Drupal\external_entity\Definition\ExternalEntityDefaultDefinition;
use Drupal\external_entity\Definition\ExternalEntityResourceDefinition;
use Drupal\external_entity\Contracts\ExternalEntityConnectionAwareInterface;
use Drupal\external_entity\Contracts\ExternalEntityAuthenticationTypeInterface;
use Drupal\external_entity\Plugin\ExternalEntity\ExternalEntityConnectionTypeBase;
use Drupal\external_entity\Plugin\ExternalEntity\ExternalEntityConnectionAwareTrait;

/**
 * @ExternalEntityConnectionType(
 *   id = "external_entity_server",
 *   label = @Translation("External Entity Server")
 * )
 */
class ExternalEntityServer extends ExternalEntityConnectionTypeBase implements ExternalEntityConnectionAwareInterface {

  use AjaxFormTrait;
  use ExternalEntityConnectionAwareTrait;

  /**
   * Define the external server's successful status.
   */
  public const string EXTERNAL_ENTITY_SERVER_OK = 'connected';

  /**
   * @var \Drupal\Core\Http\ClientFactory
   */
  protected ClientFactory $httpClientFactory;

  /**
   * The external entity server constructor.
   *
   * @param array $configuration
   *   An array of plugin configurations.
   * @param string $plugin_id
   *   The plugin identifier.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\Core\Http\ClientFactory $http_client_factory
   *   The HTTP client factory.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    ClientFactory $http_client_factory,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->httpClientFactory = $http_client_factory;
  }

  /**
   * {@inheritDoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ): static {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('http_client_factory')
    );
  }

  /**
   * {@inheritDoc}
   */
  public function defaultConfiguration(): array {
    return [
      'resources' => [],
      'server_domain' => NULL,
    ];
  }

  /**
   * {@inheritDoc}
   */
  public function buildConfigurationForm(
    array $form,
    FormStateInterface $form_state,
  ): array {
    $form['#prefix'] = '<div id="external-entity-server-connection-type">';
    $form['#suffix'] = '</div>';

    $server_domain = $this->getFormStateValue(
      'server_domain',
      $form_state,
      $this->getServerDomain()
    );

    $form['server_domain'] = [
      '#type' => 'url',
      '#title' => $this->t('Server Domain'),
      '#required' => TRUE,
      '#description' => $this->t('Input the external entity server domain.'),
      '#default_value' => $server_domain,
      '#depth' => 1,
      '#ajax' => [
        'method' => 'replaceWith',
        'event' => 'change',
        'wrapper' => 'external-entity-server-connection-type',
        'callback' => [$this, 'ajaxFormDepthCallback'],
      ],
    ];

    if (!empty($server_domain)) {
      $this->setConfiguration(['server_domain' => $server_domain]);

      if ($this->isConnectionValid()) {
        $form['resources'] = [
          '#type' => 'select',
          '#title' => $this->t('Server Resources'),
          '#required' => TRUE,
          '#description' => $this->t(
            'Select all resources that can be used when looking up definitions.'
          ),
          '#multiple' => TRUE,
          '#options' => $this->getResourceOptions(),
          '#default_value' => $this->getResources(),
        ];
      }
      else {
        $this->messenger()->addError($this->t('Unable to connect to @domain.', [
          '@domain' => $server_domain,
        ]));
      }
    }

    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function validateConfigurationForm(
    array &$form,
    FormStateInterface $form_state,
  ): void {
    if ($form_state->hasValue('server_domain') && !$this->isConnectionValid()) {
      $form_state->setError(
        $form['server_domain'],
        'Unable to connect to the external entity server.'
      );
    }
  }

  /**
   * {@inheritDoc}
   */
  public function fetchResourceDefinitions(): array {
    $resources = [];

    $results = array_intersect_key(
      $this->fetchAllResources(),
      $this->getResources()
    );

    foreach ($results as $name => $value) {
      $resources[$name] = new ExternalEntityResourceDefinition(
        $value
      );
    }

    return $resources;
  }

  /**
   * {@inheritDoc}
   */
  public function lookupDefinitions(
    array $ids = [],
    array $options = [],
  ): array {
    $entities = [];

    $results = $this->lookupDefinitionsFromResources($ids);

    foreach ($results as $uuid => $values) {
      $entities[$uuid] = new ExternalEntityDefaultDefinition($values);
    }

    return $entities;
  }

  /**
   * {@inheritDoc}
   */
  public function searchDefinitions(
    string $resource,
    SearchQuery $query,
  ): ?ExternalEntitySearchDefinition {
    if (!isset($resource)) {
      return NULL;
    }

    $response = $this->makeHttpRequest(
      'GET',
      "definition/{$resource}/search",
      $query->format()
    );

    if (!is_array($response)) {
      return NULL;
    }

    return new ExternalEntitySearchDefinition($response);
  }

  /**
   * Fetch all external entity resources.
   *
   * @return array
   *   An array of external entity resources.
   */
  protected function fetchAllResources(): array {
    return $this->makeHttpRequest(
        'GET',
        'resource'
      ) ?? [];
  }

  /**
   * Get the external entity server status info.
   *
   * @return array
   *   An array of server status.
   */
  protected function getServerInfo(): array {
    return $this->makeHttpRequest(
      'GET',
      'status'
    ) ?? ['status' => 'error'];
  }

  /**
   * Get selected external entity resources.
   *
   * @return array
   *   An array of external entity resources.
   */
  protected function getResources(): array {
    return $this->getConfiguration()['resources'] ?: [];
  }

  /**
   * Get the external entity server domain URL.
   *
   * @return string|null
   */
  protected function getServerDomain(): ?string {
    return $this->getConfiguration()['server_domain'] ?? NULL;
  }

  /**
   * Check is external entity server connection is valid.
   *
   * @return bool
   *   Return TRUE if the server connection is valid; otherwise FALSE.
   */
  protected function isConnectionValid(): bool {
    return ($this->getServerInfo()['status'] ?? NULL) === static::EXTERNAL_ENTITY_SERVER_OK;
  }

  /**
   * Get external entity resource options.
   *
   * @return array
   *   An array of resource options.
   */
  protected function getResourceOptions(): array {
    return array_column(
      $this->fetchAllResources(),
      'type',
      'type'
    );
  }

  /**
   * Lookup definition IDs within all resources.
   *
   * @param array $ids
   *   An array definition ids to look up.
   *
   * @return array
   *   An array of the HTTP request contents.
   */
  protected function lookupDefinitionsFromResources(
    array $ids = [],
  ): array {
    $resources = [];
    $lookup_id_count = count($ids);

    foreach ($this->getResources() as $resource) {
      $contents = $this->makeHttpRequest(
        'GET',
        "definition/{$resource}/lookup",
        ['entity_uuids' => $ids]
      );

      if (!empty($contents)) {
        $resources += $contents;
        // No need to make additional HTTP requests if we have retrieved all our
        // resources based on the given ids.
        if ($lookup_id_count === count($resources)) {
          break;
        }
      }
    }

    return $resources;
  }

  /**
   * Make an HTTP request to a URI.
   *
   * @param string $method
   *   The request method.
   * @param string $uri
   *   The request URI.
   * @param array $query
   *   The request query parameters.
   *
   * @return mixed
   *   Return the response contents, if the content type is application/json,
   *   then an array is provided.
   */
  protected function makeHttpRequest(
    string $method,
    string $uri,
    array $query = []
  ): mixed {
    try {
      $client = $this->httpClientFactory->fromOptions([
        'base_uri' => "{$this->getServerDomain()}/external-entity/",
      ]);

      $response = $client->request($method, $uri, [
        'query' => $query,
      ] + $this->httpRequestOptions());

      if ($response->getStatusCode() === Response::HTTP_OK) {
        $contents = $response->getBody()->getContents();

        if (
          is_string($contents)
          && 'application/json' === $response->getHeaderLine('Content-Type')
        ) {
          return json_decode($contents, TRUE, 512, JSON_THROW_ON_ERROR);
        }

        return $contents;
      }
    } catch (\Exception|GuzzleException $exception) {
      DeprecationHelper::backwardsCompatibleCall(
        \Drupal::VERSION,
        '10.1.0',
        static fn() => Error::logException(\Drupal::logger('external_entity'), $exception),
        static fn() => watchdog_exception('external_entity', $exception)
      );
    }

    return NULL;
  }

  /**
   * Get the HTTP request options.
   *
   * @return array
   *   An array of HTTP request options.
   */
  protected function httpRequestOptions(): array {
    $options = [
      'on_stats' => function (TransferStats $stats) {
        $stats->getEffectiveUri();
      },
    ];

    if ($authentication = $this->getConnectionAuthenticationType()) {
      $authentication->alterRequestOptions(
        $options
      );
    }

    return $options;
  }

  /**
   * Get the connection authentication type instance.
   *
   * @return \Drupal\external_entity\Contracts\ExternalEntityAuthenticationTypeInterface|null
   *   Return the connection authentication type; otherwise NULL.
   */
  protected function getConnectionAuthenticationType(): ?ExternalEntityAuthenticationTypeInterface {
    if ($connection = $this->getConnection()) {
      return $connection->createAuthenticationTypeInstance();
    }

    return NULL;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc