diff --git a/datasette/views/row.py b/datasette/views/row.py index bdd78ed4..71539f34 100644 --- a/datasette/views/row.py +++ b/datasette/views/row.py @@ -828,7 +828,7 @@ class RowUpdateView(BaseView): resolved.sql, resolved.params, truncate=True ) returned_row = results.dicts()[0] - result["row"] = returned_row + result["rows"] = [returned_row] await self.ds.track_event( UpdateRowEvent( diff --git a/docs/json_api.rst b/docs/json_api.rst index 54f0b6bd..b8287a64 100644 --- a/docs/json_api.rst +++ b/docs/json_api.rst @@ -1955,11 +1955,13 @@ The returned JSON will look like this: { "ok": true, - "row": { - "id": 1, - "title": "New title", - "other_column": "Will be present here too" - } + "rows": [ + { + "id": 1, + "title": "New title", + "other_column": "Will be present here too" + } + ] } Any errors will use the :ref:`standard error format `, with a ``400`` status code for a bad input or a ``403`` status code for an authentication or permission error. diff --git a/existing-api.md b/existing-api.md index abb82e2c..eb7b5c20 100644 --- a/existing-api.md +++ b/existing-api.md @@ -958,8 +958,8 @@ does not change the SQLite schema. `"Invalid keys: ..."`; write failures (bad column, constraint violation) → 400 with the message. - **Response** — 200 `{"ok": true}`; with `return: true`, - `{"ok": true, "row": {...}}` (singular `row`, unlike insert/upsert's - `rows`). Emits `update-row`. + `{"ok": true, "rows": [{...}]}` — a single-item list, matching + insert/upsert. Emits `update-row`. ### POST /\/\/\/-/delete diff --git a/stable-api-recommendations.md b/stable-api-recommendations.md index d1388549..a4dbb344 100644 --- a/stable-api-recommendations.md +++ b/stable-api-recommendations.md @@ -160,10 +160,11 @@ Endpoints disagree about the success envelope: (index.py:147-161); `/-/databases.json` returns an **array**; the database page returns `tables` as an array. Choose arrays-of-objects everywhere (objects-keyed-by-name break when names need ordering or pagination). -- Insert/upsert with `return: true` respond with `rows` (plural, list); row +- ~~Insert/upsert with `return: true` respond with `rows` (plural, list); row update with `return: true` responds with `row` (singular, object) (views/row.py:837-844). Pick one (`rows` everywhere, even for one row, - matches the read API). + matches the read API).~~ ✅ **Done** — row update now returns + `rows: [{...}]`. ### 2b. `_extra`/`_shape` support is uneven (P2) diff --git a/tests/test_api_write.py b/tests/test_api_write.py index a29f5c99..e3363db0 100644 --- a/tests/test_api_write.py +++ b/tests/test_api_write.py @@ -1481,9 +1481,9 @@ async def test_update_row(ds_write, input, expected_errors, use_return): assert response.json()["ok"] is True if not use_return: - assert "row" not in response.json() + assert "rows" not in response.json() else: - returned_row = response.json()["row"] + returned_row = response.json()["rows"][0] assert returned_row["id"] == pk for k, v in input.items(): assert returned_row[k] == v diff --git a/tests/test_error_shape.py b/tests/test_error_shape.py index 9d4a566c..29f4e8fc 100644 --- a/tests/test_error_shape.py +++ b/tests/test_error_shape.py @@ -507,3 +507,27 @@ async def test_query_html_without_sql_is_still_the_editor(ds_client): response = await ds_client.get("/fixtures/-/query") assert response.status_code == 200 assert response.headers["content-type"].startswith("text/html") + + +# Write API return:true responses use "rows" consistently + + +@pytest.mark.asyncio +async def test_row_update_return_uses_rows_list(ds_error_shape): + await ds_error_shape.client.post( + "/data/docs/-/insert", + json={"row": {"id": 1, "title": "One"}}, + headers={"Content-Type": "application/json"}, + actor={"id": "root"}, + ) + response = await ds_error_shape.client.post( + "/data/docs/1/-/update", + json={"update": {"title": "Updated"}, "return": True}, + headers={"Content-Type": "application/json"}, + actor={"id": "root"}, + ) + assert response.status_code == 200 + data = response.json() + assert data["ok"] is True + assert "row" not in data + assert data["rows"] == [{"id": 1, "title": "Updated"}] diff --git a/tests/test_table_html.py b/tests/test_table_html.py index 46d43c6c..86fb4254 100644 --- a/tests/test_table_html.py +++ b/tests/test_table_html.py @@ -1629,7 +1629,7 @@ async def test_row_update_sets_message(): json={"update": {"name": long_name}, "return": True}, ) assert response.status_code == 200 - assert response.json()["row"]["name"] == long_name + assert response.json()["rows"][0]["name"] == long_name assert ds.unsign(response.cookies["ds_messages"], "messages") == [ ["Updated row 1 ({})".format(truncated_name), ds.INFO] ]