updated API preview
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import tooltip from "@/actions/tooltip";
|
||||
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
|
||||
|
||||
const baseTabs = {
|
||||
@@ -26,6 +27,10 @@
|
||||
label: "Realtime",
|
||||
component: import("@/components/collections/docs/RealtimeApiDocs.svelte"),
|
||||
},
|
||||
batch: {
|
||||
label: "Batch",
|
||||
component: import("@/components/collections/docs/BatchApiDocs.svelte"),
|
||||
},
|
||||
};
|
||||
|
||||
const authTabs = {
|
||||
@@ -33,10 +38,6 @@
|
||||
label: "List auth methods",
|
||||
component: import("@/components/collections/docs/AuthMethodsDocs.svelte"),
|
||||
},
|
||||
refresh: {
|
||||
label: "Auth refresh",
|
||||
component: import("@/components/collections/docs/AuthRefreshDocs.svelte"),
|
||||
},
|
||||
"auth-with-password": {
|
||||
label: "Auth with password",
|
||||
component: import("@/components/collections/docs/AuthWithPasswordDocs.svelte"),
|
||||
@@ -49,6 +50,10 @@
|
||||
label: "Auth with OTP",
|
||||
component: import("@/components/collections/docs/AuthWithOtpDocs.svelte"),
|
||||
},
|
||||
refresh: {
|
||||
label: "Auth refresh",
|
||||
component: import("@/components/collections/docs/AuthRefreshDocs.svelte"),
|
||||
},
|
||||
verification: {
|
||||
label: "Verification",
|
||||
component: import("@/components/collections/docs/VerificationDocs.svelte"),
|
||||
@@ -71,13 +76,13 @@
|
||||
$: if (collection.type === "auth") {
|
||||
tabs = Object.assign({}, baseTabs, authTabs);
|
||||
if (!collection.passwordAuth.enabled) {
|
||||
delete tabs["auth-with-password"];
|
||||
tabs["auth-with-password"].disabled = true;
|
||||
}
|
||||
if (!collection.oauth2.enabled) {
|
||||
delete tabs["auth-with-oauth2"];
|
||||
tabs["auth-with-oauth2"].disabled = true;
|
||||
}
|
||||
if (!collection.otp.enabled) {
|
||||
delete tabs["auth-with-otp"];
|
||||
tabs["auth-with-otp"].disabled = true;
|
||||
}
|
||||
} else if (collection.type === "view") {
|
||||
tabs = Object.assign({}, baseTabs);
|
||||
@@ -121,14 +126,23 @@
|
||||
<hr class="m-t-sm m-b-sm" />
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="sidebar-item"
|
||||
class:active={activeTab === key}
|
||||
on:click={() => changeTab(key)}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
{#if tab.disabled}
|
||||
<div
|
||||
class="sidebar-item disabled"
|
||||
use:tooltip={{ position: "left", text: "Not enabled for the collection" }}
|
||||
>
|
||||
{tab.label}
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="sidebar-item"
|
||||
class:active={activeTab === key}
|
||||
on:click={() => changeTab(key)}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
{/if}
|
||||
{/each}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
@@ -19,6 +19,16 @@
|
||||
code: 200,
|
||||
body: isLoading ? "..." : JSON.stringify(authMethods, null, 2),
|
||||
},
|
||||
{
|
||||
code: 404,
|
||||
body: `
|
||||
{
|
||||
"code": 404,
|
||||
"message": "Missing collection context.",
|
||||
"data": {}
|
||||
}
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
listAuthMethods();
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
<h3 class="m-b-sm">Auth with OTP ({collection.name})</h3>
|
||||
<div class="content txt-lg m-b-sm">
|
||||
<p>Authenticate with an one-time password (OTP).</p>
|
||||
<p>
|
||||
Note that when requesting an OTP we return an <code>otpId</code> even if a user with the provided email
|
||||
doesn't exist as a very basic enumeration protection.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SdkTabs
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
<script>
|
||||
import { link } from "svelte-spa-router";
|
||||
import ApiClient from "@/utils/ApiClient";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import CodeBlock from "@/components/base/CodeBlock.svelte";
|
||||
import SdkTabs from "@/components/base/SdkTabs.svelte";
|
||||
|
||||
export let collection;
|
||||
|
||||
let responseTab = 200;
|
||||
let responses = [];
|
||||
|
||||
$: backendAbsUrl = CommonHelper.getApiExampleUrl(ApiClient.baseURL);
|
||||
|
||||
$: dummyRecord = CommonHelper.dummyCollectionRecord(collection);
|
||||
|
||||
$: if (collection?.id) {
|
||||
responses.push({
|
||||
code: 200,
|
||||
body: JSON.stringify(
|
||||
[dummyRecord, Object.assign({}, dummyRecord, { id: dummyRecord + "2" })],
|
||||
null,
|
||||
2,
|
||||
),
|
||||
});
|
||||
|
||||
responses.push({
|
||||
code: 400,
|
||||
body: `
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Batch transaction failed.",
|
||||
"data": {
|
||||
"requests": {
|
||||
"1": {
|
||||
"code": "batch_request_failed",
|
||||
"message": "Batch request failed.",
|
||||
"response": {
|
||||
"status": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"id": {
|
||||
"code": "validation_min_text_constraint",
|
||||
"message": "Must be at least 3 character(s).",
|
||||
"params": { "min": 3 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
responses.push({
|
||||
code: 403,
|
||||
body: `
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Batch requests are not allowed.",
|
||||
"data": {}
|
||||
}
|
||||
`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3 class="m-b-sm">Batch create/update/upsert/delete ({collection.name})</h3>
|
||||
<div class="content txt-lg m-b-sm">
|
||||
<p>Batch and transactional create/update/upsert/delete of multiple records in a single request.</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<div class="icon">
|
||||
<i class="ri-error-warning-line" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>
|
||||
The batch Web API need to be explicitly enabled and configured from the
|
||||
<a href="/settings" use:link>Dashboard settings</a>.
|
||||
</p>
|
||||
<p>
|
||||
Because this endpoint process the requests in a single transaction, it could degrade the
|
||||
performance of your application if not used with proper care and configuration (e.g. too large
|
||||
allowed execution timeout, large body size limit, etc.).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SdkTabs
|
||||
js={`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${backendAbsUrl}');
|
||||
|
||||
...
|
||||
|
||||
const batch = pb.createBatch();
|
||||
|
||||
batch.collection('${collection?.name}').create({ ... });
|
||||
batch.collection('${collection?.name}').update("RECORD_ID", { ... });
|
||||
batch.collection('${collection?.name}').delete("RECORD_ID");
|
||||
batch.collection('${collection?.name}').upsert({ ... });
|
||||
|
||||
const result = await batch.send();
|
||||
`}
|
||||
dart={`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${backendAbsUrl}');
|
||||
|
||||
...
|
||||
|
||||
final batch = pb.createBatch();
|
||||
|
||||
batch.collection('${collection?.name}').create(body: { ... });
|
||||
batch.collection('${collection?.name}').update('RECORD_ID', body: { ... });
|
||||
batch.collection('${collection?.name}').delete('RECORD_ID');
|
||||
batch.collection('${collection?.name}').upsert(body: { ... });
|
||||
|
||||
final result = await batch.send();
|
||||
`}
|
||||
/>
|
||||
|
||||
<h6 class="m-b-xs">API details</h6>
|
||||
<div class="api-route alert alert-success">
|
||||
<strong class="label label-primary">POST</strong>
|
||||
<div class="content">/api/batch</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">Body Parameters</div>
|
||||
<p>
|
||||
Body parameters could be sent as <em>application/json</em> or <em>multipart/form-data</em>.
|
||||
<br />
|
||||
File upload is supported only via <em>multipart/form-data</em> (see below for more details).
|
||||
</p>
|
||||
<table class="table-compact table-border m-t-xs m-b-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param</th>
|
||||
<th width="80%">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<div class="flex txt-nowrap">
|
||||
<span class="label label-success">Required</span>
|
||||
<span>requests</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label">{`Array<Request>`}</span> - List of the requests to process.
|
||||
|
||||
<p>The supported batch request actions are:</p>
|
||||
<ul>
|
||||
<li>record create - <code>POST /api/collections/{`{collection}`}/records</code></li>
|
||||
<li>
|
||||
record update -
|
||||
<code>PATCH /api/collections/{`{collection}`}/records/{`{id}`}</code>
|
||||
</li>
|
||||
<li>
|
||||
record upsert - <code>PUT /api/collections/{`{collection}`}/records</code>
|
||||
<br />
|
||||
<small class="txt-hint">
|
||||
(the body must have <code class="txt-sm">id</code> field)
|
||||
</small>
|
||||
</li>
|
||||
<li>
|
||||
record delete -
|
||||
<code>DELETE /api/collections/{`{collection}`}/records/{`{id}`}</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Each batch Request element have the following properties:</p>
|
||||
<ul>
|
||||
<li><code>url path</code> <em>(could include query parameters)</em></li>
|
||||
<li><code>method</code> <em>(GET, POST, PUT, PATCH, DELETE)</em></li>
|
||||
<li>
|
||||
<code>headers</code>
|
||||
<br />
|
||||
<em>
|
||||
(if the <code>POST /api/batch</code> call has <code>Authorization</code> header it
|
||||
is forwarded to all batch requests automatically)
|
||||
</em>
|
||||
</li>
|
||||
<li><code>body</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
<strong>NB!</strong> When the batch request is send as
|
||||
<code>multipart/form-data</code>, the regular batch action fields are expected to be
|
||||
submitted as serailized json under the <code>@jsonPayload</code> field and file keys need
|
||||
to follow the pattern <code>requests.N.fileField</code> or
|
||||
<code>requests[N].fileField</code>
|
||||
<em>
|
||||
(this is usually handled transparently by the SDKs when their specific object notation
|
||||
is used)
|
||||
</em>.
|
||||
<br />
|
||||
If you don't use the SDKs or prefer manually to construct the <code>FormData</code>
|
||||
body, then it could look something like:
|
||||
<CodeBlock
|
||||
language="javascript"
|
||||
content={`
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("@jsonPayload", JSON.stringify({
|
||||
requests: [
|
||||
{
|
||||
method: "POST",
|
||||
url: "/api/collections/${collection.name}/records?fields=id",
|
||||
body: { someField: "test1" }
|
||||
},
|
||||
{
|
||||
method: "PATCH",
|
||||
url: "/api/collections/${collection.name}/records/RECORD_ID",
|
||||
body: { someField: "test2" }
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
// file for the first request
|
||||
formData.append("requests.0.someFileField", new File(...))
|
||||
|
||||
// file for the second request
|
||||
formData.append("requests.1.someFileField", new File(...))
|
||||
`}
|
||||
/>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="section-title">Responses</div>
|
||||
<div class="tabs">
|
||||
<div class="tabs-header compact combined left">
|
||||
{#each responses as response (response.code)}
|
||||
<button
|
||||
class="tab-item"
|
||||
class:active={responseTab === response.code}
|
||||
on:click={() => (responseTab = response.code)}
|
||||
>
|
||||
{response.code}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="tabs-content">
|
||||
{#each responses as response (response.code)}
|
||||
<div class="tab-item" class:active={responseTab === response.code}>
|
||||
<CodeBlock content={response.body} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"newEmail": {
|
||||
"code": "validation_required",
|
||||
@@ -56,7 +56,7 @@
|
||||
/api/collections/<strong>{collection.name}</strong>/request-email-change
|
||||
</p>
|
||||
</div>
|
||||
<p class="txt-hint txt-sm txt-right">Requires <code>Authorization:TOKEN</code> header</p>
|
||||
<p class="txt-hint txt-sm txt-right">Requires <code>Authorization:TOKEN</code></p>
|
||||
</div>
|
||||
|
||||
<div class="section-title">Body Parameters</div>
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
$: backendAbsUrl = CommonHelper.getApiExampleUrl(ApiClient.baseURL);
|
||||
|
||||
$: dummyRecord = CommonHelper.dummyCollectionRecord(collection);
|
||||
|
||||
$: if (collection?.id) {
|
||||
responses.push({
|
||||
code: 200,
|
||||
@@ -26,13 +28,10 @@
|
||||
perPage: 30,
|
||||
totalPages: 1,
|
||||
totalItems: 2,
|
||||
items: [
|
||||
CommonHelper.dummyCollectionRecord(collection),
|
||||
CommonHelper.dummyCollectionRecord(collection),
|
||||
],
|
||||
items: [dummyRecord, Object.assign({}, dummyRecord, { id: dummyRecord + "2" })],
|
||||
},
|
||||
null,
|
||||
2
|
||||
2,
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"email": {
|
||||
"code": "validation_required",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
body: `
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"email": {
|
||||
"code": "validation_required",
|
||||
|
||||
Reference in New Issue
Block a user