From 3663b9df2dbcff76eb188054340f2a4440ed5755 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Mon, 20 Oct 2025 16:23:14 -0700 Subject: [PATCH] Moved Resource defaults to datasette/resources.py --- datasette/app.py | 2 +- datasette/default_actions.py | 72 ++++-------------------------------- datasette/resources.py | 66 +++++++++++++++++++++++++++++++++ tests/test_actions_sql.py | 2 +- 4 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 datasette/resources.py diff --git a/datasette/app.py b/datasette/app.py index 225d66e4..3f2e01a4 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -1359,7 +1359,7 @@ class Datasette: This is efficient - it does NOT call allowed_resources() and check membership. Example: - from datasette.default_actions import TableResource + from datasette.resources import TableResource can_view = await datasette.allowed( "view-table", TableResource(database="analytics", table="users"), diff --git a/datasette/default_actions.py b/datasette/default_actions.py index 53916259..e2050111 100644 --- a/datasette/default_actions.py +++ b/datasette/default_actions.py @@ -1,69 +1,11 @@ from datasette import hookimpl -from datasette.permissions import Action, Resource -from typing import Optional - - -class InstanceResource(Resource): - """The Datasette instance itself.""" - - name = "instance" - parent_name = None - - def __init__(self): - super().__init__(parent=None, child=None) - - @classmethod - def resources_sql(cls) -> str: - return "SELECT NULL AS parent, NULL AS child" - - -class DatabaseResource(Resource): - """A database in Datasette.""" - - name = "database" - parent_name = "instance" - - def __init__(self, database: str): - super().__init__(parent=database, child=None) - - @classmethod - def resources_sql(cls) -> str: - return """ - SELECT database_name AS parent, NULL AS child - FROM catalog_databases - """ - - -class TableResource(Resource): - """A table in a database.""" - - name = "table" - parent_name = "database" - - def __init__(self, database: str, table: str): - super().__init__(parent=database, child=table) - - @classmethod - def resources_sql(cls) -> str: - return """ - SELECT database_name AS parent, table_name AS child - FROM catalog_tables - """ - - -class QueryResource(Resource): - """A canned query in a database.""" - - name = "query" - parent_name = "database" - - def __init__(self, database: str, query: str): - super().__init__(parent=database, child=query) - - @classmethod - def resources_sql(cls) -> str: - # TODO: Need catalog for queries - return "SELECT NULL AS parent, NULL AS child WHERE 0" +from datasette.permissions import Action +from datasette.resources import ( + InstanceResource, + DatabaseResource, + TableResource, + QueryResource, +) @hookimpl diff --git a/datasette/resources.py b/datasette/resources.py new file mode 100644 index 00000000..d1c275b0 --- /dev/null +++ b/datasette/resources.py @@ -0,0 +1,66 @@ +"""Core resource types for Datasette's permission system.""" + +from datasette.permissions import Resource + + +class InstanceResource(Resource): + """The Datasette instance itself.""" + + name = "instance" + parent_name = None + + def __init__(self): + super().__init__(parent=None, child=None) + + @classmethod + def resources_sql(cls) -> str: + return "SELECT NULL AS parent, NULL AS child" + + +class DatabaseResource(Resource): + """A database in Datasette.""" + + name = "database" + parent_name = "instance" + + def __init__(self, database: str): + super().__init__(parent=database, child=None) + + @classmethod + def resources_sql(cls) -> str: + return """ + SELECT database_name AS parent, NULL AS child + FROM catalog_databases + """ + + +class TableResource(Resource): + """A table in a database.""" + + name = "table" + parent_name = "database" + + def __init__(self, database: str, table: str): + super().__init__(parent=database, child=table) + + @classmethod + def resources_sql(cls) -> str: + return """ + SELECT database_name AS parent, table_name AS child + FROM catalog_tables + """ + + +class QueryResource(Resource): + """A canned query in a database.""" + + name = "query" + parent_name = "database" + + def __init__(self, database: str, query: str): + super().__init__(parent=database, child=query) + + @classmethod + def resources_sql(cls) -> str: + # TODO: Need catalog for queries + return "SELECT NULL AS parent, NULL AS child WHERE 0" diff --git a/tests/test_actions_sql.py b/tests/test_actions_sql.py index 8fc8803d..774aaaf5 100644 --- a/tests/test_actions_sql.py +++ b/tests/test_actions_sql.py @@ -13,7 +13,7 @@ import pytest_asyncio from datasette.app import Datasette from datasette.plugins import pm from datasette.utils.permissions import PluginSQL -from datasette.default_actions import TableResource +from datasette.resources import TableResource from datasette import hookimpl