]> BookStack Code Mirror - bookstack/commitdiff
Added "Getting Started" API docs
authorDan Brown <redacted>
Sat, 18 Jan 2020 14:03:11 +0000 (14:03 +0000)
committerDan Brown <redacted>
Sat, 18 Jan 2020 14:03:11 +0000 (14:03 +0000)
app/Http/Controllers/Api/BooksApiController.php
dev/api/responses/books-list.json [moved from dev/api/responses/books-index.json with 100% similarity]
resources/js/components/code-highlighter.js [new file with mode: 0644]
resources/js/components/index.js
resources/lang/en/settings.php
resources/views/api-docs/index.blade.php
resources/views/users/api-tokens/list.blade.php [new file with mode: 0644]
resources/views/users/edit.blade.php
routes/api.php

index fa174dfd346c1f8e00e17b9cbef0e45b627cfb58..ac4ea171c7e9633606011f907b2078b52ffe3d86 100644 (file)
@@ -35,7 +35,7 @@ class BooksApiController extends ApiController
     /**
      * Get a listing of books visible to the user.
      */
     /**
      * Get a listing of books visible to the user.
      */
-    public function index()
+    public function list()
     {
         $books = Book::visible();
         return $this->apiListingResponse($books, [
     {
         $books = Book::visible();
         return $this->apiListingResponse($books, [
diff --git a/resources/js/components/code-highlighter.js b/resources/js/components/code-highlighter.js
new file mode 100644 (file)
index 0000000..db61128
--- /dev/null
@@ -0,0 +1,10 @@
+import Code from "../services/code"
+class CodeHighlighter {
+
+    constructor(elem) {
+        Code.highlightWithin(elem);
+    }
+
+}
+
+export default CodeHighlighter;
\ No newline at end of file
index d3ba539dd36814aaf30ee00d17e0dcfdeba872b9..11282733090645850ad2f4c487ca21c6955a3d25 100644 (file)
@@ -31,6 +31,7 @@ import entityPermissionsEditor from "./entity-permissions-editor";
 import templateManager from "./template-manager";
 import newUserPassword from "./new-user-password";
 import detailsHighlighter from "./details-highlighter";
 import templateManager from "./template-manager";
 import newUserPassword from "./new-user-password";
 import detailsHighlighter from "./details-highlighter";
+import codeHighlighter from "./code-highlighter";
 
 const componentMapping = {
     'dropdown': dropdown,
 
 const componentMapping = {
     'dropdown': dropdown,
@@ -66,6 +67,7 @@ const componentMapping = {
     'template-manager': templateManager,
     'new-user-password': newUserPassword,
     'details-highlighter': detailsHighlighter,
     'template-manager': templateManager,
     'new-user-password': newUserPassword,
     'details-highlighter': detailsHighlighter,
+    'code-highlighter': codeHighlighter,
 };
 
 window.components = {};
 };
 
 window.components = {};
index b1da5435fdbba0bf33929258635f9e56829d45fb..fb00c3cce45b1318ef03659731a34a7139083623 100755 (executable)
@@ -156,6 +156,7 @@ return [
     'users_api_tokens_none' => 'No API tokens have been created for this user',
     'users_api_tokens_create' => 'Create Token',
     'users_api_tokens_expires' => 'Expires',
     'users_api_tokens_none' => 'No API tokens have been created for this user',
     'users_api_tokens_create' => 'Create Token',
     'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
 
     // API Tokens
     'user_api_token_create' => 'Create API Token',
 
     // API Tokens
     'user_api_token_create' => 'Create API Token',
index a20ba04ccb2e3449701dbee59a8afe48b360047c..e9583838c4ce3f487e92d80306e3ba824a0d8c00 100644 (file)
@@ -5,8 +5,17 @@
     <div class="container pt-xl">
 
         <div class="grid right-focus reverse-collapse">
     <div class="container pt-xl">
 
         <div class="grid right-focus reverse-collapse">
-
             <div>
             <div>
+
+                <p class="text-uppercase text-muted mb-xm mt-l"><strong>Getting Started</strong></p>
+
+                <div class="text-mono">
+                    <div class="mb-xs"><a href="#authentication">Authentication</a></div>
+                    <div class="mb-xs"><a href="#request-format">Request Format</a></div>
+                    <div class="mb-xs"><a href="#listing-endpoints">Listing Endpoints</a></div>
+                    <div class="mb-xs"><a href="#error-handling">Error Handling</a></div>
+                </div>
+
                 @foreach($docs as $model => $endpoints)
                     <p class="text-uppercase text-muted mb-xm mt-l"><strong>{{ $model }}</strong></p>
 
                 @foreach($docs as $model => $endpoints)
                     <p class="text-uppercase text-muted mb-xm mt-l"><strong>{{ $model }}</strong></p>
 
             </div>
 
             <div style="overflow: auto;">
             </div>
 
             <div style="overflow: auto;">
+
+                <section code-highlighter class="card content-wrap auto-height">
+                    <h1 class="list-heading text-capitals mb-l">Getting Started</h1>
+
+                    <h5 id="authentication" class="text-mono mb-m">Authentication</h5>
+                    <p>
+                        To access the API a user has to have the <em>"Access System API"</em> permission enabled on one of their assigned roles.
+                        Permissions to content accessed via the API is limited by the roles & permissions assigned to the user that's used to access the API.
+                    </p>
+                    <p>Authentication to use the API is primarily done using API Tokens. Once the <em>"Access System API"</em> permission has been assigned to a user, a "API Tokens" section should be visible when editing their user profile. Choose "Create Token" and enter an appropriate name and expiry date, relevant for your API usage then press "Save". A "Token ID" and "Token Secret" will be immediately displayed. These values should be used as a header in API HTTP requests in the following format:</p>
+                    <pre><code class="language-css">Authorization: Token &lt;token_id&gt;:&lt;token_secret&gt;</code></pre>
+                    <p>Here's an example of an authorized cURL request to list books in the system:</p>
+                    <pre><code class="language-shell">curl --request GET \
+  --url https://example.com/api/books \
+  --header 'Authorization: Token C6mdvEQTGnebsmVn3sFNeeuelGEBjyQp:NOvD3VlzuSVuBPNaf1xWHmy7nIRlaj22'</code></pre>
+                    <p>If already logged into the system within the browser, via a user account with permission to access the API, the system will also accept an existing session meaning you can browse API endpoints directly in the browser or use the browser devtools to play with the API.</p>
+
+                    <hr>
+
+                    <h5 id="request-format" class="text-mono mb-m">Request Format</h5>
+                    <p>The API is primarily design to be interfaced using JSON so the majority of API endpoints, that accept data, will read JSON request data although <code>application/x-www-form-urlencoded</code> request data is also accepted. Endpoints that receive file data will need data sent in a <code>multipart/form-data</code> format although this will be highlighted in the documentation for such endpoints.</p>
+                    <p>For endpoints in this documentation that accept data, a "Body Parameters" table will be available showing the parameters that will accepted in the request. Any rules for the values of such parameters, such as the data-type or if they're required, will be shown alongside the parameter name.</p>
+
+                    <hr>
+
+                    <h5 id="listing-endpoints" class="text-mono mb-m">Listing Endpoints</h5>
+                    <p>Some endpoints will return a list of data models. These endpoints will return an array of the model data under a <code>data</code> property along with a numeric <code>total</code> property to indicate the total number of records found for the query within the system. Here's an example of a listing response:</p>
+                    <pre><code class="language-json">{
+  "data": [
+    {
+      "id": 1,
+      "name": "BookStack User Guide",
+      "slug": "bookstack-user-guide",
+      "description": "This is a general guide on using BookStack on a day-to-day basis.",
+      "created_at": "2019-05-05 21:48:46",
+      "updated_at": "2019-12-11 20:57:31",
+      "created_by": 1,
+      "updated_by": 1,
+      "image_id": 3
+    }
+  ],
+  "total": 16
+}</code></pre>
+                    <p>
+                        There are a number of standard URL parameters that can be supplied to manipulate and page through the results returned from a listing endpoint:
+                    </p>
+                    <table class="table">
+                        <tr>
+                            <th>Parameter</th>
+                            <th>Details</th>
+                            <th width="30%">Examples</th>
+                        </tr>
+                        <tr>
+                            <td>count</td>
+                            <td>
+                                Specify how many records will be returned in the response. <br>
+                                (Default: {{ config('api.default_item_count') }}, Max: {{ config('api.max_item_count') }})
+                            </td>
+                            <td>Limit the count to 50<br><code>?count=50</code></td>
+                        </tr>
+                        <tr>
+                            <td>offset</td>
+                            <td>
+                                Specify how many records to skip over in the response. <br>
+                                (Default: 0)
+                            </td>
+                            <td>Skip over the first 100 records<br><code>?offset=100</code></td>
+                        </tr>
+                        <tr>
+                            <td>sort</td>
+                            <td>
+                                Specify what field is used to sort the data and the direction of the sort (Ascending or Descending).<br>
+                                Value is the name of a field, A <code>+</code> or <code>-</code> prefix dictates ordering. <br>
+                                Direction defaults to ascending. <br>
+                                Can use most fields shown in the response.
+                            </td>
+                            <td>
+                                Sort by name ascending<br><code>?sort=+name</code> <br> <br>
+                                Sort by "Created At" date descending<br><code>?sort=-created_at</code>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>filter[&lt;field&gt;]</td>
+                            <td>
+                                Specify a filter to be applied to the query. Can use most fields shown in the response. <br>
+                                By default a filter will apply a "where equals" query but the below operations are available using the format filter[&lt;field&gt;:&lt;operation&gt;] <br>
+                                <table>
+                                    <tr>
+                                        <td>eq</td>
+                                        <td>Where <code>&lt;field&gt;</code> equals the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>ne</td>
+                                        <td>Where <code>&lt;field&gt;</code> does not equal the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>gt</td>
+                                        <td>Where <code>&lt;field&gt;</code> is greater than the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>lt</td>
+                                        <td>Where <code>&lt;field&gt;</code> is less than the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>gte</td>
+                                        <td>Where <code>&lt;field&gt;</code> is greater than or equal to the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>lte</td>
+                                        <td>Where <code>&lt;field&gt;</code> is less than or equal to the filter value.</td>
+                                    </tr>
+                                    <tr>
+                                        <td>like</td>
+                                        <td>
+                                            Where <code>&lt;field&gt;</code> is "like" the filter value. <br>
+                                            <code>%</code> symbols can be used as wildcards.
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                            <td>
+                                Filter where id is 5: <br><code>?filter[id]=5</code><br><br>
+                                Filter where id is not 5: <br><code>?filter[id:ne]=5</code><br><br>
+                                Filter where name contains "cat": <br><code>?filter[name:like]=%cat%</code><br><br>
+                                Filter where created after 2020-01-01: <br><code>?filter[created_at:gt]=2020-01-01</code>
+                            </td>
+                        </tr>
+                    </table>
+
+                    <hr>
+
+                    <h5 id="error-handling" class="text-mono mb-m">Error Handling</h5>
+                    <p>
+                        Successful responses will return a 200 or 204 HTTP response code. Errors will return a 4xx or a 5xx HTTP response code depending on the type of error. Errors follow a standard format as shown below. The message provided may be translated depending on the configured language of the system in addition to the API users' language preference. The code provided in the JSON response will match the HTTP response code.
+                    </p>
+
+                    <pre><code class="language-json">{
+       "error": {
+               "code": 401,
+               "message": "No authorization token found on the request"
+       }
+}
+</code></pre>
+
+                </section>
+
                 @foreach($docs as $model => $endpoints)
                     <section class="card content-wrap auto-height">
                         <h1 class="list-heading text-capitals">{{ $model }}</h1>
                 @foreach($docs as $model => $endpoints)
                     <section class="card content-wrap auto-height">
                         <h1 class="list-heading text-capitals">{{ $model }}</h1>
diff --git a/resources/views/users/api-tokens/list.blade.php b/resources/views/users/api-tokens/list.blade.php
new file mode 100644 (file)
index 0000000..ea18933
--- /dev/null
@@ -0,0 +1,34 @@
+<section class="card content-wrap auto-height" id="api_tokens">
+    <div class="grid half mb-s">
+        <div><h2 class="list-heading">{{ trans('settings.users_api_tokens') }}</h2></div>
+        <div class="text-right pt-xs">
+            @if(userCan('access-api'))
+                <a href="{{ url('/api/docs') }}" class="button outline">{{ trans('settings.users_api_tokens_docs') }}</a>
+                <a href="{{ $user->getEditUrl('/create-api-token') }}" class="button outline">{{ trans('settings.users_api_tokens_create') }}</a>
+            @endif
+        </div>
+    </div>
+    @if (count($user->apiTokens) > 0)
+        <table class="table">
+            <tr>
+                <th>{{ trans('common.name') }}</th>
+                <th>{{ trans('settings.users_api_tokens_expires') }}</th>
+                <th></th>
+            </tr>
+            @foreach($user->apiTokens as $token)
+                <tr>
+                    <td>
+                        {{ $token->name }} <br>
+                        <span class="small text-muted italic">{{ $token->token_id }}</span>
+                    </td>
+                    <td>{{ $token->expires_at->format('Y-m-d') ?? '' }}</td>
+                    <td class="text-right">
+                        <a class="button outline small" href="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}">{{ trans('common.edit') }}</a>
+                    </td>
+                </tr>
+            @endforeach
+        </table>
+    @else
+        <p class="text-muted italic py-m">{{ trans('settings.users_api_tokens_none') }}</p>
+    @endif
+</section>
\ No newline at end of file
index c69d101d422c0afbfe4b019ee0c1a0ecec5a10c1..f78c25cebf6a93a00a82975b8ba09c308505a730 100644 (file)
         @endif
 
         @if(($currentUser->id === $user->id && userCan('access-api')) || userCan('users-manage'))
         @endif
 
         @if(($currentUser->id === $user->id && userCan('access-api')) || userCan('users-manage'))
-            <section class="card content-wrap auto-height" id="api_tokens">
-                <div class="grid half">
-                    <div><h2 class="list-heading">{{ trans('settings.users_api_tokens') }}</h2></div>
-                    <div class="text-right pt-xs">
-                        @if(userCan('access-api'))
-                            <a href="{{ $user->getEditUrl('/create-api-token') }}" class="button outline">{{ trans('settings.users_api_tokens_create') }}</a>
-                        @endif
-                    </div>
-                </div>
-                @if (count($user->apiTokens) > 0)
-                    <table class="table">
-                        <tr>
-                            <th>{{ trans('common.name') }}</th>
-                            <th>{{ trans('settings.users_api_tokens_expires') }}</th>
-                            <th></th>
-                        </tr>
-                        @foreach($user->apiTokens as $token)
-                        <tr>
-                            <td>
-                                {{ $token->name }} <br>
-                                <span class="small text-muted italic">{{ $token->token_id }}</span>
-                            </td>
-                            <td>{{ $token->expires_at->format('Y-m-d') ?? '' }}</td>
-                            <td class="text-right">
-                                <a class="button outline small" href="{{ $user->getEditUrl('/api-tokens/' . $token->id) }}">{{ trans('common.edit') }}</a>
-                            </td>
-                        </tr>
-                        @endforeach
-                    </table>
-                @else
-                    <p class="text-muted italic py-m">{{ trans('settings.users_api_tokens_none') }}</p>
-                @endif
-            </section>
+            @include('users.api-tokens.list', ['user' => $user])
         @endif
     </div>
 
         @endif
     </div>
 
index 12b327798988753063db47c911d0108c1be16391..73f2faf793cc7ce344279878d0d12a270f5ecab2 100644 (file)
@@ -9,7 +9,7 @@
 Route::get('docs', 'ApiDocsController@display');
 Route::get('docs.json', 'ApiDocsController@json');
 
 Route::get('docs', 'ApiDocsController@display');
 Route::get('docs.json', 'ApiDocsController@json');
 
-Route::get('books', 'BooksApiController@index');
+Route::get('books', 'BooksApiController@list');
 Route::post('books', 'BooksApiController@create');
 Route::get('books/{id}', 'BooksApiController@read');
 Route::put('books/{id}', 'BooksApiController@update');
 Route::post('books', 'BooksApiController@create');
 Route::get('books/{id}', 'BooksApiController@read');
 Route::put('books/{id}', 'BooksApiController@update');