You are here

function apigee_edge_user_presave in Apigee Edge 8

Implements hook_user_presave().

TODO Take (configurable?) actions if a user could not be saved in Drupal but it has been in Apigee Edge.

File

./apigee_edge.module, line 1276
Copyright 2018 Google Inc.

Code

function apigee_edge_user_presave(UserInterface $account) {

  // If the developer-user synchronization is in progress, then saving
  // developers while saving Drupal user should be avoided.
  if (_apigee_edge_is_sync_in_progress()) {
    return;
  }

  /** @var \Drupal\apigee_edge\UserDeveloperConverterInterface $user_developer */
  $user_developer = \Drupal::service('apigee_edge.converter.user_developer');

  /** @var \Drupal\apigee_edge\FieldAttributeConverterInterface $field_to_attribute */
  $field_to_attribute = \Drupal::service('apigee_edge.converter.field_attribute');

  /** @var \Drupal\Core\Logger\LoggerChannelInterface $logger */
  $logger = \Drupal::service('logger.channel.apigee_edge');
  try {

    /** @var \Drupal\apigee_edge\Entity\Developer $developer */
    $result = $user_developer
      ->convertUser($account);

    // There were no changes.
    if ($result
      ->getSuccessfullyAppliedChanges() === 0) {
      return;
    }

    // Log problems occurred meanwhile the conversion process.
    foreach ($result
      ->getProblems() as $conversionProblem) {
      $context = [
        '%mail' => $account
          ->getEmail(),
        'link' => $account
          ->toLink()
          ->toString(),
      ];
      if ($conversionProblem instanceof UserDeveloperConversionUserFieldDoesNotExistException) {
        $message = "Skipping %mail developer's %attribute_name attribute update because %field_name field does not exist.";
        $context['%field_name'] = $conversionProblem
          ->getFieldName();
        $context['%attribute_name'] = $field_to_attribute
          ->getAttributeName($conversionProblem
          ->getFieldName());
        $logger
          ->warning($message, $context);
      }
      elseif ($conversionProblem instanceof UserDeveloperConversionNoStorageFormatterFoundException) {
        $message = "Skipping %mail developer's %attribute_name attribute update because there is no available storage formatter for %field_type field type.";
        $context['%field_type'] = $conversionProblem
          ->getFieldDefinition()
          ->getType();
        $context['%attribute_name'] = $field_to_attribute
          ->getAttributeName($conversionProblem
          ->getFieldDefinition()
          ->getName());
        $logger
          ->warning($message, $context);
      }
      else {
        $logger
          ->warning($conversionProblem
          ->getMessage());
      }
    }
    $developer = $result
      ->getDeveloper();
    $developer
      ->save();
  } catch (\Exception $exception) {
    $previous = $exception
      ->getPrevious();
    $context = [
      '@developer' => $account
        ->getEmail(),
      '@message' => (string) $exception,
      // UID 1 (created meanwhile the install process by config_installer) is
      // not a new account.
      // @see \Drupal\config_installer\Form\SiteConfigureForm::submitForm()
      // Also, id() returns a string not an integer.
      '@operation' => $account
        ->isNew() || $account
        ->id() == 1 ? 'create' : 'update',
    ];
    if ($previous instanceof ClientErrorException && $previous
      ->getEdgeErrorCode()) {
      if ($previous
        ->getEdgeErrorCode() === Developer::APIGEE_EDGE_ERROR_CODE_DEVELOPER_DOES_NOT_EXISTS) {
        \Drupal::service('logger.channel.apigee_edge')
          ->info('Could not update @developer developer entity because it does not exist on Apigee Edge. Automatically trying to create a new developer entity.', $context);
        try {

          // Forcibly mark developer entity as new to send POST request to Edge
          // instead of PUT. This should be a better way then clearing
          // "originalEmail" property's value on the entity.
          $developer
            ->enforceIsNew(TRUE);
          $developer
            ->save();
        } catch (\Exception $exception) {
          $context = [
            '@developer' => $account
              ->getEmail(),
            '@message' => (string) $exception,
          ];
          $context += Error::decodeException($exception);
          $logger
            ->error('Could not create developer entity: @developer. @message %function (line %line of %file). <pre>@backtrace_string</pre>', $context);
        }
      }
      elseif ($previous
        ->getEdgeErrorCode() === Developer::APIGEE_EDGE_ERROR_CODE_DEVELOPER_ALREADY_EXISTS) {
        $logger
          ->info($previous
          ->getMessage());
        $developer = Developer::load($account
          ->getEmail());
        if ($developer) {
          $developer_id = $developer
            ->getDeveloperId();

          // If a user could register on the portal with an email address
          // that already belongs to a developer on Apigee Edge then override
          // its stored developer data there with the new one.
          if (isset($account->{APIGEE_EDGE_USER_REGISTRATION_SOURCE}) && $account->{APIGEE_EDGE_USER_REGISTRATION_SOURCE} === 'user_register_form') {
            $developer = $user_developer
              ->convertUser($account);
            $developer
              ->setDeveloperId($developer_id);
            $developer
              ->enforceIsNew(FALSE);
            try {
              $developer
                ->save();
            } catch (ApiException $exception) {
              $logger
                ->error("Unable to update existing @developer developer's data after registered on the portal.", $context);
            }
          }
        }
        else {
          $logger
            ->error("Unable to save @developer developer's developer id on user.", $context);
        }
      }
      elseif ($previous
        ->getEdgeErrorCode() === Developer::APIGEE_HYBRID_ERROR_CODE_DEVELOPER_EMAIL_MISMATCH) {

        // Apigee X and Hybrid runtime v1.5.0 and v1.5.1 a call to change the developer's
        // email address will not work so need to prevent user email update on
        // Drupal as well.
        // @see https://github.com/apigee/apigee-client-php/issues/153
        // @see https://github.com/apigee/apigee-edge-drupal/issues/587
        throw new DeveloperUpdateFailedException($account
          ->getEmail(), "Developer @email profile cannot be updated. " . $previous
          ->getMessage());
      }
    }
    else {
      $context += Error::decodeException($exception);
      $logger
        ->error('Could not @operation developer entity: @developer. @message %function (line %line of %file). <pre>@backtrace_string</pre>', $context);
    }
  }
}