spec: generate openapi docs from specs
test plan: - run some controller tests with the environment variable OPENAPI=1 - see that some files have been put in the spec/openapi directory - e.g.: delete spec/openapi/lti/registration_token.yaml, then run OPENAPI=1 bundle exec rspec \ spec/controllers/lti/ims/dynamic_registration_controller_spec.rb refs INTEROP-8389 Change-Id: I3008a4079013e547bf264808aa7b2f092db8b6a6 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/323583 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Paul Gray <paul.gray@instructure.com> QA-Review: Paul Gray <paul.gray@instructure.com> Product-Review: Paul Gray <paul.gray@instructure.com> Build-Review: Aaron Ogata <aogata@instructure.com>
This commit is contained in:
parent
b93d6db55d
commit
f53bf990ce
|
@ -34,6 +34,7 @@ group :test do
|
|||
|
||||
gem "once-ler", "~> 2.0"
|
||||
|
||||
gem "rspec-openapi"
|
||||
gem "selenium-webdriver", "~> 4.12", require: false
|
||||
gem "testrailtagging", "0.3.8.7", require: false
|
||||
|
||||
|
|
|
@ -933,6 +933,9 @@ GEM
|
|||
rspec-mocks (3.12.6)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-openapi (0.10.0)
|
||||
actionpack (>= 5.2.0)
|
||||
rspec-core
|
||||
rspec-rails (6.1.0)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
|
@ -1303,6 +1306,7 @@ DEPENDENCIES
|
|||
rrule (~> 0.5)
|
||||
rspec (~> 3.12)
|
||||
rspec-collection_matchers (~> 1.2)
|
||||
rspec-openapi
|
||||
rspec-rails (~> 6.0)
|
||||
rspec_around_all (= 0.2.0)
|
||||
rspecq!
|
||||
|
|
|
@ -31,6 +31,7 @@ rescue LoadError
|
|||
end
|
||||
|
||||
require "crystalball"
|
||||
require "rspec/openapi"
|
||||
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
|
||||
|
@ -452,6 +453,26 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
if ENV["OPENAPI"]
|
||||
config.define_derived_metadata(file_path: %r{spec/controllers}) do |metadata|
|
||||
metadata[:attempt_openapi_generation] = true
|
||||
end
|
||||
|
||||
config.after(:example, :attempt_openapi_generation) do |example|
|
||||
OpenApiGenerator.generate(self, example)
|
||||
end
|
||||
|
||||
config.after(:suite) do
|
||||
result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
|
||||
result_recorder.record_results!
|
||||
if result_recorder.errors?
|
||||
error_message = result_recorder.error_message
|
||||
colorizer = RSpec::Core::Formatters::ConsoleCodes
|
||||
RSpec.configuration.reporter.message colorizer.wrap(error_message, :failure)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
config.around do |example|
|
||||
Rails.logger.info "STARTING SPEC #{example.full_description}"
|
||||
SpecTimeLimit.enforce(example) do
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2023 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module OpenApiGenerator
|
||||
def self.generate(running_context, example)
|
||||
# This looks at the "request" variable that is available
|
||||
# inside of the test ("running_context"). It will throw an exception
|
||||
# if the test has a request variable defined that is not actually
|
||||
# an HTTP request object.
|
||||
# rubocop:disable Style/RedundantBegin
|
||||
begin
|
||||
# rubocop:enable Style/RedundantBegin
|
||||
CanvasRails::Application.routes.router.recognize(running_context.request) do |route|
|
||||
path_parts_array = route.path.spec.to_s.sub(/\(\.:format\)$/, "").split("/")
|
||||
path_parts_array.reject! { |part| ["", "api", "v1"].include?(part) }
|
||||
first_part_of_path = path_parts_array.first
|
||||
if first_part_of_path == "lti"
|
||||
first_part_of_path = path_parts_array[0..1].join("/")
|
||||
end
|
||||
openapi_doc_file_location = "spec/openapi/#{first_part_of_path}.yaml"
|
||||
record = RSpec::OpenAPI::RecordBuilder.build(running_context, example:)
|
||||
RSpec::OpenAPI.path_records[openapi_doc_file_location] << record if record
|
||||
end
|
||||
rescue NoMethodError
|
||||
# If we are here, a request variable was defined in the test but
|
||||
# it wasn't readable as an HTTP request, or for some reason didn't
|
||||
# map to a route. This is fine; we won't document this one.
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue