simplytest-8.x-4.x-dev/modules/simplytest_projects/src/SimplytestProjectFetcher.php

modules/simplytest_projects/src/SimplytestProjectFetcher.php
<?php

namespace Drupal\simplytest_projects;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\simplytest_projects\Entity\SimplytestProject;
use GuzzleHttp\Client;
use Drupal\Component\Serialization\Json;

/**
 * Class SimplytestProjectFetcher
 *
 * @package Drupal\simplytest_projects
 */
class SimplytestProjectFetcher {

  /**
   * @var \Psr\Log\LoggerInterface
   */
  public $log;

  /**
   * @var \GuzzleHttp\Client
   */
  public $httpClient;

  /**
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  public $config;

  /**
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  public $entityTypeManager;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  public $connection;

  /**
   * SimplytestProjectFetcher constructor.
   *
   * @param \GuzzleHttp\Client $http_client
   * @param \Drupal\Core\Logger\LoggerChannelFactory $logger_factory
   * @param \Drupal\Core\Config\ConfigFactoryInterface
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface
   * @param \Drupal\Core\Database\Connection $connection
   */
  public function __construct(
    Client $http_client,
    LoggerChannelFactory $logger_factory,
    ConfigFactoryInterface $config_factory,
    EntityTypeManagerInterface $entity_type_manager,
    Connection $connection)
  {
    $this->httpClient = $http_client;
    $this->log = $logger_factory->get('simplytest_projects');
    $this->config = $config_factory->get('simplytest_projects.settings');
    $this->entityTypeManager = $entity_type_manager;
    $this->connection = $connection;
  }

