Example links for API explorer, closes #1871

pull/1912/head
Simon Willison 2022-11-13 20:58:45 -08:00
rodzic c603faac5b
commit db796771e2
2 zmienionych plików z 146 dodań i 4 usunięć

Wyświetl plik

@ -22,7 +22,7 @@
<form method="get" id="api-explorer-get" style="margin-top: 0.7em">
<div>
<label for="path">API path:</label>
<input type="text" id="path" name="path" value="/data/docs.json" style="width: 60%">
<input type="text" id="path" name="path" style="width: 60%">
<input type="submit" value="GET">
</div>
</form>
@ -32,7 +32,7 @@
<form method="post" id="api-explorer-post" style="margin-top: 0.7em">
<div>
<label for="path">API path:</label>
<input type="text" id="path" name="path" value="/data/docs/-/insert" style="width: 60%">
<input type="text" id="path" name="path" style="width: 60%">
</div>
<div style="margin: 0.5em 0">
<label for="apiJson" style="vertical-align: top">JSON:</label>
@ -65,8 +65,11 @@ var getForm = document.getElementById('api-explorer-get');
var output = document.getElementById('output');
var errorList = output.querySelector('.errors');
// On first load, populate forms from # in URL, if present
// On first load or fragment change populate forms from # in URL, if present
if (window.location.hash) {
onFragmentChange();
}
function onFragmentChange() {
var hash = window.location.hash.slice(1);
// Treat hash as a foo=bar string and parse it:
var params = new URLSearchParams(hash);
@ -82,6 +85,11 @@ if (window.location.hash) {
postForm.querySelector('textarea[name="json"]').value = params.get('json');
}
}
window.addEventListener('hashchange', () => {
onFragmentChange();
// Animate scroll to top of page
window.scrollTo({top: 0, behavior: 'smooth'});
});
// Cause GET and POST regions to toggle each other
var getDetails = getForm.closest('details');
@ -171,4 +179,27 @@ postForm.addEventListener("submit", (ev) => {
});
</script>
{% if example_links %}
<h2>API endpoints</h2>
<ul class="bullets">
{% for database in example_links %}
<li>Database: <strong>{{ database.name }}</strong></li>
<ul class="bullets">
{% for link in database.links %}
<li><a href="{{ api_path(link) }}">{{ link.path }}</a> - {{ link.label }} </li>
{% endfor %}
{% for table in database.tables %}
<li><strong>{{ table.name }}</strong>
<ul class="bullets">
{% for link in table.links %}
<li><a href="{{ api_path(link) }}">{{ link.path }}</a> - {{ link.label }} </li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

Wyświetl plik

@ -6,6 +6,7 @@ from datasette.permissions import PERMISSIONS
from .base import BaseView
import secrets
import time
import urllib
class JsonDataView(BaseView):
@ -275,5 +276,115 @@ class ApiExplorerView(BaseView):
name = "api_explorer"
has_json_alternate = False
async def example_links(self, request):
databases = []
for name, db in self.ds.databases.items():
if name == "_internal":
continue
if not db.is_mutable:
continue
database_visible, _ = await self.ds.check_visibility(
request.actor,
"view-database",
name,
)
if not database_visible:
continue
tables = []
table_names = await db.table_names()
for table in table_names:
visible, _ = await self.ds.check_visibility(
request.actor,
"view-table",
(name, table),
)
if not visible:
continue
table_links = []
table_links.append(
{
"label": "Get rows for {}".format(table),
"method": "GET",
"path": self.ds.urls.table(name, table, format="json")
+ "?_shape=objects".format(name, table),
}
)
if await self.ds.permission_allowed(
request.actor, "insert-row", (name, table)
):
pks = await db.primary_keys(table)
table_links.append(
{
"path": self.ds.urls.table(name, table) + "/-/insert",
"method": "POST",
"label": "Insert rows into {}".format(table),
"json": {
"rows": [
{
column: None
for column in await db.table_columns(table)
if column not in pks
}
]
},
}
)
if await self.ds.permission_allowed(
request.actor, "drop-table", (name, table)
):
table_links.append(
{
"path": self.ds.urls.table(name, table) + "/-/drop",
"label": "Drop table {}".format(table),
"json": {},
"method": "POST",
}
)
tables.append({"name": table, "links": table_links})
database_links = []
if await self.ds.permission_allowed(request.actor, "create-table", name):
database_links.append(
{
"path": self.ds.urls.database(name) + "/-/create",
"label": "Create table in {}".format(name),
"json": {
"table": "new_table",
"columns": [
{"name": "id", "type": "integer"},
{"name": "name", "type": "text"},
],
"pk": "id",
},
"method": "POST",
}
)
if database_links or tables:
databases.append(
{
"name": name,
"links": database_links,
"tables": tables,
}
)
return databases
async def get(self, request):
return await self.render(["api_explorer.html"], request)
def api_path(link):
return "/-/api#{}".format(
urllib.parse.urlencode(
{
key: json.dumps(value, indent=2) if key == "json" else value
for key, value in link.items()
if key in ("path", "method", "json")
}
)
)
return await self.render(
["api_explorer.html"],
request,
{
"example_links": await self.example_links(request),
"api_path": api_path,
},
)