Added a view for the API docs

This commit is contained in:
Dan Brown 2020-01-15 20:18:02 +00:00
parent bed2498667
commit 45b5e631e2
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
9 changed files with 133 additions and 13 deletions

View File

@ -106,7 +106,7 @@ class ApiDocsGenerator
return strpos($route->uri, 'api/') === 0; return strpos($route->uri, 'api/') === 0;
})->map(function ($route) { })->map(function ($route) {
[$controller, $controllerMethod] = explode('@', $route->action['uses']); [$controller, $controllerMethod] = explode('@', $route->action['uses']);
$baseModelName = explode('/', $route->uri)[1]; $baseModelName = explode('.', explode('/', $route->uri)[1])[0];
$shortName = $baseModelName . '-' . $controllerMethod; $shortName = $baseModelName . '-' . $controllerMethod;
return [ return [
'name' => $shortName, 'name' => $shortName,

View File

@ -13,9 +13,9 @@ class ApiDocsController extends ApiController
public function display() public function display()
{ {
$docs = $this->getDocs(); $docs = $this->getDocs();
dd($docs); return view('api-docs.index', [
// TODO - Build view for API docs 'docs' => $docs,
return view(''); ]);
} }
/** /**

View File

@ -2,8 +2,11 @@
use BookStack\Entities\Book; use BookStack\Entities\Book;
use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Repos\BookRepo;
use BookStack\Exceptions\NotifyException;
use BookStack\Facades\Activity; use BookStack\Facades\Activity;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
class BooksApiController extends ApiController class BooksApiController extends ApiController
{ {
@ -41,8 +44,8 @@ class BooksApiController extends ApiController
} }
/** /**
* Create a new book. * Create a new book in the system.
* @throws \Illuminate\Validation\ValidationException * @throws ValidationException
*/ */
public function create(Request $request) public function create(Request $request)
{ {
@ -66,7 +69,7 @@ class BooksApiController extends ApiController
/** /**
* Update the details of a single book. * Update the details of a single book.
* @throws \Illuminate\Validation\ValidationException * @throws ValidationException
*/ */
public function update(Request $request, string $id) public function update(Request $request, string $id)
{ {
@ -81,9 +84,9 @@ class BooksApiController extends ApiController
} }
/** /**
* Delete a book from the system. * Delete a single book from the system.
* @throws \BookStack\Exceptions\NotifyException * @throws NotifyException
* @throws \Illuminate\Contracts\Container\BindingResolutionException * @throws BindingResolutionException
*/ */
public function delete(string $id) public function delete(string $id)
{ {

View File

@ -0,0 +1,18 @@
import Code from "../services/code"
class DetailsHighlighter {
constructor(elem) {
this.elem = elem;
this.dealtWith = false;
elem.addEventListener('toggle', this.onToggle.bind(this));
}
onToggle() {
if (this.dealtWith) return;
Code.highlightWithin(this.elem);
this.dealtWith = true;
}
}
export default DetailsHighlighter;

View File

@ -30,6 +30,7 @@ import settingColorPicker from "./setting-color-picker";
import entityPermissionsEditor from "./entity-permissions-editor"; import entityPermissionsEditor from "./entity-permissions-editor";
import templateManager from "./template-manager"; import templateManager from "./template-manager";
import newUserPassword from "./new-user-password"; import newUserPassword from "./new-user-password";
import detailsHighlighter from "./details-highlighter";
const componentMapping = { const componentMapping = {
'dropdown': dropdown, 'dropdown': dropdown,
@ -64,6 +65,7 @@ const componentMapping = {
'entity-permissions-editor': entityPermissionsEditor, 'entity-permissions-editor': entityPermissionsEditor,
'template-manager': templateManager, 'template-manager': templateManager,
'new-user-password': newUserPassword, 'new-user-password': newUserPassword,
'details-highlighter': detailsHighlighter,
}; };
window.components = {}; window.components = {};

View File

@ -87,9 +87,20 @@ const modeMap = {
* Highlight pre elements on a page * Highlight pre elements on a page
*/ */
function highlight() { function highlight() {
let codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre'); const codeBlocks = document.querySelectorAll('.page-content pre, .comment-box .content pre');
for (let i = 0; i < codeBlocks.length; i++) { for (const codeBlock of codeBlocks) {
highlightElem(codeBlocks[i]); highlightElem(codeBlock);
}
}
/**
* Highlight all code blocks within the given parent element
* @param {HTMLElement} parent
*/
function highlightWithin(parent) {
const codeBlocks = parent.querySelectorAll('pre');
for (const codeBlock of codeBlocks) {
highlightElem(codeBlock);
} }
} }
@ -308,6 +319,7 @@ function getMetaKey() {
export default { export default {
highlight: highlight, highlight: highlight,
highlightWithin: highlightWithin,
wysiwygView: wysiwygView, wysiwygView: wysiwygView,
popupEditor: popupEditor, popupEditor: popupEditor,
setMode: setMode, setMode: setMode,

View File

@ -236,4 +236,26 @@
.tag-list div:last-child .tag-item { .tag-list div:last-child .tag-item {
margin-bottom: 0; margin-bottom: 0;
}
/**
* API Docs
*/
.api-method {
font-size: 0.75rem;
background-color: #888;
padding: $-xs;
line-height: 1.3;
opacity: 0.7;
vertical-align: top;
border-radius: 3px;
color: #FFF;
display: inline-block;
min-width: 60px;
text-align: center;
font-weight: bold;
&[data-method="GET"] { background-color: #077b70 }
&[data-method="POST"] { background-color: #cf4d03 }
&[data-method="PUT"] { background-color: #0288D1 }
&[data-method="DELETE"] { background-color: #ab0f0e }
} }

View File

@ -213,6 +213,18 @@ blockquote {
} }
} }
.text-mono {
font-family: $mono;
}
.text-uppercase {
text-transform: uppercase;
}
.text-capitals {
text-transform: capitalize;
}
.code-base { .code-base {
background-color: #F8F8F8; background-color: #F8F8F8;
font-size: 0.80em; font-size: 0.80em;

View File

@ -0,0 +1,51 @@
@extends('simple-layout')
@section('body')
<div class="container pt-xl">
<div class="grid right-focus reverse-collapse">
<div>
@foreach($docs as $model => $endpoints)
<p class="text-uppercase text-muted mb-xm mt-l"><strong>{{ $model }}</strong></p>
@foreach($endpoints as $endpoint)
<div class="mb-xs">
<a href="#{{ $endpoint['name'] }}" class="text-mono">
<span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
/{{ $endpoint['uri'] }}
</a>
</div>
@endforeach
@endforeach
</div>
<div>
@foreach($docs as $model => $endpoints)
<section class="card content-wrap auto-height">
<h1 class="list-heading text-capitals">{{ $model }}</h1>
@foreach($endpoints as $endpoint)
<h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m">
<span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
{{ url($endpoint['uri']) }}
</h5>
<p class="mb-m">{{ $endpoint['description'] ?? '' }}</p>
@if($endpoint['example_response'] ?? false)
<details details-highlighter>
<summary class="text-muted">Example Response</summary>
<pre><code class="language-json">{{ $endpoint['example_response'] }}</code></pre>
</details>
<hr class="mt-m">
@endif
@endforeach
</section>
@endforeach
</div>
</div>
</div>
@stop