From daa5653b62f2fe687f0791990eadccdfbf8034cd Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 16 Apr 2026 15:48:42 -0400 Subject: [PATCH 1/5] Return the updated board on PUT /:account_slug/boards/:id The JSON response was `204 No Content`, forcing clients to make a follow-up GET to observe their own write. The Smithy contract the SDKs are generated from already declares `UpdateBoard` returns a Board, so the server was out of sync with the documented shape. Render `show` for the JSON format so PUT returns the same payload as GET. The HTML format is unchanged. Updated test asserts the returned body matches the updated state. Updated API docs to show the 200 response shape. --- app/controllers/boards_controller.rb | 2 +- docs/api/sections/boards.md | 26 +++++++++++++++++++++- test/controllers/boards_controller_test.rb | 7 +++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 3d5485f1ff..80fdb01815 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -50,7 +50,7 @@ def update redirect_to root_path, notice: "Saved (you were removed from the board)" end end - format.json { head :no_content } + format.json { render :show } end end diff --git a/docs/api/sections/boards.md b/docs/api/sections/boards.md index fcce327628..c95d6ca94c 100644 --- a/docs/api/sections/boards.md +++ b/docs/api/sections/boards.md @@ -126,7 +126,31 @@ __Request:__ __Response:__ -Returns `204 No Content` on success. +Returns `200 OK` with the updated board in the same shape as `GET /:account_slug/boards/:board_id`: + +```json +{ + "id": "03f5v9zkft4hj9qq0lsn9ohcm", + "name": "Updated board name", + "all_access": false, + "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, + "url": "http://app.fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", + "creator": { + "id": "03f5v9zjw7pz8717a4no1h8a7", + "name": "David Heinemeier Hansson", + "role": "owner", + "active": true, + "email_address": "david@example.com", + "created_at": "2025-12-05T19:36:35.401Z", + "url": "http://app.fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7" + }, + "user_ids": [ + "03f5v9zppzlksuj4mxba2nbzn", + "03f5v9zjw7pz8717a4no1h8a7" + ] +} +``` ## `DELETE /:account_slug/boards/:board_id` diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index e4d4d3103b..91f37c596b 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -344,8 +344,13 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest put board_path(board), params: { board: { name: "Updated Name" } }, as: :json - assert_response :no_content + assert_response :success assert_equal "Updated Name", board.reload.name + + json = @response.parsed_body + assert_equal board.id, json["id"] + assert_equal "Updated Name", json["name"] + assert_equal board.creator.id, json["creator"]["id"] end test "destroy as JSON" do From 9a59e9d17a7961c8f7bc9355865047829a90542f Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 16 Apr 2026 15:57:42 -0400 Subject: [PATCH 2/5] Handle board update access loss for JSON --- app/controllers/boards_controller.rb | 8 +++++++- test/controllers/boards_controller_test.rb | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 80fdb01815..ed32731ca0 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -50,7 +50,13 @@ def update redirect_to root_path, notice: "Saved (you were removed from the board)" end end - format.json { render :show } + format.json do + if @board.accessible_to?(Current.user) + render :show + else + head :forbidden + end + end end end diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index 91f37c596b..9192d7b734 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -353,6 +353,20 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest assert_equal board.creator.id, json["creator"]["id"] end + test "update as JSON returns forbidden when user removes themselves from board" do + board = boards(:writebook) + + put board_path(board), params: { + board: { name: "Updated Name", all_access: false }, + user_ids: users(:david, :jz).pluck(:id) + }, as: :json + + assert_response :forbidden + assert_equal "Updated Name", board.reload.name + assert_not board.users.include?(users(:kevin)) + assert_empty response.body + end + test "destroy as JSON" do board = boards(:writebook) From 2ed85edb28b8adbe473b544b254fde1a8b8c984b Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 16 Apr 2026 15:59:08 -0400 Subject: [PATCH 3/5] Return no content after self-removal on JSON update --- app/controllers/boards_controller.rb | 2 +- test/controllers/boards_controller_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index ed32731ca0..3f242bcb8d 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -54,7 +54,7 @@ def update if @board.accessible_to?(Current.user) render :show else - head :forbidden + head :no_content end end end diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index 9192d7b734..896b504382 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -353,7 +353,7 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest assert_equal board.creator.id, json["creator"]["id"] end - test "update as JSON returns forbidden when user removes themselves from board" do + test "update as JSON returns no content when user removes themselves from board" do board = boards(:writebook) put board_path(board), params: { @@ -361,7 +361,7 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest user_ids: users(:david, :jz).pluck(:id) }, as: :json - assert_response :forbidden + assert_response :no_content assert_equal "Updated Name", board.reload.name assert_not board.users.include?(users(:kevin)) assert_empty response.body From bb597ee6715408dff0d8799db4557077459467f1 Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 16 Apr 2026 16:06:46 -0400 Subject: [PATCH 4/5] Update flat JSON board response test --- test/controllers/api/flat_json_params_test.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/controllers/api/flat_json_params_test.rb b/test/controllers/api/flat_json_params_test.rb index 77a74f454e..d8e8faa5e5 100644 --- a/test/controllers/api/flat_json_params_test.rb +++ b/test/controllers/api/flat_json_params_test.rb @@ -104,11 +104,13 @@ class FlatJsonParamsTest < ActionDispatch::IntegrationTest params: { name: "Flat board", auto_postpone_period_in_days: 7, public_description: "

Flat public desc

" }, as: :json - assert_response :no_content + assert_response :success board.reload assert_equal "Flat board", board.name assert_equal 7.days, board.entropy.auto_postpone_period assert_equal "Flat public desc", board.public_description.to_plain_text + assert_equal board.id, @response.parsed_body["id"] + assert_equal "Flat board", @response.parsed_body["name"] end test "create column with flat JSON" do From 3aca3bd5b13fe8fa09fdb3d033a5d0a8e78df485 Mon Sep 17 00:00:00 2001 From: Rob Zolkos Date: Thu, 16 Apr 2026 16:15:04 -0400 Subject: [PATCH 5/5] Document 204 response for board self-removal --- docs/api/sections/boards.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/sections/boards.md b/docs/api/sections/boards.md index c95d6ca94c..11e5a43fb7 100644 --- a/docs/api/sections/boards.md +++ b/docs/api/sections/boards.md @@ -152,6 +152,8 @@ Returns `200 OK` with the updated board in the same shape as `GET /:account_slug } ``` +If the update succeeds but removes the requesting user's own access to the board, the endpoint instead returns `204 No Content`. + ## `DELETE /:account_slug/boards/:board_id` Deletes a Board. Only board administrators can delete a board.