From 14bccae6bd93164c6fc647747654c0fccd8dffe7 Mon Sep 17 00:00:00 2001 From: julesdevops Date: Sun, 24 Apr 2022 10:16:45 +0200 Subject: [PATCH] do some cleanup and add doc --- .../Api/RecycleBinApiController.php | 38 +++++++++++++----- dev/api/responses/recycle_bin-destroy.json | 3 ++ dev/api/responses/recycle_bin-list.json | 34 ++++++++++++++++ dev/api/responses/recycle_bin-restore.json | 3 ++ tests/Api/RecycleBinApiTest.php | 40 +++++++++---------- 5 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 dev/api/responses/recycle_bin-destroy.json create mode 100644 dev/api/responses/recycle_bin-list.json create mode 100644 dev/api/responses/recycle_bin-restore.json diff --git a/app/Http/Controllers/Api/RecycleBinApiController.php b/app/Http/Controllers/Api/RecycleBinApiController.php index f60115c16..a2176a2c8 100644 --- a/app/Http/Controllers/Api/RecycleBinApiController.php +++ b/app/Http/Controllers/Api/RecycleBinApiController.php @@ -2,14 +2,16 @@ namespace BookStack\Http\Controllers\Api; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Deletion; use BookStack\Entities\Repos\DeletionRepo; use Closure; class RecycleBinApiController extends ApiController { + protected $fieldsToExpose = [ + 'id', 'deleted_by', 'created_at', 'updated_at', 'deletable_type', 'deletable_id', + ]; + public function __construct() { $this->middleware(function ($request, $next) { @@ -20,9 +22,13 @@ class RecycleBinApiController extends ApiController }); } + /** + * Get a top-level listing of the items in the recycle bin. + * Requires the permission to manage settings and restrictions. + */ public function list() { - return $this->apiListingResponse(Deletion::query(), [ + return $this->apiListingResponse(Deletion::query()->with('deletable'), [ 'id', 'deleted_by', 'created_at', @@ -32,6 +38,10 @@ class RecycleBinApiController extends ApiController ], [Closure::fromCallable([$this, 'listFormatter'])]); } + /** + * Restore a single deletion from the recycle bin. + * You must provide the deletion id, not the id of the corresponding deleted item. + */ public function restore(DeletionRepo $deletionRepo, string $id) { $restoreCount = $deletionRepo->restore((int) $id); @@ -39,6 +49,11 @@ class RecycleBinApiController extends ApiController return response()->json(['restore_count' => $restoreCount]); } + /** + * Remove a single deletion from the recycle bin. + * Use this endpoint carefully as it will entirely remove the underlying deleted items from the system. + * You must provide the deletion id, not the id of the corresponding deleted item. + */ public function destroy(DeletionRepo $deletionRepo, string $id) { $deleteCount = $deletionRepo->destroy((int) $id); @@ -48,23 +63,26 @@ class RecycleBinApiController extends ApiController protected function listFormatter(Deletion $deletion) { + $deletion->makeVisible($this->fieldsToExpose); + $deletion->makeHidden('deletable'); + $deletable = $deletion->deletable; - $isBook = $deletable instanceof Book; + $isBook = $deletion->deletable_type === "BookStack\Book"; $parent = null; $children = null; if ($isBook) { - $chapterCount = $deletable->chapters()->withTrashed()->count(); - $children['Bookstack\Chapter'] = $chapterCount; + $chapterCount = $deletable->chapters()->withTrashed()->count(); + $children['BookStack\Chapter'] = $chapterCount; } - if ($isBook || $deletion->deletable instanceof Chapter) { - $pageCount = $deletable->pages()->withTrashed()->count(); - $children['Bookstack\Page'] = $pageCount; + if ($isBook || $deletion->deletable_type === "BookStack\Chapter") { + $pageCount = $deletable->pages()->withTrashed()->count(); + $children['BookStack\Page'] = $pageCount; } $parentEntity = $deletable->getParent(); - $parent = []; + $parent = null; if ($parentEntity) { $parent['type'] = $parentEntity->getMorphClass(); diff --git a/dev/api/responses/recycle_bin-destroy.json b/dev/api/responses/recycle_bin-destroy.json new file mode 100644 index 000000000..21cfc401b --- /dev/null +++ b/dev/api/responses/recycle_bin-destroy.json @@ -0,0 +1,3 @@ +{ + "delete_count": 2 +} \ No newline at end of file diff --git a/dev/api/responses/recycle_bin-list.json b/dev/api/responses/recycle_bin-list.json new file mode 100644 index 000000000..025cc9825 --- /dev/null +++ b/dev/api/responses/recycle_bin-list.json @@ -0,0 +1,34 @@ +{ + "data": [ + { + "id": 25, + "deleted_by": 1, + "created_at": "2022-04-24T07:59:34.000000Z", + "updated_at": "2022-04-24T07:59:34.000000Z", + "deletable_type": "BookStack\\Book", + "deletable_id": 4, + "parent": { + "type": "BookStack\\Book", + "id": 25 + }, + "children": { + "BookStack\\Chapter": 0, + "BookStack\\Page": 1 + } + }, + { + "id": 26, + "deleted_by": 1, + "created_at": "2022-04-24T07:59:35.000000Z", + "updated_at": "2022-04-24T07:59:35.000000Z", + "deletable_type": "BookStack\\Book", + "deletable_id": 3, + "parent": [], + "children": { + "BookStack\\Chapter": 1, + "BookStack\\Page": 1 + } + } + ], + "total": 2 +} \ No newline at end of file diff --git a/dev/api/responses/recycle_bin-restore.json b/dev/api/responses/recycle_bin-restore.json new file mode 100644 index 000000000..ac5c94808 --- /dev/null +++ b/dev/api/responses/recycle_bin-restore.json @@ -0,0 +1,3 @@ +{ + "restore_count": 2 +} \ No newline at end of file diff --git a/tests/Api/RecycleBinApiTest.php b/tests/Api/RecycleBinApiTest.php index 286227896..4849080b9 100644 --- a/tests/Api/RecycleBinApiTest.php +++ b/tests/Api/RecycleBinApiTest.php @@ -33,12 +33,12 @@ class RecycleBinApiTest extends TestCase } } - public function test_restrictions_manage_all_permission_neeed_for_all_endpoints() + public function test_restrictions_manage_all_permission_needed_for_all_endpoints() { $editor = $this->getEditor(); $this->giveUserPermissions($editor, ['restrictions-manage-all']); $this->actingAs($editor); - + foreach ($this->endpointMap as [$method, $uri]) { $resp = $this->json($method, $uri); $resp->assertStatus(403); @@ -74,7 +74,7 @@ class RecycleBinApiTest extends TestCase }); $resp->assertJson([ - 'data' => $expectedData->values()->all(), + 'data' => $expectedData->values()->all(), 'total' => 2, ]); } @@ -82,7 +82,7 @@ class RecycleBinApiTest extends TestCase public function test_index_endpoint_returns_children() { $this->actingAsAuthorizedUser(); - + $book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first(); $editor = $this->getEditor(); $this->actingAs($editor)->delete($book->getUrl()); @@ -100,15 +100,15 @@ class RecycleBinApiTest extends TestCase 'deletable_type' => $book->getMorphClass(), 'deletable_id' => $book->getKey(), 'children' => [ - 'Bookstack\Page' => $book->pages_count, - 'Bookstack\Chapter' => $book->chapters_count, + 'BookStack\Page' => $book->pages_count, + 'BookStack\Chapter' => $book->chapters_count, ], 'parent' => null, - ] + ], ]; $resp->assertJson([ - 'data' => $expectedData, + 'data' => $expectedData, 'total' => 1, ]); } @@ -136,22 +136,22 @@ class RecycleBinApiTest extends TestCase 'deletable_id' => $page->getKey(), 'parent' => [ 'type' => 'BookStack\Chapter', - 'id' => $page->chapter->getKey() + 'id' => $page->chapter->getKey(), ], 'children' => null, - ] + ], ]; $resp->assertJson([ - 'data' => $expectedData, - 'total' => 1 + 'data' => $expectedData, + 'total' => 1, ]); } public function test_restore_endpoint() { $this->actingAsAuthorizedUser(); - + $page = Page::query()->first(); $editor = $this->getEditor(); $this->actingAs($editor)->delete($page->getUrl()); @@ -160,22 +160,22 @@ class RecycleBinApiTest extends TestCase $deletion = Deletion::query()->orderBy('id')->first(); $this->assertDatabaseHas('pages', [ - 'id' => $page->getKey(), - 'deleted_at' => $page->deleted_at, + 'id' => $page->getKey(), + 'deleted_at' => $page->deleted_at, ]); $this->putJson($this->baseEndpoint . '/' . $deletion->getKey()); $this->assertDatabaseHas('pages', [ - 'id' => $page->getKey(), - 'deleted_at' => null, + 'id' => $page->getKey(), + 'deleted_at' => null, ]); } public function test_destroy_endpoint() { $this->actingAsAuthorizedUser(); - + $page = Page::query()->first(); $editor = $this->getEditor(); $this->actingAs($editor)->delete($page->getUrl()); @@ -184,8 +184,8 @@ class RecycleBinApiTest extends TestCase $deletion = Deletion::query()->orderBy('id')->first(); $this->assertDatabaseHas('pages', [ - 'id' => $page->getKey(), - 'deleted_at' => $page->deleted_at, + 'id' => $page->getKey(), + 'deleted_at' => $page->deleted_at, ]); $this->deleteJson($this->baseEndpoint . '/' . $deletion->getKey());