Add /-/actions endpoint to list registered actions

This adds a new endpoint at /-/actions that lists all registered actions
in the permission system. The endpoint supports both JSON and HTML output.

Changes:
- Added _actions() method to Datasette class to return action list
- Added route for /-/actions with JsonDataView
- Created actions.html template for nice HTML display
- Added template parameter to JsonDataView for custom templates
- Moved respond_json_or_html from BaseView to JsonDataView
- Added test for the new endpoint

The endpoint requires view-instance permission and provides details about
each action including name, abbreviation, description, resource class,
and parent/child requirements.

Closes #2547

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Simon Willison 2025-10-26 16:10:58 -07:00
commit b3721eaf50
6 changed files with 121 additions and 19 deletions

View file

@ -833,6 +833,35 @@ async def test_versions_json(ds_client):
assert data["sqlite"]["extensions"]["json1"]
@pytest.mark.asyncio
async def test_actions_json(ds_client):
response = await ds_client.get("/-/actions.json")
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
# Check structure of first action
action = data[0]
for key in (
"name",
"abbr",
"description",
"takes_parent",
"takes_child",
"resource_class",
"also_requires",
):
assert key in action
# Check that some expected actions exist
action_names = {a["name"] for a in data}
for expected_action in (
"view-instance",
"view-database",
"view-table",
"execute-sql",
):
assert expected_action in action_names
@pytest.mark.asyncio
async def test_settings_json(ds_client):
response = await ds_client.get("/-/settings.json")

View file

@ -1176,3 +1176,13 @@ async def test_custom_csrf_error(ds_client):
assert response.status_code == 403
assert response.headers["content-type"] == "text/html; charset=utf-8"
assert "Error code is FORM_URLENCODED_MISMATCH." in response.text
@pytest.mark.asyncio
async def test_actions_page(ds_client):
response = await ds_client.get("/-/actions")
assert response.status_code == 200
assert "Registered Actions" in response.text
assert "<th>Name</th>" in response.text
assert "view-instance" in response.text
assert "view-database" in response.text