You are here

class ContentHubEntityExportController in Acquia Content Hub 8

Controller for Content Hub Export Entities using bulk upload.

Hierarchy

Expanded class hierarchy of ContentHubEntityExportController

2 files declare their use of ContentHubEntityExportController
ContentHubEntityExportControllerTest.php in tests/src/Unit/Controller/ContentHubEntityExportControllerTest.php
ContentHubExportQueueBase.php in src/Plugin/QueueWorker/ContentHubExportQueueBase.php
1 string reference to 'ContentHubEntityExportController'
acquia_contenthub.services.yml in ./acquia_contenthub.services.yml
acquia_contenthub.services.yml
1 service uses ContentHubEntityExportController
acquia_contenthub.acquia_contenthub_export_entities in ./acquia_contenthub.services.yml
Drupal\acquia_contenthub\Controller\ContentHubEntityExportController

File

src/Controller/ContentHubEntityExportController.php, line 23

Namespace

Drupal\acquia_contenthub\Controller
View source
class ContentHubEntityExportController extends ControllerBase {

  /**
   * Format for the cdf.
   *
   * @var string
   */
  protected $format = 'acquia_contenthub_cdf';

  /**
   * Logger.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $loggerFactory;

  /**
   * Content Hub Client Manager.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientManager
   */
  protected $clientManager;

  /**
   * Content Hub Entity Manager.
   *
   * @var \Drupal\acquia_contenthub\EntityManager
   */
  protected $entityManager;

  /**
   * Content Hub Entities Tracking.
   *
   * @var \Drupal\acquia_contenthub\ContentHubEntitiesTracking
   */
  protected $contentHubEntitiesTracking;

  /**
   * The Entity CDF Normalizer.
   *
   * @var \Drupal\acquia_contenthub\Normalizer\ContentEntityCdfNormalizer
   */
  protected $entityCdfNormalizer;

  /**
   * Content Hub Export Queue Controller.
   *
   * @var \Drupal\acquia_contenthub\Controller\ContentHubExportQueueController
   */
  protected $exportQueueController;

  /**
   * Entity Repository.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * The account switcher service.
   *
   * @var \Drupal\acquia_contenthub\ContentHubInternalRequest
   */
  protected $internalRequest;

  /**
   * The flag to check whether the export queue is active or not.
   *
   * @var bool
   */
  protected $exportQueueEnabled;

