Browse Source

Media Fedora Uri Pseudo-Fields (#126)

* Getting media fedora uris in the pseudo field as well

* Same tests, less code
pull/729/head
dannylamb 6 years ago committed by Jared Whiklo
parent
commit
b4b8fe1e12
  1. 2
      islandora.services.yml
  2. 123
      src/GeminiLookup.php
  3. 230
      tests/src/Kernel/GeminiLookupTest.php

2
islandora.services.yml

@ -57,4 +57,4 @@ services:
arguments: ['@config.factory', '@logger.channel.islandora'] arguments: ['@config.factory', '@logger.channel.islandora']
islandora.gemini.lookup: islandora.gemini.lookup:
class: Drupal\islandora\GeminiLookup class: Drupal\islandora\GeminiLookup
arguments: ['@islandora.gemini.client', '@jwt.authentication.jwt', '@logger.channel.islandora'] arguments: ['@islandora.gemini.client', '@jwt.authentication.jwt', '@islandora.media_source_service', '@http_client', '@logger.channel.islandora']

123
src/GeminiLookup.php

@ -3,10 +3,14 @@
namespace Drupal\islandora; namespace Drupal\islandora;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jwt\Authentication\Provider\JwtAuth; use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Psr7;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Islandora\Crayfish\Commons\Client\GeminiClient; use Islandora\Crayfish\Commons\Client\GeminiClient;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Locates the matching Fedora URI from the Gemini database. * Locates the matching Fedora URI from the Gemini database.
@ -29,6 +33,20 @@ class GeminiLookup {
*/ */
private $jwtProvider; private $jwtProvider;
/**
* A MediaSourceService.
*
* @var \Drupal\islandora\MediaSource\MediaSourceService
*/
private $mediaSource;
/**
* An http client.
*
* @var \GuzzleHttp\Client
*/
private $guzzle;
/** /**
* The islandora logger channel. * The islandora logger channel.
* *
@ -43,32 +61,27 @@ class GeminiLookup {
* The Gemini client. * The Gemini client.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $jwt_auth * @param \Drupal\jwt\Authentication\Provider\JwtAuth $jwt_auth
* The JWT provider. * The JWT provider.
* @param \Drupal\islandora\MediaSource\MediaSourceService $media_source
* Media source service.
* @param \GuzzleHttp\Client $guzzle
* Guzzle client.
* @param \Psr\Log\LoggerInterface $logger * @param \Psr\Log\LoggerInterface $logger
* The Islandora logger. * The Islandora logger.
*/ */
public function __construct(GeminiClient $client, JwtAuth $jwt_auth, LoggerInterface $logger) { public function __construct(
GeminiClient $client,
JwtAuth $jwt_auth,
MediaSourceService $media_source,
Client $guzzle,
LoggerInterface $logger
) {
$this->geminiClient = $client; $this->geminiClient = $client;
$this->jwtProvider = $jwt_auth; $this->jwtProvider = $jwt_auth;
$this->mediaSource = $media_source;
$this->guzzle = $guzzle;
$this->logger = $logger; $this->logger = $logger;
} }
/**
* Static creator.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The container.
*
* @return \Drupal\islandora\GeminiLookup
* A GeminiLookup service.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('islandora.gemini_client'),
$container->get('jwt.authentication.jwt'),
$container->get('logger.channel.islandora')
);
}
/** /**
* Lookup this entity's URI in the Gemini db and return the other URI. * Lookup this entity's URI in the Gemini db and return the other URI.
* *
@ -77,24 +90,72 @@ class GeminiLookup {
* *
* @return string|null * @return string|null
* Return the URI or null * Return the URI or null
*
* @throws \Drupal\Core\Entity\EntityMalformedException
* If the entity cannot be converted to a URL.
*/ */
public function lookup(EntityInterface $entity) { public function lookup(EntityInterface $entity) {
if ($entity->id() != NULL) { // Exit early if the entity hasn't been saved yet.
$drupal_uri = $entity->toUrl()->setAbsolute()->toString(); if ($entity->id() == NULL) {
$drupal_uri .= '?_format=jsonld'; 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(); $token = "Bearer " . $this->jwtProvider->generateToken();
$linked_uri = $this->geminiClient->findByUri($drupal_uri, $token); $urls = $this->geminiClient->getUrls($uuid, $token);
if (!is_null($linked_uri)) {
if (is_array($linked_uri)) { // Exit early if there's no results from Gemini.
$linked_uri = reset($linked_uri); 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]]
);
$links = Psr7\parse_header($head->getHeader("Link"));
foreach ($links as $link) {
if ($link['rel'] == 'describedby') {
return trim($link[0], '<>');
} }
return $linked_uri;
} }
} }
// Return null if we weren't in a saved entity or we didn't find a uri. catch (RequestException $e) {
$this->logger->warn(
"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; return NULL;
} }

230
tests/src/Kernel/GeminiLookupTest.php

