diff --git a/datasette/views/table.py b/datasette/views/table.py index 298b63c750..8e6d8e4a1f 100644 --- a/datasette/views/table.py +++ b/datasette/views/table.py @@ -1236,7 +1236,8 @@ async def post(self, request): request.actor, "drop-table", resource=(database_name, table_name) ): return _error(["Permission denied"], 403) - + if not db.is_mutable: + return _error(["Database is immutable"], 403) confirm = False try: data = json.loads(await request.post_body()) @@ -1248,6 +1249,8 @@ async def post(self, request): return Response.json( { "ok": True, + "database": database_name, + "table": table_name, "row_count": ( await db.execute("select count(*) from [{}]".format(table_name)) ).single_value(), diff --git a/docs/json_api.rst b/docs/json_api.rst index 61e14589f9..64a58ce3d0 100644 --- a/docs/json_api.rst +++ b/docs/json_api.rst @@ -578,6 +578,8 @@ Without a POST body this will return a status ``200`` with a note about how many { "ok": true, + "database": "", + "table": "", "row_count": 5, "message": "Pass \"confirm\": true to confirm" } diff --git a/tests/test_api_write.py b/tests/test_api_write.py index 98792d031e..6ae3ac3f8c 100644 --- a/tests/test_api_write.py +++ b/tests/test_api_write.py @@ -8,10 +8,15 @@ def ds_write(tmp_path_factory): db_directory = tmp_path_factory.mktemp("dbs") db_path = str(db_directory / "data.db") - db = sqlite3.connect(str(db_path)) - db.execute("vacuum") - db.execute("create table docs (id integer primary key, title text, score float)") - ds = Datasette([db_path]) + db_path_immutable = str(db_directory / "immutable.db") + db1 = sqlite3.connect(str(db_path)) + db2 = sqlite3.connect(str(db_path_immutable)) + for db in (db1, db2): + db.execute("vacuum") + db.execute( + "create table docs (id integer primary key, title text, score float)" + ) + ds = Datasette([db_path], immutables=[db_path_immutable]) yield ds db.close() @@ -339,7 +344,9 @@ async def test_delete_row(ds_write, scenario): @pytest.mark.asyncio -@pytest.mark.parametrize("scenario", ("no_token", "no_perm", "bad_table", "has_perm")) +@pytest.mark.parametrize( + "scenario", ("no_token", "no_perm", "bad_table", "has_perm", "immutable") +) async def test_drop_table(ds_write, scenario): if scenario == "no_token": token = "bad_token" @@ -351,7 +358,10 @@ async def test_drop_table(ds_write, scenario): await ds_write.get_database("data").execute_write( "insert into docs (id, title) values (1, 'Row 1')" ) - path = "/data/{}/-/drop".format("docs" if scenario != "bad_table" else "bad_table") + path = "/{database}/{table}/-/drop".format( + database="immutable" if scenario == "immutable" else "data", + table="docs" if scenario != "bad_table" else "bad_table", + ) response = await ds_write.client.post( path, headers={ @@ -366,17 +376,20 @@ async def test_drop_table(ds_write, scenario): else 404 ) assert response.json()["ok"] is False - assert ( - response.json()["errors"] == ["Permission denied"] - if scenario == "no_token" - else ["Table not found: bad_table"] - ) + expected_error = "Permission denied" + if scenario == "bad_table": + expected_error = "Table not found: bad_table" + elif scenario == "immutable": + expected_error = "Database is immutable" + assert response.json()["errors"] == [expected_error] assert (await ds_write.client.get("/data/docs")).status_code == 200 else: # It should show a confirmation page assert response.status_code == 200 assert response.json() == { "ok": True, + "database": "data", + "table": "docs", "row_count": 1, "message": 'Pass "confirm": true to confirm', }