From 7b8fe5fbc69a6f49c2acff938f1da48c28e19ecd Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Fri, 10 Apr 2020 16:05:17 +0100 Subject: [PATCH] Added book-export endpoints to the API --- app/Api/ApiDocsGenerator.php | 2 + .../Api/BooksExportApiController.php | 55 +++++++++++++++++++ resources/views/api-docs/index.blade.php | 4 +- routes/api.php | 4 ++ tests/Api/BooksApiTest.php | 32 +++++++++++ 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/Api/BooksExportApiController.php diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php index a0c45608a..ddba24bdb 100644 --- a/app/Api/ApiDocsGenerator.php +++ b/app/Api/ApiDocsGenerator.php @@ -3,6 +3,7 @@ use BookStack\Http\Controllers\Api\ApiController; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Str; use ReflectionClass; use ReflectionException; use ReflectionMethod; @@ -117,6 +118,7 @@ class ApiDocsGenerator 'method' => $route->methods[0], 'controller' => $controller, 'controller_method' => $controllerMethod, + 'controller_method_kebab' => Str::kebab($controllerMethod), 'base_model' => $baseModelName, ]; }); diff --git a/app/Http/Controllers/Api/BooksExportApiController.php b/app/Http/Controllers/Api/BooksExportApiController.php new file mode 100644 index 000000000..605f8f408 --- /dev/null +++ b/app/Http/Controllers/Api/BooksExportApiController.php @@ -0,0 +1,55 @@ +<?php namespace BookStack\Http\Controllers\Api; + +use BookStack\Entities\Book; +use BookStack\Entities\ExportService; +use BookStack\Entities\Repos\BookRepo; +use Throwable; + +class BooksExportApiController extends ApiController +{ + + protected $bookRepo; + protected $exportService; + + /** + * BookExportController constructor. + */ + public function __construct(BookRepo $bookRepo, ExportService $exportService) + { + $this->bookRepo = $bookRepo; + $this->exportService = $exportService; + parent::__construct(); + } + + /** + * Export a book as a PDF file. + * @throws Throwable + */ + public function exportPdf(int $id) + { + $book = Book::visible()->findOrFail($id); + $pdfContent = $this->exportService->bookToPdf($book); + return $this->downloadResponse($pdfContent, $book->slug . '.pdf'); + } + + /** + * Export a book as a contained HTML file. + * @throws Throwable + */ + public function exportHtml(int $id) + { + $book = Book::visible()->findOrFail($id); + $htmlContent = $this->exportService->bookToContainedHtml($book); + return $this->downloadResponse($htmlContent, $book->slug . '.html'); + } + + /** + * Export a book as a plain text file. + */ + public function exportPlainText(int $id) + { + $book = Book::visible()->findOrFail($id); + $textContent = $this->exportService->bookToPlainText($book); + return $this->downloadResponse($textContent, $book->slug . '.txt'); + } +} diff --git a/resources/views/api-docs/index.blade.php b/resources/views/api-docs/index.blade.php index ab4db89e8..e92b505cf 100644 --- a/resources/views/api-docs/index.blade.php +++ b/resources/views/api-docs/index.blade.php @@ -26,7 +26,7 @@ <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span> </a> <a href="#{{ $endpoint['name'] }}" class="text-mono"> - {{ $endpoint['controller_method'] }} + {{ $endpoint['controller_method_kebab'] }} </a> </div> @endforeach @@ -186,7 +186,7 @@ <h1 class="list-heading text-capitals">{{ $model }}</h1> @foreach($endpoints as $endpoint) - <h6 class="text-uppercase text-muted float right">{{ $endpoint['controller_method'] }}</h6> + <h6 class="text-uppercase text-muted float right">{{ $endpoint['controller_method_kebab'] }}</h6> <h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m"> <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span> {{ url($endpoint['uri']) }} diff --git a/routes/api.php b/routes/api.php index 7ca5e66fc..f9c27b62f 100644 --- a/routes/api.php +++ b/routes/api.php @@ -15,6 +15,10 @@ Route::get('books/{id}', 'BooksApiController@read'); Route::put('books/{id}', 'BooksApiController@update'); Route::delete('books/{id}', 'BooksApiController@delete'); +Route::get('books/{id}/export/html', 'BooksExportApiController@exportHtml'); +Route::get('books/{id}/export/pdf', 'BooksExportApiController@exportPdf'); +Route::get('books/{id}/export/plaintext', 'BooksExportApiController@exportPlainText'); + Route::get('shelves', 'BookshelfApiController@list'); Route::post('shelves', 'BookshelfApiController@create'); Route::get('shelves/{id}', 'BookshelfApiController@read'); diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php index 6f8753fb0..3fd763ec6 100644 --- a/tests/Api/BooksApiTest.php +++ b/tests/Api/BooksApiTest.php @@ -105,4 +105,36 @@ class BooksApiTest extends TestCase $resp->assertStatus(204); $this->assertActivityExists('book_delete'); } + + public function test_export_html_endpoint() + { + $this->actingAsApiEditor(); + $book = Book::visible()->first(); + + $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/html"); + $resp->assertStatus(200); + $resp->assertSee($book->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"'); + } + + public function test_export_plain_text_endpoint() + { + $this->actingAsApiEditor(); + $book = Book::visible()->first(); + + $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/plaintext"); + $resp->assertStatus(200); + $resp->assertSee($book->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"'); + } + + public function test_export_pdf_endpoint() + { + $this->actingAsApiEditor(); + $book = Book::visible()->first(); + + $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/pdf"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"'); + } } \ No newline at end of file