Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"autoload": {
"psr-4": {
"Pdsinterop\\Solid\\Resources\\": "src/"
"Pdsinterop\\Solid\\Resources\\": "src/",
"Pdsinterop\\Solid\\SolidNotifications\\": "src/"
}
},
"autoload-dev": {
Expand Down
72 changes: 21 additions & 51 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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',
Expand All @@ -55,8 +59,8 @@ class Server
private $filesystem;
/** @var Graph */
private $graph;
/** @var string */
private $pubsub;
/** @var SolidNotificationsInterface */
private $notifications;
/** @var Response */
private $response;

Expand Down Expand Up @@ -85,19 +89,17 @@ 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
final public function __construct(Filesystem $filesystem, Response $response, Graph $graph = null)
{
$this->basePath = '';
$this->baseUrl = '';
$this->pubsub = '';
$this->filesystem = $filesystem;
$this->graph = $graph ?? new Graph();
$this->response = $response;
Expand Down Expand Up @@ -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);
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
}
}

Expand All @@ -637,15 +607,15 @@ 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;
}
} else {
$success = $filesystem->delete($path);
if ($success) {
$this->sendWebsocketUpdate($path);
$this->sendNotificationUpdate($path, self::NOTIFICATION_TYPE_DELETE);
}
$status = $success ? 204 : 500;
}
Expand Down Expand Up @@ -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);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/SolidNotificationsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
namespace Pdsinterop\Solid\SolidNotifications;

interface SolidNotificationsInterface
{
public function send($path, $type);
}