From 7a01babe01e089c863297218f4cbda40a562a7e7 Mon Sep 17 00:00:00 2001 From: richie Date: Wed, 29 Jun 2016 16:11:40 -0700 Subject: [PATCH] Support mount-specific swagger_root and add swagger_filter setting --- Gemfile.lock | 3 +- README.md | 53 +++++++++---- .../swagger_rails/application_controller.rb | 8 -- .../swagger_rails/swagger_ui_controller.rb | 22 +++++- .../swagger_rails/application_helper.rb | 4 - config/routes.rb | 4 +- .../install/install_generator.rb | 4 + .../install/templates/swagger_helper.rb | 30 +++++++ .../install/templates/swagger_rails.rb | 22 +++--- lib/swagger_rails.rb | 34 ++------ lib/swagger_rails/configuration.rb | 10 +++ lib/swagger_rails/engine.rb | 6 +- lib/swagger_rails/middleware/swagger_docs.rb | 4 - lib/swagger_rails/middleware/swagger_json.rb | 35 +++++++++ lib/swagger_rails/middleware/swagger_ui.rb | 17 ++-- lib/swagger_rails/rspec/dsl.rb | 4 +- lib/swagger_rails/rspec/formatter.rb | 15 ++-- lib/tasks/swagger_rails_tasks.rake | 1 - .../config/initializers/swagger_rails.rb | 22 +++--- spec/dummy/spec/integration/blogs_spec.rb | 2 +- spec/dummy/spec/swagger_helper.rb | 30 +++++++ spec/dummy/swagger/v1/swagger.json | 2 +- .../swagger_rails/custom_ui_generator_spec.rb | 21 ++--- .../fixtures/config/routes.rb | 0 .../swagger_rails/install_generator_spec.rb | 46 ++++++----- spec/spec_helper.rb | 3 - spec/swagger_helper.rb | 30 +++++++ .../middleware/swagger_json_spec.rb | 78 +++++++++++++++++++ spec/{ => swagger_rails}/test_visitor_spec.rb | 0 swagger_rails.gemspec | 1 + 30 files changed, 367 insertions(+), 144 deletions(-) delete mode 100644 app/controllers/swagger_rails/application_controller.rb delete mode 100644 app/helpers/swagger_rails/application_helper.rb create mode 100644 lib/generators/swagger_rails/install/templates/swagger_helper.rb create mode 100644 lib/swagger_rails/configuration.rb delete mode 100644 lib/swagger_rails/middleware/swagger_docs.rb create mode 100644 lib/swagger_rails/middleware/swagger_json.rb create mode 100644 spec/dummy/spec/swagger_helper.rb rename spec/generators/{ => swagger_rails}/fixtures/config/routes.rb (100%) create mode 100644 spec/swagger_helper.rb create mode 100644 spec/swagger_rails/middleware/swagger_json_spec.rb rename spec/{ => swagger_rails}/test_visitor_spec.rb (100%) diff --git a/Gemfile.lock b/Gemfile.lock index bc69f48..6490428 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ PATH remote: . specs: swagger_rails (1.0.0.pre.beta2) + rack rails (>= 3.1, < 5) GEM @@ -111,7 +112,7 @@ GEM treetop (1.4.15) polyglot polyglot (>= 0.3.1) - tzinfo (0.3.49) + tzinfo (0.3.50) PLATFORMS ruby diff --git a/README.md b/README.md index e5d6b3d..c37e20f 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,9 @@ __NOTE__: It's early days so please be gentle when reporting issues :) As author 3. Create an integration spec to describe and test your API ```ruby - require 'rails_helper' - require 'swagger_rails/rspec/dsl' + require 'swagger_helper' describe 'Blogs API' do - extend SwaggerRails::RSpec::DSL path '/blogs' do @@ -86,33 +84,40 @@ This will wire up routes for the swagger docs and swagger-ui assets, all prefixe If you'd like your swagger resources to appear under a different base path, you can change the Engine mount point from "/api-docs" to something else. -By default, the generator will create all operation descriptions in a single swagger.json file. You can customize this by defining additional documents in the swagger_rails initializer (also created by the install generator) ... +## Multiple Swagger Documents ## + +By default, the generator will create all operation descriptions in a single swagger.json file. You can customize this by defining additional documents in the swagger_helper (installed under your spec folder) ... ```ruby - SwaggerRails.configure do |c| + RSpec.configure do |config| + ... - c.swagger_doc 'v1/swagger.json' do - { - info: { title: 'API V1', version: 'v1' } - } - end + config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1', + version: 'v1' + } + }, - c.swagger_doc 'v2/swagger.json' do - { - info: { title: 'API V2', version: 'v2' } + 'v2/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V2', + version: 'v2' + } } - end + } end ``` And then tagging your spec's with the target swagger_doc: ```ruby - require 'rails_helper' - require 'swagger_rails/rspec/dsl' + require 'swagger_helper' describe 'Blogs API V2', swagger_doc: 'v2/swagger.json' do - extend SwaggerRails::RSpec::DSL path '/blogs' do ... @@ -123,6 +128,20 @@ And then tagging your spec's with the target swagger_doc: Then, when you run the generator and spin up the swagger-ui, you'll see a select box in the top right allowing your audience to switch between the different API versions. +## Tweaking the Swagger Document with Request Context ## + +You can provide global metadata for Swagger documents in the swagger_helper file and this will be included in the resulting Swagger JSON when you run the "swaggerize" rake task. For the most part, this is sufficient. However, you may want to make some changes that require the current request context. This is possible by applying an optional swagger_filter in the swagger_rails initializer (installed into config/initializers): + + ```ruby + SwaggerRails.configure do |c| + ... + + c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } + end + ``` + +This function will get called prior to serialization of any Swagger file and is passed the rack env for the current request. This provides a lot of flexibilty. For example, you could dynamically assign the "host" property (as shown above) or you could inspect session information or Authoriation header and remove operations based on user permissions. + ## Customizing the UI ## The swagger-ui provides several options for customizing it's behavior, all of which are documented here https://github.com/swagger-api/swagger-ui#swaggerui. If you need to tweak these or customize the overall look and feel of your swagger-ui, then you'll need to provide your own version of index.html. You can do this with the following generator. diff --git a/app/controllers/swagger_rails/application_controller.rb b/app/controllers/swagger_rails/application_controller.rb deleted file mode 100644 index 23c2481..0000000 --- a/app/controllers/swagger_rails/application_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -module SwaggerRails - class ApplicationController < ActionController::Base - - def redirect_to_swagger_ui - redirect_to swagger_ui_path - end - end -end diff --git a/app/controllers/swagger_rails/swagger_ui_controller.rb b/app/controllers/swagger_rails/swagger_ui_controller.rb index 512150b..c6d60c8 100644 --- a/app/controllers/swagger_rails/swagger_ui_controller.rb +++ b/app/controllers/swagger_rails/swagger_ui_controller.rb @@ -1,14 +1,32 @@ +require 'json' + module SwaggerRails class SwaggerUiController < ApplicationController + def root + redirect_to action: 'index' + end + def index + swagger_root = SwaggerRails.config.resolve_swagger_root(request.env) + swagger_filenames = Dir["#{swagger_root}/**/*.json"] + @discovery_paths = Hash[ - SwaggerRails.config.swagger_docs.map do |path, doc| - [ "#{root_path}#{path}", doc[:info][:title] ] + swagger_filenames.map do |filename| + [ + "#{root_path.chomp('/')}#{filename.sub(swagger_root, '')}", + load_json(filename)["info"]["title"] + ] end ] render :index, layout: false end + + private + + def load_json(filename) + JSON.parse(File.read(filename)) + end end end diff --git a/app/helpers/swagger_rails/application_helper.rb b/app/helpers/swagger_rails/application_helper.rb deleted file mode 100644 index ccebb84..0000000 --- a/app/helpers/swagger_rails/application_helper.rb +++ /dev/null @@ -1,4 +0,0 @@ -module SwaggerRails - module ApplicationHelper - end -end diff --git a/config/routes.rb b/config/routes.rb index 079d448..24ff04d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,4 @@ SwaggerRails::Engine.routes.draw do - root to: 'application#redirect_to_swagger_ui' - get '/index.html', to: 'swagger_ui#index', as: :swagger_ui + root to: 'swagger_ui#root' + get '/index.html', to: 'swagger_ui#index' end diff --git a/lib/generators/swagger_rails/install/install_generator.rb b/lib/generators/swagger_rails/install/install_generator.rb index 8f5cf29..9930130 100644 --- a/lib/generators/swagger_rails/install/install_generator.rb +++ b/lib/generators/swagger_rails/install/install_generator.rb @@ -13,6 +13,10 @@ module SwaggerRails template('swagger_rails.rb', 'config/initializers/swagger_rails.rb') end + def add_rspec_helper + template('swagger_helper.rb', 'spec/swagger_helper.rb') + end + def add_routes route("mount SwaggerRails::Engine => '/api-docs'") end diff --git a/lib/generators/swagger_rails/install/templates/swagger_helper.rb b/lib/generators/swagger_rails/install/templates/swagger_helper.rb new file mode 100644 index 0000000..26523b3 --- /dev/null +++ b/lib/generators/swagger_rails/install/templates/swagger_helper.rb @@ -0,0 +1,30 @@ +require 'rails_helper' +require 'swagger_rails/rspec/dsl' + +RSpec.configure do |config| + # NOTE: Should be no need to modify these 3 lines + config.add_setting :swagger_root + config.add_setting :swagger_docs + config.extend SwaggerRails::RSpec::DSL + + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need + # to ensure that the same folder is also specified in the swagger_rails initializer + config.swagger_root = Rails.root.to_s + '/swagger' + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the "swaggerize" rake task, the complete Swagger will be generated + # at the provided relative path under swagger_root + # By default, the operations defined in spec files are added to the first + # document below. You can override this behavior by adding a swagger_doc tag to the + # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' + config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1', + version: 'v1' + } + } + } +end diff --git a/lib/generators/swagger_rails/install/templates/swagger_rails.rb b/lib/generators/swagger_rails/install/templates/swagger_rails.rb index 6c2bc04..aa0d9ec 100644 --- a/lib/generators/swagger_rails/install/templates/swagger_rails.rb +++ b/lib/generators/swagger_rails/install/templates/swagger_rails.rb @@ -1,16 +1,14 @@ SwaggerRails.configure do |c| - # Define your swagger documents and provide any global metadata here - # (Individual operations are generated from your spec/test files) - c.swagger_doc 'v1/swagger.json', - { - swagger: '2.0', - info: { - title: 'API V1', - version: 'v1' - } - } + # Specify a root folder where Swagger JSON files are located + # This is used by the Swagger middleware to serve requests for API descriptions + # NOTE: If you're using the rspec DSL to generate Swagger, you'll need to ensure + # that the same folder is also specified in spec/swagger_helper.rb + c.swagger_root = Rails.root.to_s + '/swagger' - # Specify a location to output generated swagger files - c.swagger_dir File.expand_path('../../../swagger', __FILE__) + # Inject a lamda function to alter the returned Swagger prior to serialization + # The function will have access to the rack env for the current request + # For example, you could leverage this to dynamically assign the "host" property + # + #c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } end diff --git a/lib/swagger_rails.rb b/lib/swagger_rails.rb index d6c682e..637bc72 100644 --- a/lib/swagger_rails.rb +++ b/lib/swagger_rails.rb @@ -1,33 +1,15 @@ -require "swagger_rails/engine" +require 'swagger_rails/version' +require 'swagger_rails/configuration' module SwaggerRails - class Configuration - attr_reader :swagger_docs, :swagger_dir_string - - def initialize - @swagger_docs = {} - @swagger_dir_string = nil - end - - def swagger_doc(path, doc) - @swagger_docs[path] = doc - end - - def swagger_dir(dir_string) - @swagger_dir_string = dir_string - end + def self.configure + yield(config) end - class << self - attr_reader :config - - def configure - yield config - end - - def config - @config ||= Configuration.new - end + def self.config + @config ||= Configuration.new end end + +require 'swagger_rails/engine' if defined?(Rails) diff --git a/lib/swagger_rails/configuration.rb b/lib/swagger_rails/configuration.rb new file mode 100644 index 0000000..8d434e6 --- /dev/null +++ b/lib/swagger_rails/configuration.rb @@ -0,0 +1,10 @@ +module SwaggerRails + class Configuration + attr_accessor :swagger_root, :swagger_filter + + def resolve_swagger_root(env) + path_params = env['action_dispatch.request.path_parameters'] || {} + path_params[:swagger_root] || swagger_root + end + end +end diff --git a/lib/swagger_rails/engine.rb b/lib/swagger_rails/engine.rb index 4b44f0d..43e8a99 100644 --- a/lib/swagger_rails/engine.rb +++ b/lib/swagger_rails/engine.rb @@ -1,4 +1,4 @@ -require 'swagger_rails/middleware/swagger_docs' +require 'swagger_rails/middleware/swagger_json' require 'swagger_rails/middleware/swagger_ui' module SwaggerRails @@ -6,8 +6,8 @@ module SwaggerRails isolate_namespace SwaggerRails initializer 'swagger_rails.initialize' do |app| - middleware.use SwaggerDocs, SwaggerRails.config.swagger_dir_string - middleware.use SwaggerUi, "#{root}/bower_components/swagger-ui/dist" + middleware.use SwaggerJson, SwaggerRails.config + middleware.use SwaggerUi end end end diff --git a/lib/swagger_rails/middleware/swagger_docs.rb b/lib/swagger_rails/middleware/swagger_docs.rb deleted file mode 100644 index b4c7c85..0000000 --- a/lib/swagger_rails/middleware/swagger_docs.rb +++ /dev/null @@ -1,4 +0,0 @@ -module SwaggerRails - class SwaggerDocs < ActionDispatch::Static - end -end diff --git a/lib/swagger_rails/middleware/swagger_json.rb b/lib/swagger_rails/middleware/swagger_json.rb new file mode 100644 index 0000000..56e50ed --- /dev/null +++ b/lib/swagger_rails/middleware/swagger_json.rb @@ -0,0 +1,35 @@ +require 'json' + +module SwaggerRails + class SwaggerJson + + def initialize(app, config) + @app = app + @config = config + end + + def call(env) + path = env['PATH_INFO'] + filename = "#{@config.resolve_swagger_root(env)}/#{path}" + + if env['REQUEST_METHOD'] == 'GET' && File.file?(filename) + swagger = load_json(filename) + @config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil? + + return [ + '200', + { 'Content-Type' => 'application/json' }, + [ JSON.dump(swagger) ] + ] + end + + return @app.call(env) + end + + private + + def load_json(filename) + JSON.parse(File.read(filename)) + end + end +end diff --git a/lib/swagger_rails/middleware/swagger_ui.rb b/lib/swagger_rails/middleware/swagger_ui.rb index 8b886b9..3de7939 100644 --- a/lib/swagger_rails/middleware/swagger_ui.rb +++ b/lib/swagger_rails/middleware/swagger_ui.rb @@ -1,14 +1,13 @@ module SwaggerRails - class SwaggerUi < ActionDispatch::Static - IGNORE_PATHS = [ '', '/', '/index.html' ] + class SwaggerUi < Rack::Static - def call(env) - # Serve index.html via swagger_ui_controller - if IGNORE_PATHS.include?(env['PATH_INFO']) - @app.call(env) - else - super(env) - end + def initialize(app) + options = { + root: File.expand_path('../../../../bower_components/swagger-ui/dist', __FILE__), + urls: %w(/css /fonts /images /lang /lib /oc2.html /swagger-ui.js) + } + # NOTE: /index.html is excluded as it is servered dynamically (via conrtoller) + super(app, options) end end end diff --git a/lib/swagger_rails/rspec/dsl.rb b/lib/swagger_rails/rspec/dsl.rb index 2cce961..e9ab6db 100644 --- a/lib/swagger_rails/rspec/dsl.rb +++ b/lib/swagger_rails/rspec/dsl.rb @@ -50,9 +50,9 @@ module SwaggerRails def run_test! if metadata.has_key?(:swagger_doc) - swagger_doc = SwaggerRails.config.swagger_docs[metadata[:swagger_doc]] + swagger_doc = ::RSpec.configuration.swagger_docs[metadata[:swagger_doc]] else - swagger_doc = SwaggerRails.config.swagger_docs.values.first + swagger_doc = ::RSpec.configuration.swagger_docs.values.first end test_visitor = SwaggerRails::TestVisitor.new(swagger_doc) diff --git a/lib/swagger_rails/rspec/formatter.rb b/lib/swagger_rails/rspec/formatter.rb index 4f5304a..d1aec81 100644 --- a/lib/swagger_rails/rspec/formatter.rb +++ b/lib/swagger_rails/rspec/formatter.rb @@ -1,18 +1,17 @@ -require 'rspec/core/formatters/base_text_formatter' -require 'rails_helper' +require 'rspec/core/formatters' +require 'swagger_helper' module SwaggerRails module RSpec - class Formatter ::RSpec::Core::Formatters.register self, :example_group_finished, :stop - def initialize(output, config=SwaggerRails.config) + def initialize(output) @output = output - @swagger_docs = config.swagger_docs - @swagger_dir_string = config.swagger_dir_string + @swagger_root = ::RSpec.configuration.swagger_root + @swagger_docs = ::RSpec.configuration.swagger_docs @output.puts 'Generating Swagger Docs ...' end @@ -27,8 +26,8 @@ module SwaggerRails end def stop(notification) - @swagger_docs.each do |path, doc| - file_path = File.join(@swagger_dir_string, path) + @swagger_docs.each do |url_path, doc| + file_path = File.join(@swagger_root, url_path) File.open(file_path, 'w') do |file| file.write(JSON.pretty_generate(doc)) diff --git a/lib/tasks/swagger_rails_tasks.rake b/lib/tasks/swagger_rails_tasks.rake index a0bdd9e..a31cb2a 100644 --- a/lib/tasks/swagger_rails_tasks.rake +++ b/lib/tasks/swagger_rails_tasks.rake @@ -5,7 +5,6 @@ if defined?(RSpec) require 'rspec/core/rake_task' - require 'swagger_rails' desc 'Generate Swagger JSON files from integration specs' RSpec::Core::RakeTask.new('swaggerize') do |t| diff --git a/spec/dummy/config/initializers/swagger_rails.rb b/spec/dummy/config/initializers/swagger_rails.rb index 6c2bc04..aa0d9ec 100644 --- a/spec/dummy/config/initializers/swagger_rails.rb +++ b/spec/dummy/config/initializers/swagger_rails.rb @@ -1,16 +1,14 @@ SwaggerRails.configure do |c| - # Define your swagger documents and provide any global metadata here - # (Individual operations are generated from your spec/test files) - c.swagger_doc 'v1/swagger.json', - { - swagger: '2.0', - info: { - title: 'API V1', - version: 'v1' - } - } + # Specify a root folder where Swagger JSON files are located + # This is used by the Swagger middleware to serve requests for API descriptions + # NOTE: If you're using the rspec DSL to generate Swagger, you'll need to ensure + # that the same folder is also specified in spec/swagger_helper.rb + c.swagger_root = Rails.root.to_s + '/swagger' - # Specify a location to output generated swagger files - c.swagger_dir File.expand_path('../../../swagger', __FILE__) + # Inject a lamda function to alter the returned Swagger prior to serialization + # The function will have access to the rack env for the current request + # For example, you could leverage this to dynamically assign the "host" property + # + #c.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } end diff --git a/spec/dummy/spec/integration/blogs_spec.rb b/spec/dummy/spec/integration/blogs_spec.rb index f864ae0..6f3e1e1 100644 --- a/spec/dummy/spec/integration/blogs_spec.rb +++ b/spec/dummy/spec/integration/blogs_spec.rb @@ -1,4 +1,4 @@ -require 'rails_helper' +require 'swagger_helper' describe 'Blogs API', swagger_doc: 'v1/swagger.json' do diff --git a/spec/dummy/spec/swagger_helper.rb b/spec/dummy/spec/swagger_helper.rb new file mode 100644 index 0000000..26523b3 --- /dev/null +++ b/spec/dummy/spec/swagger_helper.rb @@ -0,0 +1,30 @@ +require 'rails_helper' +require 'swagger_rails/rspec/dsl' + +RSpec.configure do |config| + # NOTE: Should be no need to modify these 3 lines + config.add_setting :swagger_root + config.add_setting :swagger_docs + config.extend SwaggerRails::RSpec::DSL + + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need + # to ensure that the same folder is also specified in the swagger_rails initializer + config.swagger_root = Rails.root.to_s + '/swagger' + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the "swaggerize" rake task, the complete Swagger will be generated + # at the provided relative path under swagger_root + # By default, the operations defined in spec files are added to the first + # document below. You can override this behavior by adding a swagger_doc tag to the + # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' + config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1', + version: 'v1' + } + } + } +end diff --git a/spec/dummy/swagger/v1/swagger.json b/spec/dummy/swagger/v1/swagger.json index b13c2e9..98c4714 100644 --- a/spec/dummy/swagger/v1/swagger.json +++ b/spec/dummy/swagger/v1/swagger.json @@ -87,4 +87,4 @@ } } } -} \ No newline at end of file +} diff --git a/spec/generators/swagger_rails/custom_ui_generator_spec.rb b/spec/generators/swagger_rails/custom_ui_generator_spec.rb index d0f6b97..50a49e5 100644 --- a/spec/generators/swagger_rails/custom_ui_generator_spec.rb +++ b/spec/generators/swagger_rails/custom_ui_generator_spec.rb @@ -1,16 +1,19 @@ require 'rails_helper' require 'generators/swagger_rails/custom_ui/custom_ui_generator' -describe SwaggerRails::CustomUiGenerator do - include GeneratorSpec::TestCase - destination File.expand_path('../tmp', __FILE__) +module SwaggerRails - before(:all) do - prepare_destination - run_generator - end + describe CustomUiGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) - it 'creates a local version of index.html.erb' do - assert_file('app/views/swagger_rails/swagger_ui/index.html.erb') + before(:all) do + prepare_destination + run_generator + end + + it 'creates a local version of index.html.erb' do + assert_file('app/views/swagger_rails/swagger_ui/index.html.erb') + end end end diff --git a/spec/generators/fixtures/config/routes.rb b/spec/generators/swagger_rails/fixtures/config/routes.rb similarity index 100% rename from spec/generators/fixtures/config/routes.rb rename to spec/generators/swagger_rails/fixtures/config/routes.rb diff --git a/spec/generators/swagger_rails/install_generator_spec.rb b/spec/generators/swagger_rails/install_generator_spec.rb index 9ecdfba..077d70d 100644 --- a/spec/generators/swagger_rails/install_generator_spec.rb +++ b/spec/generators/swagger_rails/install_generator_spec.rb @@ -1,26 +1,34 @@ require 'rails_helper' require 'generators/swagger_rails/install/install_generator' -describe SwaggerRails::InstallGenerator do - include GeneratorSpec::TestCase - destination File.expand_path('../tmp', __FILE__) +module SwaggerRails - before(:all) do - prepare_destination - config_dir = File.expand_path('../../fixtures/config', __FILE__) - FileUtils.cp_r(config_dir, destination_root) + describe InstallGenerator do + include GeneratorSpec::TestCase + destination File.expand_path('../tmp', __FILE__) - run_generator + before(:all) do + prepare_destination + fixtures_dir = File.expand_path('../fixtures', __FILE__) + FileUtils.cp_r("#{fixtures_dir}/config", destination_root) + FileUtils.cp_r("#{fixtures_dir}/spec", destination_root) + + run_generator + end + + it 'creates a default swagger directory' do + assert_directory('swagger/v1') + end + + it 'installs swagger_rails initializer' do + assert_file('config/initializers/swagger_rails.rb') + end + + it 'installs the swagger_helper for rspec' do + assert_file('spec/swagger_helper.rb') + end + + it 'wires up the swagger routes' + # Not sure how to test this end - - it 'creates a default swagger directory' do - assert_directory('swagger/v1') - end - - it 'creates a swagger_rails initializer' do - assert_file('config/initializers/swagger_rails.rb') - end - - it 'wires up the swagger routes' - # Not sure how to test this end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 08a78ee..913e28a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -89,7 +89,4 @@ RSpec.configure do |config| # as the one that triggered the failure. Kernel.srand config.seed =end - - require 'swagger_rails/rspec/dsl' - config.extend SwaggerRails::RSpec::DSL end diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb new file mode 100644 index 0000000..26523b3 --- /dev/null +++ b/spec/swagger_helper.rb @@ -0,0 +1,30 @@ +require 'rails_helper' +require 'swagger_rails/rspec/dsl' + +RSpec.configure do |config| + # NOTE: Should be no need to modify these 3 lines + config.add_setting :swagger_root + config.add_setting :swagger_docs + config.extend SwaggerRails::RSpec::DSL + + # Specify a root folder where Swagger JSON files are generated + # NOTE: If you're using the Swagger JSON middleware to serve API descriptions, you'll need + # to ensure that the same folder is also specified in the swagger_rails initializer + config.swagger_root = Rails.root.to_s + '/swagger' + + # Define one or more Swagger documents and provide global metadata for each one + # When you run the "swaggerize" rake task, the complete Swagger will be generated + # at the provided relative path under swagger_root + # By default, the operations defined in spec files are added to the first + # document below. You can override this behavior by adding a swagger_doc tag to the + # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json' + config.swagger_docs = { + 'v1/swagger.json' => { + swagger: '2.0', + info: { + title: 'API V1', + version: 'v1' + } + } + } +end diff --git a/spec/swagger_rails/middleware/swagger_json_spec.rb b/spec/swagger_rails/middleware/swagger_json_spec.rb new file mode 100644 index 0000000..805d4ea --- /dev/null +++ b/spec/swagger_rails/middleware/swagger_json_spec.rb @@ -0,0 +1,78 @@ +require 'rails_helper' + +module SwaggerRails + + describe SwaggerJson do + let(:app) { double('app') } + let(:config) do + Configuration.new.tap { |c| c.swagger_root = (Rails.root + 'swagger').to_s } + end + + subject { described_class.new(app, config) } + + describe '#call(env)' do + let(:response) { subject.call(env) } + let(:env_defaults) do + { + 'HTTP_HOST' => 'tempuri.org', + 'REQUEST_METHOD' => 'GET', + } + end + + context 'given a path that maps to an existing swagger file' do + let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.json') } + + it 'returns a 200 status' do + expect(response.length).to eql(3) + expect(response.first).to eql('200') + end + + it 'returns contents of the swagger file' do + expect(response.length).to eql(3) + expect(response.second).to include( 'Content-Type' => 'application/json') + expect(response.third.join).to include('"title":"API V1"') + end + end + + context "given a path that doesn't map to any swagger file" do + let(:env) { env_defaults.merge('PATH_INFO' => 'foobar.json') } + before do + allow(app).to receive(:call).and_return([ '500', {}, [] ]) + end + + it 'delegates to the next middleware' do + expect(response).to include('500') + end + end + + context 'when the env contains a specific swagger_root' do + let(:env) do + env_defaults.merge( + 'PATH_INFO' => 'swagger.json', + 'action_dispatch.request.path_parameters' => { + swagger_root: (Rails.root + 'swagger/v1').to_s + } + ) + end + + it 'locates files at the provided swagger_root' do + expect(response.length).to eql(3) + expect(response.second).to include( 'Content-Type' => 'application/json') + expect(response.third.join).to include('"swagger":"2.0"') + end + end + + context 'when a swagger_filter is configured' do + before do + config.swagger_filter = lambda { |swagger, env| swagger['host'] = env['HTTP_HOST'] } + end + let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.json') } + + it 'applies the filter prior to serialization' do + expect(response.length).to eql(3) + expect(response.third.join).to include('"host":"tempuri.org"') + end + end + end + end +end diff --git a/spec/test_visitor_spec.rb b/spec/swagger_rails/test_visitor_spec.rb similarity index 100% rename from spec/test_visitor_spec.rb rename to spec/swagger_rails/test_visitor_spec.rb diff --git a/swagger_rails.gemspec b/swagger_rails.gemspec index a651095..5b07844 100644 --- a/swagger_rails.gemspec +++ b/swagger_rails.gemspec @@ -16,6 +16,7 @@ Gem::Specification.new do |s| s.files = Dir["{app,bower_components/swagger-ui/dist,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"] + s.add_dependency 'rack' s.add_dependency "rails", ">= 3.1", "< 5" s.add_development_dependency "rspec-rails", "~> 3.0"