Browse Source

Removing gemini

pull/822/head
dannylamb 5 years ago
parent
commit
ce4d1fdc4d
  1. 65
      islandora.module
  2. 9
      islandora.services.yml
  3. 47
      src/Form/IslandoraSettingsForm.php
  4. 47
      src/GeminiClientFactory.php
  5. 168
      src/GeminiLookup.php
  6. 293
      tests/src/Kernel/GeminiLookupTest.php

65
islandora.module

@ -18,6 +18,7 @@ use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\islandora\Form\IslandoraSettingsForm; use Drupal\islandora\Form\IslandoraSettingsForm;
use Drupal\islandora\GeminiLookup; use Drupal\islandora\GeminiLookup;
@ -466,27 +467,53 @@ function islandora_entity_view(array &$build, EntityInterface $entity, EntityVie
// Ensure the entity matches the route. // Ensure the entity matches the route.
if ($entity === $route_match_item) { if ($entity === $route_match_item) {
if ($display->getComponent('field_gemini_uri')) { if ($display->getComponent('field_gemini_uri')) {
$gemini = \Drupal::service('islandora.gemini.lookup'); $mapper = \Drupal::service('islandora.entity_mapper');
if ($gemini instanceof GeminiLookup) { $flysystem_config = Settings::get('flysystem');
$fedora_uri = $gemini->lookup($entity); $fedora_root = $flysystem_config['fedora']['config']['root'];
if (!is_null($fedora_uri)) { $fedora_root = rtrim($fedora_root, '/');
$build['field_gemini_uri'] = [
'#type' => 'container', if ($entity->getEntityTypeId() == 'media') {
'#attributes' => [ // Check if the source file is in Fedora or not.
'id' => 'field-gemini-uri', $media_source_service = \Drupal::service('islandora.media_source_service');
], $source_file = $media_source_service->getSourceFile($entity);
'internal_label' => [ $uri = $source_file->getFileUri();
'#type' => 'item', $scheme = \Drupal::service('file_system')->uriScheme($uri);
'#title' => t('Fedora URI'), $flysystem_config = Settings::get('flysystem');
'internal_uri' => [
'#type' => 'link', // Use the file's path if it's in fedora. Otherwise do the UUID -> pair tree thang.
'#title' => t("@url", ['@url' => $fedora_uri]), if (isset($flysystem_config[$scheme]) && $flysystem_config[$scheme]['driver'] == 'fedora') {
'#url' => Url::fromUri($fedora_uri), $parts = parse_url($uri);
], $path = $parts['host'] . $parts['path'];
],
];
} }
else {
$path = $mapper->getFedoraPath($source_file->uuid());
}
$path = trim($path, '/');
$fedora_uri = "$fedora_root/$path/fcr:metadata";
}
else {
// All non-media entities do the UUID -> pair tree thang.
$path = $mapper->getFedoraPath($entity->uuid());
$path = trim($path, '/');
$fedora_uri = "$fedora_root/$path";
} }
// Stuff the fedora url into the pseudo field.
$build['field_gemini_uri'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'field-gemini-uri',
],
'internal_label' => [
'#type' => 'item',
'#title' => t('Fedora URI'),
'internal_uri' => [
'#type' => 'link',
'#title' => t("@url", ['@url' => $fedora_uri]),
'#url' => Url::fromUri($fedora_uri),
],
],
];
} }
} }
} }

9
islandora.services.yml

@ -52,10 +52,5 @@ services:
islandora.utils: islandora.utils:
class: Drupal\islandora\IslandoraUtils class: Drupal\islandora\IslandoraUtils
arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager'] arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager']
islandora.gemini.client: islandora.entity_mapper:
class: Islandora\Crayfish\Commons\Client\GeminiClient class: Islandora\Crayfish\Commons\EntityMapper\EntityMapper
factory: ['Drupal\islandora\GeminiClientFactory', create]
arguments: ['@config.factory', '@logger.channel.islandora']
islandora.gemini.lookup:
class: Drupal\islandora\GeminiLookup
arguments: ['@islandora.gemini.client', '@jwt.authentication.jwt', '@islandora.media_source_service', '@http_client', '@logger.channel.islandora']

47
src/Form/IslandoraSettingsForm.php

