-
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
Example ResourceService #131
Changes from 13 commits
015dd6c
9638bca
00c2cf3
d3d34c8
b7a5146
92d04ca
f6baff1
86bd39b
89a09cc
2a0efc3
4b84572
d733150
24513d7
0654a2b
2428860
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 |
---|---|---|
|
@@ -4,3 +4,5 @@ tests/test_config.ini | |
install/.vagrant | ||
install/downloads | ||
site/ | ||
|
||
services/ResourceService/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": "[email protected]" | ||
} | ||
] | ||
} |
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,250 @@ | ||
<?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 Symfony\Component\Yaml\Yaml; | ||
|
||
date_default_timezone_set('UTC'); | ||
|
||
$app = new Application(); | ||
|
||
$app['debug'] = true; | ||
|
||
$app->register(new \Silex\Provider\TwigServiceProvider(), array( | ||
'twig.path' => __DIR__.'/../templates', | ||
)); | ||
|
||
|
||
$app['twig'] = $app->share($app->extend('twig', function($twig, $app) { | ||
return $twig; | ||
})); | ||
|
||
$app['api'] = $app->share(function() use ($app) { | ||
return FedoraApi::create($app['config']['islandora']['fedoraProtocol'].'://'.$app['config']['islandora']['fedoraHost'].$app['config']['islandora']['fedoraPath']); | ||
}); | ||
|
||
$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. | ||
*/ | ||
$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)); | ||
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. So simple. Love it. 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. 😊 |
||
return $settings; | ||
}); | ||
|
||
/** | ||
* before middleware to handle browser requests. | ||
*/ | ||
$htmlHeaderToTurtle = 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. | ||
*/ | ||
$hostHeaderNormalize = 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 | ||
*/ | ||
$idToUri = 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, | ||
)); | ||
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']; | ||
} | ||
|
||
}; | ||
|
||
/** | ||
* Convert returned Guzzle responses to Symfony responses. | ||
*/ | ||
$app->view(function (ResponseInterface $psr7) { | ||
return new Response($psr7->getBody(), $psr7->getStatusCode(), $psr7->getHeaders()); | ||
}); | ||
|
||
/** | ||
* Resource GET route. 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 | ||
*/ | ||
$app->get("/islandora/resource/{id}/{child}",function (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; | ||
}) | ||
->value('child',"") | ||
->value('id',"") | ||
->convert('id', $idToUri) | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($htmlHeaderToTurtle) | ||
->before($hostHeaderNormalize); | ||
|
||
/** | ||
* Resource POST route. 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 | ||
*/ | ||
$app->post("/islandora/resource/{id}",function (Application $app, Request $request, $id, $checksum) { | ||
$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; | ||
}) | ||
->value('id',"") | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($htmlHeaderToTurtle) | ||
->before($hostHeaderNormalize); | ||
|
||
/** | ||
* 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. | ||
*/ | ||
$app->put("/islandora/resource/{id}/{child}",function (Application $app, Request $request, $id, $child) { | ||
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. Should there also be a PUT route for when you only have an $id? Same for PATCH below and DELETE 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. Hi, child is optional, set to "" by default at https://github.com/Islandora-CLAW/CLAW/pull/131/files#diff-079c3800368dc100b90e9a9254e6fcc6R176 , so if not passed/used in the call it's all good, the route will match. Only post is {$id} only, and i would love to add $child there 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. Ok, error was due to me sending my own Content-type header with curl. Future work might be to just strip the duplicate headers for PATCH as it has to be application/sparql-update. |
||
$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; | ||
}) | ||
->value('child',"") | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($htmlHeaderToTurtle) | ||
->before($hostHeaderNormalize); | ||
|
||
/** | ||
* Resource PATCH route. takes $id (valid UUID or empty) for the resource to be modified via SPARQL as first value to match, | ||
* optional a Child resource relative path | ||
* takes 'rx' as optional query argument | ||
* @see https://wiki.duraspace.org/display/FEDORA40/RESTful+HTTP+API#RESTfulHTTPAPI-GreenPATCHModifythetriplesassociatedwitharesourcewithSPARQL-Update | ||
*/ | ||
$app->patch("/islandora/resource/{id}/{child}",function (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; | ||
}) | ||
->value('child',"") | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($htmlHeaderToTurtle) | ||
->before($hostHeaderNormalize); | ||
|
||
/** | ||
* Resource DELETE route. takes $id (valid UUID or empty) for the resource to be modified via SPARQL as first value to match, | ||
* optional a Child resource relative path | ||
* takes 'rx' as optional query argument, also 'force' to remove the tombstone in one step | ||
* @see https://wiki.duraspace.org/display/FEDORA40/RESTful+HTTP+API#RESTfulHTTPAPI-RedDELETEDeletearesource | ||
*/ | ||
$app->delete("/islandora/resource/{id}/{child}",function (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; | ||
}) | ||
->value('child',"") | ||
->assert('id',$app['config']['islandora']['resourceIdRegex']) | ||
->before($htmlHeaderToTurtle) | ||
->before($hostHeaderNormalize); | ||
|
||
$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); | ||
}); | ||
$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 commentThe reason will be displayed to describe this comment to others. Learn more. i could stick here an extra variable, to get also type of resource. This way we can avoid hitting fedora if the send one mismatches the existing one. (it's rdf, we try to put a binary for example). |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
composer.lock | ||
composer.phar | ||
vendor |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "islandora/transaction-service", | ||
"description": "RESTful service providing transactions in Fedora 4", | ||
"repositories": [ | ||
{ | ||
"type": "vcs", | ||
"url": "/Users/dpino/Desktop/Development/ISLANDORAWORK/CLAW_MICRO/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. Should this be the main github repo and just make this PR rely on the Chullo PR? |
||
} | ||
], | ||
"require": { | ||
"islandora/chullo": "dev-master", | ||
"silex/silex": "^1.3" | ||
}, | ||
"autoload": { | ||
"psr-4": {"Islandora\\TransactionService\\": "src/"} | ||
}, | ||
"authors": [ | ||
{ | ||
"name": "Daniel Lamb", | ||
"email": "[email protected]" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
namespace Islandora\TransactionService; | ||
|
||
require_once __DIR__.'/../vendor/autoload.php'; | ||
|
||
use GuzzleHttp\Exception\ClientException; | ||
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. We can remove Guzzle here right? |
||
use Islandora\Chullo\FedoraApi; | ||
use Silex\Application; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Psr\Http\Message\ResponseInterface; | ||
|
||
date_default_timezone_set('UTC'); | ||
|
||
$app = new Application(); | ||
|
||
$app['debug'] = true; | ||
|
||
$app['fedora'] = $app->share(function () { | ||
return FedoraApi::create('http://127.0.0.1:8080/fcrepo/rest'); | ||
}); | ||
|
||
/** | ||
* Convert returned Guzzle responses to Symfony responses. | ||
*/ | ||
$app->view(function (ResponseInterface $psr7) { | ||
return new Response($psr7->getBody(), $psr7->getStatusCode(), $psr7->getHeaders()); | ||
}); | ||
|
||
$app->post( | ||
"/islandora/transaction", | ||
function (Application $app) { | ||
return $app['fedora']->createTransaction(); | ||
} | ||
); | ||
|
||
$app->post( | ||
"/islandora/transaction/{id}/extend", | ||
function (Application $app, $id) { | ||
return $app['fedora']->extendTransaction($id); | ||
} | ||
); | ||
|
||
$app->post( | ||
"/islandora/transaction/{id}/commit", | ||
function (Application $app, $id) { | ||
return $app['fedora']->commitTransaction($id); | ||
} | ||
); | ||
|
||
$app->post( | ||
"/islandora/transaction/{id}/rollback", | ||
function (Application $app, $id) { | ||
return $app['fedora']->rollbackTransaction($id); | ||
} | ||
); | ||
|
||
$app->run(); |
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.
Duplicate of below comment: Should this be the main github repo and just make this PR rely on the Chullo PR?
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.
completely. Not needed anymore, since we have agreement on chullo. Will change this during the day. Thanks @whikloj