repos: 280500027
This data as json
id | node_id | name | full_name | private | owner | html_url | description | fork | created_at | updated_at | pushed_at | homepage | size | stargazers_count | watchers_count | language | has_issues | has_projects | has_downloads | has_wiki | has_pages | forks_count | archived | disabled | open_issues_count | license | topics | forks | open_issues | watchers | default_branch | permissions | temp_clone_token | organization | network_count | subscribers_count | readme | readme_html | allow_forking | visibility | is_template | template_repository | web_commit_signoff_required | has_discussions |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
280500027 | MDEwOlJlcG9zaXRvcnkyODA1MDAwMjc= | datasette-insert | simonw/datasette-insert | 0 | 9599 | https://github.com/simonw/datasette-insert | Datasette plugin for inserting and updating data | 0 | 2020-07-17T18:40:34Z | 2022-06-27T02:54:14Z | 2022-07-22T17:52:23Z | 54 | 9 | 9 | Python | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | ["datasette", "datasette-io", "datasette-plugin"] | 0 | 1 | 9 | main | {"admin": false, "maintain": false, "push": false, "triage": false, "pull": false} | 0 | 2 | # datasette-insert [](https://pypi.org/project/datasette-insert/) [](https://github.com/simonw/datasette-insert/releases) [](https://github.com/simonw/datasette-insert/blob/master/LICENSE) Datasette plugin for inserting and updating data ## Installation Install this plugin in the same environment as Datasette. $ pip install datasette-insert This plugin should always be deployed with additional configuration to prevent unauthenticated access, see notes below. If you are trying it out on your own local machine, you can `pip install` the [datasette-insert-unsafe](https://github.com/simonw/datasette-insert-unsafe) plugin to allow access without needing to set up authentication or permissions separately. ## Inserting data and creating tables Start datasette and make sure it has a writable SQLite database attached to it. If you have not yet created a database file you can use this: datasette data.db --create The `--create` option will create a new empty `data.db` database file if it does not already exist. The plugin adds an endpoint that allows data to be inserted or updated and tables to be created by POSTing JSON data to the following URL: /-/insert/name-of-database/name-of-table The JSON should look like this: ```json [ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 5 } ] ``` The first time data is posted to the URL a table of that name will be created if it does not aready exist, with the desired columns. You can specify which column should be used as the primary key using the `?pk=` URL argument. Here's how to POST to a database and create a new table using the Python `requests` library: ```python import requests requests.post("http://localhost:8001/-/insert/data/dogs?pk=id", json=[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 4 } ]) ``` And here's how to do the same thing using `curl`: ``` curl --request POST \ --data '[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 4 } ]' \ 'http://localhost:8001/-/insert/data/dogs?pk=id' ``` Or by piping in JSON like so: cat dogs.json | curl --request POST -d @- \ 'http://localhost:8001/-/insert/data/dogs?pk=id' ### Inserting a single row If you are inserting a single row you can optionally send it as a dictionary rather than a list with a single item: ``` curl --request POST \ --data '{ "id": 1, "name": "Cleopaws", "age": 5 }' \ 'http://localhost:8001/-/insert/data/dogs?pk=id' ``` ### Automatically adding new columns If you send data to an existing table with keys that are not reflected by the existing columns, you will get an HTTP 400 error with a JSON response like this: ```json { "status": 400, "error": "Unknown keys: 'foo'", "error_code": "unknown_keys" } ``` If you add `?alter=1` to the URL you are posting to any missing columns will be automatically added: ``` curl --request POST \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs?alter=1' ``` ## Upserting data An "upsert" operation can be used to partially update a record. With upserts you can send a subset of the keys and, if the ID matches the specified primary key, they will be used to update an existing record. Upserts can be sent to the `/-/upsert` API endpoint. This example will update the dog with ID=1's age from 5 to 7: ``` curl --request POST \ --data '{ "id": 1, "age": 7 }' \ 'http://localhost:3322/-/upsert/data/dogs?pk=id' ``` Like the `/-/insert` endpoint, the `/-/upsert` endpoint can accept an array of objects too. It also supports the `?alter=1` option. ## Permissions and authentication This plugin defaults to denying all access, to help ensure people don't accidentally deploy it on the open internet in an unsafe configuration. You can read about [Datasette's approach to authentication](https://datasette.readthedocs.io/en/stable/authentication.html) in the Datasette manual. You can install the `datasette-insert-unsafe` plugin to run in unsafe mode, where all access is allowed by default. I recommend using this plugin in conjunction with [datasette-auth-tokens](https://github.com/simonw/datasette-auth-tokens), which provides a mechanism for making authenticated calls using API tokens. You can then use ["allow" blocks](https://datasette.readthedocs.io/en/stable/authentication.html#defining-permissions-with-allow-blocks) in the `datasette-insert` plugin configuration to specify which authenticated tokens are allowed to make use of the API. Here's an example `metadata.json` file which restricts access to the `/-/insert` API to an API token defined in an `INSERT_TOKEN` environment variable: ```json { "plugins": { "datasette-insert": { "allow": { "bot": "insert-bot" } }, "datasette-auth-tokens": { "tokens": [ { "token": { "$env": "INSERT_TOKEN" }, "actor": { "bot": "insert-bot" } } ] } } } ``` With this configuration in place you can start Datasette like this: INSERT_TOKEN=abc123 datasette data.db -m metadata.json You can now send data to the API using `curl` like this: ``` curl --request POST \ -H "Authorization: Bearer abc123" \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs' ``` Or using the Python `requests` library like so: ```python requests.post( "http://localhost:8001/-/insert/data/dogs", json={"id": 1, "name": "Cleopaws", "age": 5}, headers={"Authorization": "bearer abc123"}, ) ``` ### Finely grained permissions Using an `"allow"` block as described above grants full permission to the features enabled by the API. The API implements several new Datasett permissions, which other plugins can use to make more finely grained decisions. The full set of permissions are as follows: - `insert:all` - all permissions - this is used by the `"allow"` block described above. Argument: `database_name` - `insert:insert-update` - the ability to insert data into an existing table, or to update data by its primary key. Arguments: `(database_name, table_name)` - `insert:create-table` - the ability to create a new table. Argument: `database_name` - `insert:alter-table` - the ability to add columns to an existing table (using `?alter=1`). Arguments: `(database_name, table_name)` You can use plugins like [datasette-permissions-sql](https://github.com/simonw/datasette-permissions-sql) to hook into these more detailed permissions for finely grained control over what actions each authenticated actor can take. Plugins that implement the [permission_allowed()](https://datasette.readthedocs.io/en/stable/plugin_hooks.html#plugin-hook-permission-allowed) plugin hook can take full control over these permission decisions. ## CORS If you start Datasette with the `datasette --cors` option the following HTTP headers will be added to resources served by this plugin: Access-Control-Allow-Origin: * Access-Control-Allow-Headers: content-type,authorization Access-Control-Allow-Methods: POST ## Development To set up this plugin locally, first checkout the code. Then create a new virtual environment: cd datasette-insert python3 -m venv venv source venv/bin/activate Now install the dependencies and tests: pip install -e '.[test]' To run the tests: pytest | <div id="readme" class="md" data-path="README.md"><article class="markdown-body entry-content container-lg" itemprop="text"><h1 dir="auto"><a id="user-content-datasette-insert" class="anchor" aria-hidden="true" href="#user-content-datasette-insert"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>datasette-insert</h1> <p dir="auto"><a href="https://pypi.org/project/datasette-insert/" rel="nofollow"><img src="https://camo.githubusercontent.com/f8375e92990e2f5fdcf4657451dccba02132abfd34e39bb49773201264951b85/68747470733a2f2f696d672e736869656c64732e696f2f707970692f762f6461746173657474652d696e736572742e737667" alt="PyPI" data-canonical-src="https://img.shields.io/pypi/v/datasette-insert.svg" style="max-width: 100%;"></a> <a href="https://github.com/simonw/datasette-insert/releases"><img src="https://camo.githubusercontent.com/035155855612d32c93ac562f5316c97a15edc01cc0ef35ad4535951368e9ff42/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f73696d6f6e772f6461746173657474652d696e736572743f696e636c7564655f70726572656c6561736573266c6162656c3d6368616e67656c6f67" alt="Changelog" data-canonical-src="https://img.shields.io/github/v/release/simonw/datasette-insert?include_prereleases&label=changelog" style="max-width: 100%;"></a> <a href="https://github.com/simonw/datasette-insert/blob/master/LICENSE"><img src="https://camo.githubusercontent.com/1698104e976c681143eb0841f9675c6f802bb7aa832afc0c7a4e719b1f3cf955/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d417061636865253230322e302d626c75652e737667" alt="License" data-canonical-src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" style="max-width: 100%;"></a></p> <p dir="auto">Datasette plugin for inserting and updating data</p> <h2 dir="auto"><a id="user-content-installation" class="anchor" aria-hidden="true" href="#user-content-installation"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Installation</h2> <p dir="auto">Install this plugin in the same environment as Datasette.</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ pip install datasette-insert"><pre class="notranslate"><code>$ pip install datasette-insert </code></pre></div> <p dir="auto">This plugin should always be deployed with additional configuration to prevent unauthenticated access, see notes below.</p> <p dir="auto">If you are trying it out on your own local machine, you can <code>pip install</code> the <a href="https://github.com/simonw/datasette-insert-unsafe">datasette-insert-unsafe</a> plugin to allow access without needing to set up authentication or permissions separately.</p> <h2 dir="auto"><a id="user-content-inserting-data-and-creating-tables" class="anchor" aria-hidden="true" href="#user-content-inserting-data-and-creating-tables"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Inserting data and creating tables</h2> <p dir="auto">Start datasette and make sure it has a writable SQLite database attached to it. If you have not yet created a database file you can use this:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="datasette data.db --create"><pre class="notranslate"><code>datasette data.db --create </code></pre></div> <p dir="auto">The <code>--create</code> option will create a new empty <code>data.db</code> database file if it does not already exist.</p> <p dir="auto">The plugin adds an endpoint that allows data to be inserted or updated and tables to be created by POSTing JSON data to the following URL:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="/-/insert/name-of-database/name-of-table"><pre class="notranslate"><code>/-/insert/name-of-database/name-of-table </code></pre></div> <p dir="auto">The JSON should look like this:</p> <div class="highlight highlight-source-json notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 5 } ]"><pre>[ { <span class="pl-ent">"id"</span>: <span class="pl-c1">1</span>, <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>Cleopaws<span class="pl-pds">"</span></span>, <span class="pl-ent">"age"</span>: <span class="pl-c1">5</span> }, { <span class="pl-ent">"id"</span>: <span class="pl-c1">2</span>, <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>Pancakes<span class="pl-pds">"</span></span>, <span class="pl-ent">"age"</span>: <span class="pl-c1">5</span> } ]</pre></div> <p dir="auto">The first time data is posted to the URL a table of that name will be created if it does not aready exist, with the desired columns.</p> <p dir="auto">You can specify which column should be used as the primary key using the <code>?pk=</code> URL argument.</p> <p dir="auto">Here's how to POST to a database and create a new table using the Python <code>requests</code> library:</p> <div class="highlight highlight-source-python notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import requests requests.post("http://localhost:8001/-/insert/data/dogs?pk=id", json=[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 4 } ])"><pre><span class="pl-k">import</span> <span class="pl-s1">requests</span> <span class="pl-s1">requests</span>.<span class="pl-en">post</span>(<span class="pl-s">"http://localhost:8001/-/insert/data/dogs?pk=id"</span>, <span class="pl-s1">json</span><span class="pl-c1">=</span>[ { <span class="pl-s">"id"</span>: <span class="pl-c1">1</span>, <span class="pl-s">"name"</span>: <span class="pl-s">"Cleopaws"</span>, <span class="pl-s">"age"</span>: <span class="pl-c1">5</span> }, { <span class="pl-s">"id"</span>: <span class="pl-c1">2</span>, <span class="pl-s">"name"</span>: <span class="pl-s">"Pancakes"</span>, <span class="pl-s">"age"</span>: <span class="pl-c1">4</span> } ])</pre></div> <p dir="auto">And here's how to do the same thing using <code>curl</code>:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="curl --request POST \ --data '[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 4 } ]' \ 'http://localhost:8001/-/insert/data/dogs?pk=id'"><pre class="notranslate"><code>curl --request POST \ --data '[ { "id": 1, "name": "Cleopaws", "age": 5 }, { "id": 2, "name": "Pancakes", "age": 4 } ]' \ 'http://localhost:8001/-/insert/data/dogs?pk=id' </code></pre></div> <p dir="auto">Or by piping in JSON like so:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="cat dogs.json | curl --request POST -d @- \ 'http://localhost:8001/-/insert/data/dogs?pk=id'"><pre class="notranslate"><code>cat dogs.json | curl --request POST -d @- \ 'http://localhost:8001/-/insert/data/dogs?pk=id' </code></pre></div> <h3 dir="auto"><a id="user-content-inserting-a-single-row" class="anchor" aria-hidden="true" href="#user-content-inserting-a-single-row"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Inserting a single row</h3> <p dir="auto">If you are inserting a single row you can optionally send it as a dictionary rather than a list with a single item:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="curl --request POST \ --data '{ "id": 1, "name": "Cleopaws", "age": 5 }' \ 'http://localhost:8001/-/insert/data/dogs?pk=id'"><pre class="notranslate"><code>curl --request POST \ --data '{ "id": 1, "name": "Cleopaws", "age": 5 }' \ 'http://localhost:8001/-/insert/data/dogs?pk=id' </code></pre></div> <h3 dir="auto"><a id="user-content-automatically-adding-new-columns" class="anchor" aria-hidden="true" href="#user-content-automatically-adding-new-columns"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Automatically adding new columns</h3> <p dir="auto">If you send data to an existing table with keys that are not reflected by the existing columns, you will get an HTTP 400 error with a JSON response like this:</p> <div class="highlight highlight-source-json notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="{ "status": 400, "error": "Unknown keys: 'foo'", "error_code": "unknown_keys" }"><pre>{ <span class="pl-ent">"status"</span>: <span class="pl-c1">400</span>, <span class="pl-ent">"error"</span>: <span class="pl-s"><span class="pl-pds">"</span>Unknown keys: 'foo'<span class="pl-pds">"</span></span>, <span class="pl-ent">"error_code"</span>: <span class="pl-s"><span class="pl-pds">"</span>unknown_keys<span class="pl-pds">"</span></span> }</pre></div> <p dir="auto">If you add <code>?alter=1</code> to the URL you are posting to any missing columns will be automatically added:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="curl --request POST \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs?alter=1'"><pre class="notranslate"><code>curl --request POST \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs?alter=1' </code></pre></div> <h2 dir="auto"><a id="user-content-upserting-data" class="anchor" aria-hidden="true" href="#user-content-upserting-data"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Upserting data</h2> <p dir="auto">An "upsert" operation can be used to partially update a record. With upserts you can send a subset of the keys and, if the ID matches the specified primary key, they will be used to update an existing record.</p> <p dir="auto">Upserts can be sent to the <code>/-/upsert</code> API endpoint.</p> <p dir="auto">This example will update the dog with ID=1's age from 5 to 7:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="curl --request POST \ --data '{ "id": 1, "age": 7 }' \ 'http://localhost:3322/-/upsert/data/dogs?pk=id'"><pre class="notranslate"><code>curl --request POST \ --data '{ "id": 1, "age": 7 }' \ 'http://localhost:3322/-/upsert/data/dogs?pk=id' </code></pre></div> <p dir="auto">Like the <code>/-/insert</code> endpoint, the <code>/-/upsert</code> endpoint can accept an array of objects too. It also supports the <code>?alter=1</code> option.</p> <h2 dir="auto"><a id="user-content-permissions-and-authentication" class="anchor" aria-hidden="true" href="#user-content-permissions-and-authentication"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Permissions and authentication</h2> <p dir="auto">This plugin defaults to denying all access, to help ensure people don't accidentally deploy it on the open internet in an unsafe configuration.</p> <p dir="auto">You can read about <a href="https://datasette.readthedocs.io/en/stable/authentication.html" rel="nofollow">Datasette's approach to authentication</a> in the Datasette manual.</p> <p dir="auto">You can install the <code>datasette-insert-unsafe</code> plugin to run in unsafe mode, where all access is allowed by default.</p> <p dir="auto">I recommend using this plugin in conjunction with <a href="https://github.com/simonw/datasette-auth-tokens">datasette-auth-tokens</a>, which provides a mechanism for making authenticated calls using API tokens.</p> <p dir="auto">You can then use <a href="https://datasette.readthedocs.io/en/stable/authentication.html#defining-permissions-with-allow-blocks" rel="nofollow">"allow" blocks</a> in the <code>datasette-insert</code> plugin configuration to specify which authenticated tokens are allowed to make use of the API.</p> <p dir="auto">Here's an example <code>metadata.json</code> file which restricts access to the <code>/-/insert</code> API to an API token defined in an <code>INSERT_TOKEN</code> environment variable:</p> <div class="highlight highlight-source-json notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="{ "plugins": { "datasette-insert": { "allow": { "bot": "insert-bot" } }, "datasette-auth-tokens": { "tokens": [ { "token": { "$env": "INSERT_TOKEN" }, "actor": { "bot": "insert-bot" } } ] } } }"><pre>{ <span class="pl-ent">"plugins"</span>: { <span class="pl-ent">"datasette-insert"</span>: { <span class="pl-ent">"allow"</span>: { <span class="pl-ent">"bot"</span>: <span class="pl-s"><span class="pl-pds">"</span>insert-bot<span class="pl-pds">"</span></span> } }, <span class="pl-ent">"datasette-auth-tokens"</span>: { <span class="pl-ent">"tokens"</span>: [ { <span class="pl-ent">"token"</span>: { <span class="pl-ent">"$env"</span>: <span class="pl-s"><span class="pl-pds">"</span>INSERT_TOKEN<span class="pl-pds">"</span></span> }, <span class="pl-ent">"actor"</span>: { <span class="pl-ent">"bot"</span>: <span class="pl-s"><span class="pl-pds">"</span>insert-bot<span class="pl-pds">"</span></span> } } ] } } }</pre></div> <p dir="auto">With this configuration in place you can start Datasette like this:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="INSERT_TOKEN=abc123 datasette data.db -m metadata.json"><pre class="notranslate"><code>INSERT_TOKEN=abc123 datasette data.db -m metadata.json </code></pre></div> <p dir="auto">You can now send data to the API using <code>curl</code> like this:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="curl --request POST \ -H "Authorization: Bearer abc123" \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs'"><pre class="notranslate"><code>curl --request POST \ -H "Authorization: Bearer abc123" \ --data '[ { "id": 3, "name": "Boris", "age": 1, "breed": "Husky" } ]' \ 'http://localhost:8001/-/insert/data/dogs' </code></pre></div> <p dir="auto">Or using the Python <code>requests</code> library like so:</p> <div class="highlight highlight-source-python notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="requests.post( "http://localhost:8001/-/insert/data/dogs", json={"id": 1, "name": "Cleopaws", "age": 5}, headers={"Authorization": "bearer abc123"}, )"><pre><span class="pl-s1">requests</span>.<span class="pl-en">post</span>( <span class="pl-s">"http://localhost:8001/-/insert/data/dogs"</span>, <span class="pl-s1">json</span><span class="pl-c1">=</span>{<span class="pl-s">"id"</span>: <span class="pl-c1">1</span>, <span class="pl-s">"name"</span>: <span class="pl-s">"Cleopaws"</span>, <span class="pl-s">"age"</span>: <span class="pl-c1">5</span>}, <span class="pl-s1">headers</span><span class="pl-c1">=</span>{<span class="pl-s">"Authorization"</span>: <span class="pl-s">"bearer abc123"</span>}, )</pre></div> <h3 dir="auto"><a id="user-content-finely-grained-permissions" class="anchor" aria-hidden="true" href="#user-content-finely-grained-permissions"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Finely grained permissions</h3> <p dir="auto">Using an <code>"allow"</code> block as described above grants full permission to the features enabled by the API.</p> <p dir="auto">The API implements several new Datasett permissions, which other plugins can use to make more finely grained decisions.</p> <p dir="auto">The full set of permissions are as follows:</p> <ul dir="auto"> <li><code>insert:all</code> - all permissions - this is used by the <code>"allow"</code> block described above. Argument: <code>database_name</code></li> <li><code>insert:insert-update</code> - the ability to insert data into an existing table, or to update data by its primary key. Arguments: <code>(database_name, table_name)</code></li> <li><code>insert:create-table</code> - the ability to create a new table. Argument: <code>database_name</code></li> <li><code>insert:alter-table</code> - the ability to add columns to an existing table (using <code>?alter=1</code>). Arguments: <code>(database_name, table_name)</code></li> </ul> <p dir="auto">You can use plugins like <a href="https://github.com/simonw/datasette-permissions-sql">datasette-permissions-sql</a> to hook into these more detailed permissions for finely grained control over what actions each authenticated actor can take.</p> <p dir="auto">Plugins that implement the <a href="https://datasette.readthedocs.io/en/stable/plugin_hooks.html#plugin-hook-permission-allowed" rel="nofollow">permission_allowed()</a> plugin hook can take full control over these permission decisions.</p> <h2 dir="auto"><a id="user-content-cors" class="anchor" aria-hidden="true" href="#user-content-cors"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>CORS</h2> <p dir="auto">If you start Datasette with the <code>datasette --cors</code> option the following HTTP headers will be added to resources served by this plugin:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Access-Control-Allow-Origin: * Access-Control-Allow-Headers: content-type,authorization Access-Control-Allow-Methods: POST"><pre class="notranslate"><code>Access-Control-Allow-Origin: * Access-Control-Allow-Headers: content-type,authorization Access-Control-Allow-Methods: POST </code></pre></div> <h2 dir="auto"><a id="user-content-development" class="anchor" aria-hidden="true" href="#user-content-development"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Development</h2> <p dir="auto">To set up this plugin locally, first checkout the code. Then create a new virtual environment:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="cd datasette-insert python3 -m venv venv source venv/bin/activate"><pre class="notranslate"><code>cd datasette-insert python3 -m venv venv source venv/bin/activate </code></pre></div> <p dir="auto">Now install the dependencies and tests:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="pip install -e '.[test]'"><pre class="notranslate"><code>pip install -e '.[test]' </code></pre></div> <p dir="auto">To run the tests:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="pytest"><pre class="notranslate"><code>pytest </code></pre></div> </article></div> | 1 | public | 0 | 0 |
Links from other tables
- 8 rows from repo in releases