-
Notifications
You must be signed in to change notification settings - Fork 70
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
Alternative Islandora ResourceService using providers #135
Changes from 1 commit
564c2e0
d28273a
ce3699c
8ec0bef
dd65ddc
ff2750c
fae371c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Changed the one-file logic to full ServiceProvider and ControllerProvider. Allowed some external service injection/swapping possibilities for users, had to move the global middleware to specific middleware for each route to allow other silex apps to use and avoid all the transforms we do here
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,5 @@ install/downloads | |
site/ | ||
|
||
services/ResourceService/composer.lock | ||
|
||
services/ResourceServiceProvider/composer.lock |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
vendor/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "islandora/resource-service", | ||
"description": "RESTful service providing resources in Fedora 4", | ||
"repositories": [ | ||
{ | ||
"type": "vcs", | ||
"url": "/Users/dpino/Desktop/Development/ISLANDORAWORK/CLAW_MICRO/chullo" | ||
} | ||
], | ||
"require": { | ||
"islandora/chullo": "dev-api", | ||
"silex/silex": "^1.3", | ||
"symfony/config": "^3.0", | ||
"twig/twig": "^1.23", | ||
"symfony/yaml": "^3.0" | ||
}, | ||
"autoload": { | ||
"psr-4": {"Islandora\\ResourceService\\": "src/"} | ||
}, | ||
"authors": [ | ||
{ | ||
"name": "Diego Pino Navarro", | ||
"email": "dpino@krayon.cl" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Islandora Dev Settings to be used with $app['debug'] == TRUE | ||
islandora: | ||
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})?" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
islandora: | ||
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})?" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<?php | ||
|
||
namespace Islandora\ResourceService\Controller; | ||
use Silex\Application; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
class ResourceController { | ||
/** | ||
* Resource GET controller takes $id (valid UUID or empty) as first value to match, optional a child resource path | ||
* takes 'rx' and/or 'metadata' as optional query arguments | ||
* @see https://wiki.duraspace.org/display/FEDORA40/RESTful+HTTP+API#RESTfulHTTPAPI-GETRetrievethecontentoftheresource | ||
*/ | ||
public function get(Application $app, Request $request, $id, $child) { | ||
$tx = $request->query->get('tx', ""); | ||
$metadata = $request->query->get('metadata', FALSE) ? '/fcr:metadata' : ""; | ||
try { | ||
$response = $app['api']->getResource($app->escape($id).'/'.$child.$metadata, $request->headers->all(), $tx); | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, 'Chullo says "Fedora4 Repository Not available"'); | ||
} | ||
return $response; | ||
} | ||
/** | ||
* Resource POST route controller. takes $id (valid UUID or empty) for the parent resource as first value to match | ||
* takes 'rx' and/or 'checksum' as optional query arguments | ||
* @see https://wiki.duraspace.org/display/FEDORA40/RESTful+HTTP+API#RESTfulHTTPAPI-BluePOSTCreatenewresourceswithinaLDPcontainer | ||
*/ | ||
public function post(Application $app, Request $request, $id) { | ||
$tx = $request->query->get('tx', ""); | ||
$checksum = $request->query->get('checksum', ""); | ||
try { | ||
$response = $app['api']->createResource($app->escape($id), $request->getContent(), $request->headers->all(), $tx, $checksum); | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, '"Chullo says Fedora4 Repository is Not available"'); | ||
} | ||
return $response; | ||
} | ||
/** | ||
* Resource PUT route. takes $id (valid UUID or empty) for the resource to be update/created as first value to match, | ||
* optional a Child resource relative path | ||
* takes 'rx' and/or 'checksum' as optional query arguments | ||
* @see https://wiki.duraspace.org/display/FEDORA40/RESTful+HTTP+API#RESTfulHTTPAPI-YellowPUTCreatearesourcewithaspecifiedpath,orreplacethetriplesassociatedwitharesourcewiththetriplesprovidedintherequestbody. | ||
*/ | ||
public function put(Application $app, Request $request, $id, $child) { | ||
$tx = $request->query->get('tx', ""); | ||
$checksum = $request->query->get('checksum', ""); | ||
try { | ||
$response = $app['api']->saveResource($app->escape($id).'/'.$child, $request->getContent(), $request->headers->all(), $tx, $checksum); | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, '"Chullo says Fedora4 Repository is Not available"'); | ||
} | ||
return $response; | ||
} | ||
public function patch(Application $app, Request $request, $id, $child) { | ||
$tx = $request->query->get('tx', ""); | ||
try { | ||
$response = $app['api']->modifyResource($app->escape($id).'/'.$child, $request->getContent(), $request->headers->all(), $tx); | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, '"Chullo says Fedora4 Repository is Not available"'); | ||
} | ||
return $response; | ||
} | ||
public function delete(Application $app, Request $request, $id, $child) { | ||
$tx = $request->query->get('tx', ""); | ||
$force = $request->query->get('force', FALSE); | ||
try { | ||
$response = $app['api']->deleteResource($app->escape($id).'/'.$child, $tx); | ||
//remove tombstone also if 'force' == true and previous response is 204 | ||
if ((204 == $response->getStatusCode() || 410 == $response->getStatusCode()) && $force) { | ||
$response= $app['api']->deleteResource($app->escape($id).'/'.$child.'/fcr:tombstone', $tx); | ||
} | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, '"Chullo says Fedora4 Repository is Not available"'); | ||
} | ||
return $response; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
<?php | ||
|
||
namespace Islandora\ResourceService\Provider; | ||
|
||
use Silex\Application; | ||
use Silex\ServiceProviderInterface; | ||
use Silex\ControllerProviderInterface; | ||
use Islandora\Chullo\FedoraApi; | ||
use Islandora\Chullo\TriplestoreClient; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpKernel\HttpKernelInterface; | ||
use Symfony\Component\Yaml\Yaml; | ||
use Islandora\ResourceService\Controller\ResourceController; | ||
|
||
class ResourceServiceProvider implements ServiceProviderInterface, ControllerProviderInterface | ||
{ | ||
/** | ||
* Part of ServiceProviderInterface | ||
*/ | ||
function register(Application $app) { | ||
// | ||
// Define controller services | ||
// | ||
$app['islandora.resourcecontroller'] = $app->share(function() use ($app) { | ||
return new \Islandora\ResourceService\Controller\ResourceController($app); | ||
}); | ||
if (!isset($app['twig'])) { | ||
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) { | ||
return $twig; | ||
})); | ||
} | ||
if (!isset($app['api'])) { | ||
$app['api'] = $app->share(function() use ($app) { | ||
return FedoraApi::create($app['config']['islandora']['fedoraProtocol'].'://'.$app['config']['islandora']['fedoraHost'].$app['config']['islandora']['fedoraPath']); | ||
}); | ||
} | ||
if (!isset($app['triplestore'])) { | ||
$app['triplestore'] = $app->share(function() use ($app) { | ||
return TriplestoreClient::create($app['config']['islandora']['tripleProtocol'].'://'.$app['config']['islandora']['tripleHost'].$app['config']['islandora']['triplePath']); | ||
}); | ||
} | ||
/** | ||
* Ultra simplistic YAML settings loader. | ||
*/ | ||
if (!isset($app['config'])) { | ||
$app['config'] = $app->share(function() use ($app){ | ||
if ($app['debug']) { | ||
$configFile = __DIR__.'/../../config/settings.dev.yml'; | ||
} | ||
else { | ||
$configFile = __DIR__.'/../../config/settings.yml'; | ||
} | ||
$settings = Yaml::parse(file_get_contents($configFile)); | ||
return $settings; | ||
}); | ||
} | ||
/** | ||
* Make our middleware callback functions protected | ||
*/ | ||
/** | ||
* before middleware to handle browser requests. | ||
*/ | ||
$app['islandora.htmlHeaderToTurtle'] = $app->protect(function(Request $request) { | ||
// In case the request was made by a browser, avoid | ||
// returning the whole Fedora4 API Rest interface page. | ||
if (0 === strpos($request->headers->get('Accept'),'text/html')) { | ||
$request->headers->set('Accept', 'text/turtle', TRUE); | ||
} | ||
}); | ||
|
||
|
||
/** | ||
* before middleware to normalize host header to same as fedora's running | ||
* instance. | ||
*/ | ||
$app['islandora.hostHeaderNormalize'] = $app->protect(function(Request $request) use ($app) { | ||
// Normalize Host header to Repo's real location | ||
$request->headers->set('Host', $app['config']['islandora']['fedoraHost'], TRUE); | ||
}); | ||
|
||
/** | ||
* Converts request $id (uuid) into a fedora4 resourcePath | ||
*/ | ||
$app['islandora.idToUri'] = $app->protect(function ($id) use ($app) { | ||
// Run only if $id given /can also be refering root resource, | ||
// we accept only UUID V4 or empty | ||
if (NULL != $id) { | ||
$sparql_query = $app['twig']->render('getResourceByUUIDfromTS.sparql', array( | ||
'uuid' => $id, | ||
)); | ||
error_log($sparql_query); | ||
try { | ||
$sparql_result = $app['triplestore']->query($sparql_query); | ||
} | ||
catch (\Exception $e) { | ||
$app->abort(503, 'Chullo says "Triple Store Not available"'); | ||
} | ||
// We only assign one in case of multiple ones | ||
// Will have to check for edge cases? | ||
foreach ($sparql_result as $triple) { | ||
return $triple->s->getUri(); | ||
} | ||
// Abort the routes if we don't get a subject from the tripple. | ||
$app->abort(404, sprintf('Failed getting resource Path for "%s" from triple store', $id)); | ||
} | ||
else { | ||
// If $id is empty then assume we are dealing with fedora base rest endpoint | ||
return $app['config']['islandora']['fedoraProtocol'].'://'.$app['config']['islandora']['fedoraHost'].$app['config']['islandora']['fedoraPath']; | ||
} | ||
}); | ||
|
||
} | ||
|
||
function boot(Application $app) { | ||
} | ||
|
||
/** | ||
* Part of ControllerProviderInterface | ||
*/ | ||
public function connect(Application $app) { | ||
$controllers = $app['controllers_factory']; | ||
// | ||
// Define routing referring to controller services | ||
// | ||
$controllers->get("/resource/{id}/{child}", "islandora.resourcecontroller:get") | ||
->convert('id', $app['islandora.idToUri']) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($app['islandora.hostHeaderNormalize']) | ||
->before($app['islandora.htmlHeaderToTurtle']) | ||
->value('id',"") | ||
->value('child',"") | ||
->bind('islandora.resourceGet'); | ||
$controllers->post("/resource/{id}", "islandora.resourcecontroller:post") | ||
->convert('id', $app['islandora.idToUri']) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($app['islandora.hostHeaderNormalize']) | ||
->before($app['islandora.htmlHeaderToTurtle']) | ||
->value('id',"") | ||
->bind('islandora.resourcePost'); | ||
$controllers->put("/resource/{id}/{child}", "islandora.resourcecontroller:put") | ||
->convert('id', $app['islandora.idToUri']) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($app['islandora.hostHeaderNormalize']) | ||
->before($app['islandora.htmlHeaderToTurtle']) | ||
->value('id',"") | ||
->value('child',"") | ||
->bind('islandora.resourcePut'); | ||
$controllers->patch("/resource/{id}/{child}", "islandora.resourcecontroller:patch") | ||
->convert('id', $app['islandora.idToUri']) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($app['islandora.hostHeaderNormalize']) | ||
->before($app['islandora.htmlHeaderToTurtle']) | ||
->value('id',"") | ||
->value('child',"") | ||
->bind('islandora.resourcePatch'); | ||
$controllers->delete("/resource/{id}/{child}", "islandora.resourcecontroller:delete") | ||
->convert('id', $app['islandora.idToUri']) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($app['islandora.hostHeaderNormalize']) | ||
->before($app['islandora.htmlHeaderToTurtle']) | ||
->value('id',"") | ||
->value('child',"") | ||
->bind('islandora.resourceDelete'); | ||
return $controllers; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
namespace Islandora\ResourceService; | ||
|
||
require_once __DIR__.'/../vendor/autoload.php'; | ||
|
||
use Silex\Application; | ||
use Islandora\Chullo\FedoraApi; | ||
use Islandora\Chullo\TriplestoreClient; | ||
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 Islandora\ResourceService\Provider\ResourceServiceProvider; | ||
|
||
date_default_timezone_set('UTC'); | ||
|
||
$app = new Application(); | ||
|
||
$app['debug'] = true; | ||
$app->register(new \Silex\Provider\ServiceControllerServiceProvider()); | ||
$app->register(new \Silex\Provider\TwigServiceProvider(), array( | ||
'twig.path' => __DIR__.'/../templates', | ||
)); | ||
|
||
$islandoraResourceServiceProvider = new \Islandora\ResourceService\Provider\ResourceServiceProvider; | ||
|
||
$app->register($islandoraResourceServiceProvider); | ||
$app->mount("/islandora", $islandoraResourceServiceProvider); | ||
|
||
/** | ||
* Convert returned Guzzle responses to Symfony responses, type hinted. | ||
*/ | ||
$app->view(function (ResponseInterface $psr7) { | ||
return new Response($psr7->getBody(), $psr7->getStatusCode(), $psr7->getHeaders()); | ||
}); | ||
|
||
$app->after(function (Request $request, Response $response, Application $app) { | ||
// Todo a closing controller, not sure what now but i had an idea. | ||
}); | ||
$app->error(function (\Symfony\Component\HttpKernel\Exception\HttpException $e, $code) use ($app){ | ||
if ($app['debug']) { | ||
return; | ||
} | ||
return new response(sprintf('Islandora Resource Service exception: %s / HTTP %d response', $e->getMessage(), $code), $code); | ||
}); | ||
$app->error(function (\Symfony\Component\HttpKernel\Exception\NotFoundHttpException $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); | ||
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. Capital R? 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. Right!! moving to uppercase… sorry. Thanks for catching this @daniel-dgi and thanks @whikloj for explaining me what dani catched!! I can't even see my own code sometimes. |
||
}); | ||
$app->error(function (\Exception $e, $code) use ($app){ | ||
if ($app['debug']) { | ||
return; | ||
} | ||
return new response(sprintf('Islandora Resource Service uncatched exception: %s %d response', $e->getMessage(), $code), $code); | ||
}); | ||
|
||
|
||
$app->run(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
PREFIX nfo: <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo/v1.2/> | ||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> | ||
SELECT ?s WHERE { | ||
?s nfo:uuid "{{uuid}}"^^<http://www.w3.org/2001/XMLSchema#string> . | ||
} |
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.
Is this necessary, I thought the $app->share() took care of this type of logic?
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.
@whikloj, do you mean the "use" statements? I was unsure if putting there had any logic, but i thought it could be a good idea for code clarity/new users. I can remove it once all others had a look at the code