From 38883e8d46b76616d9e84dfed35ba363dc30ee79 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Tue, 20 Jun 2023 23:44:39 +0100
Subject: [PATCH] API Docs: Allowed multi-paragraph descriptions

Added support for mulit-line endpoint descriptions via blank
intermediate lines in php controller method docblocks.

Also tweaks endpoint header design for better flexing and alignment.
---
 app/Api/ApiDocsGenerator.php                  |  9 ++++---
 .../Controllers/PageApiController.php         |  1 -
 .../ContentPermissionApiController.php        |  3 +++
 .../Controllers/ImageGalleryApiController.php |  2 ++
 .../views/api-docs/parts/endpoint.blade.php   | 27 +++++++++++--------
 tests/Api/ApiDocsTest.php                     |  2 +-
 6 files changed, 27 insertions(+), 17 deletions(-)

diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php
index f13842328..3cd33ffa5 100644
--- a/app/Api/ApiDocsGenerator.php
+++ b/app/Api/ApiDocsGenerator.php
@@ -16,8 +16,8 @@ use ReflectionMethod;
 
 class ApiDocsGenerator
 {
-    protected $reflectionClasses = [];
-    protected $controllerClasses = [];
+    protected array $reflectionClasses = [];
+    protected array $controllerClasses = [];
 
     /**
      * Load the docs form the cache if existing
@@ -139,9 +139,10 @@ class ApiDocsGenerator
     protected function parseDescriptionFromMethodComment(string $comment): string
     {
         $matches = [];
-        preg_match_all('/^\s*?\*\s((?![@\s]).*?)$/m', $comment, $matches);
+        preg_match_all('/^\s*?\*\s?($|((?![\/@\s]).*?))$/m', $comment, $matches);
 
-        return implode(' ', $matches[1] ?? []);
+        $text = implode(' ', $matches[1] ?? []);
+        return str_replace('  ', "\n", $text);
     }
 
     /**
diff --git a/app/Entities/Controllers/PageApiController.php b/app/Entities/Controllers/PageApiController.php
index 93b90b4b7..0e8893450 100644
--- a/app/Entities/Controllers/PageApiController.php
+++ b/app/Entities/Controllers/PageApiController.php
@@ -82,7 +82,6 @@ class PageApiController extends ApiController
 
     /**
      * View the details of a single page.
-     *
      * Pages will always have HTML content. They may have markdown content
      * if the markdown editor was used to last update the page.
      *
diff --git a/app/Permissions/ContentPermissionApiController.php b/app/Permissions/ContentPermissionApiController.php
index cd561fdee..23b75db35 100644
--- a/app/Permissions/ContentPermissionApiController.php
+++ b/app/Permissions/ContentPermissionApiController.php
@@ -38,8 +38,10 @@ class ContentPermissionApiController extends ApiController
 
     /**
      * Read the configured content-level permissions for the item of the given type and ID.
+     *
      * 'contentType' should be one of: page, book, chapter, bookshelf.
      * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for.
+     *
      * The permissions shown are those that override the default for just the specified item, they do not show the
      * full evaluated permission for a role, nor do they reflect permissions inherited from other items in the hierarchy.
      * Fallback permission values may be `null` when inheriting is active.
@@ -57,6 +59,7 @@ class ContentPermissionApiController extends ApiController
     /**
      * Update the configured content-level permission overrides for the item of the given type and ID.
      * 'contentType' should be one of: page, book, chapter, bookshelf.
+     *
      * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for.
      * Providing an empty `role_permissions` array will remove any existing configured role permissions,
      * so you may want to fetch existing permissions beforehand if just adding/removing a single item.
diff --git a/app/Uploads/Controllers/ImageGalleryApiController.php b/app/Uploads/Controllers/ImageGalleryApiController.php
index c444ec663..0d35d2905 100644
--- a/app/Uploads/Controllers/ImageGalleryApiController.php
+++ b/app/Uploads/Controllers/ImageGalleryApiController.php
@@ -52,8 +52,10 @@ class ImageGalleryApiController extends ApiController
 
     /**
      * Create a new image in the system.
+     *
      * Since "image" is expected to be a file, this needs to be a 'multipart/form-data' type request.
      * The provided "uploaded_to" should be an existing page ID in the system.
+     *
      * If the "name" parameter is omitted, the filename of the provided image file will be used instead.
      * The "type" parameter should be 'gallery' for page content images, and 'drawio' should only be used
      * when the file is a PNG file with diagrams.net image data embedded within.
diff --git a/resources/views/api-docs/parts/endpoint.blade.php b/resources/views/api-docs/parts/endpoint.blade.php
index 60c478fe5..024a5ecdf 100644
--- a/resources/views/api-docs/parts/endpoint.blade.php
+++ b/resources/views/api-docs/parts/endpoint.blade.php
@@ -1,15 +1,20 @@
-<h6 class="text-uppercase text-muted float right">{{ $endpoint['controller_method_kebab'] }}</h6>
+<div class="flex-container-row items-center gap-m">
+    <span class="api-method text-mono" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
+    <h5 id="{{ $endpoint['name'] }}" class="text-mono pb-xs">
+        @if($endpoint['controller_method_kebab'] === 'list')
+            <a style="color: inherit;" target="_blank" rel="noopener" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
+        @else
+            <span>{{ url($endpoint['uri']) }}</span>
+        @endif
+    </h5>
+    <h6 class="text-uppercase text-muted text-mono ml-auto">{{ $endpoint['controller_method_kebab'] }}</h6>
+</div>
 
-<h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m">
-    <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
-    @if($endpoint['controller_method_kebab'] === 'list')
-        <a style="color: inherit;" target="_blank" rel="noopener" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
-    @else
-        {{ url($endpoint['uri']) }}
-    @endif
-</h5>
-
-<p class="mb-m">{{ $endpoint['description'] ?? '' }}</p>
+<div class="mb-m">
+    @foreach(explode("\n", $endpoint['description'] ?? '') as $descriptionBlock)
+        <p class="mb-xxs">{{ $descriptionBlock }}</p>
+    @endforeach
+</div>
 
 @if($endpoint['body_params'] ?? false)
     <details class="mb-m">
diff --git a/tests/Api/ApiDocsTest.php b/tests/Api/ApiDocsTest.php
index 56b09cfb8..a1603e0ef 100644
--- a/tests/Api/ApiDocsTest.php
+++ b/tests/Api/ApiDocsTest.php
@@ -8,7 +8,7 @@ class ApiDocsTest extends TestCase
 {
     use TestsApi;
 
-    protected $endpoint = '/api/docs';
+    protected string $endpoint = '/api/docs';
 
     public function test_api_endpoint_redirects_to_docs()
     {