You are here

private function EntityResource::handleIncomingEntity in CMS Content Sync 8

Same name and namespace in other branches
  1. 2.1.x src/Plugin/rest/resource/EntityResource.php \Drupal\cms_content_sync\Plugin\rest\resource\EntityResource::handleIncomingEntity()
  2. 2.0.x src/Plugin/rest/resource/EntityResource.php \Drupal\cms_content_sync\Plugin\rest\resource\EntityResource::handleIncomingEntity()

Parameters

string $api: The API {@see Flow}

string $entity_type_name: The entity type of the processed entity

string $entity_bundle: The bundle of the processed entity

string $entity_type_version: The version the config was saved for

array $data: For {@see ::ACTION_CREATE} and {@see ::ACTION_UPDATE}: the data for the entity. Will be passed to {@see SyncIntent}.

string $action: The {@see ::ACTION_*} to be performed on the entity

Return value

\Symfony\Component\HttpFoundation\Response the result (error, ignorance or success)

Throws

\Drupal\cms_content_sync\Exception\SyncException

\Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException

\Drupal\Component\Plugin\Exception\PluginNotFoundException

\Drupal\Core\Entity\EntityStorageException

3 calls to EntityResource::handleIncomingEntity()
EntityResource::delete in src/Plugin/rest/resource/EntityResource.php
Responds to entity DELETE requests.
EntityResource::patch in src/Plugin/rest/resource/EntityResource.php
Responds to entity PATCH requests.
EntityResource::post in src/Plugin/rest/resource/EntityResource.php
Responds to entity POST requests.

File

src/Plugin/rest/resource/EntityResource.php, line 355

Class

EntityResource
Provides entity interfaces for Content Sync, allowing Sync Core to request and manipulate entities.

Namespace

Drupal\cms_content_sync\Plugin\rest\resource

Code