@ -25,7 +25,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
const BROKER_USER = 'broker_user'; const BROKER_USER = 'broker_user';
const BROKER_PASSWORD = 'broker_password'; const BROKER_PASSWORD = 'broker_password';
const JWT_EXPIRY = 'jwt_expiry'; const JWT_EXPIRY = 'jwt_expiry';
const GEMINI_URL = 'gemini_url';
const GEMINI_PSEUDO = 'gemini_pseudo_bundles'; const GEMINI_PSEUDO = 'gemini_pseudo_bundles';
const FEDORA_URL = 'fedora_url'; const FEDORA_URL = 'fedora_url';
const TIME_INTERVALS = [ const TIME_INTERVALS = [
@ -148,12 +147,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
), ),
]; ];
$form[self::GEMINI_URL] = [
'#type' => 'textfield',
'#title' => $this->t('Gemini URL'),
'#default_value' => $config->get(self::GEMINI_URL),
];
$flysystem_config = Settings::get('flysystem'); $flysystem_config = Settings::get('flysystem');
if ($flysystem_config != NULL) { if ($flysystem_config != NULL) {
$fedora_url = $flysystem_config['fedora']['config']['root']; $fedora_url = $flysystem_config['fedora']['config']['root'];
@ -269,45 +262,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
); );
} }
} }
// Needed for the elseif below.
$pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO));
// Validate Gemini URL by validating the URL.
$geminiUrlValue = trim($form_state->getValue(self::GEMINI_URL));
if (!empty($geminiUrlValue)) {
try {
$geminiUrl = Url::fromUri($geminiUrlValue);
$client = GeminiClient::create($geminiUrlValue, $this->logger('islandora'));
$client->findByUri('http://example.org');
}
// Uri is invalid.
catch (\InvalidArgumentException $e) {
$form_state->setErrorByName(
self::GEMINI_URL,
$this->t(
'Cannot parse URL @url',
['@url' => $geminiUrlValue]
)
);
}
// Uri is not available.
catch (ConnectException $e) {
$form_state->setErrorByName(
self::GEMINI_URL,
$this->t(
'Cannot connect to URL @url',
['@url' => $geminiUrlValue]
)
);
}
}
elseif (count($pseudo_types) > 0) {
$form_state->setErrorByName(
self::GEMINI_URL,
$this->t('Must enter Gemini URL before selecting bundles to display a pseudo field on.')
);
}
} }
/** /**
@ -337,7 +291,6 @@ class IslandoraSettingsForm extends ConfigFormBase {
$config $config
->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL)) ->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL))
->set(self::JWT_EXPIRY, $form_state->getValue(self::JWT_EXPIRY)) ->set(self::JWT_EXPIRY, $form_state->getValue(self::JWT_EXPIRY))
->set(self::GEMINI_URL, $form_state->getValue(self::GEMINI_URL))
->set(self::GEMINI_PSEUDO, $pseudo_types) ->set(self::GEMINI_PSEUDO, $pseudo_types)
->save(); ->save();

47
src/GeminiClientFactory.php

@ -1,47 +0,0 @@
<?php
namespace Drupal\islandora;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\islandora\Form\IslandoraSettingsForm;
use Islandora\Crayfish\Commons\Client\GeminiClient;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
/**
* Creates a GeminiClient as a Drupal service.
*
* @package Drupal\islandora
*/
class GeminiClientFactory {
/**
* Factory function.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* Config.
* @param \Psr\Log\LoggerInterface $logger
* The logger channel.
*
* @return \Islandora\Crayfish\Commons\Client\GeminiClient
* Return GeminiClient
*
* @throws \Exception
* If there is no URL to connect to.
*/
public static function create(ConfigFactoryInterface $config, LoggerInterface $logger) {
// Get broker url from config.
$settings = $config->get(IslandoraSettingsForm::CONFIG_NAME);
$geminiUrl = $settings->get(IslandoraSettingsForm::GEMINI_URL);
// Only attempt if there is one.
if (!empty($geminiUrl)) {
return GeminiClient::create($geminiUrl, $logger);
}
else {
$logger->notice("Attempted to create Gemini client without a Gemini URL defined.");
throw new PreconditionFailedHttpException("Unable to instantiate GeminiClient, missing Gemini URI in Islandora setting.");
}
}
}

168
src/GeminiLookup.php

