class SpecFetcher in Apigee API Catalog 8
Same name and namespace in other branches
- 8.2 src/SpecFetcher.php \Drupal\apigee_api_catalog\SpecFetcher
Class SpecFetcher.
Hierarchy
- class \Drupal\apigee_api_catalog\SpecFetcher implements SpecFetcherInterface uses MessengerTrait, StringTranslationTrait
Expanded class hierarchy of SpecFetcher
1 string reference to 'SpecFetcher'
1 service uses SpecFetcher
File
- src/
SpecFetcher.php, line 41
Namespace
Drupal\apigee_api_catalogView source
class SpecFetcher implements SpecFetcherInterface {
use StringTranslationTrait;
use MessengerTrait;
/**
* Drupal\Core\File\FileSystemInterface definition.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* GuzzleHttp\ClientInterface definition.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The logger.
*
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* Constructs a new SpecFetcher.
*
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file_system service.
* @param \GuzzleHttp\ClientInterface $http_client
* The http_client service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The string translation.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
*/
public function __construct(FileSystemInterface $file_system, ClientInterface $http_client, EntityTypeManagerInterface $entityTypeManager, TranslationInterface $translation, MessengerInterface $messenger, LoggerInterface $logger) {
$this->fileSystem = $file_system;
$this->httpClient = $http_client;
$this->entityTypeManager = $entityTypeManager;
$this->stringTranslation = $translation;
$this->messenger = $messenger;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function fetchSpec(ApiDocInterface $apidoc) : string {
$spec_value = $apidoc
->get('spec')
->isEmpty() ? [] : $apidoc
->get('spec')
->getValue()[0];
// If "spec_file_source" uses URL, grab file from "file_link" and save it
// into the "spec" file field. The file_link field should already have
// validated that a valid file exists at that URL.
if ($apidoc
->get('spec_file_source')->value === ApiDocInterface::SPEC_AS_URL) {
// If the file_link field is empty, return without changes.
// TODO: The file link shouldn't be empty. Consider throwing an error.
if ($apidoc
->get('file_link')
->isEmpty()) {
return FALSE;
}
$source_uri = $apidoc
->get('file_link')
->getValue()[0]['uri'];
$source_uri = Url::fromUri($source_uri, [
'absolute' => TRUE,
])
->toString();
$request = new Request('GET', $source_uri);
$options = [
'exceptions' => TRUE,
'allow_redirects' => [
'strict' => TRUE,
],
];
// Generate conditional GET header.
if (!$apidoc
->get('fetched_timestamp')
->isEmpty()) {
$request = $request
->withAddedHeader('If-Modified-Since', gmdate(DateTimePlus::RFC7231, $apidoc
->get('fetched_timestamp')->value));
}
try {
$response = $this->httpClient
->send($request, $options);
} catch (GuzzleException $e) {
$this
->log(LogLevel::ERROR, 'API Doc %label: Could not retrieve OpenAPI specification file located at %url.', [
'%url' => $source_uri,
'%label' => $apidoc
->label(),
]);
return self::STATUS_ERROR;
}
// In case of a 304 Not Modified there are no changes, but update
// last fetched timestamp.
if ($response
->getStatusCode() === 304) {
$apidoc
->set('fetched_timestamp', time());
return self::STATUS_UNCHANGED;
}
$data = (string) $response
->getBody();
if (($file_size = $response
->getBody()
->getSize()) && $file_size < 1) {
$this
->log(LogLevel::ERROR, 'API Doc %label: OpenAPI specification file located at %url is empty.', [
'%url' => $source_uri,
'%label' => $apidoc
->label(),
]);
return self::STATUS_ERROR;
}
// Only save file if it hasn't been fetched previously.
$data_md5 = md5($data);
$prev_md5 = $apidoc
->get('spec_md5')
->isEmpty() ? NULL : $apidoc
->get('spec_md5')->value;
if ($prev_md5 != $data_md5) {
$filename = $this->fileSystem
->basename($source_uri);
$specs_definition = $apidoc
->getFieldDefinition('spec')
->getItemDefinition();
$target_dir = $specs_definition
->getSetting('file_directory');
$uri_scheme = $specs_definition
->getSetting('uri_scheme');
$destination = "{$uri_scheme}://{$target_dir}/";
try {
$this
->checkRequirements($destination);
$file = file_save_data($data, $destination . $filename, FileSystemInterface::EXISTS_RENAME);
if (empty($file)) {
throw new \Exception('Could not save API Doc specification file.');
}
} catch (\Exception $e) {
$this
->log(LogLevel::ERROR, 'Error while saving API Doc spec file from URL on API Doc ID: %id. Error: %error', [
'%id' => $apidoc
->id(),
'%error' => $e
->getMessage(),
]);
return self::STATUS_ERROR;
}
$spec_value = [
'target_id' => $file
->id(),
] + $spec_value;
$apidoc
->set('spec', $spec_value);
$apidoc
->set('spec_md5', $data_md5);
$apidoc
->set('fetched_timestamp', time());
return self::STATUS_UPDATED;
}
}
return self::STATUS_ERROR;
}
/**
* Log a message, and optionally display it on the UI.
*
* @param string $level
* The Error level.
* @param string $message
* The message.
* @param array $params
* Optional parameters array.
*/
private function log(string $level, string $message, array $params = []) {
$this->logger
->log($level, $message, $params);
// Show the message.
$this
->messenger()
->addMessage($this
->t($message, $params), static::LOG_LEVEL_MAP[$level] ?? MessengerInterface::TYPE_ERROR);
}
/**
* Checks requirements for saving of a file spec.
*
* If a requirement is not fulfilled it throws an exception.
*
* @param string $destination
* The specification file destination directory, including scheme.
*
* @throws \Exception
*/
private function checkRequirements(string $destination) : void {
// If using private filesystem, check that it's been configured.
if (strpos($destination, 'private://') === 0 && !$this
->isPrivateFileSystemConfigured()) {
throw new \Exception('Private filesystem has not been configured.');
}
if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
throw new \Exception('Could not prepare API Doc specification file destination directory.');
}
}
/**
* Checks whether the private filesystem is configured.
*
* @return bool
* True if configured, FALSE otherwise.
*/
private function isPrivateFileSystemConfigured() : bool {
return (bool) $this->fileSystem
->realpath('private://');
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
SpecFetcher:: |
protected | property | The entity type manager. | |
SpecFetcher:: |
protected | property | Drupal\Core\File\FileSystemInterface definition. | |
SpecFetcher:: |
protected | property | GuzzleHttp\ClientInterface definition. | |
SpecFetcher:: |
private | property | The logger. | |
SpecFetcher:: |
private | function | Checks requirements for saving of a file spec. | |
SpecFetcher:: |
public | function |
Fetch OpenAPI specification file from URL. Overrides SpecFetcherInterface:: |
|
SpecFetcher:: |
private | function | Checks whether the private filesystem is configured. | |
SpecFetcher:: |
private | function | Log a message, and optionally display it on the UI. | |
SpecFetcher:: |
public | function | Constructs a new SpecFetcher. | |
SpecFetcherInterface:: |
public | constant | ||
SpecFetcherInterface:: |
public | constant | The status when an error happened during the fetch operation. | |
SpecFetcherInterface:: |
public | constant | The status when a spec update finds the remote file unchanged. | |
SpecFetcherInterface:: |
public | constant | The status when a spec update completed successfully. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |