diff --git a/Justfile b/Justfile index a9cdd94a..adb8cf0d 100644 --- a/Justfile +++ b/Justfile @@ -31,6 +31,10 @@ export DATASETTE_SECRET := "not_a_secret" @docs: cog blacken-docs uv sync --extra docs && cd docs && uv run make livehtml +# Build docs as static HTML +@docs-build: cog blacken-docs + rm -rf docs/_build && cd docs && uv run make html + # Apply Black @black: uv run black . diff --git a/docs/upgrade-1.0a20.md b/docs/upgrade-1.0a20.md new file mode 100644 index 00000000..fcb77062 --- /dev/null +++ b/docs/upgrade-1.0a20.md @@ -0,0 +1,105 @@ +--- +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 with `register_actions` +- The `permission_allowed()` plugin hook - this should be upgraded to `permission_resources_sql()`. +- The `datasette.permission_allowed()` internal method - this should be replaced with `datasette.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: + +```python +@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, and `takes_database` and `takes_resource` have been renamed to `takes_parent` and `takes_child. The new code would look like this: + +```python +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", + takes_parent=False, + takes_child=False, + default=False, + ), + Action( + name="datasette-pins-read", + abbr=None, + description="Can read pinned items.", + takes_parent=False, + takes_child=False, + default=False, + ), + ] +``` + +## permission_allowed() hook is replaced by permission_resources_sql() + +The following old code: +```python +@hookimpl +def permission_allowed(action): + if action == "permissions-debug": + return True +``` +Can be replaced by: +```python +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: + + +## Fixing async with httpx.AsyncClient(app=app) + +Some older plugins may use the following pattern in their tests, which is no longer supported: +```python +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: +```python +ds = Datasette([], memory=True) +response = ds.client.get("/path") +``` diff --git a/docs/upgrade_guide.rst b/docs/upgrade_guide.md similarity index 52% rename from docs/upgrade_guide.rst rename to docs/upgrade_guide.md index f983fb2d..105d7281 100644 --- a/docs/upgrade_guide.rst +++ b/docs/upgrade_guide.md @@ -1,90 +1,76 @@ -.. _upgrade_guide: +(upgrade_guide)= +# Upgrade guide -=============== - Upgrade guide -=============== - -.. _upgrade_guide_v1: - -Datasette 0.X -> 1.0 -==================== +(upgrade_guide_v1)= +## Datasette 0.X -> 1.0 This section reviews breaking changes Datasette ``1.0`` has when upgrading from a ``0.XX`` version. For new features that ``1.0`` offers, see the :ref:`changelog`. -.. _upgrade_guide_v1_sql_queries: - -New URL for SQL queries ------------------------ +(upgrade_guide_v1_sql_queries)= +### New URL for SQL queries Prior to ``1.0a14`` the URL for executing a SQL query looked like this: -:: - - /databasename?sql=select+1 - # Or for JSON: - /databasename.json?sql=select+1 +```text +/databasename?sql=select+1 +# Or for JSON: +/databasename.json?sql=select+1 +``` This endpoint served two purposes: without a ``?sql=`` it would list the tables in the database, but with that option it would return results of a query instead. -The URL for executing a SQL query now looks like this:: +The URL for executing a SQL query now looks like this: - /databasename/-/query?sql=select+1 - # Or for JSON: - /databasename/-/query.json?sql=select+1 +```text +/databasename/-/query?sql=select+1 +# Or for JSON: +/databasename/-/query.json?sql=select+1 +``` **This isn't a breaking change.** API calls to the older ``/databasename?sql=...`` endpoint will redirect to the new ``databasename/-/query?sql=...`` endpoint. Upgrading to the new URL is recommended to avoid the overhead of the additional redirect. -.. _upgrade_guide_v1_metadata: +(upgrade_guide_v1_metadata)= +### Metadata changes -Metadata changes ----------------- +Metadata was completely revamped for Datasette 1.0. There are a number of related breaking changes, from the ``metadata.yaml`` file to Python APIs, that you'll need to consider when upgrading. -Metadata was completely revamped for Datasette 1.0. There are a number of related breaking changes, from the ``metadata.yaml`` file to Python APIs, that you'll need to consider when upgrading. +(upgrade_guide_v1_metadata_split)= +#### ``metadata.yaml`` split into ``datasette.yaml`` -.. _upgrade_guide_v1_metadata_split: - -``metadata.yaml`` split into ``datasette.yaml`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Before Datasette 1.0, the ``metadata.yaml`` file became a kitchen sink if a mix of metadata, configuration, and settings. Now ``metadata.yaml`` is strictly for metaata (ex title and descriptions of database and tables, licensing info, etc). Other settings have been moved to a ``datasette.yml`` configuration file, described in :ref:`configuration`. +Before Datasette 1.0, the ``metadata.yaml`` file became a kitchen sink if a mix of metadata, configuration, and settings. Now ``metadata.yaml`` is strictly for metadata (ex title and descriptions of database and tables, licensing info, etc). Other settings have been moved to a ``datasette.yml`` configuration file, described in :ref:`configuration`. To start Datasette with both metadata and configuration files, run it like this: -.. code-block:: bash +```bash +datasette --metadata metadata.yaml --config datasette.yaml +# Or the shortened version: +datasette -m metadata.yml -c datasette.yml +``` - datasette --metadata metadata.yaml --config datasette.yaml - # Or the shortened version: - datasette -m metadata.yml -c datasette.yml +(upgrade_guide_v1_metadata_upgrade)= +#### Upgrading an existing ``metadata.yaml`` file -.. _upgrade_guide_v1_metadata_upgrade: +The [datasette-upgrade plugin](https://github.com/datasette/datasette-upgrade) can be used to split a Datasette 0.x.x ``metadata.yaml`` (or ``.json``) file into separate ``metadata.yaml`` and ``datasette.yaml`` files. First, install the plugin: -Upgrading an existing ``metadata.yaml`` file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `datasette-upgrade plugin `__ can be used to split a Datasette 0.x.x ``metadata.yaml`` (or ``.json``) file into separate ``metadata.yaml`` and ``datasette.yaml`` files. First, install the plugin: - -.. code-block:: bash - - datasette install datasette-upgrade +```bash +datasette install datasette-upgrade +``` Then run it like this to produce the two new files: -.. code-block:: bash +```bash +datasette upgrade metadata-to-config metadata.json -m metadata.yml -c datasette.yml +``` - datasette upgrade metadata-to-config metadata.json -m metadata.yml -c datasette.yml - -Metadata "fallback" has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#### Metadata "fallback" has been removed Certain keys in metadata like ``license`` used to "fallback" up the chain of ownership. For example, if you set an ``MIT`` to a database and a table within that database did not have a specified license, then that table would inherit an ``MIT`` license. This behavior has been removed in Datasette 1.0. Now license fields must be placed on all items, including individual databases and tables. -.. _upgrade_guide_v1_metadata_removed: - -The ``get_metadata()`` plugin hook has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(upgrade_guide_v1_metadata_removed)= +#### The ``get_metadata()`` plugin hook has been removed In Datasette ``0.x`` plugins could implement a ``get_metadata()`` plugin hook to customize how metadata was retrieved for different instances, databases and tables. @@ -92,33 +78,29 @@ This hook could be inefficient, since some pages might load metadata for many di As of Datasette ``1.0a14`` (2024-08-05), the ``get_metadata()`` hook has been deprecated: -.. code-block:: python - - # ❌ DEPRECATED in Datasette 1.0 - @hookimpl - def get_metadata(datasette, key, database, table): - pass +```python +# ❌ DEPRECATED in Datasette 1.0 +@hookimpl +def get_metadata(datasette, key, database, table): + pass +``` Instead, plugins are encouraged to interact directly with Datasette's in-memory metadata tables in SQLite using the following methods on the :ref:`internals_datasette`: -- :ref:`get_instance_metadata() ` and :ref:`set_instance_metadata() ` -- :ref:`get_database_metadata() ` and :ref:`set_database_metadata() ` -- :ref:`get_resource_metadata() ` and :ref:`set_resource_metadata() ` -- :ref:`get_column_metadata() ` and :ref:`set_column_metadata() ` +- :ref:`get_instance_metadata() ` and :ref:`set_instance_metadata() ` +- :ref:`get_database_metadata() ` and :ref:`set_database_metadata() ` +- :ref:`get_resource_metadata() ` and :ref:`set_resource_metadata() ` +- :ref:`get_column_metadata() ` and :ref:`set_column_metadata() ` A plugin that stores or calculates its own metadata can implement the :ref:`plugin_hook_startup` hook to populate those items on startup, and then call those methods while it is running to persist any new metadata changes. -.. _upgrade_guide_v1_metadata_json_removed: - -The ``/metadata.json`` endpoint has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(upgrade_guide_v1_metadata_json_removed)= +#### The ``/metadata.json`` endpoint has been removed As of Datasette ``1.0a14``, the root level ``/metadata.json`` endpoint has been removed. Metadata for tables will become available through currently in-development extras in a future alpha. -.. _upgrade_guide_v1_metadata_method_removed: - -The ``metadata()`` method on the Datasette class has been removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +(upgrade_guide_v1_metadata_method_removed)= +#### The ``metadata()`` method on the Datasette class has been removed As of Datasette ``1.0a14``, the ``.metadata()`` method on the Datasette Python API has been removed. @@ -128,3 +110,7 @@ Instead, one should use the following methods on a Datasette class: - :ref:`get_database_metadata() ` - :ref:`get_resource_metadata() ` - :ref:`get_column_metadata() ` + +```{include} upgrade-1.0a20.md +:heading-offset: 1 +```