Simplified Action by moving takes_child/takes_parent logic to Resource - Removed InstanceResource - global actions are now simply those with resource_class=None - Resource.parent_class - Replaced parent_name: str with parent_class: type[Resource] | None for direct class references - Simplified Action dataclass - No more redundant fields, everything is derived from the Resource class structure - Validation - The __init_subclass__ method now checks parent_class.parent_class to enforce the 2-level hierarchy Closes #2563
3.9 KiB
| orphan |
|---|
| true |
Datasette 1.0a20 plugin upgrade guide
Datasette 1.0a20 makes some breaking changes to Datasette's permission system. Plugins need to be updated if they use any of the following:
- The
register_permissions()plugin hook - this should be replaced withregister_actions - The
permission_allowed()plugin hook - this should be upgraded topermission_resources_sql(). - The
datasette.permission_allowed()internal method - this should be replaced withdatasette.allowed() - Logic that grants access to the
"root"actor can be removed.
Permissions are now actions
The register_permissions() hook shoud be replaced with register_actions().
Old code:
@hookimpl
def register_permissions(datasette):
return [
Permission(
name="datasette-pins-write",
abbr=None,
description="Can pin, unpin, and re-order pins for datasette-pins",
takes_database=False,
takes_resource=False,
default=False,
),
Permission(
name="datasette-pins-read",
abbr=None,
description="Can read pinned items.",
takes_database=False,
takes_resource=False,
default=False,
),
]
The new Action does not have a default= parameter. For global actions (those that don't apply to specific resources), omit resource_class:
from datasette.permissions import Action
@hookimpl
def register_actions(datasette):
return [
Action(
name="datasette-pins-write",
abbr=None,
description="Can pin, unpin, and re-order pins for datasette-pins",
),
Action(
name="datasette-pins-read",
abbr=None,
description="Can read pinned items.",
),
]
For actions that apply to specific resources (like databases or tables), specify the resource_class instead of takes_parent and takes_child:
from datasette.permissions import Action
from datasette.resources import DatabaseResource, TableResource
@hookimpl
def register_actions(datasette):
return [
Action(
name="execute-sql",
abbr="es",
description="Execute SQL queries",
resource_class=DatabaseResource, # Parent-level resource
),
Action(
name="insert-row",
abbr="ir",
description="Insert rows",
resource_class=TableResource, # Child-level resource
),
]
The hierarchy information (whether an action takes parent/child parameters) is now derived from the Resource class hierarchy. Action has takes_parent and takes_child properties that are computed based on the resource_class and its parent_class attribute.
permission_allowed() hook is replaced by permission_resources_sql()
The following old code:
@hookimpl
def permission_allowed(action):
if action == "permissions-debug":
return True
Can be replaced by:
from datasette.permissions import PermissionSQL
@hookimpl
def permission_resources_sql(action):
return PermissionSQL.allow(reason="datasette-allow-permissions-debug")
A .deny(reason="") class method is also available.
For more complex permission checks consult the documentation for that plugin hook: https://docs.datasette.io/en/latest/plugin_hooks.html#permission-resources-sql-datasette-actor-action
Fixing async with httpx.AsyncClient(app=app)
Some older plugins may use the following pattern in their tests, which is no longer supported:
app = Datasette([], memory=True).app()
async with httpx.AsyncClient(app=app) as client:
response = await client.get("http://localhost/path")
The new pattern is to use ds.client like this:
ds = Datasette([], memory=True)
response = ds.client.get("/path")