X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/5fa728f28a0c4f09649da7b653dd66260e49a596..HEAD:/app/Exports/Controllers/ImportApiController.php diff --git a/app/Exports/Controllers/ImportApiController.php b/app/Exports/Controllers/ImportApiController.php index 682d340b3..cac155c7c 100644 --- a/app/Exports/Controllers/ImportApiController.php +++ b/app/Exports/Controllers/ImportApiController.php @@ -7,12 +7,13 @@ namespace BookStack\Exports\Controllers; use BookStack\Exceptions\ZipImportException; use BookStack\Exceptions\ZipValidationException; use BookStack\Exports\ImportRepo; -use BookStack\Http\Controller; +use BookStack\Http\ApiController; use BookStack\Uploads\AttachmentService; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Response; -class ImportApiController extends Controller +class ImportApiController extends ApiController { public function __construct( protected ImportRepo $imports, @@ -21,101 +22,123 @@ class ImportApiController extends Controller } /** - * List existing imports visible to the user. + * List existing ZIP imports visible to the user. + * Requires permission to import content. */ public function list(): JsonResponse { - $imports = $this->imports->getVisibleImports(); + $query = $this->imports->queryVisible(); - return response()->json([ - 'status' => 'success', - 'imports' => $imports, + return $this->apiListingResponse($query, [ + 'id', 'name', 'size', 'type', 'created_by', 'created_at', 'updated_at' ]); } /** - * Upload, validate and store an import file. + * Start a new import from a ZIP file. + * This does not actually run the import since that is performed via the "run" endpoint. + * This uploads, validates and stores the ZIP file so it's ready to be imported. + * + * This "file" parameter must be a BookStack-compatible ZIP file, and this must be + * sent via a 'multipart/form-data' type request. + * + * Requires permission to import content. */ - public function upload(Request $request): JsonResponse + public function create(Request $request): JsonResponse { - $this->validate($request, [ - 'file' => ['required', ...AttachmentService::getFileValidationRules()] - ]); + $this->validate($request, $this->rules()['create']); $file = $request->file('file'); try { $import = $this->imports->storeFromUpload($file); } catch (ZipValidationException $exception) { - return response()->json([ - 'status' => 'error', - 'message' => 'Validation failed', - 'errors' => $exception->errors, - ], 422); + $message = "ZIP upload failed with the following validation errors: \n" . $this->formatErrors($exception->errors); + return $this->jsonError($message, 422); } - return response()->json([ - 'status' => 'success', - 'import' => $import, - ], 201); + return response()->json($import); } /** - * Show details of a pending import. + * Read details of a pending ZIP import. + * The "details" property contains high-level metadata regarding the ZIP import content, + * and the structure of this will change depending on import "type". + * Requires permission to import content. */ public function read(int $id): JsonResponse { $import = $this->imports->findVisible($id); - return response()->json([ - 'status' => 'success', - 'import' => $import, - 'data' => $import->decodeMetadata(), - ]); + $import->setAttribute('details', $import->decodeMetadata()); + + return response()->json($import); } /** - * Run the import process. + * Run the import process for an uploaded ZIP import. + * The "parent_id" and "parent_type" parameters are required when the import type is "chapter" or "page". + * On success, this endpoint returns the imported item. + * Requires permission to import content. */ - public function create(int $id, Request $request): JsonResponse + public function run(int $id, Request $request): JsonResponse { $import = $this->imports->findVisible($id); $parent = null; + $rules = $this->rules()['run']; if ($import->type === 'page' || $import->type === 'chapter') { - $data = $this->validate($request, [ - 'parent' => ['required', 'string'], - ]); - $parent = $data['parent']; + $rules['parent_type'][] = 'required'; + $rules['parent_id'][] = 'required'; + $data = $this->validate($request, $rules); + $parent = "{$data['parent_type']}:{$data['parent_id']}"; } try { $entity = $this->imports->runImport($import, $parent); } catch (ZipImportException $exception) { - return response()->json([ - 'status' => 'error', - 'message' => 'Import failed', - 'errors' => $exception->errors, - ], 500); + $message = "ZIP import failed with the following errors: \n" . $this->formatErrors($exception->errors); + return $this->jsonError($message); } - return response()->json([ - 'status' => 'success', - 'entity' => $entity, - ]); + return response()->json($entity->withoutRelations()); } /** - * Delete a pending import. + * Delete a pending ZIP import from the system. + * Requires permission to import content. */ - public function delete(int $id): JsonResponse + public function delete(int $id): Response { $import = $this->imports->findVisible($id); $this->imports->deleteImport($import); - return response()->json([ - 'status' => 'success', - 'message' => 'Import deleted successfully', - ]); + return response('', 204); + } + + protected function rules(): array + { + return [ + 'create' => [ + 'file' => ['required', ...AttachmentService::getFileValidationRules()], + ], + 'run' => [ + 'parent_type' => ['string', 'in:book,chapter'], + 'parent_id' => ['int'], + ], + ]; + } + + protected function formatErrors(array $errors): string + { + $parts = []; + foreach ($errors as $key => $error) { + if (is_string($key)) { + $parts[] = "[{$key}] {$error}"; + } else { + $parts[] = $error; + } + } + return implode("\n", $parts); } -} \ No newline at end of file +}