From 5ec45a5dec7801cdfc291dba943a55ba48ac5544 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Fri, 27 Sep 2024 17:19:47 +0200 Subject: [PATCH] Implement all tests, collection, key and value deletion --- bin/paramcli | 56 ++++++++++++++++++++++++++++++++-- src/Daemon.php | 55 ++++++++++++++++++++++++++++++++- src/Storage/Database.php | 17 ++++++++++- tests/DaemonTest.php | 36 ++++++++++++++++++++-- tests/Storage/DatabaseTest.php | 10 +++++- 5 files changed, 167 insertions(+), 7 deletions(-) diff --git a/bin/paramcli b/bin/paramcli index c0473af..19cc31a 100755 --- a/bin/paramcli +++ b/bin/paramcli @@ -85,7 +85,7 @@ function http_get(string $url, array $headers) { return $http->get("http://{$opts->server}/{$url}", $headers); } -function http_post(string $url, array $headers, string $body) { +function http_post(string $url, array $headers, ?string $body = null) { global $opts,$http; return $http->post("http://{$opts->server}/{$url}", $headers, $body); } @@ -119,6 +119,36 @@ function action_show(object $opts) { } ); } +function action_delete(object $opts) { + $collection = array_shift($opts->args); + if (!$collection) { + exit_error("Missing collection. Expected: delete {collection} {keys|ids}*"); + } + $body = json_encode($opts->args); + http_post($collection."/delete", [ 'content-type' => 'application/json' ], $body)->then( + function ($response) { + echo $response->getBody()->getContents(); + }, + function ($error) { + echo $error->getMessage()."\n"; + } + ); +} + +function action_purge(object $opts) { + $collection = array_shift($opts->args); + if (!$collection) { + exit_error("Missing collection. Expected: purge {collection}"); + } + http_post($collection."/delete", [], null)->then( + function ($response) { + echo $response->getBody()->getContents(); + }, + function ($error) { + echo $error->getMessage()."\n"; + } + ); +} function action_set(object $opts) { $collection = array_shift($opts->args); @@ -137,7 +167,17 @@ function action_set(object $opts) { } else { $op['key'] = $key; } - $op['value'] = $opts->json ? json_decode($value) : $value; + if ($opts->json) { + $json = json_decode($value); + if ($json === null && $value !== 'null') { + $op['value'] = $value; + } else { + $op['value'] = $json; + } + } else { + $op['value'] = $value; + } + if ($opts->from || $opts->until) { $vfrom = $opts->from ? [ 'from' => $opts->from->format('Y-m-d H:i:s P') ] : null; $vuntil = $opts->until ? [ 'until' => $opts->until->format('Y-m-d H:i:s P') ] : null; @@ -170,5 +210,17 @@ switch ($action) { case 'set': action_set($opts); break; + + case 'delete': + action_delete($opts); + break; + + case 'purge': + action_purge($opts); + break; + + default: + print_help(); + exit(0); } diff --git a/src/Daemon.php b/src/Daemon.php index 8ebdfb7..4a95814 100644 --- a/src/Daemon.php +++ b/src/Daemon.php @@ -73,6 +73,7 @@ class Daemon $collectionOp = array_shift($paths); switch ($collectionOp) { + case null: if ($request->getMethod() == 'GET') { try { @@ -85,7 +86,7 @@ class Daemon $this->opCollectionSet($request, $collectionName); return Response::json(true); } - throw new \Exception("Invalid request method"); + return Response::json([ 'error' => "Invalid request method" ]); case 'all': try { @@ -94,6 +95,31 @@ class Daemon return Response::json([ 'error' => "No such collection" ])->withStatus(404); } return Response::json($result); + + case 'delete': + if ($request->getMethod() != 'POST') { + return Response::json([ 'error' => "Invalid request method" ]); + } + try { + $ids = json_decode($request->getBody()->getContents(), true); + $this->opCollectionDelete($collectionName, $ids); + } catch (\Exception $e) { + return Response::json([ 'error' => "No such collection" ])->withStatus(404); + } + return Response::json(true); + + case 'purge': + if ($request->getMethod() != 'POST') { + return Response::json([ 'error' => "Invalid request method" ]); + } + try { + $ids = json_decode($request->getBody()->getContents(), true); + $this->opCollectionPurge($collectionName); + } catch (\Exception $e) { + return Response::json([ 'error' => "No such collection" ])->withStatus(404); + } + return Response::json(true); + } return Response::json([ 'error'=>"Not Found" ])->withStatus(404); @@ -124,6 +150,33 @@ class Daemon return $result; } + private function opCollectionDelete(string $collectionName, array $ids): void + { + try { + $collection = $this->db->getCollection($collectionName); + foreach ($ids as $id) { + if (is_int($id)) { + $collection->deleteValue(intval($id)); + } else { + $collection->deleteKey($id); + } + } + } + catch (\Throwable $t) { + throw new \Exception("No such collection"); + } + } + + private function opCollectionPurge(string $collectionName): void + { + try { + $this->db->deleteCollection($collectionName); + } + catch (\Throwable $t) { + throw new \Exception("No such collection"); + } + } + private function opCollectionSet(ServerRequestInterface $request, string $collectionName): void { diff --git a/src/Storage/Database.php b/src/Storage/Database.php index 504e6dd..8eca121 100644 --- a/src/Storage/Database.php +++ b/src/Storage/Database.php @@ -85,7 +85,22 @@ class Database public function deleteCollection(string $name): void { - + $collectionQuery = $this->pdo->prepare("SELECT * FROM collections WHERE name=:name"); + $collectionQuery->execute([ 'name' => $name ]); + if (!($collection = $collectionQuery->fetch(PDO::FETCH_ASSOC))) { + return; + } + $collectionId = $collection['id']; + $keyQuery = $this->pdo->prepare("SELECT * FROM keys WHERE collection_id=:cid"); + $keyQuery->execute([ 'cid' => $collectionId ]); + while ($key = $keyQuery->fetch(PDO::FETCH_ASSOC)) { + $this->pdo->prepare("DELETE FROM vals WHERE key_id=:kid") + ->execute([ "kid" => $key['id'] ]); + $this->pdo->prepare("DELETE FROM keys WHERE id=:kid") + ->execute([ "kid" => $key['id'] ]); + } + $this->pdo->prepare("DELETE FROM collections WHERE id=:cid") + ->execute([ "cid" => $collectionId ]); } } diff --git a/tests/DaemonTest.php b/tests/DaemonTest.php index 32fbf09..5574fcd 100644 --- a/tests/DaemonTest.php +++ b/tests/DaemonTest.php @@ -75,12 +75,44 @@ class DaemonTest extends \PHPUnit\Framework\TestCase public function testHandlingDeleteRequest() { - $this->markTestSkipped(); + $body = json_encode([ 'first' ]); + $request = new ServerRequest("POST", "/test/delete", [ "content-type"=>"application/json" ], $body); + $response = $this->daemon->onRequest($request); + $this->assertEquals(200, $response->getStatusCode()); + + $request = new ServerRequest("GET", "/test"); + $response = $this->daemon->onRequest($request); + $this->assertEquals(200, $response->getStatusCode()); + $resolved = json_decode($response->getBody()->getContents(), true); + $expect = [ + 'second' => 'c', + ]; + $this->assertEquals($expect, $resolved); + + $body = json_encode([ 5, 6, 7, 8 ]); + $request = new ServerRequest("POST", "/test/delete", [ "content-type"=>"application/json" ], $body); + $response = $this->daemon->onRequest($request); + $this->assertEquals(200, $response->getStatusCode()); + + $request = new ServerRequest("GET", "/test"); + $response = $this->daemon->onRequest($request); + $this->assertEquals(200, $response->getStatusCode()); + $resolved = json_decode($response->getBody()->getContents(), true); + $expect = [ + ]; + $this->assertEquals($expect, $resolved); + } public function testHandlingPurgeRequest() { - $this->markTestSkipped(); + $request = new ServerRequest("POST", "/test/purge"); + $response = $this->daemon->onRequest($request); + $this->assertEquals(200, $response->getStatusCode()); + + $request = new ServerRequest("GET", "/test"); + $response = $this->daemon->onRequest($request); + $this->assertEquals(404, $response->getStatusCode()); } private function insertTestData(Collection $collection, array $data) diff --git a/tests/Storage/DatabaseTest.php b/tests/Storage/DatabaseTest.php index e9974cf..995942d 100644 --- a/tests/Storage/DatabaseTest.php +++ b/tests/Storage/DatabaseTest.php @@ -34,7 +34,15 @@ class DatabaseTest extends \PHPUnit\Framework\TestCase public function testPurgingCollection() { - $this->markTestSkipped(); + $database = new Database(":memory:"); + + $collection = $database->createCollection("test"); + + $database->deleteCollection("test"); + + $this->expectException(\Exception::class); + + $collection = $database->getCollection("test"); } public function testExceptionsIfCollectionNotExists()