Skip to content

Commit ed7e5fe

Browse files
authored
Merge pull request #14 from pdsinterop/fix/link-metadata
Fixes for link-metadata feature
2 parents 404594b + 14a1cc5 commit ed7e5fe

1 file changed

Lines changed: 95 additions & 9 deletions

File tree

src/Server.php

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ final public function __construct(Filesystem $filesystem, Response $response, Gr
9898
$this->filesystem = $filesystem;
9999
$this->graph = $graph ?? new Graph();
100100
$this->response = $response;
101+
// @TODO: Mention \EasyRdf_Namespace::set('lm', 'https://purl.org/pdsinterop/link-metadata#');
101102
}
102103

103104
final public function respondToRequest(Request $request): Response
@@ -280,6 +281,7 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
280281

281282
try {
282283
// Assuming this is in our native format, turtle
284+
// @CHECKME: Does the Graph Parse here also need an URI?
283285
$graph->parse($data, "turtle");
284286
// FIXME: Adding this base will allow us to parse <> entries; , $this->baseUrl . $this->basePath . $path), but that breaks the build.
285287
// FIXME: Use enums from namespace Pdsinterop\Rdf\Enum\Format instead of 'turtle'?
@@ -294,12 +296,14 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
294296
switch($command) {
295297
case "INSERT":
296298
// insert $triple(s) into $graph
299+
// @CHECKME: Does the Graph Parse here also need an URI?
297300
$graph->parse($triples, "turtle"); // FIXME: The triples here are in sparql format, not in turtle;
298301

299302
break;
300303
case "DELETE":
301304
// delete $triples from $graph
302305
$deleteGraph = $this->getGraph();
306+
// @CHECKME: Does the Graph Parse here also need an URI?
303307
$deleteGraph->parse($triples, "turtle"); // FIXME: The triples here are in sparql format, not in turtle;
304308
$resources = $deleteGraph->resources();
305309
foreach ($resources as $resource) {
@@ -339,6 +343,7 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
339343
$response = $response->withStatus($success ? 201 : 500);
340344

341345
if ($success) {
346+
$this->removeLinkFromMetaFileFor($path);
342347
$this->sendWebsocketUpdate($path);
343348
}
344349
} catch (EasyRdf_Exception $exception) {
@@ -385,6 +390,7 @@ private function handleCreateRequest(Response $response, string $path, $contents
385390
}
386391

387392
if ($success) {
393+
$this->removeLinkFromMetaFileFor($path);
388394
$response = $response->withHeader("Location", $this->baseUrl . $path);
389395
$response = $response->withStatus(201);
390396
$this->sendWebsocketUpdate($path);
@@ -420,6 +426,7 @@ private function handleCreateDirectoryRequest(Response $response, string $path):
420426
$success = $filesystem->createDir($path);
421427
$response = $response->withStatus($success ? 201 : 500);
422428
if ($success) {
429+
$this->removeLinkFromMetaFileFor($path);
423430
$this->sendWebsocketUpdate($path);
424431
}
425432
}
@@ -507,6 +514,7 @@ private function handleUpdateRequest(Response $response, string $path, string $c
507514
$success = $filesystem->update($path, $contents);
508515
$response = $response->withStatus($success ? 201 : 500);
509516
if ($success) {
517+
$this->removeLinkFromMetaFileFor($path);
510518
$this->sendWebsocketUpdate($path);
511519
}
512520
}
@@ -559,10 +567,8 @@ private function handleReadRequest(Response $response, string $path, $contents,
559567
/*/ The file does exist and no link-metadata is present /*/
560568
$response = $this->addLinkRelationHeaders($response, $path, $mime);
561569

562-
if (preg_match('/.ttl$/', $path)) {
563-
$mimetype = "text/turtle"; // FIXME: teach flysystem that .ttl means text/turtle
564-
} elseif (preg_match('/.acl$/', $path)) {
565-
$mimetype = "text/turtle"; // FIXME: teach flysystem that .acl files also want text/turtle
570+
if (preg_match('/\.(acl|meta|ttl)$/', $path)) {
571+
$mimetype = "text/turtle"; // FIXME: teach flysystem that .acl/.meta/.ttl means text/turtle
566572
} else {
567573
$mimetype = $filesystem->asMime($mime)->getMimetype($path);
568574
}
@@ -572,6 +578,11 @@ private function handleReadRequest(Response $response, string $path, $contents,
572578
if ($contents !== false) {
573579
$response->getBody()->write($contents);
574580
$response = $response->withHeader("Content-type", $mimetype)->withStatus(200);
581+
} else {
582+
/*/ The file does exist in another format and no link-metadata is present /*/
583+
$message = vsprintf(self::ERROR_PATH_DOES_NOT_EXIST, [$path]);
584+
$response->getBody()->write($message);
585+
$response = $response->withStatus(404);
575586
}
576587
} else {
577588
/*/ The file does exist in another format and no link-metadata is present /*/
@@ -694,6 +705,7 @@ private function addLinkRelationHeaders(Response $response, string $path, $mime=
694705
{
695706
// @FIXME: If a `.meta` file is requested, it must have header `Link: </path/to/resource>; rel="describes"`
696707

708+
//@CHECKME: Should the ACL link header be added here or in/by the Auth server?
697709
if ($this->hasAcl($path, $mime)) {
698710
$value = sprintf('<%s>; rel="acl"', $this->getAclPath($path, $mime));
699711
$response = $response->withAddedHeader('Link', $value);
@@ -815,23 +827,34 @@ private function parseLinkedMetadata(string $path)
815827
$describedByPath = $this->filesystem->getMetadata($path)['describedby'] ?? '';
816828
$describedByContents = $this->filesystem->read($describedByPath);
817829
} catch (FileNotFoundException $e) {
818-
// @CHECKME: If, for whatever reason, the file is not present after all... Do we care here?
830+
// If, for whatever reason, the file is not present after all, the resource should still be returned (or a 404)
831+
// @CHECKME: Should the upstream add a message to the header or something?
819832
return $linkMeta;
820833
}
821834

822835
$graph = $this->getGraph();
823836

824837
try {
825-
$graph->parse($describedByContents);
838+
$graph->parse($describedByContents, null, '/'.$describedByPath);
826839
} catch (EasyRdf_Exception $exception) {
827-
throw Exception::create(self::ERROR_CAN_NOT_PARSE_METADATA, [$path]);
840+
// If the metadata can not be parsed, the resource should still be returned (or a 404)
841+
// @CHECKME: Should the upstream add a message to the header or something?
842+
return $linkMeta;
828843
}
829844

830845
$toRdfPhp = $graph->toRdfPhp();
831846

832847
$rdfPaths = array_keys($toRdfPhp);
833848
$foundPath = $this->findPath($rdfPaths, $path);
834849

850+
// If the requested path is a sub folder or file, it also needs te be handled
851+
foreach ($rdfPaths as $rdfPath) {
852+
if (strpos($rdfPath, $path) !== false) {
853+
$foundPath = $rdfPath;
854+
break;
855+
}
856+
}
857+
835858
if (isset($toRdfPhp[$foundPath])) {
836859
$filteredRdfData = array_filter($toRdfPhp[$foundPath], static function ($key) {
837860
$uris = implode('|', [
@@ -855,12 +878,18 @@ private function parseLinkedMetadata(string $path)
855878
$value = array_pop($linkMetaValue);
856879
$url = $value['value'] ?? null;
857880

881+
if (strpos($foundPath, './') === 0) {
882+
// Filepath is relative to the meta file
883+
$path = $foundPath;
884+
}
885+
858886
if ($path !== $foundPath) {
859887
// Change the path from the request to the redirect (or not found) path
860888
$url = substr_replace($path,
861889
$url,
862890
strpos($path, $foundPath),
863-
strlen($foundPath));
891+
strlen($foundPath))
892+
;
864893
}
865894

866895
$linkMeta = [
@@ -885,12 +914,69 @@ private function findPath(array $rdfPaths, string $path)
885914
// @FIXME: We have no way of knowing if the file is a directory or a file.
886915
// This means that, unless we make a trialing slash `/` required,
887916
// (using the example for `forget.ttl`) forget.ttl/foo.txt will
888-
// also work although semantically is should not
917+
// also work although semantically it should not
889918
$path = $rdfPath;
890919
break;
891920
}
892921
}
893922

894923
return $path;
895924
}
925+
926+
private function removeLinkFromMetaFileFor($path): bool
927+
{
928+
$result = false;
929+
930+
if ($this->hasDescribedBy($path)) {
931+
$describedByPath = $this->getDescribedByPath($path);
932+
933+
$graph = $this->getGraph();
934+
935+
try {
936+
$contents = $this->filesystem->read($describedByPath);
937+
$graph->parse($contents, 'turtle', '/'.$describedByPath);
938+
} catch (\Throwable $e) {
939+
return false;
940+
}
941+
942+
// A resource might be added for a folder but written to a file,
943+
// or vice-versa. In both cases, the _other_ entry also needs to be
944+
// removed. And depending on the RDF entry, the resource might have
945+
// a leading slash or not, so that also needs to be checked.
946+
$normalizedPath = trim($path, '/');
947+
$resourcePaths = array_unique([
948+
$normalizedPath,
949+
$normalizedPath . '/',
950+
'/' . $normalizedPath,
951+
'/' . $normalizedPath . '/',
952+
]);
953+
954+
// @CHECKME: If an entry for a sub-folder is present but then a file is written,
955+
// removing the folder, should the sub-folder entry also be removed?
956+
957+
$changed = false;
958+
foreach ($resourcePaths as $resourcePath) {
959+
$resource = $graph->resource($resourcePath);
960+
961+
$predicates = $resource->propertyUris();
962+
foreach ($predicates as $predicate) {
963+
if (strpos($predicate, 'https://purl.org/pdsinterop/link-metadata#') === 0) {
964+
$changed = true;
965+
$graph->deleteSingleProperty($resource, $predicate);
966+
}
967+
}
968+
}
969+
970+
if ($changed) {
971+
$changedContents = $graph->serialise('turtle');
972+
try {
973+
$result = $this->filesystem->update($describedByPath, $changedContents);
974+
} catch (FileNotFoundException $exception) {
975+
// $result is already false;
976+
}
977+
}
978+
}
979+
980+
return $result;
981+
}
896982
}

0 commit comments

Comments
 (0)