private function handleIncomingEntity($api, $entity_type_name, $entity_bundle, $entity_type_version, array $data, $action) {
  if (self::PING_PARAMETER === $api) {
    return new ResourceResponse(SyncIntent::ACTION_DELETE === $action ? null : [
      'pong' => true,
    ]);
  }
  $entity_types = $this->entityTypeBundleInfo
    ->getAllBundleInfo();
  if (empty($entity_types[$entity_type_name])) {
    return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
      'message' => t(self::TYPE_HAS_NOT_BEEN_FOUND)
        ->render(),
    ], self::CODE_NOT_FOUND);
  }
  $is_dependency = isset($_GET['is_dependency']) && 'true' == $_GET['is_dependency'];
  $is_manual = isset($_GET['is_manual']) && 'true' == $_GET['is_manual'];
  $reason = $is_dependency ? PullIntent::PULL_AS_DEPENDENCY : ($is_manual ? PullIntent::PULL_MANUALLY : PullIntent::PULL_AUTOMATICALLY);
  $entity_uuid = null;
  $pool = Pool::getAll()[$api];
  if (empty($pool)) {
    \Drupal::logger('cms_content_sync')
      ->warning('@not PULL @action @entity_type:@bundle @uuid @reason: @message<br>Flow: @flow_id', [
      '@reason' => $reason,
      '@action' => $action,
      '@entity_type' => $entity_type_name,
      '@bundle' => $entity_bundle,
      '@uuid' => $entity_uuid,
      '@not' => 'NO',
      '@flow_id' => $api,
      '@message' => t('No pool config matches this request (@api).', [
        '@api' => $api,
      ])
        ->render(),
    ]);
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_UNKNOWN_POOL, $action, $reason);
    return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
      'message' => t(self::TYPE_HAS_NOT_BEEN_FOUND)
        ->render(),
    ], self::CODE_NOT_FOUND);
  }
  $flow = Flow::getFlowForApiAndEntityType($pool, $entity_type_name, $entity_bundle, $reason, $action);

  // Deletion requests will not provide the "is_dependency" query parameter.
  if (empty($flow) && SyncIntent::ACTION_DELETE == $action && PullIntent::PULL_AS_DEPENDENCY != $reason) {
    $flow = Flow::getFlowForApiAndEntityType($pool, $entity_type_name, $entity_bundle, PullIntent::PULL_AS_DEPENDENCY, $action);
    if (!empty($flow)) {
      $reason = PullIntent::PULL_AS_DEPENDENCY;
    }
  }
  if (empty($flow)) {
    \Drupal::logger('cms_content_sync')
      ->notice('@not PULL @action @entity_type:@bundle @uuid @reason: @message', [
      '@reason' => $reason,
      '@action' => $action,
      '@entity_type' => $entity_type_name,
      '@bundle' => $entity_bundle,
      '@uuid' => $entity_uuid,
      '@not' => 'NO',
      '@message' => t('No synchronization config matches this request (dependency: @dependency, manual: @manual).', [
        '@dependency' => $is_dependency ? 'YES' : 'NO',
        '@manual' => $is_manual ? 'YES' : 'NO',
      ])
        ->render(),
    ]);
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_NO_FLOW, $action, $reason);
    return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
      'message' => t(self::TYPE_HAS_NOT_BEEN_FOUND)
        ->render(),
    ], self::CODE_NOT_FOUND);
  }
  $operation = $pool
    ->getClient()
    ->getSyndicationService()
    ->handlePull($flow->id, $entity_type_name, $entity_bundle, $data);

  // DELETE requests only give the ID of the config, not their UUID. So we need to grab the UUID from our local
  // database before continuing.
  if (SyncIntent::ACTION_DELETE === $action && EntityHandlerPluginManager::isEntityTypeConfiguration($entity_type_name)) {
    $entity_uuid = \Drupal::entityTypeManager()
      ->getStorage($entity_type_name)
      ->load($operation
      ->getId())
      ->uuid();
  }
  else {
    $entity_uuid = $operation
      ->getUuid();
  }
  $local_version = Flow::getEntityTypeVersion($entity_type_name, $entity_bundle);

  // Allow DELETE requests- when an entity is deleted, the entity type definition may have changed in the meantime
  // but this doesn't prevent us from deleting it. The version is only important for creations and updates.
  if ($entity_type_version != $local_version && SyncIntent::ACTION_DELETE != $action) {
    \Drupal::logger('cms_content_sync')
      ->warning('@not PULL @action @entity_type:@bundle @uuid @reason: @message<br>Flow: @flow_id | Pool: @pool_id', [
      '@reason' => $reason,
      '@action' => $action,
      '@entity_type' => $entity_type_name,
      '@bundle' => $entity_bundle,
      '@uuid' => $entity_uuid,
      '@not' => 'NO',
      '@flow_id' => $api,
      '@pool_id' => $pool
        ->id(),
      '@message' => t('The requested entity type version @requested doesn\'t match the local entity type version @local.', [
        '@requested' => $entity_type_version,
        '@local' => $local_version,
      ])
        ->render(),
    ]);
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_DIFFERENT_VERSION, $action, $reason, $flow->id);
    return new ResourceResponse([
      'message' => t(self::TYPE_HAS_INCOMPATIBLE_VERSION)
        ->render(),
    ], self::CODE_NOT_FOUND);
  }
  try {
    $intent = new PullIntent($flow, $pool, $reason, $action, $entity_type_name, $entity_bundle, $operation);
    $status = $intent
      ->execute();
  } catch (SyncException $e) {
    $message = $e->parentException ? $e->parentException
      ->getMessage() : ($e->errorCode == $e
      ->getMessage() ? '' : $e
      ->getMessage());
    if ($message) {
      $message = t('Internal error @code: @message', [
        '@code' => $e->errorCode,
        '@message' => $message,
      ])
        ->render();
    }
    else {
      $message = t('Internal error @code', [
        '@code' => $e->errorCode,
      ])
        ->render();
    }
    \Drupal::logger('cms_content_sync')
      ->error('@not PULL @action @entity_type:@bundle @uuid @reason: @message' . "\n" . '@trace' . "\n" . '@request_body<br>Flow: @flow_id | Pool: @pool_id', [
      '@reason' => $reason,
      '@action' => $action,
      '@entity_type' => $entity_type_name,
      '@bundle' => $entity_bundle,
      '@uuid' => $entity_uuid,
      '@not' => 'NO',
      '@flow_id' => $api,
      '@pool_id' => $pool
        ->id(),
      '@message' => $message,
      '@trace' => ($e->parentException ? $e->parentException
        ->getTraceAsString() . "\n\n\n" : '') . $e
        ->getTraceAsString(),
      '@request_body' => json_encode($data),
    ]);
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_CONTENT_SYNC_ERROR, $action, $reason, $flow->id);
    return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
      'message' => t('SyncException @code: @message', [
        '@code' => $e->errorCode,
        '@message' => $e
          ->getMessage(),
      ])
        ->render(),
      'code' => $e->errorCode,
    ], 500);
  } catch (\Exception $e) {
    $message = $e
      ->getMessage();
    \Drupal::logger('cms_content_sync')
      ->error('@not PULL @action @entity_type:@bundle @uuid @reason: @message' . "\n" . '@trace' . "\n" . '@request_body<br>Flow: @flow_id | Pool: @pool_id', [
      '@reason' => $reason,
      '@action' => $action,
      '@entity_type' => $entity_type_name,
      '@bundle' => $entity_bundle,
      '@uuid' => $entity_uuid,
      '@not' => 'NO',
      '@flow_id' => $api,
      '@pool_id' => $pool
        ->id(),
      '@message' => $message,
      '@trace' => $e
        ->getTraceAsString(),
      '@request_body' => json_encode($data),
    ]);
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_INTERNAL_ERROR, $action, $reason, $flow->id);
    return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
      'message' => t('Unexpected error: @message', [
        '@message' => $e
          ->getMessage(),
      ])
        ->render(),
    ], 500);
  }
  if (!$status) {
    $this
      ->saveFailedPull($api, $entity_type_name, $entity_bundle, $entity_type_version, $entity_uuid, PullIntent::PULL_FAILED_HANDLER_DENIED, $action, $reason, $flow->id);
  }
  if ($status) {
    $entity = $intent
      ->getEntity();
    $url = null;
    if ($entity && $entity
      ->hasLinkTemplate('canonical')) {
      try {
        $url = $entity
          ->toUrl('canonical', [
          'absolute' => true,
        ])
          ->toString(true)
          ->getGeneratedUrl();
      } catch (\Exception $e) {
        throw new SyncException(SyncException::CODE_UNEXPECTED_EXCEPTION, $e);
      }
    }
    $response_body = $operation
      ->getResponseBody($url);

    // If we send data for DELETE requests, the Drupal Serializer will throw
    // a random error. So we just leave the body empty then.
    return new ModifiedResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : $response_body);
  }
  return new ResourceResponse(SyncIntent::ACTION_DELETE == $action ? null : [
    'message' => t('Entity is not configured to be pulled yet.')
      ->render(),
  ], 404);
}