-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Collection Service Implementation #140
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
# Islandora Dev Settings to be used with $app['debug'] == TRUE | ||
islandora: | ||
apiVersion: 0.0.1 | ||
fedoraProtocol: http | ||
fedoraHost: "localhost:8080" | ||
fedoraPath: /rest | ||
tripleProtocol: http | ||
tripleHost: "localhost:9999" | ||
triplePath: /bigdata/sparql | ||
resourceIdRegex: "(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})?" | ||
resourceIdRegex: "(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})?" | ||
# This domain is used as namespace (hashed) when generating UUID V5 identifiers, replace with your own | ||
defaultNamespaceDomainUuuidV5: "www.islandora.ca" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
islandora: | ||
apiVersion: 0.0.1 | ||
fedoraProtocol: http | ||
fedoraHost: "localhost:8080" | ||
fedoraPath: /fcrepo/rest | ||
tripleProtocol: http | ||
tripleHost: "localhost:8080" | ||
triplePath: /bigdata/sparql | ||
resourceIdRegex: "(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})?" | ||
resourceIdRegex: "(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})?" | ||
# This domain is used as namespace (hashed) when generating UUID V5 identifiers, replace with your own | ||
defaultNamespaceDomainUuuidV5: "www.islandora.ca" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,12 @@ | |
require_once __DIR__.'/../vendor/autoload.php'; | ||
|
||
use Silex\Application; | ||
use Islandora\Chullo\FedoraApi; | ||
use Islandora\Chullo\TriplestoreClient; | ||
use Islandora\ResourceService\Provider\ResourceServiceProvider; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Silex\Provider\TwigServiceProvider; | ||
use Symfony\Component\Yaml\Yaml; | ||
|
||
date_default_timezone_set('UTC'); | ||
|
||
|
@@ -25,39 +22,24 @@ | |
'twig.path' => __DIR__.'/../templates', | ||
)); | ||
|
||
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) { | ||
return $twig; | ||
})); | ||
$islandoraResourceServiceProvider = new \Islandora\ResourceService\Provider\ResourceServiceProvider; | ||
|
||
$app->register($islandoraResourceServiceProvider); | ||
//Registers Resource Service and defines current app's path for config context | ||
$app->register($islandoraResourceServiceProvider, array( | ||
'islandora.BasePath' => __DIR__, | ||
)); | ||
$app->mount("/islandora", $islandoraResourceServiceProvider); | ||
$app->register(new \Islandora\ResourceService\Provider\UuidServiceProvider(), array( | ||
'UuidServiceProvider.default_namespace' => $app['config']['islandora']['defaultNamespaceDomainUuuidV5'], | ||
)); | ||
|
||
//$app['uuid'] = $app['islandora.uuid5'](rand()); | ||
//We will use a static uuid5 to fake a real IRI to replace <> during turtle parsing | ||
$app['uuid5'] = $app->share(function () use ($app) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might as well just jam the fake uri here. Or even better, just store it as a variable. No need for it to be in the DI container. When we refactor into a thin controller, the variable should reside within the fat service the controller utilizes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I understand what you mean? He is generating the fake URI here using the uuid5 function and storing it in a variable on the app. We could do it inside the route, but this is good too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @daniel-dgi, UUIDV5 works as a constant (build from, in this case the www.islandora.ca defined the yaml file plus the route to fedora4's endpoint. But it's still an UUID so there is less Chance that people could by mistake put something like this into an input RDF that we could end stripping out and removing in our str_replace function. I could not find a simpler way of putting there something that would work as expected. I can cache this if you want or change the IRI to urn:islandora:theuuid to make it even more failsafe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand that you're using this as a dummy value to keep EasyRDF happy. I have no issue with that, nor how you're generating the value. What I'm saying is that you're not using this function anywhere else except to generate the fake id, right? So skip the middle man and just put the fake ID in there. My second point is to avoid using the DI container as a cache for variables. You'll quickly wind up with a universal resource locator, or god object, not a DI container. You can just make a variable in the index.php scope, or even better, put it in the class we create when refactoring away from a fat controller. This can wait for later, though. |
||
return $app['islandora.uuid5']($app['config']['islandora']['fedoraProtocol'].'://'.$app['config']['islandora']['fedoraHost'].$app['config']['islandora']['fedoraPath']); | ||
}); | ||
//This is the uuidV4 generator | ||
$app['uuid'] = $app['islandora.uuid4']; | ||
|
||
/** | ||
* Still Not used, this function will check for content type | ||
*/ | ||
$isFedora4Content = function (Request $request) use ($app) { | ||
$rdf_content_types = array( | ||
"text/turtle", | ||
"text/rdf+n3", | ||
"application/n3", | ||
"text/n3", | ||
"application/rdf+xml", | ||
"application/n-triples", | ||
"application/sparql-update" | ||
); | ||
if (in_array($request->headers->get('Content-type'), $rdf_content_types)) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
|
||
/** | ||
* Convert returned Guzzle responses to Symfony responses. | ||
*/ | ||
|
@@ -67,40 +49,127 @@ | |
}); | ||
|
||
/** | ||
* Collection POST route test. Does nothing more than redirect from collection to mounted | ||
* takes 'rx' and/or 'checksum' as optional query arguments | ||
* Collection POST route. | ||
* Takes $id (valid UUID or empty) for the parent resource as first value to match, | ||
* and also takes 'rx' as an optional query argument. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doc comment is unclear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not good at comments nor english. I followed the same type of comments i did for resources. Any suggestion on how to document this is appreciated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...and your English is fine 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 , sounds fine. Will update now, thanks! |
||
$app->post("/islandora/collection",function (Application $app, Request $request) { | ||
error_log($app['uuid']); | ||
$tx = $request->query->get('tx', ""); | ||
$url = $request->getUriForPath('/islandora/resource/'); | ||
error_log($url); | ||
//static public Request create(string $uri, string $method = 'GET', array $parameters = array(), array $cookies = array(), array $files = array(), array $server = array(), string $content = null) | ||
$subRequest = Request::create($url, 'GET', array(), $request->cookies->all(), $request->files->all(), $request->server->all()); | ||
error_log($subRequest->__toString()); | ||
$response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); | ||
return $response; | ||
$app->post("/islandora/collection/{id}", function (Request $request, $id) use ($app) { | ||
$tx = $request->query->get('tx', ""); | ||
|
||
//Check for format | ||
$format = NULL; | ||
try { | ||
$format = \EasyRdf_Format::getFormat($contentType = $request->headers->get('Content-Type', 'text/turtle')); | ||
} catch (\EasyRdf_Exception $e) { | ||
$app->abort(415, $e->getMessage()); | ||
} | ||
|
||
//Now check if body can be parsed in that format | ||
if ($format) { //EasyRdf_Format | ||
//@see http://www.w3.org/2011/rdfa-context/rdfa-1.1 for defaults | ||
\EasyRdf_Namespace::set('pcdm', 'http://pcdm.org/models#'); | ||
\EasyRdf_Namespace::set('nfo', 'http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.1/'); | ||
\EasyRdf_Namespace::set('isl', 'http://www.islandora.ca/ontologies/2016/02/28/isl/v1.0/'); | ||
\EasyRdf_Namespace::set('ldp', 'http://www.w3.org/ns/ldp'); | ||
|
||
//Fake IRI, default LDP one for current resource "<>" is not a valid IRI! | ||
$fakeIri = new \EasyRdf_ParsedUri('urn:uuid:'.$app['uuid5']); | ||
|
||
$graph = new \EasyRdf_Graph(); | ||
try { | ||
$graph->parse($request->getContent(), $format->getName(), $fakeIri); | ||
} catch (\EasyRdf_Exception $e) { | ||
$app->abort(415, $e->getMessage()); | ||
} | ||
//Add a pcmd:Collection type | ||
$graph->resource($fakeIri, 'pcdm:Collection'); | ||
|
||
//Check if we got an UUID inside posted RDF. We won't validate it here because it's the caller responsability | ||
if (NULL != $graph->countValues($fakeIri, '<http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.1/uuid>')) { | ||
$existingUuid = $graph->getLiteral($fakeIri, '<http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.1/uuid>'); | ||
$graph->addResource($fakeIri, 'http://www.islandora.ca/ontologies/2016/02/28/isl/v1.0/hasURN', 'urn:uuid:'.$existingUuid); //Testing an Islandora Ontology! | ||
} else { | ||
//No UUID from the caller in RDF, lets put something there | ||
$tmpUuid = $app['uuid']; //caching here, since it's regenerated each time it is used and I need the same twice | ||
$graph->addLiteral($fakeIri,"http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.1/uuid",$tmpUuid); //Keeps compat for now with other services | ||
$graph->addResource($fakeIri,"http://www.islandora.ca/ontologies/2016/02/28/isl/v1.0/hasURN",'urn:uuid:'.$tmpUuid); //Testing an Islandora Ontology | ||
} | ||
//Restore LDP <> IRI on serialised graph | ||
$pcmd_collection_rdf= str_replace($fakeIri, '', $graph->serialise('turtle')); | ||
} | ||
|
||
$urlRoute = $request->getUriForPath('/islandora/resource/'); | ||
|
||
$subRequestPost = Request::create($urlRoute.$id, 'POST', array(), $request->cookies->all(), array(), $request->server->all(), $pcmd_collection_rdf); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why subrequest when you can just use chullo? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lots of reasons, we get all the middleware ,like checking if the base resource is there, header rewriting and we don't need to rewrite psr7 responses. More over, the idea is to not use chullo directly. ResourceService should deal with any new chullo functionality if needed (if we for example need to recode) but not in this higher level services. And since we already using all the other services ResourceService provides, going to chullo when we already have the routes is sub-using our code. |
||
$subRequestPost->query->set('tx', $tx); | ||
$subRequestPost->headers->set('Content-Type', 'text/turtle'); | ||
$responsePost = $app->handle($subRequestPost, HttpKernelInterface::SUB_REQUEST, false); | ||
|
||
if (201 == $responsePost->getStatusCode()) {// OK, collection created | ||
//Lets take the location header in the response | ||
$indirect_container_rdf = $app['twig']->render('createIndirectContainerfromTS.ttl', array( | ||
'resource' => $responsePost->headers->get('location'), | ||
)); | ||
|
||
$subRequestPut = Request::create($urlRoute.$id, 'PUT', array(), $request->cookies->all(), array(), $request->server->all(), $indirect_container_rdf); | ||
$subRequestPut->query->set('tx', $tx); | ||
$subRequestPut->headers->set('Slug', 'members'); | ||
//Can't use in middleware, but needed. Without Fedora 4 throws big java errors! | ||
$subRequestPut->headers->set('Host', $app['config']['islandora']['fedoraHost'], TRUE); | ||
$subRequestPut->headers->set('Content-Type', 'text/turtle'); | ||
//Here is the thing. We don't know if UUID of the collection we just created is already in the tripple store. | ||
//So what to do? We could just try to use our routes directly, but UUID check agains triplestore we could fail! | ||
//lets invoke the controller method directly | ||
$responsePut = $app['islandora.resourcecontroller']->put($app, $subRequestPut, $responsePost->headers->get('location'), "members"); | ||
if (201 == $responsePut->getStatusCode()) {// OK, indirect container created | ||
//Return only the last created resource | ||
$putHeaders = $responsePut->getHeaders(); | ||
//Guzzle psr7 response objects are inmutable. So we have to make this an array and add directly | ||
$putHeaders['Link'] = array('<'.$urlRoute.$tmpUuid.'/members>; rel="alternate"'); | ||
|
||
return new Response($responsePut->getBody(), 201, $putHeaders); | ||
} | ||
|
||
return $responsePut; | ||
} | ||
//Abort if PCDM collection object could not be created | ||
$app->abort($responsePost->getStatusCode(), 'Failed creating PCDM Collection'); | ||
}) | ||
->value('id',""); | ||
|
||
$app->after(function (Request $request, Response $response) use ($app) { | ||
$response->headers->set('X-Powered-By', 'Islandora Collection REST API v'.$app['config']['islandora']['apiVersion'], TRUE); //Nice | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like this 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gracias! |
||
|
||
}); | ||
|
||
$app->error(function (\Symfony\Component\HttpKernel\Exception\HttpException $e, $code) use ($app){ | ||
//Common error Handling | ||
$app->error(function (\EasyRdf_Exception $e, $code) use ($app) { | ||
if ($app['debug']) { | ||
return; | ||
} | ||
return new response(sprintf('Islandora Resource Service exception: %s / HTTP %d response', $e->getMessage(), $code), $code); | ||
|
||
return new response(sprintf('RDF Library exception', $e->getMessage(), $code), $code); | ||
}); | ||
$app->error(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e, $code) use ($app){ | ||
$app->error(function (\Symfony\Component\HttpKernel\Exception\HttpException $e, $code) use ($app) { | ||
if ($app['debug']) { | ||
return; | ||
} | ||
//Not sure what the best "verbose" message is | ||
return new response(sprintf('Islandora Resource Service exception: %s / HTTP %d response', $e->getMessage(), $code), $code); | ||
|
||
return new response(sprintf('Islandora Collection Service exception: %s / HTTP %d response', $e->getMessage(), $code), $code); | ||
}); | ||
$app->error(function (\Exception $e, $code) use ($app){ | ||
$app->error(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $e, $code) use ($app) { | ||
if ($app['debug']) { | ||
return; | ||
} | ||
return new response(sprintf('Islandora Resource Service uncatched exception: %s %d response', $e->getMessage(), $code), $code); | ||
} | ||
//Not sure what the best "verbose" message is | ||
return new response(sprintf('Islandora Collection Service exception: %s / HTTP %d response', $e->getMessage(), $code), $code); | ||
}); | ||
$app->error(function (\Exception $e, $code) use ($app) { | ||
if ($app['debug']) { | ||
return; | ||
} | ||
|
||
return new response(sprintf('Islandora Collection Service uncatched exception: %s %d response', $e->getMessage(), $code), $code); | ||
}); | ||
|
||
$app->run(); |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
@prefix ldp: <http://www.w3.org/ns/ldp#> . | ||
@prefix pcdm: <http://pcdm.org/models#> . | ||
@prefix ore: <http://www.openarchives.org/ore/terms/> . | ||
|
||
<> a ldp:IndirectContainer ; | ||
ldp:membershipResource <{{ resource }}> ; | ||
ldp:hasMemberRelation pcdm:hasMember ; | ||
ldp:insertedContentRelation ore:proxyFor . |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the apiVersion number tied to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ruebot, just an idea i had. Since our api is tied to branch name right now maybe that numbering makes no sense. Any suggestions? thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No suggestions now. Just curious what it was.