diff --git a/Gemfile b/Gemfile index 31d42df9582d..127491dfb297 100644 --- a/Gemfile +++ b/Gemfile @@ -350,7 +350,7 @@ end gem "bootsnap", "~> 1.18.0", require: false # API gems -gem "grape", "~> 2.0.0" +gem "grape", "~> 2.1.0" gem "grape_logging", "~> 1.8.4" gem "roar", "~> 1.2.0" diff --git a/Gemfile.lock b/Gemfile.lock index b7b025af52c4..a817cb875e63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -595,13 +595,12 @@ GEM multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - grape (2.0.0) - activesupport (>= 5) - builder + grape (2.1.0) + activesupport (>= 6) dry-types (>= 1.1) - mustermann-grape (~> 1.0.0) - rack (>= 1.3.0) - rack-accept + mustermann-grape (~> 1.1.0) + rack (>= 2) + zeitwerk grape_logging (1.8.4) grape rack @@ -733,7 +732,7 @@ GEM multi_json (1.15.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) - mustermann-grape (1.0.2) + mustermann-grape (1.1.0) mustermann (>= 1.0.0) mutex_m (0.2.0) net-http (0.4.1) @@ -849,8 +848,6 @@ GEM raabro (1.4.0) racc (1.8.0) rack (2.2.9) - rack-accept (0.4.5) - rack (>= 0.4) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.2) @@ -1216,7 +1213,7 @@ DEPENDENCIES good_job (= 3.26.2) google-apis-gmail_v1 googleauth - grape (~> 2.0.0) + grape (~> 2.1.0) grape_logging (~> 1.8.4) grids! html-pipeline (~> 2.14.0) diff --git a/lib/api/root.rb b/lib/api/root.rb index 2fa907550dab..fe64cde5e91c 100644 --- a/lib/api/root.rb +++ b/lib/api/root.rb @@ -35,7 +35,7 @@ class Root < ::API::RootAPI parser :json, API::V3::Parser.new - error_representer ::API::V3::Errors::ErrorRepresenter, "hal+json" + error_representer ::API::V3::Errors::ErrorRepresenter, "application/hal+json; charset=utf-8" authentication_scope OpenProject::Authentication::Scope::API_V3 OpenProject::Authentication.handle_failure(scope: API_V3) do |warden, _opts| @@ -44,7 +44,7 @@ class Root < ::API::RootAPI api_error = ::API::Errors::Unauthenticated.new error_message representer = ::API::V3::Errors::ErrorRepresenter.new api_error - e.error_response status: 401, message: representer.to_json, headers: warden.headers, log: false + e.error! representer.to_json, 401, warden.headers end version "v3", using: :path do diff --git a/lib/api/utilities/grape_helper.rb b/lib/api/utilities/grape_helper.rb index 26a364e5bd62..270e6227eb3c 100644 --- a/lib/api/utilities/grape_helper.rb +++ b/lib/api/utilities/grape_helper.rb @@ -38,6 +38,10 @@ def initialize(env) @env = env @options = {} end + + def error!(message, status = nil, headers = nil, backtrace = nil, original_exception = nil) + super + end end def grape_error_for(env, api) @@ -67,7 +71,7 @@ def default_error_response(headers, log) original_exception = $! representer = error_representer.new e resp_headers = instance_exec &headers - env["api.format"] = error_content_type + resp_headers["Content-Type"] = error_content_type if log == true OpenProject.logger.error original_exception, reference: :APIv3 @@ -75,7 +79,7 @@ def default_error_response(headers, log) log.call(original_exception) end - error_response status: e.code, message: representer.to_json, headers: resp_headers + error!(representer.to_json, e.code, resp_headers) } end end diff --git a/lib/api/v3/root.rb b/lib/api/v3/root.rb index 1d2e22b576cf..8deeb4b61d64 100644 --- a/lib/api/v3/root.rb +++ b/lib/api/v3/root.rb @@ -101,6 +101,12 @@ class Root < ::API::OpenProjectAPI API::OpenAPI.spec.to_yaml end + + # Catch all unknown routes (therefore have it at the end of the file) + # and return a properly formatted 404 error. + route :any, "*path" do + raise API::Errors::NotFound + end end end end diff --git a/modules/bim/app/controllers/bim/bcf/api/root.rb b/modules/bim/app/controllers/bim/bcf/api/root.rb index aac9d5a7f132..3fdcadc37d92 100644 --- a/modules/bim/app/controllers/bim/bcf/api/root.rb +++ b/modules/bim/app/controllers/bim/bcf/api/root.rb @@ -38,7 +38,7 @@ class Root < ::API::RootAPI default_format :json - error_representer ::Bim::Bcf::API::V2_1::Errors::ErrorRepresenter, :json + error_representer ::Bim::Bcf::API::V2_1::Errors::ErrorRepresenter, "application/json; charset=utf-8" error_formatter :json, ::Bim::Bcf::API::ErrorFormatter::Json authentication_scope OpenProject::Authentication::Scope::BCF_V2_1 diff --git a/modules/budgets/spec/requests/api/v3/budgets/budget_resource_spec.rb b/modules/budgets/spec/requests/api/v3/budgets/budget_resource_spec.rb index 84c1815c1693..9e769c15723e 100644 --- a/modules/budgets/spec/requests/api/v3/budgets/budget_resource_spec.rb +++ b/modules/budgets/spec/requests/api/v3/budgets/budget_resource_spec.rb @@ -61,9 +61,7 @@ context "invalid id" do let(:get_path) { api_v3_paths.budget "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - end + it_behaves_like "not found" end end diff --git a/modules/costs/spec/requests/api/cost_entries/cost_entry_resource_spec.rb b/modules/costs/spec/requests/api/cost_entries/cost_entry_resource_spec.rb index 5d7c81755513..094ffd7e7479 100644 --- a/modules/costs/spec/requests/api/cost_entries/cost_entry_resource_spec.rb +++ b/modules/costs/spec/requests/api/cost_entries/cost_entry_resource_spec.rb @@ -62,9 +62,7 @@ context "invalid id" do let(:get_path) { api_v3_paths.cost_type "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - end + it_behaves_like "not found" end end diff --git a/modules/costs/spec/requests/api/cost_types/cost_type_resource_spec.rb b/modules/costs/spec/requests/api/cost_types/cost_type_resource_spec.rb index 763b914271eb..f66ce9964af5 100644 --- a/modules/costs/spec/requests/api/cost_types/cost_type_resource_spec.rb +++ b/modules/costs/spec/requests/api/cost_types/cost_type_resource_spec.rb @@ -67,9 +67,7 @@ context "invalid id" do let(:get_path) { api_v3_paths.cost_type "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - end + it_behaves_like "not found" end end diff --git a/modules/meeting/spec/requests/api/v3/meetings/meetings_resource_spec.rb b/modules/meeting/spec/requests/api/v3/meetings/meetings_resource_spec.rb index 5d95cc0309ff..094095c8939d 100644 --- a/modules/meeting/spec/requests/api/v3/meetings/meetings_resource_spec.rb +++ b/modules/meeting/spec/requests/api/v3/meetings/meetings_resource_spec.rb @@ -69,9 +69,7 @@ context "when invalid id" do let(:get_path) { api_v3_paths.budget "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - end + it_behaves_like "not found" end end diff --git a/spec/features/work_packages/navigation_spec.rb b/spec/features/work_packages/navigation_spec.rb index 6364eed3f47c..3375e552ec36 100644 --- a/spec/features/work_packages/navigation_spec.rb +++ b/spec/features/work_packages/navigation_spec.rb @@ -259,7 +259,7 @@ visit "/projects/#{project.identifier}/work_packages?#{url_query}" wp_table.expect_toast message: "Your view is erroneous and could not be processed.", type: :error - expect(page).to have_css "li", text: "Bad request: id is invalid" + expect(page).to have_css "li", text: "The requested resource could not be found" end end end diff --git a/spec/requests/api/v3/category_resource_spec.rb b/spec/requests/api/v3/category_resource_spec.rb index b4ad3aaea0eb..b994ec4ce772 100644 --- a/spec/requests/api/v3/category_resource_spec.rb +++ b/spec/requests/api/v3/category_resource_spec.rb @@ -100,10 +100,7 @@ context "invalid priority id" do let(:get_path) { api_v3_paths.category "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "Category" } - end + it_behaves_like "not found" end end @@ -116,10 +113,7 @@ get get_path end - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "Category" } - end + it_behaves_like "not found" end end end diff --git a/spec/requests/api/v3/help_texts/help_texts_resource_spec.rb b/spec/requests/api/v3/help_texts/help_texts_resource_spec.rb index 5a56e499fd93..cccdb84c9836 100644 --- a/spec/requests/api/v3/help_texts/help_texts_resource_spec.rb +++ b/spec/requests/api/v3/help_texts/help_texts_resource_spec.rb @@ -92,10 +92,7 @@ context "invalid type id" do let(:get_path) { api_v3_paths.type "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "HelpText" } - end + it_behaves_like "not found" end context "invisible type id" do diff --git a/spec/requests/api/v3/priority_resource_spec.rb b/spec/requests/api/v3/priority_resource_spec.rb index 38547bf77dc4..b7f93bdb39bf 100644 --- a/spec/requests/api/v3/priority_resource_spec.rb +++ b/spec/requests/api/v3/priority_resource_spec.rb @@ -86,10 +86,7 @@ context "invalid priority id" do let(:get_path) { api_v3_paths.priority "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "IssuePriority" } - end + it_behaves_like "not found" end end diff --git a/spec/requests/api/v3/status_resource_spec.rb b/spec/requests/api/v3/status_resource_spec.rb index 58bbd0bb25fa..f9c83ad19418 100644 --- a/spec/requests/api/v3/status_resource_spec.rb +++ b/spec/requests/api/v3/status_resource_spec.rb @@ -88,10 +88,7 @@ context "invalid status id" do let(:get_path) { api_v3_paths.status "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "Status" } - end + it_behaves_like "not found" end end diff --git a/spec/requests/api/v3/types/type_resource_spec.rb b/spec/requests/api/v3/types/type_resource_spec.rb index dd21d89a94e2..2b6934a37eb0 100644 --- a/spec/requests/api/v3/types/type_resource_spec.rb +++ b/spec/requests/api/v3/types/type_resource_spec.rb @@ -88,10 +88,7 @@ context "invalid type id" do let(:get_path) { api_v3_paths.type "bogus" } - it_behaves_like "param validation error" do - let(:id) { "bogus" } - let(:type) { "Type" } - end + it_behaves_like "not found" end end