{"html_url": "https://github.com/simonw/datasette/issues/1915#issuecomment-1331476246", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1915", "id": 1331476246, "node_id": "IC_kwDOBm6k_c5PXLcW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T00:04:35Z", "updated_at": "2022-11-30T00:08:24Z", "author_association": "OWNER", "body": "The new https://github.com/simonw/datasette-ephemeral-tables plugin is live now: https://latest.datasette.io/ephemeral - you have to navigate through https://latest.datasette.io/login-as-root first\r\n\r\nIt work! I created a table using https://latest.datasette.io/-/api#path=%2Fephemeral%2F-%2Fcreate&json=%7B%0A++%22table%22%3A+%22new_table%22%2C%0A++%22columns%22%3A+%5B%0A++++%7B%0A++++++%22name%22%3A+%22id%22%2C%0A++++++%22type%22%3A+%22integer%22%0A++++%7D%2C%0A++++%7B%0A++++++%22name%22%3A+%22name%22%2C%0A++++++%22type%22%3A+%22text%22%0A++++%7D%0A++%5D%2C%0A++%22pk%22%3A+%22id%22%0A%7D&method=POST\r\n\r\nThe table should vanish in a few minutes.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1468709531, "label": "Interactive demo of Datasette 1.0 write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1915#issuecomment-1331478611", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1915", "id": 1331478611, "node_id": "IC_kwDOBm6k_c5PXMBT", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T00:07:37Z", "updated_at": "2022-11-30T00:07:37Z", "author_association": "OWNER", "body": "Then I created an API token at https://latest.datasette.io/-/create-token and ran this:\r\n\r\n```\r\ncurl -XPOST 'https://latest.datasette.io/ephemeral/new_table/-/insert' \\\r\n -H 'Authorization: Bearer xxx' \\\r\n -H 'Content-Type: application/json' \\\r\n -d '{\"row\": {\"name\": \"NAME\"}}'\r\n```\r\nAnd it inserted a row into https://latest.datasette.io/ephemeral/new_table", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1468709531, "label": "Interactive demo of Datasette 1.0 write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1915#issuecomment-1331479328", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1915", "id": 1331479328, "node_id": "IC_kwDOBm6k_c5PXMMg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T00:08:41Z", "updated_at": "2022-11-30T00:08:41Z", "author_association": "OWNER", "body": "Five minute has now passed and https://latest.datasette.io/ephemeral/new_table is gone.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1468709531, "label": "Interactive demo of Datasette 1.0 write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1915#issuecomment-1331479606", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1915", "id": 1331479606, "node_id": "IC_kwDOBm6k_c5PXMQ2", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T00:09:06Z", "updated_at": "2022-11-30T00:09:06Z", "author_association": "OWNER", "body": "One last feature: I want to show an indication on the table page that the table has X seconds left to live.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1468709531, "label": "Interactive demo of Datasette 1.0 write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1917#issuecomment-1331644078", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1917", "id": 1331644078, "node_id": "IC_kwDOBm6k_c5PX0au", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T04:58:06Z", "updated_at": "2022-11-30T04:58:06Z", "author_association": "OWNER", "body": "The problem might actually be here:\r\n\r\nhttps://github.com/simonw/datasette/blob/9f5321ff1eca58c469a45cc406d7eb5ad05accbd/datasette/app.py#L280-L281\r\n\r\n`is_mutable` defaults to `True`, so this line should probably be:\r\n\r\n```python\r\n self.add_database(Database(self, is_mutable=False, is_memory=True), name=\"_memory\")\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469043836, "label": "Don't allow writable API to edit the `_memory` database"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1917#issuecomment-1331644751", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1917", "id": 1331644751, "node_id": "IC_kwDOBm6k_c5PX0lP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T04:59:22Z", "updated_at": "2022-11-30T04:59:22Z", "author_association": "OWNER", "body": "Yeah it looks like I introduced this bug here:\r\n\r\nhttps://github.com/simonw/datasette/commit/fb7e70d5e72a951efe4b29ad999d8915c032d021", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469043836, "label": "Don't allow writable API to edit the `_memory` database"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1916#issuecomment-1331651721", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1916", "id": 1331651721, "node_id": "IC_kwDOBm6k_c5PX2SJ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T05:10:27Z", "updated_at": "2022-11-30T05:10:27Z", "author_association": "OWNER", "body": "They should return 405 method not allowed with an `{\"ok\":false, \"error\": \"Method not allowed\"}` body.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469015001, "label": "GET requests against POST endpoints should not 500 error"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1919#issuecomment-1331657404", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1919", "id": 1331657404, "node_id": "IC_kwDOBm6k_c5PX3q8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T05:19:43Z", "updated_at": "2022-11-30T05:19:43Z", "author_association": "OWNER", "body": "This is the test: https://github.com/simonw/datasette/blob/8404b21556d133c89eda4bd1bf5335ed9a0785d6/tests/test_api_write.py#L342-L401\r\n\r\nI'm suspicious that there's a timing error of some sort but I can't think what it might be.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469062686, "label": "Intermittent `test_delete_row` test failure "}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1918#issuecomment-1331658629", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1918", "id": 1331658629, "node_id": "IC_kwDOBm6k_c5PX3-F", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T05:21:51Z", "updated_at": "2022-11-30T05:21:51Z", "author_association": "OWNER", "body": "Much better:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469044738, "label": "API explorer should list mutable databases first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1605#issuecomment-1331694246", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1605", "id": 1331694246, "node_id": "IC_kwDOBm6k_c5PYAqm", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T06:18:41Z", "updated_at": "2022-11-30T06:18:41Z", "author_association": "OWNER", "body": "Those sounds to me like they should be promoted to documented, supported internals.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1108671952, "label": "Scripted exports"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332492092", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332492092, "node_id": "IC_kwDOBm6k_c5PbDc8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T17:17:21Z", "updated_at": "2022-11-30T17:17:21Z", "author_association": "OWNER", "body": "I tried running `fetch()` with a POST from a separate domain and got a browser error because it did a GET against the `/db/-/create` endpoint and the 405 method not supported response did not include the CORS headers.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332493004", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332493004, "node_id": "IC_kwDOBm6k_c5PbDrM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T17:18:10Z", "updated_at": "2022-11-30T17:18:10Z", "author_association": "OWNER", "body": "Here's why:\r\n\r\nhttps://github.com/simonw/datasette/blob/4ddd77e51254bda3bac990ea662bac2e6b29c5e0/datasette/views/base.py#L71-L79\r\n\r\nThat's code in `BaseView` - but it turns out the code that adds CORS headers is in the `DataView` subclass of that (which the various write API endpoints do not use).\r\n\r\nhttps://github.com/simonw/datasette/blob/4ddd77e51254bda3bac990ea662bac2e6b29c5e0/datasette/views/base.py#L158-L162", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332504654", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332504654, "node_id": "IC_kwDOBm6k_c5PbGhO", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T17:27:39Z", "updated_at": "2022-11-30T17:27:39Z", "author_association": "OWNER", "body": "I'll test this once it's deployed to https://latest.datasette.io/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332561059", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332561059, "node_id": "IC_kwDOBm6k_c5PbUSj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T18:19:20Z", "updated_at": "2022-11-30T18:19:20Z", "author_association": "OWNER", "body": "Two test failures:\r\n```\r\n____________________________ test_homepage_options _____________________________\r\n[gw0] linux -- Python 3.11.0 /opt/hostedtoolcache/Python/3.11.0/x64/bin/python\r\n\r\napp_client = \r\n\r\n def test_homepage_options(app_client):\r\n response = app_client.get(\"/\", method=\"OPTIONS\")\r\n> assert response.status == 405\r\nE assert 200 == 405\r\nE + where 200 = .status\r\n\r\n/home/runner/work/datasette/datasette/tests/test_html.py:58: AssertionError\r\n______________________ test_client_methods[options-/-405] ______________________\r\n[gw1] linux -- Python 3.11.0 /opt/hostedtoolcache/Python/3.11.0/x64/bin/python\r\n\r\ndatasette = \r\nmethod = 'options', path = '/', expected_status = 405\r\n\r\n @pytest.mark.asyncio\r\n @pytest.mark.parametrize(\r\n \"method,path,expected_status\",\r\n [\r\n (\"get\", \"/\", 200),\r\n (\"options\", \"/\", 405),\r\n (\"head\", \"/\", 200),\r\n (\"put\", \"/\", 405),\r\n (\"patch\", \"/\", 405),\r\n (\"delete\", \"/\", 405),\r\n ],\r\n )\r\n async def test_client_methods(datasette, method, path, expected_status):\r\n client_method = getattr(datasette.client, method)\r\n response = await client_method(path)\r\n assert isinstance(response, httpx.Response)\r\n> assert response.status_code == expected_status\r\nE assert 200 == 405\r\nE + where 200 = .status_code\r\n\r\n/home/runner/work/datasette/datasette/tests/test_internals_datasette_client.py:29: AssertionError\r\n=============================== warnings summary ===============================\r\ntests/test_cli.py::test_inspect_cli_writes_to_file\r\ntests/test_cli.py::test_inspect_cli\r\n /home/runner/work/datasette/datasette/datasette/cli.py:163: DeprecationWarning: There is no current event loop\r\n loop = asyncio.get_event_loop()\r\n\r\ntests/test_cli_serve_get.py: 2 warnings\r\ntests/test_cli.py: 12 warnings\r\ntests/test_crossdb.py: 1 warning\r\n /home/runner/work/datasette/datasette/datasette/cli.py:591: DeprecationWarning: There is no current event loop\r\n asyncio.get_event_loop().run_until_complete(ds.invoke_startup())\r\n\r\ntests/test_cli_serve_get.py: 2 warnings\r\ntests/test_cli.py: 12 warnings\r\ntests/test_crossdb.py: 1 warning\r\n /home/runner/work/datasette/datasette/datasette/cli.py:594: DeprecationWarning: There is no current event loop\r\n asyncio.get_event_loop().run_until_complete(check_databases(ds))\r\n\r\n-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html\r\n=========================== short test summary info ============================\r\nFAILED tests/test_html.py::test_homepage_options - assert 200 == 405\r\n + where 200 = .status\r\nFAILED tests/test_internals_datasette_client.py::test_client_methods[options-/-405] - assert 200 == 405\r\n + where 200 = .status_code\r\n====== 2 failed, 1195 passed, 1 skipped, 32 warnings in 191.06s (0:03:11) ======\r\nError: Process completed with exit code 1.\r\n```\r\nOn reading https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS I feel like I should be a bit more thoughtful about how I treat OPTIONS - maybe it should work for every URL on the site, but return a `204 No Content` header?\r\n\r\nComparing a few different sites:\r\n\r\n```\r\n~ % curl -X OPTIONS https://www.google.com/ -i\r\nHTTP/2 405 \r\nallow: GET, HEAD\r\ndate: Wed, 30 Nov 2022 18:18:15 GMT\r\ncontent-type: text/html; charset=UTF-8\r\nserver: gws\r\ncontent-length: 1592\r\nx-xss-protection: 0\r\nx-frame-options: SAMEORIGIN\r\nalt-svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"\r\n\r\n\r\n\r\n \r\n \r\n Error 405 (Method Not Allowed)!!1\r\n \r\n \r\n

405. That\u2019s an error.\r\n

The request method OPTIONS is inappropriate for the URL /. That\u2019s all we know.\r\n~ % curl -X OPTIONS https://www.mozilla.org/ -i\r\nHTTP/2 405 \r\ncontent-type: text/html; charset=utf-8\r\ncontent-length: 0\r\nserver: meinheld/1.0.2\r\ndate: Wed, 30 Nov 2022 18:18:38 GMT\r\nallow: GET, HEAD\r\nx-frame-options: DENY\r\ncontent-security-policy: child-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; connect-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com region1.google-analytics.com logs.convertexperiments.com 1003350.metrics.convertexperiments.com 1003343.metrics.convertexperiments.com sentry.prod.mozaws.net o1069899.sentry.io o1069899.ingest.sentry.io https://accounts.firefox.com/ stage.cjms.nonprod.cloudops.mozgcp.net cjms.services.mozilla.com; frame-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; script-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com www.google-analytics.com tagmanager.google.com www.youtube.com s.ytimg.com cdn-3.convertexperiments.com app.convert.com data.track.convertexperiments.com 1003350.track.convertexperiments.com 1003343.track.convertexperiments.com; img-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com data: mozilla.org www.googletagmanager.com www.google-analytics.com adservice.google.com adservice.google.de adservice.google.dk creativecommons.org cdn-3.convertexperiments.com logs.convertexperiments.com images.ctfassets.net ad.doubleclick.net; style-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com 'unsafe-inline' app.convert.com; default-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com; font-src 'self'\r\ncache-control: max-age=600\r\nexpires: Wed, 30 Nov 2022 18:28:38 GMT\r\nx-backend-server: bedrock-prod-web-b95bc569d-grd25.iowa-a\r\nstrict-transport-security: max-age=31536000\r\nx-content-type-options: nosniff\r\nx-xss-protection: 1; mode=block\r\nreferrer-policy: strict-origin-when-cross-origin\r\nvia: 1.1 google, 1.1 6c90b631453c435bd0022caa657b67e8.cloudfront.net (CloudFront)\r\nx-cache: Error from cloudfront\r\nx-amz-cf-pop: SFO5-P2\r\nx-amz-cf-id: A6-9mLztaE2tz840CbV9bXYiBMZRKEamDj6jGGEl1U7sg8egWfsDqg==\r\n\r\n~ % curl -X OPTIONS https://example.com -i \r\nHTTP/2 200 \r\nallow: OPTIONS, GET, HEAD, POST\r\ncache-control: max-age=604800\r\ncontent-type: text/html; charset=UTF-8\r\ndate: Wed, 30 Nov 2022 18:18:59 GMT\r\nexpires: Wed, 07 Dec 2022 18:18:59 GMT\r\nserver: EOS (vny/0451)\r\ncontent-length: 0\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332561813", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332561813, "node_id": "IC_kwDOBm6k_c5PbUeV", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T18:20:05Z", "updated_at": "2022-11-30T18:20:05Z", "author_association": "OWNER", "body": "Weird, GitHub reply with a 404!\r\n```\r\n~ % curl -X OPTIONS https://github.com/ -i\r\nHTTP/2 404 \r\nserver: GitHub.com\r\ndate: Wed, 30 Nov 2022 18:19:39 GMT\r\ncontent-type: text/html; charset=utf-8\r\ncontent-length: 0\r\nstrict-transport-security: max-age=31536000; includeSubdomains; preload\r\nx-frame-options: deny\r\nx-content-type-options: nosniff\r\nx-xss-protection: 0\r\nreferrer-policy: origin-when-cross-origin, strict-origin-when-cross-origin\r\ncontent-security-policy: default-src 'none'; base-uri 'self'; block-all-mixed-content; child-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com objects-origin.githubusercontent.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com cdn.optimizely.com logx.optimizely.com/v1/events; font-src github.githubassets.com; form-action 'self' github.com gist.github.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com objects-origin.githubusercontent.com secured-user-images.githubusercontent.com/ opengraph.githubassets.com github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; worker-src github.com/assets-cdn/worker/ gist.github.com/assets-cdn/worker/\r\nvary: Accept-Encoding, Accept, X-Requested-With\r\nx-github-request-id: DD6B:5ACA:102E8A6:1164A99:63879EBB\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332572453", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332572453, "node_id": "IC_kwDOBm6k_c5PbXEl", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T18:30:38Z", "updated_at": "2022-11-30T18:30:54Z", "author_association": "OWNER", "body": "Started a conversation about how OPTIONS should work on Mastodon: https://fedi.simonwillison.net/@simon/109434148676475291", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332580395", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332580395, "node_id": "IC_kwDOBm6k_c5PbZAr", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T18:38:22Z", "updated_at": "2022-11-30T18:38:22Z", "author_association": "OWNER", "body": "> [@simon](https://fedi.simonwillison.net/@simon) IMO, it should always be a 2XX series response, typically with no content & an extra `Allow` header with a list of HTTP verbs it responds to.\r\n\r\nhttps://mastodon.social/@daniellindsley/109434186252099323", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332585861", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332585861, "node_id": "IC_kwDOBm6k_c5PbaWF", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T18:43:46Z", "updated_at": "2022-11-30T18:43:46Z", "author_association": "OWNER", "body": "Here's what Django Rest Framework does: https://github.com/encode/django-rest-framework/blob/1ae812ea209392ad76cc5d2f35f9f7fb337f63e4/rest_framework/views.py#L514-L521\r\n\r\n```python\r\n def options(self, request, *args, **kwargs):\r\n \"\"\"\r\n Handler method for HTTP 'OPTIONS' request.\r\n \"\"\"\r\n if self.metadata_class is None:\r\n return self.http_method_not_allowed(request, *args, **kwargs)\r\n data = self.metadata_class().determine_metadata(request, self)\r\n return Response(data, status=status.HTTP_200_OK)\r\n```\r\nThat default `determine_metadata` method looks like this: https://github.com/encode/django-rest-framework/blob/1ae812ea209392ad76cc5d2f35f9f7fb337f63e4/rest_framework/metadata.py#L61-L71\r\n\r\n```python\r\n def determine_metadata(self, request, view):\r\n metadata = OrderedDict()\r\n metadata['name'] = view.get_view_name()\r\n metadata['description'] = view.get_view_description()\r\n metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes]\r\n metadata['parses'] = [parser.media_type for parser in view.parser_classes]\r\n if hasattr(view, 'get_serializer'):\r\n actions = self.determine_actions(request, view)\r\n if actions:\r\n metadata['actions'] = actions\r\n return metadata\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332688245", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332688245, "node_id": "IC_kwDOBm6k_c5PbzV1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T20:15:08Z", "updated_at": "2022-11-30T20:15:08Z", "author_association": "OWNER", "body": "Still getting a CORS error:\r\n\r\n\"image\"\r\n\r\nMy hunch is this is because I'm not sending `Access-Control-Allow-Methods: GET,HEAD,POST`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332689547", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332689547, "node_id": "IC_kwDOBm6k_c5PbzqL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T20:16:21Z", "updated_at": "2022-11-30T20:16:46Z", "author_association": "OWNER", "body": "That notebook:\r\n```javascript\r\nviewof token = Inputs.text({\r\n label: \"Your API token\"\r\n})\r\n```\r\n```javascript\r\nviewof createResponse = Inputs.button(\"Create table\", {\r\n value: null,\r\n reduce: async () => {\r\n const response = await fetch(\r\n \"https://latest.datasette.io/ephemeral/-/create\",\r\n {\r\n method: \"POST\",\r\n mode: \"cors\",\r\n headers: {\r\n Authorization: `Bearer {$token}`,\r\n \"Content-Type\": \"application/json\"\r\n },\r\n body: JSON.stringify({\r\n table: \"my_new_table\",\r\n row: {\r\n task: \"Demonstrate a JSON creation from another domain\"\r\n }\r\n })\r\n }\r\n );\r\n return await response.json();\r\n }\r\n})\r\n```\r\nBased on this tip: https://talk.observablehq.com/t/best-pattern-for-click-here-to-submit-your-results-to-an-api-backend/7353/3", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332698636", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332698636, "node_id": "IC_kwDOBm6k_c5Pb14M", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T20:25:50Z", "updated_at": "2022-11-30T20:25:50Z", "author_association": "OWNER", "body": "I just shipped this:\r\n\r\n Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS\r\n\r\nI'll try this out on `latest.datasette.io` - but I need to research more to check if this is a safe thing to do or not.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1923#issuecomment-1332835664", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1923", "id": 1332835664, "node_id": "IC_kwDOBm6k_c5PcXVQ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T22:50:10Z", "updated_at": "2022-11-30T22:51:25Z", "author_association": "OWNER", "body": "https://stackoverflow.com/questions/74490465/github-actions-failing-for-setup-gcloud/74562526#74562526 suggests setting `version: '318.0.0'` of `google-github-actions/setup-gcloud`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1470320227, "label": "latest.datasette.io Cloud Run deploys failing"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1923#issuecomment-1332842435", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1923", "id": 1332842435, "node_id": "IC_kwDOBm6k_c5PcY_D", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T22:58:33Z", "updated_at": "2022-11-30T22:58:33Z", "author_association": "OWNER", "body": "https://stackoverflow.com/questions/74490465/github-actions-failing-for-setup-gcloud/74562740#74562740 suggests trying Python 3.9.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1470320227, "label": "latest.datasette.io Cloud Run deploys failing"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1923#issuecomment-1332851215", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1923", "id": 1332851215, "node_id": "IC_kwDOBm6k_c5PcbIP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T23:04:56Z", "updated_at": "2022-11-30T23:04:56Z", "author_association": "OWNER", "body": "That fixed it.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1470320227, "label": "latest.datasette.io Cloud Run deploys failing"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332855687", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332855687, "node_id": "IC_kwDOBm6k_c5PccOH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T23:09:31Z", "updated_at": "2022-11-30T23:09:31Z", "author_association": "OWNER", "body": "Still getting a CORS header.\r\n\r\nI tried it in Chrome, which outputs helpful messages to the console:\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1922#issuecomment-1332903011", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1922", "id": 1332903011, "node_id": "IC_kwDOBm6k_c5Pcnxj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-11-30T23:45:29Z", "updated_at": "2022-11-30T23:45:29Z", "author_association": "OWNER", "body": "That worked for the preflight request - got this now:\r\n\r\n\"image\"\r\n\r\nSo it looks like error responses (in this case for permission denied) are missing CORS headers.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1469973742, "label": "Make sure CORS works for write APIs"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1605#issuecomment-1332310772", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1605", "id": 1332310772, "node_id": "IC_kwDOBm6k_c5PaXL0", "user": {"value": 25778, "label": "eyeseast"}, "created_at": "2022-11-30T15:06:37Z", "updated_at": "2022-11-30T15:06:37Z", "author_association": "CONTRIBUTOR", "body": "I'll add issues for both and do a documentation PR.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1108671952, "label": "Scripted exports"}, "performed_via_github_app": null}