diff --git a/composer.json b/composer.json index a8233be..f3cbb6b 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,8 @@ { "autoload": { "psr-4": { - "Pdsinterop\\Solid\\Resources\\": "src/" + "Pdsinterop\\Solid\\Resources\\": "src/", + "Pdsinterop\\Solid\\SolidNotifications\\": "src/" } }, "autoload-dev": { diff --git a/src/Server.php b/src/Server.php index 32c3184..5965c26 100644 --- a/src/Server.php +++ b/src/Server.php @@ -2,6 +2,7 @@ namespace Pdsinterop\Solid\Resources; +use Pdsinterop\Solid\SolidNotifications\SolidNotificationsInterface; use EasyRdf\Exception as RdfException; use EasyRdf\Graph as Graph; use Laminas\Diactoros\ServerRequest; @@ -11,7 +12,6 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Throwable; -use WebSocket\Client; use pietercolpaert\hardf\TriGWriter; use pietercolpaert\hardf\TriGParser; @@ -37,6 +37,10 @@ class Server private const MIME_TYPE_DIRECTORY = 'directory'; private const QUERY_PARAM_HTTP_METHOD = 'http-method'; + private const NOTIFICATION_TYPE_CREATE = "Create"; + private const NOTIFICATION_TYPE_UPDATE = "Update"; + private const NOTIFICATION_TYPE_DELETE = "Delete"; + /** @var string[] */ private $availableMethods = [ 'DELETE', @@ -55,8 +59,8 @@ class Server private $filesystem; /** @var Graph */ private $graph; - /** @var string */ - private $pubsub; + /** @var SolidNotificationsInterface */ + private $notifications; /** @var Response */ private $response; @@ -85,11 +89,10 @@ final public function setBaseUrl($url) $this->basePath = $serverRequest->getUri()->getPath(); } - final public function setPubSubUrl($url) + final public function setNotifications(SolidNotificationsInterface $notifications) { - $this->pubsub = $url; + $this->notifications = $notifications; } - //////////////////////////////// PUBLIC API \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ // @TODO: The Graph should be injected by the caller @@ -97,7 +100,6 @@ final public function __construct(Filesystem $filesystem, Response $response, Gr { $this->basePath = ''; $this->baseUrl = ''; - $this->pubsub = ''; $this->filesystem = $filesystem; $this->graph = $graph ?? new Graph(); $this->response = $response; @@ -152,17 +154,6 @@ private function handle(string $method, string $path, $contents, $request): Resp // Lets assume the worst... $response = $response->withStatus(500); - // Set Accept, Allow, and CORS headers - // $response = $response - // ->withHeader('Access-Control-Allow-Origin', '*') - // ->withHeader('Access-Control-Allow-Credentials','true') - // ->withHeader('Access-Control-Allow-Headers', '*, authorization, accept, content-type') - // @FIXME: Add correct headers to resources (for instance allow DELETE on a GET resource) - // ->withAddedHeader('Accept-Patch', 'text/ldpatch') - // ->withAddedHeader('Accept-Post', 'text/turtle, application/ld+json, image/bmp, image/jpeg') - // ->withHeader('Allow', 'GET, HEAD, OPTIONS, PATCH, POST, PUT') - //; - switch ($method) { case 'DELETE': $response = $this->handleDeleteRequest($response, $path, $contents); @@ -175,9 +166,6 @@ private function handle(string $method, string $path, $contents, $request): Resp $response->getBody()->rewind(); $response->getBody()->write(''); $response = $response->withStatus(204); // CHECKME: nextcloud will remove the updates-via header - any objections to give the 'HEAD' request a 'no content' response type? - if ($this->pubsub) { - $response = $response->withHeader("updates-via", $this->pubsub); - } } break; @@ -350,7 +338,7 @@ private function handleSparqlUpdate(Response $response, string $path, $contents) if ($success) { $this->removeLinkFromMetaFileFor($path); - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_UPDATE); } } catch (RdfException $exception) { $response->getBody()->write(self::ERROR_CAN_NOT_PARSE_FOR_PATCH); @@ -501,7 +489,7 @@ private function handleN3Update(Response $response, string $path, $contents): Re if ($success) { $this->removeLinkFromMetaFileFor($path); - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_UPDATE); } } catch (RdfException $exception) { $response->getBody()->write(self::ERROR_CAN_NOT_PARSE_FOR_PATCH); @@ -551,7 +539,7 @@ private function handleCreateRequest(Response $response, string $path, $contents $this->removeLinkFromMetaFileFor($path); $response = $response->withHeader("Location", $this->baseUrl . $path); $response = $response->withStatus(201); - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_CREATE); } else { $response = $response->withStatus(500); } @@ -585,39 +573,21 @@ private function handleCreateDirectoryRequest(Response $response, string $path): $response = $response->withStatus($success ? 201 : 500); if ($success) { $this->removeLinkFromMetaFileFor($path); - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_CREATE); } } return $response; } - private function sendWebsocketUpdate($path) + private function sendNotificationUpdate($path, $type) { - $pubsub = $this->pubsub; - if (!$pubsub) { - return; // no pubsub server available, don't even try; - } - - $pubsub = str_replace(["https://", "http://"], "ws://", $pubsub); - $baseUrl = $this->baseUrl; + $this->notifications->send($baseUrl . $path, $type); - $client = new Client($pubsub, array( - 'headers' => array( - 'Sec-WebSocket-Protocol' => 'solid-0.1' - ) - )); - - try { - $client->send("pub $baseUrl$path\n"); - - while ($path !== "/") { - $path = $this->parentPath($path); - $client->send("pub $baseUrl$path\n"); - } - } catch (\WebSocket\Exception $exception) { - throw new Exception('Could not write to pub-sup server', 502, $exception); + while ($path !== "/") { + $path = $this->parentPath($path); + $this->notifications->send($baseUrl . $path, self::NOTIFICATION_TYPE_UPDATE); // checkme: delete on a directory triggers update notifications on parents } } @@ -637,7 +607,7 @@ private function handleDeleteRequest(Response $response, string $path, $contents } else { $success = $filesystem->deleteDir($path); if ($success) { - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_DELETE); } $status = $success ? 204 : 500; @@ -645,7 +615,7 @@ private function handleDeleteRequest(Response $response, string $path, $contents } else { $success = $filesystem->delete($path); if ($success) { - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_DELETE); } $status = $success ? 204 : 500; } @@ -673,7 +643,7 @@ private function handleUpdateRequest(Response $response, string $path, string $c $response = $response->withStatus($success ? 201 : 500); if ($success) { $this->removeLinkFromMetaFileFor($path); - $this->sendWebsocketUpdate($path); + $this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_UPDATE); } } diff --git a/src/SolidNotificationsInterface.php b/src/SolidNotificationsInterface.php new file mode 100644 index 0000000..4f402a4 --- /dev/null +++ b/src/SolidNotificationsInterface.php @@ -0,0 +1,7 @@ +