Skip to content

Commit fca6776

Browse files
committed
Add link-metadata response for when requested path is a file.
1 parent d7b82f8 commit fca6776

1 file changed

Lines changed: 142 additions & 26 deletions

File tree

src/Server.php

Lines changed: 142 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ class Server
1919

2020
public const ERROR_CAN_NOT_PARSE_FOR_PATCH = 'Could not parse the requested resource for patching';
2121
public const ERROR_CAN_NOT_DELETE_NON_EMPTY_CONTAINER = 'Only empty containers can be deleted, "%s" is not empty';
22+
public const ERROR_CAN_NOT_PARSE_METADATA = 'Could not parse metadata for %s';
23+
public const ERROR_CAN_NOT_REDIRECT_WITHOUT_URL = "Cannot create %s: no URL set";
2224
public const ERROR_MISSING_SPARQL_CONTENT_TYPE = 'Request is missing required Content-Type "application/sparql-update" or "application/sparql-update-single-match"';
25+
public const ERROR_MULTIPLE_LINK_METADATA_FOUND = 'More than one link-metadata found for %s';
2326
public const ERROR_NOT_IMPLEMENTED_SPARQL = 'SPARQL Not Implemented';
2427
public const ERROR_PATH_DOES_NOT_EXIST = 'Requested path "%s" does not exist';
2528
public const ERROR_PATH_EXISTS = 'Requested path "%s" already exists';
@@ -538,40 +541,43 @@ private function handleReadRequest(Response $response, string $path, $contents,
538541
$response->getBody()->write($contents);
539542
$response = $response->withHeader("Content-type", "text/turtle");
540543
$response = $response->withStatus(200);
541-
} elseif ($filesystem->has($path) === false) {
544+
} elseif ($filesystem->has($path) === false && $this->hasDescribedBy($path) === false) {
545+
/*/ The file does not exist and no link-metadata is present /*/
542546
$message = vsprintf(self::ERROR_PATH_DOES_NOT_EXIST, [$path]);
543547
$response->getBody()->write($message);
544548
$response = $response->withStatus(404);
545549
} else {
546-
$mimetype = $filesystem->getMimetype($path);
547-
if ($mimetype === self::MIME_TYPE_DIRECTORY) {
550+
$linkMetadataResponse = $this->handleLinkMetadata($response, $path);
551+
if ($linkMetadataResponse !== null) {
552+
/*/ Link-metadata is present, return the altered response /*/
553+
$response = $linkMetadataResponse;
554+
} elseif ($filesystem->getMimetype($path) === self::MIME_TYPE_DIRECTORY) {
548555
$contents = $this->listDirectoryAsTurtle($path);
549556
$response->getBody()->write($contents);
550-
$response = $response->withHeader("Content-type", "text/turtle");
551-
$response = $response->withStatus(200);
552-
} else {
553-
if ($filesystem->asMime($mime)->has($path)) {
554-
$contents = $filesystem->asMime($mime)->read($path);
557+
$response = $response->withHeader("Content-type", "text/turtle")->withStatus(200);
558+
} elseif ($filesystem->asMime($mime)->has($path)) {
559+
/*/ The file does exist and no link-metadata is present /*/
560+
$response = $this->addLinkRelationHeaders($response, $path, $mime);
561+
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
566+
} else {
567+
$mimetype = $filesystem->asMime($mime)->getMimetype($path);
568+
}
555569

556-
$response = $this->addLinkRelationHeaders($response, $path, $mime);
570+
$contents = $filesystem->asMime($mime)->read($path);
557571

558-
if (preg_match('/.ttl$/', $path)) {
559-
$mimetype = "text/turtle"; // FIXME: teach flysystem that .ttl means text/turtle
560-
} elseif (preg_match('/.acl$/', $path)) {
561-
$mimetype = "text/turtle"; // FIXME: teach flysystem that .acl files also want text/turtle
562-
} else {
563-
$mimetype = $filesystem->asMime($mime)->getMimetype($path);
564-
}
565-
if ($contents !== false) {
566-
$response->getBody()->write($contents);
567-
$response = $response->withHeader("Content-type", $mimetype);
568-
$response = $response->withStatus(200);
569-
}
570-
} else {
571-
$message = vsprintf(self::ERROR_PATH_DOES_NOT_EXIST, [$path]);
572-
$response->getBody()->write($message);
573-
$response = $response->withStatus(404);
574-
}
572+
if ($contents !== false) {
573+
$response->getBody()->write($contents);
574+
$response = $response->withHeader("Content-type", $mimetype)->withStatus(200);
575+
}
576+
} else {
577+
/*/ The file does exist in another format and no link-metadata is present /*/
578+
$message = vsprintf(self::ERROR_PATH_DOES_NOT_EXIST, [$path]);
579+
$response->getBody()->write($message);
580+
$response = $response->withStatus(404);
575581
}
576582
}
577583

@@ -609,6 +615,10 @@ private function listDirectoryAsTurtle($path)
609615
);
610616

611617
foreach ($listContents as $item) {
618+
619+
// @FIXME: $item needs to be checked for "describedby" metadata to see if they need Meta Link handling
620+
// for instance be added to the file list
621+
612622
switch($item['type']) {
613623
case "file":
614624
// ACL and meta files should not be listed in directory overview
@@ -734,4 +744,110 @@ private function hasDescribedBy(string $path, $mime = null): bool
734744
{
735745
return $this->getDescribedByPath($path, $mime) !== '';
736746
}
747+
748+
// =========================================================================
749+
// @TODO: All link-metadata Response logic should probably be moved to a separate class.
750+
751+
private function handleLinkMetadata(Response $response, string $path)
752+
{
753+
$returnResponse = null;
754+
755+
// @FIXME: If a $path is a file underneath a directory that has been marked as moved, the response should also be a redirect/404/410.
756+
757+
if ($this->hasDescribedBy($path)) {
758+
$linkMeta = $this->parseLinkedMetadata($path);
759+
760+
if (count($linkMeta) > 1) {
761+
throw Exception::create(self::ERROR_MULTIPLE_LINK_METADATA_FOUND, [$path]);
762+
}
763+
764+
$returnResponse = $this->buildLinkMetadataResponse($linkMeta, $response);
765+
}
766+
767+
return $returnResponse;
768+
}
769+
770+
private function buildLinkMetadataResponse(array $linkMeta, Response $response)
771+
{
772+
$returnResponse = null;
773+
774+
if (count($linkMeta) > 0) {
775+
$linkMetaType = array_key_first($linkMeta);
776+
777+
$type = substr($linkMetaType, strrpos($linkMetaType, '#') + 1);
778+
779+
$linkMetaValue = reset($linkMeta);
780+
$value = array_pop($linkMetaValue);
781+
$url = $value['value'] ?? null;
782+
783+
switch ($type) {
784+
case 'deleted':
785+
$returnResponse = $response->withStatus(404);
786+
break;
787+
788+
case 'forget':
789+
$returnResponse = $response->withStatus(410);
790+
break;
791+
792+
case 'redirectPermanent':
793+
if ($url === null) {
794+
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
795+
}
796+
$returnResponse = $response->withHeader('Location', $url)->withStatus(308);
797+
break;
798+
799+
case 'redirectTemporary':
800+
if ($url === null) {
801+
throw Exception::create(self::ERROR_CAN_NOT_REDIRECT_WITHOUT_URL, [$type]);
802+
}
803+
$returnResponse = $response->withHeader('Location', $url)->withStatus(307);
804+
break;
805+
806+
default:
807+
// No (known) Link Metadata present = follow regular logic
808+
break;
809+
}
810+
}
811+
812+
return $returnResponse;
813+
}
814+
815+
private function parseLinkedMetadata(string $path)
816+
{
817+
$linkMeta = [];
818+
819+
try {
820+
$describedByPath = $this->filesystem->getMetadata($path)['describedby'] ?? '';
821+
$describedByContents = $this->filesystem->read($describedByPath);
822+
} catch (FileNotFoundException $e) {
823+
// @CHECKME: If, for whatever reason, the file is not present after all... Do we care here?
824+
return $linkMeta;
825+
}
826+
827+
$graph = $this->getGraph();
828+
829+
try {
830+
$graph->parse($describedByContents);
831+
} catch (EasyRdf_Exception $exception) {
832+
throw Exception::create(self::ERROR_CAN_NOT_PARSE_METADATA, [$path]);
833+
}
834+
835+
$toRdfPhp = $graph->toRdfPhp();
836+
837+
$path = ltrim($path, '/');
838+
839+
if (isset($toRdfPhp[$path])) {
840+
$linkMeta = array_filter($toRdfPhp[$path], static function ($key) {
841+
$uris = implode('|', [
842+
'pdsinterop.org/solid-link-metadata/links.ttl',
843+
'purl.org/pdsinterop/link-metadata',
844+
]);
845+
846+
return (bool) preg_match("#({$uris})#",
847+
$key);
848+
}, ARRAY_FILTER_USE_KEY);
849+
}
850+
851+
return $linkMeta;
852+
}
737853
}

0 commit comments

Comments
 (0)