Book API: Added shelves list to show endpoint

For #6006
Added test to cover.
This commit is contained in:
Dan Brown
2026-02-24 10:25:17 +00:00
parent 6808292c90
commit 9a12e3a8b7
3 changed files with 45 additions and 3 deletions

View File

@@ -7,11 +7,14 @@ use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Queries\BookshelfQueries;
use BookStack\Entities\Queries\PageQueries;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
use BookStack\Http\ApiController;
use BookStack\Permissions\Permission;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
@@ -21,6 +24,7 @@ class BookApiController extends ApiController
protected BookRepo $bookRepo,
protected BookQueries $queries,
protected PageQueries $pageQueries,
protected BookshelfQueries $shelfQueries,
) {
}
@@ -60,13 +64,20 @@ class BookApiController extends ApiController
* View the details of a single book.
* The response data will contain a 'content' property listing the chapter and pages directly within, in
* the same structure as you'd see within the BookStack interface when viewing a book. Top-level
* contents will have a 'type' property to distinguish between pages & chapters.
* contents will have a 'type' property to distinguish between pages and chapters.
*/
public function read(string $id)
{
$book = $this->queries->findVisibleByIdOrFail(intval($id));
$book = $this->forJsonDisplay($book);
$book->load(['createdBy', 'updatedBy', 'ownedBy']);
$book->load([
'createdBy',
'updatedBy',
'ownedBy',
'shelves' => function (BelongsToMany $query) {
$query->select(['id', 'name', 'slug'])->scopes('visible');
}
]);
$contents = (new BookContents($book))->getTree(true, false)->all();
$contentsApiData = (new ApiEntityListFormatter($contents))

View File

@@ -19,7 +19,7 @@ class Bookshelf extends Entity implements HasDescriptionInterface, HasCoverInter
public float $searchFactor = 1.2;
protected $hidden = ['image_id', 'deleted_at', 'description_html', 'priority', 'default_template_id', 'sort_rule_id', 'entity_id', 'entity_type', 'chapter_id', 'book_id'];
protected $hidden = ['pivot', 'image_id', 'deleted_at', 'description_html', 'priority', 'default_template_id', 'sort_rule_id', 'entity_id', 'entity_type', 'chapter_id', 'book_id'];
protected $fillable = ['name'];
/**

View File

@@ -188,6 +188,37 @@ class BooksApiTest extends TestCase
$resp->assertJsonMissing(['name' => $customName]);
}
public function test_read_endpoint_lists_visible_shelves_the_book_is_assigned_to()
{
$this->actingAsApiEditor();
$shelf = $this->entities->shelf();
$otherShelf = $this->entities->shelf();
$book = $this->entities->book();
$book->shelves()->detach();
$book->shelves()->attach($shelf);
$book->shelves()->attach($otherShelf);
$this->assertEquals(2, $book->shelves()->count());
$this->permissions->disableEntityInheritedPermissions($otherShelf);
$resp = $this->getJson("{$this->baseEndpoint}/{$book->id}");
$resp->assertOk();
$resp->assertJsonCount(1, 'shelves');
$resp->assertJson([
'shelves' => [
[
'id' => $shelf->id,
'name' => $shelf->name,
'slug' => $shelf->slug,
]
]
]);
$resp->assertJsonMissingPath('shelves.0.description');
$resp->assertJsonMissingPath('shelves.0.pivot');
}
public function test_update_endpoint()
{
$this->actingAsApiEditor();