- DOI implementation wit unit testing
This commit is contained in:
parent
7f9bd089b1
commit
9b6a6469d7
20 changed files with 2128 additions and 360 deletions
203
app/Http/Controllers/DoiController.php
Normal file
203
app/Http/Controllers/DoiController.php
Normal file
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Interfaces\DOIInterface;
|
||||
use App\Models\Dataset;
|
||||
use App\Models\DatasetIdentifier;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Oai\OaiModelError;
|
||||
use App\Exceptions\OaiModelException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DoiController extends Controller
|
||||
{
|
||||
protected $doiService;
|
||||
protected $LaudatioUtils;
|
||||
|
||||
/**
|
||||
* Holds xml representation of document information to be processed.
|
||||
*
|
||||
* @var \DomDocument Defaults to null.
|
||||
*/
|
||||
protected $xml = null;
|
||||
/**
|
||||
* Holds the stylesheet for the transformation.
|
||||
*
|
||||
* @var \DomDocument Defaults to null.
|
||||
*/
|
||||
protected $xslt = null;
|
||||
|
||||
/**
|
||||
* Holds the xslt processor.
|
||||
*
|
||||
* @var \XSLTProcessor Defaults to null.
|
||||
*/
|
||||
protected $proc = null;
|
||||
|
||||
/**
|
||||
* DOIController constructor.
|
||||
* @param DoiInterface $DOIService
|
||||
*/
|
||||
public function __construct(DoiInterface $DoiClient)
|
||||
{
|
||||
$this->doiClient = $DoiClient;
|
||||
|
||||
$this->xml = new \DomDocument();
|
||||
$this->proc = new \XSLTProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create(Request $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$dataId = $request->input('publish_id');
|
||||
|
||||
// Setup stylesheet
|
||||
$this->loadStyleSheet(public_path() .'\prefixes\doi_datacite.xslt');
|
||||
|
||||
// set timestamp
|
||||
$date = new \DateTime();
|
||||
$unixTimestamp = $date->getTimestamp();
|
||||
$this->proc->setParameter('', 'unixTimestamp', $unixTimestamp);
|
||||
|
||||
$prefix = config('tethys.datacite_prefix');
|
||||
$this->proc->setParameter('', 'prefix', $prefix);
|
||||
|
||||
$repIdentifier = "tethys";
|
||||
$this->proc->setParameter('', 'repIdentifier', $repIdentifier);
|
||||
|
||||
$this->xml->appendChild($this->xml->createElement('Datasets'));
|
||||
$dataset = Dataset::where('publish_id', '=', $dataId)->firstOrFail();
|
||||
if (is_null($dataset)) {
|
||||
throw new OaiModelException('Dataset is not available for registering DOI!', OaiModelError::NORECORDSMATCH);
|
||||
}
|
||||
$dataset->fetchValues();
|
||||
$xmlModel = new \App\Library\Xml\XmlModel();
|
||||
$xmlModel->setModel($dataset);
|
||||
$xmlModel->excludeEmptyFields();
|
||||
$cache = ($dataset->xmlCache) ? $dataset->xmlCache : new \App\Models\XmlCache();
|
||||
$xmlModel->setXmlCache($cache);
|
||||
$domNode = $xmlModel->getDomDocument()->getElementsByTagName('Rdr_Dataset')->item(0);
|
||||
$node = $this->xml->importNode($domNode, true);
|
||||
$this->addSpecInformation($node, 'data-type:' . $dataset->type);
|
||||
|
||||
$this->xml->documentElement->appendChild($node);
|
||||
$xmlMeta = $this->proc->transformToXML($this->xml);
|
||||
Log::alert($xmlMeta);
|
||||
//create doiValue and correspunfing landingpage of tehtys
|
||||
$doiValue = $prefix . '/tethys.' . $dataset->publish_id;
|
||||
$appUrl = config('app.url');
|
||||
$landingPageUrl = $appUrl . "/dataset/" . $dataset->publish_id;
|
||||
$response = $this->doiClient->registerDoi($doiValue, $xmlMeta, $landingPageUrl);
|
||||
if ($response->getStatusCode() == 201) {
|
||||
$doi = new DatasetIdentifier();
|
||||
$doi['value'] = $doiValue;
|
||||
$doi['dataset_id'] = $dataset->id;
|
||||
$doi['type'] = "doi";
|
||||
$doi['status'] = "registered";
|
||||
$doi['registration_ts'] = now();
|
||||
// $doi->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param \App\Models\DatasetIdentifier $doi
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show(DatasetIdentifier $doi)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\DatasetIdentifier $doi
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(DatasetIdentifier $doi)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\DatasetIdentifier $doi
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, DatasetIdentifier $doi)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\DatasetIdentifier $doi
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(DatasetIdentifier $doi)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load an xslt stylesheet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadStyleSheet($stylesheet)
|
||||
{
|
||||
$this->xslt = new \DomDocument;
|
||||
$this->xslt->load($stylesheet);
|
||||
$this->proc->importStyleSheet($this->xslt);
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$this->proc->setParameter('', 'host', $_SERVER['HTTP_HOST']);
|
||||
}
|
||||
//$this->proc->setParameter('', 'server', $this->getRequest()->getBaseUrl());
|
||||
}
|
||||
|
||||
private function addSpecInformation(\DOMNode $document, $information)
|
||||
{
|
||||
$setSpecAttribute = $this->xml->createAttribute('Value');
|
||||
$setSpecAttributeValue = $this->xml->createTextNode($information);
|
||||
$setSpecAttribute->appendChild($setSpecAttributeValue);
|
||||
|
||||
$setSpecElement = $this->xml->createElement('SetSpec');
|
||||
//$setSpecElement =new \DOMElement("SetSpec");
|
||||
$setSpecElement->appendChild($setSpecAttribute);
|
||||
$document->appendChild($setSpecElement);
|
||||
}
|
||||
}
|
16
app/Interfaces/DoiInterface.php
Normal file
16
app/Interfaces/DoiInterface.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* Created by Visual Studio Code.
|
||||
* User: kaiarn
|
||||
* Date: 19.02.2021
|
||||
*/
|
||||
namespace App\Interfaces;
|
||||
|
||||
interface DoiInterface
|
||||
{
|
||||
public function registerDoi($doiValue, $xmlMeta, $landingPageUrl);
|
||||
public function getMetadataForDoi($identifier);
|
||||
public function updateMetadataForDoi($identifier, $new_meta);
|
||||
public function deleteMetadataForDoi($identifier);
|
||||
// public function deleteDoiByCurlRequest($doi);
|
||||
}
|
19
app/Models/DatasetIdentifier.php
Normal file
19
app/Models/DatasetIdentifier.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Dataset;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DatasetIdentifier extends Model
|
||||
{
|
||||
protected $table = 'dataset_identifierss';
|
||||
protected $guarded = array();
|
||||
|
||||
/**
|
||||
* The dataset that belong to the DocumentIdentifier.
|
||||
*/
|
||||
public function dataset()
|
||||
{
|
||||
return $this->belongsTo(Dataset::class, 'document_id', 'id');
|
||||
}
|
||||
}
|
44
app/Providers/DoiServiceProvider.php
Normal file
44
app/Providers/DoiServiceProvider.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
/**
|
||||
* Visual Studio Code.
|
||||
* User: kaiarn
|
||||
* Date: 19.02.21
|
||||
*/
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Tethys\Utils\DoiService;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class DoiServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected $defer = true;
|
||||
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
$this->app->singleton('App\Interfaces\DoiInterface', function ($app) {
|
||||
return new DoiService();
|
||||
});
|
||||
}
|
||||
|
||||
public function provides()
|
||||
{
|
||||
return [DoiService::class];
|
||||
}
|
||||
}
|
212
app/Tethys/Utils/DoiClient.php
Normal file
212
app/Tethys/Utils/DoiClient.php
Normal file
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
namespace App\Tethys\Utils;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Interfaces\DoiInterface;
|
||||
|
||||
class DoiClient implements DoiInterface
|
||||
{
|
||||
private $username;
|
||||
private $password;
|
||||
private $serviceUrl;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$datacite_environment = "production"; //config('tethys.datacite_environment');
|
||||
if ($datacite_environment == "debug") {
|
||||
$this->username = config('tethys.datacite_test_username');
|
||||
$this->password = config('tethys.datacite_test_password');
|
||||
$this->serviceUrl = config('tethys.test_datacite_service_url');
|
||||
$this->prefix = config('tethys.datacite_prefix');
|
||||
} elseif ($datacite_environment == "production") {
|
||||
$this->username = config('tethys.datacite_username');
|
||||
$this->password = config('tethys.datacite_password');
|
||||
$this->serviceUrl = config('tethys.datacite_service_url');
|
||||
$this->prefix = config('tethys.datacite_prefix');
|
||||
}
|
||||
if (is_null($this->username) || is_null($this->password) || is_null($this->serviceUrl)) {
|
||||
$message = 'missing configuration settings to properly initialize DOI client';
|
||||
Log::error($message);
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DOI or ARK with the given identifier
|
||||
*
|
||||
* @param string $identifier The desired DOI identifier e.g. '10.5072/tethys.999',
|
||||
* @param $xmlMeta
|
||||
* @param $landingPageUrl e.g. https://www.tethys.at/dataset/1
|
||||
*
|
||||
* @return GuzzleHttp\Psr7\Response The http response in the form of a Guzzle response
|
||||
*/
|
||||
public function registerDoi($doiValue, $xmlMeta, $landingPageUrl)
|
||||
{
|
||||
|
||||
// Schritt 1: Metadaten als XML registrieren
|
||||
$response = null;
|
||||
$url = $this->serviceUrl . '/metadata/' . $doiValue;
|
||||
try {
|
||||
$client = new Client([
|
||||
'auth' => [$this->username, $this->password],
|
||||
// 'base_uri' => $url,
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/xml;charset=UTF-8',
|
||||
],
|
||||
// 'body' => $xmlMeta,
|
||||
]);
|
||||
// Provide the body as a string.
|
||||
$response = $client->request('PUT', $url, [
|
||||
'body' => $xmlMeta,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$message = 'request to ' . $url . ' failed with ' . $e->getMessage();
|
||||
// $this->log($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
// Response Codes
|
||||
// 201 Created: operation successful
|
||||
// 400 Bad Request: invalid XML, wrong prefix
|
||||
// 401 Unauthorised: no login
|
||||
// 403 Forbidden: login problem, quota exceeded
|
||||
// 415 Wrong Content Type : Not including content type in the header.
|
||||
// 422 Unprocessable Entity : invalid XML
|
||||
if ($response->getStatusCode() != 201) {
|
||||
$message = 'unexpected DataCite MDS response code ' . $response->getStatusCode();
|
||||
// $this->log($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
|
||||
// Schritt 2: Register the DOI name
|
||||
// DOI und URL der Frontdoor des zugehörigen Dokuments übergeben
|
||||
$url = $this->serviceUrl . '/doi/' . $doiValue;
|
||||
try {
|
||||
$client = new Client(
|
||||
[
|
||||
'auth' => [$this->username, $this->password],
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'Content-Type' => 'text/plain;charset=UTF-8',
|
||||
],
|
||||
]
|
||||
);
|
||||
$data = "doi=$doiValue\nurl=" . $landingPageUrl;
|
||||
// $client->setRawData($data, 'text/plain;charset=UTF-8');
|
||||
$response = $client->request('PUT', $url, [
|
||||
'body' => $data,
|
||||
'headers' => [
|
||||
'Content-Type' => 'text/plain;charset=UTF-8',
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$message = 'request to ' . $url . ' failed with ' . $e->getMessage();
|
||||
// $this->log($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
// Response Codes
|
||||
// 201 Created: operation successful
|
||||
// 400 Bad Request: request body must be exactly two lines: DOI and URL; wrong domain, wrong prefix;
|
||||
// 401 Unauthorised: no login
|
||||
// 403 Forbidden: login problem, quota exceeded
|
||||
// 412 Precondition failed: metadata must be uploaded first.
|
||||
|
||||
// $this->log('DataCite response status code (expected 201): ' . $response->getStatus());
|
||||
// $this->log('DataCite response body: ' . $response->getBody());
|
||||
|
||||
if ($response->getStatusCode() != 201) {
|
||||
$message = 'unexpected DataCite MDS response code ' . $response->getStatusCode();
|
||||
// $this->log($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/* Response Status Codes
|
||||
* 200 OK: operation successful
|
||||
* 204 No Content : DOI is known to DataCite Metadata Store (MDS), but is not minted (or not resolvable e.g. due
|
||||
* to handle's latency)
|
||||
* 401 Unauthorized: no login
|
||||
* 403 Login problem or dataset belongs to another party
|
||||
* 404 Not Found: DOI does not exist in our database (e.g. registration pending)
|
||||
*
|
||||
* @param $doiValue
|
||||
* @param $landingPageURL
|
||||
*
|
||||
* @return bool Methode liefert true, wenn die DOI erfolgreich registiert wurde und die Prüfung positiv ausfällt.
|
||||
*
|
||||
* @throws ClientException
|
||||
*
|
||||
*/
|
||||
public function checkDoi($doiValue, $landingPageURL): bool
|
||||
{
|
||||
$response = null;
|
||||
$url = $this->serviceUrl . '/doi/' . $doiValue;
|
||||
try {
|
||||
$client = new Client([
|
||||
'base_uri' => $this->serviceUrl . '/doi/' . $doiValue,
|
||||
'auth' => [$this->username, $this->password],
|
||||
'verify' => false,
|
||||
]);
|
||||
$response = $client->request('GET');
|
||||
} catch (\Exception $e) {
|
||||
$message = 'request to ' . $url . ' failed with ' . $e->getMessage();
|
||||
Log::error($message);
|
||||
// throw new \Exception($message);
|
||||
return false;
|
||||
}
|
||||
|
||||
$statusCode = $response->getStatusCode();
|
||||
// in $body steht die URL zur Frontdoor, die mit der DOI verknüpft wurde
|
||||
$body = $response->getBody();
|
||||
|
||||
// $this->log('DataCite response status code (expected 200): ' . $statusCode);
|
||||
// $this->log('DataCite response body (expected ' . $landingPageURL . '): ' . $body);
|
||||
|
||||
return ($statusCode == 200 && $landingPageURL == $body);
|
||||
}
|
||||
|
||||
public function getMetadataForDoi($identifier)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function updateMetadataForDoi($identifier, $new_meta)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert den Datensatz zur übergebenen DOI als inaktiv.
|
||||
*
|
||||
* @param $doiValue
|
||||
*
|
||||
* @throws ClientException
|
||||
*/
|
||||
public function deleteMetadataForDoi($doiValue)
|
||||
{
|
||||
$response = null;
|
||||
$url = $this->serviceUrl . '/metadata/' . $doiValue;
|
||||
try {
|
||||
$client = new Client([
|
||||
'base_uri' => $url,
|
||||
'auth' => [$this->username, $this->password],
|
||||
'verify' => false,
|
||||
]);
|
||||
$response = $client->request('DELETE');
|
||||
} catch (\Exception $e) {
|
||||
$message = 'request to ' . $url . ' failed with ' . $e->getMessage();
|
||||
Log::error($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
|
||||
// $this->log('DataCite response status code (expected 200): ' . $response->getStatus());
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
$message = 'unexpected DataCite MDS response code ' . $response->getStatusCode();
|
||||
Log::error($message, 'err');
|
||||
throw new DoiClientException($message);
|
||||
}
|
||||
}
|
||||
}
|
7
app/Tethys/Utils/DoiClientException.php
Normal file
7
app/Tethys/Utils/DoiClientException.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace App\Tethys\Utils;
|
||||
|
||||
class DoiClientException extends \Exception
|
||||
{
|
||||
}
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue