@@ -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) => {
});
+{% if example_links %}
+
API endpoints
+
+ {% for database in example_links %}
+ - Database: {{ database.name }}
+
+ {% for link in database.links %}
+ - {{ link.path }} - {{ link.label }}
+ {% endfor %}
+ {% for table in database.tables %}
+ - {{ table.name }}
+
+ {% for link in table.links %}
+ - {{ link.path }} - {{ link.label }}
+ {% endfor %}
+
+
+ {% endfor %}
+
+ {% endfor %}
+
+{% endif %}
+
{% endblock %}
diff --git a/datasette/views/special.py b/datasette/views/special.py
index 79e9da72..468680d3 100644
--- a/datasette/views/special.py
+++ b/datasette/views/special.py
@@ -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,
+ },
+ )