@ -1,168 +0,0 @@
<?php
namespace Drupal\islandora;
use Drupal\Core\Entity\EntityInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Islandora\Crayfish\Commons\Client\GeminiClient;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Locates the matching Fedora URI from the Gemini database.
*
* @package Drupal\islandora
*/
class GeminiLookup {
/**
* A GeminiClient.
*
* @var \Islandora\Crayfish\Commons\Client\GeminiClient
*/
private $geminiClient;
/**
* A JWT Provider service.
*
* @var \Drupal\jwt\Authentication\Provider\JwtAuth
*/
private $jwtProvider;
/**
* A MediaSourceService.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
private $mediaSource;
/**
* An http client.
*
* @var \GuzzleHttp\Client
*/
private $guzzle;
/**
* The islandora logger channel.
*
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* GeminiField constructor.
*
* @param \Islandora\Crayfish\Commons\Client\GeminiClient $client
* The Gemini client.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $jwt_auth
* The JWT provider.
* @param \Drupal\islandora\MediaSource\MediaSourceService $media_source
* Media source service.
* @param \GuzzleHttp\Client $guzzle
* Guzzle client.
* @param \Psr\Log\LoggerInterface $logger
* The Islandora logger.
*/
public function __construct(
GeminiClient $client,
JwtAuth $jwt_auth,
MediaSourceService $media_source,
Client $guzzle,
LoggerInterface $logger
) {
$this->geminiClient = $client;
$this->jwtProvider = $jwt_auth;
$this->mediaSource = $media_source;
$this->guzzle = $guzzle;
$this->logger = $logger;
}
/**
* Lookup this entity's URI in the Gemini db and return the other URI.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to look for.
*
* @return string|null
* Return the URI or null
*/
public function lookup(EntityInterface $entity) {
// Exit early if the entity hasn't been saved yet.
if ($entity->id() == NULL) {
return NULL;
}
$is_media = $entity->getEntityTypeId() == 'media';
// Use the entity's uuid unless it's a media,
// use its file's uuid instead.
if ($is_media) {
try {
$file = $this->mediaSource->getSourceFile($entity);
$uuid = $file->uuid();
}
// If the media has no source file, exit early.
catch (NotFoundHttpException $e) {
return NULL;
}
}
else {
$uuid = $entity->uuid();
}
// Look it up in Gemini.
$token = "Bearer " . $this->jwtProvider->generateToken();
$urls = $this->geminiClient->getUrls($uuid, $token);
// Exit early if there's no results from Gemini.
if (empty($urls)) {
return NULL;
}
// If it's not a media, just return the url from Gemini;.
if (!$is_media) {
return $urls['fedora'];
}
// If it's a media, perform a HEAD request against
// the file in Fedora and get its 'describedy' link header.
try {
$head = $this->guzzle->head(
$urls['fedora'],
['allow_redirects' => FALSE, 'headers' => ['Authorization' => $token]]
);
// phpcs:disable
if (class_exists(\GuzzleHttp\Psr7\Header::class)) {
$links = \GuzzleHttp\Psr7\Header::parse($head->getHeader('Link'));
}
else {
$links = \GuzzleHttp\Psr7\parse_header($head->getHeader('Link'));
}
//phpcs:enable
foreach ($links as $link) {
if ($link['rel'] == 'describedby') {
return trim($link[0], '<>');
}
}
}
catch (RequestException $e) {
$this->logger->warning(
"Error performing Gemini lookup for media. Fedora HEAD to @url returned @status => @message",
[
'@url' => $urls['fedora'],
'@status' => $e->getCode(),
'@message' => $e->getMessage,
]
);
return NULL;
}
// Return null if no link header is found.
return NULL;
}
}

293
tests/src/Kernel/GeminiLookupTest.php

