Skip to content

Commit 7cf9faf

Browse files
committed
404 if project not found in Api::SchoolProjectsController
Previously when a request was made to either of the actions in `Api::SchoolProjectsController` for a project that does not exist, the following exception was raised: NoMethodError: undefined method `school_project' for nil:NilClass Unhandled exceptions are rescued by the default `ActionDispatch::ShowExceptions` middleware [1]. In turn this uses the default exceptions app, `ActionDispatch::PublicExceptions` [2] to render a static HTML error page from `public/500.html` in this case. Since this is an API endpoint, ideally it should always respond with JSON in the response body, otherwise it makes writing client code a lot harder. Indeed my actual motivation for fixing this is, because we've been seeing JSON:ParseError exceptions raised in experience-cs [3]. I've fixed the problem by using `Project.find_by!` instead of `Project.find_by`. This means that if the project is not found, a `ActiveRecord::RecordNotFound` exception is raised. This in turn is rescued by the `rescue_from` block in `ApiController` [4] and the action responds with a 404 Not Found head response, i.e. with no body. This head response seems to be handled OK by the HTTParty code in experience-cs [5] and thus it fixes the problem I was trying to address. The equivalent client-side code in editor-standalone [6] was already throwing an AxisError exception due to the 500 response code (not being a 2XX code [7]) and it will continue to throw the same exception, albeit with a payload reflecting the response code being 404 instead of 500 and the body being empty instead of being the Rails 500 HTML error page. There is no specific error-handling code in editor-standalone catching this exception, so I assume it's just relying on the generic error handling for the React app. Thus I think the user-facing behaviour should be unchanged. [1]: https://api.rubyonrails.org/v7.1.3.4/classes/ActionDispatch/ShowExceptions.html [2]: https://api.rubyonrails.org/v7.1.3.4/classes/ActionDispatch/PublicExceptions.html [3]: https://raspberrypifoundation.sentry.io/issues/6708738500/ [4]: https://github.com/RaspberryPiFoundation/editor-api/blob/c37ab30714edeb08beaa1929970772545f38e93d/app/controllers/api_controller.rb#L9 [5]: https://github.com/RaspberryPiFoundation/experience-cs/blob/7b559bef916e0a32b3174f2391dcc587a8c4fe3b/lib/editor_api/client.rb [6]: https://github.com/RaspberryPiFoundation/editor-standalone/blob/f1286121460e79da7ffa7431487a2fb0872f2ae5/src/utils/apiCallHandler/projects.js#L71-L91 [7]: https://axios-http.com/docs/handling_errors
1 parent c37ab30 commit 7cf9faf

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

app/controllers/api/school_projects_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ class SchoolProjectsController < ApiController
66
load_and_authorize_resource :project
77

88
def show_finished
9-
@school_project = Project.find_by(identifier: params[:id]).school_project
9+
@school_project = Project.find_by!(identifier: params[:id]).school_project
1010
authorize! :show_finished, @school_project
1111
render :finished, formats: [:json], status: :ok
1212
end
1313

1414
def set_finished
15-
project = Project.find_by(identifier: params[:id])
15+
project = Project.find_by!(identifier: params[:id])
1616
@school_project = project.school_project
1717
authorize! :set_finished, @school_project
1818
result = SchoolProject::SetFinished.call(school_project: @school_project, finished: params[:finished])

spec/requests/school_projects/set_finished_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,14 @@
8080
expect(teacher_project.school_project.finished).to be_falsey
8181
end
8282
end
83+
84+
context 'when project does not exist' do
85+
before do
86+
put('/api/projects/does-not-exist/finished', headers:, params: { finished: false })
87+
end
88+
89+
it 'returns not found response' do
90+
expect(response).to have_http_status(:not_found)
91+
end
92+
end
8393
end

spec/requests/school_projects/show_finished_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,15 @@
5252
expect(response).to have_http_status(:forbidden)
5353
end
5454
end
55+
56+
context 'when project does not exist' do
57+
before do
58+
get('/api/projects/does-not-exist/finished', headers:)
59+
end
60+
61+
it 'returns not found response' do
62+
expect(response).to have_http_status(:not_found)
63+
end
64+
end
5565
end
5666
end

0 commit comments

Comments
 (0)