@ -3,12 +3,17 @@
namespace Drupal\Tests\islandora\Kernel; namespace Drupal\Tests\islandora\Kernel;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url; use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
use Drupal\islandora\GeminiLookup; use Drupal\islandora\GeminiLookup;
use Drupal\islandora\MediaSource\MediaSourceService;
use Drupal\jwt\Authentication\Provider\JwtAuth; use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use Islandora\Crayfish\Commons\Client\GeminiClient; use Islandora\Crayfish\Commons\Client\GeminiClient;
use Prophecy\Argument; use Prophecy\Argument;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Class GeminiLookupTest. * Class GeminiLookupTest.
@ -18,104 +23,233 @@ use Psr\Log\LoggerInterface;
*/ */
class GeminiLookupTest extends IslandoraKernelTestBase { class GeminiLookupTest extends IslandoraKernelTestBase {
private $geminiLookup; private $jwtAuth;
private $logger;
private $guzzle;
private $geminiClient; private $geminiClient;
private $jwtAuth; private $mediaSource;
private $logger; private $entity;
private $media;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setUp() { public function setUp() {
parent::setUp(); parent::setUp();
// Mock up dummy objects by default.
$prophecy = $this->prophesize(JwtAuth::class); $prophecy = $this->prophesize(JwtAuth::class);
$prophecy->generateToken()->willReturn("islandora");
$this->jwtAuth = $prophecy->reveal(); $this->jwtAuth = $prophecy->reveal();
$prophecy = $this->prophesize(LoggerInterface::class); $prophecy = $this->prophesize(LoggerInterface::class);
$this->logger = $prophecy->reveal(); $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 = $this->prophesize(GeminiClient::class);
$prophecy->findByUri(Argument::any(), Argument::any())->willReturn(NULL); $prophecy->getUrls(Argument::any(), Argument::any())
->willReturn(['drupal' => '', 'fedora' => 'http://localhost:8080/fcrepo/rest/abc123']);
$this->geminiClient = $prophecy->reveal(); $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 ::lookup
* @covers ::__construct * @covers ::__construct
* @throws \Drupal\Core\Entity\EntityMalformedException
*/ */
public function testEntityNotSaved() { 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 = $this->prophesize(EntityInterface::class);
$prophecy->id()->willReturn(NULL); $prophecy->id()->willReturn(NULL);
$entity = $prophecy->reveal(); $this->entity = $prophecy->reveal();
$this->geminiLookup = new GeminiLookup(
$this->geminiClient, $gemini_lookup = $this->createGeminiLookup();
$this->jwtAuth,
$this->logger $this->assertEquals(
NULL,
$gemini_lookup->lookup($this->entity)
); );
$this->assertEquals(NULL, $this->geminiLookup->lookup($entity));
} }
/** /**
* @covers ::lookup * @covers ::lookup
* @covers ::__construct * @covers ::__construct
* @throws \Drupal\Core\Entity\EntityMalformedException
*/ */
public function testEntityNotFound() { public function testEntityNotFound() {
$prop1 = $this->prophesize(Url::class); $this->mockGeminiClientForFail();
$prop1->toString()->willReturn("http://localhost:8000/node/456");
$prop2 = $this->prophesize(Url::class);
$prop2->setAbsolute()->willReturn($prop1->reveal());
$url = $prop2->reveal();
$prophecy = $this->prophesize(EntityInterface::class); $gemini_lookup = $this->createGeminiLookup();
$prophecy->id()->willReturn(456);
$prophecy->toUrl()->willReturn($url);
$entity = $prophecy->reveal();
$this->geminiLookup = new GeminiLookup( $this->assertEquals(
$this->geminiClient, NULL,
$this->jwtAuth, $gemini_lookup->lookup($this->entity)
$this->logger
); );
$this->assertEquals(NULL, $this->geminiLookup->lookup($entity));
} }
/** /**
* @covers ::lookup * @covers ::lookup
* @covers ::__construct * @covers ::__construct
* @throws \Drupal\Core\Entity\EntityMalformedException
*/ */
public function testEntityFound() { public function testEntityFound() {
$prop1 = $this->prophesize(Url::class); $this->mockGeminiClientForSuccess();
$prop1->toString()->willReturn("http://localhost:8000/node/456");
$prop2 = $this->prophesize(Url::class); $gemini_lookup = $this->createGeminiLookup();
$prop2->setAbsolute()->willReturn($prop1->reveal());
$url = $prop2->reveal();
$prophecy = $this->prophesize(EntityInterface::class); $this->assertEquals(
$prophecy->id()->willReturn(456); 'http://localhost:8080/fcrepo/rest/abc123',
$prophecy->toUrl()->willReturn($url); $gemini_lookup->lookup($this->entity)
$entity = $prophecy->reveal(); );
}
$prophecy = $this->prophesize(GeminiClient::class); /**
$prophecy->findByUri(Argument::any(), Argument::any())->willReturn(["http://fedora:8080/some/uri"]); * @covers ::lookup
$this->geminiClient = $prophecy->reveal(); * @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)
);
}
$this->geminiLookup = new GeminiLookup( /**
$this->geminiClient, * @covers ::lookup
$this->jwtAuth, * @covers ::__construct
$this->logger */
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)
); );
}
$this->assertEquals("http://fedora:8080/some/uri", $this->geminiLookup->lookup($entity)); /**
* @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