@ -1,293 +0,0 @@
<?php
namespace Drupal\Tests\islandora\Kernel;
use Drupal\Core\Entity\EntityInterface;
use Drupal\file\FileInterface;
use Drupal\islandora\GeminiLookup;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use Drupal\media\MediaInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Islandora\Crayfish\Commons\Client\GeminiClient;
use Prophecy\Argument;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Tests Gemini Lookup.
*
* @group islandora
* @coversDefaultClass \Drupal\islandora\GeminiLookup
*/
class GeminiLookupTest extends IslandoraKernelTestBase {
/**
* JWT Auth.
*
* @var \Drupal\jwt\Authentication\Provider\JwtAuth
*/
private $jwtAuth;
/**
* Logger.
*
* @var \Psr\Log\LoggerInterface
*/
private $logger;
/**
* Guzzle.
*
* @var \GuzzleHttp\Client
*/
private $guzzle;
/**
* Gemini client.
*
* @var \Islandora\Crayfish\Commons\Client\GeminiClient
*/
private $geminiClient;
/**
* Media source service.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
private $mediaSource;
/**
* An entity.
*
* @var \Drupal\Core\Entity\EntityInterface
*/
private $entity;
/**
* A media.
*
* @var \Drupal\media\MediaInterface
*/
private $media;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Mock up dummy objects by default.
$prophecy = $this->prophesize(JwtAuth::class);
$this->jwtAuth = $prophecy->reveal();
$prophecy = $this->prophesize(LoggerInterface::class);
$this->logger = $prophecy->reveal();
$prophecy = $this->prophesize(MediaSourceService::class);
$this->mediaSource = $prophecy->reveal();
$prophecy = $this->prophesize(GeminiClient::class);
$this->geminiClient = $prophecy->reveal();
$prophecy = $this->prophesize(Client::class);
$this->guzzle = $prophecy->reveal();
// Mock up an entity to use (node in this case).
$prophecy = $this->prophesize(EntityInterface::class);
$prophecy->id()->willReturn(1);
$prophecy->getEntityTypeId()->willReturn('node');
$prophecy->uuid()->willReturn('abc123');
$this->entity = $prophecy->reveal();
// Mock up a media to use.
$prophecy = $this->prophesize(MediaInterface::class);
$prophecy->id()->willReturn(1);
$prophecy->getEntityTypeId()->willReturn('media');
$prophecy->uuid()->willReturn('abc123');
$this->media = $prophecy->reveal();
}
/**
* Mocks up a gemini client that fails its lookup.
*/
private function mockGeminiClientForFail() {
$prophecy = $this->prophesize(GeminiClient::class);
$prophecy->getUrls(Argument::any(), Argument::any())
->willReturn([]);
$this->geminiClient = $prophecy->reveal();
}
/**
* Mocks up a gemini client that finds a fedora url.
*/
private function mockGeminiClientForSuccess() {
$prophecy = $this->prophesize(GeminiClient::class);
$prophecy->getUrls(Argument::any(), Argument::any())
->willReturn([
'drupal' => '',
'fedora' => 'http://localhost:8080/fcrepo/rest/abc123',
]);
$this->geminiClient = $prophecy->reveal();
}
/**
* Mocks up a media source service that finds the source file for a media.
*/
private function mockMediaSourceForSuccess() {
$prophecy = $this->prophesize(FileInterface::class);
$prophecy->uuid()->willReturn('abc123');
$file = $prophecy->reveal();
$prophecy = $this->prophesize(MediaSourceService::class);
$prophecy->getSourceFile(Argument::any())
->willReturn($file);
$this->mediaSource = $prophecy->reveal();
}
/**
* Make the gemini lookup out of class variables.
*/
private function createGeminiLookup() {
return new GeminiLookup(
$this->geminiClient,
$this->jwtAuth,
$this->mediaSource,
$this->guzzle,
$this->logger
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testEntityNotSaved() {
// Mock an entity that returns a null id.
// That means it's not saved in the db yet.
$prophecy = $this->prophesize(EntityInterface::class);
$prophecy->id()->willReturn(NULL);
$this->entity = $prophecy->reveal();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
NULL,
$gemini_lookup->lookup($this->entity)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testEntityNotFound() {
$this->mockGeminiClientForFail();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
NULL,
$gemini_lookup->lookup($this->entity)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testEntityFound() {
$this->mockGeminiClientForSuccess();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
'http://localhost:8080/fcrepo/rest/abc123',
$gemini_lookup->lookup($this->entity)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testMediaHasNoSourceFile() {
// Mock a media source service that fails to find
// the source file for a media.
$prophecy = $this->prophesize(MediaSourceService::class);
$prophecy->getSourceFile(Argument::any())
->willThrow(new NotFoundHttpException("Media has no source"));
$this->mediaSource = $prophecy->reveal();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
NULL,
$gemini_lookup->lookup($this->media)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testMediaNotFound() {
$this->mockMediaSourceForSuccess();
$this->mockGeminiClientForFail();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
NULL,
$gemini_lookup->lookup($this->media)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testFileFoundButNoDescribedby() {
$this->mockMediaSourceForSuccess();
$this->mockGeminiClientForSuccess();
// Mock up a guzzle client that does not return
// the describedby header.
$prophecy = $this->prophesize(Client::class);
$prophecy->head(Argument::any(), Argument::any())
->willReturn(new Response(200, []));
$this->guzzle = $prophecy->reveal();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
NULL,
$gemini_lookup->lookup($this->media)
);
}
/**
* @covers ::lookup
* @covers ::__construct
*/
public function testMediaFound() {
$this->mockMediaSourceForSuccess();
$this->mockGeminiClientForSuccess();
// Mock up a guzzle client that returns
// the describedby header.
$prophecy = $this->prophesize(Client::class);
$prophecy->head(Argument::any(), Argument::any())
->willReturn(new Response(200, ['Link' => '<http://localhost:8080/fcrepo/rest/abc123/fcr:metadata>; rel="describedby"']));
$this->guzzle = $prophecy->reveal();
$gemini_lookup = $this->createGeminiLookup();
$this->assertEquals(
'http://localhost:8080/fcrepo/rest/abc123/fcr:metadata',
$gemini_lookup->lookup($this->media)
);
}
}
Loading…
Cancel
Save