  /**
   * Try to fetch project data from drupal.org's JSON / RESTWS API.
   *
   * @param $shortname
   *
   * @return array|bool
   * @throws \Exception
   */
  public function fetchProject($shortname) {
    $result = $this->httpClient->get(DrupalUrls::ORG_API . 'node.json?field_project_machine_name=' . urlencode($shortname));
    if ($result->getStatusCode() != 200 || empty($result->getBody())) {
      $this->log->warning('Failed to fetch initial data for %project (Request).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }

    // Try to parse the received JSON.
    $data = Json::decode($result->getBody());
    if ($data === null) {
      $this->log->warning('Failed to parse initial data for %project (json decode).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }

    // Did we find the project we searched for?
    if (count($data['list']) === 0 || !isset($data['list'][0])) {
      $this->log->warning('Failed to get initial data for %project (empty search result).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }
    $project_data = $data['list'][0];

    // Determine the type of this project.
    $project_type = strtolower(trim($project_data['field_project_type']));
    switch ($project_type) {

      case 'full':
        $sandbox = FALSE;
        break;

      case 'sandbox':
        $sandbox = TRUE;
        break;

      default:
        $this->log->warning('Failed to get initial data for %project (invalid project type "@type").',[
          '%project' => $shortname,
          '@type' => $project_type,
        ]);
        return FALSE;
    }

    // Determine project title.
    if (!isset($project_data['title'])) {
      $this->log->warning('Failed to get initial data for %project (no project title).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }
    $title = $project_data['title'];

    // Determine the project type term.
    if (!isset($project_data['type'])) {
      $this->log->warning('Failed to get initial data for %project (no project type).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }
    $type_term = $project_data['type'];

    // Find out the type by term.
    $type = ProjectTypes::getProjectType($type_term);
    if ($type === FALSE) {
      // Unknown type, error.
      $this->log->warning('Failed to get initial data for %project (Determine type for term "@term").', [
        '%project' => $shortname,
        '@term' => $type_term,
      ]);
      return FALSE;
    }

    // Get author name from project url.
    if ($sandbox) {
      if (!isset($project_data['url'])) {
        $this->log->warning('Failed to scrap user name from "%url".', [
          '%url' => $project_data['url'],
        ]);
        return FALSE;
      }
      $url_parts = explode('/', $project_data['url']);
      $creator = $url_parts[4];
    }
    else {
      // Creator is irrelevant for full projects; also the username is not in the URL of them.
      $creator = NULL;
    }

    // Build an array of all the new project data.
    $data = array(
      'title' => $title,
      'shortname' => $shortname,
      'sandbox' => (int) $sandbox,
      'type' => $type,
      'creator' => $creator,
    );

    $this->log->notice('Fetch initial data for %project.', [
      '%project' => $shortname,
    ]);

    // Now save the information about this project to database.
    $project = SimplytestProject::create($data);
    $project->save();

    $this->fetchVersions($shortname, TRUE);

    return $data;
  }

  /**
   * Fetches, saves and returns all available versions for a project.
   *
   * @param string $shortname
   *  The project's shortname to fetch available versions for.
   *
   * @return array|FALSE
   *  An associative array containing:
   *   - tags: Existing tags of the project.
   *   - heads: Existing heads of the project.
   */
  public function fetchVersions($shortname, $force = FALSE) {
    // Check whether project is known in database.
    $project_ids = $this->entityTypeManager
      ->getStorage('simplytest_project')
      ->getQuery('AND')
      ->condition('shortname', $shortname)
      ->execute();

    $project = SimplytestProject::load(reset($project_ids));

    if (!$project) {
      return FALSE;
    }

    if (!$force && $project->getTimestamp() > strtotime('-4 hour')) {
      return $project->getVersions();
    }

    $result = shell_exec('git ls-remote --tags ' . escapeshellarg($project->getGitWebUrl()));

    if (!empty($result) and strpos($result, 'refs/tags') === FALSE) {
      $this->log->warning('Failed to fetch version data for %project (Fetched tags).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }

    // Try to match out a list of tags of the raw HTML.
    $lines = explode("\n", $result);
    $tags = [];

    foreach ($lines as $line) {
      $tag_line = explode('refs/tags/', $line);
      if (!empty($tag_line[1])) {
        $tags[] = $tag_line[1];
      }
    }

    $result = shell_exec('git ls-remote --heads ' . escapeshellarg($project->getGitWebUrl()));

    // Try to match out a list of heads of the raw HTML.
    if (!empty($result) and strpos($result, 'refs/heads') === FALSE) {
      $this->log->warning('Failed to fetch version data for %project (Requested heads).', [
        '%project' => $shortname,
      ]);
      return FALSE;
    }

    $lines = explode("\n", $result);
    $heads = array();
    foreach ($lines as $line) {
      $head_line = explode('refs/heads/', $line);
      if (!empty($head_line[1])) {
        $heads[] = $head_line[1];
      }
    }

    // Blacklist filters.
    $blacklisted_versions = $this->config->get('blacklisted_versions');
    foreach ($blacklisted_versions as $blacklisted) {
      foreach ($tags as $key => $tag) {
        if (preg_match('!' . $blacklisted . '!', $tag)) {
          unset($tags[$key]);
        }
      }
      foreach ($heads as $key => $head) {
        if (preg_match('!' . $blacklisted . '!', $head)) {
          unset($heads[$key]);
        }
      }
    }

    // Now save the information about this project to database.
    $project->setVersions($tags, $heads);
    $project->set('timestamp', REQUEST_TIME);
    $project->save();

    $this->log->notice('Fetched version data for %project.', [
      '%project' => $shortname,
    ]);

    return $project->getVersions();
  }

  /**
   * Searches from the list of existing projects.
   *
   * @param $string
   *  The prefix string to search projects for.
   * @param int $range
   *  Maximum number of results to return.
   * @param array $types
   *  An array of project types to filter for.
   *
   * @return array
   *  An array of standard objects containing:
   *   - title: The human readable project title.
   *   - type: The projects type.
   *   - shortname: The project machine/shortname.
   *   - sandbox: Whether it's a sandbox.
   */
  public function searchFromProjects($string, $range = 100, $types = NULL) {
    $query = $this->connection->select('simplytest_project', 'p')
      ->fields('p', [
        'title',
        'shortname',
        'type',
        'sandbox',
      ])
      ->orderBy('sandbox', 'ASC')
      ->range(0, $range);

    $title_or_shortname = new Condition('OR');
    $title_or_shortname->condition('title', $this->connection->escapeLike($string) . '%', 'LIKE');
    $title_or_shortname->condition('shortname', $this->connection->escapeLike($string) . '%', 'LIKE');
    $query->condition($title_or_shortname);

    if ($types) {
      $types_or = new Condition('OR');
      foreach ($types as $type) {
        $types_or->condition('type', $type);
      }
      $query->condition($types_or);
    }

    $results = $query->execute()->fetchAll();

    $projects = [];
    foreach ($results as $result) {
      $projects[] = (array) $result;
    }

    return $projects;
  }

  /**
   * Fetch all the versions of a project.
   *
   * @param $project
   * @return array
   */
  public function fetchProjectVersions($project) {
    $versions = [];
    if ($versions_data = $this->fetchVersions($project)) {
      if ($versions_data !== FALSE) {
        usort($versions_data['tags'], 'version_compare');
        $versions_data['tags'] = array_reverse($versions_data['tags']);
        foreach ($versions_data['tags'] as $tag) {
          // Find out major / api version for structure.
          if (is_numeric($tag[0])) {
            $api_version = 'Drupal ' . $tag[0];
          }
          else {
            // Prefixed with space to get it sorted to the bottom.
            $api_version = 'Other';
          }
          $versions[$api_version][$tag] = $tag;
        }
        foreach ($versions_data['heads'] as $version) {
          $versions['Branches'][$version] = $version;
        }
      }
      // Sort it in reverse: Drupal 7, Drupal 6, Branches, Other.
      krsort($versions);
    }
    return $versions;
  }

}

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

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