  /**
   * Public Constructor.
   *
   * @param \Drupal\acquia_contenthub\Client\ClientManagerInterface $client_manager
   *   The client manager.
   * @param \Drupal\acquia_contenthub\EntityManager $entity_manager
   *   The Content Hub Entity Manager.
   * @param \Drupal\acquia_contenthub\ContentHubEntitiesTracking $contenthub_entities_tracking
   *   The table where all entities are tracked.
   * @param \Drupal\acquia_contenthub\Normalizer\ContentEntityCdfNormalizer $acquia_contenthub_normalizer
   *   The Content Hub Normalizer.
   * @param \Drupal\acquia_contenthub\Controller\ContentHubExportQueueController $export_queue_controller
   *   The Content Hub Export Queue Controller.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   Entity Repository.
   * @param \Drupal\acquia_contenthub\ContentHubInternalRequest $internal_request
   *   The Content Hub Internal Request Service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger factory.
   */
  public function __construct(ClientManagerInterface $client_manager, EntityManager $entity_manager, ContentHubEntitiesTracking $contenthub_entities_tracking, ContentEntityCdfNormalizer $acquia_contenthub_normalizer, ContentHubExportQueueController $export_queue_controller, EntityRepositoryInterface $entity_repository, ContentHubInternalRequest $internal_request, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
    $this->clientManager = $client_manager;
    $this->entityManager = $entity_manager;
    $this->contentHubEntitiesTracking = $contenthub_entities_tracking;
    $this->entityCdfNormalizer = $acquia_contenthub_normalizer;
    $this->exportQueueController = $export_queue_controller;
    $this->entityRepository = $entity_repository;
    $this->internalRequest = $internal_request;
    $entity_config = $config_factory
      ->get('acquia_contenthub.entity_config');
    $this->exportQueueEnabled = (bool) $entity_config
      ->get('export_with_queue');
    $this->loggerFactory = $logger_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('acquia_contenthub.client_manager'), $container
      ->get('acquia_contenthub.entity_manager'), $container
      ->get('acquia_contenthub.acquia_contenthub_entities_tracking'), $container
      ->get('acquia_contenthub.normalizer.entity.acquia_contenthub_cdf'), $container
      ->get('acquia_contenthub.acquia_contenthub_export_queue'), $container
      ->get('entity.repository'), $container
      ->get('acquia_contenthub.internal_request'), $container
      ->get('config.factory'), $container
      ->get('logger.factory'));
  }

  /**
   * Export entities to Content Hub (using the queue if enabled).
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface[] $candidate_entities
   *   An array of entities (uuid, entity object) to be exported to Content Hub.
   *
   * @return bool
   *   TRUE if we are using the export queue, FALSE otherwise.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function exportEntities(array $candidate_entities) {
    $candidate_entities = array_filter($candidate_entities);
    if ($this->exportQueueEnabled) {

      // These entities that reacted to a hook should always be re-exported,
      // then mark them as "queued" in the tracking table so they do not get
      // confused with exported entities (even if a previous version of the
      // entity has been previously exported).
      // This is fine because they will change back to exported status in the
      // tracking table after the queue runs, but we want to make sure that
      // these entities are taken again when collecting dependencies.
      // Dependencies are not exported if they have an entry in the tracking
      // table that says that they have been previously "exported".
      $this->exportQueueController
        ->enqueueExportEntities($candidate_entities);
      foreach ($candidate_entities as $candidate_entity) {
        $this
          ->queueExportedEntity($candidate_entity);
      }
      return TRUE;
    }
    $exported_entities = [];
    $bulk_url_array = [];
    foreach ($candidate_entities as $candidate_entity) {
      $entity_type = $candidate_entity
        ->getEntityTypeId();
      $entity_id = $candidate_entity
        ->id();
      $bulk_url_array[$entity_type][$entity_id] = $entity_id;
      $context['query_params']['include_references'] = 'true';
      $exported_entity = $this->entityCdfNormalizer
        ->normalize($candidate_entity, 'acquia_contenthub_cdf', $context);
      $exported_entity['entities'] = !empty($exported_entity) && is_array($exported_entity['entities']) ? $exported_entity['entities'] : [];
      foreach ($exported_entity['entities'] as $key => $ch_entity) {
        $exported_entity['entities'][$key] = Json::decode($ch_entity
          ->json());
      }
      $exported_entities = array_merge($exported_entities, $exported_entity['entities']);
    }

    // Eliminate duplicates.
    $exported_cdfs = [];
    foreach ($exported_entities as $cdf) {
      $exported_cdfs[$cdf['uuid']] = $cdf;
    }

    // Now implode parameters.
    foreach ($bulk_url_array as $entity_type => $entities) {
      $bulk_url_array[$entity_type] = implode(',', $entities);
    }
    $resource_url = $this->entityManager
      ->getBulkResourceUrl($bulk_url_array);
    if (!empty($exported_cdfs)) {
      if ($this->entityManager
        ->updateRemoteEntities($resource_url) !== FALSE) {

        // Setting up INITIATED status to all tracked exported entities.
        foreach ($exported_cdfs as $exported_entity) {

          // Obtaining the entity ID from the entity.
          $this
            ->trackExportedEntity($exported_entity);
        }
      }
    }

    // Log list of UUIDs being exported.
    $log_message = 'Drupal sending export request to Content Hub for UUIDs @uuids.';
    $context = [
      '@uuids' => implode(', ', array_keys($exported_cdfs)),
    ];
    $this->loggerFactory
      ->get('acquia_contenthub')
      ->debug($log_message, $context);
    return FALSE;
  }

  /**
   * Collects all Drupal Entities that needs to be sent to Hub.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function getDrupalEntities() {
    $normalized = [
      'entities' => [],
    ];
    $normalized_entities = [];

    // phpcs:ignore
    $entities = $_GET;
    foreach ($entities as $entity => $entity_ids) {
      $ids = explode(',', $entity_ids);
      foreach ($ids as $id) {
        $id = trim($id);
        try {
          $bulk_cdf = $this->internalRequest
            ->getEntityCDFByInternalRequest($entity, $id);
          $bulk_cdf = array_pop($bulk_cdf);
          if (is_array($bulk_cdf)) {
            foreach ($bulk_cdf as $cdf) {
              $normalized_entities[$cdf['uuid']] = $cdf;
            }
          }
        } catch (\Exception $e) {

          // Do nothing, route does not exist, but report it.
          $args = [
            '@type' => $entity,
            '@id' => $id,
            '@msg' => $e
              ->getMessage(),
          ];
          $this->loggerFactory
            ->get('acquia_contenthub')
            ->error('Could not obtain the CDF for entity (@type, @id) : @msg', $args);
        }
      }
    }

    // If we reach here, then there was no error processing the sub-requests.
    // Save all entities in the tracking entities and return the response.
    $normalized['entities'] = array_values($normalized_entities);
    if ($this
      ->isRequestFromAcquiaContentHub()) {
      foreach ($normalized['entities'] as $cdf) {
        $this
          ->trackExportedEntity($cdf, TRUE);
      }
    }
    return JsonResponse::create($normalized);
  }

  /**
   * Resolves whether the current request comes from Acquia Content Hub or not.
   *
   * @return bool
   *   TRUE if request comes from Content Hub, FALSE otherwise.
   */
  public function isRequestFromAcquiaContentHub() {
    $request = Request::createFromGlobals();

    // This function already sits behind an access check to confirm that the
    // request for CDF came from Content Hub, but just in case that access is
    // opened to authenticated users or during development, we are using a
    // condition to prevent false tracking of entities as exported.
    $headers = array_map('current', $request->headers
      ->all());
    if (isset($headers['user-agent']) && strpos($headers['user-agent'], 'Go-http-client') !== FALSE) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Save this entity in the Tracking table.
   *
   * @param array $cdf
   *   The entity that has to be tracked as exported entity.
   * @param bool $set_exported
   *   Set the export status to exported in the tracking table.
   *
   * @return bool
   *   TRUE if this entity was saved in the tracking table, FALSE otherwise.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function trackExportedEntity(array $cdf, $set_exported = FALSE) {
    $exported_entity = $this->contentHubEntitiesTracking
      ->loadExportedByUuid($cdf['uuid']);
    if ($exported_entity) {
      $exported_entity
        ->setModified($cdf['modified'])
        ->setInitiated();
      if ($set_exported) {
        $exported_entity
          ->setExported();
      }
      return $this->contentHubEntitiesTracking
        ->save();
    }
    $entity = $this->entityRepository
      ->loadEntityByUuid($cdf['type'], $cdf['uuid']);
    if (!$entity) {
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->warning('Cannot create record in the tracking table, because the entity cannot be loaded in Drupal. uuid: @uuid type: @type', [
        '@uuid' => $cdf['uuid'],
        '@type' => $cdf['type'],
      ]);
      return FALSE;
    }

    // Add a new tracking record with exported status set, and
    // imported status empty.
    $exported_entity = $this->contentHubEntitiesTracking
      ->setExportedEntity($cdf['type'], $entity
      ->id(), $cdf['uuid'], $cdf['modified'], $this->contentHubEntitiesTracking
      ->getSiteOrigin());
    if (!$exported_entity) {

      // In case of $exported_entity == FALSE.
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->warning('Cannot save into Acquia ContentHub tracking table; entity UUID: @uuid, @backtrack', [
        '@uuid' => $entity
          ->uuid(),
        '@backtrack' => __FUNCTION__,
      ]);
      return FALSE;
    }
    if ($set_exported) {
      $exported_entity
        ->setExported();
    }

    // Now save the entity.
    $result = $this->contentHubEntitiesTracking
      ->save();
    if ($result === FALSE) {
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->debug('Unable to save entity to the tracking table: entity_type = @type, entity_uuid = @uuid, entity_id = @id, origin = @origin, mmodified = @modified.', [
        '@uuid' => $this->contentHubEntitiesTracking
          ->getUuid(),
        '@type' => $this->contentHubEntitiesTracking
          ->getEntityType(),
        '@id' => $this->contentHubEntitiesTracking
          ->getEntityId(),
        '@origin' => $this->contentHubEntitiesTracking
          ->getOrigin(),
        '@modified' => $this->contentHubEntitiesTracking
          ->getModified(),
      ]);
    }
    return $result;
  }

  /**
   * Sets a record for an entity to be queued for export.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity that has to be queued for export.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function queueExportedEntity(ContentEntityInterface $entity) {
    $exported_entity = $this->contentHubEntitiesTracking
      ->loadExportedByUuid($entity
      ->uuid());
    if ($exported_entity) {
      $exported_entity
        ->setQueued();
      $this->contentHubEntitiesTracking
        ->save();
      return;
    }
    $entity = $this->entityRepository
      ->loadEntityByUuid($entity
      ->getEntityTypeId(), $entity
      ->uuid());
    if (!$entity) {
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->warning('Cannot create record in the tracking table, because the entity cannot be loaded in Drupal. uuid: @uuid type: @type', [
        '@uuid' => $entity
          ->uuid(),
        '@type' => $entity
          ->getEntityTypeId(),
      ]);
      return;
    }

    // Add a new tracking record with queued status set, and
    // imported status empty.
    $exported_entity = $this->contentHubEntitiesTracking
      ->setExportedEntity($entity
      ->getEntityTypeId(), $entity
      ->id(), $entity
      ->uuid(), date('c'), $this->contentHubEntitiesTracking
      ->getSiteOrigin());
    if (!$exported_entity) {
      $this->loggerFactory
        ->get('acquia_contenthub')
        ->warning('Cannot save into Acquia ContentHub tracking table; entity UUID: @uuid, @backtrack', [
        '@uuid' => $entity
          ->uuid(),
        '@backtrack' => __FUNCTION__,
      ]);
      return;
    }
    $exported_entity
      ->setQueued();
    $this->contentHubEntitiesTracking
      ->save();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ContentHubEntityExportController::$clientManager protected property Content Hub Client Manager.
ContentHubEntityExportController::$contentHubEntitiesTracking protected property Content Hub Entities Tracking.
ContentHubEntityExportController::$entityCdfNormalizer protected property The Entity CDF Normalizer.
ContentHubEntityExportController::$entityManager protected property Content Hub Entity Manager. Overrides ControllerBase::$entityManager
ContentHubEntityExportController::$entityRepository protected property Entity Repository.
ContentHubEntityExportController::$exportQueueController protected property Content Hub Export Queue Controller.
ContentHubEntityExportController::$exportQueueEnabled protected property The flag to check whether the export queue is active or not.
ContentHubEntityExportController::$format protected property Format for the cdf.
ContentHubEntityExportController::$internalRequest protected property The account switcher service.
ContentHubEntityExportController::$loggerFactory protected property Logger. Overrides LoggerChannelTrait::$loggerFactory
ContentHubEntityExportController::create public static function Instantiates a new instance of this class. Overrides ControllerBase::create
ContentHubEntityExportController::exportEntities public function Export entities to Content Hub (using the queue if enabled).
ContentHubEntityExportController::getDrupalEntities public function Collects all Drupal Entities that needs to be sent to Hub.
ContentHubEntityExportController::isRequestFromAcquiaContentHub public function Resolves whether the current request comes from Acquia Content Hub or not.
ContentHubEntityExportController::queueExportedEntity public function Sets a record for an entity to be queued for export.
ContentHubEntityExportController::trackExportedEntity public function Save this entity in the Tracking table.
ContentHubEntityExportController::__construct public function Public Constructor.
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityTypeManager protected property The entity type manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.